Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtinfo / dtinfo / src / UAS / DtSR / TextParser.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 // $XConsortium: TextParser.cc /main/4 1996/06/11 17:41:43 cde-hal $
24 /*      Copyright (c) 1995,1996 FUJITSU LIMITED         */
25 /*      All Rights Reserved                             */
26
27 #include <assert.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include <strstream.h>
33
34 #include "TextParser.hh"
35
36 #ifndef False
37 #define False   0
38 #endif
39 #ifndef True
40 #define True    1
41 #endif
42
43 char *
44 StringParser::brute_force(const char* text_in, int n_of_pats,
45                                 const char* patterns, int sensitive)
46 {
47     if (text_in == NULL || *text_in == '\0')
48         return NULL;
49     else if (patterns == NULL || *patterns == '\0')
50         return NULL;
51
52     if (! n_of_pats > 0)
53         return NULL;
54
55     char** pat_tbl = new char*[n_of_pats + 1];
56     char** pat_tbl_end = pat_tbl + n_of_pats;
57
58     int npat;
59     for (npat = 0; *patterns && n_of_pats > 0; npat++, n_of_pats--) {
60         char* del = strchr(patterns, '\n');
61         if (del != NULL) { // more pattern specified
62             pat_tbl[npat] = new char[del - patterns + 1];
63             strncpy(pat_tbl[npat], patterns, del - patterns);
64             pat_tbl[npat][del - patterns] = '\0';
65             patterns = del + 1;
66         }
67         else {
68             pat_tbl[npat] = new char[strlen(patterns) + 1];
69             strcpy(pat_tbl[npat], patterns);
70             patterns += strlen(patterns);
71             assert( *patterns == '\0' );
72         }
73     }
74     pat_tbl[npat] = NULL; // pointer table terminated
75
76     assert( npat > 0 ); // at least one pattern available here
77
78 #ifdef DEBUG
79     if (*patterns)
80         fprintf(stderr, "(WARNING) more patterns available than specified\n");
81     if (n_of_pats > 0)
82         fprintf(stderr, "(WARNING) less patterns available than specified\n");
83 #endif
84
85     int text_len = strlen(text_in);
86
87     // remove null and too long patterns
88     int i;
89     for (i = 0 ; pat_tbl[i]; i++) {
90         if (*(pat_tbl[i]) == '\0' || text_len < strlen(pat_tbl[i])) {
91             delete[] pat_tbl[i];
92             pat_tbl[i] = NULL;
93             npat--;
94         }
95     }
96
97     // remove redundance
98     char** cursor;
99     for (cursor = pat_tbl; cursor < pat_tbl_end; cursor++) {
100         if (*cursor == NULL)
101             continue;
102         char** p = cursor + 1;
103         for (; p < pat_tbl_end; p++) {
104             if (*p == NULL)
105                 continue;
106             if (strcmp(*cursor, *p) == 0)
107                 break;
108         }
109         if (p < pat_tbl_end) { // same pattern found
110             delete[] *cursor;
111             *cursor = NULL;
112             npat--;
113         }
114     }
115
116     // compact pat_tbl
117     char** free_slot;
118     for (free_slot = pat_tbl; *free_slot; free_slot++);
119     if (free_slot < pat_tbl_end) { // there is a free slot
120         cursor = pat_tbl;
121         for (i = 0; i < npat; i++, cursor++) {
122             // find next pattern
123             for (; *cursor == NULL && cursor < pat_tbl_end; cursor++);
124             assert( cursor < pat_tbl_end );
125             if (free_slot && free_slot < cursor) {
126                 *free_slot = *cursor;
127                 *cursor = NULL;
128                 // find next available free slot
129                 free_slot++;
130                 for (; *free_slot; free_slot++);
131                 if (free_slot == pat_tbl_end)
132                     free_slot = NULL;
133             }
134         }
135     }
136     else {
137         free_slot = NULL;
138     }
139
140     if (npat == 0) { // there is no effective patterns after all
141         delete[] pat_tbl;
142         return NULL;
143     }
144
145 #ifdef DEBUG
146     fprintf(stderr, "(DEBUG) %d effective patterns=", npat);
147     for (int k = 0; pat_tbl[k]; k++) {
148         fprintf(stderr, "\"%s\" ", pat_tbl[k]);
149     }
150     fprintf(stderr, "\n");
151 #endif
152
153     char* caped_text = NULL;
154
155     if (sensitive == False) { // case-insensitive search
156         unsigned char *p;
157         for (int i = 0; i < npat; i++) {
158             for (p = (unsigned char*)pat_tbl[i]; *p; p++) {
159                 if (*p < 0x7B && *p > 0x60) // a ... z
160                     *p = *p - 0x20;
161             }
162         }
163         ostrstream capitalized;
164         for (p = (unsigned char*)text_in; *p; p++) {
165             if (*p < 0x7B && *p > 0x60) // a ... z
166                 capitalized << (char)(*p - 0x20); // capitalize
167             else
168                 capitalized << *p;
169         }
170         text_in = caped_text = capitalized.str();
171         *(char*)(text_in + capitalized.pcount()) = '\0';
172     }
173
174     ostrstream text_run;
175
176     for (int index = 0; index < text_len;) {
177         unsigned int candidate = (1 << npat) - 1;
178         unsigned int success = 0;
179         int i, j;
180         for (i = index, j = 0 ; i < text_len + 1 && candidate; i++, j++) {
181             for (int n = 0; n < npat; n++) {
182                 unsigned int mask = 1;
183                 mask = mask << (npat - 1 - n);
184                 if (candidate & mask) { // still candidate
185                     if (pat_tbl[n][j] == '\0') {
186                         success |= mask;
187                         candidate &= ~mask;
188                         continue;
189                     }
190                     else if (pat_tbl[n][j] != text_in[i]) {
191                         candidate &= ~mask;
192                         continue;
193                     }
194                 }
195                 else {
196                     continue;
197                 }
198             }
199         }
200
201         if (success) { // matched
202             // select the longest one
203 #ifdef SETECT_LONGEST
204             int nth = npat;
205 #endif
206             for (int n = 0; success > 0; success /= 0x02, n++) {
207                 if (success & 0x01) {
208 #ifdef SETECT_LONGEST
209                     if (nth == npat)
210                         nth = npat - 1 - n;
211                     else {
212                         if (strlen(pat_tbl[nth])
213                                         < strlen(pat_tbl[npat - 1 - n])) {
214                             nth = npat - 1 - n;
215                         }
216                     }
217 #else
218                     text_run << index << '\t' <<
219                                 strlen(pat_tbl[npat - 1 - n]) << '\n';
220 #endif
221                 }
222             }
223 #ifdef SETECT_LONGEST
224             text_run << index << '\t' << strlen(pat_tbl[nth]) << '\n';
225 #endif
226         }
227
228         index += mblen(text_in + index, MB_CUR_MAX);
229     }
230
231     for (i = 0; i < npat; i++)
232         free (pat_tbl[i]);
233     free (pat_tbl);
234
235     if (caped_text)
236         delete[] caped_text;
237
238     char* ret_text = text_run.str();
239
240     if (ret_text == NULL)
241         return NULL;
242     else if (*ret_text == '\0') {
243         delete[] ret_text;
244         return NULL;
245     }
246     else
247         return ret_text;
248
249 }
250
251 char *
252 StringParser::project_textrun(const char* org_textrun)
253 {
254     if (org_textrun == NULL || *org_textrun == '\0')
255         return NULL;
256
257     istrstream textrun(org_textrun);
258
259     char line[128];
260     textrun.get(line, 128, '\n');
261     if (textrun.get() != '\n')
262         return NULL;
263
264     char *offstr, *lenstr;
265
266     offstr = line;
267     if ((lenstr = strchr(line, '\t')) == NULL)
268         return NULL;
269     *lenstr++ = '\0';
270
271     int off, len;
272     off = atoi(offstr);
273     len = atoi(lenstr);
274     if (off < 0 || len <= 0) {
275 #ifdef DEBUG
276         fprintf(stderr, "(ERROR) either off=%d or len=%d is invalid\n",
277                                                                 off, len);
278 #endif
279         return NULL;
280     }
281
282     ostrstream ret_text;
283
284     while (textrun.get(line, 128, '\n')) {
285         if (textrun.get() != '\n') {
286 #ifdef DEBUG
287             fprintf(stderr, "(ERROR) line is not followed by newline\n");
288 #endif
289             break;
290         }
291
292         int next_off, next_len;
293         offstr = line;
294         if ((lenstr = strchr(line, '\t')) == NULL) {
295 #ifdef DEBUG
296             fprintf(stderr, "(ERROR) tab chatacter not found in \"%s\"\n", line);
297 #endif
298             break;
299         }
300         *lenstr++ = '\0';
301         next_off = atoi(offstr);
302         next_len = atoi(lenstr);
303         if (next_off < off || next_len <= 0) {
304 #ifdef DEBUG
305             fprintf(stderr, "(ERROR) either off=%d or length=%d is invalid\n",
306                                                         next_off, next_len);
307 #endif
308             break;
309         }
310
311         if (next_off <= off + len) { // overlap detected
312             if (off + len < next_off + next_len)
313                 len = next_off + next_len - off; // merge
314         }
315         else {
316             ret_text << off << '\t' << len << '\n';
317             off = next_off;
318             len = next_len;
319         }
320     }
321
322     ret_text << off << '\t' << len << '\n' << '\0';
323
324     return ret_text.str();
325 }
326
327 char *
328 StringParser::hilite(const char* text, int n, const char* pats)
329 {
330     char* textrun = brute_force(text, n, pats);
331
332     if (textrun == NULL)
333         return NULL;
334
335     char* prjed_textrun = project_textrun(textrun);
336     delete[] textrun;
337
338     return prjed_textrun;
339 }
340
341
342