1 //
2 // "$Id: code.cxx 7903 2010-11-28 21:06:39Z matt $"
3 //
4 // Code output routines for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2010 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
22 //
23 // Please report all bugs and problems on the following page:
24 //
25 //     http://www.fltk.org/str.php
26 //
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include "../src/flstring.h"
31 #include <stdarg.h>
32 
33 #include <FL/Fl.H>
34 #include "Fl_Type.h"
35 #include "alignment_panel.h"
36 
37 static FILE *code_file;
38 static FILE *header_file;
39 
40 extern char i18n_program[];
41 extern int i18n_type;
42 extern const char* i18n_include;
43 extern const char* i18n_function;
44 extern const char* i18n_file;
45 extern const char* i18n_set;
46 
47 // return true if c can be in a C identifier.  I needed this so
48 // it is not messed up by locale settings:
is_id(char c)49 int is_id(char c) {
50   return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
51 }
52 
53 ////////////////////////////////////////////////////////////////
54 // Generate unique but human-readable identifiers:
55 
56 struct id {
57   char* text;
58   void* object;
59   id *left, *right;
idid60   id (const char* t, void* o) : text(strdup(t)), object(o) {left = right = 0;}
61   ~id();
62 };
63 
~id()64 id::~id() {
65   delete left;
66   free((void *)text);
67   delete right;
68 }
69 
70 static id* id_root;
71 
unique_id(void * o,const char * type,const char * name,const char * label)72 const char* unique_id(void* o, const char* type, const char* name, const char* label) {
73   char buffer[128];
74   char* q = buffer;
75   while (*type) *q++ = *type++;
76   *q++ = '_';
77   const char* n = name;
78   if (!n || !*n) n = label;
79   if (n && *n) {
80     while (*n && !is_id(*n)) n++;
81     while (is_id(*n)) *q++ = *n++;
82   }
83   *q = 0;
84   // okay, search the tree and see if the name was already used:
85   id** p = &id_root;
86   int which = 0;
87   while (*p) {
88     int i = strcmp(buffer, (*p)->text);
89     if (!i) {
90       if ((*p)->object == o) return (*p)->text;
91       // already used, we need to pick a new name:
92       sprintf(q,"%x",++which);
93       p = &id_root;
94       continue;
95     }
96     else if (i < 0) p = &((*p)->left);
97     else p  = &((*p)->right);
98   }
99   *p = new id(buffer, o);
100   return (*p)->text;
101 }
102 
103 ////////////////////////////////////////////////////////////////
104 // return current indentation:
105 
106 static const char* spaces = "                ";
107 int indentation;
indent()108 const char* indent() {
109   int i = indentation; if (i>16) i = 16;
110   return spaces+16-i;
111 }
112 
113 ////////////////////////////////////////////////////////////////
114 // declarations/include files:
115 // Each string generated by write_declare is written only once to
116 // the header file.  This is done by keeping a binary tree of all
117 // the calls so far and not printing it if it is in the tree.
118 
119 struct included {
120   char *text;
121   included *left, *right;
includedincluded122   included(const char *t) {
123     text = strdup(t);
124     left = right = 0;
125   }
126   ~included();
127 };
128 
~included()129 included::~included() {
130   delete left;
131   free((void *)text);
132   delete right;
133 }
134 static included *included_root;
135 
write_declare(const char * format,...)136 int write_declare(const char *format, ...) {
137   va_list args;
138   char buf[1024];
139   va_start(args, format);
140   vsnprintf(buf, sizeof(buf), format, args);
141   va_end(args);
142   included **p = &included_root;
143   while (*p) {
144     int i = strcmp(buf,(*p)->text);
145     if (!i) return 0;
146     else if (i < 0) p = &((*p)->left);
147     else p  = &((*p)->right);
148   }
149   fprintf(header_file,"%s\n",buf);
150   *p = new included(buf);
151   return 1;
152 }
153 
154 ////////////////////////////////////////////////////////////////
155 
156 // silly thing to prevent declaring unused variables:
157 // When this symbol is on, all attempts to write code don't write
158 // anything, but set a variable if it looks like the variable "o" is used:
159 int varused_test;
160 int varused;
161 
162 // write an array of C characters (adds a null):
write_cstring(const char * w,int length)163 void write_cstring(const char *w, int length) {
164   if (varused_test) {
165     varused = 1;
166     return;
167   }
168   const char *e = w+length;
169   int linelength = 1;
170   putc('\"', code_file);
171   for (; w < e;) {
172     int c = *w++;
173     switch (c) {
174     case '\b': c = 'b'; goto QUOTED;
175     case '\t': c = 't'; goto QUOTED;
176     case '\n': c = 'n'; goto QUOTED;
177     case '\f': c = 'f'; goto QUOTED;
178     case '\r': c = 'r'; goto QUOTED;
179     case '\"':
180     case '\'':
181     case '\\':
182     QUOTED:
183       if (linelength >= 77) {fputs("\\\n",code_file); linelength = 0;}
184       putc('\\', code_file);
185       putc(c, code_file);
186       linelength += 2;
187       break;
188     case '?': // prevent trigraphs by writing ?? as ?\?
189       if (*(w-2) == '?') goto QUOTED;
190       // else fall through:
191     default:
192       if (c >= ' ' && c < 127) {
193 	// a legal ASCII character
194 	if (linelength >= 78) {fputs("\\\n",code_file); linelength = 0;}
195 	putc(c, code_file);
196 	linelength++;
197 	break;
198       }
199       // otherwise we must print it as an octal constant:
200       c &= 255;
201       if (c < 8) {
202 	if (linelength >= 76) {fputs("\\\n",code_file); linelength = 0;}
203 	fprintf(code_file, "\\%o",c);
204 	linelength += 2;
205       } else if (c < 64) {
206 	if (linelength >= 75) {fputs("\\\n",code_file); linelength = 0;}
207 	fprintf(code_file, "\\%o",c);
208 	linelength += 3;
209       } else {
210 	if (linelength >= 74) {fputs("\\\n",code_file); linelength = 0;}
211 	fprintf(code_file, "\\%o",c);
212 	linelength += 4;
213       }
214       // We must not put more numbers after it, because some C compilers
215       // consume them as part of the quoted sequence.  Use string constant
216       // pasting to avoid this:
217       c = *w;
218       if (w < e && ( (c>='0'&&c<='9') || (c>='a'&&c<='f') || (c>='A'&&c<='F') )) {
219 	putc('\"', code_file); linelength++;
220 	if (linelength >= 79) {fputs("\n",code_file); linelength = 0;}
221 	putc('\"', code_file); linelength++;
222       }
223       break;
224     }
225   }
226   putc('\"', code_file);
227 }
228 
229 // write a C string, quoting characters if necessary:
write_cstring(const char * w)230 void write_cstring(const char *w) {write_cstring(w,strlen(w));}
231 
232 // write an array of C binary data (does not add a null):
write_cdata(const char * s,int length)233 void write_cdata(const char *s, int length) {
234   if (varused_test) {
235     varused = 1;
236     return;
237   }
238   if (write_sourceview) {
239     if (length>=0)
240       fprintf(code_file, "{ /* ... %d bytes of binary data... */ }", length);
241     else
242       fprintf(code_file, "{ /* ... binary data... */ }");
243     return;
244   }
245   if (length==-1) {
246     fprintf(code_file, "{ /* ... undefined size binary data... */ }");
247     return;
248   }
249   const unsigned char *w = (const unsigned char *)s;
250   const unsigned char *e = w+length;
251   int linelength = 1;
252   putc('{', code_file);
253   for (; w < e;) {
254     unsigned char c = *w++;
255     if (c>99) linelength += 4;
256     else if (c>9) linelength += 3;
257     else linelength += 2;
258     if (linelength >= 77) {fputs("\n",code_file); linelength = 0;}
259     fprintf(code_file, "%d", c);
260     if (w<e) putc(',', code_file);
261   }
262   putc('}', code_file);
263 }
264 
write_c(const char * format,...)265 void write_c(const char* format,...) {
266   if (varused_test) {
267     varused = 1;
268     return;
269   }
270   va_list args;
271   va_start(args, format);
272   vfprintf(code_file, format, args);
273   va_end(args);
274 }
275 
write_h(const char * format,...)276 void write_h(const char* format,...) {
277   if (varused_test) return;
278   va_list args;
279   va_start(args, format);
280   vfprintf(header_file, format, args);
281   va_end(args);
282 }
283 
284 #include <FL/filename.H>
285 int write_number;
286 int write_sourceview;
287 extern Fl_Widget_Class_Type *current_widget_class;
288 
289 // recursively dump code, putting children between the two parts
290 // of the parent code:
write_code(Fl_Type * p)291 static Fl_Type* write_code(Fl_Type* p) {
292   if (write_sourceview) {
293     p->code_position = (int)ftell(code_file);
294     if (p->header_position_end==-1)
295       p->header_position = (int)ftell(header_file);
296   }
297   // write all code that come before the children code
298   // (but don't write the last comment until the very end)
299   if (!(p==Fl_Type::last && p->is_comment()))
300     p->write_code1();
301   // recursively write the code of all children
302   Fl_Type* q;
303   if (p->is_widget() && p->is_class()) {
304     // Handle widget classes specially
305     for (q = p->next; q && q->level > p->level;) {
306       if (strcmp(q->type_name(), "Function")) q = write_code(q);
307       else {
308         int level = q->level;
309 	do {
310 	  q = q->next;
311 	} while (q && q->level > level);
312       }
313     }
314 
315     // write all code that come after the children
316     p->write_code2();
317 
318     for (q = p->next; q && q->level > p->level;) {
319       if (!strcmp(q->type_name(), "Function")) q = write_code(q);
320       else {
321         int level = q->level;
322 	do {
323 	  q = q->next;
324 	} while (q && q->level > level);
325       }
326     }
327 
328     write_h("};\n");
329     current_widget_class = 0L;
330   } else {
331     for (q = p->next; q && q->level > p->level;) q = write_code(q);
332     // write all code that come after the children
333     p->write_code2();
334   }
335   if (write_sourceview) {
336     p->code_position_end = (int)ftell(code_file);
337     if (p->header_position_end==-1)
338       p->header_position_end = (int)ftell(header_file);
339   }
340   return q;
341 }
342 
343 extern const char* header_file_name;
344 extern Fl_Class_Type *current_class;
345 
write_code(const char * s,const char * t)346 int write_code(const char *s, const char *t) {
347   const char *filemode = "w";
348   if (write_sourceview)
349     filemode = "wb";
350   write_number++;
351   delete id_root; id_root = 0;
352   indentation = 0;
353   current_class = 0L;
354   current_widget_class = 0L;
355   if (!s) code_file = stdout;
356   else {
357     FILE *f = fl_fopen(s, filemode);
358     if (!f) return 0;
359     code_file = f;
360   }
361   if (!t) header_file = stdout;
362   else {
363     FILE *f = fl_fopen(t, filemode);
364     if (!f) {fclose(code_file); return 0;}
365     header_file = f;
366   }
367   // if the first entry in the Type tree is a comment, then it is probably
368   // a copyright notice. We print that before anything else in the file!
369   Fl_Type* first_type = Fl_Type::first;
370   if (first_type && first_type->is_comment()) {
371     if (write_sourceview) {
372       first_type->code_position = (int)ftell(code_file);
373       first_type->header_position = (int)ftell(header_file);
374     }
375     // it is ok to write non-recusive code here, because comments have no children or code2 blocks
376     first_type->write_code1();
377     if (write_sourceview) {
378       first_type->code_position_end = (int)ftell(code_file);
379       first_type->header_position_end = (int)ftell(header_file);
380     }
381     first_type = first_type->next;
382   }
383 
384   const char *hdr = "\
385 // generated by Fast Light User Interface Designer (fluid) version %.4f\n\n";
386   fprintf(header_file, hdr, FL_VERSION);
387   fprintf(code_file, hdr, FL_VERSION);
388 
389   {char define_name[102];
390   const char* a = fl_filename_name(t);
391   char* b = define_name;
392   if (!isalpha(*a)) {*b++ = '_';}
393   while (*a) {*b++ = isalnum(*a) ? *a : '_'; a++;}
394   *b = 0;
395   fprintf(header_file, "#ifndef %s\n", define_name);
396   fprintf(header_file, "#define %s\n", define_name);
397   }
398 
399   write_declare("#include <FL/Fl.H>");
400   if (i18n_type && i18n_include[0]) {
401     if (i18n_include[0] != '<' &&
402         i18n_include[0] != '\"')
403       write_c("#include \"%s\"\n", i18n_include);
404     else
405       write_c("#include %s\n", i18n_include);
406     if (i18n_type == 2) {
407       if (i18n_file[0]) write_c("extern nl_catd %s;\n", i18n_file);
408       else {
409         write_c("// Initialize I18N stuff now for menus...\n");
410         write_c("#include <locale.h>\n");
411 	write_c("static char *_locale = setlocale(LC_MESSAGES, \"\");\n");
412         write_c("static nl_catd _catalog = catopen(\"%s\", 0);\n",
413                    i18n_program);
414       }
415     }
416   }
417   if (t && include_H_from_C) {
418     if (*header_file_name == '.' && strchr(header_file_name, '/') == NULL) {
419       write_c("#include \"%s\"\n", fl_filename_name(t));
420     } else {
421       write_c("#include \"%s\"\n", t);
422     }
423   }
424   for (Fl_Type* p = first_type; p;) {
425     // write all static data for this & all children first
426     if (write_sourceview) p->header_position = (int)ftell(header_file);
427     p->write_static();
428     if (write_sourceview) {
429       p->header_position_end = (int)ftell(header_file);
430       if (p->header_position==p->header_position_end) p->header_position_end = -1;
431     }
432     for (Fl_Type* q = p->next; q && q->level > p->level; q = q->next) {
433       if (write_sourceview) q->header_position = (int)ftell(header_file);
434       q->write_static();
435       if (write_sourceview) {
436         q->header_position_end = (int)ftell(header_file);
437         if (q->header_position==q->header_position_end) q->header_position_end = -1;
438       }
439     }
440     // then write the nested code:
441     p = write_code(p);
442   }
443 
444   delete included_root; included_root = 0;
445 
446   if (!s) return 1;
447 
448   fprintf(header_file, "#endif\n");
449 
450   Fl_Type* last_type = Fl_Type::last;
451   if (last_type && last_type->is_comment()) {
452     if (write_sourceview) {
453       last_type->code_position = (int)ftell(code_file);
454       last_type->header_position = (int)ftell(header_file);
455     }
456     last_type->write_code1();
457     if (write_sourceview) {
458       last_type->code_position_end = (int)ftell(code_file);
459       last_type->header_position_end = (int)ftell(header_file);
460     }
461   }
462 
463   int x = fclose(code_file);
464   code_file = 0;
465   int y = fclose(header_file);
466   header_file = 0;
467   return x >= 0 && y >= 0;
468 }
469 
write_strings(const char * sfile)470 int write_strings(const char *sfile) {
471   FILE *fp = fl_fopen(sfile, "w");
472   Fl_Type *p;
473   Fl_Widget_Type *w;
474   int i;
475 
476   if (!fp) return 1;
477 
478   switch (i18n_type) {
479   case 0 : /* None, just put static text out */
480       fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n",
481 	      FL_VERSION);
482       for (p = Fl_Type::first; p; p = p->next) {
483         if (p->is_widget()) {
484 	  w = (Fl_Widget_Type *)p;
485 
486 	  if (w->label()) {
487 	    for (const char *s = w->label(); *s; s ++)
488 	      if (*s < 32 || *s > 126 || *s == '\"')
489 		fprintf(fp, "\\%03o", *s);
490 	      else
491 		putc(*s, fp);
492             putc('\n', fp);
493 	  }
494 
495 	  if (w->tooltip()) {
496 	    for (const char *s = w->tooltip(); *s; s ++)
497 	      if (*s < 32 || *s > 126 || *s == '\"')
498 		fprintf(fp, "\\%03o", *s);
499 	      else
500 		putc(*s, fp);
501             putc('\n', fp);
502 	  }
503 	}
504       }
505       break;
506   case 1 : /* GNU gettext, put a .po file out */
507       fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n",
508 	      FL_VERSION);
509       for (p = Fl_Type::first; p; p = p->next) {
510         if (p->is_widget()) {
511 	  w = (Fl_Widget_Type *)p;
512 
513 	  if (w->label()) {
514 	    const char *s;
515 
516 	    fputs("msgid \"", fp);
517 	    for (s = w->label(); *s; s ++)
518 	      if (*s < 32 || *s > 126 || *s == '\"')
519 		fprintf(fp, "\\%03o", *s);
520 	      else
521 		putc(*s, fp);
522             fputs("\"\n", fp);
523 
524 	    fputs("msgstr \"", fp);
525 	    for (s = w->label(); *s; s ++)
526 	      if (*s < 32 || *s > 126 || *s == '\"')
527 		fprintf(fp, "\\%03o", *s);
528 	      else
529 		putc(*s, fp);
530             fputs("\"\n", fp);
531 	  }
532 
533 	  if (w->tooltip()) {
534 	    const char *s;
535 
536 	    fputs("msgid \"", fp);
537 	    for (s = w->tooltip(); *s; s ++)
538 	      if (*s < 32 || *s > 126 || *s == '\"')
539 		fprintf(fp, "\\%03o", *s);
540 	      else
541 		putc(*s, fp);
542             fputs("\"\n", fp);
543 
544 	    fputs("msgstr \"", fp);
545 	    for (s = w->tooltip(); *s; s ++)
546 	      if (*s < 32 || *s > 126 || *s == '\"')
547 		fprintf(fp, "\\%03o", *s);
548 	      else
549 		putc(*s, fp);
550             fputs("\"\n", fp);
551 	  }
552 	}
553       }
554       break;
555   case 2 : /* POSIX catgets, put a .msg file out */
556       fprintf(fp, "$ generated by Fast Light User Interface Designer (fluid) version %.4f\n",
557 	      FL_VERSION);
558       fprintf(fp, "$set %s\n", i18n_set);
559       fputs("$quote \"\n", fp);
560 
561       for (i = 1, p = Fl_Type::first; p; p = p->next) {
562         if (p->is_widget()) {
563 	  w = (Fl_Widget_Type *)p;
564 
565 	  if (w->label()) {
566 	    fprintf(fp, "%d \"", i ++);
567 	    for (const char *s = w->label(); *s; s ++)
568 	      if (*s < 32 || *s > 126 || *s == '\"')
569 		fprintf(fp, "\\%03o", *s);
570 	      else
571 		putc(*s, fp);
572             fputs("\"\n", fp);
573 	  }
574 
575 	  if (w->tooltip()) {
576 	    fprintf(fp, "%d \"", i ++);
577 	    for (const char *s = w->tooltip(); *s; s ++)
578 	      if (*s < 32 || *s > 126 || *s == '\"')
579 		fprintf(fp, "\\%03o", *s);
580 	      else
581 		putc(*s, fp);
582             fputs("\"\n", fp);
583 	  }
584 	}
585       }
586       break;
587   }
588 
589   return fclose(fp);
590 }
591 
592 ////////////////////////////////////////////////////////////////
593 
write_static()594 void Fl_Type::write_static() {}
write_code1()595 void Fl_Type::write_code1() {
596   write_h("// Header for %s\n", title());
597   write_c("// Code for %s\n", title());
598 }
write_code2()599 void Fl_Type::write_code2() {}
600 
601 //
602 // End of "$Id: code.cxx 7903 2010-11-28 21:06:39Z matt $".
603 //
604