Back to index

plt-scheme  4.2.1
util.c
Go to the documentation of this file.
00001 /*   wbuild
00002      Copyright (C) 1996  Joel N. Weber II <nemo@koa.iolani.honolulu.hi.us>
00003      
00004      This program is free software; you can redistribute it and/or
00005      modify it under the terms of the GNU General Public License
00006      as published by the Free Software Foundation; either version 2
00007      of the License, or (at your option) any later version.
00008      
00009      This program is distributed in the hope that it will be useful,
00010      but WITHOUT ANY WARRANTY; without even the implied warranty of
00011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012      GNU General Public License for more details.
00013      
00014      You should have received a copy of the GNU General Public License
00015      along with this program; if not, write to the Free Software
00016      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 
00020 /* Introduction. The `wbuild' program reads a file with descriptions
00021  * of widget classes and generates four files for each class defined
00022  * there: three C files and a \TeX\ file. The three C files are the two
00023  * header files and the implementation file for the widget and the \TeX\
00024  * file contains the documentation for the widget. It should never be
00025  * necessary to edit the files; they are readable, but only barely so.
00026  *
00027  * |MAXSTR| is probably enough for identifiers and such.
00028  */
00029 
00030 #include <config.h>
00031 #include <stdio.h>
00032 #include <string.h>
00033 
00034 #include <libit/string.h>
00035 #include <libit/ctype.h>
00036 #include <wbuild.h>
00037 #include <wsym.h>
00038 
00039 #if HAVE_STDARG_H
00040 #include <stdarg.h>
00041 #else
00042 #include <varargs.h>
00043 #endif
00044 
00045 #include <libintl.h>
00046 #define _(String) gettext(String)
00047 
00048 #include <libit/malloc.h>
00049 
00050 void wbuild_comment(FILE *f)
00051 {
00052        fprintf(f,
00053               "/* Generated by wbuild\n"
00054               " * (generator version %s)\n"
00055               " */\n", "3.2");
00056 }
00057 
00058 /* doctypes */
00059 
00060 Doctype doctypes = 0;
00061 
00062 void copy_taglist(taglist *dest, taglist *src)
00063 {
00064        int i;
00065 
00066        for (i = 0; i <= t_privconstraint; i++) {
00067               (*dest)[i][0] = (*src)[i][0];
00068               (*dest)[i][1] = (*src)[i][1];
00069        }
00070 }
00071 
00072 void zero_taglist(taglist *dest)
00073 {
00074        int i;
00075 
00076        for (i = 0; i <= t_privconstraint; i++) {
00077               (*dest)[i][0] = (*dest)[i][1] = 0;
00078        }
00079 }
00080 
00081 
00082 void add_doctype(Doctype d)
00083 {
00084        d->next = doctypes;
00085        doctypes = d;
00086 }
00087 
00088 
00089 static char *escape_string(char *src)
00090 {
00091        char *dest, *s;
00092 
00093        dest = s = malloc(strlen(src) + 1);
00094        while (*src) {
00095               switch (*src) {
00096                      case '\\':
00097                             src++;
00098                             switch (*src) {
00099                                    case 'a': *s = '\a'; break;
00100                                    case 'b': *s = '\b'; break;
00101                                    case 'f': *s = '\f'; break;
00102                                    case 'n': *s = '\n'; break;
00103                                    case 'r': *s = '\r'; break;
00104                                    case 't': *s = '\t'; break;
00105                                    case 'v': *s = '\v'; break;
00106                                    case 'B': *s = '\\'; break;
00107                                    default: *s = *src;
00108                             }
00109                             src++;
00110                             s++;
00111                             break;
00112                      case '\"':
00113                             src++;
00114                             break;
00115                      default:
00116                             *s = *src;
00117                             s++;
00118                             src++;
00119               }
00120        }
00121        *s = 0;
00122        return dest;
00123 }
00124 
00125 void set_doctag(taglist tag, char *name, char *open, char *close)
00126 {
00127        name = escape_string(name);
00128        open = escape_string(open);
00129        close = escape_string(close);
00130        if (!strcmp("filename", name)) {
00131               tag[t_filename][0] = strdup(open);
00132               tag[t_filename][1] = strdup(close);
00133        }
00134        if (!strcmp("start", name)) {
00135               tag[t_start][0] = strdup(open);
00136               tag[t_start][1] = strdup(close);
00137        }
00138        if (!strcmp("class", name)) {
00139               tag[t_class][0] = strdup(open);
00140               tag[t_class][1] = strdup(close);
00141        }
00142        if (!strcmp("name", name)) {
00143               tag[t_name][0] = strdup(open);
00144               tag[t_name][1] = strdup(close);
00145        }
00146        if (!strcmp("name2", name)) {
00147               tag[t_name2][0] = strdup(open);
00148               tag[t_name2][1] = strdup(close);
00149        }
00150        if (!strcmp("superclass", name)) {
00151               tag[t_superclass][0] = strdup(open);
00152               tag[t_superclass][1] = strdup(close);
00153        }
00154        if (!strcmp("publicvars", name)) {
00155               tag[t_publicvars][0] = strdup(open);
00156               tag[t_publicvars][1] = strdup(close);
00157        }
00158        if (!strcmp("privatevars", name)) {
00159               tag[t_privatevars][0] = strdup(open);
00160               tag[t_privatevars][1] = strdup(close);
00161        }
00162        if (!strcmp("actions", name)) {
00163               tag[t_actions][0] = strdup(open);
00164               tag[t_actions][1] = strdup(close);
00165        }
00166        if (!strcmp("translations", name)) {
00167               tag[t_translations][0] = strdup(open);
00168               tag[t_translations][1] = strdup(close);
00169        }
00170        if (!strcmp("exports", name)) {
00171               tag[t_exports][0] = strdup(open);
00172               tag[t_exports][1] = strdup(close);
00173        }
00174        if (!strcmp("methods", name)) {
00175               tag[t_methods][0] = strdup(open);
00176               tag[t_methods][1] = strdup(close);
00177        }
00178        if (!strcmp("imports", name)) {
00179               tag[t_imports][0] = strdup(open);
00180               tag[t_imports][1] = strdup(close);
00181        }
00182        if (!strcmp("utilities", name)) {
00183               tag[t_utilities][0] = strdup(open);
00184               tag[t_utilities][1] = strdup(close);
00185        }
00186        if (!strcmp("classvars", name)) {
00187               tag[t_classvars][0] = strdup(open);
00188               tag[t_classvars][1] = strdup(close);
00189        }
00190        if (!strcmp("section", name)) {
00191               tag[t_section][0] = strdup(open);
00192               tag[t_section][1] = strdup(close);
00193        }
00194        if (!strcmp("macro", name)) {
00195               tag[t_macro][0] = strdup(open);
00196               tag[t_macro][1] = strdup(close);
00197        }
00198        if (!strcmp("publicvar", name)) {
00199               tag[t_publicvar][0] = strdup(open);
00200               tag[t_publicvar][1] = strdup(close);
00201        }
00202        if (!strcmp("action", name)) {
00203               tag[t_action][0] = strdup(open);
00204               tag[t_action][1] = strdup(close);
00205        }
00206        if (!strcmp("code", name)) {
00207               tag[t_code][0] = strdup(open);
00208               tag[t_code][1] = strdup(close);
00209        }
00210        if (!strcmp("table", name)) {
00211               tag[t_table][0] = strdup(open);
00212               tag[t_table][1] = strdup(close);
00213        }
00214        if (!strcmp("tablehead", name)) {
00215               tag[t_tablehead][0] = strdup(open);
00216               tag[t_tablehead][1] = strdup(close);
00217        }
00218        if (!strcmp("row", name)) {
00219               tag[t_row][0] = strdup(open);
00220               tag[t_row][1] = strdup(close);
00221        }
00222        if (!strcmp("resname", name)) {
00223               tag[t_resname][0] = strdup(open);
00224               tag[t_resname][1] = strdup(close);
00225        }
00226        if (!strcmp("resclass", name)) {
00227               tag[t_resclass][0] = strdup(open);
00228               tag[t_resclass][1] = strdup(close);
00229        }
00230        if (!strcmp("restype", name)) {
00231               tag[t_restype][0] = strdup(open);
00232               tag[t_restype][1] = strdup(close);
00233        }
00234        if (!strcmp("resdefault", name)) {
00235               tag[t_resdefault][0] = strdup(open);
00236               tag[t_resdefault][1] = strdup(close);
00237        }
00238        if (!strcmp("inline", name)) {
00239               tag[t_inline][0] = strdup(open);
00240               tag[t_inline][1] = strdup(close);
00241        }
00242        if (!strcmp("underline", name)) {
00243               tag[t_underline][0] = strdup(open);
00244               tag[t_underline][1] = strdup(close);
00245        }
00246        if (!strcmp("backslash", name)) {
00247               tag[t_backslash][0] = strdup(open);
00248               tag[t_backslash][1] = strdup(close);
00249        }
00250        if (!strcmp("tilde", name)) {
00251               tag[t_tilde][0] = strdup(open);
00252               tag[t_tilde][1] = strdup(close);
00253        }
00254        if (!strcmp("hashmark", name)) {
00255               tag[t_hashmark][0] = strdup(open);
00256               tag[t_hashmark][1] = strdup(close);
00257        }
00258        if (!strcmp("dollar", name)) {
00259               tag[t_dollar][0] = strdup(open);
00260               tag[t_dollar][1] = strdup(close);
00261        }
00262        if (!strcmp("less", name)) {
00263               tag[t_less][0] = strdup(open);
00264               tag[t_less][1] = strdup(close);
00265        }
00266        if (!strcmp("greater", name)) {
00267               tag[t_greater][0] = strdup(open);
00268               tag[t_greater][1] = strdup(close);
00269        }
00270        if (!strcmp("percent", name)) {
00271               tag[t_percent][0] = strdup(open);
00272               tag[t_percent][1] = strdup(close);
00273        }
00274        if (!strcmp("caret", name)) {
00275               tag[t_caret][0] = strdup(open);
00276               tag[t_caret][1] = strdup(close);
00277        }
00278        if (!strcmp("ampersand", name)) {
00279               tag[t_ampersand][0] = strdup(open);
00280               tag[t_ampersand][1] = strdup(close);
00281        }
00282        if (!strcmp("lbrace", name)) {
00283               tag[t_lbrace][0] = strdup(open);
00284               tag[t_lbrace][1] = strdup(close);
00285        }
00286        if (!strcmp("rbrace", name)) {
00287               tag[t_rbrace][0] = strdup(open);
00288               tag[t_rbrace][1] = strdup(close);
00289        }
00290        if (!strcmp("bar", name)) {
00291               tag[t_bar][0] = strdup(open);
00292               tag[t_bar][1] = strdup(close);
00293        }
00294        if (!strcmp("at", name)) {
00295               tag[t_at][0] = strdup(open);
00296               tag[t_at][1] = strdup(close);
00297        }
00298        if (!strcmp("type", name)) {
00299               tag[t_type][0] = strdup(open);
00300               tag[t_type][1] = strdup(close);
00301        }
00302        if (!strcmp("incl", name)) {
00303               tag[t_incl][0] = strdup(open);
00304               tag[t_incl][1] = strdup(close);
00305        }
00306        if (!strcmp("constraints", name)) {
00307               tag[t_constraints][0] = strdup(open);
00308               tag[t_constraints][1] = strdup(close);
00309        }
00310        if (!strcmp("constraint", name)) {
00311               tag[t_constraint][0] = strdup(open);
00312               tag[t_constraint][1] = strdup(close);
00313        }
00314        if (!strcmp("privconstraints", name)) {
00315               tag[t_privconstraints][0] = strdup(open);
00316               tag[t_privconstraints][1] = strdup(close);
00317        }
00318        if (!strcmp("privconstraint", name)) {
00319               tag[t_privconstraint][0] = strdup(open);
00320               tag[t_privconstraint][1] = strdup(close);
00321        }
00322        free(open);
00323        free(close);
00324        free(name);
00325 }
00326 
00327 /* Classes. There are functions for adding classes to the list,
00328  * looking up classes by name and finding the highest superclass to
00329  * define a certain method. A global variable |classes| holds the list of
00330  * all classes.
00331  */
00332 
00333 Class classes = 0;
00334 int classnodoc = 0, classnocode = 0;
00335 STRING classfilename = 0;
00336 
00337 /* The function |add_class| is used by the parser and is therefore
00338  * exported. It uses a recursive procedure to append the new class at the
00339  * end of |classes|. It is assumed that |c->next| is already set to |NULL|.
00340  */
00341 
00342 void add_class(c)
00343        Class c;
00344 {
00345        c->next = classes;
00346        classes = c;
00347 }
00348 
00349 /* Strings. Strings are allocated dynamically, but a hash table makes
00350  * sure that equal strings share the same pointer. The hash technique is
00351  * a simple open hash.
00352  *
00353  * The |strdup| and |malloc| standard functions return |NULL| if they
00354  * could not allocate enough memory. The two routines |Strdup| and
00355  * |Malloc| call these routines and check for |NULL|. They abort the
00356  * program with an `out of memory' message.
00357  *
00358  * The macro |new| is a convenient way of allocating unnamed variables
00359  * via the pointer, as in Pascal.
00360  */
00361 
00362 #define new(ptr) ptr = xmalloc(sizeof(*(ptr)))
00363 
00364 /* xmalloc puzzles me.  If we're out of memory, why do we expect to
00365  * have enough to write something to stderr?  Something to argue about
00366  * with RMS --nemo
00367  */
00368 
00369 void *xmalloc(size)
00370        size_t size;
00371 {
00372        void *t;
00373 
00374        t = malloc(size);
00375        if (t == NULL) {
00376               /* I deliberately decided NOT to internationalize--see above
00377                */
00378               fprintf(stderr, "out of memory\n");
00379               exit(1);
00380        }
00381        return t;
00382 }
00383 
00384 char * xstrdup(s)
00385        char *s;
00386 {
00387        char *t;
00388        if (s) {
00389               t = xmalloc(strlen(s) + 1);
00390               strcpy(t, s);
00391        } else {
00392               t = xmalloc(1);
00393               *t = 0;
00394        }
00395        return t;
00396 }
00397 
00398 /* The hash table is not initialized, instead a scheme with two extra
00399  * arrays |r1| and |r2| and a variable |nused| is used, which allows the
00400  * hash table entries to be initialized the first time they are actually
00401  * used. This saves time at start up. The opaque type |STRING| (defined
00402  * in `types'), is implemented here as a bucket in the hash table,
00403  * containing a pointer to a dynamically allocated string, a link counter
00404  * and a next-pointer. The link counter is used to deallocate the string
00405  * when it is no longer used.
00406  */
00407 
00408 #define HASHSIZE 8009
00409 
00410 static STRING hashtable[HASHSIZE];
00411 static int r2[HASHSIZE], r1[HASHSIZE];
00412 static int nused = 0;
00413 
00414 /* The hash function uses a proven technique of shifting ASCII codes,
00415  * ignoring overflow. We limit the computation to the first twenty
00416  * characters of the string.
00417  */
00418 
00419 static int hashval(name)
00420        char* name;
00421 {
00422        char *p;
00423        unsigned int h = 0, g, n = 20;
00424 
00425        if (name == NULL)
00426               return 0;
00427 
00428        for (p = name; n && *p; p++, n--) {
00429               h = (h << 4) + (*p);
00430               if ((g = h & 0xf0000000)) {
00431                      h = h ^ (g >> 24);
00432                      h = h ^ g;
00433               }
00434        }
00435        return h % HASHSIZE;
00436 }
00437 
00438 /* The function |hash| adds a new string to the buffer. It checks if
00439  * the string is already there, before creating a new copy.
00440  *
00441  * The array |r1| holds the indexes of the hashtable entries that are
00442  * used. There are |nused| such entries, stored in |r1[0]| through
00443  * |r1[nused-1]|. The array |r2| is used to look up the position in |r1|
00444  * where an index is stored. If we want to know if |hashtable[n]| is
00445  * valid, we first look in |r2[n]|. If |r2[n]| contains nonsense (i.e., a
00446  * number less than zero or greater or the same as |nused|), we know that
00447  * entry |n| is invalid. If |r2[n]| points to a valid entry in |r1|, but
00448  * the entry in |r1| doesn't contain |n|, then entry |n| is also invalid.
00449  * Only if |r1[r2[n]] == n| is entry |n| valid.
00450  */
00451 
00452 #define valid(n) (0 <= r2[n] && r2[n] < nused && r1[r2[n]] == n)
00453 
00454 STRING hash(char *s)
00455 {
00456        int h;
00457        STRING t;
00458 
00459        if (!s || s[0] == '\0')
00460               return 0;
00461 
00462        h = hashval(s);
00463        if (! valid(h)) {
00464               new(t);
00465               t->s = xstrdup(s);
00466               t->linkcount = 1;
00467               t->next = 0;
00468               do {
00469                      r2[h] = nused;
00470                      r1[nused] = h;
00471                      nused++;
00472               } while (0);
00473               hashtable[h] = t;
00474               return t;
00475        }
00476        for (t = hashtable[h]; t; t = t->next) {
00477               if (strcmp(t->s, s) == 0) {
00478                      t->linkcount++;
00479                      return t;
00480               }
00481        }
00482        new(t);
00483        t->s = xstrdup(s);
00484        t->linkcount = 1;
00485        t->next = hashtable[h];
00486        hashtable[h] = t;
00487        return t;
00488 }
00489 
00490 /* |delete| tries to free the space occupied by a string, but only if
00491  * the |linkcount| indicates that there are no other pointers to the
00492  * string.
00493  *
00494  * The routine could be made more efficient if the |struct _STRING|
00495  * contained |h| or, even beter, a backpointer. We will have to see how
00496  * the performance of Tovenaar is.
00497  *
00498  * I'm not sure what Bert was saying in the preceeding paragraph.  gcc
00499  * generally takes much longer than wbuild, some I'm sure the that whatever
00500  * it is works fine.  Amazing what you fail to learn when all you do
00501  * with the code is reformat it.  --nemo
00502  */
00503 
00504 void delete(s)
00505        STRING s;
00506 {
00507 #if 0
00508 #ifdef NDEBUG
00509        int h; STRING t;
00510 #endif /*NDEBUG*/
00511 
00512        if (!s)
00513               return;
00514 
00515        s->linkcount--;
00516        if (s->linkcount > 0)
00517               return; /* still in use elsewhere */
00518 
00519 #ifdef NDEBUG
00520        h = hashval(s->s);
00521        if (hashtable[h] == s)
00522               hashtable[h] = s->next;
00523        else {
00524               for (t = hashtable[h]; t->next != s; t = t->next) ;
00525               t->next = s->next;
00526        }
00527        if (s->s)
00528               free(s->s);
00529        free(s);
00530 #else /*NDEBUG*/
00531        if (s->linkcount < 0)
00532               debug(("linkcount on %s is %d\n", s->s, s->linkcount));
00533 #endif /*NDEBUG*/
00534 #endif
00535 }
00536 
00537 /* To convert a |STRING| to a |char *|, the function |get| must be
00538  * used.
00539  */
00540 
00541 char *get(s)
00542        STRING s;
00543 {
00544        if (!s)
00545               return "";
00546        else
00547               return s->s;
00548 }
00549 
00550 STRING hdup(s)
00551        STRING s;
00552 {
00553        if (!s)
00554               return 0;
00555        s->linkcount++;
00556        return s;
00557 }
00558 
00559 /* |catstr| creates a new |STRING| which is a concatenation of all
00560  * arguments. The argument list must end with a 0.
00561  */
00562 
00563 #if HAVE_STDARG_H
00564 #ifdef __STDC__
00565 STRING catstr(int n,...)
00566 #else /* not __STDC__ */
00567 STRING catstr(n) int n;
00568 #endif /* not __STDC__ */
00569 #else /* not HAVE_STDARG_H */
00570 STRING catstr(va_alist) va_dcl
00571 #endif /* not HAVE_STDARG_H */
00572 {
00573        va_list ap;
00574 #if !HAVE_STDARG_H
00575        int n;
00576 #endif /* !HAVE_STDARG_H */
00577        unsigned int len = 0;
00578        char *s, *s1;
00579        STRING t;
00580        int i;
00581 
00582 #ifdef HAVE_STDARG_H
00583        va_start(ap, n);
00584 #else /* !HAVE_STDARG_H */
00585        va_start(ap);
00586        n = va_arg(ap, int);
00587 #endif /* !HAVE_STDARG_H */
00588        if (n == 0) {
00589               va_end(ap);
00590               return 0;
00591        } else {
00592               for (i = n; i > 0; i--) {
00593                      s1 = va_arg(ap, char *);
00594                      len += s1 ? strlen(s1) : 0;
00595               }
00596               va_end(ap);
00597               s = xmalloc((len + 2) * sizeof(char));
00598               s[0] = '\0';
00599 #if HAVE_STDARG_H
00600               va_start(ap, n);
00601 #else /* !HAVE_STDARG_H */
00602               va_start(ap);
00603               n = va_arg(ap, int);
00604 #endif /* !HAVE_STDARG_H */
00605               for (i = n; i > 0; i--) {
00606                      s1 = va_arg(ap, char *);
00607                      if (s1) strcat(s, s1);
00608               }
00609               va_end(ap);
00610               t = hash(s);
00611               return t;
00612        }
00613 }
00614 
00615 /* |Strlen| is another convenience function for |STRING|s, it is
00616  * equivalent to |strlen(get(s))|, but it also works if |s| is |NULL|.
00617  */
00618 
00619 size_t Strlen(s)
00620        STRING s;
00621 {
00622        return s ? (s->s != NULL ? strlen(s->s) : 0) : 0;
00623 }
00624 
00625 /* |get_classname| returns the string |s|, but with the first letter
00626  * converted to uppercase. If the first letter is `X' the second letter is
00627  * also converted.
00628  * If the first letter is a \$, it is omitted.
00629  */
00630 
00631 STRING get_classname(s)
00632        STRING s;
00633 {
00634        char *h, *h1;
00635        STRING t;
00636        h = h1 = xstrdup(s->s);
00637        if (*h == '$') h++;
00638        if ('a' <= h[0] && h[0] <= 'z') h[0] += 'A' - 'a';
00639        if (h[0] == 'X' && 'a' <= h[1] && h[1] <= 'z') h[1] += 'A' - 'a';
00640        t = hash(h); free(h1);
00641        return t;
00642 }
00643 
00644 /* |get_instname| returns the string |s|, but with the first letter
00645  * converted to lowercase. If the first letter is `X' the second letter is
00646  * also converted.
00647  * If the first letter is a \$, it is omitted.
00648  */
00649 
00650 STRING get_instname(s)
00651        STRING s;
00652 {
00653        char *h, *h1;
00654        STRING t;
00655        h = h1 = xstrdup(s->s);
00656        if (*h == '$') h++;
00657        if ('A' <= h[0] && h[0] <= 'Z') h[0] += 'a' - 'A';
00658        if (h[0] == 'x' && 'A' <= h[1] && h[1] <= 'Z') h[1] += 'a' - 'A';
00659        t = hash(h); free(h1);
00660        return t;
00661 }
00662 
00663 /* The function |get_word| copies the next `word' from |s| to |word|. A
00664  * word is defined as a C identifier.
00665  */
00666 
00667 STRING get_word(s)
00668        char *s;
00669 {
00670        char *word;
00671        STRING t;
00672        int i;
00673 
00674        if (!s)
00675               return 0;
00676        else {
00677               word = xmalloc(sizeof(char) * strlen(s) + 1);
00678               for (i = 0; isalnum(s[i]) || s[i] == '_'; i++)
00679                      word[i] = s[i];
00680               word[i] = '\0';
00681               t = hash(word);
00682               free(word);
00683               return t;
00684        }
00685 }
00686 
00687 #if 0
00688 STRING get_guard(c)
00689        Class c;
00690 {
00691        STRING guard;
00692 
00693        foo bar die c compiler TODO
00694 
00695        if (! get_option(&guard, c, hash("guard")))
00696               guard = catstr(3, "_", get(c->name), "_H_");
00697        return guard;
00698 }
00699 
00700 STRING get_guardP(c)
00701        Class c;
00702 {
00703        STRING guard;
00704        if (! get_option(&guard, c, hash("guardP")))
00705               guard = catstr(3, "_", get(c->name), "P_H_");
00706        return guard;
00707 }
00708 
00709 #endif
00710 
00711 /* The public header file name is constructed from the class name,
00712  * unless there is an option that names a different file. The directory
00713  * |dir| is prefixed to it. The name including `.h' should not exceed 12
00714  * characters, the name of the class is therefore truncated to 10
00715  * characters.
00716  */
00717 
00718 #define copy_directory \
00719        if (dir != NULL && dir[0] != '\0') {                           \
00720               strcpy(s, dir);                                         \
00721               if (dir[strlen(dir)-1] != '/')                          \
00722                      strcat(s, "/");                                  \
00723        }
00724 
00725 #if 0
00726 STRING get_headername(char *dir, Class c)
00727 {
00728        STRING t;
00729        static STRING file = 0;
00730        char *s;
00731        if (!file)
00732               file = hash("file");
00733     s = Malloc((dir != NULL ? strlen(dir) : 0) + 16);
00734 
00735     @<Copy directory |dir| to |s|@>@;
00736     if (! get_option(&t, c, file)) t = c->name;
00737     (void) strncat(s, get(t), 10);
00738     (void) strcat(s, ".h");
00739     t = hash(s); free(s);
00740     return t;
00741   }
00742 #endif
00743 
00744 /* The private header file name is taken from an option or from the
00745  * class name, prefixed with the directory |dir| and affixed with
00746  * |"P.h"|. The total length of the name including `P.h' should not
00747  * exceed 12 characters.
00748  */
00749 
00750 STRING get_headerPname(char *dir, Class c)
00751 {
00752        char *s;
00753        static STRING file = 0;
00754        STRING t;
00755 
00756        if (!file)
00757               file = hash("file");
00758 
00759        s = xmalloc((dir != NULL ? strlen(dir) : 0) + 16);
00760        if (dir != NULL && dir[0] != '\0') {
00761               strcpy(s, dir);
00762               if (dir[strlen(dir)-1] != '/')
00763                      strcat(s, "/");
00764        }
00765        if (c->filenamepart)
00766               t = c->filenamepart;
00767        else
00768               t = c->name;
00769        /* strncat(s, get(t), 9); */
00770        strcat(s, get(t));
00771        strcat(s, "P.h");
00772        t = hash(s);
00773        free(s);
00774        return t;
00775 }
00776 
00777 /* The name of the implementation file is normally the same as the name
00778  * of the class (truncated to 12 characters). But if the option `@@file'
00779  * is present, the value of that option will be the base name of all C
00780  * files.
00781  */
00782 
00783 #if 0
00784   STRING get_implementationname(dir, c)
00785     char *dir;
00786     Class c;
00787   {
00788     char *s;
00789     static STRING file = NIL;
00790     STRING t;
00791     if (file == NIL) file = hash("file");
00792     s = Malloc((dir != NULL ? strlen(dir) : 0) + 16);
00793     @<Copy directory |dir| to |s|@>@;
00794     if (! get_option(&t, c, file)) t = c->name;
00795     (void) strncat(s, get(t), 10);
00796     (void) strcat(s, ".c");
00797     t = hash(s); free(s);
00798     return t;
00799   }
00800 #endif
00801 
00802 /* The name of the \TeX-file is normally the same as class name plus
00803  * `.doc', but the `@@file' option overrides that.
00804  */
00805 
00806 #if 0
00807   STRING get_docfilename(dir, c)
00808     char *dir;
00809     Class c;
00810   {
00811     char *s;
00812     static STRING file = NIL;
00813     STRING t;
00814     if (file == NIL) file = hash("file");
00815     s = Malloc((dir != NULL ? strlen(dir) : 0) + 16);
00816     @<Copy directory |dir| to |s|@>@;
00817     if (! get_option(&t, c, file)) t = c->name;
00818     (void) strncat(s, get(t), 8);
00819     (void) strcat(s, ".doc");
00820     t = hash(s); free(s);
00821     return t;
00822   }
00823 #endif
00824 
00825 /* Declaration functions.
00826  *
00827  * |find_class| searches through the list of classes for the one named
00828  * |name|. It returns |NULL| if no class is found.
00829  */
00830 
00831 static Class find_class(STRING name)
00832 {
00833        Class h;
00834        h = classes;
00835        while (h && h->name != name)
00836               h = h->next;
00837        return h;
00838 }
00839 
00840 /* |find_superclass| searches through the superclasses of |c| for the
00841  * class named |name|. It returns |NULL| if no class is found.
00842  */
00843 
00844 Class find_superclass(Class c, STRING name)
00845 {
00846        if (!c)
00847               return 0;
00848        else if (c->name == name)
00849               return c;
00850        else
00851               return find_superclass(c->super, name);
00852 }
00853 
00854 /* |find_classvar_class| searches |c| and its superclasses for the
00855  * first declaration of the identifier |name|. It returns the defining
00856  * class or |NULL|. |find_instvar_class| is similar, but searches for
00857  * instance variables (either public or private). |find_constr_class|
00858  * searches for constraint resources. |find_method_class| searches for
00859  * methods.
00860  */
00861 
00862 Class find_classvar_class(Class c, STRING name)
00863 {
00864        Class h;
00865        Section d;
00866        if (!c)
00867               return NULL;
00868        if ((h = find_classvar_class(c->super, name)))
00869               return h;
00870        for (d = c->classvars; d; d = d->next)
00871               if (d->decl && d->decl->tp == Var && d->decl->name == name)
00872                      return c;
00873        return 0;
00874 }
00875 
00876 Class find_instvar_class(Class c, STRING name)
00877 {
00878        Class h;
00879        Section d;
00880 
00881        if (!c)
00882               return 0;
00883        if ((h = find_instvar_class(c->super, name)))
00884               return h;
00885        for (d = c->publicvars; d; d = d->next)
00886               if (d->decl && d->decl->tp == Var && d->decl->name == name)
00887                      return c;
00888        for (d = c->privatevars; d; d = d->next)
00889               if (d->decl && d->decl->tp == Var && d->decl->name == name)
00890                      return c;
00891        return 0;
00892 }
00893 
00894 Class find_constr_class(Class c, STRING name)
00895 {
00896        Class h;
00897        Section d;
00898 
00899        if (!c)
00900               return 0;
00901        if ((h = find_constr_class(c->super, name)))
00902               return h;
00903        for (d = c->constraints; d; d = d->next)
00904               if (d->decl && d->decl->tp == Var && d->decl->name == name)
00905                      return c;
00906        for (d = c->privconstr; d; d = d->next)
00907               if (d->decl && d->decl->tp == Var && d->decl->name == name)
00908                      return c;
00909        return 0;
00910 }
00911 
00912 Class find_method_class(Class c, STRING name)
00913 {
00914        Class h;
00915        Section d;
00916 
00917        if (!c)
00918               return 0;
00919        if ((h = find_method_class(c->super, name)))
00920               return h;
00921        for (d = c->methods; d; d = d->next)
00922               if (d->decl && d->decl->tp == Proc && d->decl->name == name)
00923                      return c;
00924        return 0;
00925 }
00926 
00927 /* |find_method| finds the declaration of given method |m| in class
00928  * |c| or one of its superclasses.
00929  */
00930 
00931 Section find_method(Class c, STRING m)
00932 {
00933        Section d, h;
00934 
00935        if (!c)
00936               return 0;
00937        if ((h = find_method(c->super, m)))
00938               return h;
00939        for (d = c->methods; d; d = d->next)
00940               if (d->decl && d->decl->tp == Proc && d->decl->name == m)
00941                      return d;
00942        return 0;
00943 }
00944 
00945 /* |has_method| checks if class |c| has (re)defined method |m|. This is
00946  * used by |init_class_parts| in the module `generate', to check if a
00947  * method should be inherited.
00948  */
00949 
00950 int has_method(Class c, STRING m)
00951 {
00952        Section d;
00953        for (d = c->methods; d; d = d->next)
00954               if (d->decl && d->decl->tp == Proc && d->decl->name == m)
00955                      return 1;
00956        return 0;
00957 }
00958 
00959 /* |has_classvar| checks if class |c| has (re)defined class varaible
00960  * |m|. This is used by |init_class_parts| in the module `generate'.
00961  */
00962 
00963 int has_classvar(Class c, STRING m)
00964 {
00965        Section d;
00966        for (d = c->classvars; d; d = d->next)
00967               if (d->decl && d->decl->tp == Var && d->decl->name == m)
00968                      return 1;
00969        return 0;
00970 }
00971 
00972 /* |find_pubvar| finds the declaration of given public variable |m| in class
00973  * |c| or one of its superclasses.
00974  */
00975 
00976 Section find_pubvar(Class c, STRING m)
00977 {
00978        Section d, h;
00979 
00980        if (!c)
00981               return 0;
00982        if ((h = find_pubvar(c->super, m)))
00983               return h;
00984        for (d = c->publicvars; d; d = d->next)
00985               if (d->decl && d->decl->tp == Var && d->decl->name == m)
00986                      return d;
00987        return 0;
00988 }
00989 
00990 /* |find_pubvar_back| finds the declaration of given public variable
00991  * |m| in class |c| or one of its superclasses.
00992  */
00993 
00994 Section find_pubvar_back(Class c, STRING m)
00995 {
00996        Section d, h;
00997 
00998        if (!c)
00999               return 0;
01000        for (d = c->publicvars; d; d = d->next)
01001               if (d->decl && d->decl->tp == Var && d->decl->name == m)
01002                      return d;
01003        if ((h = find_pubvar(c->super, m)))
01004               return h;
01005        return 0;
01006 }
01007 
01008 /* |find_constraint| finds the declaration of given constraint variable
01009  * |m| in class |c| or one of its superclasses.
01010  */
01011 
01012 Section find_constraint(Class c, STRING m)
01013 {
01014        Section d, h;
01015 
01016        if (!c)
01017               return 0;
01018        if ((h = find_constraint(c->super, m)))
01019               return h;
01020        for (d = c->constraints; d; d = d->next)
01021               if (d->decl && d->decl->tp == Var && d->decl->name == m)
01022                      return d;
01023        for (d = c->privconstr; d; d = d->next)
01024               if (d->decl && d->decl->tp == Var && d->decl->name == m)
01025                      return d;
01026        return 0;
01027 }
01028 
01029 /* |find_classvar_value| looks for the initialization value of a class
01030  * variable in the class |c| or its superclasses.
01031  */
01032 
01033 STRING find_classvar_value(Class c, STRING name)
01034 {
01035        Section d;
01036 
01037        for (d = c->classvars; d; d = d->next)
01038               if (d->decl && d->decl->tp == Var && d->decl->name == name)
01039                      return d->decl->value;
01040        if (c->super)
01041               return find_classvar_value(c->super, name);
01042        else
01043               return 0;
01044 }
01045 
01046 /* |find_value| finds the value of an instance variable, by searching
01047  * back through the hierarchy of classes for the inherited initial value.
01048  */
01049 
01050 STRING find_instvar_value(Class c, STRING name)
01051 {
01052        Section d;
01053 
01054        for (d = c->publicvars; d; d = d->next)
01055               if (d->decl && d->decl->tp == Var && d->decl->name == name)
01056                      return d->decl->value;
01057        for (d = c->privatevars; d; d = d->next)
01058               if (d->decl && d->decl->tp == Var && d->decl->name == name)
01059                      return d->decl->value; /* should be NULL, though */
01060        if (c->super)
01061               return find_instvar_value(c->super, name);
01062        else
01063               return NULL;
01064 }
01065 
01066 /* Hierarchy. The classes are arranged in a tree with the function
01067  * |set_hierarchy|. This function should be called after all files have
01068  * been parsed, but before any of the other class handling function above
01069  * is called.
01070  *
01071  * |toplevel| holds the list of classes that have no superclass. The list
01072  * is chained on the |sister| field.
01073  */
01074 
01075 Class toplevel = NULL;
01076 
01077 /* The superclass' name may include one or more slashes, e.g.,
01078  * |"Xfwf/XfwfLabel"|, meaning that the header files can be found in the
01079  * |Xfwf| directory, instead of in the directory given by the |-p|
01080  * command line option.
01081  *
01082  * After every class has been given a pointer to its superclass, a check
01083  * is made to see if there are no cycles: eventually, every chain of
01084  * |super| must end with |NULL|.
01085  *
01086  * The function returns the number of errors.
01087  */
01088 
01089 
01090 int set_hierarchy(void)
01091 {
01092        Class c, c1;
01093        char *p;
01094        int err = 0;
01095 
01096        for (c = classes; c; c = c->next) {
01097               if ((p = strrchr(get(c->superclass), '/')))
01098                      c->super = find_class(hash(p + 1));
01099               else
01100                      c->super = find_class(c->superclass);
01101               if (c->super == NULL) {
01102                      c->sister = toplevel;
01103                      toplevel = c;
01104               } else {
01105                      c->sister = c->super->daughters;
01106                      c->super->daughters = c;
01107               }
01108        }
01109        for (c = classes; c; c = c->next) {
01110               for (c1 = c->super; c1; c1 = c1->super) {
01111                      if (c1 == c) {
01112                             fprintf(stderr,
01113                                    _("%s:%d: cyclic dependency found "
01114                                    "in superclasses of %s\n"),
01115                                    get(c->filename), c->lineno,
01116                                    get(c->name));
01117                             err++;
01118                             break;
01119                      }
01120               }
01121        }
01122        return err;
01123 }
01124 
01125 /* strncmp replacement.
01126  */
01127 
01128 int strneq(char *a, char *b, int n)
01129 {
01130        if (!a)
01131               return (!b) || (*b == '\0');
01132        if (!b)
01133               return 0;
01134        while (n && *a && (*a == *b)) {
01135               a++;
01136               b++;
01137               n--;
01138        }
01139        return n == 0 || *a == *b /* == '\0' */;
01140 }