senc

Simple fuzzy search
Log | Files | Refs

senc.c (3528B)


      1 #include <ctype.h>
      2 #include <errno.h>
      3 #include <limits.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <unistd.h>
      8 
      9 // IDEA: Color results based on match level
     10 // or even better, color matching characters
     11 
     12 #define MAX_RESULTS 50
     13 #define MAX_PATH_LENGTH 256
     14 
     15 /**
     16  * Search match level
     17  * FUZZY example: search 'his ring' will match on 'this string' 
     18  */
     19 enum {NONE, FUZZY, NOCASE, EXACT};
     20 
     21 FILE * fp;
     22 typedef struct {
     23 	char * filename;
     24 	int matchlvl;
     25 } match;
     26 
     27 int
     28 search(char * search_string, char * filename) {
     29 	int search_len = strlen(search_string);
     30 	int filename_len = strlen(filename);
     31 	if (search_len > filename_len) {
     32 		return NONE;
     33 	}
     34 	// Exact matches
     35 	int mchars = 0;
     36 	for (int i = 0; i < filename_len; i++) {
     37 		if (search_string[mchars] == filename[i]) {
     38 			mchars++;
     39 		} else {
     40 			mchars = 0;
     41 		}
     42 		if (mchars == search_len) {
     43 			return EXACT;
     44 		}
     45 	}
     46 	// Case insensitive matches
     47 	mchars = 0;
     48 	for (int i = 0; i < filename_len; i++) {
     49 		if (tolower(search_string[mchars]) == tolower(filename[i])) {
     50 			mchars++;
     51 		} else {
     52 			mchars = 0;
     53 		}
     54 		if (mchars == search_len) {
     55 			return NOCASE;
     56 		}
     57 	}
     58 	// Weak matches
     59 	mchars = 0;
     60 	for (int i = 0; i < filename_len; i++) {
     61 		if (tolower(search_string[mchars]) == tolower(filename[i])) {
     62 			mchars++;
     63 		}
     64 		if (mchars == search_len) {
     65 			return FUZZY;
     66 		}
     67 	}
     68 	return NONE;
     69 }
     70 
     71 
     72 /**
     73  * Searches favorite paths and stores matches in given matches variable
     74  */
     75 int
     76 search_favs(match * matches, char * search_string) {
     77 	char buf[LINE_MAX];
     78 	int line_match;
     79 	int read_chars;
     80 	int match_i = 0;
     81 	// TODO: Find out if getline is the preferred way
     82 	while (fgets(buf, LINE_MAX, fp)) {
     83 		line_match = search(search_string, buf);
     84 		int len = strlen(buf);
     85 		if (buf[len - 1] == '\n') {
     86 			buf[len - 1] = '\0';
     87 		}
     88 		if (line_match == NONE) {
     89 			continue;
     90 		}
     91 		// Have to copy string, because setting 
     92 		// filename = buf, will just make all filenames point to
     93 		// the same memory location and overwrite each other.
     94 		strcpy(matches[match_i].filename, buf);
     95 		matches[match_i].matchlvl = line_match;
     96 		match_i++;
     97 		if (match_i == MAX_RESULTS) {
     98 			break;
     99 		}
    100 	}
    101 	return match_i;
    102 }
    103 
    104 int
    105 add_entry(char * new) {
    106 	char *line = NULL;
    107 	size_t len = 0;
    108 	ssize_t nread;
    109 
    110 	/*
    111 	const char * str = "Hello yy'aaalll\n";
    112 	fwrite(str, sizeof(char), strlen(str), f);
    113 	if (errno) {
    114 		printf("errno = %d\n", errno);
    115 	}
    116 	*/
    117 	return 0;
    118 }
    119 
    120 /**
    121  * Sort matches by matchlvl
    122  */
    123 int
    124 compare_matches(const void * e1, const void * e2) {
    125 	match m1 = *((match*) e1);
    126 	match m2 = *((match*) e2);
    127 	return m2.matchlvl - m1.matchlvl;
    128 }
    129 
    130 char *
    131 matchlvl_to_string(int match_level) {
    132 	switch (match_level) {
    133 		case FUZZY: return "fuzzy";
    134 		case NOCASE: return "case ignored";
    135 		case EXACT: return "exact";
    136 	}
    137 }
    138 
    139 int
    140 main(int nargs, char ** args) {
    141 	char * favs_file;
    142 	char * term;
    143 	int i;
    144 	if (nargs < 2) {
    145 		printf(
    146 			"Usage: senc [favorites file] <search term>\n"
    147 		);
    148 		exit(EXIT_FAILURE);
    149 	} else if (nargs < 3) {
    150 		favs_file = strcat(getenv("HOME"), "/.sencs");
    151 		term = args[1];
    152 	} else {
    153 		favs_file = args[1];
    154 		term = args[2];
    155 	}
    156 	if ((fp = fopen(favs_file, "a+")) == NULL) {
    157 		perror("fopen");
    158 		exit(EXIT_FAILURE);
    159 	}
    160 
    161 	match matches[MAX_RESULTS];
    162 	for (i = 0; i < MAX_RESULTS; i++) {
    163 		matches[i].filename = malloc(LINE_MAX);
    164 	}
    165 	int match_i = search_favs(matches, term);
    166 	qsort(matches, match_i, sizeof(match), compare_matches);
    167 	for (i = 0; i < match_i; i++) {
    168 		printf("%s\t \e[1m%s\e[0m \n", matches[i].filename,
    169 				matchlvl_to_string(matches[i].matchlvl));
    170 	}
    171 	fclose(fp);
    172 }