diff options
| author | Matthias Melcher <github@matthiasm.com> | 2023-01-26 15:23:43 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-26 15:23:43 +0100 |
| commit | 179771acd25ee5ff8e36a5fd192682d4d55c52af (patch) | |
| tree | f7d32685bccef27e64a6f706e7c0e0c9d099c372 /fluid/code.cxx | |
| parent | f314ca75feab8c472a9b136fd25636d2d2da68a7 (diff) | |
Fixing FLUID file corruption from issue #653 (#662)
Removing all globals in file writer (#653 )
Fix some static analyser complaints
Valgrind: handle width==0 in GfxDrivers on Wayland and X11
Don't use `Fl_Input_::static_value`, it accesses previous
buffer that may be deleted
Project file write encapsulated, removing globals
Encapsulating project file reader, removing states in glbals
Project i/o increased source code readability
Diffstat (limited to 'fluid/code.cxx')
| -rw-r--r-- | fluid/code.cxx | 460 |
1 files changed, 271 insertions, 189 deletions
diff --git a/fluid/code.cxx b/fluid/code.cxx index 6d1ab3f7c..f257c6a59 100644 --- a/fluid/code.cxx +++ b/fluid/code.cxx @@ -34,15 +34,6 @@ /// \defgroup cfile C Code File Operations /// \{ -static FILE *code_file = NULL; -static FILE *header_file = NULL; - -/// Store the current indentation level for the C source code. -int indentation = 0; - -int write_number = 0; - -int write_sourceview = 0; /** Return true if c can be in a C identifier. @@ -52,40 +43,177 @@ int is_id(char c) { return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; } +/** + Write a file that contains all label and tooltip strings for internationalisation. + */ +int write_strings(const char *sfile) { + FILE *fp = fl_fopen(sfile, "w"); + Fl_Type *p; + Fl_Widget_Type *w; + int i; + + if (!fp) return 1; + + switch (g_project.i18n_type) { + case 0 : /* None, just put static text out */ + fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n", + FL_VERSION); + for (p = Fl_Type::first; p; p = p->next) { + if (p->is_widget()) { + w = (Fl_Widget_Type *)p; + + if (w->label()) { + for (const char *s = w->label(); *s; s ++) + if (*s < 32 || *s > 126 || *s == '\"') + fprintf(fp, "\\%03o", *s); + else + putc(*s, fp); + putc('\n', fp); + } + + if (w->tooltip()) { + for (const char *s = w->tooltip(); *s; s ++) + if (*s < 32 || *s > 126 || *s == '\"') + fprintf(fp, "\\%03o", *s); + else + putc(*s, fp); + putc('\n', fp); + } + } + } + break; + case 1 : /* GNU gettext, put a .po file out */ + fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n", + FL_VERSION); + for (p = Fl_Type::first; p; p = p->next) { + if (p->is_widget()) { + w = (Fl_Widget_Type *)p; + + if (w->label()) { + const char *s; + + fputs("msgid \"", fp); + for (s = w->label(); *s; s ++) + if (*s < 32 || *s > 126 || *s == '\"') + fprintf(fp, "\\%03o", *s); + else + putc(*s, fp); + fputs("\"\n", fp); + + fputs("msgstr \"", fp); + for (s = w->label(); *s; s ++) + if (*s < 32 || *s > 126 || *s == '\"') + fprintf(fp, "\\%03o", *s); + else + putc(*s, fp); + fputs("\"\n", fp); + } + + if (w->tooltip()) { + const char *s; + + fputs("msgid \"", fp); + for (s = w->tooltip(); *s; s ++) + if (*s < 32 || *s > 126 || *s == '\"') + fprintf(fp, "\\%03o", *s); + else + putc(*s, fp); + fputs("\"\n", fp); + + fputs("msgstr \"", fp); + for (s = w->tooltip(); *s; s ++) + if (*s < 32 || *s > 126 || *s == '\"') + fprintf(fp, "\\%03o", *s); + else + putc(*s, fp); + fputs("\"\n", fp); + } + } + } + break; + case 2 : /* POSIX catgets, put a .msg file out */ + fprintf(fp, "$ generated by Fast Light User Interface Designer (fluid) version %.4f\n", + FL_VERSION); + fprintf(fp, "$set %s\n", g_project.i18n_set.value()); + fputs("$quote \"\n", fp); + + for (i = 1, p = Fl_Type::first; p; p = p->next) { + if (p->is_widget()) { + w = (Fl_Widget_Type *)p; + + if (w->label()) { + fprintf(fp, "%d \"", i ++); + for (const char *s = w->label(); *s; s ++) + if (*s < 32 || *s > 126 || *s == '\"') + fprintf(fp, "\\%03o", *s); + else + putc(*s, fp); + fputs("\"\n", fp); + } + + if (w->tooltip()) { + fprintf(fp, "%d \"", i ++); + for (const char *s = w->tooltip(); *s; s ++) + if (*s < 32 || *s > 126 || *s == '\"') + fprintf(fp, "\\%03o", *s); + else + putc(*s, fp); + fputs("\"\n", fp); + } + } + } + break; + } + + return fclose(fp); +} + //////////////////////////////////////////////////////////////// // Generate unique but human-readable identifiers: -struct id { +struct Fd_Identifier_Tree { char* text; void* object; - id *left, *right; - id (const char* t, void* o) : text(fl_strdup(t)), object(o) {left = right = 0;} - ~id(); + Fd_Identifier_Tree *left, *right; + Fd_Identifier_Tree (const char* t, void* o) : text(fl_strdup(t)), object(o) {left = right = 0;} + ~Fd_Identifier_Tree(); }; -id::~id() { +Fd_Identifier_Tree::~Fd_Identifier_Tree() { delete left; free((void *)text); delete right; } -static id* id_root; +/** \brief Return a unique name for the given object. + + This function combines the anem and label into an identifier. It then checks + if that id was already taken by another object, and if so, appends a + hexadecimal value which is incremented until the id is unique in this file. + + If a new id was created, it is stored in the id tree. -// TODO: document me -const char* unique_id(void* o, const char* type, const char* name, const char* label) { + \param[in] o create an ID for this object + \param[in] type is the first word of the ID + \param[in] name if name is set, it is appended to the ID + \param[in] label else if label is set, it is appended, skipping non-keyword characters + \return buffer to a unique identifier, managed by Fd_Code_Writer, so caller must NOT free() it + */ +const char* Fd_Code_Writer::unique_id(void* o, const char* type, const char* name, const char* label) { char buffer[128]; char* q = buffer; + char* q_end = q + 128 - 8 - 1; // room for hex number and NUL while (*type) *q++ = *type++; *q++ = '_'; const char* n = name; if (!n || !*n) n = label; if (n && *n) { while (*n && !is_id(*n)) n++; - while (is_id(*n)) *q++ = *n++; + while (is_id(*n) && (q < q_end)) *q++ = *n++; } *q = 0; // okay, search the tree and see if the name was already used: - id** p = &id_root; + Fd_Identifier_Tree** p = &id_root; int which = 0; while (*p) { int i = strcmp(buffer, (*p)->text); @@ -99,7 +227,7 @@ const char* unique_id(void* o, const char* type, const char* name, const char* l else if (i < 0) p = &((*p)->left); else p = &((*p)->right); } - *p = new id(buffer, o); + *p = new Fd_Identifier_Tree(buffer, o); return (*p)->text; } @@ -121,7 +249,7 @@ const char* unique_id(void* o, const char* type, const char* name, const char* l \param[in] set generate this indent depth \return pointer to a static string */ -const char *indent(int set) { +const char *Fd_Code_Writer::indent(int set) { static const char* spaces = " "; int i = set * 2; if (i>32) i = 32; @@ -133,7 +261,7 @@ const char *indent(int set) { Return a C string that indents code to the current source file depth. \return pointer to a static string */ -const char *indent() { +const char *Fd_Code_Writer::indent() { return indent(indentation); } @@ -143,45 +271,59 @@ const char *indent() { change the `indentation` variable; offset can be negative \return pointer to a static string */ -const char *indent_plus(int offset) { +const char *Fd_Code_Writer::indent_plus(int offset) { return indent(indentation+offset); } //////////////////////////////////////////////////////////////// // declarations/include files: -// Each string generated by write_declare is written only once to +// Each string generated by write_h_once is written only once to // the header file. This is done by keeping a binary tree of all // the calls so far and not printing it if it is in the tree. -struct included { +struct Fd_Text_Tree { char *text; - included *left, *right; - included(const char *t) { + Fd_Text_Tree *left, *right; + Fd_Text_Tree(const char *t) { text = fl_strdup(t); left = right = 0; } - ~included(); + ~Fd_Text_Tree(); }; -included::~included() { +Fd_Text_Tree::~Fd_Text_Tree() { delete left; free((void *)text); delete right; } -static included *included_root; + +struct Fd_Pointer_Tree { + void *ptr; + Fd_Pointer_Tree *left, *right; + Fd_Pointer_Tree(void *p) { + ptr = p; + left = right = 0; + } + ~Fd_Pointer_Tree(); +}; + +Fd_Pointer_Tree::~Fd_Pointer_Tree() { + delete left; + delete right; +} /** - Print a formatted line to the header file, unless the same line was produced before. + Print a formatted line to the header file, unless the same line was produced before in this header file. \param[in] format printf-style formatting text, followed by a vararg list */ -int write_declare(const char *format, ...) { +int Fd_Code_Writer::write_h_once(const char *format, ...) { va_list args; char buf[1024]; va_start(args, format); vsnprintf(buf, sizeof(buf), format, args); va_end(args); - included **p = &included_root; + Fd_Text_Tree **p = &text_in_header; while (*p) { int i = strcmp(buf,(*p)->text); if (!i) return 0; @@ -189,17 +331,53 @@ int write_declare(const char *format, ...) { else p = &((*p)->right); } fprintf(header_file,"%s\n",buf); - *p = new included(buf); + *p = new Fd_Text_Tree(buf); return 1; } -//////////////////////////////////////////////////////////////// +/** + Print a formatted line to the source file, unless the same line was produced before in this code file. + \param[in] format printf-style formatting text, followed by a vararg list + */ +int Fd_Code_Writer::write_c_once(const char *format, ...) { + va_list args; + char buf[1024]; + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + Fd_Text_Tree **p = &text_in_header; + while (*p) { + int i = strcmp(buf,(*p)->text); + if (!i) return 0; + else if (i < 0) p = &((*p)->left); + else p = &((*p)->right); + } + p = &text_in_code; + while (*p) { + int i = strcmp(buf,(*p)->text); + if (!i) return 0; + else if (i < 0) p = &((*p)->left); + else p = &((*p)->right); + } + fprintf(code_file,"%s\n",buf); + *p = new Fd_Text_Tree(buf); + return 1; +} -// silly thing to prevent declaring unused variables: -// When this symbol is on, all attempts to write code don't write -// anything, but set a variable if it looks like the variable "o" is used: -int varused_test; -int varused; +/** + Return true if this pointer was already included in the code file. + If it was not, add it to the list and return false. + */ +bool Fd_Code_Writer::c_contains(void *pp) { + Fd_Pointer_Tree **p = &ptr_in_code; + while (*p) { + if ((*p)->ptr == pp) return true; + else if ((*p)->ptr < pp) p = &((*p)->left); + else p = &((*p)->right); + } + *p = new Fd_Pointer_Tree(pp); + return false; +} /** Write a C string to the code file, escaping non-ASCII characters. @@ -215,9 +393,9 @@ int varused; \param[in] s write this string \param[in] length write so many bytes in this string - \see write_cstring(const char*) + \see f.write_cstring(const char*) */ -void write_cstring(const char *s, int length) { +void Fd_Code_Writer::write_cstring(const char *s, int length) { if (varused_test) { varused = 1; return; @@ -314,9 +492,9 @@ void write_cstring(const char *s, int length) { /** Write a C string, escaping non-ASCII characters. \param[in] s write this string - \see write_cstring(const char*, int) + \see f.write_cstring(const char*, int) */ -void write_cstring(const char *s) { +void Fd_Code_Writer::write_cstring(const char *s) { write_cstring(s, (int)strlen(s)); } @@ -325,7 +503,7 @@ void write_cstring(const char *s) { The output is bracketed in { and }. The content is written as decimal bytes, i.e. `{ 1, 2, 200 }` */ -void write_cdata(const char *s, int length) { +void Fd_Code_Writer::write_cdata(const char *s, int length) { if (varused_test) { varused = 1; return; @@ -363,7 +541,7 @@ void write_cdata(const char *s, int length) { \param[in] format printf-style formatting text \param[in] args list of arguments */ -void vwrite_c(const char* format, va_list args) { +void Fd_Code_Writer::vwrite_c(const char* format, va_list args) { if (varused_test) { varused = 1; return; @@ -375,7 +553,7 @@ void vwrite_c(const char* format, va_list args) { Print a formatted line to the source file. \param[in] format printf-style formatting text, followed by a vararg list */ -void write_c(const char* format,...) { +void Fd_Code_Writer::write_c(const char* format,...) { va_list args; va_start(args, format); vwrite_c(format, args); @@ -390,7 +568,7 @@ void write_c(const char* format,...) { \param[in] c line of code \param[in] com optional commentary */ -void write_cc(const char *indent, int n, const char *c, const char *com) { +void Fd_Code_Writer::write_cc(const char *indent, int n, const char *c, const char *com) { write_c("%s%.*s", indent, n, c); char cc = c[n-1]; if (cc!='}' && cc!=';') @@ -404,7 +582,7 @@ void write_cc(const char *indent, int n, const char *c, const char *com) { Print a formatted line to the header file. \param[in] format printf-style formatting text, followed by a vararg list */ -void write_h(const char* format,...) { +void Fd_Code_Writer::write_h(const char* format,...) { if (varused_test) return; va_list args; va_start(args, format); @@ -420,7 +598,7 @@ void write_h(const char* format,...) { \param[in] c line of code \param[in] com optional commentary */ -void write_hc(const char *indent, int n, const char* c, const char *com) { +void Fd_Code_Writer::write_hc(const char *indent, int n, const char* c, const char *com) { write_h("%s%.*s", indent, n, c); char cc = c[n-1]; if (cc!='}' && cc!=';') @@ -434,7 +612,7 @@ void write_hc(const char *indent, int n, const char* c, const char *com) { Write one or more lines of code, indenting each one of them. \param[in] textlines one or more lines of text, seperated by \\n */ -void write_c_indented(const char *textlines, int inIndent, char inTrailwWith) { +void Fd_Code_Writer::write_c_indented(const char *textlines, int inIndent, char inTrailwWith) { if (textlines) { indentation += inIndent; for (;;) { @@ -470,7 +648,7 @@ void write_c_indented(const char *textlines, int inIndent, char inTrailwWith) { /** Recursively dump code, putting children between the two parts of the parent code. */ -static Fl_Type* write_code(Fl_Type* p) { +Fl_Type* Fd_Code_Writer::write_code(Fl_Type* p) { if (write_sourceview) { p->code_position = (int)ftell(code_file); if (p->header_position_end==-1) @@ -479,7 +657,7 @@ static Fl_Type* write_code(Fl_Type* p) { // write all code that come before the children code // (but don't write the last comment until the very end) if (!(p==Fl_Type::last && p->is_comment())) - p->write_code1(); + p->write_code1(*this); // recursively write the code of all children Fl_Type* q; if (p->is_widget() && p->is_class()) { @@ -495,7 +673,7 @@ static Fl_Type* write_code(Fl_Type* p) { } // write all code that come after the children - p->write_code2(); + p->write_code2(*this); for (q = p->next; q && q->level > p->level;) { if (!strcmp(q->type_name(), "Function")) q = write_code(q); @@ -512,7 +690,7 @@ static Fl_Type* write_code(Fl_Type* p) { } else { for (q = p->next; q && q->level > p->level;) q = write_code(q); // write all code that come after the children - p->write_code2(); + p->write_code2(*this); } if (write_sourceview) { p->code_position_end = (int)ftell(code_file); @@ -531,11 +709,11 @@ static Fl_Type* write_code(Fl_Type* p) { \param[in] t filename of the header file \return 0 if the operation failed, 1 if it was successful */ -int write_code(const char *s, const char *t) { +int Fd_Code_Writer::write_code(const char *s, const char *t, bool to_sourceview) { + write_sourceview = to_sourceview; const char *filemode = "w"; if (write_sourceview) filemode = "wb"; - write_number++; delete id_root; id_root = 0; indentation = 0; current_class = 0L; @@ -561,7 +739,7 @@ int write_code(const char *s, const char *t) { first_type->header_position = (int)ftell(header_file); } // it is ok to write non-recusive code here, because comments have no children or code2 blocks - first_type->write_code1(); + first_type->write_code1(*this); if (write_sourceview) { first_type->code_position_end = (int)ftell(code_file); first_type->header_position_end = (int)ftell(header_file); @@ -585,10 +763,12 @@ int write_code(const char *s, const char *t) { } if (g_project.avoid_early_includes==0) { - write_declare("#include <FL/Fl.H>"); + write_h_once("#include <FL/Fl.H>"); } if (t && g_project.include_H_from_C) { - if (g_project.header_file_name[0] == '.' && strchr(g_project.header_file_name, '/') == NULL) { + if (to_sourceview) { + write_c("#include \"CodeView.h\"\n"); + } else if (g_project.header_file_name[0] == '.' && strchr(g_project.header_file_name, '/') == NULL) { write_c("#include \"%s\"\n", fl_filename_name(t)); } else { write_c("#include \"%s\"\n", t); @@ -641,14 +821,14 @@ int write_code(const char *s, const char *t) { for (Fl_Type* p = first_type; p;) { // write all static data for this & all children first if (write_sourceview) p->header_position = (int)ftell(header_file); - p->write_static(); + p->write_static(*this); if (write_sourceview) { p->header_position_end = (int)ftell(header_file); if (p->header_position==p->header_position_end) p->header_position_end = -1; } for (Fl_Type* q = p->next; q && q->level > p->level; q = q->next) { if (write_sourceview) q->header_position = (int)ftell(header_file); - q->write_static(); + q->write_static(*this); if (write_sourceview) { q->header_position_end = (int)ftell(header_file); if (q->header_position==q->header_position_end) q->header_position_end = -1; @@ -658,8 +838,6 @@ int write_code(const char *s, const char *t) { p = write_code(p); } - delete included_root; included_root = 0; - if (!s) return 1; fprintf(header_file, "#endif\n"); @@ -670,147 +848,29 @@ int write_code(const char *s, const char *t) { last_type->code_position = (int)ftell(code_file); last_type->header_position = (int)ftell(header_file); } - last_type->write_code1(); + last_type->write_code1(*this); if (write_sourceview) { last_type->code_position_end = (int)ftell(code_file); last_type->header_position_end = (int)ftell(header_file); } } + int x = 0, y = 0; - int x = fclose(code_file); + if (code_file != stdout) + x = fclose(code_file); code_file = 0; - int y = fclose(header_file); + if (header_file != stdout) + y = fclose(header_file); header_file = 0; return x >= 0 && y >= 0; } -int write_strings(const char *sfile) { - FILE *fp = fl_fopen(sfile, "w"); - Fl_Type *p; - Fl_Widget_Type *w; - int i; - - if (!fp) return 1; - - switch (g_project.i18n_type) { - case 0 : /* None, just put static text out */ - fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n", - FL_VERSION); - for (p = Fl_Type::first; p; p = p->next) { - if (p->is_widget()) { - w = (Fl_Widget_Type *)p; - - if (w->label()) { - for (const char *s = w->label(); *s; s ++) - if (*s < 32 || *s > 126 || *s == '\"') - fprintf(fp, "\\%03o", *s); - else - putc(*s, fp); - putc('\n', fp); - } - - if (w->tooltip()) { - for (const char *s = w->tooltip(); *s; s ++) - if (*s < 32 || *s > 126 || *s == '\"') - fprintf(fp, "\\%03o", *s); - else - putc(*s, fp); - putc('\n', fp); - } - } - } - break; - case 1 : /* GNU gettext, put a .po file out */ - fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n", - FL_VERSION); - for (p = Fl_Type::first; p; p = p->next) { - if (p->is_widget()) { - w = (Fl_Widget_Type *)p; - - if (w->label()) { - const char *s; - - fputs("msgid \"", fp); - for (s = w->label(); *s; s ++) - if (*s < 32 || *s > 126 || *s == '\"') - fprintf(fp, "\\%03o", *s); - else - putc(*s, fp); - fputs("\"\n", fp); - - fputs("msgstr \"", fp); - for (s = w->label(); *s; s ++) - if (*s < 32 || *s > 126 || *s == '\"') - fprintf(fp, "\\%03o", *s); - else - putc(*s, fp); - fputs("\"\n", fp); - } - - if (w->tooltip()) { - const char *s; - - fputs("msgid \"", fp); - for (s = w->tooltip(); *s; s ++) - if (*s < 32 || *s > 126 || *s == '\"') - fprintf(fp, "\\%03o", *s); - else - putc(*s, fp); - fputs("\"\n", fp); - - fputs("msgstr \"", fp); - for (s = w->tooltip(); *s; s ++) - if (*s < 32 || *s > 126 || *s == '\"') - fprintf(fp, "\\%03o", *s); - else - putc(*s, fp); - fputs("\"\n", fp); - } - } - } - break; - case 2 : /* POSIX catgets, put a .msg file out */ - fprintf(fp, "$ generated by Fast Light User Interface Designer (fluid) version %.4f\n", - FL_VERSION); - fprintf(fp, "$set %s\n", g_project.i18n_set.value()); - fputs("$quote \"\n", fp); - - for (i = 1, p = Fl_Type::first; p; p = p->next) { - if (p->is_widget()) { - w = (Fl_Widget_Type *)p; - - if (w->label()) { - fprintf(fp, "%d \"", i ++); - for (const char *s = w->label(); *s; s ++) - if (*s < 32 || *s > 126 || *s == '\"') - fprintf(fp, "\\%03o", *s); - else - putc(*s, fp); - fputs("\"\n", fp); - } - - if (w->tooltip()) { - fprintf(fp, "%d \"", i ++); - for (const char *s = w->tooltip(); *s; s ++) - if (*s < 32 || *s > 126 || *s == '\"') - fprintf(fp, "\\%03o", *s); - else - putc(*s, fp); - fputs("\"\n", fp); - } - } - } - break; - } - - return fclose(fp); -} /** Write the public/private/protected keywords inside the class. This avoids repeating these words if the mode is already set. */ -void write_public(int state) { +void Fd_Code_Writer::write_public(int state) { if (!current_class && !current_widget_class) return; if (current_class && current_class->write_public_state == state) return; if (current_widget_class && current_widget_class->write_public_state == state) return; @@ -823,5 +883,27 @@ void write_public(int state) { } } + +Fd_Code_Writer::Fd_Code_Writer() +: code_file(NULL), + header_file(NULL), + id_root(NULL), + text_in_header(NULL), + text_in_code(NULL), + ptr_in_code(NULL), + indentation(0), + write_sourceview(false), + varused_test(0), + varused(0) +{ +} + +Fd_Code_Writer::~Fd_Code_Writer() +{ + delete ptr_in_code; + delete text_in_code; + delete text_in_header; +} + /// \} |
