/* * gracula 3.0 * * Graphic Counter Language * * C source code * * Copyright 1999 G. Adam Stanislav * All rights reserved * * This program is released under the terms of the Whiz Kid Technomagic * No-Nonsense License. See the file NNS for details. * * Started: 11-Jan-1999 * Updated: 18-Jun-1999 * * Compile with: * * gcc -O3 gcl.c gd.c -lm -o gracula * * or just type: * * make * * NOTE: You need several files from the "gd" package from Boutell.Com * However, gd.c as distributed with this program has been slightly * patched to accomodate specific needs of Graphic Counter Language. * * Please send bug fixes to gcl@whizkidtech.net. If you find a bug and * cannot fix it, send a bug report, please. The more detailed the * description of the bug, the easier it is for me to fix. * * NOTE: This program contains some Unix-specific code (such as file * locking). Generally, the Unix-specific code can be modified * to work under other operating systems (such as Windows), but * it requires some work (unfortunately, Microsoft has decided * to implement certain Unix features differently from the * accepted practice -- don't blame me, blame them). To simplify * porting of this code, any Unix-specific code is marked as * such by a comment. Everything else is pure ANSI C. * * Look for the latest version at ftp.whizkidtech.net * * History: * * 22-Jun-1999 - released version 3.0 * Added signed and unsigned keywords * Added the #? directive (include file) * Added registers a-d * Added the now function * Added the random function * Added the srandom function * Added the increment keyword * Added the fudge keyword * Added the sigma keyword * Added the #~ directive (space) * Added the #; directive (dot) * Added the box frame * Added configurable group separators * Extended the group keyword to accept group separator * Added 16-integer stack * Added push keyword * Added pop keyword * Added tos function * Added conditionals (if, else) * Added compound statements * Added numeric expressions * Added ternary statements * Added array statements * Added Unicode support * Got rid of case sensitivity * Added natural language processing * Fixed an inhibitor bug that has been there since 1.00! * Added relays * Added the := (starts with) operator to inhibitors and relays * Fixed a spurious string processing bug in lexical analyzer * Added the Persian flaw, so read the docs!!! * * 30-May-1999 - released version 2.30 * Added the nocompile directive * Added the -g option switch * Added the -p option switch * Added the print keyword * Added input from environment vars and external programs * * 22-May-1999 - released version 2.20 * Changed name from gcl to gracula * Changed counter from unsigned int to unsigned long long. * This is optional as it may not work with compilers other * than gcc, or different C libraries. * Sped up keyword lookup by using a size-based switch. * Added the group keyword. * Added the reverse, reverse digits, and reverse commas keywords. * Made more compatible with standard C (thanks to Frank Denis). * Fixed some obscure bugs. * * 22-Mar-1999 - released version 2.10 * Added custom defaults, including custom default graphics. * Added a few keywords: portable, default, nodefault. * Fixed a problem 2.00 had with compiling under Linux. * Changed "annualy" to "annually". * Fixed minor bugs. * * 22-Feb-1999 - released version 2.00 * Added default graphic file names in specified directory. * Added array graphic. * Added colorize keyword. * Added conditional inhibitors. * Added URL redirection. * Added silent mode. * Added forking of another program in the background. * Added daily, weekly, monthly, and annual counters. * Added ability to adjust count manually. * Added ability to display date, time, time zone. * Added text output with SSI. * Fixed minor bugs. * Changed some ifs to switches for faster speed. * Increased optimization in makefile for speed. * * 21-Jan-1999 - released version 1.00 * Original release. * * Acknowledgements: * * Special thanks to Joe Price of Nevaeh Technologies Inc. He pointed out * two lines of code in version 2.00 which were FreeBSD specific and made * it impossible to compile under Linux. He also agreed to test future * versions under Linux before I release them. * * Thanks to Frank Denis for pointing out certain non-standard C * extensions in v2.10. Those are now changed to comply with the standard. * * Thanks to Dominique Voillemot for testing gracula under Red Hat Linux, * and for designing the Poorcount collection. * */ #include #include #include #include #include #include #include /* Unix specific */ #include /* Unix specific */ #include /* Unix specific */ #include "gcl.h" #include "gcldefaults.h" #include "gcldefaults.c" #if (GCLDEFAULTGROUP <= 0) #undef GCLDEFAULTGROUP #define GCLDEFAULTGROUP 3 #endif /* * Note on "graceful recovery." You will find comments about recovering * gracefully throughout this code. It is common in computer languages * to abort compilation or interpretation when a syntax error (or lack of * allocatable memory) is encountered. This is the right thing to do * (I suppose) under most circumstances. * * Alas, when working with CGI, aborting the interpreter would make * the browser wait for input that would never come. The browser would * give up eventually (one would hope), but might not show the rest of * the web page while waiting. This is not good, IMHO. * * Hence, the idea of graceful recovery: It is better to feed the browser * something than to let it wait. So, GCL will ALWAYS produce some output, * even if it is just a one-pixel GIF. That is unless you use the -s (silent) * switch, in which case it NEVER produces output. */ static FILE *gclfile; static FILE *gcldefaultfile = NULL; static unsigned int lineno = 1; static int lexstack = -1; static counter_t lexvalue; static char lexbuffer[LBSIZE]; static char const unexpected[] = "Unexpected end of file"; static char const colval[] = "Expected red, green, and blue, values."; static char const expiration[] = "Expected expiration value (seconds from now)"; static char const expalign[] = "Expected alignment value"; static char const ashiftval[] = "Expected alignment offset"; static char const shiftvalue[] = "Expected shift value or none"; static char const shifttype[] = "Expected shift type or none"; static char const fileformat[] = "Expected image file format"; static char const exppath[] = "Expected path"; static char const padvalue[] = "Expected pad value"; static char const numexp[] = "Expected numeric expression"; static char const regops[] = "Expected `=', `+=', `-=', `*=', `/=', `%=', `&=', `|=', `^=', `++', or `--'"; static char from[] = "HTTP_FROM"; static char httpcookie[] = "HTTP_COOKIE"; static void noframe(gdImagePtr, int, int, int, int); static void popup(gdImagePtr, int, int, int, int); static void button(gdImagePtr, int, int, int, int); static void defaultbutton(gdImagePtr, int, int, int, int); static void shadow(gdImagePtr, int, int, int, int); static void box(gdImagePtr, int, int, int, int); /* Declare the overhead of various frame types */ static gclframe const frames[FRAMES] = { { 0, 0, 0, 0, TRANSOK, noframe }, { 2, 2, 2, 2, NOTRANS, popup }, { 2, 2, 2, 2, NOTRANS, button }, { 3, 3, 3, 3, NOTRANS, defaultbutton }, { 2, 2, 3, 3, NOTRANS, shadow }, { 3, 3, 3, 3, NOTRANS, box } }; static char const characters[] = "0123456789-%^@<>~;,:.$+[/"; static char const echars[ACCEPTABLECHARS] = "0123456789-%^@<>,~;:TZ+ ."; static char const pchars[ACCEPTABLECHARS] = "0123456789-%^@<>,~;%^@>~;"; /* * Note that dash and minus are treated as separate entities. This * allows for greater flexibility. But you can use the same * graphic for both. In that case, you can use one file and * create a symbolic link to it with a different name. */ static char const * const defaultpicturename[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "dash", "colon", "time", "zone", "minus", "plus", "space", "dot", "comma", "head", "tail", "bkg", "tile", "array", "" }; static char const * const picname[] = { "digit `0'", "digit `1'", "digit `2'", "digit `3'", "digit `4'", "digit `5'", "digit `6'", "digit `7'", "digit `8'", "digit `9'", "dash", "colon", "\"T\"", "\"Z\"", "minus", "plus", "space", "dot", "comma", "head", "tail", "background", "tile", "array", "picture directory" }; static char const * const graphictypesnames[] = { "head", "tail", "digits", "commas", "spaces", "dots", "dashes", "colons", "plusses", "minuses", "times", "zones" }; static char const * const definecharname[] = { "comma", "colon", "dash", "time", "zone" }; static char const * const definecharnameplural[] = { "commas", "colons", "dashes", "time character", "zone character" }; static char const * const framenames[] = { "none", "popup", "button", "defaultbutton", "shadow", "box" }; static char pathbuffer[MAXPATHLEN]; static char *filename = NULL; static char const * const graphictypes[] = { "?", "gif", "gd", "xbm" }; static char const * const day[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char const * const month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char const * const gcldefaultpads[] = { "GCLDEFAULTTPAD", "GCLDEFAULTBPAD", "GCLDEFAULTLPAD", "GCLDEFAULTRPAD" }; static char const * const gcldefaultkern[] = { "GCLDEFAULTKERNHEAD", "GCLDEFAULTKERNTAIL", "GCLDEFAULTKERNDIGITS", "GCLDEFAULTKERNCOMMAS", "GCLDEFAULTKERNSPACES", "GCLDEFAULTKERNDOTS", "GCLDEFAULTKERNDASHES", "GCLDEFAULTKERNCOLONS", "GCLDEFAULTKERNPLUSSES", "GCLDEFAULTKERNMINUSES", "GCLDEFAULTKERNTIMES", "GCLDEFAULTKERNZONES" }; static char const * const gcldefaulthalign[] = { "GCLDEFAULTHALIGNHEAD", "GCLDEFAULTHALIGNTAIL", "GCLDEFAULTHALIGNDIGITS", "GCLDEFAULTHALIGNCOMMAS", "GCLDEFAULTHALIGNSPACES", "GCLDEFAULTHALIGNDOTS", "GCLDEFAULTHALIGNDASHES", "GCLDEFAULTHALIGNCOLONS", "GCLDEFAULTHALIGNPLUSSES", "GCLDEFAULTHALIGNMINUSES", "GCLDEFAULTHALIGNTIMES", "GCLDEFAULTHALIGNZONES" }; static char const * const gcldefaultvalign[] = { "GCLDEFAULTVALIGNHEAD", "GCLDEFAULTVALIGNTAIL", "GCLDEFAULTVALIGNDIGITS", "GCLDEFAULTVALIGNCOMMAS", "GCLDEFAULTVALIGNSPACES", "GCLDEFAULTVALIGNDOTS", "GCLDEFAULTVALIGNDASHES", "GCLDEFAULTVALIGNCOLONS", "GCLDEFAULTVALIGNPLUSSES", "GCLDEFAULTVALIGNMINUSES", "GCLDEFAULTVALIGNTIMES", "GCLDEFAULTVALIGNZONES" }; static char const * const gcldefaulthashift[] = { "GCLDEFAULTHASHIFTHEAD", "GCLDEFAULTHASHIFTTAIL", "GCLDEFAULTHASHIFTDIGITS", "GCLDEFAULTHASHIFTCOMMAS", "GCLDEFAULTHASHIFTSPACES", "GCLDEFAULTHASHIFTDOTS", "GCLDEFAULTHASHIFTDASHES", "GCLDEFAULTHASHIFTCOLONS", "GCLDEFAULTHASHIFTPLUSSES", "GCLDEFAULTHASHIFTMINUSES", "GCLDEFAULTHASHIFTTIMES", "GCLDEFAULTHASHIFTZONES" }; static char const * const gcldefaultvashift[] = { "GCLDEFAULTVASHIFTHEAD", "GCLDEFAULTVASHIFTTAIL", "GCLDEFAULTVASHIFTDIGITS", "GCLDEFAULTVASHIFTCOMMAS", "GCLDEFAULTVASHIFTSPACES", "GCLDEFAULTVASHIFTDOTS", "GCLDEFAULTVASHIFTDASHES", "GCLDEFAULTVASHIFTCOLONS", "GCLDEFAULTVASHIFTPLUSSES", "GCLDEFAULTVASHIFTMINUSES", "GCLDEFAULTVASHIFTTIMES", "GCLDEFAULTVASHIFTZONES" }; static counter_t gclregs[NUMREGS] = {0}; static int slashnotneeded; static time_t timer; static struct tm *now; static unsigned int weeks; static gcldate today = { -1, -1, -1, -1 }; static gcltimezone gtz = { GCLDEFAULTTIMEZONE, GCLDEFAULTTIMEZONEOFFSET }; static gclreset rst = NEVER; static int whatami = SHOWCOUNT; static int seconds = 0; static int zonehours = 0; static int zoneminutes = 0; static char *gclpath = NULL; static char *gclprint = NULL; static char *redirect = NULL; static char *cookie = NULL; static char *cookies = NULL; static char *script = NULL; static char *scriptname = NULL; static cookiepair *cookiepairs = NULL; static fi *includefile = NULL; static gclredirection redirection = NOREDIRECTION; static int gotcookies = 0; static int numcookies = 0; static int inhibited = 0; static inhibit *firstinhibitor = NULL; static inhibit *lastinhibitor = NULL; static relay *firstrelayor = NULL; static relay *lastrelayor = NULL; static int nodefaultpicture[GRAPHICS] = { 0 }; static gclpic picture[GRAPHICS] = { {NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; static rgbcolor tt = { TTRED, TTGREEN, TTBLUE }; static rgbcolor bkg = { BKGRED, BKGGREEN, BKGBLUE }; static rgbcolor invis = { INVISRED, INVISGREEN, INVISBLUE }; static int one = 1; static int secure = 1; static scounter_t increment = 1; static scounter_t incrementrange[2] = { 1, 1 }; static int transparent = GCLDEFAULTTRANSPARENT; static int warnings = 0; static int vertical = GCLDEFAULTVERTICAL; static int expires = GCLDEFAULTEXPIRES; static unsigned int group = GCLDEFAULTGROUP; static unsigned int groupseparator = GCLDEFAULTGROUPSEPARATOR; static int revcom = GCLDEFAULTREVCOM; static int revdig = GCLDEFAULTREVDIG; static counter_t randomnumber; static scounter_t mean; static counter_t gclstack[16]; static unsigned int gclstackptr = 0; static unsigned int numericerror = 0; static unsigned int premature = 0; static unsigned int insidenumericexpression = 0; static gclalign alignflag = { { GCLDEFAULTHALIGNHEAD, GCLDEFAULTHALIGNTAIL, GCLDEFAULTHALIGNDIGITS, GCLDEFAULTHALIGNCOMMAS, GCLDEFAULTHALIGNSPACES, GCLDEFAULTHALIGNDOTS, GCLDEFAULTHALIGNDASHES, GCLDEFAULTHALIGNCOLONS, GCLDEFAULTHALIGNPLUSSES, GCLDEFAULTHALIGNMINUSES, GCLDEFAULTHALIGNTIMES, GCLDEFAULTHALIGNZONES }, { GCLDEFAULTVALIGNHEAD, GCLDEFAULTVALIGNTAIL, GCLDEFAULTVALIGNDIGITS, GCLDEFAULTVALIGNCOMMAS, GCLDEFAULTVALIGNSPACES, GCLDEFAULTVALIGNDOTS, GCLDEFAULTVALIGNDASHES, GCLDEFAULTVALIGNCOLONS, GCLDEFAULTVALIGNPLUSSES, GCLDEFAULTVALIGNMINUSES, GCLDEFAULTVALIGNTIMES, GCLDEFAULTVALIGNZONES } }; static gclalign const defaultalignflag = { { GCLDEFAULTHALIGNHEAD, GCLDEFAULTHALIGNTAIL, GCLDEFAULTHALIGNDIGITS, GCLDEFAULTHALIGNCOMMAS, GCLDEFAULTHALIGNSPACES, GCLDEFAULTHALIGNDOTS, GCLDEFAULTHALIGNDASHES, GCLDEFAULTHALIGNCOLONS, GCLDEFAULTHALIGNPLUSSES, GCLDEFAULTHALIGNMINUSES, GCLDEFAULTHALIGNTIMES, GCLDEFAULTHALIGNZONES }, { GCLDEFAULTVALIGNHEAD, GCLDEFAULTVALIGNTAIL, GCLDEFAULTVALIGNDIGITS, GCLDEFAULTVALIGNCOMMAS, GCLDEFAULTVALIGNSPACES, GCLDEFAULTVALIGNDOTS, GCLDEFAULTVALIGNDASHES, GCLDEFAULTVALIGNCOLONS, GCLDEFAULTVALIGNPLUSSES, GCLDEFAULTVALIGNMINUSES, GCLDEFAULTVALIGNTIMES, GCLDEFAULTVALIGNZONES } }; static gclalign ashift = { { GCLDEFAULTHASHIFTHEAD, GCLDEFAULTHASHIFTTAIL, GCLDEFAULTHASHIFTDIGITS, GCLDEFAULTHASHIFTCOMMAS, GCLDEFAULTHASHIFTSPACES, GCLDEFAULTHASHIFTDOTS, GCLDEFAULTHASHIFTDASHES, GCLDEFAULTHASHIFTCOLONS, GCLDEFAULTHASHIFTPLUSSES, GCLDEFAULTHASHIFTMINUSES, GCLDEFAULTHASHIFTTIMES, GCLDEFAULTHASHIFTZONES }, { GCLDEFAULTVASHIFTHEAD, GCLDEFAULTVASHIFTTAIL, GCLDEFAULTVASHIFTDIGITS, GCLDEFAULTVASHIFTCOMMAS, GCLDEFAULTVASHIFTSPACES, GCLDEFAULTVASHIFTDOTS, GCLDEFAULTVASHIFTDASHES, GCLDEFAULTVASHIFTCOLONS, GCLDEFAULTVASHIFTPLUSSES, GCLDEFAULTVASHIFTMINUSES, GCLDEFAULTVASHIFTTIMES, GCLDEFAULTVASHIFTZONES } }; static gclalign const defaultashift = { { GCLDEFAULTHASHIFTHEAD, GCLDEFAULTHASHIFTTAIL, GCLDEFAULTHASHIFTDIGITS, GCLDEFAULTHASHIFTCOMMAS, GCLDEFAULTHASHIFTSPACES, GCLDEFAULTHASHIFTDOTS, GCLDEFAULTHASHIFTDASHES, GCLDEFAULTHASHIFTCOLONS, GCLDEFAULTHASHIFTPLUSSES, GCLDEFAULTHASHIFTMINUSES, GCLDEFAULTHASHIFTTIMES, GCLDEFAULTHASHIFTZONES }, { GCLDEFAULTVASHIFTHEAD, GCLDEFAULTVASHIFTTAIL, GCLDEFAULTVASHIFTDIGITS, GCLDEFAULTVASHIFTCOMMAS, GCLDEFAULTVASHIFTSPACES, GCLDEFAULTVASHIFTDOTS, GCLDEFAULTVASHIFTDASHES, GCLDEFAULTVASHIFTCOLONS, GCLDEFAULTVASHIFTPLUSSES, GCLDEFAULTVASHIFTMINUSES, GCLDEFAULTVASHIFTTIMES, GCLDEFAULTVASHIFTZONES } }; static int hshiftflag = GCLDEFAULTHSHIFTFLAG; static int vshiftflag = GCLDEFAULTVSHIFTFLAG; static int hshift = GCLDEFAULTHSHIFT; #undef vshift static int vshift = GCLDEFAULTVSHIFT; static unsigned int frametype = GCLDEFAULTFRAMETYPE; static int kern[GRAPHICTYPES] = { GCLDEFAULTKERNHEAD, GCLDEFAULTKERNTAIL, GCLDEFAULTKERNDIGITS, GCLDEFAULTKERNCOMMAS, GCLDEFAULTKERNSPACES, GCLDEFAULTKERNDOTS, GCLDEFAULTKERNDASHES, GCLDEFAULTKERNCOLONS, GCLDEFAULTKERNPLUSSES, GCLDEFAULTKERNMINUSES, GCLDEFAULTKERNTIMES, GCLDEFAULTKERNZONES }; static int const defaultkern[GRAPHICTYPES] = { GCLDEFAULTKERNHEAD, GCLDEFAULTKERNTAIL, GCLDEFAULTKERNDIGITS, GCLDEFAULTKERNCOMMAS, GCLDEFAULTKERNSPACES, GCLDEFAULTKERNDOTS, GCLDEFAULTKERNDASHES, GCLDEFAULTKERNCOLONS, GCLDEFAULTKERNPLUSSES, GCLDEFAULTKERNMINUSES, GCLDEFAULTKERNTIMES, GCLDEFAULTKERNZONES }; static unsigned int pad[NUMPADS] = { GCLDEFAULTTPAD, GCLDEFAULTBPAD, GCLDEFAULTLPAD, GCLDEFAULTRPAD }; static unsigned int mindigits = 1; static int optimize = 0; static int silent = 0; static unsigned int complevel = 0; static unsigned skip = 0; static unsigned reduce = 0; static counter_t numericvalue; static int insertcomments = 0; static int compileonly = 0; static int debugging = 0; static int nocompile = 0; static int gclnocompile = 0; static int verbose = 0; static int localuse = 0; static int mdoverride = 0; static int help = 0; static int outputtext = 0; static int make = 0; static int portable = 0; static int shellcount = 0; static int nocommas = 0; static int publish = 0; static int gallery = 0; static int issigned = 0; static int issigma = 0; static unsigned char definechar[DEFINECHAR+1] = GCLDEFAULTDEFINECHAR; static unsigned char const definedchar[DEFINECHAR] = GCLDEFAULTDEFINECHAR; static int rgbwarning(int); static void cleanup(void); static void addinhibitor(char *, int, gclcondition, gclinhibitor, int); static void freeinhibitors(void); static void addrelayor(char*, int, gclcondition, gclrelayor, char*, int); static void freerelayors(void); static void htmlputc(unsigned char); static int timesprint(char *, int); static int zonesprint(char *); static int datesprint(char *); static void makedefaults(void); static void openarraypicture(int); static void openpicture(int); static counter_t getrandomnumber(int); static void setincrementrange(scounter_t, scounter_t); static int statement(void); static int expression(void); static void printfilename(void) { if (filename || includefile && includefile->filename) fprintf(stderr, "%s: ", includefile && includefile->filename ? includefile->filename : filename); } static void gclwarning(char *msg, ...) { va_list vl; if (warnings) { printfilename(); fprintf(stderr, "GCL Warning (line %u): ", lineno); if (msg != NULL) { va_start(vl, msg); vfprintf(stderr, msg, vl); va_end(vl); fprintf(stderr, ".\n"); } } } static void gcldebug(char *msg, ...) { va_list vl; if (debugging) { printfilename(); fprintf(stderr, "GCL Debug: "); if (msg != NULL) { va_start(vl, msg); vfprintf(stderr, msg, vl); va_end(vl); fprintf(stderr, ".\n"); } } } static int uninclude(void) { register fi *temp; if (includefile == NULL) return EOF; fclose(gclfile); gclfile = includefile->fh; temp = includefile; lineno = includefile->lineno; includefile = includefile->prev; if (temp->filename) free(temp->filename); free(temp); return '}'; } static int nonblank(void) { register int c; for (;;) { c = getc(gclfile); if (c == EOF) return uninclude(); else if (c == '\n') lineno++; else if ((unsigned int)c > ' ') return c; } } /* Lexically analyze GCL file */ static int lexanal(void) { int chars = 0; register int c, d; register int i; if (lexstack >= 0) { c = lexstack; lexstack = -1; if (c < 256) ungetc(c, gclfile); else return c; } for (;;) { chars = 0; c = nonblank(); if (c == EOF) return EOF; /* Check for a hash mark (or is it an octothorp?) */ else if (c == '#') { c = getc(gclfile); if (c == '!') { for(; (chars < LBSIZE) && (c != '\n') && (c != EOF); chars++) { c = getc(gclfile); lexbuffer[chars] = (char)c; } /* for */ lineno++; lexbuffer[chars-1] = '\0'; /* Skip trailing garbage */ for (chars--;chars;chars--) { if (lexbuffer[chars-1] <= ' ') lexbuffer[chars-1] = '\0'; else break; } /* If the line buffer is too small, skip to EOL or EOF. In a typical language we would issue an error message but since we work with CGI, we try to recover gracefully. */ while ((c != '\n') && (c != EOF)) c = getc(gclfile); /* Ignore if just `#!' */ if (chars == 2) continue; return GCLPATH; } else for (lexvalue = 0; lexvalue < GRAPHICS; lexvalue++) if (c == characters[lexvalue]) return GCLGRAPHICNUMBER; if (c == '?') return FILEINCLUDE; while ((c != '\n') && !feof(gclfile)) /* comment, skip to EOL */ c = getc(gclfile); } /* '#' */ else if (c == '@') return TODAY; /* Check for an integer */ else if (isdigit(c)) { do { lexbuffer[chars++] = (char)c; c = getc(gclfile); } while (isdigit(c) && chars < (LBSIZE-1)); if (c != EOF) ungetc(c, gclfile); lexbuffer[chars] = '\0'; lexvalue = (counter_t)strtoul(lexbuffer, NULL, 10); return INTEGER; } /* integer */ /* * Check for a quoted string. * * A string must not extend beyond the end of the line. * If it does, we try to recover gracefully by * assuming the string ends there. * * If that assumption is incorrect, we will probably * get a syntax error soon anyway. */ else if (c == '\"') { c = 0; for (; (chars < (LBSIZE)); chars++) { c = d = getc(gclfile); /* * In GCL, if a line ends with a backslash, * skip to the next non-empty line. * That line, however, may start with * a backslash. That special case was * not handled properly in versions * prior to 2.30. It is now... */ while (c == '\\') { if ((d = getc(gclfile)) == '\n') { while (d == '\n') { lineno++; d = getc(gclfile); } c = d; } else c = 0; } /* while (c == '\\') */ if ((d == EOF) || (c == '\"') || (c == '\n')) break; lexbuffer[chars] = (d == '\t') ? ' ' : (char)d; } /* for */ lexbuffer[chars] = '\0'; if (c == '\n') lineno++; return STRING; } /* quoted string */ /* Check for an environment string */ else if (c == '$') { register int bracket; register char *env; switch (c = getc(gclfile)) { case '{': bracket = '}'; c = getc(gclfile); break; case '(': bracket = ')'; c = getc(gclfile); break; default: bracket = 0; break; } for (; chars < (LBSIZE-1); c = getc(gclfile)) { if ((c == bracket) || (c == EOF) || (c == '\n') || ((bracket == 0) && (!isalnum(c)))) break; lexbuffer[chars++] = c; } if (bracket == 0) ungetc(c, gclfile); lexbuffer[chars] = '\0'; env = getenv(lexbuffer); if (env && *env) { strncpy(lexbuffer, env, LBSIZE-1); lexbuffer[LBSIZE] = '\0'; } else lexbuffer[0] = '\0'; lexvalue = strtoul(lexbuffer, NULL, 0); return GCLENV; } /* Check for a command string */ else if (c == '`') { register FILE *pipe; for (c = d = getc(gclfile); (chars < LBSIZE - 1); c = d = getc(gclfile)) { /* See comment above about backslashes. */ while (c == '\\') { if ((d = getc(gclfile)) == '\n') { while (d == '\n') { d = getc(gclfile); lineno++; } c = d; } else c = 0; } if ((d == EOF) || (c == '`') || (c == '\n')) break; /* * Note that while in regular strings * we replace tabs with spaces, here * we do not. We let the shell deal * with them. */ lexbuffer[chars++] = d; } lexbuffer[chars] = '\0'; if (c == '\n') lineno++; /* Unix specific */ if ((pipe = popen(lexbuffer, "r")) != NULL) { for (chars = 0, c = getc(pipe); (chars < LBSIZE) && (c != EOF); c = getc(pipe)) lexbuffer[chars++] = c < ' ' ? ' ' : c; lexbuffer[chars - (chars && lexbuffer[chars-1] == ' ')] = '\0'; pclose(pipe); } else lexbuffer[0] = '\0'; lexvalue = strtoul(lexbuffer, NULL, 0); return GCLENV; } /* * Try unquoted string. It can consist of alphabetic characters, * as well as any character with its hight bit set. This allows * us to process UTF-8 encoded Unicode text, as well as text * created to the ISO 8859 series of standards. */ else if (isalpha(c) || (c > 0x7F)) { for (; (chars < (LBSIZE-1)) && (isalpha(c) || (c > 0x7F)); chars++, c = getc(gclfile)) lexbuffer[chars] = isalpha(c) ? (char)tolower(c) : c; lexbuffer[chars] = '\0'; if (c != EOF) ungetc(c, gclfile); /* Hopefully, it is a keyword */ for (i = 1; i < FRAMES; i++) { lvtoken(framenames[i], i, FRAMETYPE); } /* * Speed the rest of it up by using a switch based on the length * of the keyword. * * This makes the code admittedly less readable, but it executes * faster, especially since we already have the size in chars * and do not have to calculate it. */ switch (chars) { case 1: switch (c = tolower(*lexbuffer) - 'a') { case REGA: case REGB: case REGC: case REGD: lexvalue = c; return GCLREGISTER; } break; case 2: lvtoken("gd", GDSOURCE, PICTYPE); lvtoken("up", UP, VSHIFT); token( "if", AK); token( "or", LOR); break; case 3: lvtoken("gif", GIFSOURCE, PICTYPE); lvtoken("xbm", XBMSOURCE, PICTYPE); lvtoken("stz", STZ, TIMEZONE); lvtoken("utc", UTC, TIMEZONE); lvtoken("top", TOP, TOPBOTTOM); lvtoken("now", timer, INTEGER); token( "bkg", BACKGROUND); token( "pad", PAD); token( "end", '}'); token( "tri", TRI); if (strcmp(lexbuffer, "pop") == 0) { gclstackptr--; gclstackptr &= 0x0F; lexvalue = gclstack[gclstackptr]; return INTEGER; } if (strcmp(lexbuffer, "tos") == 0) { lexvalue = gclstack[(gclstackptr - 1) & 0x0F]; return INTEGER; } token( "and", LAND); token( "xor", LXOR); break; case 4: lvtoken("left", LEFT, HSHIFT); lvtoken("down", DOWN, VSHIFT); lvtoken("none", 0, NONE); lvtoken("head", HEAD, COUNTER); lvtoken("tail", TAIL, COUNTER); lvtoken("time", DEFINETIME, TEXTCHAR); lvtoken("zone", DEFINEZONE, TEXTCHAR); lvtoken("dash", DEFINEDASH, TEXTCHAR); lvtoken("when", WHEN, CONDITION); lvtoken("dots", DOTS, COUNTER); token( "kern", KERN); token( "from", FROM); token( "fork", FORK); token( "push", GCLPUSH); token( "then", TAK); token( "else", INAK); token( "plus", '+'); token( "sign", CMP); break; case 5: lvtoken("right", RIGHT, HSHIFT); lvtoken("times", TIMES, COUNTER); lvtoken("zones", ZONES, COUNTER); lvtoken("daily", DAILY, TIMEPERIOD); lvtoken("comma", DEFINECOMMA, TEXTCHAR); lvtoken("colon", DEFINECOLON, TEXTCHAR); lvtoken("count", COUNT, GCLREGISTER); lvtoken("relay", RELAY, RELAYOR); lvtoken("serve", SERVE, RELAYOR); token( "group", GROUP); token( "invis", INVIS); token( "trans", TRANSPARENT); token( "frame", FRAME); token( "align", TOKALIGN); token( "shift", SHIFT); token( "reset", PER); token( "print", GCLPRINT); token( "fudge", GCLFUDGE); token( "sigma", GCLSIGMA); token( "begin", '{'); token( "times", '*'); token( "minus", '-'); token( "equal", '='); break; case 6: lvtoken("middle", MIDDLE, VALIGN); lvtoken("bottom", BOTTOM, TOPBOTTOM); lvtoken("center", CENTER, HALIGN); lvtoken("digits", DIGITS, COUNTER); lvtoken("commas", COMMAS, COUNTER); lvtoken("spaces", SPACES, COUNTER); lvtoken("dashes", DASHES, COUNTER); lvtoken("colons", COLONS, COUNTER); lvtoken("unless", UNLESS, CONDITION); lvtoken("yearly", ANNUALY, TIMEPERIOD); lvtoken("weekly", WEEKLY, TIMEPERIOD); lvtoken("random", getrandomnumber(0), INTEGER); token( "secure", SECURE); token( "cookie", COOKIE); token( "silent", SILENT); /* * This counts on compiler optimization * to save space, working on the * assumption the two occurences of * "unsigned" will be merged into one... */ token("unsigned" + 2, SIGNEDCOUNTER); token( "equals", '='); token( "lesser", '<'); token( "larger", '>'); token( "modulo", '%'); break; case 7: lvtoken("plusses", PLUSSES, COUNTER); lvtoken("minuses", MINUSES, COUNTER); lvtoken("inhibit", INHIBIT, INHIBITOR); lvtoken("concede", CONCEDE, INHIBITOR); lvtoken("annualy", ANNUALY, TIMEPERIOD); lvtoken("monthly", MONTHLY, TIMEPERIOD); lvtoken("srandom", getrandomnumber(1), INTEGER); token( "expires", EXPIRES); token( "seconds", SECONDS); token( "default", USEDEFAULT); token( "reverse", GCLREVERSE); token( "divided", '/'); token( "smaller", '<'); token( "greater", '>'); token( "modulus", '%'); token( "include", FILEINCLUDE); break; case 8: lvtoken("annually", ANNUALY, TIMEPERIOD); token( "vertical", VERTICAL); token( "optimize", OPTIMIZE); token( "colorize", TEDTURNER); token( "redirect", REDIRECT); token( "portable", PORTABLEGCL); token( "unsigned", UNSIGNEDCOUNTER); token( "negative", '-'); token( "positive", '+'); break; case 9: token( "mindigits", MINDIGITS); token( "nodefault", NODEFAULT); token( "nocompile", NOCOMPILE); token( "increment", GCLINCREMENT); token( "otherwise", INAK); case 10: token( "horizontal", HORIZONTAL); token( "multiplied", '*'); break; } /* Just ignore anything else. */ } /* unquoted string */ else { /* possible operator, or error */ lexbuffer[0] = c; lexbuffer[1] = '\0'; lexbuffer[2] = '\0'; switch (c) { case '+': switch (c = nonblank()) { case '+': lexbuffer[1] = c; return INC; case '=': lexbuffer[1] = c; return ADD; default: ungetc(c, gclfile); return '+'; } break; case '-': switch (c = nonblank()) { case '-': lexbuffer[1] = c; return DEC; case '=': lexbuffer[1] = c; return SUB; default: ungetc(c, gclfile); return '-'; } break; case '>': switch (c = nonblank()) { case '>': lexbuffer[1] = c; return SHR; case '=': lexbuffer[1] = c; return GE; default: ungetc(c, gclfile); return '>'; } break; case '<': switch (c = nonblank()) { case '<': lexbuffer[1] = c; return SHL; case '=': lexbuffer[1] = c; return LE; case '>': lexbuffer[1] = c; return CMP; default: ungetc(c, gclfile); return '<'; } break; case '&': switch (c = nonblank()) { case '&': lexbuffer[1] = c; return LAND; case '=': lexbuffer[1] = c; return BAND; default: ungetc(c, gclfile); return '&'; } break; case '|': switch (c = nonblank()) { case '|': lexbuffer[1] = c; return LOR; case '=': lexbuffer[1] = c; return BOR; default: ungetc(c, gclfile); return '|'; } break; case '^': switch (c = nonblank()) { case '^': lexbuffer[1] = c; return LXOR; case '=': lexbuffer[1] = c; return XOR; default: ungetc(c, gclfile); return '^'; } break; case '*': case '/': case '%': case '!': case '=': case ':': switch (c = nonblank()) { case '=': lexbuffer[1] = '='; switch (lexbuffer[0]) { case '*': return MUL; case '/': return DIV; case '%': return MOD; case '!': return NE; case '=': return EQU; case ':': return PASSIGN; } break; default: ungetc(c, gclfile); /* fall through */ } /* fall through */ return lexbuffer[0]; /* Ignore punctuation */ case ',': case '.': case ';': case '\'': if (!insidenumericexpression) { chars = 0; break; } /* else fall through */ default: return c; } } } /* for(;;) */ } static int syntax(char const *errmsg) { printfilename(); fprintf(stderr, errmsg == lexbuffer ? "GCL Syntax error in line %u: `%s'.\n" : "GCL Syntax error in line %u (`%s'): %s.\n", lineno, lexbuffer, errmsg); return 1; } static int getinteger(void) { register int l; if (reduce) { reduce = 0; return INTEGER; } switch (lex) { case GCLREGISTER: lexvalue = gclregs[lexvalue]; /* fall through */ case GCLENV: case INTEGER: numericvalue = lexvalue; return INTEGER; case SIGNEDCOUNTER: numericvalue = issigned != 0; return INTEGER; case UNSIGNEDCOUNTER: numericvalue = issigned == 0; return INTEGER; default: return l; } } static int unaryexpression(void) { register int op; register int l; /* * Do not try to "optimize" this by just using switch (l = getinteger()). * While that would work, it would cause problems later on if an expression * is immediately followed by an assignment statement! */ switch (l = getinteger()) { /* * Following the philosophy of graceful recovery, we accept such * "weird" constructs as "++++--!!~--~-+A" without blinking an eye. */ case INC: /* ++ */ case DEC: /* -- */ l = '+'; /* fall through */ case '+': case '-': case '~': case '!': case '\\': case CMP: numericerror = 1; op = l; switch (l = unaryexpression()) { case INTEGER: if ((op == '-') || (issigned && (op == '\\') && ((scounter_t)numericvalue < 0))) numericvalue = -numericvalue; else if (op == '~') numericvalue = ~numericvalue; else if (op == '!') numericvalue = !numericvalue; else if (op == CMP) numericvalue = issigned && ((scounter_t)numericvalue > 0) || !issigned && (numericvalue > 0) ? 1 : numericvalue == 0 ? 0 : -1; return INTEGER; case EOF: return EOF; default: /* Only show this error once when called recursively */ if (numericerror) { numericerror = 0; syntax("Expected integral value"); } return l; } break; case '(': switch (expression()) { case INTEGER: switch (lex) { default: unputlex(l); syntax("Missing parenthesis"); /* fall through */ case ')': return INTEGER; } break; case EOF: syntax(unexpected); return EOF; default: syntax(numexp); return l; } break; /* Default may include INTEGER */ default: return l; } } static int mulexpression(void) { register int l, op; register counter_t temp; for (;;) switch (l = unaryexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case '*': case '/': case '%': switch (l = unaryexpression()) { case INTEGER: switch (op) { case '*': numericvalue *= temp; break; case '/': if (numericvalue) { if (!issigned) numericvalue = temp / numericvalue; else numericvalue = (scounter_t)temp / (scounter_t)numericvalue; } else numericvalue = temp; break; case '%': if (numericvalue) { if (!issigned) numericvalue = temp % numericvalue; else numericvalue = (scounter_t)temp % (scounter_t)numericvalue; } else numericvalue = temp; break; } reduce = 1; continue; case EOF: syntax(unexpected); return INTEGER; default: unputlex(l); syntax(numexp); return INTEGER; } break; default: unputlex(op); return INTEGER; } break; default: return l; } } static int addexpression(void) { register int l, op; register counter_t temp; for (;;) switch (l = mulexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case INC: case DEC: /* --, i.e. + */ case '+': case '-': switch (l = mulexpression()) { case INTEGER: numericvalue = temp + (op == '-' ? -numericvalue : numericvalue); reduce = 1; continue; case EOF: syntax(unexpected); return INTEGER; default: unputlex(l); syntax(numexp); return INTEGER; } break; default: unputlex(op); return INTEGER; } break; default: return l; } } static int shiftexpression(void) { register int l, op; register counter_t temp; switch (l = addexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case SHL: case SHR: switch (l = shiftexpression()) { case INTEGER: switch (op) { case SHL: numericvalue = temp << numericvalue; break; case SHR: if (!issigned) numericvalue = temp >> numericvalue; else numericvalue = (scounter_t)temp >> numericvalue; break; } return INTEGER; default: unputlex(l); break; } break; default: unputlex(op); break; } numericvalue = temp; return INTEGER; default: return l; } } static int ltgtexpression(void) { register int l, op; register counter_t temp; switch (l = shiftexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case '<': case '>': case LE: case GE: case CMP: switch (l = ltgtexpression()) { case INTEGER: switch (op) { case '<': if (!issigned) numericvalue = temp < numericvalue; else numericvalue = (scounter_t)temp < (scounter_t)numericvalue; break; case '>': if (!issigned) numericvalue = temp > numericvalue; else numericvalue = (scounter_t)temp > (scounter_t)numericvalue; break; case LE: if (!issigned) numericvalue = temp <= numericvalue; else numericvalue = (scounter_t)temp <= (scounter_t)numericvalue; break; case GE: if (!issigned) numericvalue = temp >= numericvalue; else numericvalue = (scounter_t)temp >= (scounter_t)numericvalue; break; case CMP: if (!issigned) numericvalue = temp > numericvalue ? 1 : temp == numericvalue ? 0 : -1; else numericvalue = (scounter_t)temp > (scounter_t)numericvalue ? 1 : temp == numericvalue ? 0 : -1; break; } return INTEGER; default: unputlex(l); break; } break; default: unputlex(op); break; } numericvalue = temp; return INTEGER; default: return l; } } static int eqexpression(void) { register int l, op; register counter_t temp; switch (l = ltgtexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case '=': case EQU: case NE: switch (l = eqexpression()) { case INTEGER: switch (op) { default: numericvalue = temp == numericvalue; break; case NE: numericvalue = temp != numericvalue; break; } return INTEGER; default: unputlex(l); break; } break; default: unputlex(op); break; } numericvalue = temp; return INTEGER; default: return l; } } static int bandexpression(void) { register int l, op; register counter_t temp; for (;;) switch (l = eqexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case '&': switch (l = eqexpression()) { case INTEGER: numericvalue &= temp; reduce = 1; continue; case EOF: syntax(unexpected); return INTEGER; default: unputlex(l); syntax(numexp); return INTEGER; } break; default: unputlex(op); return INTEGER; } break; default: return l; } } static int xorexpression(void) { register int l, op; register counter_t temp; for (;;) switch (l = bandexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case '^': switch (l = bandexpression()) { case INTEGER: numericvalue ^= temp; reduce = 1; continue; case EOF: syntax(unexpected); return INTEGER; default: unputlex(l); syntax(numexp); return INTEGER; } break; default: unputlex(op); return INTEGER; } break; default: return l; } } static int borexpression(void) { register int l, op; register counter_t temp; for (;;) switch (l = xorexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case '|': switch (l = xorexpression()) { case INTEGER: numericvalue |= temp; reduce = 1; continue; case EOF: syntax(unexpected); return INTEGER; default: unputlex(l); syntax(numexp); return INTEGER; } break; default: unputlex(op); return INTEGER; } break; default: return l; } } static int landexpression(void) { register int l, op; register counter_t temp; for (;;) switch (l = borexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case LAND: switch (l = borexpression()) { case INTEGER: numericvalue = temp && numericvalue; reduce = 1; continue; case EOF: syntax(unexpected); return INTEGER; default: unputlex(l); syntax(numexp); return INTEGER; } break; default: unputlex(op); return INTEGER; } break; default: return l; } } static int lxorexpression(void) { register int l, op; register counter_t temp; for (;;) switch (l = landexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case LXOR: switch (l = landexpression()) { case INTEGER: numericvalue = temp && !numericvalue || !temp && numericvalue; reduce = 1; continue; case EOF: syntax(unexpected); return INTEGER; default: unputlex(l); syntax(numexp); return INTEGER; } break; default: unputlex(op); return INTEGER; } break; default: return l; } } static int lorexpression(void) { register int l, op; register counter_t temp; for (;;) switch (l = lxorexpression()) { case INTEGER: temp = numericvalue; switch (op = lexanal()) { case LOR: switch (l = lxorexpression()) { case INTEGER: numericvalue = temp || numericvalue; reduce = 1; continue; case EOF: syntax(unexpected); return INTEGER; default: unputlex(l); syntax(numexp); return INTEGER; } break; default: unputlex(op); return INTEGER; } break; default: return l; } } int expression(void) { register int l; reduce = 0; insidenumericexpression++; l = lorexpression(); insidenumericexpression--; if (l == INTEGER) { register counter_t cond, true; switch (lex) { case '?': cond = numericvalue; switch (l = expression()) { case INTEGER: true = numericvalue; switch (lex) { case ':': switch (l = expression()) { case INTEGER: if (cond) numericvalue = true; break; default: syntax(numexp); unputlex(l); if (cond) numericvalue = cond; } break; default: unputlex(l); if (cond) numericvalue = cond; break; } break; default: unputlex(l); syntax(numexp); break; } break; default: unputlex(l); break; } return INTEGER; } return l; } static void parserror(void) { fprintf(stderr, "GCL Error: Parser out of memory in line %u.", lineno); if (nocompile == 0) { fprintf(stderr, " Compilation aborted."); nocompile = 1; } fprintf(stderr, "\n"); } /* Parse GCL file */ static int parse(void) { for (;;) switch (statement()) { case 1: return 1; case EOF: if (complevel) { strcpy(lexbuffer, "{' without `}"); syntax("Programmer needs a vacation"); return EOF; } else return 0; case '}': if (complevel) { complevel--; return '}'; } syntax("Programmer way out of control"); break; } } static int condition(gclrelayor r, char *url, gclcondition cond) { register int l, op; register char *tempstr; switch (lex) { case FROM: switch (lex) { case GCLENV: case STRING: if (!skip) addrelayor(from, 0, cond, r, url, '='); break; case EOF: if (!skip && url) free(url); return syntax(unexpected); default: if (!skip && url) free(url); synterr("Expected email address"); } break; case COOKIE: switch (lex) { case GCLENV: case STRING: if (!skip) { tempstr = strdup(lexbuffer); if (tempstr == NULL) parserror(); } switch (lex) { case '=': case PASSIGN: op = l; switch (lex) { case GCLENV: case STRING: if (!skip) addrelayor(tempstr, 1, cond, r, url, op); break; case EOF: if (!skip) { if (tempstr) free(tempstr); if (url) free(url); } return syntax(unexpected); default: if (!skip) { if (tempstr) free(tempstr); if (url) free(url); } synterr("Expected cookie value (text string)"); } break; case EOF: if (!skip) { if (tempstr) free(tempstr); if (url) free(url); } return syntax(unexpected); default: if (!skip) { if (tempstr) free(tempstr); if (url) free(url); } synterr("Expected `='"); } break; case EOF: if (!skip && url) free(url); return syntax(unexpected); default: if (!skip && url) free(url); synterr("Expected cookie name"); } break; case GCLENV: case STRING: if (!skip) { tempstr = strdup(lexbuffer); if (tempstr == NULL) parserror(); } switch (lex) { case '=': case PASSIGN: op = l; switch (lex) { case GCLENV: case STRING: if (!skip) addrelayor(tempstr, 0, cond, r, url, op); break; case EOF: if (!skip) { if (tempstr) free(tempstr); if (url) free(url); } return syntax(unexpected); default: if (!skip && tempstr) free(tempstr); synterr("Expected environment variable value"); } break; case EOF: if (!skip) { if (tempstr) free(tempstr); if (url) free(url); } return syntax(unexpected); default: if (!skip) { if (tempstr) free(tempstr); if (url) free(url); } synterr("Expected `='"); } break; case EOF: if (!skip && url) free(url); return syntax(unexpected); default: if (!skip && url) free(url); synterr("Expected environment variable name"); } return 0; } static int statement(void) { register int l, m; register counter_t temp; register int op; register char *tempstr; register gclinhibitor tempinhibitor; rgbcolor temprgb; register fi *fitemp; switch (lex) { case GCLPATH: if (!skip) { if (gclpath != NULL) free(gclpath); gclpath = strdup(lexbuffer); if (gclpath == NULL) { lineno--; parserror(); lineno++; } } break; case GCLGRAPHICNUMBER: temp = lexvalue; lex; if (l == NODEFAULT) { if (!skip) nodefaultpicture[temp] = 1; } else if (l == USEDEFAULT) { if (!skip) nodefaultpicture[temp] = 0; } else { if (!skip) { if (picture[temp].graphic != NULL) { free(picture[temp].graphic); picture[temp].graphic = NULL; } nodefaultpicture[temp] = 0; picture[temp].gtype = 0; } switch (l) { case EOF: return syntax(unexpected); case NONE: break; case GCLENV: case STRING: if (!skip) { picture[temp].graphic = strdup(lexbuffer); if (picture[temp].graphic == NULL) parserror(); } switch (lex) { case PICTYPE: if (!skip && picture[temp].graphic) picture[temp].gtype = lexvalue; break; case EOF: if (!skip && picture[temp].graphic) { free(picture[temp].graphic); picture[temp].graphic = NULL; } return syntax(unexpected); default: if (!skip && picture[temp].graphic) { free(picture[temp].graphic); picture[temp].graphic = NULL; } synterr(fileformat); } break; case '[': switch (signedinteger) { case INTEGER: if (!skip) picture[temp].x = numericvalue; switch (signedinteger) { case INTEGER: if (!skip) picture[temp].y = numericvalue; switch (signedinteger) { case INTEGER: if (!skip) picture[temp].dx = numericvalue; switch (signedinteger) { case INTEGER: if (!skip) picture[temp].dy = numericvalue; switch (lex) { default: /* Accept this error gracefully */ switch (l) { case EOF: syntax(unexpected); break; /* no return here */ default: syntax("Expected `]'"); unputlex(l); break; } /* fall through */ case ']': switch (temp) { case PICTUREDIRECTORY: case ARRAYPICTURE: syntax("Expected path, got array"); break; default: if (!skip) picture[temp].isarray = 1; break; } break; } break; case EOF: return syntax(unexpected); default: synterr("Expected y dimension"); } break; case EOF: return syntax(unexpected); default: synterr("Expected x dimension"); } break; case EOF: return syntax(unexpected); default: synterr("Expected starting y coordinate"); } break; case EOF: return syntax(unexpected); default: synterr("Expected starting x coordinate"); } break; default: synterr(exppath); } } break; case BACKGROUND: temprgb = bkg; switch (signedinteger){ case INTEGER: if (!skip) bkg.red = rgbwarning(numericvalue); switch (signedinteger){ case INTEGER: if (!skip) bkg.green = rgbwarning(numericvalue); switch (signedinteger) { case INTEGER: if (!skip) bkg.blue = rgbwarning(numericvalue); break; case EOF: if (!skip) { bkg.red = temprgb.red; bkg.green = temprgb.green; } return syntax(unexpected); default: if (!skip) { bkg.red = temprgb.red; bkg.green = temprgb.green; } synterr(colval); } break; case EOF: if (!skip) bkg.red = temprgb.red; return syntax(unexpected); default: if (!skip) bkg.red = temprgb.red; synterr(colval); } break; case USEDEFAULT: if (!skip) { bkg.red = BKGRED; bkg.green = BKGGREEN; bkg.blue = BKGBLUE; } break; case EOF: return syntax(unexpected); default: synterr(colval); } break; case INVIS: temprgb = invis; switch (signedinteger) { case INTEGER: invis.red = rgbwarning(numericvalue); switch (signedinteger) { case INTEGER: if (!skip) invis.green = rgbwarning(numericvalue); switch (signedinteger) { case INTEGER: if (!skip) invis.blue = rgbwarning(numericvalue); break; case EOF: if (!skip) { invis.red = temprgb.red; invis.green = temprgb.green; } return syntax(unexpected); default: if (!skip) { invis.red = temprgb.red; invis.green = temprgb.green; } synterr(colval); } break; case EOF: if (!skip) invis.red = temprgb.red; return syntax(unexpected); default: if (!skip) invis.red = temprgb.red; synterr(colval); } break; case USEDEFAULT: if (!skip) { invis.red = INVISRED; invis.green = INVISGREEN; invis.blue = INVISBLUE; } break; case EOF: return syntax(unexpected); default: synterr(colval); } break; case TRANSPARENT: switch (lex) { case NONE: if (!skip) transparent = 0; break; case USEDEFAULT: if (!skip) transparent = GCLDEFAULTTRANSPARENT; break; default: if (!skip) transparent = 1; unputlex(l); break; } break; case EXPIRES: switch (signedinteger) { case INTEGER: if (!skip) expires = numericvalue; /* * See RFC 2068 for the reasons behind this condition. * Of course, it is hard to imagine a counter that should * be cached for more than a year. :-) */ if (!skip && (expires > SECONDSINAYEAR)) expires = SECONDSINAYEAR; break; case NONE: /* * Just what should "none" mean in this context? * Most likely, "never expires." So, we use * the highest value permitted by RFC 2068. */ if (!skip) expires = SECONDSINAYEAR; break; case USEDEFAULT: if (!skip) expires = GCLDEFAULTEXPIRES; break; case EOF: return syntax(unexpected); default: synterr(expiration); } break; case TOKALIGN: switch (lex) { case COUNTER: temp = lexvalue; switch (lex) { case VALIGN: case TOPBOTTOM: if (!skip) valignflag[temp] = lexvalue; switch (signedinteger) { case INTEGER: if (!skip) vashift[temp] = numericvalue; break; default: if (!skip) vashift[temp] = 0; unputlex(l); break; } break; case HALIGN: case HSHIFT: if (!skip) halignflag[temp] = lexvalue; switch(signedinteger) { case INTEGER: if (!skip) hashift[temp] = numericvalue; break; default: if (!skip) hashift[temp] = 0; unputlex(l); break; } break; case USEDEFAULT: if (!skip) { valignflag[temp] = defaultalignflag.v[temp]; halignflag[temp] = defaultalignflag.h[temp]; vashift[temp] = defaultashift.v[temp]; hashift[temp] = defaultashift.h[temp]; } break; case NONE: if (!skip) valignflag[temp] = halignflag[temp] = vashift[temp] = hashift[temp] = 0; break; default: synterr("Expected alignment type"); } break; case VALIGN: case TOPBOTTOM: if (!skip) { valignflag[HEAD] = valignflag[DIGITS] = valignflag[COMMAS] = valignflag[DASHES] = valignflag[COLONS] = valignflag[PLUSSES] = valignflag[MINUSES] = valignflag[TIMES] = valignflag[ZONES] = valignflag[TAIL] = lexvalue; vashift[HEAD] = vashift[DIGITS] = vashift[COMMAS] = vashift[DASHES] = vashift[COLONS] = vashift[PLUSSES] = vashift[MINUSES] = vashift[TIMES] = vashift[ZONES] = vashift[TAIL] = 0; } break; case HALIGN: case HSHIFT: if (!skip) { halignflag[HEAD] = halignflag[DIGITS] = halignflag[COMMAS] = halignflag[DASHES] = halignflag[COLONS] = halignflag[PLUSSES] = halignflag[MINUSES] = halignflag[TIMES] = halignflag[ZONES] = halignflag[TAIL] = lexvalue; hashift[HEAD] = hashift[DIGITS] = hashift[COMMAS] = hashift[DASHES] = hashift[COLONS] = hashift[PLUSSES] = hashift[MINUSES] = hashift[TIMES] = hashift[ZONES] = hashift[TAIL] = 0; } break; case USEDEFAULT: if (!skip) { alignflag = defaultalignflag; ashift = defaultashift; } break; case NONE: if (!skip) { memset((void *)&alignflag, 0, sizeof(gclalign)); memset((void *)&ashift, 0, sizeof(gclalign)); } break; case EOF: return syntax(unexpected); default: synterr("Expected alignment type"); } break; case SHIFT: switch (lex) { case HSHIFT: if (!skip) hshiftflag = lexvalue; switch (signedinteger) { case INTEGER: if (!skip) hshift = numericvalue; break; case NONE: if (!skip) { hshift = 0; hshiftflag = 0; } break; case USEDEFAULT: if (!skip) { hshift = GCLDEFAULTHSHIFT; hshiftflag = GCLDEFAULTHSHIFTFLAG; } break; case EOF: return syntax(unexpected); default: synterr(shiftvalue); } break; case VSHIFT: if (!skip) vshiftflag = lexvalue; switch (signedinteger) { case INTEGER: if (!skip) vshift = numericvalue; break; case NONE: if (!skip) { vshift = 0; vshiftflag = 0; } break; case USEDEFAULT: if (!skip) { vshift = GCLDEFAULTVSHIFT; vshiftflag = GCLDEFAULTVSHIFTFLAG; } break; case EOF: return syntax(unexpected); default: synterr(shiftvalue); } break; case NONE: if (!skip) vshiftflag = hshiftflag = vshift = hshift = 0; break; case USEDEFAULT: if (!skip) { vshiftflag = GCLDEFAULTVSHIFTFLAG; hshiftflag = GCLDEFAULTHSHIFTFLAG; vshift = GCLDEFAULTVSHIFT; hshift = GCLDEFAULTHSHIFT; } break; case EOF: return syntax(unexpected); default: synterr(shifttype); } break; case FRAME: switch (signedinteger) { case NONE: case FRAMETYPE: numericvalue = lexvalue; case INTEGER: if (!skip) { frametype = numericvalue; if (frametype >= FRAMES) { gclwarning("Overriding `frame %u' with `frame %s'", frametype, framenames[frametype % FRAMES]); frametype %= FRAMES; } } break; case USEDEFAULT: if (!skip) frametype = GCLDEFAULTFRAMETYPE; break; case EOF: return syntax(unexpected); default: synterr("Expected frame type"); } break; case VERTICAL: switch (lex) { case USEDEFAULT: if (!skip) vertical = GCLDEFAULTVERTICAL; break; default: if (!skip) vertical = 1; unputlex(l); break; } break; case HORIZONTAL: switch (lex) { case USEDEFAULT: if (!skip) vertical = GCLDEFAULTVERTICAL; break; default: if (!skip) vertical = 0; unputlex(l); break; } break; case KERN: switch (signedinteger) { case INTEGER: if (!skip) kern[HEAD] = kern[TAIL] = kern[DIGITS] = kern[COMMAS] = kern[SPACES] = kern[DOTS] = kern[DASHES] = kern[COLONS] = kern[PLUSSES] = kern[MINUSES] = kern[TIMES] = kern[ZONES] = (op == '-') ? -numericvalue : numericvalue; break; case NONE: if (!skip) memset(kern, 0, GRAPHICTYPES * sizeof(int)); break; case COUNTER: temp = lexvalue; switch (signedinteger) { case NONE: case INTEGER: if (!skip) kern[temp] = numericvalue; break; case USEDEFAULT: if (!skip) kern[temp] = defaultkern[temp]; break; case EOF: return syntax(unexpected); default: synterr("Expected kern value or none"); } break; case USEDEFAULT: if (!skip) memcpy(kern, defaultkern, GRAPHICTYPES * sizeof(int)); break; case EOF: return syntax(unexpected); default: synterr("Expected kern value or none"); } break; case PAD: switch (signedinteger) { case NONE: case INTEGER: if (!skip) tpad = bpad = lpad = rpad = numericvalue; break; case VPAD: temp = lexvalue; switch (signedinteger) { case NONE: numericvalue = lexvalue; case INTEGER: if (!skip) switch (temp) { case TOP: tpad = numericvalue; break; case BOTTOM: bpad = numericvalue; break; } break; case USEDEFAULT: if (!skip) switch (temp) { case TOP: tpad = GCLDEFAULTTPAD; break; case BOTTOM: bpad = GCLDEFAULTBPAD; break; } break; case EOF: return syntax(unexpected); default: synterr(padvalue); } break; case HPAD: temp = lexvalue; switch (signedinteger) { case NONE: numericvalue = lexvalue; case INTEGER: if (!skip) switch (temp) { case LEFT: lpad = numericvalue; break; case RIGHT: rpad = numericvalue; break; } break; case USEDEFAULT: if (!skip) switch (temp) { case LEFT: lpad = GCLDEFAULTLPAD; break; case RIGHT: rpad = GCLDEFAULTRPAD; break; } break; case EOF: return syntax(unexpected); default: synterr(padvalue); } break; case USEDEFAULT: if (!skip) { tpad = GCLDEFAULTTPAD; bpad = GCLDEFAULTBPAD; lpad = GCLDEFAULTLPAD; rpad = GCLDEFAULTRPAD; } break; case EOF: return syntax(unexpected); default: synterr("Expected direction, none, default, or pad value"); } break; case MINDIGITS: switch(signedinteger) { case NONE: numericvalue = lexvalue; case INTEGER: if (!skip) mindigits = numericvalue ? numericvalue > MAXDIGITS ? MAXDIGITS : numericvalue : 1; break; case USEDEFAULT: if (!skip) mindigits = 1; break; case EOF: return syntax(unexpected); default: synterr("Expected minimum number of digits"); } break; case OPTIMIZE: switch (lex) { case NONE: if (!skip) optimize = 0; break; case USEDEFAULT: if (!skip) optimize = GCLDEFAULTOPTIMIZE; break; default: unputlex(l); if (!skip) optimize = 1; break; } break; case SECURE: switch (lex) { case NONE: if (!skip) secure = 0; break; default: unputlex(l); if (!skip) secure = 1; break; } break; case COOKIE: if (!skip && (cookie != NULL)) { free(cookie); cookie = NULL; } switch (lex) { case NONE: break; case '=': case PASSIGN: switch (lex) { case GCLENV: case STRING: if (!skip) { cookie = strdup(lexbuffer); if (cookie == NULL) parserror(); } break; case EOF: return syntax(unexpected); default: synterr("Expected cookie string"); } break; case EOF: return syntax(unexpected); default: synterr("Expected `='"); } break; case INHIBITOR: tempinhibitor = (gclinhibitor)lexvalue; switch (lex) { case NONE: if (!skip) freeinhibitors(); break; case CONDITION: temp = lexvalue; switch (lex) { case FROM: switch (lex) { case GCLENV: case STRING: if (!skip) addinhibitor(from, 0, (gclcondition)temp, tempinhibitor, '='); break; case EOF: return syntax(unexpected); default: synterr("Expected email address"); } break; case COOKIE: switch (lex) { case GCLENV: case STRING: if (!skip) { tempstr = strdup(lexbuffer); if (tempstr == NULL) parserror(); } switch (lex) { case '=': case PASSIGN: op = l; switch (lex) { case GCLENV: case STRING: if (!skip) addinhibitor(tempstr, 1, (gclcondition)temp, tempinhibitor, op); break; case EOF: if (!skip && tempstr) free(tempstr); return syntax(unexpected); default: if (!skip &&tempstr) free(tempstr); synterr("Expected cookie value (text string)"); } break; case EOF: if (!skip && tempstr) free(tempstr); return syntax(unexpected); default: if (!skip && tempstr) free(tempstr); synterr("Expected `='"); } break; case EOF: return syntax(unexpected); default: synterr("Expected cookie name"); } break; case GCLENV: case STRING: if (!skip) { tempstr = strdup(lexbuffer); if (tempstr == NULL) parserror(); } switch (lex) { case '=': case PASSIGN: op = l; switch (lex) { case GCLENV: case STRING: if (!skip) addinhibitor(tempstr, 0, (gclcondition)temp, tempinhibitor, op); break; case EOF: if (!skip && tempstr) free(tempstr); return syntax(unexpected); default: if (!skip && tempstr) free(tempstr); synterr("Expected environment variable value"); } break; case EOF: if (!skip && tempstr) free(tempstr); return syntax(unexpected); default: if (!skip && tempstr) free(tempstr); synterr("Expected `='"); } break; case EOF: return syntax(unexpected); default: synterr("Expected environment variable name"); } break; case EOF: return syntax(unexpected); default: synterr("Expected `none', `unless', or `when'"); } break; case RELAYOR: if (lexvalue == SERVE) switch (lex) { case NONE: if (!skip) freerelayors(); break; case CONDITION: return condition(SERVE, NULL, lexvalue); case EOF: return syntax(unexpected); default: synterr("Expected `none', `unless', or `when'"); } else switch (lex) { case NONE: if (!skip) freerelayors(); break; case STRING: if (!skip) { tempstr = strdup(lexbuffer); if (tempstr == NULL) parserror(); } switch (lex) { case CONDITION: return condition(RELAY, tempstr, lexvalue); case EOF: if (!skip && tempstr) free(tempstr); return syntax(unexpected); default: if (!skip && tempstr) free(tempstr); synterr("Expected `unless' or `when'"); } break; case EOF: return syntax(unexpected); default: synterr("Expected URL or `none'"); } break; case TEXTCHAR: temp = lexvalue; switch (lex) { case NONE: if (!skip) definechar[temp] = '\0'; break; case '=': case PASSIGN: l = lexanal(); switch(l) { case GCLREGISTER: lexvalue = gclregs[lexvalue]; l = INTEGER; /* fall through */ case GCLENV: if (issigned && ((scounter_t)lexvalue < 0)) lexvalue = -lexvalue; } if ((l == STRING) && (strlen(lexbuffer) <= 1)) { if (!skip) definechar[temp] = lexbuffer[0] < ' ' ? '\0' : (unsigned char)lexbuffer[0]; } else if (l == INTEGER) { if (!skip) definechar[temp] = (lexvalue & 0xFF) < ' ' ? '\0' : (unsigned char)(lexvalue & 0xFF); } else if (l == GCLENV) { register unsigned int val = lexvalue; register unsigned char c = (unsigned char)lexbuffer[0]; switch (lex) { case '!': if (!skip) definechar[temp] = (val & 0xFF) < ' ' ? '\0' : (unsigned char)(val & 0xFF); break; default: unputlex(l); if (!skip) definechar[temp] = c < ' ' ? '\0' : c; break; } } else if (l < ' ') { if (!skip) definechar[temp] = '\0'; } else if (l <= 255) { if (!skip) definechar[temp] = (unsigned char)l; } else { syntax(lexbuffer); unputlex(l); } break; case USEDEFAULT: if (!skip) definechar[temp] = definedchar[temp]; break; case EOF: return syntax(unexpected); default: synterr("Expected `none' or `='"); } break; case TEDTURNER: switch (signedinteger) { case NONE: if (!skip) tt.red = tt.green = tt.blue = 0; break; case USEDEFAULT: if (!skip) { tt.red = TTRED; tt.green = TTGREEN; tt.blue = TTBLUE; } break; case INTEGER: temprgb.red = rgbwarning(numericvalue); switch (signedinteger) { case INTEGER: temprgb.green = rgbwarning(numericvalue); switch (signedinteger) { case INTEGER: if (!skip) { tt.red = temprgb.red; tt.green = temprgb.green; tt.blue = rgbwarning(numericvalue); } else rgbwarning(numericvalue); break; case EOF: return syntax(unexpected); default: synterr("Expected blue value"); } break; case EOF: return syntax(unexpected); default: synterr("Expected green value"); } break; case EOF: return syntax(unexpected); default: synterr("Expected red value or `none'"); } break; case SILENT: if ((lex) == NONE) { if (!skip) silent = 0; } else { if (!skip) silent = 1; unputlex(l); } break; case REDIRECT: if (!skip) { if (redirect) { free(redirect); redirect = NULL; } redirection = NOREDIRECTION; } switch (lex) { case GCLENV: case STRING: if (!skip) { redirect = strdup(lexbuffer); if (redirect == NULL) parserror(); } switch(lex) { case '!': if (!skip && redirect) redirection = UNINHIBITEDREDIRECTION; break; default: /* Not an error */ if (!skip && redirect) redirection = REDIRECTORINHIBIT; unputlex(l); break; } case NONE: case USEDEFAULT: break; case EOF: return syntax(unexpected); default: synterr("Expected `none' or URL"); } break; case TODAY: switch (lex) { case INTEGER: today.year = lexvalue; switch (lex) { case INTEGER: today.month = lexvalue; switch (lex) { case INTEGER: today.day = lexvalue; switch (lex) { case INTEGER: today.week = lexvalue; break; /* Ignore any errors since they are not programmer's (hopefully!) */ default: unputlex(l); break; } break; default: unputlex(l); break; } break; default: unputlex(l); break; } break; default: unputlex(l); break; } break; case PER: switch (lex) { case NONE: case USEDEFAULT: if (!skip) rst = NEVER; break; case TIMEPERIOD: if (!skip) rst = lexvalue; break; case EOF: return syntax(unexpected); default: synterr("Expected `none', `daily', `weekly', `monthly', or `annually'"); } break; case TIMEZONE: if (!skip) { gtz.tz = (gcltzone)lexvalue; gtz.secs = 0; } switch (lex) { case '+': case '-': op = l; switch (signedinteger) { case INTEGER: if (!skip) gtz.secs = (long)(op == '-' ? -numericvalue : numericvalue); break; case EOF: return syntax(unexpected); default: synterr("Expected time zone offset in seconds"); } break; case USEDEFAULT: if (!skip) { gtz.tz = GCLDEFAULTTIMEZONE; gtz.secs = GCLDEFAULTTIMEZONEOFFSET; } break; default: unputlex(l); break; } break; case SECONDS: switch (lex) { case NONE: if (!skip) seconds = 0; break; case USEDEFAULT: if (!skip) seconds = GCLDEFAULTSHOWSECONDS; break; default: if (!skip) seconds = 1; unputlex(l); break; } break; case FORK: switch (lex) { case NONE: case USEDEFAULT: if (!skip && script) { free(script); script = NULL; } break; case GCLENV: case STRING: if (!skip) { if (script) free(script); script = strdup(lexbuffer); if (script == NULL) parserror(); } break; case EOF: return syntax(unexpected); default: synterr("Expected script path or `none'"); } break; case PORTABLEGCL: switch (lex) { case NONE: case USEDEFAULT: if (!skip) portable = 0; break; default: if (!skip) portable = 1; unputlex(l); break; } break; /* * The group keyword can be followed by an integer. * In that case it tells us how many digits to group. * * It can also be followed by commas, spaces, or dots. * That tells us what kind of group separator to use. * * Or it can be followed by both in unspecified order. */ case GROUP: switch (signedinteger) { case INTEGER: if (!skip) { if ((scounter_t)numericvalue <= 0) { gclwarning("Overriding `group " sinteger "' with `group default'", numericvalue); numericvalue = GCLDEFAULTGROUP; } group = numericvalue; } switch (lex) { case COUNTER: switch (lexvalue) { case COMMAS: case SPACES: case DOTS: if (!skip) groupseparator = lexvalue; break; default: unputlex(l); break; } break; default: unputlex(l); break; } break; case NONE: case USEDEFAULT: if (!skip) { group = GCLDEFAULTGROUP; groupseparator = GCLDEFAULTGROUPSEPARATOR; } break; case COUNTER: switch (lexvalue) { case COMMAS: case SPACES: case DOTS: if (!skip) groupseparator = lexvalue; switch (signedinteger) { case INTEGER: if (!skip) { if ((scounter_t)numericvalue <= 0) { gclwarning("Overriding `group " sinteger "' with `group default'", numericvalue); numericvalue = GCLDEFAULTGROUP; } group = numericvalue; } break; default: unputlex(l); break; } break; default: synterr("Expected group value, group separator, or `default'"); } break; case EOF: return syntax(unexpected); default: synterr("Expected group value, group separator, or `default'"); } break; case GCLREVERSE: switch (lex) { case COUNTER: switch (lexvalue) { case DIGITS: switch (lex) { case NONE: if (!skip) revdig = 0; break; case USEDEFAULT: if (!skip) revdig = GCLDEFAULTREVDIG; break; default: if (!skip) revdig = 1; unputlex(l); break; } break; case COMMAS: switch (lex) { case NONE: if (!skip) revcom = 0; break; case USEDEFAULT: if (!skip) revcom = GCLDEFAULTREVCOM; break; default: if (!skip) revcom = 1; unputlex(l); break; } break; default: synterr("Expected `digits', `commas', `default', `none', or next keyword"); } break; case NONE: if (!skip) revdig = revcom = 0; break; case USEDEFAULT: if (!skip) { revdig = GCLDEFAULTREVDIG; revcom = GCLDEFAULTREVCOM; } break; default: if (!skip) revdig = revcom = 1; unputlex(l); break; } break; case GCLPRINT: if (!skip) { if (gclprint != NULL) { free(gclprint); gclprint = NULL; } shellcount = 0; } switch (lex) { case NONE: case USEDEFAULT: break; case GCLENV: case STRING: if (!skip) { gclprint = strdup(lexbuffer); if (gclprint == NULL) parserror(); } switch (lex) { case '!': if (!skip) shellcount = 1; break; default: unputlex(l); break; } break; case EOF: return syntax(unexpected); default: synterr("Expected a string, environment variable, or command"); } break; case NOCOMPILE: switch (lex) { case NONE: case USEDEFAULT: if (!skip) gclnocompile = 0; break; default: unputlex(l); if (!skip) gclnocompile = 1; break; } break; case SIGNEDCOUNTER: if (!skip) issigned = 1; break; case UNSIGNEDCOUNTER: if (!skip) issigned = 0; break; case FILEINCLUDE: switch (lex) { case GCLENV: case STRING: if (!skip) { fitemp = includefile; includefile = malloc(sizeof(fi)); if (includefile == NULL) parserror(); else { includefile->fh = gclfile; gclfile = fopen(lexbuffer, "r"); if (gclfile == NULL) { gclfile = includefile->fh; free(includefile); includefile = fitemp; gclwarning(NULL); perror(lexbuffer); } else { includefile->prev = fitemp; includefile->lineno = lineno; lineno = 1; includefile->filename = strdup(lexbuffer); /* Imply squiggles */ unputlex('{'); return statement(); } } } break; case EOF: return syntax(unexpected); default: synterr("Expected include path"); } break; case GCLREGISTER: temp = lexvalue; if (!skip && (lexvalue == COUNT)) { shellcount = 0; if (gclprint != NULL) { free(gclprint); gclprint = NULL; } } switch (lex) { case '=': case PASSIGN: switch (signedinteger) { case INTEGER: if (!skip) gclregs[temp] = numericvalue; break; case EOF: return syntax(unexpected); default: synterr(numexp); } break; case ADD: switch (signedinteger) { case INTEGER: if (!skip) gclregs[temp] += numericvalue; break; case EOF: return syntax(unexpected); default: synterr(numexp); } break; case INC: if (!skip) gclregs[temp]++; break; case SUB: switch (signedinteger) { case INTEGER: if (!skip) gclregs[temp] -= numericvalue; break; case EOF: return syntax(unexpected); default: synterr(numexp); } break; case DEC: if (!skip) gclregs[temp]--; break; case BAND: switch (signedinteger) { case INTEGER: if (!skip) gclregs[temp] &= numericvalue; break; case EOF: return syntax(unexpected); default: synterr(numexp); } break; case BOR: switch (signedinteger) { case INTEGER: if (!skip) gclregs[temp] |= numericvalue; break; case EOF: return syntax(unexpected); default: synterr(numexp); } break; case MUL: switch (signedinteger) { case INTEGER: if (!skip) gclregs[temp] *= numericvalue; break; case EOF: return syntax(unexpected); default: synterr(numexp); } break; case DIV: switch (signedinteger) { case INTEGER: if (!skip) { if (numericvalue != 0) { if (!issigned) gclregs[temp] /= numericvalue; else gclregs[temp] = (scounter_t)gclregs[temp] / (scounter_t)numericvalue; } } break; case EOF: return syntax(unexpected); default: synterr(numexp); } break; case MOD: switch (signedinteger) { case INTEGER: if (!skip && (numericvalue != 0)) { if (!issigned) gclregs[temp] %= numericvalue; else gclregs[temp] = (scounter_t)gclregs[temp] % (scounter_t)numericvalue; } break; case EOF: return syntax(unexpected); default: synterr(numexp); } break; case XOR: switch (signedinteger) { case INTEGER: if (!skip) gclregs[temp] ^= numericvalue; break; case EOF: return syntax(unexpected); default: synterr(numexp); } break; case EOF: return syntax(unexpected); default: synterr(regops); } break; case GCLINCREMENT: switch (signedinteger) { case INTEGER: if (!skip) setincrementrange(numericvalue, numericvalue); break; case '[': switch (signedinteger) { case INTEGER: temp = numericvalue; switch(signedinteger) { case INTEGER: if (!skip) setincrementrange(temp, numericvalue); switch (lex) { case ']': break; case EOF: return syntax(unexpected); default: /* Complain but accept it */ synterr("Expected `]'"); } break; case EOF: return syntax(unexpected); default: synterr("Expected increment upper range"); } break; case EOF: return syntax(unexpected); default: synterr("Expected increment lower range"); } break; case EOF: return syntax(unexpected); default: synterr("Expected increment value or range"); } break; case GCLFUDGE: switch (signedinteger) { case INTEGER: if (issigned && ((scounter_t)numericvalue < 0)) numericvalue = -numericvalue; temp = (numericvalue & 0x03FF) * 2 - 1; if (!skip) setincrementrange(1, temp ? temp : 1); break; case NONE: case USEDEFAULT: if (!skip) setincrementrange(1, 1); break; case EOF: return syntax(unexpected); default: synterr("Expected fudge factor"); } break; case GCLSIGMA: switch (signedinteger) { case INTEGER: temp = numericvalue; switch (signedinteger) { case INTEGER: if (!skip) { setincrementrange(temp, -temp); issigma = 1; mean = numericvalue; } break; case EOF: return syntax(unexpected); default: synterr("Expected mean value"); } break; case EOF: return syntax(unexpected); default: synterr("Expected sigma value"); } break; case GCLPUSH: switch (signedinteger) { case INTEGER: if (!skip) { gclstack[gclstackptr++] = numericvalue; gclstackptr &= 0x0F; } break; case EOF: return syntax(unexpected); default: synterr("Nothing to push"); } break; case AK: switch (signedinteger) { case INTEGER: temp = numericvalue == 0; skip += temp; if (lex != TAK) unputlex(l); premature++; if (statement() == EOF) return EOF; premature--; skip -= temp; switch (lex) { case INAK: skip += !temp; premature++; if (statement() == EOF) return EOF; premature--; skip -= !temp; break; default: unputlex(l); break; } break; case EOF: return syntax(unexpected); default: synterr("Expected logical expression"); } break; case TRI: switch (signedinteger) { case INTEGER: temp = numericvalue; premature++; skip += (scounter_t)temp >= 0; if (statement() == EOF) return EOF; skip -= (scounter_t)temp >= (scounter_t)0; skip += temp != 0; if (statement() == EOF) return EOF; skip -= temp != 0; skip += (scounter_t)temp <= 0; if (statement() == EOF) return EOF; skip -= (scounter_t)temp <= 0; premature--; break; case EOF: return syntax(unexpected); default: synterr("Expected ternary expression"); } break; case '[': op = 0; switch (signedinteger) { case INTEGER: temp = numericvalue; m = 0; gcldebug("Array condition = " integer, numericvalue); for (;;) { register unsigned int eq; switch (signedinteger) { case ']': if (!op) gclwarning("No statements in array"); gcldebug("Processed %u statement%s in array", op, "s" + (op == 1)); return 0; default: syntax(numexp); return 0; case INTEGER: op++; eq = temp != numericvalue; if (!eq) m = 1; gcldebug("Statement %u %s condition", op, eq ? "does not match" : "matches"); skip += eq; premature++; if (statement() == EOF) return EOF; premature--; skip -= eq; break; case USEDEFAULT: op++; gcldebug("Statement %u %s condition", op, m ? "does not match" : "matches"); skip += m; premature++; if (statement() == EOF) return EOF; premature--; skip -= m; break; case EOF: return syntax(unexpected); } } break; case ']': /* Empty array */ gclwarning("Empty array statement"); break; case EOF: return syntax(unexpected); default: synterr(numexp); } break; case '{': complevel++; parse(); break; case '}': return '}'; default: /* Do not use the "synterr" macro here to prevent endless loop. */ syntax(lexbuffer); break; case EOF: if (premature) { premature = 0; return syntax(unexpected); } return EOF; break; } return 0; } static int printstring(char const *str, const int ispath) { int i; register int bytes; char const *strptr; fputc('\"', gclfile); bytes = 2; if (ispath) { if (publish && *str) { bytes += fprintf(gclfile, "/usr/local/share/gracula/pix"); strptr = str; for (i = 0; str[i]; i++) if (str[i] == '/') strptr = (char const *)(str + i); if (*strptr != '/') { bytes++; fputc('/', gclfile); } } else { realpath(str, pathbuffer); strptr = (char const *)pathbuffer; } } else strptr = str; for (i = 0; strptr[i]; i++) switch (strptr[i]) { case '\"': case '\\': fputc('\\', gclfile); bytes++; default: fputc(strptr[i], gclfile); bytes++; } fputc('\"', gclfile); return bytes; } /* Compile a new GCL file */ static void compile(void) { int i; register int bytes; register int neednonewline; register int commentstring; register inhibit *tempinhibitor; register relay *temprelayor; if (gclpath && *gclpath && !publish) { bytes = fprintf(gclfile, "#!%s\n", gclpath); if (gclpath) { free(gclpath); gclpath = NULL; } } else bytes = 0; if (publish) { register char *env; bytes += fprintf(gclfile, "#!/usr/local/bin/gracula\n" "###############################################################################\n" "############### CHANGE THE ABOVE PATH AS NEEDED FOR YOUR SYSTEM ###############\n" "###############################################################################\n" "##\n"); env = getenv("GCLSCRIPTNAME"); if (!env || !*env) env = scriptname; if (env && *env) bytes += fprintf(gclfile, "##\t%s\n##\n", env); env = getenv("GCLAUTHOR"); if (env && *env) bytes += fprintf(gclfile, "##\tCopyright (c) %i %s\n" "##\tAll rights reserved.\n##\n", now->tm_year + 1900, env); bytes += fprintf(gclfile, "## This is a sample GCL (Graphic Counter Language) script. You can use it for\n" "## the creation of graphic counters or timers, such as those used on web pages.\n" "##\n" "## To run this script, you need a GCL compiler/interpreter, which you can\n" "## obtain from:\n" "##\n" "##\thttp://www.whizkidtech.net/gcl/\n" "##\n" "## Please make sure you have at least version " GCLRELEASEVERSION " of gracula.\n" "##\n" "## NOTES:\n" "##\n" "##\t1. You may need to edit any paths in this script to match the paths\n" "##\t on your system. Hint: Use sed. Or use these paths. They are the\n" "##\t recommended defaults.\n" "##\n" "##\t2. Any line in this script starting with two octothorps (`##')\n" "##\t is a comment. Any line starting with one is a compiler directive.\n" "##\t Blank lines are ignored.\n" "##\n" "##\t3. It is strongly suggested you use a COPY of this file as your script.\n" "##\t This is because gracula will overwrite it, and you will lose all\n" "##\t these nice comments.\n" "##\n" "##\t4. Since this script needs to be executed by the shell, and read and\n" "##\t written by gracula, it is important to have the necessary file access\n" "##\t permissions set. Using `chmod 777 %s' will do.\n" "##\n" "###############################################################################\n\n", scriptname && *scriptname ? scriptname : "scriptname"); } if (cookie && !publish) { if (*cookie) { bytes += fprintf(gclfile, "cookie = "); bytes += printstring(cookie, 0); bytes += fprintf(gclfile, "\n"); } } if (insertcomments) bytes += fprintf(gclfile, "###############################################################################\n" "##\n" "## Determine where to find the graphics.\n" "##\n" "###############################################################################\n\n"); neednonewline = 1; commentstring = 1; for (i = 0; i < GRAPHICS; i++) { if (insertcomments && (picture[i].isarray || (picture[i].graphic && picture[i].gtype && *(picture[i].graphic)))) { bytes += fprintf(gclfile, "\n## Define `#%c' (%s)" + neednonewline, characters[i], picname[i]); bytes += fprintf(gclfile, publish && !picture[i].isarray ? " ===> CHANGE THE PATH BELOW AS NEEDED <===\n" : ":\n"); neednonewline = 0; commentstring = 0; } if (picture[i].isarray) { bytes += fprintf(gclfile, "#%c [%i, %i, %i, %i]\n", characters[i], picture[i].x, picture[i].y, picture[i].dx, picture[i].dy); } else if (picture[i].graphic && picture[i].gtype && *(picture[i].graphic)) { bytes += fprintf(gclfile, "#%c ", characters[i]); bytes += printstring(picture[i].graphic, 1); bytes += fprintf(gclfile, " %s\n", graphictypes[picture[i].gtype]); } else if (insertcomments) { if (((i < ARRAYPICTURE) || !publish) && !(picture[PICTUREDIRECTORY].graphic && picture[PICTUREDIRECTORY].gtype && *(picture[PICTUREDIRECTORY].graphic))) bytes += fprintf(gclfile, "\n## Graphic `#%c' (%s) not used in this script.\n" + commentstring, characters[i], picname[i]); if (publish) nodefaultpicture[i] = 0; neednonewline = 0; commentstring = 1; } if (nodefaultpicture[i]) bytes += fprintf(gclfile, "#%c nodefault\n", characters[i]); } if (portable && !publish) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Compile portable code.\n" "##\n" "## This option will guarantee you keep all your other options when porting this\n" "## source file to an environment where gcl may have been compiled with.\n" "## different defaults from your current environment.\n" "##\n" "## If you are not planning to move to a different environment, you should turn\n" "## this option off (\"portable none\") to speed up processing.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "portable\n"); } if (!secure && !publish) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Turn security protection OFF.\n" "##\n" "## This option allows you, and ANYONE ELSE, to change the count from the\n" "## command line.\n" "##\n" "## Do not use this option, except in controlled and safe environment.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "secure none\n"); } else if (portable && !publish) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Turn security protection ON.\n" "##\n" "## This is the default. It is only listed here, so you know it is on.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "secure\n"); } if (portable || (bkg.red != BKGRED) || (bkg.green != BKGGREEN) || (bkg.blue != BKGBLUE)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the color of the background (the frame layer).\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "bkg %i %i %i\n", bkg.red, bkg.green, bkg.blue); } if (portable || (invis.red != INVISRED) || (invis.green != INVISGREEN) || (invis.blue != INVISBLUE)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the \"invisible\" color. This color is used for transparent pixels.\n" "## It is always used in the counter layer, optionally in the image layer.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "invis %u %u %u\n", invis.red, invis.green, invis.blue); } if ((portable || (kern[HEAD] != GCLDEFAULTKERNHEAD)) && (kern[HEAD] == kern[TAIL]) && (kern[HEAD] == kern[DIGITS]) && (kern[HEAD] == kern[COMMAS]) && (kern[HEAD] == kern[SPACES]) && (kern[HEAD] == kern[DOTS]) && (kern[HEAD] == kern[DASHES]) && (kern[HEAD] == kern[COLONS]) && (kern[HEAD] == kern[PLUSSES]) && (kern[HEAD] == kern[MINUSES]) && (kern[HEAD] == kern[TIMES]) && (kern[HEAD] == kern[ZONES])) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the kerning value.\n" "##\n" "## This value determines the number of pixels to insert between adjacent\n" "## digits, or between a digit and a comma, head and a digit, a digit and tail.\n" "##\n" "## Kerning value may also be negative. This helps with non-rectangular images.\n" "## But be careful here. If you overdo your negative kerning, you may be\n" "## rejected by Inspector Kern who makes sure you do not end up with negative\n" "## width or height!\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern %i\n", kern[HEAD]); } else { if (portable || (kern[HEAD] != GCLDEFAULTKERNHEAD)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the head kerning value.\n" "##\n" "## This value determines the number of pixels to insert after the head image.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern head %i\n", kern[HEAD]); } if (portable || (kern[TAIL] != GCLDEFAULTKERNTAIL)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the tail kerning value.\n" "##\n" "## This value determines the number of pixels to insert before the tail image.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern tail %i\n", kern[TAIL]); } if (portable || (kern[DIGITS] != GCLDEFAULTKERNDIGITS)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the digits kerning value.\n" "##\n" "## This value determines the number of pixels to insert between digits.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern digits %i\n", kern[DIGITS]); } if (portable || (kern[COMMAS] != GCLDEFAULTKERNCOMMAS)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the commas kerning value.\n" "##\n" "## This value determines the number of pixels to insert around commas.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern commas %i\n", kern[COMMAS]); } if (portable || (kern[SPACES] != GCLDEFAULTKERNSPACES)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the spaces kerning value.\n" "##\n" "## This value determines the number of pixels to insert around spaces. This\n" "## value is typically 0.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern spaces %i\n", kern[SPACES]); } if (portable || (kern[DOTS] != GCLDEFAULTKERNDOTS)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the dots kerning value.\n" "##\n" "## This value determines the number of pixels to insert around dots.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern dots %i\n", kern[DOTS]); } if (portable || (kern[DASHES] != GCLDEFAULTKERNDASHES)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the dashes kerning value.\n" "##\n" "## This value determines the number of pixels to insert around dashes.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern dashes %i\n", kern[DASHES]); } if (portable || (kern[COLONS] != GCLDEFAULTKERNCOLONS)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the colons kerning value.\n" "##\n" "## This value determines the number of pixels to insert around colons.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern colons %i\n", kern[COLONS]); } if (portable || (kern[PLUSSES] != GCLDEFAULTKERNPLUSSES)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the plusses kerning value.\n" "##\n" "## This value determines the number of pixels to insert around plusses.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern plusses %i\n", kern[PLUSSES]); } if (portable || (kern[MINUSES] != GCLDEFAULTKERNMINUSES)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the minuses kerning value.\n" "##\n" "## This value determines the number of pixels to insert around minuses.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern minuses %i\n", kern[MINUSES]); } if (portable || (kern[TIMES] != GCLDEFAULTKERNTIMES)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the times kerning value.\n" "##\n" "## This value determines the number of pixels to insert around times (`T').\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern times %i\n", kern[TIMES]); } if (portable || (kern[ZONES] != GCLDEFAULTKERNZONES)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the zones kerning value.\n" "##\n" "## This value determines the number of pixels to insert around zones (`Z').\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "kern zones %i\n", kern[ZONES]); } } if ((portable || optimize) && !publish) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Turn o%s optimization.\n" "##\n" "## Optimization gives priority to colors of the counter over the colors of the\n" "## background. However, it slows down processing somewhat. It should not be on\n" "## if the total number of colors is less than 256.\n" "##\n" "###############################################################################\n\n", optimize ? "n" : "ff"); bytes += fprintf(gclfile, optimize ? "optimize\n" : "optimize none\n"); } if (portable || (tt.red != TTRED) || (tt.green != TTGREEN) || (tt.blue != TTBLUE)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Turn o%s colorization.\n" "##\n" "## Colorization changes the first occurence of black in the array image to\n" "## the color specified below. If black is not found, it will use the nearest\n" "## match. However, it will not work if black is the transparent color.\n" "##\n" "## This option is useful when you want to reuse the same images in different\n" "## counters, but use different colors in each.\n" "##\n" "###############################################################################\n\n", tt.red || tt.green || tt.blue ? "n" : "ff"); bytes += fprintf(gclfile, "colorize %u %u %u\n", tt.red, tt.green, tt.blue); } if (portable || ((transparent != GCLDEFAULTTRANSPARENT) && !frametype)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Declare transparency of the frame background.\n" "##\n" "## Ignored if frame type declared.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, transparent ? "trans\n" : "trans none\n"); } if (portable || (frametype != GCLDEFAULTFRAMETYPE)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the type of frame to use.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "frame %s\n", framenames[frametype]); } if (insertcomments & (portable || (tpad != GCLDEFAULTTPAD) || (bpad != GCLDEFAULTBPAD) || (lpad != GCLDEFAULTLPAD) || (rpad != GCLDEFAULTRPAD))) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the padding around the sides of the counter layer.\n" "##\n" "###############################################################################\n\n"); if ((portable || (tpad != GCLDEFAULTTPAD)) && (tpad == bpad) && (tpad == lpad) && (tpad == rpad)) { bytes += fprintf(gclfile, "pad %u\n", tpad); } else { if (portable || (tpad != GCLDEFAULTTPAD)) { bytes += fprintf(gclfile, "pad top %u\n", tpad); } if (portable || (bpad != GCLDEFAULTBPAD)) { bytes += fprintf(gclfile, "pad bottom %u\n", bpad); } if (portable || (lpad != GCLDEFAULTLPAD)) { bytes += fprintf(gclfile, "pad left %u\n", lpad); } if (portable || (rpad != GCLDEFAULTRPAD)) { bytes += fprintf(gclfile, "pad right %u\n", rpad); } } if (portable || (group != GCLDEFAULTGROUP)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the size of group of digits separated by a comma, space, or dot.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "group %u\n", group); } /* * The 3 if (1) will make transition to the use of spaces as default * more painless. It will be deleted once the default has changed. */ if (1 || portable || (groupseparator != GCLDEFAULTGROUPSEPARATOR)) { if (insertcomments) { bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the group separator.\n"); if (1 || publish) bytes += fprintf(gclfile, "##\n" "## Uncomment the group separator you want to use. Comment out the rest.\n"); bytes += fprintf(gclfile, "##\n" "###############################################################################\n\n"); } bytes += fprintf(gclfile, "## group %s\n" + 3, groupseparator == COMMAS ? "commas" : groupseparator == DOTS ? "dots" : "spaces"); if (1 || publish) { if (groupseparator != COMMAS) bytes += fprintf(gclfile, "## group %s\n", "commas"); if (groupseparator != DOTS) bytes += fprintf(gclfile, "## group %s\n", "dots"); if (groupseparator != SPACES) bytes += fprintf(gclfile, "## group %s\n", "spaces"); } } if ((portable || (expires != GCLDEFAULTEXPIRES)) && !publish) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the number of seconds from \"now\" the counter should expire from\n" "## browser cache. A negative value will make it always expired. However, some\n" "## browsers may ignore this value.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "expires %i\n", expires); } if (portable || (vertical != GCLDEFAULTVERTICAL)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Declare counter orientation.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, vertical ? "vertical\n" : "horizontal\n"); } /* Commutate hashifts and vashifts if they are all non-zero */ if (hashift[HEAD] && hashift[DIGITS] && hashift[COMMAS] && hashift[TAIL] && hashift[DASHES] && hashift[COLONS] && hashift[PLUSSES] && hashift[MINUSES] && hashift[TIMES] && hashift[ZONES]) { register int delta; delta = !halignflag[DIGITS] ? hashift[DIGITS] : !halignflag[COMMAS] ? hashift[COMMAS] : !halignflag[HEAD] ? hashift[HEAD] : !halignflag[DASHES] ? hashift[DASHES] : !halignflag[COLONS] ? hashift[COLONS] : !halignflag[PLUSSES] ? hashift[PLUSSES] : !halignflag[MINUSES] ? hashift[MINUSES] : !halignflag[TIMES] ? hashift[TIMES] : !halignflag[ZONES] ? hashift[ZONES] : !halignflag[TAIL] ? hashift[TAIL] : hashift[DIGITS]; for (i = 0; i < GRAPHICTYPES; i++) hashift[i] -= delta; } if (vashift[HEAD] && vashift[DIGITS] && vashift[COMMAS] && vashift[TAIL] && vashift[DASHES] && vashift[COLONS] && vashift[PLUSSES] && vashift[MINUSES] && vashift[TIMES] && vashift[ZONES]) { register int delta; delta = !valignflag[DIGITS] ? vashift[DIGITS] : !valignflag[COMMAS] ? vashift[COMMAS] : !valignflag[HEAD] ? vashift[HEAD] : !valignflag[DASHES] ? vashift[DASHES] : !valignflag[COLONS] ? vashift[COLONS] : !valignflag[PLUSSES] ? vashift[PLUSSES] : !valignflag[TIMES] ? vashift[TIMES] : !valignflag[ZONES] ? vashift[ZONES] : !valignflag[TAIL] ? vashift[TAIL] : vashift[DIGITS]; for (i = 0; i < GRAPHICTYPES; i++) vashift[i] -= delta; } if (insertcomments && (portable || (halignflag[HEAD] != GCLDEFAULTHALIGNHEAD) || (halignflag[DIGITS] != GCLDEFAULTHALIGNDIGITS) || (halignflag[COMMAS] != GCLDEFAULTHALIGNCOMMAS) || (halignflag[TAIL] != GCLDEFAULTHALIGNTAIL) || (hashift[HEAD] != GCLDEFAULTHASHIFTHEAD) || (hashift[DIGITS] != GCLDEFAULTHASHIFTDIGITS) || (hashift[COMMAS] != GCLDEFAULTHASHIFTCOMMAS) || (hashift[TAIL] != GCLDEFAULTHASHIFTTAIL) || (hashift[DASHES] != GCLDEFAULTHASHIFTDASHES) || (hashift[COLONS] != GCLDEFAULTHASHIFTCOLONS) || (hashift[PLUSSES] != GCLDEFAULTHASHIFTPLUSSES) || (hashift[MINUSES] != GCLDEFAULTHASHIFTMINUSES) || (hashift[TIMES] != GCLDEFAULTHASHIFTTIMES) || (hashift[ZONES] != GCLDEFAULTHASHIFTZONES))) { bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define horizontal alignment.\n" "##\n"); if (!vertical) bytes += fprintf(gclfile, "## Ignored by GCL interpreter because this counter is not vertical. You should\n" "## comment it out unless you plan to change the counter into a vertical one at\n" "## some later time.\n" "##\n"); bytes += fprintf(gclfile, "###############################################################################\n\n"); } if ((hashift[HEAD] == hashift[DIGITS]) && (hashift[HEAD] == hashift[COMMAS]) && (hashift[HEAD] == hashift[TAIL]) && (hashift[HEAD] == hashift[DASHES]) && (hashift[HEAD] == hashift[COLONS]) && (hashift[HEAD] == hashift[PLUSSES]) && (hashift[HEAD] == hashift[MINUSES]) && (hashift[HEAD] == hashift[TIMES]) && (hashift[HEAD] == hashift[ZONES]) && (halignflag[HEAD] == halignflag[DIGITS]) && (halignflag[HEAD] == halignflag[COMMAS]) && (halignflag[HEAD] == halignflag[TAIL]) && (halignflag[HEAD] == halignflag[DASHES]) && (halignflag[HEAD] == halignflag[COLONS]) && (halignflag[HEAD] == halignflag[PLUSSES]) && (halignflag[HEAD] == halignflag[MINUSES]) && (halignflag[HEAD] == halignflag[TIMES]) && (halignflag[HEAD] == halignflag[ZONES])) { if (portable || (halignflag[HEAD] != GCLDEFAULTHALIGNHEAD)) { if (publish && !vertical) bytes += fprintf(gclfile, "## "); switch (halignflag[HEAD]) { case LEFT: bytes += fprintf(gclfile, "align left\n"); break; case CENTER: bytes += fprintf(gclfile, "align center\n"); break; case RIGHT: bytes += fprintf(gclfile, "align right\n"); break; } } } else for (i = 0; i < GRAPHICTYPES; i++) if (portable || (halignflag[i] != defaultalignflag.h[i]) || (hashift[i] != defaultashift.h[i])) { if (publish && !vertical) bytes += fprintf(gclfile, "## "); switch(halignflag[i]) { case LEFT: bytes += fprintf(gclfile, "align %s left", graphictypesnames[i]); bytes += fprintf(gclfile, " %+i\n" + (hashift[i] == 0) * 4, hashift[i]); break; case CENTER: bytes += fprintf(gclfile, "align %s center", graphictypesnames[i]); bytes += fprintf(gclfile, " %+i\n" + (hashift[i] == 0) * 4, hashift[i]); break; case RIGHT: bytes += fprintf(gclfile, "align %s right", graphictypesnames[i]); bytes += fprintf(gclfile, " %+i\n" + (hashift[i] == 0) * 4, hashift[i]); break; } } if (insertcomments && (portable || (valignflag[HEAD] != GCLDEFAULTVALIGNHEAD) || (valignflag[DIGITS] != GCLDEFAULTVALIGNDIGITS) || (valignflag[COMMAS] != GCLDEFAULTVALIGNCOMMAS) || (valignflag[TAIL] != GCLDEFAULTVALIGNTAIL) || (vashift[HEAD] != GCLDEFAULTVASHIFTHEAD) || (vashift[DIGITS] != GCLDEFAULTVASHIFTDIGITS) || (vashift[COMMAS] != GCLDEFAULTVASHIFTCOMMAS) || (vashift[TAIL] != GCLDEFAULTVASHIFTTAIL) || (vashift[DASHES] != GCLDEFAULTVASHIFTDASHES) || (vashift[COLONS] != GCLDEFAULTVASHIFTCOLONS) || (vashift[PLUSSES] != GCLDEFAULTVASHIFTPLUSSES) || (vashift[MINUSES] != GCLDEFAULTVASHIFTMINUSES) || (vashift[TIMES] != GCLDEFAULTVASHIFTTIMES) || (vashift[ZONES] != GCLDEFAULTVASHIFTZONES))) { bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define vertical alignment.\n" "##\n"); if (vertical) bytes += fprintf(gclfile, "## Ignored by GCL interpreter because this counter is vertical. You should\n" "## comment it out unless you plan to change the counter to a horizontal one at\n" "## some later time.\n" "##\n"); bytes += fprintf(gclfile, "###############################################################################\n\n"); } if ((vashift[HEAD] == vashift[DIGITS]) && (vashift[HEAD] == vashift[COMMAS]) && (vashift[HEAD] == vashift[TAIL]) && (vashift[HEAD] == vashift[DASHES]) && (vashift[HEAD] == vashift[COLONS]) && (vashift[HEAD] == vashift[PLUSSES]) && (vashift[HEAD] == vashift[MINUSES]) && (vashift[HEAD] == vashift[TIMES]) && (vashift[HEAD] == vashift[ZONES]) && (valignflag[HEAD] == valignflag[DIGITS]) && (valignflag[HEAD] == valignflag[COMMAS]) && (valignflag[HEAD] == valignflag[TAIL]) && (valignflag[HEAD] == valignflag[DASHES]) && (valignflag[HEAD] == valignflag[COLONS]) && (valignflag[HEAD] == valignflag[PLUSSES]) && (valignflag[HEAD] == valignflag[MINUSES]) && (valignflag[HEAD] == valignflag[TIMES]) && (valignflag[HEAD] == valignflag[ZONES])) { if (portable || (valignflag[HEAD] != GCLDEFAULTVALIGNHEAD)) { if (publish && vertical) bytes += fprintf(gclfile, "## "); switch (valignflag[HEAD]) { case TOP: bytes += fprintf(gclfile, "align top\n"); break; case MIDDLE: bytes += fprintf(gclfile, "align middle\n"); break; case BOTTOM: bytes += fprintf(gclfile, "align bottom\n"); break; } } } else for (i = 0; i < GRAPHICTYPES; i++) if (portable || (valignflag[i] != defaultalignflag.v[i]) || (vashift[i] != defaultashift.v[i])) { if (publish && vertical) bytes += fprintf(gclfile, "## "); switch (valignflag[i]) { case TOP: bytes += fprintf(gclfile, "align %s top", graphictypesnames[i]); bytes += fprintf(gclfile, " %+i\n" + (vashift[i] == 0) * 4, vashift[i]); break; case MIDDLE: bytes += fprintf(gclfile, "align %s middle", graphictypesnames[i]); bytes += fprintf(gclfile, " %+i\n" + (vashift[i] == 0) * 4, vashift[i]); break; case BOTTOM: bytes += fprintf(gclfile, "align %s bottom", graphictypesnames[i]); bytes += fprintf(gclfile, " %+i\n" + (vashift[i] == 0) * 4, vashift[i]); break; } } if ((portable && !publish) || vshift) { if (insertcomments) { bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the number of pixels to shift the counter vertically relative to the.\n" "## background graphic.\n" "##\n"); if (bkgpicture.graphic == NULL) bytes += fprintf(gclfile, "## Since no background graphic is defined, you should comment this shift out.\n" "##\n"); bytes += fprintf(gclfile, "###############################################################################\n\n"); } switch (vshiftflag) { case UP: bytes += fprintf(gclfile, "shift up %u\n", vshift); break; case DOWN: bytes += fprintf(gclfile, "shift down %u\n", vshift); break; } } if ((portable && !publish) || hshift) { if (insertcomments) { bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the number of pixels to shift the counter horizontally relative to.\n" "## background graphic.\n" "##\n"); if (bkgpicture.graphic == NULL) bytes += fprintf(gclfile, "## Since no background graphic is defined, you should comment this shift out.\n" "##\n"); bytes += fprintf(gclfile, "###############################################################################\n\n"); } switch (hshiftflag) { case LEFT: bytes += fprintf(gclfile, "shift left %u\n", hshift); break; case RIGHT: bytes += fprintf(gclfile, "shift right %u\n", hshift); break; } } if (portable || revcom || revdig) { if (insertcomments) { bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## %sake the counter or timer print in reverse (%s).\n" "##\n" "###############################################################################\n\n", (revcom || revdig) ? "M" : "Do not m", vertical ? "bottom to top" : "right to left"); } bytes += fprintf(gclfile, revcom && revdig ? "reverse\n" : revcom ? portable ? "reverse digits none reverse commas\n" : "reverse commas\n" : revdig ? portable ? "reverse digits reverse commas none\n" : "reverse digits\n" : "reverse none\n"); } if (portable || (mindigits > 1)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define minimum number of digits to draw.\n" "##\n" "## If the counter value is too small for the minimum number of digits, it will\n" "## be left-padded with zeros.\n" "##\n" "## The acceptable range is 1 - %u.\n" "##\n" "###############################################################################\n\n", MAXDIGITS); bytes += fprintf(gclfile, "mindigits %i\n", mindigits); } /* * Process inhibitors. This is only necessary if -i was not specified * on the command line, since that option overrides any inhibitors * defined in the source file. */ if (one && firstinhibitor) { register char *e; register int match; /* * Note: Because of the way strcmp works, * match == 0 means TRUE (we have a match), * match != 0 means FALSE (we don't have a match). */ tempinhibitor = firstinhibitor; while (tempinhibitor != NULL) { if (tempinhibitor->cookie == 0) { e = getenv(tempinhibitor->env); if (e) match = tempinhibitor->op == '=' ? strcmp(tempinhibitor->val, e) : strncmp(tempinhibitor->val, e, strlen(tempinhibitor->val)); else match = 1; } /* not a cookie */ else { match = 1; /* i.e., FALSE */ for (i = 0; i < numcookies; i++) { if (!strcmp(tempinhibitor->env, cookiepairs[i].name) && (tempinhibitor->op == '=' ? !strcmp(tempinhibitor->val, cookiepairs[i].value) : !strncmp(tempinhibitor->val, cookiepairs[i].value, strlen(tempinhibitor->val)))) { match = 0; break; } } /* for */ } /* cookie */ if ( (when(tempinhibitor->condition) && (match == 0)) || (unless(tempinhibitor->condition) && (match != 0)) ) inhibited = (int)tempinhibitor->inhibitor; /* otherwise, keep "inhibited" unchanged */ tempinhibitor = tempinhibitor->next; } /* while */ } if (!publish) { /* Write inhibitors to the source file in proper sequence */ tempinhibitor = firstinhibitor; if (insertcomments && (portable || firstinhibitor)) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Declare inhibitors.\n" "##\n" "###############################################################################\n\n"); if (portable && !firstinhibitor) bytes += fprintf(gclfile, "inhibit none\n"); else while (tempinhibitor != NULL) { if (tempinhibitor->cookie != 0) { bytes += fprintf(gclfile, "%s %s cookie ", tempinhibitor->inhibitor == CONCEDE ? "concede" : "inhibit", when(tempinhibitor->condition) ? "when" : "unless"); bytes += printstring(tempinhibitor->env, 0); bytes += fprintf(gclfile, tempinhibitor->op == '=' ? " = " : " := "); bytes += printstring(tempinhibitor->val, 0); bytes += fprintf(gclfile, "\n"); } else if (tempinhibitor->env == from) { bytes += fprintf(gclfile, "%s %s from ", tempinhibitor->inhibitor == CONCEDE ? "concede" : "inhibit", when(tempinhibitor->condition) ? "when" : "unless"); bytes += printstring(tempinhibitor->val, 0); bytes += fprintf(gclfile, "\n"); } else { bytes += fprintf(gclfile, "%s %s ", tempinhibitor->inhibitor == CONCEDE ? "concede" : "inhibit", when(tempinhibitor->condition) ? "when" : "unless"); bytes += printstring(tempinhibitor->env, 0); bytes += fprintf(gclfile, tempinhibitor->op == '=' ? " = " : " := "); bytes += printstring(tempinhibitor->val, 0); bytes += fprintf(gclfile, "\n"); } tempinhibitor = tempinhibitor->next; } /* while */ /* Write relays to the source file in proper sequence */ temprelayor = firstrelayor; if (insertcomments && (portable || firstrelayor)) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Declare relays.\n" "##\n" "###############################################################################\n\n"); if (portable && !firstrelayor) bytes += fprintf(gclfile, "relay none\n"); else while (temprelayor != NULL) { if (temprelayor->relayor == SERVE) bytes += fprintf(gclfile, "serve"); else { bytes += fprintf(gclfile, "relay "); bytes += printstring(temprelayor->url, 0); } if (temprelayor->cookie != 0) { bytes += fprintf(gclfile, "%s %s cookie " + 2, when(temprelayor->condition) ? "when" : "unless"); bytes += printstring(temprelayor->env, 0); bytes += fprintf(gclfile, temprelayor->op == '=' ? " = " : " := "); } else if (temprelayor->env == from) { bytes += fprintf(gclfile, "%s %s from " + 2, when(temprelayor->condition) ? "when" : "unless"); } else { bytes += fprintf(gclfile, "%s %s " + 2, when(temprelayor->condition) ? "when" : "unless"); bytes += printstring(temprelayor->env, 0); bytes += fprintf(gclfile, temprelayor->op == '=' ? " = " : " := "); } bytes += printstring(temprelayor->val, 0); bytes += fprintf(gclfile, "\n"); temprelayor = temprelayor->next; } /* while */ } /* !publish */ if ((portable && !publish) || script) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Fork a background script or program.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "fork "); bytes += script ? printstring(script, 0) : fprintf(gclfile, "none"); bytes += fprintf(gclfile, "\n"); } if (silent) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Do not produce any output.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "silent") + fprintf(gclfile, (portable || redirect) && !insertcomments ? " " : "\n"); } if (portable || redirect) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Redirect browser to a different URL. Must contain the full URL, starting\n" "## with \"http://\" or \"ftp://\" and such. Typically combined with \"silent.\"\n" "##\n" "## May be followed by an exclamation point to turn off inhibition when using\n" "## the `-r' command line override.\n" "##\n" "## Without the exclamation point, GCL interprets the \"redirect\" keyword as\n" "## \"redirect as well as inhibit\".\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "redirect "); bytes += redirect ? printstring(redirect, 0) : fprintf(gclfile, "none"); if (redirect && uninhibitedredirection(redirection)) bytes += fprintf(gclfile, "!"); bytes += fprintf(gclfile, "\n"); } for (i = DEFINECOMMA; i < DEFINECHAR; i++) { if (definechar[i] == '\0') { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Do not use %s in text %sers.\n" "##\n" "###############################################################################\n\n", definecharnameplural[i], i == DEFINECOMMA ? "count" : "tim"); bytes += fprintf(gclfile, "%s none\n", definecharname[i]); } else if ((portable && !publish) || (definechar[i] != definedchar[i])) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Declare the %s character for text %sers.\n" "##\n" "###############################################################################\n\n", definecharname[i], i == DEFINECOMMA ? "count" : "tim"); bytes += fprintf(gclfile, "%s = %u\t## `%c'\n", definecharname[i], (unsigned int)definechar[i], definechar[i]); } } if (portable || seconds) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## %show seconds in timers.\n" "##\n" "###############################################################################\n\n", seconds ? "S" : "Do not s"); bytes += fprintf(gclfile, seconds ? "seconds\n" : "seconds none\n"); } if (((portable || (gtz.secs != GCLDEFAULTTIMEZONEOFFSET) || (gtz.tz != GCLDEFAULTTIMEZONE))) && !publish) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Specify time zone.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, gtz.tz == STZ ? "stz" : "utc"); bytes += fprintf(gclfile, gtz.secs ? " %+i\n" : "\n", gtz.secs); } if ((portable && !publish) || rst) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Determine how often to reset.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "reset "); switch (rst) { case WEEKLY: bytes += fprintf(gclfile, "weekly"); break; case DAILY: bytes += fprintf(gclfile, "daily"); break; case MONTHLY: bytes += fprintf(gclfile, "monthly"); break; case ANNUALY: bytes += fprintf(gclfile, "annually"); break; default: bytes += fprintf(gclfile, "none"); break; } bytes += fprintf(gclfile, publish ? "\n" : " "); } if (!publish) { if (incrementrange[0] != incrementrange[1]) { if ((incrementrange[0] == 1) && (incrementrange[1] < 0x07FF) && (incrementrange[1] & 1)) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the fudge factor. The counter will be increased by a random value\n" "## between 1 and fudge * 2 - 1. The actual number of hits will be very close to\n" "## to count / fudge, so you can still get a good overall idea of the real count.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "fudge %i\n", (int)((incrementrange[1] + 1) / 2)); } else if (issigma) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Define the sigma. The counter will be a random value between mean +/- sigma.\n" "## The first integer is sigma, the second mean. Useful for emulation of certain\n" "## kind of statistical data.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "sigma " sinteger ", " sinteger "\n", incrementrange[1], mean); } else { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Set increment range. Unless inhibited, the counter will be increased by\n" "## at least the lower value and at most the higher.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "increment [" sinteger ", " sinteger "]\n", incrementrange[0], incrementrange[1]); } } else if (increment != 1) { if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Set increment. Unless inhibited, the counter will be increased by its value.\n" "##\n" "###############################################################################\n\n"); bytes += fprintf(gclfile, "increment " sinteger "\n", increment); } bytes += fprintf(gclfile, "@ %u %u %u %u\n", now->tm_year, now->tm_mon, now->tm_mday, weeks); if (insertcomments) bytes += fprintf(gclfile, "\n" "###############################################################################\n" "##\n" "## Declare the current value of the counter.\n" "##\n" "###############################################################################\n\n"); if (issigned || insertcomments) bytes += fprintf(gclfile, "unsigned " + issigned * 2); bytes += fprintf(gclfile, issigned ? "count = " sinteger "\n" : "count = " integer "\n", issigma ? (counter_t)mean : ((counter_t)((counter_t)count + (scounter_t)(one && !inhibited) * increment))); } /* !publish */ /* The following is Unix specific */ if (gclfile != stdout) ftruncate(fileno(gclfile), bytes); } static void noframe(gdImagePtr image, int bgcolor, int invisible, int width, int height) { /* This is frame 0 (noframe). It does nothing. */ } static void popup(gdImagePtr image, int bgcolor, int invisible, int width, int height) { /* * Create a frame in the style of a popup window. * This code is based on my button.c CGI program * used to create buttons and popup windows on * line at http://www.whizkidtech.net/ */ int dark, light, white, black; black = gdImageColorAllocate(image, 0, 0, 0); white = gdImageColorAllocate(image, 255, 255, 255); dark = gdImageColorAllocate(image, bkg.red >> 1, bkg.green >> 1, bkg.blue >> 1); light = gdImageColorAllocate(image, (bkg.red + 255) >> 1, (bkg.green + 255) >> 1, (bkg.blue + 255) >> 1); gdImageLine(image, 0, height - 1, 0, 0, light); gdImageLine(image, 0, 0, width - 1, 0, light); gdImageLine(image, width - 1, 0, width - 1, height - 1, black); gdImageLine(image, width - 1, height - 1, 1, height - 1, black); gdImageLine(image, 1, height - 2, 1, 1, white); gdImageLine(image, 1, 1, width - 2, 1, white); gdImageLine(image, width - 2, 1, width - 2, height - 2, dark); gdImageLine(image, width - 2, height - 2, 1, height - 2, dark); } static void button(gdImagePtr image, int bgcolor, int invisible, int width, int height) { /* * Create a button-style frame. Based on the same * code as popup(), and only subtly different. */ int dark, light, white, black; black = gdImageColorAllocate(image, 0, 0, 0); white = gdImageColorAllocate(image, 255, 255, 255); dark = gdImageColorAllocate(image, bkg.red >> 1, bkg.green >> 1, bkg.blue >> 1); light = gdImageColorAllocate(image, (bkg.red + 255) >> 1, (bkg.green + 255) >> 1, (bkg.blue + 255) >> 1); gdImageLine(image, 0, height - 1, 0, 0, white); gdImageLine(image, 0, 0, width - 1, 0, white); gdImageLine(image, width - 1, 0, width - 1, height - 1, black); gdImageLine(image, width - 1, height - 1, 1, height - 1, black); gdImageLine(image, 1, height - 2, 1, 1, light); gdImageLine(image, 1, 1, width - 2, 1, light); gdImageLine(image, width - 2, 1, width - 2, height - 2, dark); gdImageLine(image, width - 2, height - 2, 1, height - 2, dark); } static void defaultbutton(gdImagePtr image, int bgcolor, int invisible, int width, int height) { /* * Create a button-style frame. Based on the same * code as button(), but adds a black frame. */ int dark, light, white, black; black = gdImageColorAllocate(image, 0, 0, 0); white = gdImageColorAllocate(image, 255, 255, 255); dark = gdImageColorAllocate(image, bkg.red >> 1, bkg.green >> 1, bkg.blue >> 1); light = gdImageColorAllocate(image, (bkg.red + 255) >> 1, (bkg.green + 255) >> 1, (bkg.blue + 255) >> 1); gdImageRectangle(image, 0, 0, width - 1, height - 1, black); gdImageLine(image, 1, height - 2, 1, 1, white); gdImageLine(image, 1, 1, width - 2, 1, white); gdImageLine(image, width - 2, 1, width - 2, height - 2, black); gdImageLine(image, width - 2, height - 2, 2, height - 2, black); gdImageLine(image, 2, height - 3, 2, 2, light); gdImageLine(image, 2, 2, width - 3, 2, light); gdImageLine(image, width - 3, 2, width - 3, height - 3, dark); gdImageLine(image, width - 3, height - 3, 2, height - 3, dark); } static void shadow(gdImagePtr image, int bgcolor, int invisible, int width, int height) { /* * Create a "shadow" frame. Derived from popup(), but adds * some shadow on the dark side. */ int dark, light, white, black; black = gdImageColorAllocate(image, 0, 0, 0); white = gdImageColorAllocate(image, 255, 255, 255); dark = gdImageColorAllocate(image, bkg.red >> 1, bkg.green >> 1, bkg.blue >> 1); light = gdImageColorAllocate(image, (bkg.red + 255) >> 1, (bkg.green + 255) >> 1, (bkg.blue + 255) >> 1); gdImageLine(image, 0, height - 1, 0, 0, light); gdImageLine(image, 0, 0, width - 1, 0, light); gdImageLine(image, width - 2, 1, width - 2, height - 2, black); gdImageLine(image, width - 2, height - 2, 2, height - 2, black); gdImageLine(image, 1, height - 2, 1, 1, white); gdImageLine(image, 1, 1, width - 2, 1, white); gdImageLine(image, width - 3, 2, width - 3, height - 3, dark); gdImageLine(image, width - 3, height - 3, 2, height - 3, dark); gdImageLine(image, width - 1, 1, width - 1, height - 1, black); gdImageLine(image, width - 1, height - 1, 1, height - 1, black); } static void box(gdImagePtr image, int bgcolor, int invisible, int width, int height) { int dark, light, black; black = gdImageColorAllocate(image, 0, 0, 0); dark = gdImageColorAllocate(image, bkg.red >> 1, bkg.green >> 1, bkg.blue >> 1); light = gdImageColorAllocate(image, (bkg.red + 255) >> 1, (bkg.green + 255) >> 1, (bkg.blue + 255) >> 1); gdImageLine(image, 0, height - 1, 0, 0, light); gdImageLine(image, 0, 0, width - 1, 0, light); gdImageLine(image, width - 2, 1, width - 2, height - 2, black); gdImageLine(image, width - 2, height - 2, 2, height - 2, black); gdImageLine(image, 1, height - 2, 1, 1, light); gdImageLine(image, 1, 1, width - 2, 1, light); gdImageLine(image, 2, height -3, 2, 2, black); gdImageLine(image, 2, 2, width - 3, 2, black); gdImageLine(image, width - 3, 2, width - 3, height - 3, dark); gdImageLine(image, width - 3, height - 3, 2, height - 3, dark); gdImageLine(image, width - 1, 1, width - 1, height - 1, black); gdImageLine(image, width - 1, height - 1, 1, height - 1, black); } /* * Want to contribute with your own unique frame? * Send me your code for inclusion in future versions * of GCL! * * Don't know how to code? Describe your frame in * minute details, so I can code it. */ static int relaying(void) { register relay *temprelay; register char *url = NULL; int i, match; for (temprelay = firstrelayor; temprelay != NULL; temprelay = temprelay->next) { if (temprelay->cookie != 0) for (i = 0, match = 0; i < numcookies && !match; i++) { if (!strcmp(temprelay->env, cookiepairs[i].name) && (temprelay->op == '=' ? !strcmp(temprelay->val, cookiepairs[i].value) : !strncmp(temprelay->val, cookiepairs[i].value, strlen(cookiepairs[i].value)))) { match = 1; } } else { register char *strptr = getenv(temprelay->env); match = strptr != NULL && (temprelay->op == '=' ? strcmp(temprelay->val, strptr) == 0 : strncmp(temprelay->val, strptr, strlen(strptr))); } if (match && when(temprelay->condition) || !match && unless(temprelay->condition)) { url = temprelay->relayor == SERVE ? NULL : temprelay->url; } } if (url != NULL) { gcldebug("Relaying to `%s'", url); printf("Location: %s\n\n", url); return 1; } return 0; } /* Interpret GCL code */ static void interpret(void) { gdImagePtr counter; gdImagePtr image; register gclpic *grouppicture; int width, height; int widest, tallest; int bx, by, cx, cy; int rejected; int bgcolor; int invisible; register int digits; register int nondigits; int commas = 0; int spaces = 0; int dots = 0; int dashes = 0; int colons = 0; int plusses = 0; int minuses = 0; int times = 0; int zones = 0; int signedcounter = 0; register int lastprinted; register int delta; int i, j; int x, y; char buffer[MAXDIGITS+1]; if (relaying()) return; if (redirect) { gcldebug("Redirecting to `%s'", redirect); printf("Location: %s\n", redirect); if (silent) printf("\n"); } if (silent) { gcldebug("My mouth is sealed"); return; } /* * It would be rather complicated trying * to create a complex image in one step. * * We divide the process into four steps, * creating three different layers in the * first three steps, combining them in * the forth. */ /* STEP ONE - create the counter layer */ /* * Probably the easiest way to do this is in two * passes. * * In pass 1 we: * * o figure out how many digits * we need to display, * * o open the input graphics, * * o calculate the total width and height * of the final graphic. * * In pass 2 we produce the graphic counter. */ /* P A S S O N E */ if (gallery) { digits = 19; strcpy(buffer, "0123456789~-;%,><^@"); nocommas = 1; } else if (whatami != SHOWCOUNT) { nocommas = 1; switch (whatami) { case SHOWTIME: digits = timesprint(buffer, 0); break; case SHOWDATE: digits = datesprint(buffer); break; case SHOWDATE | SHOWTIME: digits = datesprint(buffer); digits += timesprint(buffer + digits, 1); break; case SHOWZONE: digits = zonesprint(buffer); break; case SHOWTIME | SHOWZONE: digits = timesprint(buffer, 0); digits += zonesprint(buffer + digits); break; case SHOWDATE | SHOWZONE: digits = datesprint(buffer); digits += zonesprint(buffer + digits); break; case SHOWDATE | SHOWTIME | SHOWZONE: digits = datesprint(buffer); digits += timesprint(buffer + digits, 1); digits += zonesprint(buffer + digits); break; } } else if (gclprint != NULL) { register unsigned char *strptr = gclprint; for (digits = 0; (digits <= MAXDIGITS) && *strptr; strptr++) { for (i = 0; i < ACCEPTABLECHARS; i++) { if (*strptr == echars[i]) { buffer[digits++] = pchars[i]; break; } } } nocommas = 1; free(gclprint); gclprint = NULL; } else { digits = sprintf(buffer, issigned ? scounteger : counteger, outputtext ? 1 : mindigits > MAXDIGITS ? MAXDIGITS : mindigits, count); if (*buffer == '-') { signedcounter = 1; *buffer = '<'; } gcldebug(issigned ? "Counter = " sinteger : "Counter = " integer, count); gcldebug("Digits = %i", digits); } if (outputtext != 0) { register unsigned char separator = groupseparator == COMMAS ? comma : groupseparator == DOTS ? '.' : ' '; gcldebug("Creating text output (`-t' specified)"); if (localuse == 0) printf("Content-Type: text/html\n\n"); for (i = 0; i < digits; i++) { if ((nocommas == 0) && (i > signedcounter) && (separator >= ' ') && (((digits - i) % group) == 0)) htmlputc(separator); switch (buffer[i]) { default: htmlputc(buffer[i]); break; case '%': htmlputc(colon); break; case '-': htmlputc(dash); break; case '<': htmlputc('-'); break; case '>': htmlputc('+'); break; case '^': htmlputc(timechar); break; case '@': htmlputc(zone); break; case ',': htmlputc(comma); break; case '~': htmlputc(' '); break; case ';': htmlputc('.'); break; } } return; } /* * If we are to display digits in reverse, we need to swap * the contents of the buffer. * * Note: This works for both an even and an odd number of * digits, since with an odd number of digits, the middle * digit remains the same. */ if (revdig != 0) { register char tmp; register int digs = digits - 1; for (i = (digits / 2) - 1; i >= 0; i--) { tmp = buffer[i]; buffer[i] = buffer[digs - i]; buffer[digs - i] = tmp; } } for (i = signedcounter; i < digits; i++) switch (buffer[i]) { case '%': colons++; break; case '-': dashes++; break; case '<': minuses++; break; case '>': plusses++; break; case '^': times++; break; case '@': zones++; break; case ',': commas++; break; case '~': spaces++; break; case ';': dots++; break; } nondigits = colons + dashes + minuses + plusses + times + zones + commas + spaces + dots; digits -= nondigits; openarraypicture(1); openpicture(HEADPICTURE); openpicture(TAILPICTURE); /* * Note that the following has no influence on the number of nondigits. * That is because these commas are not in the buffer but are added * algorithmically (although Al Gore has nothing to do with it :). */ if (nocommas == 0) commas = (digits - 1 - signedcounter) / group; i = groupseparator == COMMAS ? COMMA : groupseparator == DOTS ? DOT : SPACE; if (commas) openpicture(i); grouppicture = &picture[i]; if (!grouppicture->iscreated) { commas = 0; nocommas = 1; } else { grouppicture->isused = 1; if (!nocommas) gcldebug("Image will contain %i group separator%s", commas, "s" + (commas == 1)); } rejected = 0; for (;;) { register int kerning; lastprinted = HEAD; if (headpicture.iscreated) { kerning = kern[HEAD]; widest = width = headpicture.dx; tallest = height = headpicture.dy; } else width = height = widest = tallest = kerning = 0; if (!nocommas) { if (vertical && (width < grouppicture->dx)) width = grouppicture->dx; else if (!vertical && (height < grouppicture->dy)) height = grouppicture->dy; if (widest < grouppicture->dx) widest = grouppicture->dx; if (tallest < grouppicture->dy) tallest = grouppicture->dy; } for (i = 0; i < digits + nondigits; i++) { if (i && !nocommas && (((revcom == 0) && (buffer[i-1] != '<') && (((digits - i) % group) == 0)) || ((revcom != 0) && (buffer[i] != '<')&& (((i % group) == 0))))) { if (lastprinted != HEAD) kerning += kern[groupseparator]; lastprinted = groupseparator; if (vertical) height += grouppicture->dy; else width += grouppicture->dx; } for (j = 0; j <= COMMA; j++) if (buffer[i] == characters[j]) break; gcldebug("Digit[%i] = %c", i, characters[j]); /* * If we have not used this image, open it. * But remember, it may not have been listed, * or it may not exist. */ if (picture[j].isused == 0) { openpicture(j); picture[j].isused = 1; } /* picture not used */ if (picture[j].iscreated) { switch (buffer[i]) { case '%': if (lastprinted != HEAD) kerning += kern[COLONS]; lastprinted = COLONS; break; case '-': if (lastprinted != HEAD) kerning += kern[DASHES]; lastprinted = DASHES; break; case '<': if (lastprinted != HEAD) kerning += kern[MINUSES]; lastprinted = MINUSES; break; case '>': if (lastprinted != HEAD) kerning += kern[PLUSSES]; lastprinted = PLUSSES; break; case '^': if (lastprinted != HEAD) kerning += kern[TIMES]; lastprinted = TIMES; break; case '@': if (lastprinted != HEAD) kerning += kern[ZONES]; lastprinted = ZONES; break; case ',': if (lastprinted != HEAD) kerning += kern[COMMAS]; lastprinted = COMMAS; break; case '~': if (lastprinted != HEAD) kerning += kern[SPACES]; lastprinted = SPACES; break; case ';': if (lastprinted != HEAD) kerning += kern[DOTS]; lastprinted = DOTS; break; default: if (lastprinted != HEAD) kerning += kern[lastprinted]; lastprinted = DIGITS; break; } if (vertical) { if (width < picture[j].dx) width = picture[j].dx; height += picture[j].dy; } else { width += picture[j].dx; if (height < picture[j].dy) height = picture[j].dy; } if (widest < picture[j].dx) widest = picture[j].dx; if (tallest < picture[j].dy) tallest = picture[j].dy; } /* image dimensions */ } /* for each digit and non-digit */ if (tailpicture.iscreated) { if (lastprinted != HEAD) kerning += kern[TAIL]; if (vertical) { if (width < tailpicture.dx) width = tailpicture.dx; height += tailpicture.dy; } else { width += tailpicture.dx; if (height < tailpicture.dy) height = tailpicture.dy; } if (widest < tailpicture.dx) widest = tailpicture.dx; if (tallest < tailpicture.dy) tallest = tailpicture.dy; } if (lastprinted == HEAD) kerning = 0; if (vertical) height += kerning; else width += kerning; /* Call Inspector Kern */ if ((width < widest) || (height < tallest)) { if (debugging) { gcldebug(NULL); fprintf(stderr, "Inspector Kern says: \"Forget it, Buster!\"\n"); gcldebug("Overriding specified kerning and trying again"); } if (kern[DIGITS] < 0) kern[DIGITS] = 0; else if (kern[COMMAS] < 0) kern[COMMAS] = 0; else if (kern[DOTS] < 0) kern[DOTS] = 0; else if (kern[DASHES] < 0) kern[DASHES] = 0; else if (kern[COLONS] < 0) kern[COLONS] = 0; else if (kern[PLUSSES] < 0) kern[PLUSSES] = 0; else if (kern[MINUSES] < 0) kern[MINUSES] = 0; else if (kern[TIMES] < 0) kern[TIMES] = 0; else if (kern[ZONES] < 0) kern[ZONES] = 0; else if (kern[SPACES] < 0) kern[SPACES] = 0; else kern[HEAD] = kern[TAIL] = 0; rejected = 1; } else { if (debugging) { gcldebug(NULL); fprintf(stderr, rejected ? "Inspector Kern says: \"Much better!\"\n" : "Inspector Kern approved.\n"); } break; } /* Inspector Kern */ } /* for(;;) */ /* * At this point "width" and "height" would be the * actual dimensions of the counter layer if not for * the alignment shift feature of GCL. * * Because this feature may increase the counter layer, * we need to do three more things before proceding to * pass two: * * 1. Calculate the "origin" of each graphic - this may * turn out to be negative. * * 2. Adjust origins so none is negative, and at least one * equals to zero. * * 3. Calculate the new height or width. * * The "origin" refers to the topmost pixels in a horizontal * counter; the leftmost pixels in a vertical one. * * NOTE: We reverse the sign of vertical alignment shift * (i.e. we subtract it, not add) because GCL defines * a negative vertical shift as shifting down, while * GIF (and computer graphics in general) increases * downwards. */ if (vertical) { if (headpicture.iscreated) { headpicture.origin = (halignflag[HEAD] == 0) ? (width - headpicture.dx) / 2 : (halignflag[HEAD] > 0) ? width - headpicture.dx : 0; headpicture.origin += hashift[HEAD]; } for (i = 0; i <10; i++) if (picture[i].iscreated) { picture[i].origin = (halignflag[DIGITS] == 0) ? (width - picture[i].dx) / 2 : (halignflag[DIGITS] > 0) ? width - picture[i].dx : 0; picture[i].origin += hashift[DIGITS]; } if (commapicture.iscreated) { commapicture.origin = (halignflag[COMMAS] == 0) ? (width - commapicture.dx) / 2 : (halignflag[COMMAS] > 0) ? width - commapicture.dx : 0; commapicture.origin += hashift[COMMAS]; } if (dotpicture.iscreated) { dotpicture.origin = (halignflag[DOTS] == 0) ? (width - dotpicture.dx) / 2 : (halignflag[DOTS] > 0) ? width - dotpicture.dx : 0; dotpicture.origin += hashift[DOTS]; } if (spacepicture.iscreated) { spacepicture.origin = (halignflag[SPACES] == 0) ? (width - spacepicture.dx) / 2 : (halignflag[SPACES] > 0) ? width - spacepicture.dx : 0; spacepicture.origin += hashift[SPACES]; } if (tailpicture.iscreated) { tailpicture.origin = (halignflag[TAIL] == 0) ? (width - tailpicture.dx) / 2 : (halignflag[TAIL] > 0) ? width - tailpicture.dx : 0; tailpicture.origin += hashift[TAIL]; } if (dashpicture.iscreated) { dashpicture.origin = (halignflag[DASHES] == 0) ? (width - dashpicture.dx) / 2 : (halignflag[DASHES] > 0) ? width - dashpicture.dx : 0; dashpicture.origin += hashift[DASHES]; } if (colonpicture.iscreated) { colonpicture.origin = (halignflag[COLONS] == 0) ? (width - colonpicture.dx) / 2 : (halignflag[COLONS] > 0) ? width - colonpicture.dx : 0; colonpicture.origin += hashift[COLONS]; } if (pluspicture.iscreated) { pluspicture.origin = (halignflag[PLUSSES] == 0) ? (width - pluspicture.dx) / 2 : (halignflag[PLUSSES] > 0) ? width - pluspicture.dx : 0; pluspicture.origin += hashift[PLUSSES]; } if (minuspicture.iscreated) { minuspicture.origin = (halignflag[MINUSES] == 0) ? (width - minuspicture.dx) / 2 : (halignflag[MINUSES] > 0) ? width - minuspicture.dx : 0; minuspicture.origin += hashift[MINUSES]; } if (timepicture.iscreated) { timepicture.origin = (halignflag[TIMES] == 0) ? (width - timepicture.dx) / 2 : (halignflag[TIMES] > 0) ? width - timepicture.dx : 0; timepicture.origin += hashift[TIMES]; } if (zonepicture.iscreated) { zonepicture.origin = (halignflag[ZONES] == 0) ? (width - zonepicture.dx) / 2 : (halignflag[ZONES] > 0) ? width - zonepicture.dx : 0; zonepicture.origin += hashift[TIMES]; } } else { /* horizontal */ if (headpicture.iscreated) { headpicture.origin = (valignflag[HEAD] == 0) ? (height - headpicture.dy) / 2 : (valignflag[HEAD] > 0) ? height - headpicture.dy : 0; headpicture.origin -= vashift[HEAD]; } for (i = 0; i < 10; i++) if (picture[i].iscreated) { picture[i].origin = (valignflag[DIGITS] == 0) ? (height - picture[i].dy) / 2 : (valignflag[DIGITS] > 0) ? height - picture[i].dy : 0; picture[i].origin -= vashift[DIGITS]; } if (commapicture.iscreated) { commapicture.origin = (valignflag[COMMAS] == 0) ? (height - commapicture.dy) / 2 : (valignflag[COMMAS] > 0) ? height - commapicture.dy : 0; commapicture.origin -= vashift[COMMAS]; } if (dotpicture.iscreated) { dotpicture.origin = (valignflag[DOTS] == 0) ? (height - dotpicture.dy) / 2 : (valignflag[DOTS] > 0) ? height - dotpicture.dy : 0; dotpicture.origin -= vashift[DOTS]; } if (spacepicture.iscreated) { spacepicture.origin = (valignflag[SPACES] == 0) ? (height - spacepicture.dy) / 2 : (valignflag[SPACES] > 0) ? height - spacepicture.dy : 0; spacepicture.origin -= vashift[SPACES]; } if (tailpicture.iscreated) { tailpicture.origin = (valignflag[TAIL] == 0) ? (height - tailpicture.dy) / 2 : (valignflag[TAIL] > 0) ? height - tailpicture.dy : 0; tailpicture.origin -= vashift[TAIL]; } if (dashpicture.iscreated) { dashpicture.origin = (valignflag[DASHES] == 0) ? (height - dashpicture.dy) / 2 : (valignflag[DASHES] > 0) ? height - dashpicture.dy : 0; dashpicture.origin -= vashift[DASHES]; } if (colonpicture.iscreated) { colonpicture.origin = (valignflag[COLONS] == 0) ? (height - colonpicture.dy) / 2 : (valignflag[COLONS] > 0) ? height - colonpicture.dy : 0; colonpicture.origin -= vashift[COLONS]; } if (pluspicture.iscreated) { pluspicture.origin = (valignflag[PLUSSES] == 0) ? (height - pluspicture.dy) / 2 : (valignflag[PLUSSES] > 0) ? height - pluspicture.dy : 0; pluspicture.origin -= vashift[PLUSSES]; } if (minuspicture.iscreated) { minuspicture.origin = (valignflag[MINUSES] == 0) ? (height - minuspicture.dy) / 2 : (valignflag[MINUSES] > 0) ? height - minuspicture.dy : 0; minuspicture.origin -= vashift[MINUSES]; } if (timepicture.iscreated) { timepicture.origin = (valignflag[TIMES] == 0) ? (height - timepicture.dy) / 2 : (valignflag[TIMES] > 0) ? height - timepicture.dy : 0; timepicture.origin -= vashift[TIMES]; } if (zonepicture.iscreated) { zonepicture.origin = (valignflag[ZONES] == 0) ? (height - zonepicture.dy) / 2 : (valignflag[ZONES] > 0) ? height - zonepicture.dy : 0; zonepicture.origin -= vashift[TIMES]; } } delta = 0x7FFFFFFF; for (i = 0; i <= TAILPICTURE; i++) if (picture[i].iscreated && (delta > picture[i].origin)) delta = picture[i].origin; for (i = 0; i <= TAILPICTURE; i++) if (picture[i].iscreated) { picture[i].origin -= delta; if (vertical) { if (width < (picture[i].dx + picture[i].origin)) width = picture[i].dx + picture[i].origin; } else if (height < (picture[i].dy + picture[i].origin)) height = picture[i].dy + picture[i].origin; } /* * If width or height is still zero or less, we make them equal * to one. */ if (width <= 0) width = 1; if (height <= 0) height = 1; /* P A S S T W O */ counter = gdImageCreate(width, height); gcldebug("Creating counter layer (width = %i, height = %i)", width, height); invisible = gdImageColorAllocate(counter, invis.red, invis.green, invis.blue); gdImageColorTransparent(counter, invisible); lastprinted = HEAD; if (vertical) { y = 0; if (headpicture.iscreated) { x = headpicture.origin; gcldebug("Copying head vertically at x = %i, y = %i", x, y); gdImageCopy(counter, headpicture.image ? headpicture.image : arraypicture.image, x, y, headpicture.x, headpicture.y, headpicture.dx, headpicture.dy); y += headpicture.dy + kern[HEAD]; } /* head printed */ for (i = 0; i < digits + nondigits; i++) { /* "print" a group separator where appropriate */ if (i && !nocommas && (((revcom == 0) && (buffer[i-1] != '<') && (((digits - i) % group) == 0)) || ((revcom != 0) && (buffer[i] != '<') && (((i % group) == 0))))) { x = grouppicture->origin; if (lastprinted != HEAD) y += kern[groupseparator]; lastprinted = groupseparator; gcldebug("Copying group separator vertically at x = %i, y = %i", x, y); gdImageCopy(counter, grouppicture->image ? grouppicture->image : arraypicture.image, x, y, grouppicture->x, grouppicture->y, grouppicture->dx, grouppicture->dy); y += grouppicture->dy; } /* group separator */ /* find the digit or non-digit */ for (j = 0; j <= COMMA; j++) if (buffer[i] == characters[j]) break; if (picture[j].iscreated) { /* Apply vertical kerning */ switch (buffer[i]) { case '%': if (lastprinted != HEAD) y += kern[COLONS]; lastprinted = COLONS; break; case '-': if (lastprinted != HEAD) y += kern[DASHES]; lastprinted = DASHES; break; case '<': if (lastprinted != HEAD) y += kern[MINUSES]; lastprinted = MINUSES; break; case '>': if (lastprinted != HEAD) y += kern[PLUSSES]; lastprinted = PLUSSES; break; case '^': if (lastprinted != HEAD) y += kern[TIMES]; lastprinted = TIMES; break; case '@': if (lastprinted != HEAD) y += kern[ZONES]; lastprinted = ZONES; break; case ',': if (lastprinted != HEAD) y += kern[COMMAS]; lastprinted = COMMAS; break; case '~': if (lastprinted != HEAD) y += kern[SPACES]; lastprinted = SPACES; break; case ';': if (lastprinted != HEAD) y += kern[DOTS]; lastprinted = DOTS; default: /* * If a digit was preceded by a non-digit * use the kerning of the non-digit. */ if (lastprinted != HEAD) y += kern[lastprinted]; lastprinted = DIGITS; break; } /* fit the digit or non-digit horizontally */ x = picture[j].origin; gcldebug("Copying %s vertically at x = %i, y = %i", picname[j], x, y); gdImageCopy(counter, picture[j].image ? picture[j].image : arraypicture.image, x, y, picture[j].x, picture[j].y, picture[j].dx, picture[j].dy); y += picture[j].dy; } /* image exists */ } /* digits and non-digits printed */ if (tailpicture.iscreated) { x = tailpicture.origin; if (lastprinted != HEAD) y += kern[TAIL]; gcldebug("Copying tail vertically at x = %i, y = %i", x, y); gdImageCopy(counter, tailpicture.image ? tailpicture.image : arraypicture.image, x, y, tailpicture.x, tailpicture.y, tailpicture.dx, tailpicture.dy); } /* tail printed */ } /* vertical counter */ else { x = 0; if (headpicture.iscreated) { y = headpicture.origin; gcldebug("Copying head horizontally at x = %i, y = %i", x, y); gdImageCopy(counter, headpicture.image ? headpicture.image : arraypicture.image, x, y, headpicture.x, headpicture.y, headpicture.dx, headpicture.dy); x += headpicture.dx + kern[HEAD]; } /* head printed */ for (i = 0; i < digits + nondigits; i++) { /* "print" a group separator where appropriate */ if (i && !nocommas && (((revcom == 0) && (buffer[i-1] != '<') && (((digits - i) % group) == 0)) || ((revcom != 0) && (buffer[i] != '<') && (((i % group) == 0))))) { y = grouppicture->origin; if (lastprinted != HEAD) x += kern[groupseparator]; lastprinted = groupseparator; gcldebug("Copying group separator horizontally at x = %i, y = %i", x, y); gdImageCopy(counter, grouppicture->image ? grouppicture->image : arraypicture.image, x, y, grouppicture->x, grouppicture->y, grouppicture->dx, grouppicture->dy); x += grouppicture->dx; } /* group separator */ /* find out what digit or non-digit to use */ for (j = 0; j <= COMMA; j++) if (buffer[i] == characters[j]) break; if (picture[j].iscreated) { switch (buffer[i]) { case '%': if (lastprinted != HEAD) x += kern[COLONS]; lastprinted = COLONS; break; case '-': if (lastprinted != HEAD) x += kern[DASHES]; lastprinted = DASHES; break; case '<': if (lastprinted != HEAD) x += kern[MINUSES]; lastprinted = MINUSES; break; case '>': if (lastprinted != HEAD) x += kern[PLUSSES]; lastprinted = PLUSSES; break; case '^': if (lastprinted != HEAD) x += kern[TIMES]; lastprinted = TIMES; break; case '@': if (lastprinted != HEAD) x += kern[ZONES]; lastprinted = ZONES; break; case ',': if (lastprinted != HEAD) x += kern[COMMAS]; lastprinted = COMMAS; break; case '~': if (lastprinted != HEAD) x += kern[SPACES]; lastprinted = SPACES; break; case ';': if (lastprinted != HEAD) x += kern[DOTS]; lastprinted = DOTS; break; default: if (lastprinted != HEAD) x += kern[lastprinted]; lastprinted = DIGITS; } /* fit the digit vertically */ y = picture[j].origin; /* now copy it */ gdImageCopy(counter, picture[j].image ? picture[j].image : arraypicture.image, x, y, picture[j].x, picture[j].y, picture[j].dx, picture[j].dy); gcldebug("Copying %s horizontally at x = %i, y = %i", picname[j], x, y); x += picture[j].dx; } /* image exists */ } /* digits printed */ if (tailpicture.iscreated) { y = tailpicture.origin; if (lastprinted != HEAD) x += kern[TAIL]; gcldebug("Copying tail horizontally at x = %i, y = %i", x, y); gdImageCopy(counter, tailpicture.image ? tailpicture.image : arraypicture.image, x, y, tailpicture.x, tailpicture.y, tailpicture.dx, tailpicture.dy); } /* tail printed */ } /* horizontal counter */ /* Destroy individual digits, comma, head, and tail */ for (i = 0; i <= TAILPICTURE; i++) if (picture[i].image && (picture[i].image != defaultimage[i])) { gdImageDestroy(picture[i].image); picture[i].image = NULL; gcldebug("Destroyed %s image", picname[i]); } /* STEP TWO - create the background layer */ /* * This one is simple: If a background graphic * is defined, open it. */ openpicture(BKGPICTURE); /* STEP THREE - create the frame layer */ /* * The frame layer is underneath the other two layers. * The simplest frame layer is nothing but plain * background, perhaps transparent. * * We can also make it quite fancy. But remember: * Because of the structure of a GIF file, we only * have 256 colors to work with in the whole graphic. * * We start step three by calculating the size of the * frame layer. * * First of all, the frame layer must be large enough * to encompass the counter and the "background" graphic. * * We already have the dimensions of the counter layer * in the width and height variables. */ if (bkgpicture.iscreated) { if (width < bkgpicture.dx) width = bkgpicture.dx; if (height < bkgpicture.dy) height = bkgpicture.dy; /* * "width" and "height" now contain the dimmensions * of the window that exactly encompasses the counter * and the background layers centered over each other. * Let this window be called the focus window. * * Now calculate the position of the counter and background * layers inside the focus window, disregarding any shifts. * Do this by simply centering the two layers within the * focus window. * * Remember we are still within the "if" statement here. */ bx = (width - bkgpicture.dx) / 2; by = (height - bkgpicture.dy) / 2; } cx = (width - gdImageSX(counter)) / 2; cy = (height - gdImageSY(counter)) / 2; /* Apply any shifts. Do it only if we have a background layer. */ if (bkgpicture.iscreated) { register int temp; if (vshiftflag) { if (vshiftflag < 0) { /* * Shifting the counter layer up. This is effectively the * same as shifting the background layer down, and easier * to compute that way. */ by += vshift; /* * We may now be out of bounds of the focus window. * Adjust its height. */ if (height < (by + bkgpicture.dy)) height = by + bkgpicture.dy; } else { /* Shifting the counter layer down. */ cy += vshift; /* Adjust height */ if (height < (cy + gdImageSY(counter))) height = cy + gdImageSY(counter); } /* * This may have made the focus window too big. * So, cut off any empty space on top. */ temp = cy; if (temp > by) temp = by; height -= temp; by -= temp; cy -= temp; } if(hshiftflag) { if (hshiftflag < 0) { /* * Shifting the counter layer left. * Same as shifting the background layer right. */ bx += hshift; if (width < (bx + gdImageSX(bkgpicture.image))) width = bx + gdImageSX(bkgpicture.image); } else { /* Shifting the counter layer right */ cx += hshift; if (width < (cx + gdImageSX(counter))) width = cx + gdImageSX(counter); } temp = cx; if (temp > bx) temp = bx; width -= temp; bx -= temp; cx -= temp; } } /* * Now, make room for any padding. * * Finally, add the dimensions of any actual frame. */ width += lpad + rpad + frames[frametype].left + frames[frametype].right; height += tpad + bpad + frames[frametype].top + frames[frametype].bottom; bx += lpad + frames[frametype].left; cx += lpad + frames[frametype].left; by += tpad + frames[frametype].top; cy += tpad + frames[frametype].top; /* * Now we have enough information to create the image * of the frame layer. * * Since this is the same image where all three layers * will merge, we just refer to it as "image." * * But at this point it is just the frame layer, its * name notwithstanding. */ image = gdImageCreate(width, height); invisible = transrequired(frames[frametype]) || (transok(frames[frametype]) && transparent) ? gdImageColorAllocate(image, invis.red, invis.green, invis.blue) : -1; gdImageColorTransparent(image, invisible); bgcolor = gdImageColorAllocate(image, bkg.red, bkg.green, bkg.blue); /* * At last we have everything we need to build the final * image. * * But we have a dilemma. It stems from the fact GIF * images are limited to 256 colors, including the * possibly transparent background. * * That is not a problem: The gd library we are using * can easily reduce the number of colors as more images * are being added. But that is where the dilemma comes from: * * To get a sharp image, we should favor the colors * of the counter over those of the background, * and the colors of the background over those of the * frame. * * Yet, to place the frame underneath the background * we need to draw the frame before the background. * And to have the background under the counter, we * need to copy the counter last. This is the exact * opposite of what we need. * * Our two options are the trade between speed and * quality. * * If we opt for speed, we draw the frame first, the * counter last. We can do this if all of our graphic * source files are optimized, i.e., their total * number of colors does not exceed 256, or if the * color reduction still results in a pleasing image. * * If, on the other hand, we choose quality, we need * to copy the counter to the image first, the frame * last. After that, we re-copy the other images in * reversed order. That means that, save for the frame, * all images are copied twice. * * Thus we can obtain quality at the cost of some speed, * or speed while possibly losing some quality. * * Which way does GCL choose? Well, it does not. I am * a strong believer in user choices, rather than * programmer choices. * * My solution: By default, GCL chooses speed, but if * you enter the "optimize" keyword in the source code, * GCL chooses quality. You can also type "optimize none" * to opt for speed explicitly. */ if (optimize) { /* Copy the counter image to force its color palette */ gdImageCopy(image, counter, cx, cy, 0, 0, gdImageSX(counter), gdImageSY(counter)); /* Copy the background image to add its palette to image */ if (bkgpicture.iscreated) gdImageCopy(image, bkgpicture.image ? bkgpicture.image : arraypicture.image, bx, by, bkgpicture.x, bkgpicture.y, bkgpicture.dx, bkgpicture.dy); } /* Draw any frame */ frames[frametype].draw(image, bgcolor, invisible, width, height); /* Add the tile, if any */ openpicture(TILE); /* * That may not have been enough: If all we got is a reference * to the array image, we must create a separate gdImage. This * is because gd 1.3 will not tile from a partial image. */ if (tilepicture.isarray) { tilepicture.image = gdImageCreate(tilepicture.dx, tilepicture.dy); if (tilepicture.image != NULL) { gdImageCopy(tilepicture.image, arraypicture.image, 0, 0, tilepicture.x, tilepicture.y, tilepicture.dx, tilepicture.dy); /* * The following line is not necessary. * So it is commented out. But I keep it * there, albeit as a comment, as it may * come handy in some future version. In that * case I want to be reminded it is NOT there. */ /* tilepicture.isarray = tilepicture.x = tilepicture.y = 0; */ } } if (tilepicture.image != NULL) { gdImageSetTile(image, tilepicture.image); gdImageFilledRectangle(image, frames[frametype].left, frames[frametype].top, width - frames[frametype].right - 1, height - frames[frametype].bottom - 1, gdTiled); if (tilepicture.image != tileimage) { gdImageDestroy(tilepicture.image); tilepicture.image = NULL; gcldebug("Tile image destroyed"); } } /* Copy the background image over the frame image */ if (bkgpicture.iscreated) { gdImageCopy(image, bkgpicture.image ? bkgpicture.image : arraypicture.image, bx, by, bkgpicture.x, bkgpicture.y, bkgpicture.dx, bkgpicture.dy); /* Destroy it */ if (bkgpicture.image && (bkgpicture.image != bkgimage)) { gdImageDestroy(bkgpicture.image); bkgpicture.image = NULL; gcldebug("Background image destroyed"); } } /* Copy the counter image over everything else */ gdImageCopy(image, counter, cx, cy, 0, 0, gdImageSX(counter), gdImageSY(counter)); /* Destroy it */ gdImageDestroy(counter); counter = NULL; gcldebug("Counter image destroyed"); /* Send it out */ if (!localuse) { if (expires <= 0) printf("Cache-Control: no-cache\n"); if (expires >= 0) { now = gmtime(&timer); printf("Last-Modified: %s, %i %s %i %.2i:%.2i:%.2i GMT\n", day[now->tm_wday], now->tm_mday, month[now->tm_mon], now->tm_year + 1900, now->tm_hour, now->tm_min, now->tm_sec); } timer += expires; now = gmtime(&timer); printf("Expires: %s, %i %s %i %.2i:%.2i:%.2i GMT\n" "Content-Type: image/gif\n\n", day[now->tm_wday], now->tm_mday, month[now->tm_mon], now->tm_year + 1900, now->tm_hour, now->tm_min, now->tm_sec); } gdImageGif(image, stdout); fflush(stdout); /* Clean up */ gdImageDestroy(image); image = NULL; gcldebug("Output image destroyed"); for (i = 0; i < GRAPHICS; i++) { if (picture[i].graphic) { free (picture[i].graphic); picture[i].graphic = NULL; } if (picture[i].image && (picture[i].image != defaultimage[i])) { gdImageDestroy(picture[i].image); picture[i].image = NULL; gcldebug("Destroyed %s image", picname[i]); } } } int main(int argc, char *argv[]) { int i, j; int retval; int parser; scounter_t cmdcount = -1; int noredirect = 0; int nosilent = 0; int cmdwhatami = SHOWCOUNT; time_t whattime; #ifdef __FreeBSD__ #define gmtoff now->tm_gmtoff #else long gmtoff; #endif timer = time(NULL); randomnumber = -timer; if (argc > 1) { for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { if (argv[i][1] == '\0') fprintf(stderr, "GCL Warning: Dangling dash in command line argument %i.\n", i); else for (j = 1; argv[i][j]; j++) switch (argv[i][j]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': mdoverride = argv[i][j] - '0'; break; case 'a': cmdwhatami |= SHOWDATE; break; case 'A': cmdwhatami &= ~SHOWDATE; break; case 'c': compileonly = 1; break; case 'C': compileonly = 0; break; case 'd': debugging = 1; break; case 'D': debugging = 0; break; case 'e': insertcomments = 1; break; case 'E': insertcomments = 0; break; case 'g': gallery = 1; break; case 'G': gallery = 0; break; case 'i': one = 0; break; case 'I': one = 1; break; case 'k': make = 1; break; case 'K': make = 0; break; case 'l': localuse = 1; break; case 'L': localuse = 0; break; case 'm': cmdwhatami |= SHOWTIME; break; case 'M': cmdwhatami &= ~SHOWTIME; break; case 'n': nocompile = 1; break; case 'N': nocompile = 0; break; case 'o': nocommas = 1; break; case 'O': nocommas = 0; break; case 'p': publish = 1; break; case 'P': publish = 0; break; case 'r': noredirect = 1; break; case 'R': noredirect = 0; break; case 's': nosilent = 1; break; case 'S': nosilent = 0; break; case 't': outputtext = 1; break; case 'T': outputtext = 0; break; case 'v': verbose = 1; break; case 'V': verbose = 0; break; default: fprintf(stderr, "GCL Warning: Ignoring unknown option `-%c'.\n", argv[i][j]); help |= 1; break; case 'h': help |= 2; break; case 'H': help &= ~2; break; case 'w': warnings = 1; break; case 'W': warnings = 0; break; case 'z': cmdwhatami |= SHOWZONE; break; case 'Z': cmdwhatami &= ~SHOWZONE; break; } /* switch */ } /* '-' */ else if (isdigit(argv[i][0])) cmdcount = atoi(argv[i]); else if (filename) fprintf(stderr, "GCL Warning: Ignoring unknown option `%s'.\n", argv[i]); else filename = argv[i]; } /* for */ if (verbose) { fprintf(stderr, "GCL (Graphic Counter Language), v." GCLVERSION ":\n\n" "\tCopyright 1999 G. Adam Stanislav.\n" "\tAll rights reserved.\n\n" "\tFor more information visit\n" "\thttp://www.whizkidtech.net/gcl/\n\n" "\tFor current version visit\n" "\tftp://ftp.whizkidtech.net/cgi/gcl/\n\n" "\tFor license information read the file NNL, or visit\n" "\thttp://www.whizkidtech.net/nnl/\n\n"); if (!help) fprintf(stderr, "\tFor command line options enter\n\n" "\t\tgracula -h\n\n"); } if (help) { fprintf(stderr, "GCL Usage: gracula [-acdeghiklmnoprstvwz0..9] [] [filename]\n\n" "\t-0 do not override minimum digits,\n" "\t-1..9 override minimum digits,\n" "\t-a show current date,\n" "\t-c compile only,\n" "\t-d debugging mode on,\n" "\t-e elaborate (insert comments),\n" "\t-g gallery (also good as test mode),\n" "\t-h help,\n" "\t-i inhibit (do not increase count),\n" "\t-k make (create custom version),\n" "\t-l local use,\n" "\t-m show current time,\n" "\t-n no compilation,\n" "\t-o omit commas,\n" "\t-p publish script to stdout,\n" "\t-r do not redirect,\n" "\t-s do not stay silent,\n" "\t-t text output (SSI),\n" "\t-v version,\n" "\t-w warnings on,\n" "\t-z show time zone,\n" "\t Set counter to number,\n" "\tfilename GCL code file.\n\n" "\tUsing capital letters turns an option OFF (default).\n"); if (help & 2) return 0; else if (localuse) return 3; } /* help */ } /* argc > 1 */ if (nocompile) publish = 0; else if (publish) { register char *strptr; insertcomments = compileonly = portable = 1; if (filename && *filename) { for (scriptname = strptr = filename; *strptr; strptr++) if (*strptr == '/') scriptname = strptr + 1; } } if (localuse) { nosilent = noredirect = 1; } if (filename) { gclfile = fopen(filename, "r+"); if (gclfile == NULL) { perror(filename); /* * Remember, this is CGI: We MUST * produce some output even if * we have no input file. * * Normally, we would just create * an error page, but we are not * making HTML code here. We need * to send a GIF out, even if all * we "show" is one transparent * pixel. */ if (!localuse) { gcldebug("Attempting to produce default GIF output"); if (cmdcount >= 0) count = (counter_t)cmdcount; if (mdoverride) { mindigits = mdoverride; gcldebug("Overriding minimum digits: %i", mdoverride); } if (noredirect && redirect) { free(redirect); redirect = NULL; } if (nosilent) silent = 0; interpret(); } cleanup(); return 2; } /* error opening file */ else flock(fileno(gclfile), LOCK_EX); /* Unix specific */ } /* filename */ else gclfile = stdin; /* no filename specified */ gcldebug("Entering the parser"); parser = parse(); if ((gclprint != NULL) && ((cmdcount >= 0) || publish)) { free(gclprint); gclprint = NULL; shellcount = 0; } /* Turn off compilation if not a counter, if make, or if "print" used */ nocompile |= cmdwhatami | whatami | make | (gclnocompile && !publish) | (gclprint != NULL); /* Also turn off interpretation if make */ compileonly |= make; if (shellcount && gclprint) { count = strtoul(gclprint, NULL, 0); free(gclprint); gclprint = NULL; } #ifdef __FreeBSD__ whattime = timer + (time_t)gtz.secs; now = gtz.tz == STZ ? localtime(&whattime) : gmtime(&whattime); #else gmtoff = gtz.tz == STZ ? timer - mktime(gmtime(&timer)) : 0; #endif zoneminutes = (gmtoff + gtz.secs) / 60; zonehours = zoneminutes / 60; if (zoneminutes < 0) zoneminutes = -zoneminutes; zoneminutes %= 60; #ifndef __FreeBSD__ whattime = timer + (time_t)gtz.secs; now = gtz.tz == STZ ? localtime(&whattime) : gmtime(&whattime); #endif /* * C library does not give us a clue as to what week we are in. * But the time() function returns the number of seconds elapsed * since 1-1-1970 0:00:00. That happened to be a Thursday. * Three days later was a Sunday. We need to SUBTRACT three days * from the number of seconds elapsed, and divide the result by * the number of seconds in a week. To make it easier on the * microprocessor (division can take some time), we subtract * a number of additional weeks to bring us closer to our own time. * * The numbers used here will result in the number of weeks elapsed * since Sunday, 18 May 1997, 0:00:00 of the time zone used. * * We also need to adjust for whatever time zone the counter is * supposed to work. * * Note: This is not perfect and needs to be refined: It assumes * week starts on a Sunday. That is not true in all countries. */ weeks = (whattime - 24*60*60*9999 + gmtoff) / (24*60*60*7); if (rst > NEVER) { if (today.year != now->tm_year) { count = 1; } else if (today.month != now->tm_mon) { if (rst >= MONTHLY) count = 1; } else if ((today.day != now->tm_mday) && (rst == DAILY)) count = 1; } else if (rst < NEVER) { /* i.e., WEEKLY */ if (today.week != weeks) count = 1; } if (gotcookies) { /* * Parse cookies * * This section may look somewhat confusing if you * are not thoroughly familiar with C pointers. * * HTTP cookies come to us as one big string * of pairs of name and value. The name is separated * from the value by an equal sign; the pairs are * separated, but not terminated, by a semicolon. * * As usual, we deal with errors (out of memory, in * this case) gracefully: We proceed with the program * at the cost of increasing the counter value when we * may have been asked not to... * * Note that if getenv() fails, that is not an error. * It simply means no cookie is set. */ register char *e; gcldebug("Looking into cookie jar"); e = getenv(httpcookie); if (e) { cookies = strdup(e); if (cookies == NULL) { gotcookies = 0; gclwarning("Not enough memory to read HTTP cookies"); } else { for (i = 0; cookies[i]; i++) if (cookies[i] == '=') numcookies++; gcldebug("Found %i cookie%s", numcookies, "s" + (numcookies == 1)); cookiepairs = malloc(numcookies * sizeof(cookiepair)); if (cookiepairs == NULL) { numcookies = gotcookies = 0; free(cookies); cookies = NULL; gclwarning("Not enough memory to analyze HTTP cookies"); } else { for (i = 0, cookiepairs[0].name = e = cookies; *e; e++) { if (*e == ';') { *e = '\0'; cookiepairs[i].name = e + 1; } else if (*e == '=') { *e = '\0'; cookiepairs[i++].value = e + 1; } } } } } else gcldebug("Jar empty"); } if (!parser) { if (publish && (gclfile != stdin)) { flock(fileno(gclfile), LOCK_UN); /* Unix specific */ fclose(gclfile); gclfile = stdout; } else if (gclfile == stdin) gclfile = stdout; else rewind(gclfile); if (!nocompile) { gcldebug("Entering the compiler"); if (!secure && (cmdcount >= 0)) { if (warnings) fprintf(stderr, "GCL Warning: Changing count from " integer " to " integer ".\n", count, (counter_t)cmdcount); count = (counter_t)cmdcount; } if (noredirect && (redirectorinhibit(redirection))) one = 0; compile(); gcldebug("Returned from the compiler"); } else gcldebug("Skipping compilation (`-n' selected)"); if (gclfile != stdout) { flock(fileno(gclfile), LOCK_UN); /* Unix specific */ fclose(gclfile); } if (issigma) { count = mean + increment; } if (script && !make && !publish) switch (fork()) { /* Unix specific */ case 0: sprintf(lexbuffer, issigned ? "GCLCOUNT=" sinteger: "GCLCOUNT=" integer, count); putenv(lexbuffer); if (issigma == 0) { sprintf(lexbuffer, issigned ? "GCLNEXT=" sinteger: "GCLNEXT=" integer, (counter_t)(count + increment * (one && !inhibited && !nocompile))); putenv(lexbuffer); } putenv("GCLVER=" GCLVERSION); if (gclfile != stdout) { realpath(filename, lexbuffer); /* Unix specific */ setenv("GCLFILE", lexbuffer, 1); } if (cookie) setenv("GCLCOOKIE", cookie, 1); system(script); return 0; default: break; } if (!compileonly) { gcldebug("Entering the interpreter"); if (mdoverride) { mindigits = mdoverride; gcldebug("Overriding minimum digits: %i", mdoverride); } if (cmdcount >= 0) count = (counter_t)cmdcount; if (noredirect && redirect) { free(redirect); redirect = NULL; } if (nosilent) silent = 0; whatami = cmdwhatami; interpret(); gcldebug("Done interpreting"); } else gcldebug("Skipping interpretation (`-c' selected)"); if (make) makedefaults(); retval = 0; } /* !parse() */ else { if (gclfile != stdin) { flock(fileno(gclfile), LOCK_UN); /* Unix specific */ fclose(gclfile); } if (!compileonly) { gcldebug("Skipping the compiler"); gcldebug("Entering the interpreter"); if (cmdcount >= 0) count = (counter_t)cmdcount; whatami = cmdwhatami; interpret(); gcldebug("Done interpreting"); } retval = 1; } /* syntax errors */ cleanup(); gcldebug("Done. Returning (%i) to system", retval); return retval; } static int rgbwarning(int color) { if (color > 255) gclwarning("Color value %u is out of range. Converting to %u", color, color & 0xFF); return color & 0xFF; } static void cleanup(void) { register int i; if (gclpath) { free(gclpath); gclpath = NULL; } if (script) { free(script); script = NULL; } if (cookie) { free(cookie); cookie = NULL; } if (cookiepairs) { free(cookiepairs); cookiepairs = NULL; } if (redirect) { free(redirect); redirect = NULL; } if (gclprint) { free(gclprint); gclprint = NULL; } for (i = 0; i < GRAPHICS; i++) if (picture[i].graphic) { free(picture[i].graphic); } memset(picture, 0, sizeof(picture)); freeinhibitors(); freerelayors(); } static void addinhibitor(char *env, int iscookie, gclcondition condition, gclinhibitor inh, int op) { register inhibit *temp; if (env == NULL) { parserror(); return; } temp = (inhibit *)malloc(sizeof(inhibit)); if (temp == NULL) { if (env != from) free(env); parserror(); return; } temp->val = strdup(lexbuffer); if (temp->val == NULL) { free(temp); if (env != from) free(env); parserror(); return; } temp->env = env; temp->condition = condition; temp->inhibitor = inh; temp->cookie = iscookie; temp->op = op; temp->next = NULL; if (firstinhibitor == NULL) firstinhibitor = lastinhibitor = temp; else { lastinhibitor->next = temp; lastinhibitor = temp; } gotcookies |= iscookie != 0; } static void freeinhibitors(void) { while (firstinhibitor != NULL) { lastinhibitor = firstinhibitor->next; if (firstinhibitor->env != from) free(firstinhibitor->env); free(firstinhibitor->val); free(firstinhibitor); firstinhibitor = lastinhibitor; } gotcookies &= ~1; } static void addrelayor(char *env, int iscookie, gclcondition condition, gclrelayor rel, char *url, int op) { register relay *temp; if (env == NULL) { parserror(); if (url) free(url); return; } temp = (relay *)malloc(sizeof(relay)); if (temp == NULL) { if (env != from) free(env); if (url) free(url); parserror(); return; } temp->val = strdup(lexbuffer); if (temp->val == NULL) { free(temp); if (env != from) free(env); if (url) free(url); parserror(); return; } temp->env = env; temp->condition = condition; temp->relayor = rel; temp->cookie = iscookie; temp->url = url; temp->op = op; temp->next = NULL; if (firstrelayor == NULL) firstrelayor = lastrelayor = temp; else { lastrelayor->next = temp; lastrelayor = temp; } gotcookies |= (iscookie != 0) << 1; } static void freerelayors(void) { while (firstrelayor != NULL) { lastrelayor = firstrelayor->next; if (firstrelayor->env != from) free(firstrelayor->env); if (firstrelayor->url != NULL) free(firstrelayor->url); free(firstrelayor->val); free(firstrelayor); firstrelayor = lastrelayor; } gotcookies &= ~2; } static void htmlputc(unsigned char c) { if (c) { if (localuse) fputc(c, stdout); else switch (c) { case ' ': printf(" "); break; case '<': printf("<"); break; case '>': printf(">"); break; case '&': printf("&"); break; case '"': printf("""); break; default: if ((c == '$') || (c == '#') || (c == '%') || (c == '@') || (c >= 127)) printf("&#%u;", (unsigned int)c); else fputc(c, stdout); break; } } } static int timesprint(char *buffer, int t) { register int digits; digits = sprintf(buffer, "^%.2i%%%.2i" + !t, now->tm_hour, now->tm_min); if (seconds) digits += sprintf(buffer + digits, "%%%.2i", now->tm_sec); return digits; } static int zonesprint(char *buffer) { if (zonehours || zoneminutes) { register int digits; digits = sprintf(buffer, zoneminutes ? "%+.2i%%%02i" : "%+.2i", zonehours, zoneminutes); if (*buffer == '-') *buffer = '<'; else if (*buffer == '+') *buffer = '>'; return digits; } else return sprintf(buffer, "@"); } static int datesprint(char *buffer) { return sprintf(buffer, "%i-%.2i-%.2i", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday); } static void dd(char const * const name, int const value) { fprintf(gcldefaultfile, value < 0 ? "\n#define\t%s\t(%i)" : "\n#define\t%s\t%i", name, value); } static void dds(char const * const name, char const * const value) { fprintf(gcldefaultfile, "\n#define\t%s\t\"%s\"", name, value); } static void ddc(char const * const name, char const * const value) { fprintf(gcldefaultfile, "\n#define\t%s\t%s", name, value); } static void makedefaults(void) { int i, j, k; register gdImagePtr imgptr; gcldebug("Creating new defaults"); rename("gcldefaults.h", "gcldefaults.h.old"); gcldefaultfile = fopen("gcldefaults.h", "w"); if (gcldefaultfile == NULL) { fprintf(stderr, "GCL Make: Can't create gcldefaults.h\n"); return; } fprintf(gcldefaultfile, "/*\n * Graphic Counter Language\n *\n * Customized defaults\n *\n * Automaticaly created by GCL " GCLVERSION "\n *\n * Copyright 1999 G. Adam Stanislav.\n * All rights reserved.\n */\n\n#ifndef\tGCLDEFAULTS_H\n#define\tGCLDEFAULTS_H\n"); dd("GCLDEFAULTOPTIMIZE", optimize); ddc("GCLDEFAULTTIMEZONE", gtz.tz == STZ ? "STZ" : "UTC"); dd("GCLDEFAULTTIMEZONEOFFSET", gtz.secs); fprintf(gcldefaultfile, "L"); dd("GCLDEFAULTSHOWSECONDS", seconds); dd("GCLDEFAULTTRANSPARENT", transparent); dd("GCLDEFAULTVERTICAL", vertical); dd("GCLDEFAULTEXPIRES", expires); for (i = 0; i < NUMPADS; i++) dd(gcldefaultpads[i], pad[i]); for (i = 0; i < GRAPHICTYPES; i++) dd(gcldefaultkern[i], kern[i]); dd("GCLDEFAULTFRAMETYPE", frametype); for (i = 0; i < GRAPHICTYPES; i++) ddc(gcldefaulthalign[i], halignflag[i] < 0 ? "LEFT" : halignflag[i] == 0 ? "CENTER" : "RIGHT"); for (i = 0; i < GRAPHICTYPES; i++) ddc(gcldefaultvalign[i], valignflag[i] < 0 ? "TOP" : valignflag[i] == 0 ? "MIDDLE" : "BOTTOM"); for (i = 0; i < GRAPHICTYPES; i++) dd(gcldefaulthashift[i], hashift[i]); for (i = 0; i < GRAPHICTYPES; i++) dd(gcldefaultvashift[i], vashift[i]); ddc("GCLDEFAULTHSHIFTFLAG", hshiftflag < 0 ? "LEFT" : hshiftflag == 0 ? "0" : "RIGHT"); ddc("GCLDEFAULTVSHIFTFLAG", vshiftflag < 0 ? "UP" : vshiftflag == 0 ? "0" : "DOWN" ); dd("GCLDEFAULTHSHIFT", hshift); dd("GCLDEFAULTVSHIFT", vshift); dd("GCLDEFAULTGROUP", group); ddc("GCLDEFAULTGROUPSEPARATOR", groupseparator == COMMAS ? "COMMAS" : groupseparator == DOTS ? "DOTS" : "SPACES"); dd("GCLDEFAULTREVCOM", revcom); dd("GCLDEFAULTREVDIG", revdig); fprintf(gcldefaultfile, "\n"); dds("GCLDEFAULTDEFINECHAR", definechar); fprintf(gcldefaultfile, "\n"); dd("BKGRED", bkg.red); dd("BKGGREEN", bkg.green); dd("BKGBLUE", bkg.blue); dd("INVISRED", invis.red); dd("INVISGREEN", invis.green); dd("INVISBLUE", invis.blue); dd("TTRED", tt.red); dd("TTGREEN", tt.green); dd("TTBLUE", tt.blue); fprintf(gcldefaultfile, "\n\n#endif\t/* GCLDEFAULTS_H not defined */\n"); fclose(gcldefaultfile); rename("gcldefaults.c", "gcldefaults.c.old"); gcldefaultfile = fopen("gcldefaults.c", "w"); if (gcldefaultfile == NULL) { fprintf(stderr, "GCL Make: Can't create gcldefaults.c\n"); return; } fprintf(gcldefaultfile, "/*\n * Graphic Counter Language\n *\n * C defaults source file\n *\n * Automatically created by GCL " GCLVERSION "\n *\n * Copyright 1999 G. Adam Stanislav\n * All rights reserved\n */"); openarraypicture(0); for (i = 0; i < ARRAYPICTURE; i++) { openpicture(i); if (picture[i].iscreated) { fprintf(gcldefaultfile, "\n\n/* Define %s image. */", picname[i]); imgptr = picture[i].isarray ? arraypicture.image : picture[i].image; for (j = 0; j < picture[i].dy; j++) { fprintf(gcldefaultfile, "\nstatic unsigned char defpic_%i_%i[%i] = {", i, j, picture[i].dx); for (k = 0; k < picture[i].dx; k++) { fprintf(gcldefaultfile, ",\n\t%i" + (k == 0), imgptr->pixels[j][k + picture[i].x]); } fprintf(gcldefaultfile, "\n};"); } fprintf(gcldefaultfile, "\n\nstatic unsigned char *defpic_%i[%i] = {", i, picture[i].dy); for (j = 0; j < picture[i].dy; j++) { fprintf(gcldefaultfile, ",\n\tdefpic_%i_%i" + (j == 0), i, j); } fprintf(gcldefaultfile, "\n};\n\nstatic gdImage img_%i = {\n\tdefpic_%i,\n\t%i,\n\t%i,\n\t%i,\n\t{", i, i, picture[i].dx, picture[i].dy, imgptr->colorsTotal); for (j = 0; j < imgptr->colorsTotal; j++) fprintf(gcldefaultfile, ",\n\t\t%i" + (j == 0), imgptr->red[j]); fprintf(gcldefaultfile, "\n\t},\n\t{"); for (j = 0; j < imgptr->colorsTotal; j++) fprintf(gcldefaultfile, ",\n\t\t%i" + (j == 0), imgptr->green[j]); fprintf(gcldefaultfile, "\n\t},\n\t{"); for (j = 0; j < imgptr->colorsTotal; j++) fprintf(gcldefaultfile, ",\n\t\t%i" + (j == 0), imgptr->blue[j]); fprintf(gcldefaultfile, "\n\t},\n\t{"); for (j = 0; j < gdMaxColors; j++) fprintf(gcldefaultfile, ",\n\t\t%i" + (j == 0), imgptr->open[j]); fprintf(gcldefaultfile, "\n\t},\n\t%i,\n\tNULL,\n\t0,\n\tNULL,\n\tNULL,\n\t{ 0 },\n\t{ 0 },\n\t0,\n\t0,\n\tNULL,\n\t0\n};", imgptr->transparent); } } fprintf(gcldefaultfile, "\n\nstatic gdImagePtr const defaultimage[GRAPHICS] = {"); for (i = 0; i < ARRAYPICTURE; i++) { if (picture[i].iscreated) fprintf(gcldefaultfile, ",\n\t&img_%i" + (i == 0), i); else fprintf(gcldefaultfile, ",\n\tNULL" + (i == 0)); } for (;i < GRAPHICS; i++) fprintf(gcldefaultfile, ",\n\tNULL"); fprintf(gcldefaultfile, "\n};\n\n"); fclose(gcldefaultfile); } static void createimage(int inumber) { register FILE *tempfile; if (picture[inumber].graphic != NULL) { tempfile = fopen(picture[inumber].graphic, "rb"); if (tempfile != NULL) { switch (picture[inumber].gtype) { case GIFSOURCE: picture[inumber].image = gdImageCreateFromGif(tempfile); gcldebug("Creating %s image from GIF", picname[inumber]); break; case GDSOURCE: picture[inumber].image = gdImageCreateFromGd(tempfile); gcldebug("Creating %s image from GD", picname[inumber]); break; case XBMSOURCE: picture[inumber].image = gdImageCreateFromXbm(tempfile); gcldebug("Creating %s image from Xbm", picname[inumber]); break; } /* picture type */ fclose(tempfile); if (picture[inumber].image == NULL) gclwarning("Failed to create %s image", picname[inumber]); gcldebug(picture[inumber].image == NULL ? "Failed to create %s image" : "Successfully created %s image", picname[inumber]); } /* picture opened */ else { gclwarning("Failed to open %s picture file `%s'", picname[inumber], picture[inumber].graphic); gcldebug("Failed to open %s picture file `%s'", picname[inumber], picture[inumber].graphic); } free (picture[inumber].graphic); picture[inumber].graphic = NULL; } /* picture defined */ } static void openarraypicture(int turner) { register int i; register int ted; slashnotneeded = (picturedirectory.graphic != NULL) && (picturedirectory.graphic[strlen(picturedirectory.graphic) - 1] == '/'); if ((arraypicture.graphic == NULL) && (picturedirectory.graphic != NULL)) { sprintf(pathbuffer, "%s%sarray.%s", picturedirectory.graphic, "/" + slashnotneeded, graphictypes[picturedirectory.gtype]); if (access(pathbuffer, F_OK) == 0) { arraypicture.graphic = strdup(pathbuffer); if (arraypicture.graphic == NULL) fprintf(stderr, "GCL Interpreter: Not enough memory: `%s'.\n", pathbuffer); else arraypicture.gtype = picturedirectory.gtype; } } createimage(ARRAYPICTURE); if (arraypicture.image == NULL) for (i = 0; i < GRAPHICS; i++) picture[i].isarray = 0; else if (turner && (tt.red || tt.green || tt.blue)) { ted = gdImageColorExact(arraypicture.image, 0, 0, 0); if (ted < 0) ted = gdImageColorClosest(arraypicture.image, 0, 0, 0); if ((ted >= 0) && (ted != gdImageGetTransparent(arraypicture.image))) { arraypicture.image->red[ted] = tt.red; arraypicture.image->green[ted] = tt.green; arraypicture.image->blue[ted] = tt.blue; } } /* colorize default images */ if (turner && (tt.red || tt.green || tt.blue)) { for (i = 0; i < GRAPHICS; i++) if (defaultimage[i] != NULL) { ted = gdImageColorExact(defaultimage[i], 0, 0, 0); if (ted < 0) ted = gdImageColorClosest(defaultimage[i], 0, 0, 0); if ((ted >= 0) && (ted != gdImageGetTransparent(defaultimage[i]))) { defaultimage[i]->red[ted] = tt.red; defaultimage[i]->green[ted] = tt.green; defaultimage[i]->blue[ted] = tt.blue; } } } } static void openpicture(int inumber) { if (picture[inumber].isarray) { picture[inumber].iscreated = 1; gcldebug("Using array image for %s", picname[inumber]); } else { if ((picture[inumber].graphic == NULL) && (picturedirectory.graphic != NULL)) { sprintf(pathbuffer, "%s%s%s.%s", picturedirectory.graphic, "/" + slashnotneeded, defaultpicturename[inumber], graphictypes[picturedirectory.gtype]); if (access(pathbuffer, F_OK) == 0) { picture[inumber].graphic = strdup(pathbuffer); picture[inumber].gtype = picturedirectory.gtype; } } createimage(inumber); if ((picture[inumber].image == NULL) && (defaultimage[inumber] != NULL) && (nodefaultpicture[inumber] == 0)) { gcldebug("Using default %s image", picname[inumber]); picture[inumber].image = defaultimage[inumber]; } if (picture[inumber].image) { picture[inumber].iscreated = 1; picture[inumber].x = picture[inumber].y = 0; picture[inumber].dx = gdImageSX(picture[inumber].image); picture[inumber].dy = gdImageSY(picture[inumber].image); } } } static counter_t getrandomnumber(int s) { /* * This is not a very good generator. I am open to suggestions... */ randomnumber = randomnumber * GCLRNDMUL + GCLRNDADD; if (s == 0) randomnumber &= ((counter_t)(-1) >> 1); return randomnumber; } static void setincrementrange(scounter_t a, scounter_t b) { register scounter_t temp; if (a <= b) { incrementrange[0] = a; incrementrange[1] = b; } else { incrementrange[0] = b; incrementrange[1] = a; } if (incrementrange[0] == incrementrange[1]) increment = incrementrange[0]; else { temp = incrementrange[1] - incrementrange[0] + 1; increment = getrandomnumber(1); if (temp) { increment %= (counter_t)temp; increment += incrementrange[0]; } } issigma = 0; }