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 "Colorizer.h"
21 #include "CfgFileParser.h"
22 #include "TailFile.h"
23 
24 #include <assert.h>
25 #include <iostream>
26 #include <string.h>
27 
28 using namespace std;
29 
Colorizer()30 Colorizer::Colorizer()
31 {
32    // default constructor
33 
34    m_items_list = NULL;
35 }
36 
free_items()37 void Colorizer::free_items()
38 {
39    // frees the memory that the items uses
40 
41    // check if there is a list
42    if (m_items_list)
43    {
44       SearchData *tmp;
45 
46       // go through all the elements in the list,
47       // and free each SearchData instance
48       while (!m_items_list->is_empty())
49       {
50          tmp = m_items_list->first_element();
51          m_items_list->remove_first();
52          delete tmp;
53       }
54 
55       // delete the list
56       delete m_items_list;
57       m_items_list = NULL;
58    }
59 }
60 
61 
Colorizer(const char * cfg_file)62 Colorizer::Colorizer(const char *cfg_file)
63 {
64    // other constructor
65 
66    // sends the cfg_file to the CfgFileParser
67    // gets the items_list from the CfgFileParser and saves it
68 
69    // check that cfg_file isn't NULL
70    assert (cfg_file != NULL);
71 
72    CfgFileParser parser;
73 
74    // parse the cfg file
75    int n = parser.parse(cfg_file);
76 
77    // get the items list
78    m_items_list = parser.get_items_list();
79 }
80 
~Colorizer()81 Colorizer::~Colorizer()
82 {
83    // destructor
84 
85    // free the memory the search data items use
86    free_items();
87 }
88 
colorize(const char * str)89 string Colorizer::colorize(const char *str)
90 {
91    // colorize the string, returns a new string containing
92    // the colorized version of str
93    // RETURN: new string with result
94 
95    // check that a list exists
96    assert (m_items_list != NULL);
97 
98    regmatch_t pmatch[10];
99    int found = 0, submatch = 0, j;
100    char color[MAX_CHARS_READ][20];
101 
102    // set all the color strings to empty strings
103    for (int f = 0 ; f < MAX_CHARS_READ ; f++)
104    {
105       color[f][0] = '\0';
106    }
107 
108    // make an iterator
109    ListIterator<SearchData*> itr(*m_items_list);
110 
111    // will contain the new string
112    ostringstream newstr;
113 
114    SearchData *current;
115    int i = 0;
116    // go through all the elements in the list
117    for (itr.init() ; !itr ; ++itr, i++)
118    {
119       current = itr();
120 
121       // check for match
122       if (regexec(current->m_preg, str, 10, pmatch, 0) == 0)
123       {
124 	 // TODO: check for callback function
125 	 // TODO: call callback function
126 
127 	 found = 1;
128 
129 	 // Check to see if this is a substring match
130 	 if (pmatch[1].rm_so != -1)
131 	 {
132 	    submatch = 1;
133 	    // Note: start at offset 1 to get only submatches
134 
135 	    for (j = 1 ; pmatch[j].rm_so != -1 && j <= 10 ; j++)
136 	    {
137 	       // go through all the character positions
138 	       // that the submatch is for
139 	       for (int k = pmatch[j].rm_so ; k < (pmatch[j].rm_eo) ; k++)
140 	       {
141 
142 		  // set the color in the color string array
143 		  strcpy(color[k], current->m_ansi_color_code);
144 	       }
145 	    }
146 	 }
147 	 else
148 	 {
149 	    // not a substring match, colorize whole string.
150 	    // stop looking for other matches as well
151 
152 	    submatch = 0;
153 
154 	    // write ansi color code
155 	    newstr << current->m_ansi_color_code;
156 
157 	    // check if str ends in '\n'
158 	    int len = strlen(str);
159 	    if (str[len-1] == '\n')
160 	    {
161 	       for (int a = 0 ; a < len-1 ; a++)
162 	       {
163 		  newstr.put(str[a]);
164 	       }
165 	    }
166 	    else
167 	    {
168 	       // doesn't end in '\n'
169 	       newstr << str;
170 	    }
171 
172 	    // write ansi reset str and a newline
173 	    newstr << ANSI_RESET_STR << endl << ends;
174 	    // return the new string
175 	    return newstr.str();
176 	 }
177       }
178    }
179 
180    // did we get a match
181    if (found == 0)
182    {
183       // no we didn't
184       // print without color
185       // check if str ends in '\n'
186       if (str[strlen(str)-1] == '\n')
187       {
188 	 newstr << str << ends;
189       }
190       else
191       {
192 	 // doesn't end in '\n'
193 	 newstr << str << endl << ends;
194       }
195 
196       // return the new string
197       return newstr.str();
198    }
199 
200    // did we find submatches?
201    if (submatch == 1)
202    {
203       int last_was_reset_str = 1;
204       // iiterate through all the characters
205       int l = strlen(str);
206       for (i = 0 ; i < l ; i++)
207       {
208 	 if (color[i][0] == '\0')
209 	 {
210 	    // no color for this position
211 	    if (last_was_reset_str != 1)
212 	    {
213 	       // write reset string
214 	       newstr << ANSI_RESET_STR;
215 	       // remeber that we wrote it
216 	       last_was_reset_str = 1;
217 	    }
218 	 }
219 	 else
220 	 {
221 	    // a color for this position
222 	    // reset ansi reset string checker
223 	    last_was_reset_str = 0;
224 	    // write color
225 	    newstr << color[i];
226 	 }
227 
228 	 // write current character
229 	 newstr.put(str[i]);
230       }
231 
232       // check if last wasn't the ansi reset string
233       if (last_was_reset_str != 1)
234       {
235 	 // write reset string
236 	 newstr << ANSI_RESET_STR;
237       }
238 
239       // write newline and null
240       //newstr << endl << ends;
241       newstr << ends;
242 
243       return newstr.str();
244 
245    }
246 
247 
248    // should never get here
249 	string empty = "";
250    	return empty;
251 }
252