177 lines
5.6 KiB
C
177 lines
5.6 KiB
C
/**
|
|
* @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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
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);
|
|
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);
|
|
}
|
|
}
|