annotate rcg.c @ 23:ce077dec4459

fix dealing with newlines
author paulo
date Tue, 05 Aug 2014 02:05:34 -0700
parents b18d74b37e9e
children
rev   line source
paulo@17 1 // rcg.c -- regex colored grep
paulo@17 2 // Copyright (C) 2010 by Paulo Ang (pbba13@gmail.com)
paulo@17 3 //
paulo@17 4 // This program is free software: you can redistribute it and/or modify
paulo@17 5 // it under the terms of the GNU General Public License as published by
paulo@17 6 // the Free Software Foundation, either version 3 of the License, or
paulo@17 7 // (at your option) any later version.
paulo@17 8 //
paulo@17 9 // This program is distributed in the hope that it will be useful,
paulo@17 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
paulo@17 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
paulo@17 12 // GNU General Public License for more details.
paulo@17 13 //
paulo@17 14 // You should have received a copy of the GNU General Public License
paulo@17 15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
paulo@17 16
paulo@17 17
pang@1 18 #include <stdio.h>
pang@1 19 #include <stdlib.h>
pang@1 20 #include <regex.h>
pang@1 21
pang@1 22 #include <string.h>
pang@1 23
paulo@4 24 #include <unistd.h>
paulo@4 25
paulo@4 26 #define _GNU_SOURCE
paulo@4 27 #include <getopt.h>
paulo@4 28
paulo@22 29 size_t bufsize;
paulo@22 30 const size_t BUFSIZE_MIN = 1024;
paulo@22 31 const size_t ERRBUFSIZE = 512;
pang@1 32
paulo@9 33 const char *_CLR_START = "\x1B[";
paulo@9 34 const char *_CLR_BOLD = "1;";
paulo@9 35 char *CLR_START;
paulo@5 36 const char *CLR_END = "m";
paulo@5 37 const char *CLR_CLEAR = "\x1B[0m";
pang@1 38
paulo@9 39 typedef struct _colors {
paulo@9 40 char *name;
paulo@9 41 char *fg;
paulo@9 42 char *bg;
paulo@9 43 } Colors;
paulo@9 44
paulo@9 45 const Colors COLORS[] = {
paulo@9 46 { "black", "30", "40" },
paulo@9 47 { "red", "31", "41" },
paulo@9 48 { "green", "32", "42" },
paulo@9 49 { "brown", "33", "43" },
paulo@9 50 { "blue", "34", "44" },
paulo@9 51 { "magenta", "35", "45" },
paulo@9 52 { "cyan", "36", "46" },
paulo@9 53 { "white", "37", "47" },
paulo@9 54 { "default", "39", "49" },
paulo@9 55 { NULL },
paulo@9 56 };
paulo@9 57
paulo@9 58 char *getFGColor(char *cname)
paulo@9 59 {
paulo@9 60 Colors *c;
paulo@22 61 for (c = (Colors *)COLORS; c->name; c++)
paulo@9 62 {
paulo@9 63 if (strncmp(cname, c->name, strlen(c->name)) == 0)
paulo@9 64 return c->fg;
paulo@9 65 }
paulo@9 66 return NULL;
paulo@9 67 }
paulo@9 68
paulo@9 69 char *getBGColor(char *cname)
paulo@9 70 {
paulo@9 71 Colors *c;
paulo@22 72 for (c = (Colors *)COLORS; c->name; c++)
paulo@9 73 {
paulo@9 74 if (strncmp(cname, c->name, strlen(c->name)) == 0)
paulo@9 75 return c->bg;
paulo@9 76 }
paulo@9 77 return NULL;
paulo@9 78 }
paulo@9 79
paulo@8 80 int colorLine = 0;
paulo@9 81 int embolden = 0;
paulo@12 82 char *g_fg = "";
paulo@9 83 char *g_bg = "";
paulo@8 84
paulo@4 85 typedef enum _exit_code {
paulo@4 86 EXIT_OK,
paulo@4 87 EXIT_REALLOC_ERROR,
paulo@4 88 EXIT_ARGS_ERROR,
paulo@4 89 } Exit_code;
paulo@4 90
paulo@3 91 int re_error(int errcode, const regex_t *re)
paulo@3 92 {
paulo@22 93 char *err_string = calloc(ERRBUFSIZE, sizeof(char));
paulo@22 94 regerror(errcode, re, err_string, ERRBUFSIZE*sizeof(char));
paulo@3 95 fprintf(stderr, "%s \n", err_string);
paulo@3 96 free(err_string);
paulo@3 97 return errcode;
paulo@3 98 }
paulo@3 99
paulo@4 100 Exit_code realloc_error()
paulo@3 101 {
paulo@3 102 fprintf(stderr, "realloc() failure \n");
paulo@4 103 return EXIT_REALLOC_ERROR;
paulo@4 104 }
paulo@4 105
paulo@21 106 void *realloc_buffer(void *buffer, size_t buffer_len)
paulo@21 107 {
paulo@21 108 void *new_buffer = realloc(buffer, buffer_len);
paulo@21 109 if (!new_buffer)
paulo@21 110 exit(realloc_error());
paulo@21 111 return new_buffer;
paulo@21 112 }
paulo@21 113
paulo@4 114 Exit_code args_error()
paulo@4 115 {
paulo@16 116 char *usage = "\
paulo@16 117 Usage: rcg [options] <PATTERN> \n\
paulo@16 118 \n\
paulo@16 119 Options: \n\
paulo@22 120 -z, --bufsize <size> Buffer size \n\
paulo@22 121 -l, --line Highlight whole line \n\
paulo@22 122 -B, --bold Bold \n\
paulo@22 123 -b <color> Background color \n\
paulo@22 124 -f <color> Foreground color \n\
paulo@16 125 \n\
paulo@16 126 <color> can be one of the following: \n\
paulo@16 127 ";
paulo@16 128
paulo@16 129 fprintf(stdout, usage);
paulo@16 130
paulo@16 131 Colors *c;
paulo@22 132 for (c = (Colors *)COLORS; c->name; c++)
paulo@16 133 fprintf(stdout, " %s \n", c->name);
paulo@16 134
paulo@4 135 return EXIT_ARGS_ERROR;
paulo@4 136 }
paulo@4 137
paulo@22 138 Exit_code bufsize_args_error()
paulo@22 139 {
paulo@22 140 fprintf(stdout, "bufsize has to be a minimum of %d \n", (int)BUFSIZE_MIN);
paulo@22 141 return EXIT_ARGS_ERROR;
paulo@22 142 }
paulo@22 143
paulo@4 144 // returns regular expression argument
paulo@4 145 char *parseArgs(int argc, char **argv)
paulo@4 146 {
paulo@4 147 static struct option long_options[] =
paulo@4 148 {
paulo@22 149 { "line", 0, NULL, 'l' },
paulo@22 150 { "bold", 0, NULL, 'B' },
paulo@22 151 { "bufsize", required_argument, NULL, 'z' },
paulo@22 152 { NULL, 0, NULL, 0 },
paulo@4 153 };
paulo@4 154
paulo@4 155 int c;
paulo@22 156 while ((c = getopt_long(argc, argv, "lBz:f:b:", long_options, NULL)) >= 0)
paulo@4 157 {
paulo@8 158 if (c == 'l')
paulo@8 159 colorLine = 1;
paulo@9 160 else if (c == 'B')
paulo@9 161 embolden = 1;
paulo@22 162 else if (c == 'z')
paulo@22 163 bufsize = (size_t)atoi(optarg);
paulo@9 164 else if (c == 'f')
paulo@9 165 g_fg = strdup(optarg);
paulo@9 166 else if (c == 'b')
paulo@9 167 g_bg = strdup(optarg);
paulo@4 168 }
paulo@4 169
paulo@4 170 if (optind >= argc)
paulo@4 171 exit(args_error());
paulo@4 172
paulo@22 173 if (bufsize < BUFSIZE_MIN)
paulo@22 174 exit(bufsize_args_error());
paulo@22 175
paulo@4 176 return argv[optind];
paulo@3 177 }
paulo@3 178
pang@1 179 int main(int argc, char **argv)
pang@1 180 {
paulo@22 181 bufsize = BUFSIZE_MIN;
paulo@22 182
paulo@4 183 char *re_expression = parseArgs(argc, argv);
paulo@22 184 char *buf = calloc(bufsize, sizeof(char));
pang@1 185
paulo@3 186 int re_err;
pang@1 187 regex_t *re = calloc(1, sizeof(regex_t));
paulo@13 188 re_err = regcomp(re, re_expression, REG_EXTENDED | REG_NEWLINE);
paulo@3 189 if (re_err != 0)
paulo@3 190 exit(re_error(re_err, re));
pang@1 191
pang@2 192 regmatch_t *rem = calloc(1, sizeof(regmatch_t));
pang@1 193
paulo@9 194 if (embolden)
paulo@9 195 {
paulo@9 196 CLR_START = calloc(strlen(_CLR_START) + strlen(_CLR_BOLD) + 1, sizeof(char));
paulo@9 197 strcpy(CLR_START, _CLR_START);
paulo@9 198 strcat(CLR_START, _CLR_BOLD);
paulo@9 199 }
paulo@9 200 else
paulo@9 201 CLR_START = strdup(_CLR_START);
paulo@9 202
paulo@12 203 if (strlen(g_fg) == 0 && strlen(g_bg) == 0)
paulo@12 204 g_fg = "red";
paulo@12 205 else if (strlen(g_fg) == 0 && strlen(g_bg) > 0)
paulo@12 206 g_fg = "default";
paulo@12 207
paulo@12 208 char *fgcolor = getFGColor(g_fg);
paulo@12 209 if (!fgcolor)
paulo@9 210 exit(args_error());
paulo@12 211
paulo@12 212 char *clr = strdup(fgcolor);
paulo@9 213 if (strlen(g_bg) > 0)
paulo@9 214 {
paulo@9 215 char *bgcolor = getBGColor(g_bg);
paulo@9 216 if (bgcolor)
paulo@9 217 {
paulo@12 218 size_t l = strlen(clr) + 1 + strlen(bgcolor);
paulo@21 219 clr = realloc_buffer(clr, (l + 1)*sizeof(char));
paulo@12 220 strcat(clr, ";");
paulo@12 221 strcat(clr, bgcolor);
paulo@9 222 }
paulo@9 223 else
paulo@9 224 exit(args_error());
paulo@9 225 }
paulo@9 226
paulo@7 227 char *out = NULL;
paulo@7 228
paulo@22 229 while (fgets(buf, bufsize, stdin))
pang@1 230 {
pang@2 231 int so = -1;
pang@2 232 int eo = -1;
pang@1 233
paulo@6 234 size_t out_len = strlen(buf) + 1;
paulo@3 235 unsigned int out_pos = 0;
paulo@3 236 unsigned int buf_pos = 0;
paulo@3 237
paulo@4 238 for (re_err = 0; re_err != REG_NOMATCH;)
pang@2 239 {
paulo@3 240 re_err = regexec(re, &buf[buf_pos], 1, rem, 0);
pang@1 241
paulo@13 242 so = rem[0].rm_so;
paulo@13 243 eo = rem[0].rm_eo;
paulo@13 244
paulo@13 245 int match_len = eo - so;
paulo@13 246
paulo@13 247 if (colorLine && match_len > 0)
paulo@8 248 {
paulo@8 249 so = 0;
paulo@21 250 eo = out_len - 1;
paulo@13 251 match_len = eo - so;
paulo@23 252 if (buf[eo - 1] == '\n')
paulo@23 253 match_len -= 1;
paulo@8 254 }
pang@1 255
paulo@7 256 if (re_err != REG_NOMATCH && so >= 0 && eo >= 0 && match_len > 0)
pang@1 257 {
paulo@14 258 size_t CLR_STRING_len = strlen(CLR_START) + strlen(clr) + strlen(CLR_END) + strlen(CLR_CLEAR);
paulo@14 259
paulo@14 260 out_len += CLR_STRING_len;
paulo@21 261 out = realloc_buffer(out, out_len*sizeof(char));
pang@2 262
paulo@15 263 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 264
paulo@14 265 buf_pos += match_len + so;
paulo@14 266 out_pos += CLR_STRING_len + match_len + so;
pang@1 267 }
pang@2 268 else
paulo@5 269 {
paulo@21 270 out = realloc_buffer(out, out_len*sizeof(char));
paulo@3 271 strncpy(&out[out_pos], &buf[buf_pos], out_len - out_pos);
paulo@7 272 out[out_len - 1] = '\0';
paulo@13 273 re_err = REG_NOMATCH;
paulo@5 274 }
pang@1 275 }
pang@1 276
pang@1 277 fputs(out, stdout);
paulo@10 278 fflush(stdout);
pang@1 279 }
pang@1 280
paulo@4 281 return EXIT_OK;
pang@1 282 }