summaryrefslogblamecommitdiff
path: root/pgm2/rgpp.c
blob: 6d7b2ee47dc54d85ce0f7e92ff4c94ff1c9762c1 (plain) (tree)




























































































































































































































































































































































































                                                                                               
#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);
}