#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define TRUE 1
#define FALSE 0
#define BUFF_FILE 256
#define BUFF_LINE 2048
struct NODE {
char filename[BUFF_FILE];
char lineNumber[10];
char line[BUFF_LINE];
struct NODE *next;
};
int buildList(struct NODE **listHead) {
/*
* buildList() reads input from stdin (the resulting output of a grep call)
* and generates a linked list of nodes. Each node contains the filename,
* line number, and line of text of the match that grep specified. The
* function returns a hitCount to main() so that the program knows
* whether or not the input (grep's output) was valid. The line number is
* not inserted into each node, but is available for testing purposes
* should this be necessary.
*/
char filename[BUFF_FILE];
char lineNumber[4];
char line[BUFF_LINE];
int hitCount = 0;
struct NODE *newNode = NULL;
while (fscanf(stdin, "%256[^:]:%4[^:]:%2048[^\n]\n", filename,
lineNumber, line) == 3) {
newNode = (struct NODE *) malloc(sizeof(struct NODE));
strncpy(newNode->filename, filename, BUFF_FILE);
strncpy(newNode->line, line, BUFF_LINE);
newNode->next = *listHead;
*listHead = newNode;
hitCount++;
}
return hitCount;
}
int lineHunter (struct NODE **listHead, int hitCount,
int lineNumberSwitch, int bannerSwitch) {
/*
* lineHunter() iterates through the nodes built in buildList()
* and looks for lines in the given files that match the lines presented
* in the nodes themselves. If it finds a match, it prints an '*' next
* to the line that it finds. It only opens new files (files that haven't
* been opened before) and then runs through the nodes a second time to
* find matches to the lines being printed.
*/
char filename[BUFF_FILE];
char line[BUFF_LINE];
int tagBool;
int lineCount;
struct NODE *currentNode = *listHead;
struct NODE *matchNode = NULL;
FILE *grepFile = NULL;
// If args specify banner then print it.
if (bannerSwitch) {
printf("\nTHERE ARE %d MATCHING LINES\n", hitCount);
}
// Iterate through filenames
while (currentNode->next != NULL) {
if (!(grepFile = fopen(currentNode->filename, "r"))) {
fprintf(stderr, "rgpp: error: grep filename couldn't "
"be opened -- check input.\n");
exit(1);
}
strcpy(filename, currentNode->filename);
printf("\n====================%s\n", currentNode->filename);
lineCount = 1;
// While in the same file print file contents.
while (fgets(line, BUFF_LINE, grepFile)) {
// Strip the newline off of lines pulled from file.
line[strlen(line) - 1] = '\0';
tagBool = FALSE;
matchNode = *listHead;
// Search through nodes to find match to line.
while (matchNode != NULL) {
if (strcmp(currentNode->filename, matchNode->filename) == 0
&& strcmp(currentNode->lineNumber,
matchNode->lineNumber) == 0
&& strcmp(line, matchNode->line) == 0) {
tagBool = TRUE;
}
matchNode = matchNode->next;
}
// Print tag if match was found in line.
if (tagBool) {
printf("* ");
}
else {
printf(" ");
}
// If args specify line numbering, print them.
if (lineNumberSwitch) {
printf("%4d: ", lineCount);
}
// Print the line of text.
printf("%s\n", line);
lineCount++;
}
fclose(grepFile);
// Search for next new file to open.
while (strcmp(filename, currentNode->filename) == 0
&& currentNode->next != NULL) {
currentNode = currentNode->next;
}
}
return 0;
}
int wordHunter (struct NODE **listHead, int lineNumberSwitch,
int bannerSwitch, char *word) {
/*
* wordHunter() iterates through the nodes created in buildList()
* and looks for lines in the given files that match the lines
* specified by the nodes themselves. If a match is found (aka a line
* is determined to contain matches according to grep) then the function
* goes through the line in the node looking for matching words, printing
* them in inverse video.
*/
char *matchWord[BUFF_FILE];
char filename[BUFF_FILE];
char line[BUFF_LINE];
int i;
int j;
int k;
int hitCount = 0;
int matchBool;
int lineCount;
struct NODE *currentNode = *listHead;
struct NODE *matchNode = NULL;
FILE *grepFile = NULL;
// Print out the banner based on args.
if (bannerSwitch) {
// Iterate through files to find matches for hitCount
while (currentNode != NULL) {
i = 0;
j = 0;
// This while loop checks the string character by character for
// matches to the given word.
while (currentNode->line[i] != '\0') {
if (word[j] == '\0') {
j = 0;
hitCount++;
}
else if (tolower(currentNode->line[i]) == tolower(word[j])) {
j++;
}
else {
j = 0;
}
i++;
}
currentNode = currentNode->next;
}
printf("\nTHERE ARE %d MATCHING WORDS\n", hitCount);
}
currentNode = *listHead;
// Iterate through filenames
while (currentNode->next != NULL) {
if (!(grepFile = fopen(currentNode->filename, "r"))) {
fprintf(stderr, "rgpp: error: grep filename couldn't be "
"opened -- check input\n");
exit(1);
}
strcpy(filename, currentNode->filename);
// Print out filename header.
printf("\n=====================%s\n", currentNode->filename);
lineCount = 1;
// While in the same file, print file contents
while (fgets(line, BUFF_LINE, grepFile)) {
// Strip the newline off of lines pulled from file.
line[strlen(line) - 1] = '\0';
// If the args specify line numbering, print them.
if (lineNumberSwitch) {
printf("%4d: ", lineCount);
}
// Find if line is a match in nodes.
matchNode = *listHead;
matchBool = FALSE;
while (matchNode != NULL) {
if (strcmp(line, matchNode->line) == 0) {
matchBool = TRUE;
}
matchNode = matchNode->next;
}
// If the line isn't a match then print the line
if (!matchBool) {
printf("%s", line);
}
else {
// Find and highlight the matching words in the node line.
i = 0;
j = 0;
*matchWord = "";
// This while loop checks the string character by character for
// matches to the given word. If a match is not found, the
// characters are printed to output. If a match is found, it
// is highlighted with inverse video and then printed.
while (line[i] != '\0') {
if (word[j] == '\0') {
printf("\e[7m");
for (k = i - j; k < i; k++) {
printf("%c", line[k]);
}
printf("\e[0m");
j = 0;
}
if (tolower(line[i]) == tolower(word[j])) {
j++;
}
else {
if (j != 0) {
for (k = i - j; k <= i; k++) {
printf("%c", line[k]);
}
j = 0;
}
else {
printf("%c", line[i]);
}
}
i++;
}
}
lineCount++;
printf("\n");
}
fclose(grepFile);
// Search for next file to open.
while (strcmp(filename, currentNode->filename) == 0
&& currentNode->next != NULL) {
currentNode = currentNode->next;
}
}
return 0;
}
int main(int argc, char *argv[]) {
/*
* main()'s soul purpose is to receive the command line arguments for rgpp
* and then process them and make the necessary function calls to fulfill
* the user's request. It also checks to make sure that return values are
* valid from those functions.
*/
char word[BUFF_LINE];
int i = 1;
int modeSwitch = -1;
int lineNumberSwitch = 0;
int bannerSwitch = 0;
int hitCount = 0;
struct NODE *list = NULL;
// Check for minimum number of required args.
if (strcmp(argv[1], "-l") != 0 && strcmp(argv[1], "-w") != 0) {
fprintf(stderr, "rgpp: usage: rgpp [-l | -w word] {-n} {-b}\n");
exit(1);
}
// Read args, set variables based on switches and optional word.
while(argv[i] != NULL) {
if (!strcmp(argv[i], "-l")) {
modeSwitch = 0;
}
else if (!strcmp(argv[i], "-w")) {
modeSwitch = 1;
strncpy(word, argv[i+1], BUFF_LINE);
i++;
}
else if (!strcmp(argv[i], "-b")) {
bannerSwitch = 1;
}
else if (!strcmp(argv[i], "-n")) {
lineNumberSwitch = 1;
}
else {
fprintf(stderr, "rgpp: usage: rgpp [-l | -w word] {-n} {-b}\n");
exit(1);
}
i++;
}
// Call buildList to read input and build linked list.
hitCount = buildList(&list);
if (hitCount == 0) {
fprintf(stderr, "rgpp: error: no grep results found -- "
"check grep input\n");
exit(1);
}
if (modeSwitch) {
if (wordHunter(&list, lineNumberSwitch, bannerSwitch, word) != 0) {
fprintf(stderr, "rgpp: error: no word matches --"
" check grep input and word arg\n");
exit(1);
}
}
else {
if (lineHunter(&list, hitCount, lineNumberSwitch, bannerSwitch) != 0) {
fprintf(stderr, "rgpp: error: no line matches --"
" check grep input\n");
exit(1);
}
}
// Free allocated memory.
struct NODE *tmp;
while (list != NULL) {
tmp = list;
list = list->next;
free(tmp);
}
exit(0);
}