home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 2 / goldfish_vol2_cd1.bin / files / dev / misc / flexcat / src / flexcat.c < prev    next >
C/C++ Source or Header  |  1994-10-24  |  38KB  |  1,810 lines

  1. /*
  2.     FlexCat.c:    The flexible catalog creator            V 1.4
  3.  
  4.     This program is free software; you can redistribute it and/or modify
  5.     it under the terms of the GNU General Public License as published by
  6.     the Free Software Foundation; either version 2 of the License, or
  7.     (at your option) any later version.
  8.  
  9.     This program is distributed in the hope that it will be useful,
  10.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.     GNU General Public License for more details.
  13.  
  14.     You should have received a copy of the GNU General Public License
  15.     along with this program; if not, write to the Free Software
  16.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18.  
  19.  
  20.     Ok, this is nothing special. It grabs a catalog translation and a
  21.     catalog description file and produces catalogs and the source to
  22.     handle 'em. What is it else than lots of other programs?
  23.  
  24.     The difference is, that YOU determine what source FlexCat produces.
  25.     Another file is scanned by FlexCat to produce code. This file contains
  26.     some c-string like special characters (%v for version for example)
  27.     You can edit this file and modify it as you want. So FlexCat can produce
  28.     C source as well as Assembler, Oberon, Modula 2, E, ...
  29.  
  30.  
  31.     Command line syntax:
  32.  
  33.     FlexCat CDFILE/A,CTFILE,NEWCTFILE/K,CATALOG/K,SOURCES/M
  34.  
  35.     CDFILE    - the name of the catalog description file; see CatComp for
  36.         syntax
  37.     CTFILE    - the name of a catalog translation file; see CatComp for
  38.         syntax
  39.     NEWCTFILE - the name of a catalog transcription file to be created
  40.         (will be a copy of CDFILE except that the strings remain
  41.         empty and will be put as comments behind the string
  42.         definition)
  43.     CATALOG   - the name of the catalog to be created; default is the
  44.         basename of the CDFILE and .catalog appended
  45.     SOURCES   - the sourcefiles to be created. These arguments take a special
  46.         form: sourcefile=templatefile where sourcefile is the
  47.         name of the file to create and templatefile is the name of
  48.         a template file containing some special patterns. The
  49.         template file will be scanned and written to the source
  50.         file with these special patterns replaced by certain
  51.         strings. Possible patterns are:
  52.  
  53.             %b        basename of CDFILE (for example "file", if
  54.                 CDFILE is "file.cd")
  55.             %f0     the filename of CDFILE
  56.             %f1     same as %f0, except that a trailing
  57.                 ending will be removed
  58.             %f2     same as %f0, except that leading pathname
  59.                 components will be removed
  60.             %f3     same as %f0, except that both ending and
  61.                 pathname components will be removed
  62.             %l        language (of CDFILE)
  63.             %n        number of catalog strings
  64.             %o0     the name of the sourcefile, that is currently
  65.                 created; (What the heck do the guys need this
  66.                 for? :-)
  67.             %o1     same as %00, except that a trailing
  68.                 ending will be removed
  69.             %o2     same as %o0, except that leading pathname
  70.                 components will be removed
  71.             %o3     same as %o0, except that both ending and
  72.                 pathname components will be removed
  73.             %v        version
  74.             %%        the percent sign itself
  75.  
  76.         The following patterns indicate, that the line should be
  77.         repeated for each catalog string.
  78.             %i        the string's ID descriptor
  79.             %d        the string's ID
  80.             %e        the string's number (counting from 0)
  81.             %s        the string itself
  82.             %(...)  indicates that anything between the brackets
  83.                 should appear for any line except the last
  84.                 (Oberon and Modula need a "," to separate array
  85.                 entries, but the last entry must not be
  86.                 terminated by a ",". So you would terminate an
  87.                 array entry by a %(,).)
  88.  
  89.     Example: Let the string description be
  90.  
  91.     MSG_HELLO (5//)
  92.     Hello!
  93.  
  94.     Then the special characters produce
  95.     %i = MSG_HELLO
  96.     %d = 5
  97.     %s = "Hello!"
  98.  
  99.     Additionally the following characters may appear (as well as in the
  100.     catalog description and catalog translation file)
  101.     \b    = Backspace (Ascii 8)
  102.     \c    = Control sequence introducer (Ascii 155)
  103.     \e    = Escape (Ascii 27)
  104.     \f    = Form feed (Ascii 12)
  105.     \g    = Display beep (Ascii 7)
  106.     \n    = line feed, newline (Ascii 10)
  107.     \r    = carriage return (Ascii 13)
  108.     \t    = Tab (Ascii 9)
  109.     \v    = Vertical tab (Ascii 11)
  110.     \)    = the character ')' (Needed inside %(..))
  111.     \\    = the backslash itself
  112.     \xHH  = Ascii code HH (where HH are hex digits)
  113.     \OOO  = Ascii code OOO (where OOO are octal digits)
  114.  
  115.     A backslash at the end of a line indicates that the line should be
  116.     concatenated with the next one.
  117.  
  118.  
  119.     Computer:    Amiga 1200            Compiler: Dice, 3.01
  120.  
  121.     Author:    V 1.0        31.06.1993        Jochen Wiedmann
  122.                         Am Eisteich 9
  123.                       72555 Metzingen
  124.                         Tel. 07123 / 14881
  125.  
  126.                 Internet: wiedmann@uni-tuebingen.de
  127.  
  128.         V1.01        Fixed a bug: The length of the source string
  129.                 was used to check for the stringlen instead of
  130.                 the real stringlen.
  131.  
  132.         V1.1        Fixed two bugs: FlexCat didn't notice, if an
  133.                 ID was defined twice (C-Compiler did later.) and
  134.                 using language strings like français did not work
  135.                 because of the the accented char. Introduced E
  136.                 support. (Thanks Lionel Vintenat)
  137.  
  138.         V1.2        Fixed a bug in the E source generator: " was
  139.                 converted into \" and ' was not converted.
  140.  
  141.         V1.3        Fixed a bug that caused FlexCat to hang if a
  142.                 catalog translation was updated and a string
  143.                 was missing in the catalog description.
  144.  
  145.                 Fixed a bug that caused FlexCat to create
  146.                 invalid catalogs probably. (Seems like the
  147.                 locale.library expects a completely different
  148.                 behaviour in padding version and language
  149.                 strings and the real catalog strings.)
  150.  
  151.                 Added %e to source descriptions.
  152.  
  153.         V1.4        Added the Environment variable FLEXCAT_SDDIR.
  154.                 Uses AutoC_c.sd and AutoC_h.sd now and is thus
  155.                 compilable by Dice and SAS/C only.
  156.  
  157.                 Fixed a problem in stringtype Oberon:
  158.                 Binary characters should be like \000 and
  159.                 not \0.
  160.  
  161.                 Fixed a problem in stringtype E:
  162.                 \e was written as \033.
  163.  
  164.                 Added %f* (source description filename) and %o*
  165.                 (source filename) to source descriptions.
  166. */
  167.  
  168.  
  169.  
  170. #include <stdlib.h>
  171. #include <stdio.h>
  172. #include <string.h>
  173. #include <ctype.h>
  174. #ifdef AMIGA
  175. #include <exec/types.h>
  176. #include <clib/exec_protos.h>
  177. #include <clib/dos_protos.h>
  178. #include <clib/utility_protos.h>
  179. #include "FlexCat_cat.h"
  180.  
  181. #ifdef AZTEC_C
  182. #include <pragmas/exec_lib.h>
  183. #include <pragmas/dos_lib.h>
  184. #include <pragmas/utility_lib.h>
  185. #endif
  186.  
  187. #if defined(_DCC)  ||  defined(__SASC)  ||  defined(__MAXON__)
  188. #include <pragmas/exec_pragmas.h>
  189. #include <pragmas/dos_pragmas.h>
  190. #include <pragmas/utility_pragmas.h>
  191. #endif
  192.  
  193. #ifdef __GNUC__
  194. #include <inline/exec.h>
  195. #include <inline/dos.h>
  196. #include <inline/utility.h>
  197. #endif
  198.  
  199. #ifdef tolower
  200. #undef tolower
  201. #endif
  202. #define tolower ToLower
  203. #define stricmp Stricmp
  204. #define strnicmp Strnicmp
  205.  
  206. #endif
  207.  
  208.  
  209. #ifndef FALSE
  210. #define FALSE 0
  211. #endif
  212. #ifndef TRUE
  213. #define TRUE (!FALSE)
  214. #endif
  215.  
  216.  
  217. #define MAXPATHLEN 512
  218. #define FLEXCAT_SDDIR "FLEXCAT_SDDIR"
  219. #ifdef AMIGA
  220. #define DEFAULT_FLEXCAT_SDDIR "PROGDIR:lib"
  221. #else
  222. #define DEFAULT_FLEXCAT_SDDIR "lib"
  223. #endif
  224.  
  225.  
  226.  
  227. enum StringTypes {
  228.     TYPE_C,        /*    Produce C strings            */
  229.     TYPE_ASSEMBLER, /*    Produce Assembler strings        */
  230.     TYPE_OBERON,    /*    Produce Oberon strings            */
  231.     TYPE_E,        /*    Produce E strings. (Oops, thought   */
  232.             /*    it allows only 32 bit integers? ;-) */
  233.     TYPE_NONE        /*    Simple strings                */
  234. };
  235.  
  236.  
  237. enum OutputModes {
  238.     OutputMode_None,    /*  Nothing written yet         */
  239.     OutputMode_Bin,    /*  Last character written was binary    */
  240.     OutputMode_Ascii    /*  Last character written was Ascii    */
  241. };
  242.  
  243. struct CatString
  244. { struct CatString *Next;
  245.   char *CD_Str;
  246.   char *CT_Str;
  247.   char *ID_Str;
  248.   int MinLen, MaxLen, ID, Nr;
  249. };
  250.  
  251. struct CDLine
  252. { struct CDLine *Next;
  253.   char *Line;
  254. };
  255.  
  256.  
  257. struct CatString *FirstCatString = NULL;  /*  First catalog string    */
  258. struct CDLine *FirstCDLine = NULL;  /*    First catalog description line    */
  259.  
  260. char *BaseName = "";                /*  Basename of catalog description */
  261. char *Language = "english";         /*  Language of catalog description */
  262. int CatVersion = 0;            /*    Version of catalog to be opened */
  263. int LengthBytes = 0;            /*    Number of bytes to preceed a    */
  264.                     /*    created string and containing    */
  265.                     /*    its length.            */
  266. char *CatLanguage = NULL;        /*    Language of catalog translation */
  267. char *CatVersionString = NULL;        /*    version string of catalog    */
  268.                     /*    translation            */
  269. int CodeSet = 0;            /*    Codeset of catalog translation    */
  270. int NumStrings = 0;            /*    Number of catalog strings    */
  271. int LongStrings = TRUE;         /*    Generate long or short strings    */
  272.  
  273.  
  274. char *ScanFile;             /*    File currently scanned        */
  275. int ScanLine;                /*    Line currently scanned        */
  276.  
  277. struct Library *UtilityBase = NULL;
  278. extern struct ExecBase *SysBase;
  279. extern struct Library *DOSBase;
  280.  
  281.  
  282.  
  283.  
  284. const UBYTE VERSION[] = "$VER: FlexCat 1.4 (28.07.94)   by  Jochen Wiedmann";
  285.  
  286.  
  287.  
  288.  
  289. /*
  290.     This terminates the program.
  291. */
  292. void Cleanup(int result)
  293.  
  294. {
  295. #ifdef AMIGA
  296.   if (UtilityBase)
  297.   { CloseLibrary(UtilityBase);
  298.   }
  299. #endif
  300.   exit(result);
  301. }
  302.  
  303. /*
  304.     This shows an error message and terminates
  305. */
  306. struct RDArgs *Args;
  307. void ShowError(const UBYTE (*msg), ...)
  308.  
  309. { char **ptr = (char **) &msg;
  310.  
  311.   fprintf(stderr, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]);
  312.   putc('\n', stderr);
  313.   Cleanup(10);
  314. }
  315.  
  316.  
  317.  
  318. /*
  319.     This shows the message: Memory error.
  320. */
  321. void MemError(void)
  322.  
  323. { ShowError(GetString(msgMemoryError), NULL);
  324. }
  325.  
  326.  
  327.  
  328.  
  329. /*
  330.     This shows a warning
  331. */
  332. void ShowWarn(const UBYTE (*msg), ...)
  333.  
  334. { char **ptr = (char **) &msg;
  335.  
  336.   fprintf(stderr, (char *) GetString(msgWarning), ScanFile, ScanLine);
  337.   fprintf(stderr, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]);
  338.   putc('\n', stderr);
  339. }
  340.  
  341.  
  342.  
  343.  
  344. /*
  345.     This allocates a string
  346. */
  347. char *AllocString(const char *str)
  348.  
  349. { char *ptr;
  350.  
  351.   if (!(ptr = malloc(strlen(str)+1)))
  352.   { MemError();
  353.   }
  354.   strcpy(ptr, str);
  355.   return(ptr);
  356. }
  357.  
  358.  
  359.  
  360.  
  361. /*
  362.     This translates a hex character.
  363. */
  364. int gethex(int c)
  365.  
  366. {
  367.   if (c >= '0'  &&  c <= '9')
  368.   { return(c - '0');
  369.   }
  370.   else if (c >= 'a'  &&  c <= 'f')
  371.   { return(c - 'a' + 10);
  372.   }
  373.   else if (c >= 'A'  &&  c <= 'F')
  374.   { return(c - 'A' + 10);
  375.   }
  376.   ShowWarn(GetString(msgExpectedHex));
  377.   return(0);
  378. }
  379.  
  380.  
  381.  
  382.  
  383. /*
  384.     This translates an octal digit.
  385. */
  386. int getoctal(int c)
  387.  
  388. {
  389.   if (c >= '0'  &&  c <= '7')
  390.   { return(c - '0');
  391.   }
  392.   ShowWarn(GetString(msgExpectedOctal));
  393.   return(0);
  394. }
  395.  
  396.  
  397.  
  398.  
  399. /*
  400.     Reading a line is somewhat complicated in order to allow lines of any
  401.     length.
  402.  
  403.     Inputs: fp         - the file, where the input comes from
  404.         AllowComment - TRUE, if a leading semicolon should force to
  405.                interpret the line as a comment line
  406. */
  407. #define BUFSIZE 4096
  408. char *ReadLine(FILE *fp, int AllowComment)
  409.  
  410. { char *OldLine, *NewLine = NULL;
  411.   int c = '\0';
  412.   int Len = 0, LineLen = 0;
  413.   int FirstChar = TRUE;
  414.   int BackslashSeen = FALSE;
  415.  
  416.   while (c != EOF)
  417.   { if (Len+10 > LineLen)
  418.     { OldLine = NewLine;
  419.       if (!(NewLine = malloc(LineLen+BUFSIZE)))
  420.       { MemError();
  421.       }
  422.       strncpy(NewLine, OldLine, LineLen);
  423.       if (OldLine)
  424.       { free(OldLine);
  425.       }
  426.       LineLen += BUFSIZE;
  427.     }
  428.  
  429.     c = getc(fp);
  430.     if (FirstChar)
  431.     { if (c == EOF)
  432.       { free(NewLine);
  433.     return(NULL);
  434.       }
  435.       FirstChar = FALSE;
  436.     }
  437.  
  438.     switch(c)
  439.     { case '\r':
  440.     break;
  441.       case '\n':
  442.     ++ScanLine;
  443.     if (BackslashSeen)
  444.     { NewLine[Len++] = c;
  445.       BackslashSeen = FALSE;
  446.       break;
  447.     }
  448.     c = EOF;
  449.       case EOF:
  450.     break;
  451.       case '\\':
  452.     BackslashSeen = TRUE;
  453.     NewLine[Len++] = c;
  454.     break;
  455.       default:
  456.     BackslashSeen = FALSE;
  457.     NewLine[Len++] = c;
  458.     }
  459.   }
  460.   NewLine[Len] = '\0';
  461.  
  462.   return(NewLine);
  463. }
  464.  
  465.  
  466.  
  467.  
  468. /*
  469.     This removes trailing blanks.
  470. */
  471. void OverSpace(char **strptr)
  472.  
  473. { int c;
  474.  
  475.   while ((c = **strptr) == ' '  ||  c == '\t')
  476.   { (*strptr)++;
  477.   }
  478. }
  479.  
  480.  
  481.  
  482.  
  483.  
  484.  
  485.  
  486.  
  487. /*
  488.     ReadChar scans an input line translating the backslash characters.
  489.  
  490.     Inputs: strptr  - a pointer to a stringpointer; the latter points to the
  491.               next character to be read and points behind the read
  492.               bytes after executing ReadChar
  493.         dest    - a pointer to a buffer, where the read bytes should be
  494.               stored
  495.  
  496.     Result: number of bytes that are written to dest (between 0 and 2)
  497. */
  498. int ReadChar(char **strptr, char *dest)
  499.  
  500. { char c;
  501.   int i;
  502.  
  503.   switch(c = *((*strptr)++))
  504.   { case '\\':
  505.       switch(c = tolower((int) *((*strptr)++)))
  506.       { case '\n':
  507.       return(0);
  508.     case 'b':
  509.       *dest = '\b';
  510.       break;
  511.     case 'c':
  512.       *dest = '\233';
  513.       break;
  514.     case 'e':
  515.       *dest = '\033';
  516.       break;
  517.     case 'f':
  518.       *dest = '\f';
  519.       break;
  520.     case 'g':
  521.       *dest = '\007';
  522.       break;
  523.     case 'n':
  524.       *dest = '\n';
  525.       break;
  526.     case 'r':
  527.       *dest = '\r';
  528.       break;
  529.     case 't':
  530.       *dest = '\t';
  531.       break;
  532.     case 'v':
  533.       *dest = '\013';
  534.       break;
  535.     case 'x':
  536.       *dest = gethex((int) **strptr);
  537.       (*strptr)++;
  538.       if (((c = **strptr) >= '0'  &&  c <= '9')  ||
  539.           (c >= 'a'  &&  c <= 'f')  ||  (c >= 'A'  &&  c <= 'F'))
  540.       { *dest = (*dest << 4) + gethex((int) c);
  541.         (*strptr)++;
  542.       }
  543.       break;
  544.     case '0':
  545.     case '1':
  546.     case '2':
  547.     case '3':
  548.     case '4':
  549.     case '5':
  550.     case '6':
  551.     case '7':
  552.       *dest = getoctal((int) **strptr);
  553.       (*strptr)++;
  554.       for (i = 0;  i < 2;  i++)
  555.       { if ((c = **strptr) >= '0'  &&  c <= '7')
  556.         { *dest = (*dest << 3) + getoctal((int) c);
  557.           (*strptr)++;
  558.         }
  559.       }
  560.       break;
  561.     case ')':
  562.     case '\\':
  563.       *(dest++) = '\\';
  564.       *dest = c;
  565.       return(2);
  566.     default:
  567.       *dest = c;
  568.       }
  569.       break;
  570.     default:
  571.       *dest = c;
  572.   }
  573.   return(1);
  574. }
  575.  
  576.  
  577.  
  578.  
  579. /*
  580.     This scans the catalog description file.
  581.  
  582.     Inputs: cdfile  - name of the catalog description file
  583.  
  584.     Result: TRUE, if successfull, FALSE otherwise
  585. */
  586. int ScanCDFile(char *cdfile)
  587.  
  588. { FILE *fp;
  589.   struct CDLine *cdline, **cdptr = &FirstCDLine;
  590.   struct CatString *cs, **csptr = &FirstCatString;
  591.   char *line, *newline;
  592.   char *ptr;
  593.   int NextID = 0, len;
  594.   int Result = TRUE;
  595.  
  596.   ScanFile = cdfile;
  597.   ScanLine = 0;
  598.  
  599.   if (!(fp = fopen(cdfile, "r")))
  600.   { ShowError(GetString(msgNoCatalogDescription), cdfile);
  601.   }
  602.  
  603.   /*
  604.       Get the basename
  605.   */
  606.   if ((ptr = strchr(cdfile, ':')))
  607.   { cdfile = ptr+1;
  608.   }
  609.   if ((ptr = strrchr(cdfile, '/')))
  610.   { cdfile = ptr+1;
  611.   }
  612.   if ((ptr = strrchr(cdfile, '.')))
  613.   { len = ptr-cdfile;
  614.   }
  615.   else
  616.   { len = strlen(cdfile);
  617.   }
  618.   if (!(BaseName = malloc(len+1)))
  619.   { MemError();
  620.   }
  621.   strncpy(BaseName, cdfile, len);
  622.   BaseName[len] = '\0';
  623.  
  624.   while(!feof(fp)  &&  (line = newline = ReadLine(fp, TRUE)))
  625.   { if (!(cdline = malloc(sizeof(*cdline))))
  626.     { MemError();
  627.     }
  628.     *cdptr = cdline;
  629.     cdptr = &cdline->Next;
  630.     cdline->Next = NULL;
  631.     cdline->Line = line = AllocString(newline);
  632.     free(newline);
  633.  
  634.     if (*line == ';')
  635.     { continue;
  636.     }
  637.     if (*line == '#')
  638.     { int CheckExtra = TRUE;
  639.  
  640.       if (strnicmp(line+1, "language", 8) == 0)
  641.       { char *ptr;
  642.  
  643.     line += 9;
  644.     OverSpace(&line);
  645.     Language = AllocString(line);
  646.     for (ptr = Language;  *ptr;  ptr++)
  647.     { *ptr = tolower((int) *ptr);
  648.     }
  649.     CheckExtra = FALSE;
  650.       }
  651.       else if (strnicmp(line+1, "version", 7) == 0)
  652.       { CatVersion = strtol(line+8, &line, 10);
  653.       }
  654.       else if (strnicmp(line+1, "lengthbytes", 11) == 0)
  655.       { line += 12;
  656.     if ((LengthBytes = strtol(line, &line, 10))
  657.              > sizeof(long))
  658.     { ShowWarn(GetString(msgNoLengthBytes), sizeof(long));
  659.       LengthBytes = sizeof(long);
  660.     }
  661.       }
  662.       else if (strnicmp(line+1, "basename", 8) == 0)
  663.       { line += 9;
  664.     OverSpace(&line);
  665.     free(BaseName);
  666.     BaseName = AllocString(line);
  667.     CheckExtra = FALSE;
  668.       }
  669.       else
  670.       { ShowWarn(GetString(msgUnknownCDCommand));
  671.     Result = FALSE;
  672.     CheckExtra = FALSE;
  673.       }
  674.       if (CheckExtra)
  675.       { OverSpace(&line);
  676.     if (*line)
  677.     { ShowWarn(GetString(msgExtraCharacters));
  678.       Result = FALSE;
  679.     }
  680.       }
  681.     }
  682.     else
  683.     { char *idstr;
  684.  
  685.       if (*line == ' '  ||  *line == '\t')
  686.       { ShowWarn(GetString(msgUnexpectedBlanks));
  687.     Result = FALSE;
  688.     OverSpace(&line);
  689.       }
  690.  
  691.       idstr = line;
  692.       while ((*line >= 'a'  &&  *line <= 'z')  ||
  693.          (*line >= 'A'  &&  *line <= 'Z')  ||
  694.          (*line >= '0'  &&  *line <= '9')  ||
  695.          *line == '_')
  696.       { ++line;
  697.       }
  698.  
  699.       if (idstr == line)
  700.       { ShowWarn(GetString(msgNoIdentifier));
  701.     Result = FALSE;
  702.       }
  703.       else
  704.       { int found;
  705.  
  706.     if (!(cs = malloc(sizeof(*cs))))
  707.     { MemError();
  708.     }
  709.  
  710.     do
  711.     { struct CatString *scs;
  712.  
  713.       found = TRUE;
  714.       for (scs = FirstCatString;  scs != NULL;  scs = scs->Next)
  715.       { if (scs->ID == NextID)
  716.         { found = FALSE;
  717.           ++NextID;
  718.           break;
  719.         }
  720.       }
  721.     }
  722.     while (!found);
  723.  
  724.     cs->Next = NULL;
  725.     cs->ID = NextID;
  726.     cs->MinLen = 0;
  727.     cs->MaxLen = -1;
  728.     cs->CD_Str = "";
  729.     cs->CT_Str = NULL;
  730.  
  731.     if (!(cs->ID_Str = malloc((line-idstr)+1)))
  732.     { MemError();
  733.     }
  734.     strncpy(cs->ID_Str, idstr, line-idstr);
  735.     cs->ID_Str[line-idstr] = '\0';
  736.  
  737.     OverSpace(&line);
  738.  
  739.     if (*line != '(')
  740.     { ShowWarn(GetString(msgNoLeadingBracket));
  741.       Result = FALSE;
  742.     }
  743.     else
  744.     { char *oldstr;
  745.       struct CatString *scs;
  746.       char bytes[10];
  747.       int bytesread, reallen;
  748.  
  749.       ++line;
  750.       OverSpace(&line);
  751.       if (*line != '/')
  752.       {
  753.         cs->ID = NextID = strtol(line, &line, 10);
  754.         OverSpace(&line);
  755.       }
  756.  
  757.       for(scs = FirstCatString;  scs != NULL;  scs = scs->Next)
  758.       { if (scs->ID == cs->ID)
  759.         { ShowWarn(GetString(msgDoubleID));
  760.           Result = FALSE;
  761.         }
  762.         if (strcmp(cs->ID_Str, scs->ID_Str)  ==  0)
  763.         { ShowWarn(GetString(msgDoubleIdentifier));
  764.           Result = FALSE;
  765.         }
  766.       }
  767.  
  768.       if (*line != '/')
  769.       { ShowWarn(GetString(msgNoMinLen));
  770.         Result = FALSE;
  771.       }
  772.       else
  773.       { ++line;
  774.         OverSpace(&line);
  775.         if (*line != '/')
  776.         { cs->MinLen = strtol(line, &line, 10);
  777.           OverSpace(&line);
  778.         }
  779.         if (*line != '/')
  780.         { ShowWarn(GetString(msgNoMaxLen));
  781.           Result = FALSE;
  782.         }
  783.         else
  784.         { ++line;
  785.           OverSpace(&line);
  786.           if (*line != ')')
  787.           { cs->MaxLen = strtol(line, &line, 10);
  788.         OverSpace(&line);
  789.           }
  790.           if (*line != ')')
  791.           { ShowWarn(GetString(msgNoTrailingBracket));
  792.         Result = FALSE;
  793.           }
  794.           else
  795.           { ++line;
  796.         OverSpace(&line);
  797.         if (*line)
  798.         { ShowWarn(GetString(msgExtraCharacters));
  799.         }
  800.           }
  801.         }
  802.       }
  803.     if (!(newline = ReadLine(fp, FALSE)))
  804.     { ShowWarn(GetString(msgNoString));
  805.       Result = FALSE;
  806.       cs->CD_Str = "";
  807.     }
  808.     else
  809.     { cs->CD_Str = AllocString(newline);
  810.       free(newline);
  811.     }
  812.  
  813.     /*
  814.         Get stringlen
  815.     */
  816.     oldstr = cs->CD_Str;
  817.     reallen = 0;
  818.     while (*oldstr)
  819.     { bytesread = ReadChar(&oldstr, bytes);
  820.       if (bytesread == 2)
  821.       { bytesread--;
  822.       }
  823.       reallen += bytesread;
  824.     }
  825.  
  826.     if (cs->MinLen > 0  &&  reallen < cs->MinLen)
  827.     { ShowWarn(GetString(msgShortString));
  828.     }
  829.     if (cs->MaxLen > 0  &&  reallen > cs->MaxLen)
  830.     { ShowWarn(GetString(msgLongString));
  831.     }
  832.  
  833.     cs->Nr = NumStrings;
  834.  
  835.     *csptr = cs;
  836.     csptr = &cs->Next;
  837.     ++NumStrings;
  838.     }
  839.       }
  840.     }
  841.   }
  842.   fclose(fp);
  843.   return(Result);
  844. }
  845.  
  846.  
  847.  
  848.  
  849. /*
  850.     This scans a catalog translation file.
  851.  
  852.     Inputs: ctfile    - name of the translation file to scan.
  853.  
  854.     Result: TRUE, if successfull, FALSE otherwise.
  855. */
  856. int ScanCTFile(char *ctfile)
  857.  
  858. { FILE *fp;
  859.   char *newline, *line, *idstr, *newidstr, *newstr;
  860.   struct CatString *cs;
  861.   int Result = TRUE;
  862.  
  863.   ScanFile = ctfile;
  864.   ScanLine = 0;
  865.  
  866.   if (!(fp = fopen(ctfile, "r")))
  867.   { ShowError(GetString(msgNoCatalogTranslation), ctfile);
  868.   }
  869.  
  870.  
  871.   while (!feof(fp)  &&  (line = newline = ReadLine(fp, TRUE)))
  872.   { switch(*line)
  873.     { case ';':
  874.     break;
  875.       case '#':
  876.     if (*(++line) != '#')
  877.     { ShowWarn(GetString(msgNoCTCommand));
  878.     }
  879.     ++line;
  880.     OverSpace(&line);
  881.     if (strnicmp(line, "version", 7) == 0)
  882.     { line += 7;
  883.       OverSpace(&line);
  884.       CatVersionString = AllocString(line);
  885.     }
  886.     else if (strnicmp(line, "codeset", 7) == 0)
  887.     { line += 7;
  888.       CodeSet = strtol(line, &line, 10);
  889.       OverSpace(&line);
  890.       if (*line)
  891.       { ShowWarn(GetString(msgExtraCharacters));
  892.       }
  893.     }
  894.     else if (strnicmp(line, "language", 8) == 0)
  895.     { char *ptr;
  896.  
  897.       line += 8;
  898.       OverSpace(&line);
  899.       CatLanguage = AllocString(line);
  900.       for (ptr = CatLanguage;  *ptr;  ptr++)
  901.       { *ptr = tolower((int) *ptr);
  902.       }
  903.     }
  904.     else
  905.     { ShowWarn(GetString(msgUnknownCTCommand));
  906.     }
  907.     break;
  908.       default:
  909.     if (*line == ' '  ||  *line == '\t')
  910.     { ShowWarn(GetString(msgUnexpectedBlanks));
  911.       OverSpace(&line);
  912.     }
  913.     idstr = line;
  914.     while ((*line >= 'a'  &&  *line <= 'z')  ||
  915.            (*line >= 'A'  &&  *line <= 'Z')  ||
  916.            (*line >= '0'  &&  *line <= '9')  ||
  917.            *line == '_')
  918.     { ++line;
  919.     }
  920.     if (idstr == line)
  921.     { ShowWarn(GetString(msgNoIdentifier));
  922.       break;
  923.     }
  924.     if (!(newidstr = malloc(line-idstr+1)))
  925.     { MemError();
  926.     }
  927.     strncpy(newidstr, idstr, line-idstr);
  928.     newidstr[line-idstr] = '\0';
  929.     OverSpace(&line);
  930.     if (*line)
  931.     { ShowWarn(GetString(msgExtraCharacters));
  932.     }
  933.     if ((newstr = ReadLine(fp, FALSE)))
  934.     { for(cs = FirstCatString;  cs != NULL;  cs = cs->Next)
  935.       { if (strcmp(cs->ID_Str, newidstr) == 0)
  936.         { break;
  937.         }
  938.       }
  939.       if (cs == NULL)
  940.       { ShowWarn(GetString(msgUnknownIdentifier), newidstr);
  941.       }
  942.       else
  943.       { char *oldstr;
  944.         char bytes[10];
  945.         int bytesread, reallen;
  946.  
  947.         if (cs->CT_Str)
  948.         { ShowWarn(GetString(msgDoubleIdentifier));
  949.           Result = FALSE;
  950.           free (cs->CT_Str);
  951.         }
  952.         cs->CT_Str = AllocString(newstr);
  953.  
  954.         /*
  955.         Get stringlen
  956.         */
  957.         oldstr = cs->CT_Str;
  958.         reallen = 0;
  959.         while (*oldstr)
  960.         { bytesread = ReadChar(&oldstr, bytes);
  961.           if (bytesread == 2)
  962.           { bytesread--;
  963.           }
  964.           reallen += bytesread;
  965.         }
  966.  
  967.         if (cs->MinLen > 0  &&  reallen < cs->MinLen)
  968.         { ShowWarn(GetString(msgShortString));
  969.         }
  970.         if (cs->MaxLen > 0  &&  reallen > cs->MaxLen)
  971.         { ShowWarn(GetString(msgLongString));
  972.         }
  973.       }
  974.       free(newstr);
  975.     }
  976.     else
  977.     { ShowWarn(GetString(msgNoString));
  978.       cs->CT_Str = "";
  979.     }
  980.     free(newidstr);
  981.     }
  982.     free(newline);
  983.   }
  984.  
  985.   fclose(fp);
  986.   return(Result);
  987. }
  988. /*
  989.     CatPuts prints a string to a catalog. (The string is preceded by a
  990.     long integer containing its length and probably padded up to word
  991.     boundary or longword boundary, depending on the argument padbytes.)
  992.     The arguments countnul should be TRUE if the NUL byte at the end of
  993.     the string should be counted.
  994. */
  995. int CatPuts(FILE *fp, char *str, int padbytes, int countnul)
  996.  
  997. { unsigned long reallen, virtuallen, chunklen;
  998.   int bytesread;
  999.   char *oldstr;
  1000.   char bytes[10];
  1001.  
  1002.   /*
  1003.       Get Length of string.
  1004.   */
  1005.   oldstr = str;
  1006.   reallen = 0;
  1007.   while (*oldstr)
  1008.   { bytesread = ReadChar(&oldstr, bytes);
  1009.     if (bytesread == 2)
  1010.     { bytesread--;
  1011.     }
  1012.     reallen += bytesread;
  1013.   }
  1014.   virtuallen = chunklen = reallen + LengthBytes;
  1015.   if (countnul  ||  chunklen % padbytes == 0)
  1016.   { virtuallen++;
  1017.   }
  1018.  
  1019.   fwrite(&virtuallen, sizeof(virtuallen), 1, fp);
  1020.   if (LengthBytes)
  1021.   { fwrite(((char *) &reallen)+sizeof(reallen)-LengthBytes,
  1022.        LengthBytes, 1, fp);
  1023.   }
  1024.   while(*str)
  1025.   { bytesread = ReadChar(&str, bytes);
  1026.     if (bytesread)
  1027.     { fwrite(bytes+bytesread-1, 1, 1, fp);
  1028.     }
  1029.   }
  1030.  
  1031.   do
  1032.   { putc('\0', fp);
  1033.   }
  1034.   while(++chunklen % padbytes);
  1035.  
  1036.   return((int) chunklen+4);
  1037. }
  1038.  
  1039.  
  1040.  
  1041.  
  1042. /*
  1043.     This creates a catalog.
  1044. */
  1045. void CreateCat(char *CatFile)
  1046.  
  1047. { FILE *fp;
  1048.   int CatLen, HeadLen;
  1049.   struct CatString *cs;
  1050.   int i;
  1051.  
  1052.   if (!CatVersionString)
  1053.   { ShowError(GetString(msgNoCTVersion));
  1054.   }
  1055.   if (!CatLanguage)
  1056.   { ShowError(GetString(msgNoCTLanguage));
  1057.   }
  1058.  
  1059.   if (!(fp = fopen(CatFile, "w")))
  1060.   { ShowError(GetString(msgNoCatalog), CatFile);
  1061.   }
  1062.  
  1063.   fputs("FORM0000CTLGFVER", fp);
  1064.   CatLen = 8 + CatPuts(fp, CatVersionString, 2, TRUE);
  1065.   fputs("LANG", fp);
  1066.   CatLen += 4 + CatPuts(fp, CatLanguage, 2, TRUE);
  1067.   fputs("CSET", fp);
  1068.   i = 32;
  1069.   fwrite(&i, sizeof(i), 1, fp);
  1070.   while(i-- > 0)
  1071.   { putc('\0', fp);
  1072.   }
  1073.   CatLen += 48;
  1074.   fprintf(fp, "STRS0000");
  1075.   HeadLen = CatLen;
  1076.  
  1077.   for (cs = FirstCatString;  cs != NULL;  cs = cs->Next)
  1078.   { if (strcmp(cs->CT_Str, cs->CD_Str))
  1079.     { fwrite(&cs->ID, sizeof(cs->ID), 1, fp);
  1080.       CatLen += 4 + CatPuts(fp, cs->CT_Str, 4, FALSE);
  1081.     }
  1082.   }
  1083.  
  1084.   fseek(fp, 4, SEEK_SET);
  1085.   fwrite(&CatLen, sizeof(CatLen), 1, fp);
  1086.   fseek(fp, HeadLen-4, SEEK_CUR);
  1087.   CatLen -= HeadLen;
  1088.   fwrite(&CatLen, sizeof(CatLen), 1, fp);
  1089.   fclose(fp);
  1090. }
  1091.  
  1092.  
  1093.  
  1094.  
  1095. /*
  1096.     This creates a new catalog translation file.
  1097. */
  1098. void CreateCTFile(char *NewCTFile)
  1099.  
  1100. { FILE *fp;
  1101.   struct CDLine *cd;
  1102.   struct CatString *cs;
  1103.   char *line;
  1104.  
  1105.   if (!(fp = fopen(NewCTFile, "w")))
  1106.   { ShowError(GetString(msgNoNewCTFile));
  1107.   }
  1108.  
  1109.   fprintf(fp, "## version %s\n## language %s\n## codeset %d\n",
  1110.       CatVersionString, CatLanguage, CodeSet);
  1111.  
  1112.   for(cd = FirstCDLine, cs = FirstCatString;
  1113.       cd != NULL;
  1114.       cd = cd->Next)
  1115.   { switch(*cd->Line)
  1116.     { case '#':
  1117.     break;
  1118.       case ';':
  1119.     fprintf(fp, "%s\n", cd->Line);
  1120.     break;
  1121.       default:
  1122.     if(cs)
  1123.     { fprintf(fp, "%s\n", cs->ID_Str);
  1124.       fprintf(fp, "%s\n", cs->CT_Str ? cs->CT_Str : "");
  1125.       putc(';', fp);
  1126.       for (line = cs->CD_Str;  *line;  ++line)
  1127.       { putc((int) *line, fp);
  1128.         if(*line == '\n')
  1129.         { putc(';', fp);
  1130.         }
  1131.       }
  1132.       putc('\n', fp);
  1133.       cs = cs->Next;
  1134.     }
  1135.     }
  1136.   }
  1137.  
  1138.   fclose(fp);
  1139. }
  1140.  
  1141.  
  1142.  
  1143.  
  1144. /*
  1145.     InitCatStringOutput gets called before writing a catalog string as
  1146.     source.
  1147.  
  1148.     Inputs: fp     = file pointer to the output file
  1149.         type = one of   TYPE_C        create C strings
  1150.                 TYPE_ASSEMBLER  create Assembler strings
  1151.                 TYPE_OBERON     create Oberon strings
  1152.                 TYPE_E        create E strings
  1153.                 TYPE_NONE        create simple strings
  1154. */
  1155. int OutputMode = OutputMode_None;
  1156. int OutputType = TYPE_C;
  1157. FILE *OutputFile;
  1158. int OutputLen;
  1159. void InitCatStringOutput(FILE *fp)
  1160.  
  1161. {
  1162.   OutputLen = 0;
  1163.   OutputFile = fp;
  1164.   OutputMode = OutputMode_None;
  1165.   switch(OutputType)
  1166.   { case TYPE_C:
  1167.     case TYPE_OBERON:
  1168.       putc('\"', fp);
  1169.       OutputMode = OutputMode_Ascii;
  1170.       break;
  1171.     case TYPE_E:
  1172.       putc('\'', fp);
  1173.     case TYPE_ASSEMBLER:
  1174.     case TYPE_NONE:
  1175.       break;
  1176.   }
  1177. }
  1178.  
  1179.  
  1180.  
  1181. /*
  1182.     SeparateCatStringOutput gets called to split a catalog into separate
  1183.     lines.
  1184. */
  1185. void SeparateCatStringOutput(void)
  1186.  
  1187. {
  1188.     switch(OutputType)
  1189.     { case TYPE_C:
  1190.     if (!LongStrings)
  1191.     { fputs("\"\\\n\t\"", OutputFile);
  1192.     }
  1193.     break;
  1194.       case TYPE_E:
  1195.     if (!LongStrings)
  1196.     { fputs("\' +\n\t\'", OutputFile);
  1197.     }
  1198.     break;
  1199.       case TYPE_OBERON:
  1200.     if (!LongStrings)
  1201.     { fputs("\"\n\t\"", OutputFile);
  1202.     }
  1203.     break;
  1204.       case TYPE_ASSEMBLER:
  1205.     if (!LongStrings)
  1206.     { if (OutputMode == OutputMode_Ascii)
  1207.       { putc('\'', OutputFile);
  1208.       }
  1209.       putc('\n', OutputFile);
  1210.       OutputMode = OutputMode_None;
  1211.     }
  1212.     break;
  1213.       case TYPE_NONE:
  1214.     break;
  1215.     }
  1216. }
  1217.  
  1218.  
  1219.  
  1220.  
  1221. /*
  1222.     WriteBinChar writes one binary character into the source file
  1223. */
  1224. void WriteBinChar(int c)
  1225.  
  1226. {
  1227.   switch(OutputType)
  1228.   { case TYPE_C:
  1229.     case TYPE_E:
  1230.     case TYPE_OBERON:
  1231.       switch(c)
  1232.       { case '\b':
  1233.       fputs("\\b", OutputFile);
  1234.       break;
  1235.     case '\n':
  1236.       fputs("\\n", OutputFile);
  1237.       break;
  1238.     case '\r':
  1239.       fputs("\\r", OutputFile);
  1240.       break;
  1241.     case '\t':
  1242.       fputs("\\t", OutputFile);
  1243.       break;
  1244.     case '\f':
  1245.       fputs("\\f", OutputFile);
  1246.       break;
  1247.     case '\0':
  1248.       fputs("\\000", OutputFile);
  1249.       break;
  1250.     default:
  1251.       if(OutputType == TYPE_E  &&  c == '\033')
  1252.       { fputs("\\e", OutputFile);
  1253.       }
  1254.       else
  1255.       { fprintf(OutputFile, "\\%c%c%c", ((c >> 6) & 3) + '0',
  1256.             ((c >> 3) & 7) + '0', (c & 7) + '0');
  1257.       }
  1258.       break;
  1259.       }
  1260.       ++OutputLen;
  1261.       OutputMode = OutputMode_Bin;
  1262.       break;
  1263.     case TYPE_ASSEMBLER:
  1264.       switch(OutputMode)
  1265.       { case OutputMode_None:
  1266.       fprintf(OutputFile, "\tdc.b\t$%02x", c & 0xff);
  1267.       break;
  1268.     case OutputMode_Ascii:
  1269.       putc('\'', OutputFile);
  1270.     case OutputMode_Bin:
  1271.       fprintf(OutputFile, ",$%02x", c & 0xff);
  1272.       break;
  1273.       }
  1274.       ++OutputLen;
  1275.       OutputMode = OutputMode_Bin;
  1276.       break;
  1277.     case TYPE_NONE:
  1278.       ShowWarn(GetString(msgNoBinChars));
  1279.       break;
  1280.   }
  1281. }
  1282.  
  1283.  
  1284.  
  1285.  
  1286. /*
  1287.     WriteAsciiChar writes one ascii character into the source file.
  1288. */
  1289. void WriteAsciiChar(int c)
  1290.  
  1291. {
  1292.   switch(OutputType)
  1293.   { case TYPE_C:
  1294.     case TYPE_OBERON:
  1295.       switch(c)
  1296.       { case '\"':
  1297.       fputs("\\\"", OutputFile);
  1298.       break;
  1299.     default:
  1300.       putc(c, OutputFile);
  1301.       break;
  1302.       }
  1303.       ++OutputLen;
  1304.       OutputMode = OutputMode_Ascii;
  1305.       break;
  1306.     case TYPE_E:
  1307.       switch(c)
  1308.       { case '\'':
  1309.       fputs("''", OutputFile);
  1310.       break;
  1311.     default:
  1312.       putc(c, OutputFile);
  1313.       break;
  1314.       }
  1315.       ++OutputLen;
  1316.       OutputMode = OutputMode_Ascii;
  1317.       break;
  1318.     case TYPE_ASSEMBLER:
  1319.       if (c == '\'')
  1320.       { WriteBinChar(c);
  1321.       }
  1322.       else
  1323.       { switch (OutputMode)
  1324.     { case OutputMode_None:
  1325.         fprintf(OutputFile, "\tdc.b\t\'%c", c);
  1326.         break;
  1327.       case OutputMode_Ascii:
  1328.         putc(c, OutputFile);
  1329.         break;
  1330.       case OutputMode_Bin:
  1331.         fprintf(OutputFile, ",\'%c", c);
  1332.         break;
  1333.     }
  1334.     ++OutputLen;
  1335.     OutputMode = OutputMode_Ascii;
  1336.       }
  1337.       break;
  1338.     case TYPE_NONE:
  1339.       putc(c, OutputFile);
  1340.       break;
  1341.   }
  1342. }
  1343.  
  1344.  
  1345.  
  1346.  
  1347. /*
  1348.     TerminateCatStringOutput finishs the output of a catalog string.
  1349. */
  1350. void TerminateCatStringOutput(void)
  1351.  
  1352. {
  1353.   switch(OutputType)
  1354.   { case TYPE_C:
  1355.     case TYPE_OBERON:
  1356.       putc('\"', OutputFile);
  1357.       break;
  1358.     case TYPE_E:
  1359.       putc('\'', OutputFile);
  1360.       break;
  1361.     case TYPE_ASSEMBLER:
  1362.       switch(OutputMode)
  1363.       { case OutputMode_Ascii:
  1364.       putc('\'', OutputFile);
  1365.     case OutputMode_Bin:
  1366.       break;
  1367.     case OutputMode_None:
  1368.       break;
  1369.       }
  1370.     case TYPE_NONE:
  1371.       break;
  1372.   }
  1373. }
  1374.  
  1375.  
  1376.  
  1377.  
  1378. /*
  1379.     This writes a sourcestring.
  1380. */
  1381. void WriteString(FILE *fpout, char *str, long Len)
  1382.  
  1383. { char bytes[10];
  1384.   int bytesread;
  1385.   int needseparate = FALSE;
  1386.  
  1387.   InitCatStringOutput(fpout);
  1388.   if (Len >= 0)
  1389.   { int i;
  1390.  
  1391.     for(i = LengthBytes;  i >= 1;  i--)
  1392.     { WriteBinChar((int) ((char *) &Len)[sizeof(Len)-i]);
  1393.     }
  1394.   }
  1395.  
  1396.   while (*str)
  1397.   { bytesread = ReadChar(&str, bytes);
  1398.     if (bytesread)
  1399.     { unsigned char c;
  1400.  
  1401.       if (needseparate)
  1402.       { SeparateCatStringOutput();
  1403.     needseparate = FALSE;
  1404.       }
  1405.  
  1406.       c = bytes[bytesread-1];
  1407.       if ((c >= 0x20  &&  c < 0x7f)  ||  c >= 0xa0)
  1408.       { WriteAsciiChar((int) c);
  1409.       }
  1410.       else
  1411.       { WriteBinChar((int) c);
  1412.       }
  1413.     }
  1414.     else
  1415.     { needseparate = TRUE;
  1416.     }
  1417.   }
  1418.   TerminateCatStringOutput();
  1419. }
  1420.  
  1421.  
  1422.  
  1423.  
  1424. /*
  1425.     This function creates a copy of a filename, removes an
  1426.     optional ending and pathname components, if desired.
  1427.  
  1428.     Inputs: filename - the filename to copy
  1429.         howto - a set of bits
  1430.             bit 0: 1 = remove ending, 0 = leave it
  1431.             bit 1: 1 = remove pathname, 0 = leave it
  1432.  
  1433.     Result: The copy of the filename
  1434. */
  1435. char *AllocFileName(char *filename, int howto)
  1436.  
  1437. { char *tempstr, *ptr;
  1438.  
  1439.   if (!(tempstr = strdup(filename)))
  1440.   { MemError();
  1441.     exit(10);
  1442.   }
  1443.  
  1444.   /*  Remove pathname components, if desired    */
  1445.   if (howto & 2)
  1446.   { if ((ptr = strchr(tempstr, ':')))
  1447.     { tempstr = ptr+1;
  1448.     }
  1449.     if ((ptr = strrchr(tempstr, '/')))
  1450.     { tempstr = ptr+1;
  1451.     }
  1452.   }
  1453.  
  1454.   /*  Remove ending, if desired.        */
  1455.   if (howto & 1)
  1456.   { if ((ptr = strrchr(tempstr, '.')))
  1457.     { *ptr = '\0';
  1458.     }
  1459.   }
  1460.  
  1461.   return(tempstr);
  1462. }
  1463.  
  1464.  
  1465.  
  1466.  
  1467.  
  1468. /*
  1469.     This function adds a pathname and a filename to a full
  1470.     filename.
  1471.  
  1472.     Inputs: pathname - the leading pathname
  1473.         filename - the filename
  1474.  
  1475.     Result: The new filename
  1476. */
  1477. char *AddFileName(char *pathname, char *filename)
  1478.  
  1479. { char *buffer;
  1480.   int size = strlen(pathname) + strlen(filename) + 2;
  1481.  
  1482.   if (!(buffer = malloc(size)))
  1483.   { MemError();
  1484.     exit(10);
  1485.   }
  1486.  
  1487. #ifdef AMIGA
  1488.   strcpy(buffer, pathname);
  1489.   AddPart(buffer, filename, size);
  1490. #else
  1491.   sprintf(buffer, "%s/%s", pathname, filename);
  1492. #endif
  1493.  
  1494.   return(buffer);
  1495. }
  1496.  
  1497.  
  1498.  
  1499.  
  1500. /*
  1501.     Finally the source creation.
  1502. */
  1503. void CreateSourceFile(char *SourceFile, char *TemplateFile,
  1504.               char *CDFile)
  1505.  
  1506. { FILE *fpin, *fpout;
  1507.   char *line;
  1508.   char *OrigTemplateFile = TemplateFile;
  1509.  
  1510.   /*
  1511.     Open the source file. This may be found in various places
  1512.   */
  1513.   if (!(fpin = fopen(TemplateFile, "r")))
  1514.   { char *sddir;
  1515.  
  1516.     if ((sddir = getenv(FLEXCAT_SDDIR)))
  1517.     { TemplateFile = AddFileName(sddir, OrigTemplateFile);
  1518.       fpin = fopen(TemplateFile, "r");
  1519.     }
  1520.   }
  1521.  
  1522.   if (!fpin)
  1523.   { TemplateFile = AddFileName(DEFAULT_FLEXCAT_SDDIR, OrigTemplateFile);
  1524.     fpin = fopen(TemplateFile, "r");
  1525.   }
  1526.  
  1527.   if (!fpin)
  1528.     {
  1529.       ShowError(GetString(msgNoSourceDescription), OrigTemplateFile);
  1530.       return;
  1531.     }
  1532.  
  1533.   if (!(fpout = fopen(SourceFile, "w")))
  1534.   {
  1535.     ShowError(GetString(msgNoSource), SourceFile);
  1536.     return;
  1537.   }
  1538.  
  1539.   while(!feof(fpin)  &&  (line = ReadLine(fpin, FALSE)))
  1540.   { struct CatString *cs;
  1541.     int NeedRepeat;
  1542.     char bytes[10];
  1543.     int bytesread;
  1544.  
  1545.     cs = FirstCatString;
  1546.     do
  1547.     { char *currentline = line;
  1548.       NeedRepeat = FALSE;
  1549.  
  1550.       if (*currentline == '#'  &&  *(++currentline) == '#')
  1551.       { ++currentline;
  1552.     OverSpace(¤tline);
  1553.     if (strnicmp(currentline, "stringtype", 10) == 0)
  1554.     { currentline += 10;
  1555.       OverSpace(¤tline);
  1556.       if (strnicmp(currentline, "c", 1) == 0)
  1557.       { OutputType = TYPE_C;
  1558.         ++currentline;
  1559.       }
  1560.       else if (strnicmp(currentline, "assembler", 9) == 0)
  1561.       { OutputType = TYPE_ASSEMBLER;
  1562.         currentline += 9;
  1563.       }
  1564.       else if (strnicmp(currentline, "oberon", 6) == 0)
  1565.       { OutputType = TYPE_OBERON;
  1566.         currentline += 6;
  1567.       }
  1568.       else if (strnicmp(currentline, "e", 1)  ==  0)
  1569.       { OutputType = TYPE_E;
  1570.         ++currentline;
  1571.       }
  1572.       else if (strnicmp(currentline, "none", 4)  ==  0)
  1573.       { OutputType = TYPE_NONE;
  1574.         currentline += 4;
  1575.       }
  1576.       else
  1577.       { ShowWarn(GetString(msgUnknownStringType));
  1578.         currentline += strlen(currentline);
  1579.       }
  1580.       OverSpace(¤tline);
  1581.       if (*currentline)
  1582.       { ShowWarn(GetString(msgExtraCharacters));
  1583.       }
  1584.       continue;
  1585.     }
  1586.     else if (strnicmp(currentline, "shortstrings", 12) == 0)
  1587.     { currentline += 12;
  1588.       LongStrings = FALSE;
  1589.       OverSpace(¤tline);
  1590.       if (*currentline)
  1591.       { ShowWarn(GetString(msgExtraCharacters));
  1592.       }
  1593.       continue;
  1594.     }
  1595.       }
  1596.  
  1597.       currentline = line;
  1598.       while(*currentline)
  1599.       { bytesread = ReadChar(¤tline, bytes);
  1600.     if (bytesread)
  1601.     { if (*bytes == '%')
  1602.       { switch(*(currentline++))
  1603.         { case 'b':
  1604.         fputs(BaseName, fpout);
  1605.         break;
  1606.           case 'n':
  1607.         fprintf(fpout, "%d", NumStrings);
  1608.         break;
  1609.           case 'v':
  1610.         fprintf(fpout, "%d", CatVersion);
  1611.         break;
  1612.           case 'l':
  1613.         WriteString(fpout, Language, -1);
  1614.         break;
  1615.           case 'f':
  1616.         { char *tempstr;
  1617.  
  1618.           tempstr = AllocFileName(CDFile, *currentline++ - '0');
  1619.           fputs(tempstr, fpout);
  1620.         }
  1621.         break;
  1622.           case 'o':
  1623.         { char *tempstr;
  1624.  
  1625.           tempstr = AllocFileName(SourceFile, *currentline++ - '0');
  1626.           fputs(tempstr, fpout);
  1627.         }
  1628.         break;
  1629.           case 'i':
  1630.         NeedRepeat = TRUE;
  1631.         if (cs) fputs(cs->ID_Str, fpout);
  1632.         break;
  1633.           case 'd':
  1634.         NeedRepeat = TRUE;
  1635.         if (cs) fprintf(fpout, "%d", cs->ID);
  1636.         break;
  1637.           case 'e':
  1638.         NeedRepeat = TRUE;
  1639.         if (cs) fprintf(fpout, "%d", cs->Nr);
  1640.         break;
  1641.           case 's':
  1642.         NeedRepeat = TRUE;
  1643.         if (cs)
  1644.         { char *idstr;
  1645.           unsigned long len = 0;
  1646.  
  1647.           if (LengthBytes)
  1648.           { idstr = cs->CD_Str;
  1649.             while(*idstr)
  1650.             { bytesread = ReadChar(&idstr, bytes);
  1651.               if (bytesread)
  1652.               { ++len;
  1653.               }
  1654.             }
  1655.           }
  1656.           WriteString(fpout, cs->CD_Str, LengthBytes ? len : -1);
  1657.         }
  1658.         break;
  1659.           case '(':
  1660.         NeedRepeat = TRUE;
  1661.         while(*currentline  &&  *currentline != ')')
  1662.         { bytesread = ReadChar(¤tline, bytes);
  1663.           if (bytesread  &&  cs  &&  cs->Next)
  1664.           { putc((int) bytes[bytesread-1], fpout);
  1665.           }
  1666.         }
  1667.         if (!*currentline)
  1668.         { ShowWarn(GetString(msgNoTerminateBracket));
  1669.         }
  1670.         else
  1671.         { ++currentline;
  1672.         }
  1673.         break;
  1674.           default:
  1675.         { int c = *currentline++;
  1676.  
  1677.           putc(c, fpout);
  1678.         }
  1679.         }
  1680.       }
  1681.       else
  1682.       { putc((int) bytes[bytesread-1], fpout);
  1683.       }
  1684.     }
  1685.       }
  1686.       putc('\n', fpout);
  1687.     }
  1688.     while(NeedRepeat  &&  cs  &&  (cs = cs->Next));
  1689.  
  1690.     free(line);
  1691.   }
  1692.  
  1693.   fclose(fpin);
  1694.   fclose(fpout);
  1695. }
  1696.  
  1697.  
  1698.  
  1699.  
  1700. /*
  1701.     The Usage function describes the programs calling syntax.
  1702. */
  1703. void Usage(void)
  1704.  
  1705. {
  1706.   fputs((char *) GetString(msgUsage), stderr);
  1707.   fprintf(stderr, "\n%s\n", VERSION+6);
  1708.   Cleanup(5);
  1709. }
  1710.  
  1711. /*
  1712.     Finally the main function. Does nothing special except for scanning
  1713.     the arguments.
  1714. */
  1715. void main (int argc, char *argv [])
  1716.  
  1717. { char *cdfile, *ctfile, *newctfile, *catalog;
  1718.   char *source, *template;
  1719.   int i;
  1720.  
  1721.   if (argc == 0)    /*  Aztec's entry point for workbench programs  */
  1722.   { exit(5);
  1723.   }
  1724.  
  1725. #ifdef AMIGA
  1726.   if (!(UtilityBase = OpenLibrary((STRPTR) "utility.library", 37)))
  1727.   { fprintf(stderr, "Need utility.library, V37.\n");
  1728.     exit(20);
  1729.   }
  1730. #endif
  1731.  
  1732.   cdfile = ctfile = newctfile = catalog = NULL;
  1733.  
  1734.   if (argc == 1)
  1735.   { Usage();
  1736.   }
  1737.  
  1738.   for (i = 1;  i < argc;  i++)
  1739.   { if (strnicmp (argv[i], "catalog=", 8) == 0)
  1740.     { catalog = argv[i] + 8;
  1741.     }
  1742.     else if (stricmp (argv[i], "catalog") == 0)
  1743.     { if (i+1 == argc)
  1744.       { Usage();
  1745.       }
  1746.       catalog = argv[++i];
  1747.     }
  1748.     else if (strnicmp (argv[i], "newctfile=", 10) == 0)
  1749.     { newctfile = argv[i] + 10;
  1750.     }
  1751.     else if (strnicmp (argv[i], "newctfile", 10) == 0)
  1752.     { if (i+1 == argc)
  1753.       { Usage();
  1754.       }
  1755.       newctfile = argv[++i];
  1756.     }
  1757.     else if (cdfile == NULL)
  1758.     { if (stricmp(argv[i], "?") == 0  ||  stricmp(argv[i], "-h") == 0  ||
  1759.       stricmp(argv[i], "help") == 0)
  1760.       { Usage();
  1761.       }
  1762.       if (!ScanCDFile(cdfile = argv[i]))
  1763.       { Cleanup(10);
  1764.       }
  1765.     }
  1766.     else if (strchr(argv[i], '='))
  1767.     { source = AllocString(argv[i]);
  1768.       *(template = strchr(source, '=')) = '\0';
  1769.       ++template;
  1770.  
  1771.       CreateSourceFile(source, template, cdfile);
  1772.     }
  1773.     else
  1774.     { if (ctfile)
  1775.       { Usage();
  1776.       }
  1777.       ctfile = argv[i];
  1778.     }
  1779.   }
  1780.  
  1781.   if (ctfile)
  1782.   { if(!ScanCTFile(ctfile))
  1783.     { Cleanup(10);
  1784.     }
  1785.   }
  1786.   if (catalog)
  1787.   { if (!ctfile)
  1788.     { fprintf(stderr, (char *) GetString(msgNoCTArgument));
  1789.       Usage();
  1790.     }
  1791.     CreateCat(catalog);
  1792.   }
  1793.   if (newctfile)
  1794.   { CreateCTFile(newctfile);
  1795.   }
  1796.  
  1797.   Cleanup(0);
  1798. }
  1799.  
  1800.  
  1801.  
  1802.  
  1803. /*
  1804.     Dice's entry point for workbench programs
  1805. */
  1806. void wbmain(struct WBStartup *wbmsg)
  1807.  
  1808. { exit(5);
  1809. }
  1810.