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
|
pang@1
|
29 const size_t BUFSIZE = 5000;
|
pang@1
|
30
|
paulo@9
|
31 const char *_CLR_START = "\x1B[";
|
paulo@9
|
32 const char *_CLR_BOLD = "1;";
|
paulo@9
|
33 char *CLR_START;
|
paulo@5
|
34 const char *CLR_END = "m";
|
paulo@5
|
35 const char *CLR_CLEAR = "\x1B[0m";
|
pang@1
|
36
|
paulo@9
|
37 typedef struct _colors {
|
paulo@9
|
38 char *name;
|
paulo@9
|
39 char *fg;
|
paulo@9
|
40 char *bg;
|
paulo@9
|
41 } Colors;
|
paulo@9
|
42
|
paulo@9
|
43 const Colors COLORS[] = {
|
paulo@9
|
44 { "black", "30", "40" },
|
paulo@9
|
45 { "red", "31", "41" },
|
paulo@9
|
46 { "green", "32", "42" },
|
paulo@9
|
47 { "brown", "33", "43" },
|
paulo@9
|
48 { "blue", "34", "44" },
|
paulo@9
|
49 { "magenta", "35", "45" },
|
paulo@9
|
50 { "cyan", "36", "46" },
|
paulo@9
|
51 { "white", "37", "47" },
|
paulo@9
|
52 { "default", "39", "49" },
|
paulo@9
|
53 { NULL },
|
paulo@9
|
54 };
|
paulo@9
|
55
|
paulo@9
|
56 char *getFGColor(char *cname)
|
paulo@9
|
57 {
|
paulo@9
|
58 Colors *c;
|
paulo@9
|
59 for (c = COLORS; c->name; c++)
|
paulo@9
|
60 {
|
paulo@9
|
61 if (strncmp(cname, c->name, strlen(c->name)) == 0)
|
paulo@9
|
62 return c->fg;
|
paulo@9
|
63 }
|
paulo@9
|
64 return NULL;
|
paulo@9
|
65 }
|
paulo@9
|
66
|
paulo@9
|
67 char *getBGColor(char *cname)
|
paulo@9
|
68 {
|
paulo@9
|
69 Colors *c;
|
paulo@9
|
70 for (c = COLORS; c->name; c++)
|
paulo@9
|
71 {
|
paulo@9
|
72 if (strncmp(cname, c->name, strlen(c->name)) == 0)
|
paulo@9
|
73 return c->bg;
|
paulo@9
|
74 }
|
paulo@9
|
75 return NULL;
|
paulo@9
|
76 }
|
paulo@9
|
77
|
paulo@8
|
78 int colorLine = 0;
|
paulo@9
|
79 int embolden = 0;
|
paulo@12
|
80 char *g_fg = "";
|
paulo@9
|
81 char *g_bg = "";
|
paulo@8
|
82
|
paulo@4
|
83 typedef enum _exit_code {
|
paulo@4
|
84 EXIT_OK,
|
paulo@4
|
85 EXIT_REALLOC_ERROR,
|
paulo@4
|
86 EXIT_ARGS_ERROR,
|
paulo@4
|
87 } Exit_code;
|
paulo@4
|
88
|
paulo@3
|
89 int re_error(int errcode, const regex_t *re)
|
paulo@3
|
90 {
|
paulo@3
|
91 char *err_string = calloc(BUFSIZE, sizeof(char));
|
paulo@3
|
92 regerror(errcode, re, err_string, BUFSIZE*sizeof(char));
|
paulo@3
|
93 fprintf(stderr, "%s \n", err_string);
|
paulo@3
|
94 free(err_string);
|
paulo@3
|
95 return errcode;
|
paulo@3
|
96 }
|
paulo@3
|
97
|
paulo@4
|
98 Exit_code realloc_error()
|
paulo@3
|
99 {
|
paulo@3
|
100 fprintf(stderr, "realloc() failure \n");
|
paulo@4
|
101 return EXIT_REALLOC_ERROR;
|
paulo@4
|
102 }
|
paulo@4
|
103
|
paulo@21
|
104 void *realloc_buffer(void *buffer, size_t buffer_len)
|
paulo@21
|
105 {
|
paulo@21
|
106 void *new_buffer = realloc(buffer, buffer_len);
|
paulo@21
|
107 if (!new_buffer)
|
paulo@21
|
108 exit(realloc_error());
|
paulo@21
|
109 return new_buffer;
|
paulo@21
|
110 }
|
paulo@21
|
111
|
paulo@4
|
112 Exit_code args_error()
|
paulo@4
|
113 {
|
paulo@16
|
114 char *usage = "\
|
paulo@16
|
115 Usage: rcg [options] <PATTERN> \n\
|
paulo@16
|
116 \n\
|
paulo@16
|
117 Options: \n\
|
paulo@16
|
118 -l, --line Highlight whole line \n\
|
paulo@16
|
119 -B, --bold Bold \n\
|
paulo@16
|
120 -b <color> Background color \n\
|
paulo@16
|
121 -f <color> Foreground color \n\
|
paulo@16
|
122 \n\
|
paulo@16
|
123 <color> can be one of the following: \n\
|
paulo@16
|
124 ";
|
paulo@16
|
125
|
paulo@16
|
126 fprintf(stdout, usage);
|
paulo@16
|
127
|
paulo@16
|
128 Colors *c;
|
paulo@16
|
129 for (c = COLORS; c->name; c++)
|
paulo@16
|
130 fprintf(stdout, " %s \n", c->name);
|
paulo@16
|
131
|
paulo@4
|
132 return EXIT_ARGS_ERROR;
|
paulo@4
|
133 }
|
paulo@4
|
134
|
paulo@4
|
135 // returns regular expression argument
|
paulo@4
|
136 char *parseArgs(int argc, char **argv)
|
paulo@4
|
137 {
|
paulo@4
|
138 static struct option long_options[] =
|
paulo@4
|
139 {
|
paulo@8
|
140 { "line", 0, 0, 'l' },
|
paulo@9
|
141 { "bold", 0, 0, 'B' },
|
paulo@4
|
142 { 0, 0, 0, 0 },
|
paulo@4
|
143 };
|
paulo@4
|
144
|
paulo@4
|
145 int c;
|
paulo@4
|
146 int l;
|
paulo@9
|
147 while ((c = getopt_long(argc, argv, "lBf:b:", long_options, &l)) >= 0)
|
paulo@4
|
148 {
|
paulo@8
|
149 if (c == 'l')
|
paulo@8
|
150 colorLine = 1;
|
paulo@9
|
151 else if (c == 'B')
|
paulo@9
|
152 embolden = 1;
|
paulo@9
|
153 else if (c == 'f')
|
paulo@9
|
154 g_fg = strdup(optarg);
|
paulo@9
|
155 else if (c == 'b')
|
paulo@9
|
156 g_bg = strdup(optarg);
|
paulo@4
|
157 }
|
paulo@4
|
158
|
paulo@4
|
159 if (optind >= argc)
|
paulo@4
|
160 exit(args_error());
|
paulo@4
|
161
|
paulo@4
|
162 return argv[optind];
|
paulo@3
|
163 }
|
paulo@3
|
164
|
pang@1
|
165 int main(int argc, char **argv)
|
pang@1
|
166 {
|
paulo@4
|
167 char *re_expression = parseArgs(argc, argv);
|
paulo@4
|
168
|
paulo@5
|
169 //fprintf(stderr, "re_expression = %s \n", re_expression); //d/ 20100405 PBA
|
paulo@4
|
170
|
pang@1
|
171 char *buf = calloc(BUFSIZE, sizeof(char));
|
pang@1
|
172
|
paulo@3
|
173 int re_err;
|
pang@1
|
174 regex_t *re = calloc(1, sizeof(regex_t));
|
paulo@13
|
175 re_err = regcomp(re, re_expression, REG_EXTENDED | REG_NEWLINE);
|
paulo@3
|
176 if (re_err != 0)
|
paulo@3
|
177 exit(re_error(re_err, re));
|
pang@1
|
178
|
pang@2
|
179 regmatch_t *rem = calloc(1, sizeof(regmatch_t));
|
pang@1
|
180
|
paulo@9
|
181 if (embolden)
|
paulo@9
|
182 {
|
paulo@9
|
183 CLR_START = calloc(strlen(_CLR_START) + strlen(_CLR_BOLD) + 1, sizeof(char));
|
paulo@9
|
184 strcpy(CLR_START, _CLR_START);
|
paulo@9
|
185 strcat(CLR_START, _CLR_BOLD);
|
paulo@9
|
186 }
|
paulo@9
|
187 else
|
paulo@9
|
188 CLR_START = strdup(_CLR_START);
|
paulo@9
|
189
|
paulo@12
|
190 if (strlen(g_fg) == 0 && strlen(g_bg) == 0)
|
paulo@12
|
191 g_fg = "red";
|
paulo@12
|
192 else if (strlen(g_fg) == 0 && strlen(g_bg) > 0)
|
paulo@12
|
193 g_fg = "default";
|
paulo@12
|
194
|
paulo@12
|
195 char *fgcolor = getFGColor(g_fg);
|
paulo@12
|
196 if (!fgcolor)
|
paulo@9
|
197 exit(args_error());
|
paulo@12
|
198
|
paulo@12
|
199 char *clr = strdup(fgcolor);
|
paulo@9
|
200 if (strlen(g_bg) > 0)
|
paulo@9
|
201 {
|
paulo@9
|
202 char *bgcolor = getBGColor(g_bg);
|
paulo@9
|
203 if (bgcolor)
|
paulo@9
|
204 {
|
paulo@12
|
205 size_t l = strlen(clr) + 1 + strlen(bgcolor);
|
paulo@21
|
206 clr = realloc_buffer(clr, (l + 1)*sizeof(char));
|
paulo@12
|
207 strcat(clr, ";");
|
paulo@12
|
208 strcat(clr, bgcolor);
|
paulo@9
|
209 }
|
paulo@9
|
210 else
|
paulo@9
|
211 exit(args_error());
|
paulo@9
|
212 }
|
paulo@9
|
213
|
paulo@7
|
214 char *out = NULL;
|
paulo@7
|
215
|
pang@1
|
216 while (fgets(buf, BUFSIZE, stdin))
|
pang@1
|
217 {
|
pang@2
|
218 int so = -1;
|
pang@2
|
219 int eo = -1;
|
pang@1
|
220
|
paulo@6
|
221 size_t out_len = strlen(buf) + 1;
|
paulo@3
|
222 unsigned int out_pos = 0;
|
paulo@3
|
223 unsigned int buf_pos = 0;
|
paulo@3
|
224
|
paulo@4
|
225 for (re_err = 0; re_err != REG_NOMATCH;)
|
pang@2
|
226 {
|
paulo@3
|
227 re_err = regexec(re, &buf[buf_pos], 1, rem, 0);
|
pang@1
|
228
|
paulo@13
|
229 so = rem[0].rm_so;
|
paulo@13
|
230 eo = rem[0].rm_eo;
|
paulo@13
|
231
|
paulo@13
|
232 int match_len = eo - so;
|
paulo@13
|
233
|
paulo@13
|
234 if (colorLine && match_len > 0)
|
paulo@8
|
235 {
|
paulo@8
|
236 so = 0;
|
paulo@21
|
237 eo = out_len - 1;
|
paulo@13
|
238 match_len = eo - so;
|
paulo@8
|
239 }
|
pang@1
|
240
|
paulo@7
|
241 if (re_err != REG_NOMATCH && so >= 0 && eo >= 0 && match_len > 0)
|
pang@1
|
242 {
|
paulo@14
|
243 size_t CLR_STRING_len = strlen(CLR_START) + strlen(clr) + strlen(CLR_END) + strlen(CLR_CLEAR);
|
paulo@14
|
244
|
paulo@14
|
245 out_len += CLR_STRING_len;
|
paulo@21
|
246 out = realloc_buffer(out, out_len*sizeof(char));
|
pang@2
|
247
|
paulo@15
|
248 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
|
249
|
paulo@14
|
250 buf_pos += match_len + so;
|
paulo@14
|
251 out_pos += CLR_STRING_len + match_len + so;
|
pang@1
|
252 }
|
pang@2
|
253 else
|
paulo@5
|
254 {
|
paulo@21
|
255 out = realloc_buffer(out, out_len*sizeof(char));
|
paulo@3
|
256 strncpy(&out[out_pos], &buf[buf_pos], out_len - out_pos);
|
paulo@7
|
257 out[out_len - 1] = '\0';
|
paulo@13
|
258 re_err = REG_NOMATCH;
|
paulo@5
|
259 }
|
pang@1
|
260 }
|
pang@1
|
261
|
pang@1
|
262 fputs(out, stdout);
|
paulo@10
|
263 fflush(stdout);
|
pang@1
|
264 }
|
pang@1
|
265
|
paulo@4
|
266 return EXIT_OK;
|
pang@1
|
267 }
|