Bump version.
[ntk.git] / fluid / file.cxx
blobcd2c6c519bab448f3ce60854a32e1a95214e75e1
1 //
2 // "$Id: file.cxx 7903 2010-11-28 21:06:39Z matt $"
3 //
4 // Fluid file routines for the Fast Light Tool Kit (FLTK).
5 //
6 // You may find the basic read_* and write_* routines to
7 // be useful for other programs. I have used them many times.
8 // They are somewhat similar to tcl, using matching { and }
9 // to quote strings.
11 // Copyright 1998-2010 by Bill Spitzak and others.
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Library General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Library General Public License for more details.
23 // You should have received a copy of the GNU Library General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 // USA.
28 // Please report all bugs and problems on the following page:
30 // http://www.fltk.org/str.php
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include "../src/flstring.h"
36 #include <stdarg.h>
37 #include "alignment_panel.h"
39 ////////////////////////////////////////////////////////////////
40 // BASIC FILE WRITING:
42 static FILE *fout;
44 int open_write(const char *s) {
45 if (!s) {fout = stdout; return 1;}
46 FILE *f = fl_fopen(s,"w");
47 if (!f) return 0;
48 fout = f;
49 return 1;
52 int close_write() {
53 if (fout != stdout) {
54 int x = fclose(fout);
55 fout = stdout;
56 return x >= 0;
58 return 1;
61 static int needspace;
62 int is_id(char); // in code.C
64 // write a string, quoting characters if necessary:
65 void write_word(const char *w) {
66 if (needspace) putc(' ', fout);
67 needspace = 1;
68 if (!w || !*w) {fprintf(fout,"{}"); return;}
69 const char *p;
70 // see if it is a single word:
71 for (p = w; is_id(*p); p++) ;
72 if (!*p) {fprintf(fout,"%s",w); return;}
73 // see if there are matching braces:
74 int n = 0;
75 for (p = w; *p; p++) {
76 if (*p == '{') n++;
77 else if (*p == '}') {n--; if (n<0) break;}
79 int mismatched = (n != 0);
80 // write out brace-quoted string:
81 putc('{', fout);
82 for (; *w; w++) {
83 switch (*w) {
84 case '{':
85 case '}':
86 if (!mismatched) break;
87 case '\\':
88 case '#':
89 putc('\\',fout);
90 break;
92 putc(*w,fout);
94 putc('}', fout);
97 // write an arbitrary formatted word, or a comment, etc:
98 void write_string(const char *format, ...) {
99 va_list args;
100 va_start(args, format);
101 if (needspace) fputc(' ',fout);
102 vfprintf(fout, format, args);
103 va_end(args);
104 needspace = !isspace(format[strlen(format)-1] & 255);
107 // start a new line and indent it for a given nesting level:
108 void write_indent(int n) {
109 fputc('\n',fout);
110 while (n--) {fputc(' ',fout); fputc(' ',fout);}
111 needspace = 0;
114 // write a '{' at the given indenting level:
115 void write_open(int) {
116 if (needspace) fputc(' ',fout);
117 fputc('{',fout);
118 needspace = 0;
121 // write a '}' at the given indenting level:
122 void write_close(int n) {
123 if (needspace) write_indent(n);
124 fputc('}',fout);
125 needspace = 1;
128 ////////////////////////////////////////////////////////////////
129 // BASIC FILE READING:
131 static FILE *fin;
132 static int lineno;
133 static const char *fname;
135 int open_read(const char *s) {
136 lineno = 1;
137 if (!s) {fin = stdin; fname = "stdin"; return 1;}
138 FILE *f = fl_fopen(s,"r");
139 if (!f) return 0;
140 fin = f;
141 fname = s;
142 return 1;
145 int close_read() {
146 if (fin != stdin) {
147 int x = fclose(fin);
148 fin = 0;
149 return x >= 0;
151 return 1;
154 #include <FL/fl_message.H>
156 void read_error(const char *format, ...) {
157 va_list args;
158 va_start(args, format);
159 if (!fin) {
160 char buffer[1024];
161 vsnprintf(buffer, sizeof(buffer), format, args);
162 fl_message("%s", buffer);
163 } else {
164 fprintf(stderr, "%s:%d: ", fname, lineno);
165 vfprintf(stderr, format, args);
166 fprintf(stderr, "\n");
168 va_end(args);
171 static int hexdigit(int x) {
172 if (isdigit(x)) return x-'0';
173 if (isupper(x)) return x-'A'+10;
174 if (islower(x)) return x-'a'+10;
175 return 20;
179 static int read_quoted() { // read whatever character is after a \ .
180 int c,d,x;
181 switch(c = fgetc(fin)) {
182 case '\n': lineno++; return -1;
183 case 'a' : return('\a');
184 case 'b' : return('\b');
185 case 'f' : return('\f');
186 case 'n' : return('\n');
187 case 'r' : return('\r');
188 case 't' : return('\t');
189 case 'v' : return('\v');
190 case 'x' : /* read hex */
191 for (c=x=0; x<3; x++) {
192 int ch = fgetc(fin);
193 d = hexdigit(ch);
194 if (d > 15) {ungetc(ch,fin); break;}
195 c = (c<<4)+d;
197 break;
198 default: /* read octal */
199 if (c<'0' || c>'7') break;
200 c -= '0';
201 for (x=0; x<2; x++) {
202 int ch = fgetc(fin);
203 d = hexdigit(ch);
204 if (d>7) {ungetc(ch,fin); break;}
205 c = (c<<3)+d;
207 break;
209 return(c);
212 // return a word read from the file, or NULL at the EOF:
213 // This will skip all comments (# to end of line), and evaluate
214 // all \xxx sequences and use \ at the end of line to remove the newline.
215 // A word is any one of:
216 // a continuous string of non-space chars except { and } and #
217 // everything between matching {...} (unless wantbrace != 0)
218 // the characters '{' and '}'
220 static char *buffer;
221 static int buflen;
222 static void expand_buffer(int length) {
223 if (length >= buflen) {
224 if (!buflen) {
225 buflen = length+1;
226 buffer = (char*)malloc(buflen);
227 } else {
228 buflen = 2*buflen;
229 if (length >= buflen) buflen = length+1;
230 buffer = (char *)realloc((void *)buffer,buflen);
235 const char *read_word(int wantbrace) {
236 int x;
238 // skip all the whitespace before it:
239 for (;;) {
240 x = getc(fin);
241 if (x < 0 && feof(fin)) { // eof
242 return 0;
243 } else if (x == '#') { // comment
244 do x = getc(fin); while (x >= 0 && x != '\n');
245 lineno++;
246 continue;
247 } else if (x == '\n') {
248 lineno++;
249 } else if (!isspace(x & 255)) {
250 break;
254 expand_buffer(100);
256 if (x == '{' && !wantbrace) {
258 // read in whatever is between braces
259 int length = 0;
260 int nesting = 0;
261 for (;;) {
262 x = getc(fin);
263 if (x<0) {read_error("Missing '}'"); break;}
264 else if (x == '#') { // embedded comment
265 do x = getc(fin); while (x >= 0 && x != '\n');
266 lineno++;
267 continue;
268 } else if (x == '\n') lineno++;
269 else if (x == '\\') {x = read_quoted(); if (x<0) continue;}
270 else if (x == '{') nesting++;
271 else if (x == '}') {if (!nesting--) break;}
272 buffer[length++] = x;
273 expand_buffer(length);
275 buffer[length] = 0;
276 return buffer;
278 } else if (x == '{' || x == '}') {
279 // all the punctuation is a word:
280 buffer[0] = x;
281 buffer[1] = 0;
282 return buffer;
284 } else {
286 // read in an unquoted word:
287 int length = 0;
288 for (;;) {
289 if (x == '\\') {x = read_quoted(); if (x<0) continue;}
290 else if (x<0 || isspace(x & 255) || x=='{' || x=='}' || x=='#') break;
291 buffer[length++] = x;
292 expand_buffer(length);
293 x = getc(fin);
295 ungetc(x, fin);
296 buffer[length] = 0;
297 return buffer;
302 ////////////////////////////////////////////////////////////////
304 #include <FL/Fl.H>
305 #include "Fl_Widget_Type.h"
307 // global int variables:
308 extern int i18n_type;
309 extern const char* i18n_include;
310 extern const char* i18n_function;
311 extern const char* i18n_file;
312 extern const char* i18n_set;
315 extern int header_file_set;
316 extern int code_file_set;
317 extern const char* header_file_name;
318 extern const char* code_file_name;
320 int write_file(const char *filename, int selected_only) {
321 if (!open_write(filename)) return 0;
322 write_string("# data file for the Fltk User Interface Designer (fluid)\n"
323 "version %.4f",FL_VERSION);
324 if(!include_H_from_C)
325 write_string("\ndo_not_include_H_from_C");
326 if(use_FL_COMMAND)
327 write_string("\nuse_FL_COMMAND");
328 if (i18n_type) {
329 write_string("\ni18n_type %d", i18n_type);
330 write_string("\ni18n_include %s", i18n_include);
331 switch (i18n_type) {
332 case 1 : /* GNU gettext */
333 write_string("\ni18n_function %s", i18n_function);
334 break;
335 case 2 : /* POSIX catgets */
336 if (i18n_file[0]) write_string("\ni18n_file %s", i18n_file);
337 write_string("\ni18n_set %s", i18n_set);
338 break;
341 if (!selected_only) {
342 write_string("\nheader_name"); write_word(header_file_name);
343 write_string("\ncode_name"); write_word(code_file_name);
345 for (Fl_Type *p = Fl_Type::first; p;) {
346 if (!selected_only || p->selected) {
347 p->write();
348 write_string("\n");
349 int q = p->level;
350 for (p = p->next; p && p->level > q; p = p->next);
351 } else {
352 p = p->next;
355 return close_write();
358 ////////////////////////////////////////////////////////////////
359 // read all the objects out of the input file:
361 double read_version;
363 extern Fl_Type *Fl_Type_make(const char *tn);
365 static void read_children(Fl_Type *p, int paste) {
366 Fl_Type::current = p;
367 for (;;) {
368 const char *c = read_word();
369 REUSE_C:
370 if (!c) {
371 if (p && !paste) read_error("Missing '}'");
372 break;
375 if (!strcmp(c,"}")) {
376 if (!p) read_error("Unexpected '}'");
377 break;
380 if (!strcmp(c,"version")) {
381 c = read_word();
382 read_version = strtod(c,0);
383 if (read_version<=0 || read_version>FL_VERSION)
384 read_error("unknown version '%s'",c);
385 continue;
388 // back compatibility with Vincent Penne's original class code:
389 if (!p && !strcmp(c,"define_in_struct")) {
390 Fl_Type *t = Fl_Type_make("class");
391 t->name(read_word());
392 Fl_Type::current = p = t;
393 paste = 1; // stops "missing }" error
394 continue;
397 if (!strcmp(c,"do_not_include_H_from_C")) {
398 include_H_from_C=0;
399 goto CONTINUE;
401 if (!strcmp(c,"use_FL_COMMAND")) {
402 use_FL_COMMAND=1;
403 goto CONTINUE;
405 if (!strcmp(c,"i18n_type")) {
406 i18n_type = atoi(read_word());
407 goto CONTINUE;
409 if (!strcmp(c,"i18n_function")) {
410 i18n_function = strdup(read_word());
411 goto CONTINUE;
413 if (!strcmp(c,"i18n_file")) {
414 i18n_file = strdup(read_word());
415 goto CONTINUE;
417 if (!strcmp(c,"i18n_set")) {
418 i18n_set = strdup(read_word());
419 goto CONTINUE;
421 if (!strcmp(c,"i18n_include")) {
422 i18n_include = strdup(read_word());
423 goto CONTINUE;
425 if (!strcmp(c,"i18n_type"))
427 i18n_type = atoi(read_word());
428 goto CONTINUE;
430 if (!strcmp(c,"i18n_type"))
432 i18n_type = atoi(read_word());
433 goto CONTINUE;
435 if (!strcmp(c,"header_name")) {
436 if (!header_file_set) header_file_name = strdup(read_word());
437 else read_word();
438 goto CONTINUE;
441 if (!strcmp(c,"code_name")) {
442 if (!code_file_set) code_file_name = strdup(read_word());
443 else read_word();
444 goto CONTINUE;
447 if (!strcmp(c, "snap") || !strcmp(c, "gridx") || !strcmp(c, "gridy")) {
448 // grid settings are now global
449 read_word();
450 goto CONTINUE;
453 {Fl_Type *t = Fl_Type_make(c);
454 if (!t) {
455 read_error("Unknown word \"%s\"", c);
456 continue;
458 t->name(read_word());
460 c = read_word(1);
461 if (strcmp(c,"{") && t->is_class()) { // <prefix> <name>
462 ((Fl_Class_Type*)t)->prefix(t->name());
463 t->name(c);
464 c = read_word(1);
467 if (strcmp(c,"{")) {
468 read_error("Missing property list for %s\n",t->title());
469 goto REUSE_C;
472 t->open_ = 0;
473 for (;;) {
474 const char *cc = read_word();
475 if (!cc || !strcmp(cc,"}")) break;
476 t->read_property(cc);
479 if (!t->is_parent()) continue;
480 c = read_word(1);
481 if (strcmp(c,"{")) {
482 read_error("Missing child list for %s\n",t->title());
483 goto REUSE_C;
485 read_children(t, 0);}
486 Fl_Type::current = p;
487 CONTINUE:;
491 extern void deselect();
493 int read_file(const char *filename, int merge) {
494 Fl_Type *o;
495 read_version = 0.0;
496 if (!open_read(filename)) return 0;
497 if (merge) deselect(); else delete_all();
498 read_children(Fl_Type::current, merge);
499 Fl_Type::current = 0;
500 // Force menu items to be rebuilt...
501 for (o = Fl_Type::first; o; o = o->next)
502 if (o->is_menu_button()) o->add_child(0,0);
503 for (o = Fl_Type::first; o; o = o->next)
504 if (o->selected) {Fl_Type::current = o; break;}
505 selection_changed(Fl_Type::current);
506 return close_read();
510 // End of "$Id: file.cxx 7903 2010-11-28 21:06:39Z matt $".