file.cxx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. //
  2. // "$Id: file.cxx 8864 2011-07-19 04:49:30Z greg.ercolano $"
  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.
  10. //
  11. // Copyright 1998-2010 by Bill Spitzak and others.
  12. //
  13. // This library is free software. Distribution and use rights are outlined in
  14. // the file "COPYING" which should have been included with this file. If this
  15. // file is missing or damaged, see the license at:
  16. //
  17. // http://www.fltk.org/COPYING.php
  18. //
  19. // Please report all bugs and problems on the following page:
  20. //
  21. // http://www.fltk.org/str.php
  22. //
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include "../src/flstring.h"
  26. #include <stdarg.h>
  27. #include "alignment_panel.h"
  28. ////////////////////////////////////////////////////////////////
  29. // BASIC FILE WRITING:
  30. static FILE *fout;
  31. int open_write(const char *s) {
  32. if (!s) {fout = stdout; return 1;}
  33. FILE *f = fl_fopen(s,"w");
  34. if (!f) return 0;
  35. fout = f;
  36. return 1;
  37. }
  38. int close_write() {
  39. if (fout != stdout) {
  40. int x = fclose(fout);
  41. fout = stdout;
  42. return x >= 0;
  43. }
  44. return 1;
  45. }
  46. static int needspace;
  47. int is_id(char); // in code.C
  48. // write a string, quoting characters if necessary:
  49. void write_word(const char *w) {
  50. if (needspace) putc(' ', fout);
  51. needspace = 1;
  52. if (!w || !*w) {fprintf(fout,"{}"); return;}
  53. const char *p;
  54. // see if it is a single word:
  55. for (p = w; is_id(*p); p++) ;
  56. if (!*p) {fprintf(fout,"%s",w); return;}
  57. // see if there are matching braces:
  58. int n = 0;
  59. for (p = w; *p; p++) {
  60. if (*p == '{') n++;
  61. else if (*p == '}') {n--; if (n<0) break;}
  62. }
  63. int mismatched = (n != 0);
  64. // write out brace-quoted string:
  65. putc('{', fout);
  66. for (; *w; w++) {
  67. switch (*w) {
  68. case '{':
  69. case '}':
  70. if (!mismatched) break;
  71. case '\\':
  72. case '#':
  73. putc('\\',fout);
  74. break;
  75. }
  76. putc(*w,fout);
  77. }
  78. putc('}', fout);
  79. }
  80. // write an arbitrary formatted word, or a comment, etc:
  81. void write_string(const char *format, ...) {
  82. va_list args;
  83. va_start(args, format);
  84. if (needspace) fputc(' ',fout);
  85. vfprintf(fout, format, args);
  86. va_end(args);
  87. needspace = !isspace(format[strlen(format)-1] & 255);
  88. }
  89. // start a new line and indent it for a given nesting level:
  90. void write_indent(int n) {
  91. fputc('\n',fout);
  92. while (n--) {fputc(' ',fout); fputc(' ',fout);}
  93. needspace = 0;
  94. }
  95. // write a '{' at the given indenting level:
  96. void write_open(int) {
  97. if (needspace) fputc(' ',fout);
  98. fputc('{',fout);
  99. needspace = 0;
  100. }
  101. // write a '}' at the given indenting level:
  102. void write_close(int n) {
  103. if (needspace) write_indent(n);
  104. fputc('}',fout);
  105. needspace = 1;
  106. }
  107. ////////////////////////////////////////////////////////////////
  108. // BASIC FILE READING:
  109. static FILE *fin;
  110. static int lineno;
  111. static const char *fname;
  112. int open_read(const char *s) {
  113. lineno = 1;
  114. if (!s) {fin = stdin; fname = "stdin"; return 1;}
  115. FILE *f = fl_fopen(s,"r");
  116. if (!f) return 0;
  117. fin = f;
  118. fname = s;
  119. return 1;
  120. }
  121. int close_read() {
  122. if (fin != stdin) {
  123. int x = fclose(fin);
  124. fin = 0;
  125. return x >= 0;
  126. }
  127. return 1;
  128. }
  129. #include <FL/fl_message.H>
  130. void read_error(const char *format, ...) {
  131. va_list args;
  132. va_start(args, format);
  133. if (!fin) {
  134. char buffer[1024];
  135. vsnprintf(buffer, sizeof(buffer), format, args);
  136. fl_message("%s", buffer);
  137. } else {
  138. fprintf(stderr, "%s:%d: ", fname, lineno);
  139. vfprintf(stderr, format, args);
  140. fprintf(stderr, "\n");
  141. }
  142. va_end(args);
  143. }
  144. static int hexdigit(int x) {
  145. if (isdigit(x)) return x-'0';
  146. if (isupper(x)) return x-'A'+10;
  147. if (islower(x)) return x-'a'+10;
  148. return 20;
  149. }
  150. static int read_quoted() { // read whatever character is after a \ .
  151. int c,d,x;
  152. switch(c = fgetc(fin)) {
  153. case '\n': lineno++; return -1;
  154. case 'a' : return('\a');
  155. case 'b' : return('\b');
  156. case 'f' : return('\f');
  157. case 'n' : return('\n');
  158. case 'r' : return('\r');
  159. case 't' : return('\t');
  160. case 'v' : return('\v');
  161. case 'x' : /* read hex */
  162. for (c=x=0; x<3; x++) {
  163. int ch = fgetc(fin);
  164. d = hexdigit(ch);
  165. if (d > 15) {ungetc(ch,fin); break;}
  166. c = (c<<4)+d;
  167. }
  168. break;
  169. default: /* read octal */
  170. if (c<'0' || c>'7') break;
  171. c -= '0';
  172. for (x=0; x<2; x++) {
  173. int ch = fgetc(fin);
  174. d = hexdigit(ch);
  175. if (d>7) {ungetc(ch,fin); break;}
  176. c = (c<<3)+d;
  177. }
  178. break;
  179. }
  180. return(c);
  181. }
  182. // return a word read from the file, or NULL at the EOF:
  183. // This will skip all comments (# to end of line), and evaluate
  184. // all \xxx sequences and use \ at the end of line to remove the newline.
  185. // A word is any one of:
  186. // a continuous string of non-space chars except { and } and #
  187. // everything between matching {...} (unless wantbrace != 0)
  188. // the characters '{' and '}'
  189. static char *buffer;
  190. static int buflen;
  191. static void expand_buffer(int length) {
  192. if (length >= buflen) {
  193. if (!buflen) {
  194. buflen = length+1;
  195. buffer = (char*)malloc(buflen);
  196. } else {
  197. buflen = 2*buflen;
  198. if (length >= buflen) buflen = length+1;
  199. buffer = (char *)realloc((void *)buffer,buflen);
  200. }
  201. }
  202. }
  203. const char *read_word(int wantbrace) {
  204. int x;
  205. // skip all the whitespace before it:
  206. for (;;) {
  207. x = getc(fin);
  208. if (x < 0 && feof(fin)) { // eof
  209. return 0;
  210. } else if (x == '#') { // comment
  211. do x = getc(fin); while (x >= 0 && x != '\n');
  212. lineno++;
  213. continue;
  214. } else if (x == '\n') {
  215. lineno++;
  216. } else if (!isspace(x & 255)) {
  217. break;
  218. }
  219. }
  220. expand_buffer(100);
  221. if (x == '{' && !wantbrace) {
  222. // read in whatever is between braces
  223. int length = 0;
  224. int nesting = 0;
  225. for (;;) {
  226. x = getc(fin);
  227. if (x<0) {read_error("Missing '}'"); break;}
  228. else if (x == '#') { // embedded comment
  229. do x = getc(fin); while (x >= 0 && x != '\n');
  230. lineno++;
  231. continue;
  232. } else if (x == '\n') lineno++;
  233. else if (x == '\\') {x = read_quoted(); if (x<0) continue;}
  234. else if (x == '{') nesting++;
  235. else if (x == '}') {if (!nesting--) break;}
  236. buffer[length++] = x;
  237. expand_buffer(length);
  238. }
  239. buffer[length] = 0;
  240. return buffer;
  241. } else if (x == '{' || x == '}') {
  242. // all the punctuation is a word:
  243. buffer[0] = x;
  244. buffer[1] = 0;
  245. return buffer;
  246. } else {
  247. // read in an unquoted word:
  248. int length = 0;
  249. for (;;) {
  250. if (x == '\\') {x = read_quoted(); if (x<0) continue;}
  251. else if (x<0 || isspace(x & 255) || x=='{' || x=='}' || x=='#') break;
  252. buffer[length++] = x;
  253. expand_buffer(length);
  254. x = getc(fin);
  255. }
  256. ungetc(x, fin);
  257. buffer[length] = 0;
  258. return buffer;
  259. }
  260. }
  261. ////////////////////////////////////////////////////////////////
  262. #include <FL/Fl.H>
  263. #include "Fl_Widget_Type.h"
  264. // global int variables:
  265. extern int i18n_type;
  266. extern const char* i18n_include;
  267. extern const char* i18n_function;
  268. extern const char* i18n_file;
  269. extern const char* i18n_set;
  270. extern int header_file_set;
  271. extern int code_file_set;
  272. extern const char* header_file_name;
  273. extern const char* code_file_name;
  274. int write_file(const char *filename, int selected_only) {
  275. if (!open_write(filename)) return 0;
  276. write_string("# data file for the Fltk User Interface Designer (fluid)\n"
  277. "version %.4f",FL_VERSION);
  278. if(!include_H_from_C)
  279. write_string("\ndo_not_include_H_from_C");
  280. if(use_FL_COMMAND)
  281. write_string("\nuse_FL_COMMAND");
  282. if (i18n_type) {
  283. write_string("\ni18n_type %d", i18n_type);
  284. write_string("\ni18n_include %s", i18n_include);
  285. switch (i18n_type) {
  286. case 1 : /* GNU gettext */
  287. write_string("\ni18n_function %s", i18n_function);
  288. break;
  289. case 2 : /* POSIX catgets */
  290. if (i18n_file[0]) write_string("\ni18n_file %s", i18n_file);
  291. write_string("\ni18n_set %s", i18n_set);
  292. break;
  293. }
  294. }
  295. if (!selected_only) {
  296. write_string("\nheader_name"); write_word(header_file_name);
  297. write_string("\ncode_name"); write_word(code_file_name);
  298. }
  299. for (Fl_Type *p = Fl_Type::first; p;) {
  300. if (!selected_only || p->selected) {
  301. p->write();
  302. write_string("\n");
  303. int q = p->level;
  304. for (p = p->next; p && p->level > q; p = p->next);
  305. } else {
  306. p = p->next;
  307. }
  308. }
  309. return close_write();
  310. }
  311. ////////////////////////////////////////////////////////////////
  312. // read all the objects out of the input file:
  313. void read_fdesign();
  314. double read_version;
  315. extern Fl_Type *Fl_Type_make(const char *tn);
  316. static void read_children(Fl_Type *p, int paste) {
  317. Fl_Type::current = p;
  318. for (;;) {
  319. const char *c = read_word();
  320. REUSE_C:
  321. if (!c) {
  322. if (p && !paste) read_error("Missing '}'");
  323. break;
  324. }
  325. if (!strcmp(c,"}")) {
  326. if (!p) read_error("Unexpected '}'");
  327. break;
  328. }
  329. // this is the first word in a .fd file:
  330. if (!strcmp(c,"Magic:")) {
  331. read_fdesign();
  332. return;
  333. }
  334. if (!strcmp(c,"version")) {
  335. c = read_word();
  336. read_version = strtod(c,0);
  337. if (read_version<=0 || read_version>FL_VERSION)
  338. read_error("unknown version '%s'",c);
  339. continue;
  340. }
  341. // back compatibility with Vincent Penne's original class code:
  342. if (!p && !strcmp(c,"define_in_struct")) {
  343. Fl_Type *t = Fl_Type_make("class");
  344. t->name(read_word());
  345. Fl_Type::current = p = t;
  346. paste = 1; // stops "missing }" error
  347. continue;
  348. }
  349. if (!strcmp(c,"do_not_include_H_from_C")) {
  350. include_H_from_C=0;
  351. goto CONTINUE;
  352. }
  353. if (!strcmp(c,"use_FL_COMMAND")) {
  354. use_FL_COMMAND=1;
  355. goto CONTINUE;
  356. }
  357. if (!strcmp(c,"i18n_type")) {
  358. i18n_type = atoi(read_word());
  359. goto CONTINUE;
  360. }
  361. if (!strcmp(c,"i18n_function")) {
  362. i18n_function = strdup(read_word());
  363. goto CONTINUE;
  364. }
  365. if (!strcmp(c,"i18n_file")) {
  366. i18n_file = strdup(read_word());
  367. goto CONTINUE;
  368. }
  369. if (!strcmp(c,"i18n_set")) {
  370. i18n_set = strdup(read_word());
  371. goto CONTINUE;
  372. }
  373. if (!strcmp(c,"i18n_include")) {
  374. i18n_include = strdup(read_word());
  375. goto CONTINUE;
  376. }
  377. if (!strcmp(c,"i18n_type"))
  378. {
  379. i18n_type = atoi(read_word());
  380. goto CONTINUE;
  381. }
  382. if (!strcmp(c,"i18n_type"))
  383. {
  384. i18n_type = atoi(read_word());
  385. goto CONTINUE;
  386. }
  387. if (!strcmp(c,"header_name")) {
  388. if (!header_file_set) header_file_name = strdup(read_word());
  389. else read_word();
  390. goto CONTINUE;
  391. }
  392. if (!strcmp(c,"code_name")) {
  393. if (!code_file_set) code_file_name = strdup(read_word());
  394. else read_word();
  395. goto CONTINUE;
  396. }
  397. if (!strcmp(c, "snap") || !strcmp(c, "gridx") || !strcmp(c, "gridy")) {
  398. // grid settings are now global
  399. read_word();
  400. goto CONTINUE;
  401. }
  402. {Fl_Type *t = Fl_Type_make(c);
  403. if (!t) {
  404. read_error("Unknown word \"%s\"", c);
  405. continue;
  406. }
  407. t->name(read_word());
  408. c = read_word(1);
  409. if (strcmp(c,"{") && t->is_class()) { // <prefix> <name>
  410. ((Fl_Class_Type*)t)->prefix(t->name());
  411. t->name(c);
  412. c = read_word(1);
  413. }
  414. if (strcmp(c,"{")) {
  415. read_error("Missing property list for %s\n",t->title());
  416. goto REUSE_C;
  417. }
  418. t->open_ = 0;
  419. for (;;) {
  420. const char *cc = read_word();
  421. if (!cc || !strcmp(cc,"}")) break;
  422. t->read_property(cc);
  423. }
  424. if (!t->is_parent()) continue;
  425. c = read_word(1);
  426. if (strcmp(c,"{")) {
  427. read_error("Missing child list for %s\n",t->title());
  428. goto REUSE_C;
  429. }
  430. read_children(t, 0);}
  431. Fl_Type::current = p;
  432. CONTINUE:;
  433. }
  434. }
  435. extern void deselect();
  436. int read_file(const char *filename, int merge) {
  437. Fl_Type *o;
  438. read_version = 0.0;
  439. if (!open_read(filename)) return 0;
  440. if (merge) deselect(); else delete_all();
  441. read_children(Fl_Type::current, merge);
  442. Fl_Type::current = 0;
  443. // Force menu items to be rebuilt...
  444. for (o = Fl_Type::first; o; o = o->next)
  445. if (o->is_menu_button()) o->add_child(0,0);
  446. for (o = Fl_Type::first; o; o = o->next)
  447. if (o->selected) {Fl_Type::current = o; break;}
  448. selection_changed(Fl_Type::current);
  449. return close_read();
  450. }
  451. ////////////////////////////////////////////////////////////////
  452. // Read Forms and XForms fdesign files:
  453. int read_fdesign_line(const char*& name, const char*& value) {
  454. int length = 0;
  455. int x;
  456. // find a colon:
  457. for (;;) {
  458. x = getc(fin);
  459. if (x < 0 && feof(fin)) return 0;
  460. if (x == '\n') {length = 0; continue;} // no colon this line...
  461. if (!isspace(x & 255)) {
  462. buffer[length++] = x;
  463. expand_buffer(length);
  464. }
  465. if (x == ':') break;
  466. }
  467. int valueoffset = length;
  468. buffer[length-1] = 0;
  469. // skip to start of value:
  470. for (;;) {
  471. x = getc(fin);
  472. if ((x < 0 && feof(fin)) || x == '\n' || !isspace(x & 255)) break;
  473. }
  474. // read the value:
  475. for (;;) {
  476. if (x == '\\') {x = read_quoted(); if (x<0) continue;}
  477. else if (x == '\n') break;
  478. buffer[length++] = x;
  479. expand_buffer(length);
  480. x = getc(fin);
  481. }
  482. buffer[length] = 0;
  483. name = buffer;
  484. value = buffer+valueoffset;
  485. return 1;
  486. }
  487. int fdesign_flip;
  488. int fdesign_magic;
  489. #include <FL/Fl_Group.H>
  490. static const char *class_matcher[] = {
  491. "FL_CHECKBUTTON", "Fl_Check_Button",
  492. "FL_ROUNDBUTTON", "Fl_Round_Button",
  493. "FL_ROUND3DBUTTON", "Fl_Round_Button",
  494. "FL_LIGHTBUTTON", "Fl_Light_Button",
  495. "FL_FRAME", "Fl_Box",
  496. "FL_LABELFRAME", "Fl_Box",
  497. "FL_TEXT", "Fl_Box",
  498. "FL_VALSLIDER", "Fl_Value_Slider",
  499. "FL_MENU", "Fl_Menu_Button",
  500. "3", "FL_BITMAP",
  501. "1", "FL_BOX",
  502. "71","FL_BROWSER",
  503. "11","FL_BUTTON",
  504. "4", "FL_CHART",
  505. "42","FL_CHOICE",
  506. "61","FL_CLOCK",
  507. "25","FL_COUNTER",
  508. "22","FL_DIAL",
  509. "101","FL_FREE",
  510. "31","FL_INPUT",
  511. "12","Fl_Light_Button",
  512. "41","FL_MENU",
  513. "23","FL_POSITIONER",
  514. "13","Fl_Round_Button",
  515. "21","FL_SLIDER",
  516. "2", "FL_BOX", // was FL_TEXT
  517. "62","FL_TIMER",
  518. "24","Fl_Value_Slider",
  519. 0};
  520. void read_fdesign() {
  521. fdesign_magic = atoi(read_word());
  522. fdesign_flip = (fdesign_magic < 13000);
  523. Fl_Widget_Type *window = 0;
  524. Fl_Widget_Type *group = 0;
  525. Fl_Widget_Type *widget = 0;
  526. if (!Fl_Type::current) {
  527. Fl_Type *t = Fl_Type_make("Function");
  528. t->name("create_the_forms()");
  529. Fl_Type::current = t;
  530. }
  531. for (;;) {
  532. const char *name;
  533. const char *value;
  534. if (!read_fdesign_line(name, value)) break;
  535. if (!strcmp(name,"Name")) {
  536. window = (Fl_Widget_Type*)Fl_Type_make("Fl_Window");
  537. window->name(value);
  538. window->label(value);
  539. Fl_Type::current = widget = window;
  540. } else if (!strcmp(name,"class")) {
  541. if (!strcmp(value,"FL_BEGIN_GROUP")) {
  542. group = widget = (Fl_Widget_Type*)Fl_Type_make("Fl_Group");
  543. Fl_Type::current = group;
  544. } else if (!strcmp(value,"FL_END_GROUP")) {
  545. if (group) {
  546. Fl_Group* g = (Fl_Group*)(group->o);
  547. g->begin();
  548. g->forms_end();
  549. Fl_Group::current(0);
  550. }
  551. group = widget = 0;
  552. Fl_Type::current = window;
  553. } else {
  554. for (int i = 0; class_matcher[i]; i += 2)
  555. if (!strcmp(value,class_matcher[i])) {
  556. value = class_matcher[i+1]; break;}
  557. widget = (Fl_Widget_Type*)Fl_Type_make(value);
  558. if (!widget) {
  559. printf("class %s not found, using Fl_Button\n", value);
  560. widget = (Fl_Widget_Type*)Fl_Type_make("Fl_Button");
  561. }
  562. }
  563. } else if (widget) {
  564. if (!widget->read_fdesign(name, value))
  565. printf("Ignoring \"%s: %s\"\n", name, value);
  566. }
  567. }
  568. }
  569. //
  570. // End of "$Id: file.cxx 8864 2011-07-19 04:49:30Z greg.ercolano $".
  571. //