Update the changelog
[opencv] / apps / cvenv / colorer.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #include <assert.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <ctype.h>
46 #include "colorer.h"
47
48 typedef struct _HashEntry
49 {
50     unsigned  hash;
51     int       len;
52     struct _HashEntry* next;
53     const char* str;
54 }
55 HashEntry;
56
57 HashEntry   keyword_storage[100];
58
59 #define  HASHTABLE_SIZE   17
60 HashEntry*  keyword_table[HASHTABLE_SIZE];
61 static int  hash_init = 0;
62
63 const char* keywords[] =
64 {
65     "break",    "case",     "char",     "const",
66     "continue", "default",  "do",       "double",
67     "else",     "enum",     "extern",   "float",
68     "for",      "goto",     "if",       "int",
69     "long",     "register", "return",   "short",
70     "signed",   "sizeof",   "static",   "struct",
71     "switch",   "typedef",  "union",    "unsigned",
72     "void",     "volatile", "while",    0
73 };
74
75
76 inline unsigned  calc_hash( const char* text, int len )
77 {
78     int j, shift = 0;
79     unsigned hash = len;
80     for( j = 0; j < len; j++ )
81     {
82         shift += 11;
83         if( shift >= 32 ) shift -= 32;
84         hash ^= ((unsigned char*)text)[j] << shift;
85     }
86     return hash;
87 }
88
89
90 void InitLexer( Lexer* lexer, const char* text )
91 {
92     lexer->text = text;
93     lexer->pos = 0;
94
95     if( !hash_init )
96     {
97         int  i;
98         int  count[HASHTABLE_SIZE];
99         memset( count, 0, sizeof(count));
100         memset( keyword_table, 0, sizeof( keyword_table));
101
102         for( i = 0; keywords[i] != 0; i++ )
103         {
104             int idx, len;
105             keyword_storage[i].len = len = strlen( keywords[i] );
106             keyword_storage[i].hash = calc_hash( keywords[i], len );
107             keyword_storage[i].str = keywords[i];
108             idx = keyword_storage[i].hash % HASHTABLE_SIZE;
109             keyword_storage[i].next = keyword_table[idx];
110             keyword_table[idx] = keyword_storage + i;
111             count[idx]++;
112         }
113         hash_init = 1;
114     }
115 }
116
117
118 HashEntry* find_text( const char* str, int len )
119 {
120     unsigned hash = calc_hash( str, len );
121     int idx = hash % HASHTABLE_SIZE;
122     HashEntry* entry = keyword_table[idx];
123
124     while( entry )
125     {
126         if( entry->hash == hash &&
127             entry->len == len &&
128             !strncmp( entry->str, str, len )) break;
129         entry = entry->next;
130     }
131     return entry;
132 }
133
134
135 void  GetToken( Lexer* lexer, Token* token )
136 {
137     const char* text = lexer->text;
138     int pos = lexer->pos;
139     token->type = TOKEN_NORMAL;
140
141     while( isspace(text[pos])) pos++;
142     token->start = pos;
143
144     switch( text[pos] )
145     {
146     case '/': pos++;
147               switch( text[pos] )
148               {
149               case '/': /* end-line comment */
150                   token->type = TOKEN_COMMENT;
151                   ++pos; while( text[pos] != '\n' && text[pos] != '\0' ) pos++;
152                   break;
153               case '*':
154                   token->type = TOKEN_COMMENT;
155                   ++pos;
156                   while( text[pos] != '\0' )
157                   {
158                       if( text[pos] == '*' && text[pos+1] == '/')
159                       {
160                           pos += 2;
161                           break;
162                       }
163                       pos++;
164                   }
165                   break;
166               }
167               break;
168     case '\0':  token->type = TOKEN_END;
169                 return;
170
171     case '\"':  token->type = TOKEN_STRING;
172                 pos++;
173                 for(;;)
174                 {
175                     if( text[pos] == '\0' || text[pos] == '\"' || text[pos] == '\n' )
176                         break;
177                     if( text[pos] == '\\' )
178                     {
179                         pos += 2;
180                         if( text[pos] == '\n' ) pos++;
181                     }
182                     else
183                     {
184                         pos++;
185                     }
186                 }
187                 if( text[pos] == '\"') pos++;
188                 break;
189
190     case '\'':  token->type = TOKEN_STRING;
191                 pos++;
192                 for(;;)
193                 {
194                     if( text[pos] == '\0' || text[pos] == '\'' || text[pos] == '\n' )
195                         break;
196                     pos += text[pos] == '\\' ? 2 : 1;
197                 }
198                 if( text[pos] == '\'') pos++;
199                 break;
200     default:
201         if( isalpha( text[pos] ) || text[pos] == '_' )
202         {
203             pos++;
204             while( isalnum( text[pos] ) || text[pos] == '_' ) pos++;
205
206             if( find_text( text + token->start, pos - token->start ))
207             {
208                 token->type = TOKEN_KEYWORD;
209             }
210         }
211         else if( isdigit(text[pos]) || (text[pos] == '.' && isdigit(text[pos+1])))
212         {
213             int pos1 = pos;
214             token->type = TOKEN_NUMBER;
215             pos++; while( isalnum( text[pos])) pos++;
216             if( (text[pos] == '+' || text[pos] == '-') && text[pos-1] == 'e')
217             {
218                 while( isdigit(text[pos1]) || text[pos1] == '.') pos1++;
219                 if( pos1 == pos - 1 )
220                 {
221                     pos++;
222                     while( isdigit(text[pos])) pos++;
223                 }
224             }
225         }
226         else
227         {
228             pos++;
229         }
230     }
231
232     lexer->pos = pos;
233 }
234
235 ///////////////////////////////////////////////////////////////////////////////
236 //parser
237 //
238
239 int GetTokenFromPos(int pos, Token* tokens, int num)
240 {
241     int i;
242     for(i = 1; i < num; i++)
243         if(pos < tokens[i].start) return i -1;
244     return i - 1;
245 }
246
247 int SetMap(char* map, Token* tokens, int num)
248 {
249     tokens->start = 0;
250     for(int i = 0; i < num - 1; i++)
251         for(int j = 0; j < tokens[i + 1].start - tokens[i].start; j++)
252             map[tokens[i].start + j] = (char)tokens[i].type;
253     map[tokens[max(0, num - 1)].start] = TOKEN_END;
254     return 0;
255 }
256
257 static char* old_text = 0;
258 static char* old_map = 0;
259
260 int ParseTextBegin(ClientData, Tcl_Interp *interp,
261               int, char**)
262 {
263     Token tokens__[MAX_TOKNS] = { TOKEN_END, 0 };
264     Token* tokens_ = tokens__;
265     int num = 0;
266     int max_num = MAX_TOKNS;
267
268     Tcl_Eval(interp, "$CVEnv::curframe get 1.0 end");
269
270     if(old_text) delete old_text;
271     if(old_map) delete old_map;
272
273     int len = strlen(interp->result);
274     if(!len) return 0;
275     old_text = new char[len + 1];
276     assert(old_text);
277     old_map  = new char[len + 1];
278     assert(old_map);
279
280     strcpy(old_text, interp->result);
281
282     Lexer lexer;
283     Token token;
284     Token prev_token = { TOKEN_END, 0 };
285
286     InitLexer( &lexer, old_text );
287     lexer.pos = 0;
288
289     for(num = 0;;)
290     {
291         GetToken( &lexer, &token );
292         if( token.type != prev_token.type )
293         {
294             tokens_[num] = token;
295             prev_token = token;
296             num++;
297             if(num >= max_num)
298             {
299                 max_num *= 2;
300                 Token* tokens_tmp = new Token[max_num];
301                 assert(tokens_tmp);
302                 for(int i = 0; i < num; i++) tokens_tmp[i] = tokens_[i];
303                 if(tokens_ != tokens__) delete tokens_;
304                 tokens_ = tokens_tmp;
305             }
306         }
307         if( token.type == TOKEN_END ) break;
308     }
309
310     SetMap(old_map, tokens_, num);
311
312     assert(*old_map <= TOKEN_END);
313
314     if(tokens_ != tokens__) delete tokens_;
315     return 0;
316 }
317
318
319 int ParseTextEnd(ClientData, Tcl_Interp *interp,
320               int, char**)
321 {
322     Token tokens__[MAX_TOKNS] = { TOKEN_END, 0 };
323     Token* tokens_ = tokens__;
324     int num = 0;
325     int max_num = MAX_TOKNS;
326     
327     Tcl_Eval(interp, "$CVEnv::curframe get 1.0 end");
328
329     int len = strlen(interp->result);
330     if(!len) return 0;
331     char* text = new char[len + 1];
332     assert(text);
333     char* map  = new char[len + 1];
334     assert(map);
335
336     strcpy(text, interp->result);
337
338     if(old_text && !strcmp(text, old_text)) goto end;
339
340     {
341     if(!old_text) {old_text = new char[1]; *old_text = 0;}
342     if(!old_map) {old_map = new char[1]; *old_map = TOKEN_END;}
343
344     assert(*old_map <= TOKEN_END);
345
346     Lexer lexer;
347     Token* tokens = tokens_;
348     Token token;
349     Token prev_token = { TOKEN_END, 0 };
350
351     InitLexer( &lexer, text );
352     lexer.pos = 0;
353
354     for(num = 0;;)
355     {
356         GetToken( &lexer, &token );
357         if( token.type != prev_token.type )
358         {
359             tokens_[num] = token;
360             prev_token = token;
361             num++;
362             if(num >= max_num)
363             {
364                 max_num *= 2;
365                 Token* tokens_tmp = new Token[max_num];
366                 assert(tokens_tmp);
367                 for(int i = 0; i < num; i++) tokens_tmp[i] = tokens_[i];
368                 if(tokens_ != tokens__) delete tokens_;
369                 tokens_ = tokens_tmp;
370             }
371         }
372         if( token.type == TOKEN_END ) break;
373     }
374
375     assert(*old_map <= TOKEN_END);
376
377     int len = strlen(text);
378     int old_len = strlen(old_text);
379
380     SetMap(map, tokens_, num);
381     // finding first & last changed tokens
382     int first;
383     int last;
384     for(first = 0; map[first] == old_map[first]; first++);
385     for(last = len - 1; map[last] == old_map[max(0, last + (old_len - len))]; last--);
386
387     if(first > last) swap(first, last);
388
389     int first_t = GetTokenFromPos(first, tokens_, num);
390     int last_t = GetTokenFromPos(last, tokens_, num);
391
392     if(first == len)
393         goto end2;
394
395     char command[1000];
396
397     char* normal  = new char[len * strlen("\"0.0 + 0000000 chars\" \"0.0 + 0000000 chars\" ")];
398     char* comment = new char[len * strlen("\"0.0 + 0000000 chars\" \"0.0 + 0000000 chars\" ")];
399     char* strng  = new char[len * strlen("\"0.0 + 0000000 chars\" \"0.0 + 0000000 chars\" ")];
400     char* keyword = new char[len * strlen("\"0.0 + 0000000 chars\" \"0.0 + 0000000 chars\" ")];
401     char* number  = new char[len * strlen("\"0.0 + 0000000 chars\" \"0.0 + 0000000 chars\" ")];
402
403     assert( normal && comment && strng && keyword && number );
404     strcpy( normal, "$CVEnv::curframe tag add normal " );
405     strcpy( comment, "$CVEnv::curframe tag add comment " );
406     strcpy( strng, "$CVEnv::curframe tag add string " );
407     strcpy( keyword, "$CVEnv::curframe tag add keyword " );
408     strcpy( number, "$CVEnv::curframe tag add number " );
409
410     sprintf(command, "ClearColors %d %d $CVEnv::curframe", first, last);
411     Tcl_Eval(interp, command);
412     assert(*interp->result == 0);
413
414     tokens = tokens_;
415     for( int t = first_t; t <=last_t; t++ )
416     {
417         char pos[60];
418         sprintf(pos, "\"0.0 + %d chars\" \"0.0 + %d chars\" ", tokens[t].start, tokens[t + 1].start);
419         switch(tokens[t].type)
420         {
421         case TOKEN_COMMENT:
422             strcat(comment, pos);
423             break;
424         case TOKEN_STRING:
425             strcat(strng, pos);
426             break;
427         case TOKEN_KEYWORD:
428             strcat(keyword, pos);
429             break;
430         case TOKEN_NUMBER:
431             strcat(number, pos);
432             break;
433         case TOKEN_END:
434             goto end2;
435             break;
436         default:
437             strcat(normal, pos);
438             break;
439         }
440     }
441     if(strlen(normal) > 34)
442     {
443         Tcl_Eval(interp, normal);
444         assert(*interp->result == 0);
445     }
446     if(strlen(comment) > 34)
447     {
448         Tcl_Eval(interp, comment);
449         assert(*interp->result == 0);
450     }
451     if(strlen(strng) > 34)
452     {
453         Tcl_Eval(interp, strng);
454         assert(*interp->result == 0);
455     }
456     if(strlen(number) > 34)
457     {
458         Tcl_Eval(interp, number);
459         assert(*interp->result == 0);
460     }
461     if(strlen(keyword) > 34)
462     {
463         Tcl_Eval(interp, keyword);
464         assert(*interp->result == 0);
465     }
466
467     delete normal;
468     delete comment;
469     delete strng;
470     delete keyword;
471     delete number;
472     }
473 end2:
474     if(old_map) delete old_map;
475     old_map = map;
476 end:
477     if(old_text) delete old_text;
478     old_text = text;
479     if(tokens_ != tokens__) delete tokens_;
480     return 0;
481 }
482
483 ///////////////////////////////////////////////////////////////////////////////
484 // color's function
485 //
486
487 bool LoadColors(Tcl_Interp *pinterp, const char* colorsfile)
488 {
489     int code = Tcl_EvalFile(pinterp, (char*)colorsfile);
490     if (code != TCL_OK)
491                     return false;
492
493     return true;
494 }
495
496 bool SaveColors(Tcl_Interp *pinterp, const char* colorsfile)
497 {
498     FILE* pfile  = fopen(colorsfile, "w");
499     if (pfile == NULL) 
500         return false;
501
502     fprintf( pfile, "# Custom Colors\n");
503     fprintf( pfile, "set ::EditorTextbg ");
504     fprintf( pfile, "%s\n",Tcl_GetVar(pinterp, "EditorTextbg", TCL_GLOBAL_ONLY));
505     fprintf( pfile, "set ::EditorTextfg ");
506     fprintf( pfile, "%s\n",Tcl_GetVar(pinterp, "EditorTextfg", TCL_GLOBAL_ONLY));
507     fprintf( pfile, "set ::EditorStringfg ");
508     fprintf( pfile, "%s\n",Tcl_GetVar(pinterp, "EditorStringfg", TCL_GLOBAL_ONLY));
509     fprintf( pfile, "set ::EditorNumberfg ");
510     fprintf( pfile, "%s\n",Tcl_GetVar(pinterp, "EditorNumberfg", TCL_GLOBAL_ONLY));
511     fprintf( pfile, "set ::EditorCommentfg ");
512     fprintf( pfile, "%s\n",Tcl_GetVar(pinterp, "EditorCommentfg", TCL_GLOBAL_ONLY));
513     fprintf( pfile, "set ::EditorKeywordfg ");
514     fprintf( pfile, "%s\n",Tcl_GetVar(pinterp, "EditorKeywordfg", TCL_GLOBAL_ONLY));
515     fprintf( pfile, "set ::EditorSelectbg ");
516     fprintf( pfile, "%s\n",Tcl_GetVar(pinterp, "EditorSelectbg", TCL_GLOBAL_ONLY));
517     fprintf( pfile, "set ::EditorSelectfg ");
518     fprintf( pfile, "%s\n",Tcl_GetVar(pinterp, "EditorSelectfg", TCL_GLOBAL_ONLY));
519     fprintf( pfile, "set ::EditorCursorbg ");
520     fprintf( pfile, "%s\n",Tcl_GetVar(pinterp, "EditorCursorbg", TCL_GLOBAL_ONLY));
521
522     fprintf( pfile, "# Custom Fonts\n");
523     fprintf( pfile, "set ::EditorTextfont \"");
524     fprintf( pfile, "%s\"\n",Tcl_GetVar(pinterp, "EditorTextfont", TCL_GLOBAL_ONLY));
525     fprintf( pfile, "set ::EditorStringfont \"");
526     fprintf( pfile, "%s\"\n",Tcl_GetVar(pinterp, "EditorStringfont", TCL_GLOBAL_ONLY));
527     fprintf( pfile, "set ::EditorNumberfont \"");
528     fprintf( pfile, "%s\"\n",Tcl_GetVar(pinterp, "EditorNumberfont", TCL_GLOBAL_ONLY));
529     fprintf( pfile, "set ::EditorCommentfont \"");
530     fprintf( pfile, "%s\"\n",Tcl_GetVar(pinterp, "EditorCommentfont", TCL_GLOBAL_ONLY));
531     fprintf( pfile, "set ::EditorKeywordfont \"");
532     fprintf( pfile, "%s\"\n",Tcl_GetVar(pinterp, "EditorKeywordfont", TCL_GLOBAL_ONLY));
533
534     fclose(pfile);
535     
536     return true;
537 }