mygrep.c 5.63 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/**
 * @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.
 *
 **/

30
#define _GNU_SOURCE ///< in order for strcasestr to work
Ivaylo Ivanov's avatar
Ivaylo Ivanov committed
31
#include <stdio.h>
32 33
#include <stdlib.h>
#include <unistd.h>
34
#include <string.h>
Ivaylo Ivanov's avatar
Ivaylo Ivanov committed
35

36 37 38 39 40 41
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);
42 43 44 45 46

    int iflag = 0;
    char *ofile = NULL;
    char *keyword = NULL;
    char *filename = NULL;
47
    int c;
48

49
    /// Process the options
50
    while((c = getopt(argc, argv, "io:h")) != -1) {
51 52 53 54 55 56 57 58
        switch(c) {
            case 'i':
                iflag = 1;
                break;
            case 'o':
                ofile = optarg;
                break;
            case 'h':
59
                printf("Command usage:\n");
60
                print_usage();
61
                exit(EXIT_SUCCESS);
62 63 64 65 66 67 68 69 70
                break;
            case '?':
                print_usage();
                exit(EXIT_FAILURE);
            default:
                abort();
        }
    }

71
    /// Check if the required argument is there
72
    if(argv[optind] == NULL) {
73
        fprintf(stderr, "Mandatory argument 'keyword' missing.\n");
74 75 76 77 78 79 80
        print_usage();
        exit(EXIT_FAILURE);
    } else {
        keyword = argv[optind];
        filename = argv[optind + 1];
    }

81
    /// Get user input if there is no input file defined
82
    while(filename == NULL) {
83
        size_t len; ///< Define an unsigned string length value
84

85
        if(getline(&filename, &len, stdin) != -1) {  ///< Read until new line
86
            check_for_string(filename, keyword, iflag, ofile);
87
            filename = NULL; ///< Reset the input
88 89 90
        }
    }

91
    /// Get data from input file
92
    if(filename != NULL) {
93 94
        FILE *in = fopen(filename, "r");
        if(in == NULL) {
95
            fprintf(stderr, "Error opening the file %s\n", filename);
96 97 98 99
            exit(EXIT_FAILURE);
        }

        size_t len;
100
        char *line = NULL; ///< Define a pointer to hold our value
101

102
        /// Read the file line by line and execute check_for_string for each line
103 104 105
        while(getline(&line, &len, in) != -1) {
            check_for_string(line, keyword, iflag, ofile);
        }
106 107
    }

108
    return EXIT_SUCCESS;
109 110
}

111
static void print_usage(void) {
112 113 114
    printf("mygrep [-i] [-o outfile] keyword [file...]\n");
}

115 116
/**
 * @brief Function to check the number of arguments
117
 * @details Checks the number of arguments supplied to the command line.
118 119
 *          If the arguments are less than the required or more than possible, it exits
 * @param argc Number of arguments obtained from the command line
120 121
 * @return none
 *
122 123
 **/
static void check_opts_number(int argc) {
124 125 126 127 128 129 130 131 132 133 134
    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);
    }
}

135 136
/**
 * @brief Function to check whether or not a string is contained in another string
137
 * @details Checks whether or not a substring is contained in a line.
138 139
 *          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
140
 *
141 142 143 144 145 146
 * @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
147
 *
148 149
 **/
static void check_for_string(char *to_check, char *to_find, int iflag, char *ofile) {
150 151 152 153 154 155 156 157 158 159 160 161 162 163
    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
         **/
164
        FILE *out = fopen(ofile, "a");
165

166 167
        /// Check if the file was opened successfully
        if(out == NULL) {
168
            fprintf(stderr, "Error opening the file %s\n", ofile);
169 170 171 172 173 174 175 176
            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);
    }
Ivaylo Ivanov's avatar
Ivaylo Ivanov committed
177
}