/** * @file mygrep.c * @author Ivaylo Ivanov 11777707 * @date 03.11.2018 * * @brief Main program module. * * A reduced variation of the Unix-command `grep`. * It reads in several files and prints all lines containing a keyword. * * SYNOPSIS * mygrep [-i] [-o outfile] keyword [file...] * * The program `mygrep` reads files line by line and for each line checks whether it contains the search * term keyword. The line is printed if it contains the keyword, otherwise it is not printed. * The program accepts lines of any length. * * If one or multiple input files are specified (given as positional arguments after keyword), then mygrep * reads each of them in the order they are given. If no input file is specified, the program reads from * `stdin`. * * If the option `-o` is given, the output is written to the specified file (outfile). Otherwise, the output is * written to `stdout`. * * If the option `-i` is given, the program does not differentiate between lower and upper case letters, i.e. * the search for the keyword in a line is case insensitive. * **/ #define _GNU_SOURCE ///< in order for strcasestr to work #include #include #include #include static void print_usage(void); static void check_opts_number(int argc); static void check_for_string(char *to_check, char *to_find, int iflag, char *ofile); int main(int argc, char *argv[]) { check_opts_number(argc); int iflag = 0; char *ofile = NULL; char *keyword = NULL; char *filename = NULL; int c; /// Process the options while((c = getopt(argc, argv, "io:h")) != -1) { switch(c) { case 'i': iflag = 1; break; case 'o': ofile = optarg; break; case 'h': printf("Command usage:\n"); print_usage(); exit(EXIT_SUCCESS); break; case '?': print_usage(); exit(EXIT_FAILURE); default: abort(); } } /// Check if the required argument is there if(argv[optind] == NULL) { fprintf(stderr, "Mandatory argument 'keyword' missing.\n"); print_usage(); exit(EXIT_FAILURE); } else { keyword = argv[optind]; filename = argv[optind + 1]; } /// Get user input if there is no input file defined while(filename == NULL) { size_t len; ///< Define an unsigned string length value if(getline(&filename, &len, stdin) != -1) { ///< Read until new line check_for_string(filename, keyword, iflag, ofile); filename = NULL; ///< Reset the input } } /// Get data from input file if(filename != NULL) { FILE *in = fopen(filename, "r"); if(in == NULL) { fprintf(stderr, "Error opening the file %s\n", filename); exit(EXIT_FAILURE); } size_t len; char *line = NULL; ///< Define a pointer to hold our value /// Read the file line by line and execute check_for_string for each line while(getline(&line, &len, in) != -1) { check_for_string(line, keyword, iflag, ofile); } } return EXIT_SUCCESS; } static void print_usage(void) { printf("mygrep [-i] [-o outfile] keyword [file...]\n"); } /** * @brief Function to check the number of arguments * @details Checks the number of arguments supplied to the command line. * If the arguments are less than the required or more than possible, it exits * @param argc Number of arguments obtained from the command line * @return none * **/ static void check_opts_number(int argc) { if(argc <= 1) { fprintf(stderr, "At least one argument expected.\n"); print_usage(); exit(EXIT_FAILURE); } else if(argc > 6) { fprintf(stderr, "Too many arguments supplied.\n"); print_usage(); exit(EXIT_FAILURE); } } /** * @brief Function to check whether or not a string is contained in another string * @details Checks whether or not a substring is contained in a line. * If it is there and no output file is passed, it will print the line to stdout * If it is there and an output file is passed, it will append the line to the end of the output file * * @param * to_check: String to be checked against * to_find: String to search for * iflag: Whether or not to ignore the casing of the letters * ofile: The file to append the output to * @return none * **/ static void check_for_string(char *to_check, char *to_find, int iflag, char *ofile) { if(ofile == NULL) { /** * Find the first occurance of needle in haystack and return it as a pointer. * If found, print haystack **/ if(iflag == 0 && strstr(to_check, to_find) != NULL) printf("%s", to_check); else if(iflag == 1 && strcasestr(to_check, to_find)) printf("%s", to_check); } else { /** * Open the specified file for appending * Find the first occurance of needle in haystack and return it as a pointer. * If found, append haystack to the end of the file * Close the file **/ FILE *out = fopen(ofile, "a"); /// Check if the file was opened successfully if(out == NULL) { fprintf(stderr, "Error opening the file %s\n", ofile); exit(EXIT_FAILURE); } if(iflag == 0 && strstr(to_check, to_find)) fprintf(out, "%s", to_check); else if(iflag == 1 && strcasestr(to_check, to_find)) fprintf(out, "%s", to_check); fclose(out); } }