]> git.sur5r.net Git - cc65/blob - src/grc65/main.c
1cdf8e56a03b3c07b0c904790b4ba641b7a56889
[cc65] / src / grc65 / main.c
1 /* GEOS resource compiler
2
3    by Maciej 'YTM/Elysium' Witkowiak
4
5    see GEOSLib documentation for license info
6 */
7
8 /* - make it work, then do it better
9    - more or less comments? it was hard to code, should be even harder to
10      understand =D
11    - add loadable icons feature (binary - 63 bytes)
12 */
13
14 /* - err, maybe free allocated memory, huh? (who cares, it's just a little prog...)
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <time.h>
23
24 /* common stuff */
25 #include "abend.h"
26 #include "cmdline.h"
27 #include "fname.h"
28 #include "chartype.h"
29 #include "target.h"
30 #include "version.h"
31 #include "xmalloc.h"
32
33 /* I hope that no one will be able to create a .grc bigger than this... */
34 #define BLOODY_BIG_BUFFER 65000
35
36
37
38 struct menuitem {
39     char *name;
40     char *type;
41     char *target;
42     struct menuitem *next;
43 };
44
45 struct menu {
46     char *name;
47     int top, left;
48     int bot, right;
49     char *type;
50     struct menuitem *item;
51 };
52
53 struct appheader {
54     int year, month, day, hour, min;
55     int mode;
56     int dostype;
57     int geostype;
58     int structure;
59     char *dosname;
60     char *classname;
61     char *version;
62     char *author;
63     char *info;
64     char *icon;
65 };
66
67 const char *mainToken[] = {"MENU", "HEADER", "ICON", "DIALOG", "VLIR", ""};
68
69 const char *hdrFTypes[] = {"APPLICATION", "AUTO_EXEC", "DESK_ACC", "ASSEMBLY",
70                            "DISK_DEVICE", "PRINTER", "SYSTEM", ""};
71
72 const char *hdrFields[] = {"author", "info", "date", "dostype", "mode", "structure", "icon", ""};
73
74 const char *hdrDOSTp[] = {"seq", "SEQ", "prg", "PRG", "usr", "USR", ""};
75
76 const char *hdrStructTp[] = {"seq", "SEQ", "vlir", "VLIR", ""};
77
78 const char *hdrModes[] = {"any", "40only", "80only", "c64only", ""};
79
80 const int BSWTab[] = {0, 0x005, 0x007, 0x00b, 0x011, 0x017, 0x01d, 0x023,
81     0x025, 0x029, 0x02d, 0x033, 0x039, 0x03c, 0x041, 0x043, 0x04a, 0x04f,
82     0x052, 0x056, 0x05a, 0x05f, 0x063, 0x068, 0x06d, 0x072, 0x077, 0x079,
83     0x07c, 0x080, 0x084, 0x088, 0x08e, 0x094, 0x09a, 0x09f, 0x0a4, 0x0a9,
84     0x0ad, 0x0b1, 0x0b6, 0x0bc, 0x0be, 0x0c2, 0x0c8, 0x0cc, 0x0d4, 0x0da,
85     0x0e0, 0x0e5, 0x0eb, 0x0f0, 0x0f5, 0x0f9, 0x0fe, 0x104, 0x10c, 0x112,
86     0x118, 0x11e, 0x121, 0x129, 0x12c, 0x132, 0x13a, 0x13e, 0x143, 0x148,
87     0x14d, 0x152, 0x157, 0x15a, 0x15f, 0x164, 0x166, 0x168, 0x16d, 0x16f,
88     0x177, 0x17c, 0x182, 0x187, 0x18c, 0x18f, 0x193, 0x196, 0x19b, 0x1a1,
89     0x1a9, 0x1af, 0x1b4, 0x1ba, 0x1be, 0x1c0, 0x1c4, 0x1ca, 0x1d2, 0x1dd};
90
91 const unsigned char icon1[] = {255, 255, 255, 128,   0,   1, 128,   0,   1,
92                                128,   0,   1, 128,   0,   1, 128,   0,   1,
93                                128,   0,   1, 128,   0,   1, 128,   0,   1,
94                                128,   0,   1, 128,   0,   1, 128,   0,   1,
95                                128,   0,   1, 128,   0,   1, 128,   0,   1,
96                                128,   0,   1, 128,   0,   1, 128,   0,   1,
97                                128,   0,   1, 128,   0,   1, 255, 255, 255};
98
99 const char *outputCName = NULL;
100 const char *outputSName = NULL;
101 FILE *outputCFile, *outputSFile;
102 int CFnum = 0, SFnum = 0;
103 int apple = 0;
104 char outputCMode[2] = "w";
105 char outputSMode[2] = "w";
106
107
108 static void Usage (void)
109 {
110     printf ("Usage: %s [options] file\n"
111             "Short options:\n"
112             "  -V\t\t\tPrint the version number\n"
113             "  -h\t\t\tHelp (this text)\n"
114             "  -o name\t\tName the C output file\n"
115             "  -s name\t\tName the asm output file\n"
116             "  -t sys\t\tSet the target system\n"
117             "\n"
118             "Long options:\n"
119             "  --help\t\tHelp (this text)\n"
120             "  --target sys\t\tSet the target system\n"
121             "  --version\t\tPrint the version number\n",
122             ProgName);
123 }
124
125
126 static void OptHelp (const char* Opt attribute ((unused)),
127                      const char* Arg attribute ((unused)))
128 /* Print usage information and exit */
129 {
130     Usage ();
131     exit (EXIT_SUCCESS);
132 }
133
134
135 static void OptTarget (const char* Opt attribute ((unused)), const char* Arg)
136 /* Set the target system */
137 {
138     switch (FindTarget (Arg)) {
139
140         case TGT_GEOS_CBM:
141             apple = 0;
142             break;
143
144         case TGT_GEOS_APPLE:
145             apple = 1;
146             break;
147
148         case TGT_UNKNOWN:
149             AbEnd ("Unknown target system `%s'", Arg);
150             break;
151
152         default:
153             /* Target is known but unsupported */
154             AbEnd ("Unsupported target system `%s'", Arg);
155             break;
156
157     }
158 }
159
160
161 static void OptVersion (const char* Opt attribute ((unused)),
162                         const char* Arg attribute ((unused)))
163 /* Print the assembler version */
164 {
165     fprintf (stderr,
166              "grc65 V%s - (C) Copyright, Maciej 'YTM/Elysium' Witkowiak\n",
167              GetVersionAsString ());
168 }
169
170
171
172 static void printCHeader (void)
173 {
174     fprintf (outputCFile,
175         "//\n"
176         "//\tThis file was generated by the GEOS Resource Compiler\n"
177         "//\n"
178         "//\tDO NOT EDIT! Any changes will be lost!\n"
179         "//\n"
180         "//\tEdit proper resource file instead.\n"
181         "//\n\n");
182 }
183
184
185 static void printSHeader (void)
186 {
187     fprintf (outputSFile,
188         ";\n"
189         ";\tThis file was generated by the GEOS Resource Compiler\n"
190         ";\n"
191         ";\tDO NOT EDIT! Any changes will be lost!\n"
192         ";\n"
193         ";\tEdit proper resource file instead.\n"
194         ";\n\n");
195 }
196
197
198 static void openCFile (void)
199 {
200     if ((outputCFile = fopen (outputCName,outputCMode)) == 0) {
201         AbEnd ("Can't open file %s for writing: %s", outputCName, strerror (errno));
202     }
203
204     if (CFnum == 0) {
205         outputCMode[0] = 'a';
206         printCHeader ();
207         CFnum++;
208     }
209 }
210
211
212 static void openSFile (void)
213 {
214     if ((outputSFile = fopen (outputSName, outputSMode)) == 0) {
215         AbEnd ("Can't open file %s for writing: %s", outputSName, strerror (errno));
216     }
217
218     if (SFnum == 0) {
219         outputSMode[0] = 'a';
220         printSHeader ();
221         SFnum++;
222     }
223 }
224
225
226 static int findToken (const char **tokenTbl, const char *token)
227 {
228     /* takes as input table of tokens and token, returns position in table or -1 if not found */
229     int a = 0;
230
231     while (strlen (tokenTbl[a]) != 0) {
232         if (strcmp (tokenTbl[a], token) == 0) break;
233         a++;
234     }
235
236     if (strlen (tokenTbl[a]) == 0) a = -1;
237     return a;
238 }
239
240
241 static char *nextPhrase (void)
242 {
243     return strtok (NULL, "\"");
244 }
245
246
247 static char *nextWord (void)
248 {
249     return strtok (NULL, " ");
250 }
251
252
253 static void setLen (char *name, unsigned len)
254 {
255     if (strlen (name) > len)
256         name[len] = '\0';
257 }
258
259
260 static void fillOut (char *name, int len, char *filler)
261 {
262     int a;
263
264     setLen(name, len);
265     fprintf (outputSFile, "\t.byte \"%s\"\n", name);
266
267     a = strlen (name);
268     if (a < len) {
269         fprintf (outputSFile, "\t.res  (%i - %i), %s\n", len, a, filler);
270     }
271 }
272
273
274 static char *bintos (unsigned char a, char out[7])
275 {
276     int i=0;
277
278     for (; i < 8; i++) {
279         out[7 - i] = ((a & 1) == 0) ? '0' : '1';
280         a = a >> 1;
281     }
282     out[i] = '\0';
283
284     return out;
285 }
286
287
288 static int getNameSize (const char *word)
289 {
290     /* count length of a word using BSW 9 font table */
291     int a = 0, i = 0;
292
293     while (word[i] != '\0') {
294         a += (BSWTab[word[i] - 31] - BSWTab[word[i] - 32]);
295         i++;
296     }
297
298     return a;
299 }
300
301
302 static void DoMenu (void)
303 {
304     int a, size, tmpsize, item = 0;
305     char *token;
306     char namebuff[255] = "";
307     struct menu myMenu;
308     struct menuitem *curItem, *newItem;
309
310     openCFile ();
311
312     myMenu.name = nextWord ();
313     myMenu.left = atoi (nextWord ());
314     myMenu.top = atoi (nextWord ());
315     myMenu.type = nextWord ();
316
317     if (strcmp(nextWord (), "{") != 0) {
318         AbEnd ("Menu '%s' description has no opening bracket!", myMenu.name);
319     }
320     curItem = xmalloc (sizeof(struct menuitem));
321     myMenu.item = curItem;
322     do {
323         token = nextWord ();
324         if (strcmp (token, "}") == 0) break;
325         if (token[strlen(token) - 1] != '"') {
326             strcpy (namebuff, token);
327             do {
328                 token = nextWord ();
329                 strcat (namebuff, " ");
330                 strcat (namebuff, token);
331             } while (token[strlen(token) - 1] != '"');
332             token = xmalloc (strlen (namebuff));
333             strcpy (token, namebuff);
334         }
335         curItem->name = token;
336         curItem->type = nextWord ();
337         curItem->target = nextWord ();
338         newItem = xmalloc (sizeof(struct menuitem));
339         curItem->next = newItem;
340         curItem = newItem;
341         item++;
342         } while (strcmp (token, "}") != 0);
343     if (item == 0) AbEnd ("Menu '%s' has 0 items!", myMenu.name);
344     if (item > 31) AbEnd ("Menu '%s' has too many items!", myMenu.name);
345
346     curItem->next = NULL;
347
348     /* count menu sizes */
349     size = 0;
350     curItem = myMenu.item;
351     if (strstr (myMenu.type, "HORIZONTAL") != NULL) {
352         /* menu is HORIZONTAL, ysize=15, sum xsize of all items +~8?*/
353         myMenu.bot = myMenu.top + 15;
354         for (a = 0; a != item; a++) {
355             size += getNameSize (curItem->name);
356             curItem = curItem->next;
357         }
358     } else {
359         /* menu is VERTICAL, ysize=item*15, count largest xsize of all items +~8? */
360         myMenu.bot = myMenu.top + (14 * item);
361         for (a = 0; a != item; a++) {
362             tmpsize = getNameSize (curItem->name);
363             size = (size > tmpsize) ? size : tmpsize;
364             curItem = curItem->next;
365         }
366     }
367     myMenu.right = myMenu.left + size - 1;
368
369     curItem = myMenu.item;
370     for (a = 0; a != item; a++) {
371         /* print prototype only if MENU_ACTION or DYN_SUB_MENU are present in type */
372         if ((strstr (curItem->type, "MENU_ACTION") != NULL) || (strstr (curItem->type, "DYN_SUB_MENU") != NULL)) {
373             fprintf (outputCFile,
374                 "void %s (void);\n",
375                 curItem->target);
376         }
377         curItem=curItem->next;
378     }
379
380     fprintf (outputCFile,
381         "\n"
382         "const void %s = {\n"
383         "\t(char)%i, (char)%i,\n"
384         "\t(int)%i, (int)%i,\n"
385         "\t(char)(%i | %s),\n",
386         myMenu.name, myMenu.top, myMenu.bot, myMenu.left, myMenu.right, item, myMenu.type);
387
388     curItem = myMenu.item;
389     for (a = 0; a != item; a++) {
390         fprintf (outputCFile,
391             "\t%s, (char)%s, (int)",
392             curItem->name, curItem->type);
393         if ((strstr (curItem->type, "SUB_MENU") != NULL) && (strstr (curItem->type, "DYN_SUB_MENU") == NULL))
394             fprintf (outputCFile,
395                 "&");
396         fprintf (outputCFile,
397             "%s,\n",
398             curItem->target);
399         curItem = curItem->next;
400     }
401
402     fprintf (outputCFile,
403         "};\n\n");
404
405     if (fclose (outputCFile) != 0)
406         AbEnd ("Error closing %s: %s", outputCName, strerror (errno));
407 }
408
409
410 static void DoHeader (void)
411 {
412     time_t t;
413     struct tm *my_tm;
414
415     struct appheader myHead;
416     char *token;
417     char i1[9], i2[9], i3[9];
418     int a, b;
419
420     openSFile ();
421
422     token = nextWord ();
423
424     a = findToken(hdrFTypes, token);
425
426     if (apple == 1) {
427         switch (a) {
428             case 0:
429                 myHead.geostype = 0x82;
430                 break;
431             default:
432                 AbEnd ("Filetype '%s' is not supported yet", token);
433         }
434     } else {
435         switch (a) {
436             case 0:
437                 myHead.geostype = 6;
438                 break;
439             case 1:
440                 myHead.geostype = 14;
441                 break;
442             default:
443                 AbEnd ("Filetype '%s' is not supported yet", token);
444         }
445     }
446
447     myHead.dosname = nextPhrase ();
448     nextPhrase ();
449     myHead.classname = nextPhrase ();
450     nextPhrase ();
451     myHead.version = nextPhrase ();
452
453     /* put default values into myHead here */
454     myHead.author = "cc65";
455     myHead.info = "Program compiled with cc65 and GEOSLib.";
456     myHead.dostype = 128;
457     if (apple == 0) myHead.dostype += 3;
458     myHead.structure = 0;
459     myHead.mode = 0;
460     myHead.icon = NULL;
461
462     t = time (NULL);
463     my_tm = localtime (&t);
464
465     myHead.year = my_tm->tm_year % 100;
466     myHead.month = my_tm->tm_mon + 1;
467     myHead.day = my_tm->tm_mday;
468     myHead.hour = my_tm->tm_hour;
469     myHead.min = my_tm->tm_min;
470
471     if (strcmp (nextWord (), "{") != 0) {
472         AbEnd ("Header '%s' has no opening bracket!", myHead.dosname);
473     }
474
475     do {
476         token = nextWord ();
477         if (strcmp (token, "}") == 0) break;
478         switch (a = findToken (hdrFields, token)) {
479             case -1:
480                 AbEnd ("Unknown field '%s' in header '%s'", token, myHead.dosname);
481                 break;
482             case 0: /* author */
483                 myHead.author = nextPhrase ();
484                 break;
485             case 1: /* info */
486                 myHead.info = nextPhrase ();
487                 break;
488             case 2: /* date */
489                 myHead.year = atoi (nextWord ());
490                 myHead.month = atoi (nextWord ());
491                 myHead.day = atoi (nextWord ());
492                 myHead.hour = atoi (nextWord ());
493                 myHead.min = atoi (nextWord ());
494                 break;
495             case 3: /* dostype */
496                 switch (b = findToken (hdrDOSTp, nextWord ())) {
497                     case -1:
498                         AbEnd ("Unknown dostype in header '%s'", myHead.dosname);
499                         break;
500                     default:
501                         if (apple == 0) myHead.dostype = b / 2 + 128 + 1;
502                         break;
503                 }
504                 break;
505             case 4: /* mode */
506                 switch (b = findToken (hdrModes, nextWord ())) {
507                     case -1:
508                         AbEnd ("Unknown mode in header '%s'", myHead.dosname);
509                     case 0:
510                         if (apple == 0) myHead.mode = 0x40;
511                         break;
512                     case 1:
513                         if (apple == 0) myHead.mode = 0x00;
514                         break;
515                     case 2:
516                         if (apple == 0) myHead.mode = 0xc0;
517                         break;
518                     case 3:
519                         if (apple == 0) myHead.mode = 0x80;
520                         break;
521                 }
522                 break;
523             case 5: /* structure */
524                 switch (b = findToken (hdrStructTp, nextWord ())) {
525                     case -1:
526                         AbEnd ("unknown structure type in header '%s'", myHead.dosname);
527                     case 0:
528                     case 1:
529                         myHead.structure = 0;
530                         break;
531                     case 2:
532                     case 3:
533                         myHead.structure = 1;
534                         break;
535                 }
536                 break;
537             case 6: /* icon */
538                 myHead.icon = nextPhrase ();
539                 break;
540         }
541
542     } while (strcmp (token, "}") != 0);
543
544     /* OK, all information is gathered, do flushout */
545
546     fprintf (outputSFile,
547         "\n"
548         "\t\t.segment \"DIRENTRY\"\n\n");
549
550     if (apple == 1) {
551
552         fprintf (outputSFile,
553             "\t.import __VLIR0_START__, __VLIR0_LAST__, __BSS_SIZE__\n\n"
554             "\t.byte %i << 4 | %u\n",
555             myHead.structure + 2, (unsigned)strlen (myHead.dosname));
556
557         fillOut (myHead.dosname, 15, "0");
558
559         fprintf (outputSFile,
560             "\t.byte $%02x\n"
561             "\t.word 0\n"
562             "\t.word 0\n"
563             "\t.word __VLIR0_LAST__ - __VLIR0_START__ - __BSS_SIZE__\n"
564             "\t.byte 0\n"
565             "\t.word %i << 9 | %i << 5 | %i, %i << 8 | %i\n"
566             "\t.byte 0\n"
567             "\t.byte 0\n"
568             "\t.byte 0\n"
569             "\t.word 0\n"
570             "\t.word %i << 9 | %i << 5 | %i, %i << 8 | %i\n"
571             "\t.word 0\n",
572             myHead.geostype,
573             myHead.year, myHead.month, myHead.day, myHead.hour, myHead.min,
574             myHead.year, myHead.month, myHead.day, myHead.hour, myHead.min);
575
576     } else {
577
578         fprintf (outputSFile,
579             "\t.byte %i\n"
580             "\t.word 0\n",
581             myHead.dostype);
582
583         fillOut (myHead.dosname, 16, "$a0");
584
585         fprintf (outputSFile,
586             "\t.word 0\n"
587             "\t.byte %i\n"
588             "\t.byte %i\n"
589             "\t.byte %i, %i, %i, %i, %i\n\n"
590             "\t.word 0\n"
591             "\t.byte \"PRG formatted GEOS file V1.0\"\n\n",
592             myHead.structure, myHead.geostype,
593             myHead.year, myHead.month, myHead.day, myHead.hour, myHead.min);
594     }
595
596     fprintf (outputSFile,
597         "\n"
598         "\t\t.segment \"FILEINFO\"\n\n"
599         "\t.import __VLIR0_START__, __STARTUP_RUN__\n\n"
600         "\t.byte 3, 21, 63 | $80\n");
601
602     if (myHead.icon != NULL) {
603         fprintf (outputSFile,
604             "\t.incbin \"%s\", 0, 63\n",
605             myHead.icon);
606     } else {
607         for (a = 0; a != 63; a = a + 3) {
608             fprintf (outputSFile,
609                 "\t.byte %%%s, %%%s, %%%s\n",
610                 bintos (icon1[a], i1), bintos (icon1[a+1], i2), bintos (icon1[a+2], i3));
611         }
612     }
613
614     fprintf (outputSFile,
615         "\t.byte %i, %i, %i\n"
616         "\t.word __VLIR0_START__, __VLIR0_START__ - 1, __STARTUP_RUN__\n\n",
617         myHead.dostype, myHead.geostype, myHead.structure);
618
619     fillOut (myHead.classname, 12, "$20");
620
621     fillOut (myHead.version, 4, "0");
622
623     fprintf (outputSFile,
624         "\t.byte 0, 0, 0\n"
625         "\t.byte %i\n\n",
626         myHead.mode);
627
628     setLen (myHead.author, 62);
629     fprintf (outputSFile,
630         "\t.byte \"%s\"\n"
631         "\t.byte 0\n"
632         "\t.res  (63 - %i)\n\n",
633         myHead.author, (int)(strlen (myHead.author) + 1));
634
635     setLen (myHead.info, 95);
636     fprintf (outputSFile,
637         "\t.byte \"%s\"\n"
638         "\t.byte 0\n\n",
639         myHead.info);
640
641     if (fclose (outputSFile) != 0)
642         AbEnd ("Error closing %s: %s", outputSName, strerror (errno));
643 }
644
645
646 static void DoVLIR (void)
647 {
648     char *token;
649     int record, lastrecord;
650     int vlirsize, vlirtable[127];
651
652     openSFile ();
653
654     vlirsize = strtol (nextWord (), NULL, 0);
655
656     if (strcmp(nextWord (), "{") != 0) {
657         AbEnd ("VLIR description has no opening bracket!");
658     }
659
660     lastrecord = -1;
661     memset (vlirtable, 0, sizeof(vlirtable));
662
663     do {
664         token = nextWord ();
665         if (strcmp (token, "}") == 0) break;
666
667         record = atoi (token);
668         if (record < 0 || record > 126) {
669             AbEnd ("VLIR record %i is out of range 0-126.", record);
670         }
671         if (vlirtable[record] == 1) {
672             AbEnd ("VLIR record %i is defined twice.", record);
673         }
674
675         vlirtable[record] = 1;
676         if (record > lastrecord) lastrecord = record;
677     } while (strcmp (token, "}") != 0);
678
679     if (lastrecord == -1) {
680         AbEnd ("There must be at least one VLIR record.");
681     }
682
683     /* always include record 0 */
684     vlirtable[0] = 1;
685
686     /* OK, all information is gathered, do flushout */
687
688     fprintf (outputSFile,
689         "\n"
690         "\t\t.segment \"RECORDS\"\n\n"
691         "\t.export __OVERLAYSIZE__ : absolute = $%04x\n\n",
692         vlirsize);
693
694     for (record = 0; record <= lastrecord; record++) {
695         if (vlirtable[record] == 1) {
696             fprintf (outputSFile,
697                 "\t.import __VLIR%i_START__, __VLIR%i_LAST__\n",
698                 record, record);
699         }
700     }
701     fprintf (outputSFile,
702         "\n");
703
704     for (record = 0; record <= lastrecord; record++) {
705         if (vlirtable[record] == 1) {
706             fprintf (outputSFile,
707                 "\t.byte .lobyte ((__VLIR%i_LAST__ - __VLIR%i_START__ - 1) /    254) + 1\n"
708                 "\t.byte .lobyte ((__VLIR%i_LAST__ - __VLIR%i_START__ - 1) .MOD 254) + 2\n",
709                 record, record, record, record);
710         } else {
711             fprintf (outputSFile,
712                 "\t.byte $00\n"
713                 "\t.byte $FF\n");
714         }
715     }
716     fprintf (outputSFile,
717         "\n");
718
719     if (fclose (outputSFile) != 0)
720         AbEnd ("Error closing %s: %s", outputSName, strerror (errno));
721
722     openCFile ();
723
724     fprintf (outputCFile,
725         "extern void _OVERLAYADDR__;\n"
726         "extern void _OVERLAYSIZE__;\n\n"
727         "#define OVERLAY_ADDR (char*)   &_OVERLAYADDR__\n"
728         "#define OVERLAY_SIZE (unsigned)&_OVERLAYSIZE__\n\n");
729
730     if (fclose (outputCFile) != 0)
731         AbEnd ("Error closing %s: %s", outputCName, strerror (errno));
732 }
733
734
735 static char *filterInput (FILE *F, char *tbl)
736 {
737     /* loads file into buffer filtering it out */
738     int a, prevchar = -1, i = 0, bracket = 0, quote = 1;
739
740     while (1) {
741         a = getc(F);
742         if ((a == '\n') || (a == '\015')) a = ' ';
743         if (a == ',' && quote) a = ' ';
744         if (a == '\042') quote =! quote;
745         if (quote) {
746             if ((a == '{') || (a == '(')) bracket++;
747             if ((a == '}') || (a == ')')) bracket--;
748         }
749         if (a == EOF) {
750             tbl[i] = '\0';
751             xrealloc (tbl, i + 1);
752             break;
753         }
754         if (IsSpace (a)) {
755             if ((prevchar != ' ') && (prevchar != -1)) {
756                 tbl[i++] = ' ';
757                 prevchar = ' ';
758             }
759         } else {
760             if (a == ';' && quote) {
761                 do {
762                     a = getc (F);
763                 } while (a != '\n');
764                 fseek (F, -1, SEEK_CUR);
765             } else {
766                 tbl[i++] = a;
767                 prevchar = a;
768             }
769         }
770     }
771
772     if (bracket != 0) AbEnd ("There are unclosed brackets!");
773
774     return tbl;
775 }
776
777
778 static void processFile (const char *filename)
779 {
780     FILE *F;
781
782     char *str;
783     char *token;
784
785     int head = 0; /* number of processed HEADER sections */
786     int vlir = 0; /* number of processed VLIR sections */
787
788     if ((F = fopen (filename, "r")) == 0) {
789         AbEnd ("Can't open file %s for reading: %s", filename, strerror (errno));
790     }
791
792     str = filterInput (F, xmalloc (BLOODY_BIG_BUFFER));
793
794     token = strtok (str, " ");
795
796     do {
797         if (str != NULL) {
798             switch (findToken (mainToken, token)) {
799                 case 0:
800                     DoMenu ();
801                     break;
802                 case 1:
803                     if (++head != 1) {
804                         AbEnd ("More than one HEADER section, aborting.");
805                     } else {
806                         DoHeader ();
807                     }
808                     break;
809                 case 2: break; /* icon not implemented yet */
810                 case 3: break; /* dialog not implemented yet */
811                 case 4:
812                     if (++vlir != 1) {
813                         AbEnd ("More than one VLIR section, aborting.");
814                     } else {
815                         DoVLIR ();
816                     }
817                     break;
818                 default:
819                     AbEnd ("Unknown section %s.", token);
820                     break;
821             }
822         }
823         token = nextWord ();
824     } while (token != NULL);
825 }
826
827
828 int main (int argc, char *argv[])
829 {
830     /* Program long options */
831     static const LongOpt OptTab[] = {
832         { "--help",    0, OptHelp},
833         { "--target",  1, OptTarget},
834         { "--version", 0, OptVersion},
835     };
836
837     unsigned ffile = 0;
838     unsigned I;
839
840     /* Initialize the cmdline module */
841     InitCmdLine (&argc, &argv, "grc65");
842
843     /* Check the parameters */
844     I = 1;
845     while (I < ArgCount) {
846
847         /* Get the argument */
848         const char* Arg = ArgVec [I];
849
850         /* Check for an option */
851         if (Arg[0] == '-') {
852             switch (Arg[1]) {
853
854                 case '-':
855                     LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
856                     break;
857
858                 case 'o':
859                     outputCName = GetArg (&I, 2);
860                     break;
861
862                 case 's':
863                     outputSName = GetArg (&I, 2);
864                     break;
865
866                 case 't':
867                     OptTarget (Arg, GetArg (&I, 2));
868                     break;
869
870                 case 'h':
871                 case '?':
872                     OptHelp (Arg, 0);
873                     break;
874
875                 case 'V':
876                     OptVersion (Arg, 0);
877                     break;
878
879                 default:
880                     UnknownOption (Arg);
881             }
882
883         } else {
884             ffile++;
885
886             if (outputCName == NULL) outputCName = MakeFilename (Arg, ".h");
887             if (outputSName == NULL) outputSName = MakeFilename (Arg, ".s");
888
889             processFile (Arg);
890         }
891
892         /* Next argument */
893         ++I;
894     }
895
896     if (ffile == 0) AbEnd ("No input file");
897
898     return EXIT_SUCCESS;
899 }