src/tools/genclasses/gc.c
/***************************************************************************
GenClasses - MUI class dispatcher generator
Copyright (C) 2001 by Andrew Bell <mechanismx@lineone.net>
Contributed to the YAM Open Source Team as a special version
Copyright (C) 2000-2022 YAM Open Source Team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
$Id$
TODO
- Handle object creation order.
- Improve tag value generation.
- Generation of desciptive class tree in header comment.
- DECLARE param parser should allow normal C style comments to be used
too.
***************************************************************************/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <string.h>
#include "gc.h"
#include "lists.h"
#include "crc32.h"
#if defined(__SASC)
/* SAS/C does not know snprintf(), hence we redirect it back to sprintf() */
size_t snprintf(char *s, size_t len, const char *f, ...)
{
va_list args;
size_t result;
va_start(args, f);
result = vsprintf(s, f, args);
va_end(args);
return result;
}
#endif
/*
* When compiling for YAM under VBCC use:
*
* GenClasses <classespath> -bYAM -gpl -i"/YAM.h" -mkfilevmakefile,vc,-o,-+,-c*
*
*
* History
* -------
* 0.37 - implemented an ASM stub for MorphOS compiles with GCC > 2 because newer
* GCC versions unfortunately don't support VARARGS68K anymore on MorphOS.
* 0.36 - a .crc file will be created for each class. This makes it possible to
* regenerate only those classes which really have been modified.
* 0.35 - each class now gets its own public header file to avoid depency of lots
* of source file on one single generated classes.h file. The classes.h
* file now contains the function prototypes used in classes.c only.
* Furthermore added the new keyword INCLUDE to let classes specify
* additional #include statements in case their method need definitions
* from other headers.
* 0.34 - multiple dependencies from a private class were not handled correctly
* and rejected as a dependency loop. Additionally the parent's class name
* will now be included in the error message.
* 0.33 - the .crc file is now written on every run, even if the checksum did not
* change. This solves some dependency issues in YAM's Makefile.
* 0.32 - CRC checksums are now used to check whether any class definition has
* changed. Without any public change the sources will not be regenerated.
* 0.31 - classes without any instance data don't need to pecify a Data structure
* with an unused dummy variable anymore.
* 0.30 - the classes and their code are sorted alphabetically now.
* 0.29 - added support for private classes being subclassed from other private
* classes.
*
* 0.28 - changed to use the new SDISPATCHER macros instead which are generating
* static dispatcher functions instead of global ones.
*
* 0.27 - added warning in case a BOOL parameter type is used in a DECLARE()
* statement as this type is known to case trouble.
*
* 0.26 - fixed a bug in the list_findname() function where after the last fix
* the function didn't correctly find a named node. In addition, a new "-q"
* option now prevents all standard output as only fprintf(stderr,...)
* will be shown upon execution
*
* 0.25 - fixed a memory leak due to a wrong linked list implementation. There
* was always one node left in a list upon calling list_remhead() until
* it returned NULL. Also added a sort functions for linked lists.
*
* 0.24 - compilable by SAS/C again. The missing snprintf() is redirected to
* sprintf().
*
* 0.23 - use new DISPATCHER() macro instead of DISPATCHERPROTO()
*
* 0.22 - removed unncesessary STDARGS uses
*
* 0.21 - raised the warning levels to -pedantic and fixed some compilation
* warnings triggers by some oddities in our code.
*
* 0.20 - modified generated code to be somewhat more compatible to compiling
* with -Wwrite-strings
*
* 0.19 - replaced all sprintf() uses by proper snprintf() ones.
*
* 0.18 - minor cosmetic changes to the generated source layout
*
* 0.17 - removed all 'long' datatype uses as this generates tag values that
* are larger than 32bit and therefore make us run into problems.
* Also modified the whole tagvalue generation code to just generate
* tag values that are between TAG_USER (0x80000000) and the MUI's
* one (0x80010000) because otherwise we might run into problems.
*
* 0.16 - exported text is now placed before the generated MUIP_ structures
* because it may contain definition which are required there.
*
* 0.15 - changed the variable argument definition to use the new macros from
* SDI_stdarg.h
*
* 0.14 - removed the special OS4 ASMSTUB handling from the generated code again
* as the 51.4 kernel of AmigaOS4 seem to handle m68k<>PPC cross calls
* correctly now.
*
* 0.13 - added UNUSED define specification to function prototype macros of
* each class. This prevents dozen of warnings because of not used
* parameters.
*
* 0.12 - added special AmigaOS4 only dependent muiDispatcherEntry wrapper
* dispatcher call. This should be only temporarly here and will be
* removed as soon as AmigaOS4 allows full PPC->68k cross hook calls.
*
* 0.11 - added AmigaOS4 support to the vararg _NewObject() function.
*
* 0.10 - fixed CleanupClasses to deal with NULL classes
*
* 0.9 - fixed crash in processclasssrc()
*
* 0.8 - added MORPHOS support for the vararg _NewObject() function.
* added STDARGS to _NewObject() function.
*
* 0.7 - added cast to CreateCustomClass call to get rid of #120 Warnings
*
* 0.6 - generated headernames now end with _cl.h to avoid name clashes
*
* 0.5 - fixed mass of enforcer hits
* - fixed serious bug in list handling
*
* 0.4 - fixed bug in extra header inclusion and makefile argument
* - the collision array of the hash function will be alloced dynamically
* so that we still can compile genclasses with near data & code.
* - updated TAG_ID to use 0x00430000 and a BUCKET_CNT of 98304
* - modified GPL header to reflect standard YAM source header
*
* 0.3 - List counter wasn't being set to 0 on init
* resulting in some large trashy values. :-)
* - Some prototypes with MUIP_xxxx weren't saved
* correctly.
* - makefile is marked as do not edit.
* - Doesn't use join #?.o in makefile anymore.
* - New keyword called CLASSDATA.
* - xxxxGetSize() function is now saved in header.
* - Uses strstr() to extract SuperClass and Description.
* This allows for the keywords to be embedded in comments.
* - Misc code changes.
*
* 0.2 - Added error reports to all fopen() calls.
* - Added myaddpart(), mypathpart() and
* myfilepart(). All path related problems should
* be gone now.
* - Now checks for .c extensions at the end of the
* filename instead of using strstr().
* - No longer casts the msg argument.
* - Source files without a Superclass: keyword are
* skipped.
* - Unterminated EXPORT block warning now gives
* correct line number in warning.
* - Classes.h now generates the prototypes for the
* OVERLOADS and DECLARES.
* - makefile now produces all.o target.
* - Implemented free_classdef().
* - Implemented linked-list routines.
* - Removed some globals.
* - Added generation of class headers.
* - New tag value generation.
* - Using long instead of int now.
* - exlineno was defined as char, oops.
*
* 0.1 - Initial version, given to Allan and Jens.
*
*/
static const char * const verstr = "0.37";
/* Every shitty hack wouldn't be complete without some shitty globals... */
char arg_classdir [256];
char arg_basename [256];
char arg_includes [256];
char arg_mkfile [256];
char arg_mkfile_dest [256];
char *arg_mkfile_cc = NULL;
char *arg_mkfile_outarg = NULL;
char *arg_mkfile_ccopts = NULL;
int arg_gpl = 0; /* booleans */
int arg_storm = 0;
int arg_v = 0;
int arg_q = 0;
/*******************************************************************************
*
* Emulation of opendir()/closedir()/readdir() on AmigaOS
*
* Only include this if your compiler doesn't support the <dirent.h> routines.
*
*******************************************************************************
*
*
*/
#ifdef EMULATE_DIRENT
#include <proto/dos.h>
#include <exec/types.h>
#include <dos/dosextens.h>
typedef struct dirent
{
char *d_name;
BPTR lock;
struct FileInfoBlock *fib;
} DIR;
void closedir( DIR *de )
{
if (!de) return;
if (de->lock) UnLock(de->lock);
if (de->fib) FreeDosObject(DOS_FIB, de->fib);
free(de);
}
DIR *opendir( char *name )
{
DIR *de;
if (!(de = calloc(1, sizeof(struct dirent)))) return NULL;
if ((de->fib = AllocDosObject(DOS_FIB, NULL)) &&
(de->lock = Lock(name, SHARED_LOCK)) &&
(Examine(de->lock, de->fib)) && (de->fib->fib_DirEntryType > 0))
{
de->d_name = de->fib->fib_FileName;
}
else
{
closedir(de); de = NULL;
}
return de;
}
DIR *readdir( DIR *de )
{
if (!de) return NULL;
return ExNext(de->lock, de->fib) ? de : NULL;
}
#else
#include <dirent.h>
#ifndef TAG_USER
#define TAG_USER ((unsigned int)(1UL<<31))
#endif
#endif /* EMULATE_DIRENT */
/*******************************************************************************
*
* Assorted routines
*
*******************************************************************************
*
*
*/
char *skipwhitespaces( char *p ) /* Note: isspace() sucks... */
{
unsigned char c;
while ((c = *p))
{
if (c == '\t' || c == '\r' || c == '\n' || c == ' ' || c == 0xa0)
{
++p;
}
else break;
}
return p;
}
char *stralloc( char *str )
{
/* Copy str to buffer and strip leading and trailing white spaces, LFs, CRs, etc. */
char *strvec, *estr;
unsigned c;
if (!str) str = "";
if (!(strvec = calloc(1, strlen(str) + 1))) return NULL;
if (*str == 0) return strvec; /* Empty string? */
while ((c = *str)) if (c == '\t' || c == '\r' || c == '\n' || c == ' ' || c == 0xa0)
++str; else break;
if (*str == 0) return strvec; /* Empty string still? */
estr = str + (strlen(str) - 1);
while (estr > str)
{
c = *estr;
if (c == '\t' || c == '\r' || c == '\n' || c == ' ' || c == 0xa0) --estr; else break;
}
memcpy(strvec, str, (size_t)(estr - str) + 1);
return strvec;
}
int myaddpart( char *path, char *name, size_t len )
{
/* A version of dos.library/AddPart() that should
work on both UNIX and Amiga systems. */
char c;
c = path[strlen(path) - 1];
if (c != ':' && c != '/') strncat(path, "/", len);
strncat(path, name, len);
return 1;
}
char *mypathpart( char *path )
{
char *p;
if (!path) return NULL;
if ((p = strrchr(path, '/'))) return p;
if ((p = strrchr(path, ':'))) return ++p;
return path;
}
char *myfilepart( char *path )
{
char *p;
if (!path) return NULL;
if ((p = strrchr(path, '/'))) return ++p;
if ((p = strrchr(path, ':'))) return ++p;
return path;
}
/*******************************************************************************
*
* Tag value generation
*
*******************************************************************************
*
*
*/
int *collision_cnts;
static unsigned int gethash( char *str )
{
static unsigned int primes[10] = { 3, 5, 7, 11, 13, 17, 19, 23, 27, 31 };
unsigned int i = 0;
unsigned int hash = strlen(str);
while (*str)
{
hash *= 13;
hash += (*str++) * primes[(++i) % 10];
}
return hash % BUCKET_CNT;
}
static unsigned int gettagvalue(char *tag, int checkcol)
{
unsigned int hash = gethash(tag);
unsigned int val = TAG_USER + (hash << 8) + collision_cnts[hash];
/* now we check that val is definitely between TAG_USER and the MUI tag base
because anything above/below that might cause problems. */
if(val < TAG_USER || val >= 0x80010000)
fprintf(stderr, "WARNING: generate tag value '%x' of class '%s' collides with BOOPSI/MUI tag values!\n", val, tag);
/* if we should check for a collision we do so */
if(checkcol)
++collision_cnts[hash];
if(arg_v)
fprintf(stdout, "Assigning tag %-35s with value %x\n", tag, val);
return val;
}
/*******************************************************************************
*
* Parsing routines
*
*******************************************************************************
*
*
*/
void free_overload( struct overloaddef *od )
{
if (!od) return;
free(od->name);
}
void free_declare( struct declaredef *dd )
{
if (!dd) return;
free(dd->name);
free(dd->params);
}
void free_exportblk( struct exportdef *ed )
{
if (!ed) return;
free(ed->exporttext);
}
void free_includeblk( struct includedef *id )
{
if (!id) return;
free(id->includetext);
}
void free_attr( struct attrdef *ad )
{
if (!ad) return;
free(ad->name);
}
void free_classdef( struct classdef *cd )
{
struct node *n;
if (!cd) return;
free(cd->classdata);
while((n = list_remhead(&cd->overloadlist)))
{
free_overload(n->data);
free(n);
}
while((n = list_remhead(&cd->declarelist)))
{
free_declare(n->data);
free(n);
}
while((n = list_remhead(&cd->attrlist)))
{
free_attr(n->data);
free(n);
}
while((n = list_remhead(&cd->exportlist)))
{
free_exportblk(n->data);
free(n);
}
while((n = list_remhead(&cd->includelist)))
{
free_includeblk(n->data);
free(n);
}
free(cd);
}
void add_overload( struct classdef *cd, char *name )
{
struct overloaddef *od;
if (!(od = (struct overloaddef *) calloc(1, sizeof(struct overloaddef)))) return;
if (!(od->name = stralloc(name)))
{
free(od);
return;
}
list_saveitem(&cd->overloadlist, od->name, od);
}
void add_declare( struct classdef *cd, char *name, char *params )
{
struct declaredef *dd;
if (!(dd = (struct declaredef *) calloc(1, sizeof(struct declaredef)))) return;
dd->name = stralloc(name);
dd->params = stralloc(params);
if (!dd->name || !dd->params)
{
free_declare(dd);
free(dd);
return;
}
list_saveitem(&cd->declarelist, dd->name, dd);
}
void add_exportblk( struct classdef *cd, char *textblk )
{
struct exportdef *ed;
if (!(ed = (struct exportdef *) calloc(1, sizeof(struct exportdef)))) return;
if (!(ed->exporttext = stralloc(textblk)))
{
free(ed);
return;
}
list_saveitem(&cd->exportlist, ed->exporttext, ed);
}
void add_includeblk( struct classdef *cd, char *textblk )
{
struct includedef *id;
if (!(id = (struct includedef *) calloc(1, sizeof(struct includedef)))) return;
if (!(id->includetext = stralloc(textblk)))
{
free(id);
return;
}
list_saveitem(&cd->includelist, id->includetext, id);
}
void add_attr( struct classdef *cd, char *name )
{
struct attrdef *ad;
if (!(ad = (struct attrdef *) calloc(1, sizeof(struct attrdef)))) return;
if (!(ad->name = stralloc(name))) return;
if (list_findname(&cd->attrlist, name))
{
if(arg_v)
fprintf(stdout, "ATTR '%s' already collected, skipped.\n", name);
free_attr(ad);
free(ad);
return;
}
list_saveitem(&cd->attrlist, ad->name, ad);
}
struct classdef *processclasssrc( char *path )
{
FILE *fp;
int lineno = 0;
char line[256], *p, *ob, *cb, *sub;
struct classdef *cd;
int spos, epos = 0, exlineno = lineno;
char *blk = NULL;
if (!(cd = calloc(1, sizeof(struct classdef)))) return NULL;
if (!(fp = fopen(path, "r")))
{
fprintf(stderr, "ERROR: Unable to open %s\n", path);
free(cd);
return NULL;
}
list_init(&cd->overloadlist);
list_init(&cd->declarelist);
list_init(&cd->attrlist);
list_init(&cd->exportlist);
list_init(&cd->includelist);
if ((cd->name = stralloc(myfilepart(path))))
{
if ((p = strrchr(cd->name, '.'))) *p = 0;
}
while (fgets(line, 255, fp))
{
lineno++;
p = skipwhitespaces(line);
if (!*p) continue;
/*printf("line number = %ld [%s]\n", lineno, p);*/
ob = cb = NULL;
/******** Scan line for keywords and extract the associated information... ********/
if (!cd->superclass && (sub = strstr(p, KEYWD_SUPERCLASS)))
{
if(arg_v)
fprintf(stdout, KEYWD_SUPERCLASS " keyword found at line %d in file %s\n", lineno, path);
sub += sizeof(KEYWD_SUPERCLASS) - 1;
cd->superclass = stralloc(skipwhitespaces(sub));
/* assume public superclasses first */
cd->index = -1;
}
else if (!cd->desc && (sub = strstr(p, KEYWD_DESC)))
{
if(arg_v)
fprintf(stdout, KEYWD_DESC " keyword found at line %d\n", lineno);
sub += sizeof(KEYWD_DESC) - 1;
cd->desc = stralloc(skipwhitespaces(sub));
}
else if (!cd->classdata && strstr(line, KEYWD_CLASSDATA))
{
if(arg_v)
fprintf(stdout, KEYWD_CLASSDATA " keyword found at line %d\n", lineno);
spos = ftell(fp);
while(fgets(p = line, 255, fp))
{
lineno++; epos = ftell(fp);
if (!(p = strstr(line, "*/"))) continue;
epos += (p - line) - 3; /* + offset into line... */
fseek(fp, spos, SEEK_SET);
if (!(blk = calloc(1, (size_t)(epos - spos + 1))))
{
fprintf(stderr, "WARNING: Cannot read " KEYWD_CLASSDATA " block at line %d, out of memory!\n", exlineno); break;
}
if (fread(blk, (size_t)(epos - spos), 1, fp) != 1)
{
fprintf(stderr, "WARNING: Cannot read " KEYWD_CLASSDATA " block at line %d, fread() fails!\n", exlineno); break;
}
if ((ob = strchr(blk, '{')))
{
if (!(cb = strchr(ob + 1, '}'))) cb = blk + strlen(blk);
*cb = 0;
if ((cd->classdata = calloc(1, strlen(++ob) + 1)))
strcpy(cd->classdata, ob);
}
else if ((cd->classdata = calloc(1, strlen(blk) + 1)))
strcpy(cd->classdata, blk);
free(blk);
blk = NULL;
break;
}
}
else if (strncmp(KEYWD_OVERLOAD, p, sizeof(KEYWD_OVERLOAD) - 1) == 0)
{
if(arg_v)
fprintf(stdout, KEYWD_OVERLOAD " keyword found at line %d in file %s\n", lineno, path);
p += sizeof(KEYWD_OVERLOAD) - 1;
if (!(ob = strchr(p, '('))) continue; /* There's no open bracket, ignore it... */
if (!(cb = strchr(ob, ')'))) cb = p + strlen(p);
*cb = 0; add_overload(cd, ++ob);
}
else if (strncmp(KEYWD_DECLARE, p, sizeof(KEYWD_DECLARE) - 1) == 0)
{
if(arg_v)
fprintf(stdout, KEYWD_DECLARE " keyword found at line %d in file %s\n", lineno, path);
p += sizeof(KEYWD_DECLARE) - 1;
if (!(ob = strchr(p, '('))) continue; /* There's no open bracket, ignore it... */
if (!(cb = strchr(ob, ')'))) cb = p + strlen(p);
if ((p = strstr(cb + 1, "//"))) p += 2;
*cb = 0; add_declare(cd, ++ob, p);
/* we check for the existance of a "BOOL" as a parameter
and warn the user accordingly because BOOL is known to be unsafe */
if(p != NULL && strstr(p, "BOOL ") != NULL)
fprintf(stderr, "WARNING: " KEYWD_DECLARE "() - BOOL parameter type is unsafe at line %d in file %s\n", lineno, path);
}
else if ((sub = strstr(p, KEYWD_ATTR)) != NULL)
{
if(arg_v)
fprintf(stdout, KEYWD_ATTR " keyword found at line %d in file %s\n", lineno, path);
p = sub + sizeof(KEYWD_ATTR) - 1;
if (!(ob = strchr(p, '('))) continue; /* There's no open bracket, ignore it... */
if (!(cb = strchr(ob, ')'))) cb = p + strlen(p);
*cb = 0; add_attr(cd, ++ob);
}
else if (strncmp("/*", p, 2) == 0) /* TODO: Use strstr() here, like CLASSDATA */
{
p = skipwhitespaces(p + 2);
if (strncmp(KEYWD_EXPORT, p, sizeof(KEYWD_EXPORT) - 1) == 0)
{
if(arg_v)
fprintf(stdout, KEYWD_EXPORT " keyword found at line %d in file %s\n", lineno, path);
p += sizeof(KEYWD_EXPORT) - 1;
spos = ftell(fp);
while(fgets(p = line, 255, fp))
{
lineno++; epos = ftell(fp);
if (!(p = strstr(line, "*/"))) continue;
epos += (p - line) - 3; /* + offset into line... */
fseek(fp, spos, SEEK_SET);
if (!(blk = calloc(1, (size_t)(epos - spos + 1))))
{
fprintf(stderr, "WARNING: Cannot read " KEYWD_EXPORT " block at line %d, out of memory!\n", exlineno); break;
}
if (fread(blk, (size_t)(epos - spos), 1, fp) != 1)
{
fprintf(stderr, "WARNING: Cannot read " KEYWD_EXPORT " block at line %d, fread fails!\n", exlineno); break;
}
add_exportblk(cd, blk);
free(blk);
blk = NULL;
break;
}
if(epos == 0)
fprintf(stderr, "WARNING: Unterminated EXPORT block at line %d\n", lineno);
}
else if (strncmp(KEYWD_INCLUDE, p, sizeof(KEYWD_INCLUDE) - 1) == 0)
{
if(arg_v)
fprintf(stdout, KEYWD_INCLUDE " keyword found at line %d in file %s\n", lineno, path);
p += sizeof(KEYWD_INCLUDE) - 1;
spos = ftell(fp);
while(fgets(p = line, 255, fp))
{
lineno++; epos = ftell(fp);
if (!(p = strstr(line, "*/"))) continue;
epos += (p - line) - 3; /* + offset into line... */
if(fseek(fp, spos, SEEK_SET) == 0)
{
if (!(blk = calloc(1, (size_t)(epos - spos + 1))))
{
fprintf(stderr, "WARNING: Cannot read " KEYWD_INCLUDE " block at line %d, out of memory!\n", exlineno); break;
}
if (fread(blk, (size_t)(epos - spos), 1, fp) != 1)
{
fprintf(stderr, "WARNING: Cannot read " KEYWD_INCLUDE " block at line %d, fread fails!\n", exlineno); break;
}
add_includeblk(cd, blk);
free(blk);
blk = NULL;
}
break;
}
if(epos == 0)
fprintf(stderr, "WARNING: Unterminated INCLUDE block at line %d\n", lineno);
}
}
} /* while() */
/* a superclass is always required */
if(cd->superclass == NULL)
{
fprintf(stderr, "WARNING: Source file '%s' doesn't contain a " KEYWD_SUPERCLASS " keyword. Skipping.\n", path);
free_classdef(cd);
cd = NULL;
}
/* instance data per object are not always required and
* specifying a dummy structure only consumes memory
*/
/*
else if(cd->classdata == NULL)
{
fprintf(stderr, "WARNING: Source file '%s' doesn't contain a " KEYWD_CLASSDATA " keyword. Skipping.\n", path);
free_classdef(cd);
cd = NULL;
}
*/
if (blk != NULL)
free(blk);
fclose(fp);
return cd;
}
void printclasslist( struct list *classlist )
{
struct classdef *cd;
struct overloaddef *od;
struct declaredef *dd;
struct attrdef *ad;
struct node *n, *nn;
fprintf(stdout, "The following keywords were extracted:\n");
for(nn = NULL; (nn = list_getnext(classlist, nn, (void **)&cd)); )
{
fprintf(stdout, "CLASS: %s\n", cd->name);
for(n = NULL; (n = list_getnext(&cd->overloadlist, n, (void **) &od)); )
fprintf(stdout, " OVERLOAD: %s\n", od->name);
for(n = NULL; (n = list_getnext(&cd->declarelist, n, (void **) &dd)); )
fprintf(stdout, " DECLARE: %s\n", dd->name);
for(n = NULL; (n = list_getnext(&cd->attrlist, n, (void **) &ad)); )
fprintf(stdout, " ATTR: %s\n", ad->name);
}
}
int compareclasses(const struct node *n1, const struct node *n2)
{
struct classdef *cd1 = n1->data;
struct classdef *cd2 = n2->data;
return strcasecmp(cd1->name, cd2->name);
}
int scanclasses( char *dirname, struct list *classlist )
{
DIR *dir;
struct dirent *de;
char *n, dirbuf[256];
int len, srccnt = 0;
struct classdef *cd;
strncpy(dirbuf, dirname, 255);
if(arg_v)
fprintf(stdout, "scanning classes dir '%s'\n", dirbuf);
if (!(dir = opendir(dirname)))
{
fprintf(stderr, "ERROR: Unable to open directory %s\n", dirname);
return 0;
}
while ((de = readdir(dir)))
{
n = de->d_name; len = strlen(n);
if (len < 2) continue;
if ((n[len - 2] != '.') || (tolower(n[len - 1]) != 'c'))
{
if(arg_v)
fprintf(stdout, "Skipping: %s\n", n);
continue;
}
if (!strcmp(SOURCE_NAME, n) || !strcmp(HEADER_NAME, n)) continue;
++srccnt;
myaddpart(dirbuf, de->d_name, 255);
if(arg_v)
fprintf(stdout, "processing: %s\n", dirbuf);
if ((cd = processclasssrc(dirbuf)))
list_saveitem(classlist, cd->name, cd);
*mypathpart(dirbuf) = 0;
}
closedir(dir);
if (srccnt == 0)
{
fprintf(stderr, "ERROR: Was unable to find any sources in %s\n", dirname);
return 0;
}
/* alphabetically sort the class list */
list_sort(classlist, compareclasses);
if (arg_v) printclasslist(classlist);
return 1;
}
void buildclasstree( struct list *classlist )
{
struct classdef *outercd;
struct node *outern;
/* iterate over all classes and check whether one class has one
* of our private classes as superclass
*/
for(outern = NULL; (outern = list_getnext(classlist, outern, (void **)&outercd)); )
{
struct classdef *innercd;
struct node *innern;
for(innern = NULL; (innern = list_getnext(classlist, innern, (void **)&innercd)); )
{
if(innercd != outercd)
{
/* assume that the superclass name begins with "MUIC_"
* and only respect nodes with a yet public superclass (supernode == NULL)
*/
if(outercd->supernode == NULL && strcmp(&outercd->superclass[5], innercd->name) == 0)
{
/* remember the superclass */
outercd->supernode = innercd;
}
}
else
{
if(strcmp(&outercd->superclass[5], outercd->name) == 0)
{
/* one *VERY* dumb developer declared the class to be subclassed
* from itself. This of course is absolute nonsense.
* We just remember the class to be its own superclass. Eventually
* this will generate an #error statement in the generated source
* because of a zero sized dependency loop.
*/
outercd->supernode = outercd;
}
}
}
}
}
unsigned long read_crc( const char *crcfile )
{
FILE *fp;
unsigned long crc = INVALID_CRC;
if((fp = fopen(crcfile, "r")) != NULL)
{
char line[32];
/* read the CRC from the file
*/
if(fgets(line, 31, fp))
{
crc = strtoul(line, NULL, 16);
}
fclose(fp);
}
return crc;
}
void write_crc( const char *crcfile, const unsigned long crc )
{
FILE *fp;
if((fp = fopen(crcfile, "w")) != NULL)
{
fprintf(fp, "0x%08lx", crc);
fclose(fp);
}
}
unsigned long gen_class_crc( struct classdef *cd, unsigned long crc )
{
struct declaredef *nextdd;
struct exportdef *nexted;
struct attrdef *nextad;
struct overloaddef *nextod;
struct includedef *nextid;
struct node *n;
if(cd->name != NULL)
crc = crc32(cd->name, strlen(cd->name), crc);
if(cd->superclass != NULL)
crc = crc32(cd->superclass, strlen(cd->superclass), crc);
if(cd->desc != NULL)
crc = crc32(cd->desc, strlen(cd->desc), crc);
if(cd->classdata != NULL)
crc = crc32(cd->classdata, strlen(cd->classdata), crc);
for(n = NULL; (n = list_getnext(&cd->overloadlist, n, (void **)&nextod));)
{
crc = crc32(nextod->name, strlen(nextod->name), crc);
}
for(n = NULL; (n = list_getnext(&cd->declarelist, n, (void **)&nextdd));)
{
crc = crc32(nextdd->name, strlen(nextdd->name), crc);
crc = crc32(nextdd->params, strlen(nextdd->params), crc);
}
for(n = NULL; (n = list_getnext(&cd->attrlist, n, (void **)&nextad));)
{
crc = crc32(nextad->name, strlen(nextad->name), crc);
}
for(n = NULL; (n = list_getnext(&cd->exportlist, n, (void **)&nexted));)
{
crc = crc32(nexted->exporttext, strlen(nexted->exporttext), crc);
}
for(n = NULL; (n = list_getnext(&cd->includelist, n, (void **)&nextid));)
{
crc = crc32(nextid->includetext, strlen(nextid->includetext), crc);
}
return crc;
}
unsigned long gen_crc( struct list *classlist )
{
unsigned long crc;
struct classdef *nextcd;
struct node *n;
crc = 0;
/* iterate over all classes and calculate the CRC checksum from
* all stuff the classes expose to the public (methods, definitions,
* class data, etc).
*/
for(n = NULL; (n = list_getnext(classlist, n, (void **)&nextcd)); )
{
crc = gen_class_crc(nextcd, crc);
}
return crc;
}
/*******************************************************************************
*
* Source code generation
*
*******************************************************************************
*
*
*/
void gen_gpl( FILE *fp )
{
if (!fp || !arg_gpl) return;
fprintf(fp,
"/***************************************************************************\n"
"\n"
" YAM - Yet Another Mailer\n"
" Copyright (C) 1995-2000 Marcel Beck\n"
" Copyright (C) 2000-2022 YAM Open Source Team\n"
"\n"
" This program is free software; you can redistribute it and/or modify\n"
" it under the terms of the GNU General Public License as published by\n"
" the Free Software Foundation; either version 2 of the License, or\n"
" (at your option) any later version.\n"
"\n");
fprintf(fp,
" This program is distributed in the hope that it will be useful,\n"
" but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
" GNU General Public License for more details.\n"
"\n");
fprintf(fp,
" You should have received a copy of the GNU General Public License\n"
" along with this program; if not, write to the Free Software\n"
" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
"\n"
" YAM Official Support Site : http://www.yam.ch/\n"
" YAM OpenSource project : http://sourceforge.net/projects/yamos/\n"
"\n"
"***************************************************************************/\n");
}
void gen_supportroutines( FILE *fp )
{
char *bn = arg_basename;
fprintf(fp, "%s%s%s", arg_storm ? "/// " : "", arg_storm ? bn : "", arg_storm ? "_NewObject()\n" : "");
fprintf(fp, "Object * %s_NewObject(CONST_STRPTR className, ...)\n", bn);
fprintf(fp, "{\n");
fprintf(fp, " Object *obj = NULL;\n");
fprintf(fp, " unsigned int i;\n");
fprintf(fp, "\n");
fprintf(fp, " ENTER();\n");
fprintf(fp, "\n");
fprintf(fp, " for(i = 0; i < NUMBEROFCLASSES; i++)\n");
fprintf(fp, " {\n");
fprintf(fp, " if(strcmp(MCCInfo[i].Name, className) == 0)\n");
fprintf(fp, " {\n");
fprintf(fp, " unsigned int j;\n");
fprintf(fp, " struct TagItem tags[128];\n");
fprintf(fp, " va_list args;\n");
fprintf(fp, "\n");
fprintf(fp, " va_start(args, className);");
fprintf(fp, "\n");
fprintf(fp, " for(j=0; j < (sizeof(tags) / sizeof(tags[0])); j++)\n");
fprintf(fp, " {\n");
fprintf(fp, " tags[j].ti_Tag = va_arg(args, ULONG);\n");
fprintf(fp, " if(tags[j].ti_Tag != TAG_DONE)\n");
fprintf(fp, " tags[j].ti_Data = va_arg(args, ULONG);\n");
fprintf(fp, " else\n");
fprintf(fp, " {\n");
fprintf(fp, " tags[j].ti_Data = 0;\n");
fprintf(fp, " break;\n");
fprintf(fp, " }\n");
fprintf(fp, " }\n");
fprintf(fp, "\n");
fprintf(fp, " #if defined(DEBUG)\n");
fprintf(fp, " if(j >= (sizeof(tags) / sizeof(tags[0])))\n");
fprintf(fp, " {\n");
fprintf(fp, " E(DBF_ALWAYS, \"FATAL ERROR: size of tags[%%ld] array exhausted or no TAG_DONE in %s_NewObject(%%s, ...) call!!!!\", (sizeof(tags) / sizeof(tags[0])), className);\n", bn);
fprintf(fp, " ASSERT(j < (sizeof(tags) / sizeof(tags[0])));\n");
fprintf(fp, " }\n");
fprintf(fp, " #endif\n");
fprintf(fp, "\n");
fprintf(fp, " obj = NewObjectA(%sClasses[i]->mcc_Class, NULL, (struct TagItem *)&tags);\n", bn);
fprintf(fp, "\n");
fprintf(fp, " va_end(args);\n");
fprintf(fp, "\n");
fprintf(fp, " break;\n");
fprintf(fp, " }\n");
fprintf(fp, " }\n");
fprintf(fp, "\n");
fprintf(fp, " RETURN(obj);\n");
fprintf(fp, " return obj;\n");
fprintf(fp, "}\n");
fprintf(fp, "%s\n", arg_storm ? "\n///" : "");
fprintf(fp, "%s%s%s", arg_storm ? "/// " : "", arg_storm ? bn : "", arg_storm ? "_SetupClasses()\n" : "");
fprintf(fp, "BOOL %s_SetupClasses(const char **failClass, const char **failSuperClass)\n", bn);
fprintf(fp, "{\n");
fprintf(fp, " BOOL success = TRUE;\n");
fprintf(fp, " unsigned int i;\n");
fprintf(fp, "\n");
fprintf(fp, " ENTER();\n");
fprintf(fp, "\n");
fprintf(fp, " *failClass = NULL;\n");
fprintf(fp, " *failSuperClass = NULL;\n");
fprintf(fp, "\n");
fprintf(fp, " memset(%sClasses, 0, sizeof(%sClasses));\n", bn, bn);
fprintf(fp, " for (i = 0; i < NUMBEROFCLASSES; i++)\n");
fprintf(fp, " {\n");
fprintf(fp, " const char *superClassName;\n");
fprintf(fp, " struct MUI_CustomClass *superMCC;\n");
fprintf(fp, " ULONG classdataSize;\n");
fprintf(fp, "\n");
fprintf(fp, " if(MCCInfo[i].SuperMCCIndex == -1)\n");
fprintf(fp, " {\n");
fprintf(fp, " superClassName = MCCInfo[i].SuperClass;\n");
fprintf(fp, " superMCC = NULL;\n");
fprintf(fp, " }\n");
fprintf(fp, " else\n");
fprintf(fp, " {\n");
fprintf(fp, " superClassName = NULL;\n");
fprintf(fp, " superMCC = %sClasses[MCCInfo[i].SuperMCCIndex];\n", bn);
fprintf(fp, " if(superMCC == NULL)\n");
fprintf(fp, " {\n");
fprintf(fp, " E(DBF_STARTUP, \"superclass '%%s' of class '%%s' not yet created!\", MCCInfo[i].SuperClass, MCCInfo[i].Name);\n");
fprintf(fp, " *failClass = MCCInfo[i].Name;\n");
fprintf(fp, " *failSuperClass = MCCInfo[i].SuperClass;\n");
fprintf(fp, " success = FALSE;\n");
fprintf(fp, " break;\n");
fprintf(fp, " }\n");
fprintf(fp, " }\n");
fprintf(fp, "\n");
fprintf(fp, " if(MCCInfo[i].GetSize != NULL)\n");
fprintf(fp, " classdataSize = MCCInfo[i].GetSize();\n");
fprintf(fp, " else\n");
fprintf(fp, " classdataSize = 0;\n");
fprintf(fp, "\n");
fprintf(fp, " D(DBF_STARTUP, \"creating class '%%s' as subclass of '%%s'\", MCCInfo[i].Name, MCCInfo[i].SuperClass);\n");
fprintf(fp, " %sClasses[i] = MUI_CreateCustomClass(NULL, superClassName, superMCC, classdataSize, MCCInfo[i].Dispatcher);\n", bn);
fprintf(fp, " if(%sClasses[i] == NULL)\n", bn);
fprintf(fp, " {\n");
fprintf(fp, " E(DBF_STARTUP, \"failed to create class '%%s' as subclass of '%%s'\", MCCInfo[i].Name, MCCInfo[i].SuperClass);\n");
fprintf(fp, " *failClass = MCCInfo[i].Name;\n");
fprintf(fp, " *failSuperClass = MCCInfo[i].SuperClass;\n");
fprintf(fp, " success = FALSE;\n");
fprintf(fp, " break;\n");
fprintf(fp, " }\n");
fprintf(fp, " else\n");
fprintf(fp, " {\n");
fprintf(fp, " if(LIB_VERSION_IS_AT_LEAST(MUIMasterBase, 20, 0) == TRUE)\n");
fprintf(fp, " %sClasses[i]->mcc_Class->cl_ID = (STRPTR)MCCInfo[i].Name;\n", bn);
fprintf(fp, " }\n");
fprintf(fp, " }\n");
fprintf(fp, "\n");
fprintf(fp, " if(success == FALSE)\n");
fprintf(fp, " %s_CleanupClasses();\n", bn);
fprintf(fp, "\n");
fprintf(fp, " RETURN(success);\n");
fprintf(fp, " return success;\n");
fprintf(fp, "}\n");
fprintf(fp, "%s", arg_storm ? "\n///" : "");
fprintf(fp, "\n");
fprintf(fp, "%s%s%s", arg_storm ? "/// " : "", arg_storm ? bn : "", arg_storm ? "_CleanupClasses()\n" : "");
fprintf(fp, "void %s_CleanupClasses(void)\n", bn);
fprintf(fp, "{\n");
fprintf(fp, " int i;\n");
fprintf(fp, "\n");
fprintf(fp, " ENTER();\n");
fprintf(fp, "\n");
fprintf(fp, " for(i = NUMBEROFCLASSES-1; i >= 0; i--)\n");
fprintf(fp, " {\n");
fprintf(fp, " if(%sClasses[i] != NULL)\n", bn);
fprintf(fp, " {\n");
fprintf(fp, " if(%sClasses[i]->mcc_Class->cl_ObjectCount != 0)\n", bn);
fprintf(fp, " E(DBF_STARTUP, \"class '%%s' has still %%ld living objects!\", MCCInfo[i].Name, %sClasses[i]->mcc_Class->cl_ObjectCount);\n", bn);
fprintf(fp, "\n");
fprintf(fp, " MUI_DeleteCustomClass(%sClasses[i]);\n", bn);
fprintf(fp, " %sClasses[i] = NULL;\n", bn);
fprintf(fp, " }\n");
fprintf(fp, " }\n");
fprintf(fp, "\n");
fprintf(fp, " LEAVE();\n");
fprintf(fp, "}\n");
if(arg_storm)
{
fprintf(fp, "\n");
fprintf(fp, "///\n");
}
fprintf(fp, "\n");
}
int gen_source( char *destfile, struct list *classlist )
{
FILE *fp;
struct classdef *nextcd;
struct overloaddef *nextod;
struct declaredef *nextdd;
struct node *n, *nn;
int goOn;
int listindex;
if(arg_v)
fprintf(stdout, "Creating source : %s\n", destfile);
if((fp = fopen(destfile, "w")) == NULL)
{
fprintf(stderr, "ERROR: Unable to open %s\n", destfile);
return 0;
}
/***************************************/
/* Write header... */
/***************************************/
gen_gpl(fp);
fprintf(fp, "\n");
fprintf(fp, "/* " EDIT_WARNING " */\n");
fprintf(fp, "\n");
fprintf(fp, "#define INCLUDE_KITCHEN_SINK 1\n");
fprintf(fp, "#include \"Classes.h\"\n");
fprintf(fp, "#include \"Debug.h\"\n");
fprintf(fp, "\n");
fprintf(fp, "struct MUI_CustomClass *%sClasses[NUMBEROFCLASSES];\n", arg_basename);
fprintf(fp, "\n");
/***************************************/
/* Write dispatchers... */
/***************************************/
fprintf(fp, "/*** Custom Class Dispatchers ***/\n");
for(nn = NULL; (nn = list_getnext(classlist, nn, (void **)&nextcd)); )
{
if(arg_storm)
fprintf(fp, "/// %sDispatcher()\n", nextcd->name);
fprintf(fp, "SDISPATCHER(%sDispatcher)\n", nextcd->name);
fprintf(fp, "{\n switch(msg->MethodID)\n {\n");
/* Write OVERLOADs */
for(n = NULL; (n = list_getnext(&nextcd->overloadlist, n, (void **)&nextod)); )
{
fprintf(fp, " case %-40s: return m_%s_%-30s(cl, obj, msg);\n", nextod->name, nextcd->name, nextod->name);
}
/* Write DECLAREs */
for(n = NULL; (n = list_getnext(&nextcd->declarelist, n, (void **)&nextdd)); )
{
char name[128];
snprintf(name, sizeof(name), "MUIM_%s_%s", nextcd->name, nextdd->name);
fprintf(fp, " case %-40s: return m_%s_%-30s(cl, obj, (APTR)msg);\n", name, nextcd->name, nextdd->name);
}
fprintf(fp, " }\n");
fprintf(fp, " \n");
fprintf(fp, " return DoSuperMethodA(cl, obj, msg);\n");
fprintf(fp, "}\n");
fprintf(fp, "\n");
if(arg_storm)
fprintf(fp, "///\n");
}
/*****************************************/
/* Write MCCInfo struct */
/*****************************************/
fprintf(fp, "\n");
fprintf(fp, "/*** Custom Class Support ***/\n");
if(arg_storm)
fprintf(fp, "/// struct MCCInfo\n");
fprintf(fp, "const struct\n");
fprintf(fp, "{\n");
fprintf(fp, " CONST_STRPTR Name;\n");
fprintf(fp, " CONST_STRPTR SuperClass;\n");
fprintf(fp, " LONG SuperMCCIndex;\n");
fprintf(fp, " ULONG (*GetSize)(void);\n");
fprintf(fp, " APTR Dispatcher;\n");
fprintf(fp, "} MCCInfo[NUMBEROFCLASSES] =\n");
fprintf(fp, "{\n");
/* do a breadth search first style iteration over all
* classes to resolve possible dependencies between
* private classes
*/
listindex = 0;
do
{
goOn = 0;
for(n = NULL; (n = list_getnext(classlist, n, (void **) &nextcd)); )
{
/* handle unfinished class nodes only for which either the class is a subclass
* of a public class (supernode == NULL), or for which the superclass' list
* index in known already
*/
if(nextcd->finished == 0 && (nextcd->supernode == NULL || nextcd->supernode->index != -1))
{
if(nextcd->classdata != NULL)
fprintf(fp, " { MUIC_%s, %s, %d, %sGetSize, ENTRY(%sDispatcher) },\n", nextcd->name, nextcd->superclass, nextcd->supernode != NULL ? nextcd->supernode->index : -1, nextcd->name, nextcd->name);
else
fprintf(fp, " { MUIC_%s, %s, %d, NULL, ENTRY(%sDispatcher) },\n", nextcd->name, nextcd->superclass, nextcd->supernode != NULL ? nextcd->supernode->index : -1, nextcd->name);
/* remember index within the list of this class
* this one will be used later for depending subclasses
*/
nextcd->index = listindex;
listindex++;
if(nextcd->supernode != NULL)
{
goOn = 1;
}
/* mark this class as finished */
nextcd->finished = 1;
}
}
}
while(goOn == 1);
/* if we handled less classes than are in our list then we most probably
* skipped them because of a dependency loop
*/
if(listindex < classlist->cnt)
{
fprintf(fp, "#error %ld unfinished classes, possible dependency loops in these classes:", classlist->cnt-listindex);
for(n = NULL; (n = list_getnext(classlist, n, (void **) &nextcd)); )
{
/* handle all still unfinished class nodes with private superclasses */
if(nextcd->finished == 0 && nextcd->supernode != NULL)
fprintf(fp, " %s (subclass of %s)", nextcd->name, nextcd->supernode->name);
}
fprintf(fp, "\n");
}
fprintf(fp, "};\n");
fprintf(fp, "\n");
if(arg_storm)
fprintf(fp, "///\n");
/*****************************************/
/* Append support routines */
/*****************************************/
gen_supportroutines(fp);
fclose(fp);
return 1;
}
int gen_header( char *destfile, struct list *classlist )
{
FILE *fp;
char *bn = arg_basename, *cn;
struct classdef *nextcd;
struct declaredef *nextdd;
struct overloaddef *nextod;
struct node *n, *nn;
if(arg_v)
fprintf(stdout, "Creating header : %s\n", destfile);
if((fp = fopen(destfile, "w")) == NULL)
{
fprintf(stderr, "ERROR: Unable to open %s\n", destfile);
return 0;
}
/***************************************/
/* Write header... */
/***************************************/
gen_gpl(fp);
fprintf(fp, "\n");
fprintf(fp, "#ifndef CLASSES_CLASSES_H\n");
fprintf(fp, "#define CLASSES_CLASSES_H\n");
fprintf(fp, "\n");
fprintf(fp, "/* " EDIT_WARNING " */\n");
fprintf(fp, "\n");
/* TODO: write class tree in header here */
/***************************************/
/* Write includes... */
/***************************************/
/*
fprintf(fp,
"#include <clib/alib_protos.h>\n"
"#include <libraries/mui.h>\n"
"#include <proto/intuition.h>\n"
"#include <proto/muimaster.h>\n"
"#include <proto/utility.h>\n");
*/
if(arg_includes[0])
{
char *nx, *p = arg_includes;
do
{
if((nx = strchr(p, ',')) != NULL)
*nx++ = 0;
fprintf(fp, "#include \"%s\"\n", p);
}
while ((p = nx) != NULL);
}
/***************************************/
/* Write misc... */
/***************************************/
fprintf(fp, "\n");
fprintf(fp, "#define NUMBEROFCLASSES %ld\n", classlist->cnt);
fprintf(fp, "\n");
fprintf(fp, "extern struct MUI_CustomClass *%sClasses[NUMBEROFCLASSES];\n", bn);
fprintf(fp, "\n");
/***************************************/
/* Class loop */
/***************************************/
for(nn = NULL; (nn = list_getnext(classlist, nn, (void **) &nextcd)); )
{
cn = nextcd->name;
fprintf(fp, "#include \"%s.h\"\n", cn);
/***************************************/
/* Write protos for this class */
/***************************************/
if(nextcd->classdata != NULL)
fprintf(fp, "ULONG %sGetSize(void);\n", cn);
/* Write OVERLOADs */
for(n = NULL; (n = list_getnext(&nextcd->overloadlist, n, (void **)&nextod));)
{
fprintf(fp, "ULONG m_%s_%s(struct IClass *cl, Object *obj, Msg msg);\n", nextcd->name, nextod->name);
}
/* Write DECLAREs */
for(n = NULL; (n = list_getnext(&nextcd->declarelist, n, (void **)&nextdd));)
{
fprintf(fp, "ULONG m_%s_%s(struct IClass *cl, Object *obj, struct MUIP_%s_%s *msg);\n", nextcd->name, nextdd->name, cn, nextdd->name);
}
fprintf(fp, "\n");
}
fprintf(fp, "\n#endif /* CLASSES_CLASSES_H */\n\n");
fclose(fp);
return 1;
}
int gen_classheaders( struct list *classlist )
{
struct node *n;
struct node *nn;
struct classdef *nextcd;
struct declaredef *nextdd;
struct exportdef *nexted;
struct attrdef *nextad;
struct includedef *nextid;
FILE *fp;
for(n = NULL; (n = list_getnext(classlist, n, (void **)&nextcd));)
{
char crcname[128];
char *cn = nextcd->name;
char *bn = arg_basename;
unsigned long old_crc;
unsigned long new_crc;
/* get the previous CRC of the current class */
snprintf(crcname, sizeof(crcname), "%s.crc", cn);
myaddpart(arg_classdir, crcname, 255);
old_crc = read_crc(arg_classdir);
*mypathpart(arg_classdir) = 0;
/* calculate the CRC of the current class */
new_crc = gen_class_crc(nextcd, 0);
/* generate a new header only if there was no CRC before
* or if the old CRC does not match the new one
*/
if(old_crc == INVALID_CRC || old_crc != new_crc)
{
char name[128], buf[128], *p;
snprintf(name, sizeof(name), "%s_cl.h", cn);
myaddpart(arg_classdir, name, 255);
if(arg_v)
fprintf(stdout, "Creating private class header: %s\n", arg_classdir);
if((fp = fopen(arg_classdir, "w")) == NULL)
{
fprintf(stderr, "WARNING: Unable to write %s\n", name);
*mypathpart(arg_classdir) = 0;
continue;
}
strncpy(buf, cn, 127);
for (p = buf; *p; p++)
*p = toupper(*p);
*mypathpart(arg_classdir) = 0;
/* write the gpl to this class header also */
gen_gpl(fp);
fprintf(fp, "\n");
fprintf(fp, "/* " EDIT_WARNING " */\n");
fprintf(fp, "\n");
fprintf(fp, "#ifndef %s_CL_H\n", buf);
fprintf(fp, "#define %s_CL_H 1\n", buf);
fprintf(fp, "\n");
#if 0
fprintf(fp, "#ifndef CLASSES_CLASSES_H\n");
fprintf(fp, " #define INCLUDE_KITCHEN_SINK 1\n");
fprintf(fp, " #include \"Classes.h\"\n");
fprintf(fp, "#endif /* CLASSES_CLASSES_H */\n");
fprintf(fp, "\n");
#endif
fprintf(fp, "#ifndef CLIB_ALIB_PROTOS_H\n");
fprintf(fp, " #include <clib/alib_protos.h>\n");
fprintf(fp, "#endif\n");
fprintf(fp, "#ifndef PROTO_EXEC_H\n");
fprintf(fp, " #include <proto/exec.h>\n");
fprintf(fp, "#endif\n");
fprintf(fp, "#ifndef PROTO_INTUITION_H\n");
fprintf(fp, " #include <proto/intuition.h>\n");
fprintf(fp, "#endif\n");
fprintf(fp, "#ifndef PROTO_UTILITY_H\n");
fprintf(fp, " #include <proto/utility.h>\n");
fprintf(fp, "#endif\n");
fprintf(fp, "#ifndef EXEC_TYPES_H\n");
fprintf(fp, " #include <exec/types.h>\n");
fprintf(fp, "#endif\n");
fprintf(fp, "#ifndef LIBRARIES_MUI_H\n");
fprintf(fp, " #include <libraries/mui.h>\n");
fprintf(fp, "#endif\n");
fprintf(fp, "#ifndef CLASSES_CLASSES_EXTRA_H\n");
fprintf(fp, " #include \"ClassesExtra.h\"\n");
fprintf(fp, "#endif\n");
fprintf(fp, "#ifndef EXTRASRC_H\n");
fprintf(fp, " #include \"extrasrc.h\"\n");
fprintf(fp, "#endif\n");
fprintf(fp, "#include \"%s/%s.h\"\n", arg_classdir, cn);
fprintf(fp, "\n");
fprintf(fp, "#define inittags(msg) (((struct opSet *)msg)->ops_AttrList)\n");
fprintf(fp, "#define GETDATA struct Data *data = (struct Data *)INST_DATA(cl,obj)\n");
fprintf(fp, "#define DECLARE(method) ULONG m_%s_## method(UNUSED struct IClass *cl, UNUSED Object *obj, UNUSED struct MUIP_%s_## method *msg )\n", cn, cn);
fprintf(fp, "#define OVERLOAD(method) ULONG m_%s_## method(UNUSED struct IClass *cl, UNUSED Object *obj, UNUSED Msg msg )\n", cn);
fprintf(fp, "#define METHOD(method) MUIM_%s_## method\n", cn);
fprintf(fp, "#define ATTR(attr) MUIA_%s_## attr\n", cn);
fprintf(fp, "\n");
if(nextcd->classdata != NULL)
{
fprintf(fp, "/* Exported CLASSDATA */\n");
fprintf(fp, "\n");
fprintf(fp, "struct Data\n");
fprintf(fp, "{\n");
fprintf(fp, "%s\n", nextcd->classdata);
fprintf(fp, "};\n");
fprintf(fp, "\n");
fprintf(fp, "ULONG %sGetSize( void ) { return sizeof(struct Data); }\n", cn);
}
fprintf(fp, "\n");
fprintf(fp, "#endif /* %s_CL_H */\n", buf);
fclose(fp);
snprintf(name, sizeof(name), "%s.h", cn);
myaddpart(arg_classdir, name, 255);
if(arg_v)
fprintf(stdout, "Creating public class header: %s\n", arg_classdir);
if((fp = fopen(arg_classdir, "w")) == NULL)
{
fprintf(stderr, "WARNING: Unable to write %s\n", name);
*mypathpart(arg_classdir) = 0;
continue;
}
strncpy(buf, cn, 127);
for (p = buf; *p; p++)
*p = toupper(*p);
/* write the gpl to this class header also */
gen_gpl(fp);
fprintf(fp, "\n");
fprintf(fp, "/* " EDIT_WARNING " */\n");
fprintf(fp, "\n");
fprintf(fp, "#ifndef MUI_%s_H\n", buf);
fprintf(fp, "#define MUI_%s_H 1\n", buf);
fprintf(fp, "\n");
fprintf(fp, "#ifndef SDI_COMPILER_H\n");
fprintf(fp, " #include \"SDI_compiler.h\"\n");
fprintf(fp, "#endif\n");
fprintf(fp, "\n");
/*************************************************/
/* Write include statements text for this class */
/*************************************************/
if(nextcd->includelist.cnt)
{
fprintf(fp, "/* Exported includes */\n\n");
for (nn = NULL; (nn = list_getnext(&nextcd->includelist, nn, (void **) &nextid));)
fprintf(fp, "%s\n\n", nextid->includetext);
}
fprintf(fp, "Object * %s_NewObject(CONST_STRPTR className, ...);\n", bn);
fprintf(fp, "\n");
fprintf(fp, "#define MUIC_%s \"%s_%s\"\n", cn, bn, cn);
fprintf(fp, "\n");
fprintf(fp, "#define %sObject %s_NewObject(MUIC_%s\n", cn, bn, cn);
fprintf(fp, "\n");
for(nn = NULL; (nn = list_getnext(&nextcd->declarelist, nn, (void **) &nextdd));)
{
char name[128];
snprintf(name, sizeof(name), "MUIM_%s_%s", cn, nextdd->name);
fprintf(fp, "#define %-45s 0x%08x\n", name, gettagvalue(cn, 1));
}
/***************************************/
/* Write attributes for this class */
/***************************************/
for(nn = NULL; (nn = list_getnext(&nextcd->attrlist, nn, (void **) &nextad));)
{
char name[128];
snprintf(name, sizeof(name), "MUIA_%s_%s", cn, nextad->name);
fprintf(fp, "#define %-45s 0x%08x\n", name, gettagvalue(cn, 1));
}
fprintf(fp, "\n");
/***************************************/
/* Write exported text for this class */
/***************************************/
if(nextcd->exportlist.cnt)
{
fprintf(fp, "/* Exported text */\n\n");
for (nn = NULL; (nn = list_getnext(&nextcd->exportlist, nn, (void **) &nexted));)
fprintf(fp, "%s\n\n", nexted->exporttext);
}
/*****************************************/
/* Write MUIP_ structures for this class */
/*****************************************/
for(nn = NULL; (nn = list_getnext(&nextcd->declarelist, nn, (void **) &nextdd));)
{
fprintf(fp, "struct MUIP_%s_%s\n", cn, nextdd->name);
fprintf(fp, "{\n");
fprintf(fp, " ULONG methodID;\n");
if(strlen(nextdd->params) > 0)
{
char *lp;
for(p = lp = nextdd->params;;)
{
if((p = strpbrk(lp, ",;")) != NULL)
{
*p++ = '\0';
fprintf(fp, " %s;\n", lp);
lp = p;
}
else
{
if(strlen(lp) > 0)
fprintf(fp, " %s;\n", lp);
break;
}
}
}
fprintf(fp, "};\n");
fprintf(fp, "\n");
}
fprintf(fp, "#endif /* MUI_%s_H */\n", buf);
fclose(fp);
*mypathpart(arg_classdir) = 0;
}
/* save the new CRC for the next run */
snprintf(crcname, sizeof(crcname), "%s.crc", cn);
myaddpart(arg_classdir, crcname, 255);
write_crc(arg_classdir, new_crc);
*mypathpart(arg_classdir) = 0;
}
return 1;
}
/*******************************************************************************
*
* Makefile generation
*
*
* .p.s. I'm not an expert on makefiles, so someone improve this. ;-)
*
*******************************************************************************
*
*
*/
int gen_makefile( char *destfile, struct list *classlist )
{
struct classdef *nextcd;
char *cn, *p;
FILE *fp;
struct node *n;
for(p = arg_mkfile_ccopts;;)
{
if((p = strchr(p, ',')))
*p++ = ' ';
else
break;
}
if(arg_v)
fprintf(stdout, "Creating makefile : %s\n", destfile);
if((fp = fopen(destfile, "w")) == NULL)
{
fprintf(stderr, "ERROR: Unable to open %s\n", destfile);
return 0;
}
fprintf(fp, "#\n"
"# " EDIT_WARNING "\n"
"#\n");
fprintf(fp, "\nCC = %s\n"
"CCOPTS = %s\n\n", arg_mkfile_cc, arg_mkfile_ccopts);
fprintf(fp, "all.o : %s ", OBJECT_NAME);
for (n = NULL; (n = list_getnext(classlist, n, (void **) &nextcd));)
fprintf(fp, "%s.o ", nextcd->name);
fprintf(fp, "\n\tjoin " OBJECT_NAME " ");
for (n = NULL; (n = list_getnext(classlist, n, (void **) &nextcd));)
fprintf(fp, "%s.o ", nextcd->name);
fprintf(fp, "AS all.o\n\n");
fprintf(fp, "%s : %s %s ", OBJECT_NAME, SOURCE_NAME, HEADER_NAME);
fprintf(fp, "\n\t$(CC) %s %s %s $(CCOPTS)\n\n",
SOURCE_NAME, arg_mkfile_outarg, OBJECT_NAME);
for (n = NULL; (n = list_getnext(classlist, n, (void **) &nextcd));)
{
cn = nextcd->name;
fprintf(fp, "%s.o : %s.c\n"
"\t$(CC) %s.c %s %s.o $(CCOPTS)\n\n", cn, cn, cn, arg_mkfile_outarg, cn);
}
fclose(fp);
return 1;
}
/*******************************************************************************
*
* Argument processing and entry point
*
*******************************************************************************
*
*
*/
int getstrarg( char *argid, char *argline, char *dest, size_t destlen )
{
char *p;
size_t arglen = strlen(argid);
if (strncmp(argid, argline, arglen) != 0) return 0;
p = &argline[arglen];
strncpy(dest, p, destlen);
return 1;
}
int getblnarg( char *argid, char *argline, int *blnlong )
{
if (strncmp(argid, argline, strlen(argid)) != 0) return 0;
*blnlong = 1;
return 1;
}
int doargs( unsigned int argc, char *argv[] )
{
unsigned int i;
int success;
arg_gpl = 0;
for (i = 1; i < argc; i++)
{
if (getstrarg("-b", argv[i], arg_basename, 255)) continue;
if (getstrarg("-i", argv[i], arg_includes, 255)) continue;
if (getblnarg("-gpl", argv[i], &arg_gpl )) continue;
if (getblnarg("-storm", argv[i], &arg_storm )) continue;
if (getstrarg("-mkfile", argv[i], arg_mkfile, 255)) continue;
if (getblnarg("-v", argv[i], &arg_v )) continue;
if (getblnarg("-verbose",argv[i], &arg_v )) continue;
if (getblnarg("-q", argv[i], &arg_q )) continue;
}
if(arg_q == 0 || argc < 2)
fprintf(stdout, "GenClasses v%s by Andrew Bell <mechanismx@lineone.net>\n\n", verstr);
if(argc < 2)
{
fprintf(stderr,
"Usage: GenClasses <classdir> -b<basename> <options>\n"
"\n"
"Options:\n"
"\n"
" -b<basename> - basename (.i.e. YAM) used in sources (required)\n"
" -gpl - write GPL headers into sources\n");
fprintf(stderr,
" -storm - include storm/GoldED fold markers\n"
" -i<includes> - includes for Classes.h (.i.e. -i\"YAM.h\",\"YAM_hook.h\",<stdlib.h>\n"
" -v - verbose output while generating files\n"
" -q - keep output quiet and only output errors\n"
" -mkfile<makefile>,<cc>,<outarg>,<ccopts,...> - Create a makefile\n"
" (.i.e. -mkfileVMakefile,vc,-o,-O3,-+)\n");
return 0;
}
success = 1;
strncpy(arg_classdir, argv[1], 255);
if (arg_classdir[0] == 0 || arg_classdir[0] == '-')
{
fprintf(stderr, "No class dir specified, using current directory.\n");
strcpy(arg_classdir, "");
}
if (!arg_basename[0])
{
success = 0;
fprintf(stderr, "ERROR: You MUST provide a basename using the -b argument\n");
}
if(arg_mkfile[0])
{
if((arg_mkfile_cc = strchr(arg_mkfile, ','))) *arg_mkfile_cc++ = 0;
if(arg_mkfile_cc)
{
if((arg_mkfile_outarg = strchr(arg_mkfile_cc, ','))) *arg_mkfile_outarg++ = 0;
}
if(arg_mkfile_outarg)
{
if ((arg_mkfile_ccopts = strchr(arg_mkfile_outarg, ','))) *arg_mkfile_ccopts++ = 0;
}
if (!arg_mkfile_cc) arg_mkfile_cc = "cc";
if (!arg_mkfile_outarg) arg_mkfile_outarg = "-o";
if (!arg_mkfile_ccopts) arg_mkfile_ccopts = "";
strncpy(arg_mkfile_dest, arg_classdir, 255);
myaddpart(arg_mkfile_dest, arg_mkfile, 255);
}
return success;
}
int main( int argc, char *argv[] )
{
struct node *n;
struct list classlist;
list_init(&classlist);
if(!doargs(argc, argv))
return 0;
/* get memory for the hash */
if((collision_cnts = calloc(BUCKET_CNT, sizeof(int))) == NULL)
return 0;
if(scanclasses(arg_classdir, &classlist))
{
unsigned long old_crc;
unsigned long new_crc;
buildclasstree(&classlist);
/* get the previous CRC of all classes */
myaddpart(arg_classdir, CRC_NAME, 255);
old_crc = read_crc(arg_classdir);
*mypathpart(arg_classdir) = 0;
/* calculate the CRC of all just scanned classes */
new_crc = gen_crc(&classlist);
/* generate a new source only if there was no CRC before
* or if the old CRC does not match the new one
*/
if(old_crc == INVALID_CRC || old_crc != new_crc)
{
myaddpart(arg_classdir, SOURCE_NAME, 255);
gen_source(arg_classdir, &classlist);
*mypathpart(arg_classdir) = 0;
myaddpart(arg_classdir, HEADER_NAME, 255);
gen_header(arg_classdir, &classlist);
*mypathpart(arg_classdir) = 0;
gen_classheaders(&classlist);
}
/* save the new CRC for the next run */
myaddpart(arg_classdir, CRC_NAME, 255);
write_crc(arg_classdir, new_crc);
*mypathpart(arg_classdir) = 0;
if(arg_mkfile_dest[0])
gen_makefile(arg_mkfile_dest, &classlist);
}
while((n = list_remhead(&classlist)) != NULL)
{
free_classdef(n->data);
free(n);
}
return 0;
}