1 /*
2     colortail -- output last part of file(s) in color.
3     Copyright (C) 2009  Joakim Andersson <ja@joakimandersson.se>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 #include "CfgFileParser.h"
21 
22 #include <iostream>
23 #include <assert.h>
24 #include <cstring>
25 #include <stdlib.h>
26 
27 using namespace std;
28 
29 // ## class SearchData ##
30 
SearchData()31 SearchData::SearchData()
32 {
33    // default constructor
34    m_preg = NULL;
35    m_ansi_color_code = NULL;
36    m_pf = NULL;
37    m_param_to_callback_fkn = NULL;
38 }
39 
~SearchData()40 SearchData::~SearchData()
41 {
42    if (m_preg)
43    {
44       // is allocated with malloc
45       free(m_preg);
46    }
47 
48    if (m_ansi_color_code)
49    {
50       delete m_ansi_color_code;
51    }
52 
53    if (m_param_to_callback_fkn)
54    {
55       delete m_param_to_callback_fkn;
56    }
57 }
58 
set_color(char * color)59 void SearchData::set_color(char *color)
60 {
61    // allocates memory and sets the m_ansi_color_code string to color
62 
63    if (m_ansi_color_code)
64    {
65       delete m_ansi_color_code;
66    }
67 
68    m_ansi_color_code = new char[strlen(color)+1];
69    strcpy(m_ansi_color_code, color);
70 }
71 
CfgFileParser()72 CfgFileParser::CfgFileParser()
73 {
74    m_items_list = NULL;
75    m_filename = NULL;
76    m_line = 0;
77 }
78 
~CfgFileParser()79 CfgFileParser::~CfgFileParser()
80 {
81    free_items();
82    if (m_filename)
83    {
84       delete m_filename;
85    }
86 }
87 
free_items()88 void CfgFileParser::free_items()
89 {
90    // frees the memory that the items uses
91 
92    // check if there is a list
93    if (m_items_list)
94    {
95       SearchData *tmp;
96 
97       // go through all the elements in the list,
98       // and free each SearchData instance
99       while (!m_items_list->is_empty())
100       {
101 	 tmp = m_items_list->first_element();
102 	 m_items_list->remove_first();
103 	 delete tmp;
104       }
105 
106       // delete the list
107       delete m_items_list;
108       m_items_list = NULL;
109    }
110 }
111 
read_line()112 char* CfgFileParser::read_line()
113 {
114    // reads the next line from the file and increases the line counter
115    // prints error message if no characters were read.
116    // returns: string or NULL if error.
117 
118    char *str = new char[MAX_CFG_LINE_LENGTH];
119    assert (str != NULL);
120 
121    // read next line
122    m_infile.getline(str, MAX_CFG_LINE_LENGTH);
123 
124    // increase line counter
125    m_line++;
126 
127    // read less than zero chars?
128    if (m_infile.gcount() <= 0)
129    {
130       // found EOF
131       delete str;
132       return NULL;
133    }
134 
135    return str;
136 }
137 
read_item()138 int CfgFileParser::read_item()
139 {
140    // reads the color for the reg exps, then eads the reg exps and makes
141    // SearchData instances for them and adds to the m_items_list
142    // only reads and processes ONE item
143    // returns number of SearchData instances added to the list
144 
145    int nr_items_added = 0;
146 
147    // is there a list?
148    assert (m_items_list != NULL);
149 
150    // is there a open file?
151    assert (m_infile);
152 
153    // read color
154    char *color = read_color();
155 
156    if (color == NULL)
157    {
158       // didn't get a color
159 
160       // quit, return 0
161       return 0;
162    }
163 
164    // TODO: add action reading
165    // read action
166 
167    if (read_left() == 0)
168    {
169       // found '{'
170 
171       // loop until '}'
172       while (1)
173       {
174 	 // read string
175 	 char *regexp = read_regexp();
176 	 if (regexp)
177 	 {
178 	    // check if it's a '}'
179 	    if (regexp[0] == '}')
180 	    {
181 	       // free mem
182 	       delete regexp;
183 	       // stop looping
184 	       break;
185 	    }
186 
187 	    // has a regexp.. make a SearchData item
188 
189 	    SearchData *searchdata = new SearchData();
190 	    // check allocation
191 	    assert (searchdata != NULL);
192 
193 	    // set color
194 	    searchdata->set_color(color);
195 
196 	    // allocate memory to the pattern storage buffer
197 	    searchdata->m_preg = (regex_t*) malloc(sizeof(regex_t));
198 	    // check allocation
199 	    assert (searchdata->m_preg != NULL);
200 
201 	    // make compiled pattern buffer
202 	    if (regcomp(searchdata->m_preg, regexp, REG_EXTENDED) != 0)
203 	    {
204 	       // failed to make compiled reg exp pattern
205 	       cout << "colortail: Failed to make compiled reg exp pattern for "
206 		    << "reg exp in config file " << m_filename << " at line "
207 		    << m_line << ". Skipping this line." << endl;
208 
209 	       // free mem
210 	       delete searchdata;
211 	    }
212 	    else
213 	    {
214 	       // TODO: set callback fkn
215 	       // TODO: set param to callback fkn
216 
217 	       // add the search data item to the items list
218 	       m_items_list->add(searchdata);
219 	       // increase items added counter
220 	       nr_items_added++;
221 	    }
222 	    // free mem
223 	    delete regexp;
224 	 }
225 	 else
226 	 {
227 	    // error reading string, eof maybe..
228 	    // stop looping
229 	    break;
230 	 }
231       }
232    }
233    return nr_items_added;
234 }
235 
read_color()236 char* CfgFileParser::read_color()
237 {
238    // reads and skips lines until a 'COLOR' statement is found,
239    // it then extracts the color after 'COLOR' and returns a new string
240    // containing the ANSI color code for that string.
241    // RETURNS: the new string, NULL on error.
242 
243    // is there a open file?
244    assert (m_infile);
245 
246    while (1)
247    {
248       // read line
249       char *tmp = read_line();
250       if (!tmp)
251       {
252 	 	// found EOF
253 	 	return NULL;
254       }
255 
256       // got a line to look at
257 
258       // process line if it's not empty or doesn't starts with a '#'
259       if (tmp[0] != '\0' && tmp[0] != '#')
260       {
261 	 if (strncmp(tmp, "COLOR", 5) != 0)
262 	 {
263 	    cout << "colortail: Error in config file: " << m_filename
264 		 << " at line " << m_line << ". Skipping this line." << endl;
265 	 }
266 	 else
267 	 {
268 	    int linepos = 5;
269 	    char *color = new char[20];
270 
271 	    // skip all spaces
272 	    while (tmp[linepos] == ' ')
273 	    {
274 	       linepos++;
275 	    }
276 
277 	    // read which color it is
278 	    int found_color = 0;
279 
280 	    if (strncmp(&tmp[linepos], "black", 5) == 0) {
281 	       strcpy(color, BLACK);
282 	       found_color = 1;
283 	    }
284 
285 	    if (strncmp(&tmp[linepos], "red", 3) == 0) {
286 	       strcpy(color, RED);
287 	       found_color = 1;
288 	    }
289 
290 	    if (strncmp(&tmp[linepos], "green", 5) == 0) {
291 	       strcpy(color, GREEN);
292 	       found_color = 1;
293 	    }
294 
295 	    if (strncmp(&tmp[linepos], "yellow", 6) == 0) {
296 	       strcpy(color, YELLOW);
297 	       found_color = 1;
298 	    }
299 
300 	    if (strncmp(&tmp[linepos], "blue", 4) == 0) {
301 	       strcpy(color, BLUE);
302 	       found_color = 1;
303 	    }
304 
305 	    if (strncmp(&tmp[linepos], "magenta", 7) == 0) {
306 	       strcpy(color, MAGENTA);
307 	       found_color = 1;
308 	    }
309 
310 	    if (strncmp(&tmp[linepos], "cyan", 4) == 0) {
311 	       strcpy(color, CYAN);
312 	       found_color = 1;
313 	    }
314 
315 	    if (strncmp(&tmp[linepos], "white", 6) == 0) {
316 	       strcpy(color, WHITE);
317 	       found_color = 1;
318 	    }
319 
320 	    if (strncmp(&tmp[linepos], "brightblack", 6+5) == 0) {
321 	       strcpy(color, BRIGHTBLACK);
322 	       found_color = 1;
323 	    }
324 
325 	    if (strncmp(&tmp[linepos], "brightred", 6+3) == 0) {
326 	       strcpy(color, BRIGHTRED);
327 	       found_color = 1;
328 	    }
329 
330 	    if (strncmp(&tmp[linepos], "brightgreen", 6+5) == 0) {
331 	       strcpy(color, BRIGHTGREEN);
332 	       found_color = 1;
333 	    }
334 
335 	    if (strncmp(&tmp[linepos], "brightyellow", 6+6) == 0) {
336 	       strcpy(color, BRIGHTYELLOW);
337 	       found_color = 1;
338 	    }
339 
340 	    if (strncmp(&tmp[linepos], "brightblue", 6+4) == 0) {
341 	       strcpy(color, BRIGHTBLUE);
342 	       found_color = 1;
343 	    }
344 
345 	    if (strncmp(&tmp[linepos], "brightmagenta", 6+7) == 0) {
346 	       strcpy(color, BRIGHTMAGENTA);
347 	       found_color = 1;
348 	    }
349 
350             if (strncmp(&tmp[linepos], "brightcyan", 6+4) == 0) {
351 	       strcpy(color, BRIGHTCYAN);
352 	       found_color = 1;
353 	    }
354 
355 	    if (strncmp(&tmp[linepos], "brightwhite", 6+5) == 0) {
356 	       strcpy(color, BRIGHTWHITE);
357 	       found_color = 1;
358 	    }
359 
360 	    if (found_color == 0)
361 	    {
362 	       // didn't found color
363 	       cout << "colortail: Don't recognize color in config file: "
364 		    << m_filename << " at line " << m_line << endl;
365 
366 	       // free mem
367 	       delete tmp;
368 	       delete color;
369 	       // error, return NULL
370 	       return NULL;
371 	    }
372 	    else
373 	    {
374 	       // free mem
375 	       delete tmp;
376 	       // found color
377 	       return color;
378 	    }
379 	 }
380 	 // free memory for read line
381 	 delete tmp;
382       }
383    }
384    // should never execute this
385    return NULL;
386 }
387 
read_left()388 int CfgFileParser::read_left()
389 {
390    // reads lines (skips empty and comments lines) until a '{' is found
391    // RETURNS: 0 if a '{' is found, 1 if not found, 2 if error
392 
393    // is there a open file?
394    assert (m_infile);
395 
396    while (1)
397    {
398       // read line
399       char *tmp = read_line();
400       if (!tmp)
401       {
402 	 // error reading line
403 	 cout << "colortail: Error reading line in config file: "
404 	      << m_filename << " at line " << m_line << "." << endl;
405 	 // error, return 2
406 	 return 2;
407       }
408 
409       // got a line to look at
410 
411       // process line if it's not empty or doesn't starts with a '#'
412       if (tmp[0] != '\n' && tmp[0] != '#')
413       {
414 	 if (tmp[0] == '{')
415 	 {
416 	    // free mem
417 	    delete tmp;
418 
419 	    return 0;
420 	 }
421 	 else
422 	 {
423 	    // not a '{'
424 	    cout << "colortail: Error, expected '{' but found '"
425 		 << tmp[0] << "' in config file: " << m_filename
426 		 << " at line " << m_line << "." << endl;
427 	    // free mem
428 	    delete tmp;
429 
430 	    return 1;
431 	 }
432       }
433 
434       // free mem
435       delete tmp;
436    }
437 
438    // should never get this far
439    return 1;
440 }
441 
read_regexp()442 char* CfgFileParser::read_regexp()
443 {
444    // reads and skips empty and comments lines until a regexp is found
445    // RETURNS: a new string containing the reg exp, NULL on error
446 
447    // is there a open file?
448    assert (m_infile);
449 
450    while (1)
451    {
452       // read line
453       char *tmp = read_line();
454 
455       if (!tmp)
456       {
457 	 // error reading line
458 	 cout << "colortail: Error reading line in config file: "
459 	      << m_filename << " at line " << m_line << "." << endl;
460 
461 	 // error, return NULL
462 	 return NULL;
463       }
464 
465       // got a line
466 
467       // process line if it's not empty or doesn't starts with a '#'
468       if (tmp[0] != '\n' && tmp[0] != '#')
469       {
470 	 // found a string, return it
471 	 return tmp;
472       }
473 
474       // this is a empty line or a comment, skip it
475 
476       // free mem
477       delete tmp;
478    }
479 
480    // should never get this far
481    return NULL;
482 }
483 
484 
parse(const char * filename)485 int CfgFileParser::parse(const char *filename)
486 {
487    // parses the cfg file and sets up the list of SearchData elements
488    // returns number of SearchData items created
489 
490    // is there a list?
491    if (m_items_list)
492    {
493       // delete it
494       free_items();
495    }
496 
497    // make a new list
498    m_items_list = new List<SearchData*>;
499 
500    // check the allocation
501    assert (m_items_list != NULL);
502 
503    // try to open the file
504    m_infile.open(filename, ios::in);
505 
506    if (!m_infile)
507    {
508       // open failed
509       cout << "colortail: Failed to open config file: " << filename << endl;
510       return 0;
511    }
512 
513    // save filename of config file
514    if (m_filename)
515    {
516       delete m_filename;
517    }
518 
519    m_filename = new char[strlen(filename) + 1];
520    strcpy(m_filename, filename);
521 
522 
523    int items_counter = 0;
524 
525    // read the items
526    while (!m_infile.eof())
527    {
528       // read in a item
529       int n = read_item();
530 
531       items_counter += n;
532    }
533 
534    return items_counter;
535 }
536 
get_items_list()537 List<SearchData*>* CfgFileParser::get_items_list()
538 {
539    // returns the items list, and sets this class item list to NULL
540 
541    List<SearchData*>* tmp = m_items_list;
542    m_items_list = NULL;
543 
544    return tmp;
545 }
546 
547 
548 
549 
550 
551 
552 
553 
554