paulo@17: // rcg.c -- regex colored grep paulo@17: // Copyright (C) 2010 by Paulo Ang (pbba13@gmail.com) paulo@17: // paulo@17: // This program is free software: you can redistribute it and/or modify paulo@17: // it under the terms of the GNU General Public License as published by paulo@17: // the Free Software Foundation, either version 3 of the License, or paulo@17: // (at your option) any later version. paulo@17: // paulo@17: // This program is distributed in the hope that it will be useful, paulo@17: // but WITHOUT ANY WARRANTY; without even the implied warranty of paulo@17: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the paulo@17: // GNU General Public License for more details. paulo@17: // paulo@17: // You should have received a copy of the GNU General Public License paulo@17: // along with this program. If not, see . paulo@17: paulo@17: pang@1: #include pang@1: #include pang@1: #include pang@1: pang@1: #include pang@1: paulo@4: #include paulo@4: paulo@4: #define _GNU_SOURCE paulo@4: #include paulo@4: paulo@22: size_t bufsize; paulo@22: const size_t BUFSIZE_MIN = 1024; paulo@22: const size_t ERRBUFSIZE = 512; pang@1: paulo@9: const char *_CLR_START = "\x1B["; paulo@9: const char *_CLR_BOLD = "1;"; paulo@9: char *CLR_START; paulo@5: const char *CLR_END = "m"; paulo@5: const char *CLR_CLEAR = "\x1B[0m"; pang@1: paulo@9: typedef struct _colors { paulo@9: char *name; paulo@9: char *fg; paulo@9: char *bg; paulo@9: } Colors; paulo@9: paulo@9: const Colors COLORS[] = { paulo@9: { "black", "30", "40" }, paulo@9: { "red", "31", "41" }, paulo@9: { "green", "32", "42" }, paulo@9: { "brown", "33", "43" }, paulo@9: { "blue", "34", "44" }, paulo@9: { "magenta", "35", "45" }, paulo@9: { "cyan", "36", "46" }, paulo@9: { "white", "37", "47" }, paulo@9: { "default", "39", "49" }, paulo@9: { NULL }, paulo@9: }; paulo@9: paulo@9: char *getFGColor(char *cname) paulo@9: { paulo@9: Colors *c; paulo@22: for (c = (Colors *)COLORS; c->name; c++) paulo@9: { paulo@9: if (strncmp(cname, c->name, strlen(c->name)) == 0) paulo@9: return c->fg; paulo@9: } paulo@9: return NULL; paulo@9: } paulo@9: paulo@9: char *getBGColor(char *cname) paulo@9: { paulo@9: Colors *c; paulo@22: for (c = (Colors *)COLORS; c->name; c++) paulo@9: { paulo@9: if (strncmp(cname, c->name, strlen(c->name)) == 0) paulo@9: return c->bg; paulo@9: } paulo@9: return NULL; paulo@9: } paulo@9: paulo@8: int colorLine = 0; paulo@9: int embolden = 0; paulo@12: char *g_fg = ""; paulo@9: char *g_bg = ""; paulo@8: paulo@4: typedef enum _exit_code { paulo@4: EXIT_OK, paulo@4: EXIT_REALLOC_ERROR, paulo@4: EXIT_ARGS_ERROR, paulo@4: } Exit_code; paulo@4: paulo@3: int re_error(int errcode, const regex_t *re) paulo@3: { paulo@22: char *err_string = calloc(ERRBUFSIZE, sizeof(char)); paulo@22: regerror(errcode, re, err_string, ERRBUFSIZE*sizeof(char)); paulo@3: fprintf(stderr, "%s \n", err_string); paulo@3: free(err_string); paulo@3: return errcode; paulo@3: } paulo@3: paulo@4: Exit_code realloc_error() paulo@3: { paulo@3: fprintf(stderr, "realloc() failure \n"); paulo@4: return EXIT_REALLOC_ERROR; paulo@4: } paulo@4: paulo@21: void *realloc_buffer(void *buffer, size_t buffer_len) paulo@21: { paulo@21: void *new_buffer = realloc(buffer, buffer_len); paulo@21: if (!new_buffer) paulo@21: exit(realloc_error()); paulo@21: return new_buffer; paulo@21: } paulo@21: paulo@4: Exit_code args_error() paulo@4: { paulo@16: char *usage = "\ paulo@16: Usage: rcg [options] \n\ paulo@16: \n\ paulo@16: Options: \n\ paulo@22: -z, --bufsize Buffer size \n\ paulo@22: -l, --line Highlight whole line \n\ paulo@22: -B, --bold Bold \n\ paulo@22: -b Background color \n\ paulo@22: -f Foreground color \n\ paulo@16: \n\ paulo@16: can be one of the following: \n\ paulo@16: "; paulo@16: paulo@16: fprintf(stdout, usage); paulo@16: paulo@16: Colors *c; paulo@22: for (c = (Colors *)COLORS; c->name; c++) paulo@16: fprintf(stdout, " %s \n", c->name); paulo@16: paulo@4: return EXIT_ARGS_ERROR; paulo@4: } paulo@4: paulo@22: Exit_code bufsize_args_error() paulo@22: { paulo@22: fprintf(stdout, "bufsize has to be a minimum of %d \n", (int)BUFSIZE_MIN); paulo@22: return EXIT_ARGS_ERROR; paulo@22: } paulo@22: paulo@4: // returns regular expression argument paulo@4: char *parseArgs(int argc, char **argv) paulo@4: { paulo@4: static struct option long_options[] = paulo@4: { paulo@22: { "line", 0, NULL, 'l' }, paulo@22: { "bold", 0, NULL, 'B' }, paulo@22: { "bufsize", required_argument, NULL, 'z' }, paulo@22: { NULL, 0, NULL, 0 }, paulo@4: }; paulo@4: paulo@4: int c; paulo@22: while ((c = getopt_long(argc, argv, "lBz:f:b:", long_options, NULL)) >= 0) paulo@4: { paulo@8: if (c == 'l') paulo@8: colorLine = 1; paulo@9: else if (c == 'B') paulo@9: embolden = 1; paulo@22: else if (c == 'z') paulo@22: bufsize = (size_t)atoi(optarg); paulo@9: else if (c == 'f') paulo@9: g_fg = strdup(optarg); paulo@9: else if (c == 'b') paulo@9: g_bg = strdup(optarg); paulo@4: } paulo@4: paulo@4: if (optind >= argc) paulo@4: exit(args_error()); paulo@4: paulo@22: if (bufsize < BUFSIZE_MIN) paulo@22: exit(bufsize_args_error()); paulo@22: paulo@4: return argv[optind]; paulo@3: } paulo@3: pang@1: int main(int argc, char **argv) pang@1: { paulo@22: bufsize = BUFSIZE_MIN; paulo@22: paulo@4: char *re_expression = parseArgs(argc, argv); paulo@22: char *buf = calloc(bufsize, sizeof(char)); pang@1: paulo@3: int re_err; pang@1: regex_t *re = calloc(1, sizeof(regex_t)); paulo@13: re_err = regcomp(re, re_expression, REG_EXTENDED | REG_NEWLINE); paulo@3: if (re_err != 0) paulo@3: exit(re_error(re_err, re)); pang@1: pang@2: regmatch_t *rem = calloc(1, sizeof(regmatch_t)); pang@1: paulo@9: if (embolden) paulo@9: { paulo@9: CLR_START = calloc(strlen(_CLR_START) + strlen(_CLR_BOLD) + 1, sizeof(char)); paulo@9: strcpy(CLR_START, _CLR_START); paulo@9: strcat(CLR_START, _CLR_BOLD); paulo@9: } paulo@9: else paulo@9: CLR_START = strdup(_CLR_START); paulo@9: paulo@12: if (strlen(g_fg) == 0 && strlen(g_bg) == 0) paulo@12: g_fg = "red"; paulo@12: else if (strlen(g_fg) == 0 && strlen(g_bg) > 0) paulo@12: g_fg = "default"; paulo@12: paulo@12: char *fgcolor = getFGColor(g_fg); paulo@12: if (!fgcolor) paulo@9: exit(args_error()); paulo@12: paulo@12: char *clr = strdup(fgcolor); paulo@9: if (strlen(g_bg) > 0) paulo@9: { paulo@9: char *bgcolor = getBGColor(g_bg); paulo@9: if (bgcolor) paulo@9: { paulo@12: size_t l = strlen(clr) + 1 + strlen(bgcolor); paulo@21: clr = realloc_buffer(clr, (l + 1)*sizeof(char)); paulo@12: strcat(clr, ";"); paulo@12: strcat(clr, bgcolor); paulo@9: } paulo@9: else paulo@9: exit(args_error()); paulo@9: } paulo@9: paulo@7: char *out = NULL; paulo@7: paulo@22: while (fgets(buf, bufsize, stdin)) pang@1: { pang@2: int so = -1; pang@2: int eo = -1; pang@1: paulo@6: size_t out_len = strlen(buf) + 1; paulo@3: unsigned int out_pos = 0; paulo@3: unsigned int buf_pos = 0; paulo@3: paulo@4: for (re_err = 0; re_err != REG_NOMATCH;) pang@2: { paulo@3: re_err = regexec(re, &buf[buf_pos], 1, rem, 0); pang@1: paulo@13: so = rem[0].rm_so; paulo@13: eo = rem[0].rm_eo; paulo@13: paulo@13: int match_len = eo - so; paulo@13: paulo@13: if (colorLine && match_len > 0) paulo@8: { paulo@8: so = 0; paulo@21: eo = out_len - 1; paulo@13: match_len = eo - so; paulo@23: if (buf[eo - 1] == '\n') paulo@23: match_len -= 1; paulo@8: } pang@1: paulo@7: if (re_err != REG_NOMATCH && so >= 0 && eo >= 0 && match_len > 0) pang@1: { paulo@14: size_t CLR_STRING_len = strlen(CLR_START) + strlen(clr) + strlen(CLR_END) + strlen(CLR_CLEAR); paulo@14: paulo@14: out_len += CLR_STRING_len; paulo@21: out = realloc_buffer(out, out_len*sizeof(char)); pang@2: paulo@15: snprintf(&out[out_pos], out_len, "%.*s%s%s%s%.*s%s", so, &buf[buf_pos], CLR_START, clr, CLR_END, match_len, &buf[buf_pos + so], CLR_CLEAR); paulo@14: paulo@14: buf_pos += match_len + so; paulo@14: out_pos += CLR_STRING_len + match_len + so; pang@1: } pang@2: else paulo@5: { paulo@21: out = realloc_buffer(out, out_len*sizeof(char)); paulo@3: strncpy(&out[out_pos], &buf[buf_pos], out_len - out_pos); paulo@7: out[out_len - 1] = '\0'; paulo@13: re_err = REG_NOMATCH; paulo@5: } pang@1: } pang@1: pang@1: fputs(out, stdout); paulo@10: fflush(stdout); pang@1: } pang@1: paulo@4: return EXIT_OK; pang@1: }