]> git.sur5r.net Git - cc65/blob - src/grc/grc.c
Error message for negative array sizes.
[cc65] / src / grc / grc.c
1
2 /*
3     GEOS resource compiler
4
5     by Maciej 'YTM/Elysium' Witkowiak
6
7     see GEOSLib documentation for license info
8
9 */
10
11 /*
12  - make it work, then do it better
13  - more or less comments? it was hard to code, should be even harder to
14    understand =D
15  - add loadable icons feature (binary - 63 bytes)
16 */
17
18 /* - err, maybe free allocated memory, huh? (who cares, it's just a little prog...)
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <time.h>
27
28 #include "grc.h"
29
30 /* common stuff */
31 //#include "cmdline.h"
32 #include "fname.h"
33 #include "abend.h"
34 #include "chartype.h"
35
36 void VLIRLinker(int argc, char *argv[]) {
37 FILE *outCVT, *input;
38 unsigned char buffer[BLOODY_BIG_BUFFER];
39 unsigned char vlirtabt[127];
40 unsigned char vlirtabs[127];
41 int i,j;
42 int bytes;
43 int blocks,rest;
44
45         i=2;
46
47         /* check if we know enough */
48
49         if (argc<4)
50             AbEnd("too few arguments, required [out] [cvthead] [vlir0] ...\n");
51
52         /* first open and copy CVT header */
53
54         outCVT = fopen(argv[i],"wb+");
55         if (outCVT==NULL)
56             AbEnd("can't open output:%s\n",strerror(errno));
57
58         ++i;
59         input = fopen(argv[i],"rb");
60         if (input==NULL)
61             AbEnd("can't open input:%s\n",strerror(errno));
62
63         bytes = fread(buffer,1,BLOODY_BIG_BUFFER,input);
64         fclose(input);
65         if (bytes!=508)
66                 AbEnd("%s is not a cvt header\n",argv[i]);
67
68         fwrite(buffer,1,bytes,outCVT);
69
70         /* now put 254 bytes of VLIR table, to update later */
71
72         /* clear out things */
73         memset(buffer,0,254);
74         fwrite(buffer,1,254,outCVT);
75         for (j=0;j!=126;j++) {
76                 vlirtabt[j]=0;
77                 vlirtabs[j]=0;
78         }
79
80         /* now read all VLIR chains, align to 254 bytes */
81
82         ++i;
83         j=0;
84         while (i!=argc) {
85                 if (strcmp(argv[i],"blank")==0) {
86                         vlirtabt[j]=0; vlirtabs[j]=0; }
87                 else if (strcmp(argv[i],"noexist")==0) {
88                         vlirtabt[j]=0; vlirtabs[j]=0xff; }
89                 else {
90                         memset(buffer,0,BLOODY_BIG_BUFFER);
91                         input = fopen(argv[i],"rb");
92                         if (input==NULL)
93                                 AbEnd("couldn't open %s:%s\n",argv[i],strerror(errno));
94                         bytes = fread(buffer,1,BLOODY_BIG_BUFFER,input);
95                         fclose(input);
96                         if (bytes==0)
97                                 AbEnd("couldn't read %s:%s\n",argv[i],strerror(errno));
98                         blocks = bytes / 254;
99                         rest = bytes % 254 + 2;
100                         if (rest>255) rest=255;
101                         vlirtabt[j]=blocks+1; vlirtabs[j]=rest;
102                         fwrite(buffer,1,(blocks+1)*254,outCVT);
103                 }
104                 ++j;
105                 ++i;
106         }
107
108         /* now rewind and update VLIR table */
109
110         fflush(outCVT);
111         fseek(outCVT,508,SEEK_SET);
112         for (i=0;i!=127;i++) {
113                 fputc(vlirtabt[i],outCVT);
114                 fputc(vlirtabs[i],outCVT);
115         }
116         fclose(outCVT);
117         exit(EXIT_SUCCESS);
118 }
119
120 void printCHeader (void) {
121
122     fprintf(outputCFile, "\n/*\n\tThis file was generated by GEOS Resource Compiler\n"
123             "\n\tDO NOT EDIT! Any changes will be lost!\n"
124             "\n\tEdit proper resource file instead\n"
125             "\n*/\n\n");
126 }
127
128 void printSHeader (void) {
129
130     fprintf(outputSFile, "\n;\n;\tThis file was generated by GEOS Resource Compiler\n;"
131             "\n;\tDO NOT EDIT! Any changes will be lost!\n;"
132             "\n;\tEdit proper resource file instead\n;"
133             "\n;\n\n");
134 }
135
136 void printVHeader (void) {
137     fprintf(outputVFile, "\n#\n#\tThis file was generated by GEOS Resource Compiler\n#"
138             "\n#\tDO NOT EDIT! Any changes will be lost!\n#"
139             "\n#\tEdit proper resource file instead\n#"
140             "\n#\tLook at end of this file to find commandline that must be used\n"
141               "#\tto invoke ld65 and grc (as VLIR linker)\n#"
142             "\n#\n\n");
143 }
144
145 void openCFile (void) {
146     if ((CFnum==0) && (forceFlag==0)) {
147         /* test if file exists already and no forcing*/
148             if ((outputCFile = fopen (outputCName,"r"))!=0)
149                 AbEnd("file %s already exists, aborting\n", outputCName);
150         }
151     if ((outputCFile = fopen (outputCName,outputCMode))==0)
152             AbEnd("can't open file %s for writing: %s\n",outputCName,strerror (errno));
153     if (CFnum==0) {
154                 outputCMode[0]='a';
155                 printCHeader();
156                 CFnum++;
157         }
158 }
159
160 void openSFile (void) {
161     if ((SFnum==0) && (forceFlag==0)) {
162         /* test if file exists already and no forcing*/
163             if ((outputSFile = fopen (outputSName,"r"))!=0)
164                 AbEnd("file %s already exists, aborting\n", outputSName);
165         }
166     if ((outputSFile = fopen (outputSName,outputSMode))==0)
167              AbEnd("can't open file %s for writing: %s\n",outputSName,strerror (errno));
168     if (SFnum==0) {
169                 outputSMode[0]='a';
170                 printSHeader();
171                 SFnum++;
172         }
173 }
174
175 void openVFile (void) {
176     if ((VFnum==0) && (forceFlag==0)) {
177         /* test if file exists already and no forcing*/
178             if ((outputVFile = fopen (outputVName,"r"))!=0)
179                 AbEnd("file %s already exists, aborting\n", outputVName);
180         }
181     if ((outputVFile = fopen (outputVName,outputVMode))==0)
182              AbEnd("can't open file %s for writting: %s\n",outputVName,strerror (errno));
183     if (VFnum==0) {
184                 outputVMode[0]='a';
185                 printVHeader();
186                 VFnum++;
187         }
188 }
189
190 void printUsage (void) {
191     fprintf(stderr, "Usage: %s [options] file\n"
192             "Options:\n"
193             "\t-h, -?\t\tthis help\n"
194             "\t-f\t\tforce writting files\n"
195             "\t-o name\t\tname C output file\n"
196             "\t-s name\t\tname asm output file\n"
197             "\t-l name\t\tname ld65 config output file (for vlir)\n"
198             "Or as VLIR linker: %s -vlir output.cvt header [vlir0] ... [blank] ... [vlir_n]\n",
199             ProgName,ProgName);
200 }
201
202 int findToken (const char **tokenTbl, const char *token) {
203 /* takes as input table of tokens and token, returns position in table or -1 if not found */
204 int a=0;
205
206     while (strlen(tokenTbl[a])!=0) {
207                 if (strcmp(tokenTbl[a],token)==0) break;
208                 a++;
209     }
210     if (strlen(tokenTbl[a])==0) a=-1;
211     return a;
212 }
213
214 char *nextPhrase() {
215     return strtok(NULL, "\"");
216     }
217
218 char *nextWord() {
219     return strtok(NULL, " ");
220     }
221
222 void setLen (char *name, unsigned len) {
223     if (strlen(name)>len)
224         name[len]='\0';
225 }
226
227 void fillOut (char *name, int len, char *filler) {
228 int a;
229     setLen (name, len);
230     fprintf(outputSFile, ".byte \"%s\"\n\t\t", name);
231     a = len - strlen(name);
232     if (a!=0) {
233         fprintf(outputSFile, ".byte %s", filler);
234         while (--a!=0) fprintf(outputSFile, ", %s", filler);
235         fprintf(outputSFile, "\n\t\t");
236         }
237 }
238
239 //char *bintos(unsigned char a, char *out) {
240 char *bintos(unsigned char a, char out[7]) {
241 int i=0;
242     for (;i<8;i++) {
243     out[7-i] = ((a & 1)==0) ? '0' : '1';
244     a = a >> 1; };
245     out[i]='\0';
246 return out;
247 }
248
249 int getNameSize (const char *word) {
250 /* count length of a word using BSW 9 font table */
251 int a=0, i=0;
252
253     while (word[i]!='\0') {
254         a+=(BSWTab[word[i]-31] - BSWTab[word[i]-32]); i++; }
255
256     return a;
257 }
258
259 void DoMenu (void) {
260
261 int a, size, tmpsize, item=0;
262 char *token;
263 char namebuff[255]="";
264 struct menu myMenu;
265 struct menuitem *curItem, *newItem;
266
267     openCFile();
268
269     myMenu.name=nextWord();
270     myMenu.left=atoi(nextWord());
271     myMenu.top=atoi(nextWord());
272     myMenu.type=nextWord();
273
274     if (strcmp(nextWord(),"{")!=0) {
275         AbEnd ("menu '%s' description has no opening bracket!\n", myMenu.name);
276         };
277     curItem=malloc(sizeof(struct menuitem));
278     myMenu.item=curItem;
279     do {
280         token = nextWord();
281         if (strcmp(token,"}")==0) break;
282         if (token[strlen(token)-1]!='"') {
283             strcpy (namebuff, token);
284             do {
285                 token = nextWord();
286                 strcat (namebuff, " ");
287                 strcat (namebuff, token);
288                 } while (token[strlen(token)-1]!='"');
289             token = malloc(strlen(namebuff));
290             strcpy (token, namebuff);
291         }
292         curItem->name=token;
293         curItem->type=nextWord();
294         curItem->target=nextWord();
295         newItem=malloc(sizeof(struct menuitem));
296         curItem->next=newItem;
297         curItem=newItem;
298         item++;
299         } while (strcmp(token,"}")!=0);
300     if (item==0) AbEnd ("menu '%s' has 0 items!\n", myMenu.name);
301     if (item>31) AbEnd ("menu '%s' has too many items!\n", myMenu.name);
302
303     curItem->next=NULL;
304
305     /* Count menu sizes */
306
307     size=0;
308     curItem=myMenu.item;
309     if (strstr(myMenu.type,"HORIZONTAL")!=NULL) {
310         /* menu is HORIZONTAL, ysize=15, sum xsize of all items +~8?*/
311             myMenu.bot=myMenu.top+15;
312             for (a=0;a!=item;a++) {
313                 size+=getNameSize(curItem->name);
314                 curItem=curItem->next;
315                 };
316         } else {
317         /* menu is VERTICAL, ysize=item*15, count largest xsize of all items +~8? */
318             myMenu.bot=myMenu.top+(14*item)-1;
319             for (a=0;a!=item;a++) {
320                 tmpsize=getNameSize(curItem->name);
321                 size = (size > tmpsize) ? size : tmpsize;
322                 curItem=curItem->next;
323                 };
324         };
325     myMenu.right=myMenu.left+size-1;
326
327     curItem=myMenu.item;
328     for (a=0;a!=item;a++) {
329         /* print prototype only if MENU_ACTION or DYN_SUB_MENU are present in type */
330         if ((strstr(curItem->type, "MENU_ACTION")!=NULL) || (strstr(curItem->type, "DYN_SUB_MENU")!=NULL))
331             fprintf(outputCFile, "void %s (void);\n", curItem->target);
332         curItem=curItem->next;
333         }
334
335     fprintf(outputCFile, "\nconst void %s = {\n\t(char)%i, (char)%i,\n\t(int)%i, (int)%i,\n\t"
336             "(char)(%i | %s),\n", myMenu.name, myMenu.top, myMenu.bot, myMenu.left,
337             myMenu.right, item, myMenu.type);
338
339     curItem=myMenu.item;
340     for (a=0;a!=item;a++) {
341         fprintf(outputCFile, "\t%s, (char)%s, (int)", curItem->name, curItem->type);
342         if ((strstr(curItem->type, "SUB_MENU")!=NULL) && (strstr(curItem->type, "DYN_SUB_MENU")==NULL))
343             fprintf(outputCFile, "&");
344         fprintf(outputCFile, "%s,\n", curItem->target);
345         curItem=curItem->next;
346         }
347
348     fprintf(outputCFile, "\t};\n\n");
349
350     if (fclose (outputCFile)!=0)
351         AbEnd("error closing %s: %s\n",outputCName,strerror (errno));
352 }
353
354 void DoHeader (void) {
355
356 time_t t;
357 struct tm *my_tm;
358
359 struct appheader myHead;
360 char *token;
361 char i1[9], i2[9], i3[9];
362 int a, b;
363
364     openSFile();
365
366     token = nextWord();
367
368     a = findToken (hdrFTypes, token);
369
370     if (a>1)
371            AbEnd("filetype '%s' is not supported yet\n", token);
372
373     switch (a) {
374         case 0: myHead.geostype = 6; break;
375         case 1: myHead.geostype = 14; break;
376         }
377
378     myHead.dosname = nextPhrase();
379     nextPhrase();
380     myHead.classname = nextPhrase();
381     nextPhrase();
382     myHead.version = nextPhrase();
383
384     /* put default values into myHead here */
385     myHead.author = "cc65";
386     myHead.info = "Program compiled with cc65 and GEOSLib.";
387     myHead.dostype = 128+3;
388     myHead.structure = 0;
389     myHead.mode = 0;
390
391     t = time(NULL);
392     my_tm = localtime (&t);
393
394     myHead.year = my_tm->tm_year;
395     myHead.month = my_tm->tm_mon+1;
396     myHead.day = my_tm->tm_mday;
397     myHead.hour = my_tm->tm_hour;
398     myHead.min = my_tm->tm_min;
399
400     if (strcmp(nextWord(),"{")!=0)
401                 AbEnd ("header '%s' has no opening bracket!\n", myHead.dosname);
402
403     do {
404         token=nextWord();
405         if (strcmp(token, "}")==0) break;
406         switch (a = findToken (hdrFields, token)) {
407                 case -1:
408                     AbEnd ("unknown field '%s' in header '%s'\n", token, myHead.dosname);
409                     break;
410                 case 0: /* author */
411                     myHead.author = nextPhrase(); break;
412                 case 1: /* info */
413                     myHead.info = nextPhrase(); break;
414                 case 2: /* date */
415                     myHead.year = atoi(nextWord());
416                     myHead.month = atoi(nextWord());
417                     myHead.day = atoi(nextWord());
418                     myHead.hour = atoi(nextWord());
419                     myHead.min = atoi(nextWord());
420                     break;
421                 case 3: /* dostype */
422                     switch (b = findToken (hdrDOSTp, nextWord())) {
423                         case -1:
424                             AbEnd ("unknown dostype in header '%s'\n", myHead.dosname);
425                             break;
426                         default:
427                             myHead.dostype = b/2 + 128 + 1;
428                             break;
429                     }
430                     break;
431                 case 4: /* mode */
432                     switch (b = findToken (hdrModes, nextWord())) {
433                         case -1:
434                             AbEnd ("unknown mode in header '%s'\n", myHead.dosname);
435                         case 0:
436                             myHead.mode = 0x40; break;
437                         case 1:
438                             myHead.mode = 0x00; break;
439                         case 2:
440                             myHead.mode = 0xc0; break;
441                         case 3:
442                             myHead.mode = 0x80; break;
443                     }
444                     break;
445                 case 5: /* structure */
446                     switch (b = findToken(hdrStructTp, nextWord())) {
447                         case -1:
448                             AbEnd ("unknown structure type in header '%s'\n", myHead.dosname);
449                         case 0:
450                         case 1:
451                             myHead.structure = 0; break;
452                         case 2:
453                         case 3:
454                             myHead.structure = 1; break;
455                     }
456                     break;
457         }
458
459     } while (strcmp(token, "}")!=0);
460
461     /* OK, all information is gathered, do flushout */
462
463     fprintf(outputSFile,
464          "\t\t\t.segment \"HEADER\"\n\n\t\t.byte %i\n\t\t.word 0\n\t\t", myHead.dostype);
465
466     fillOut(myHead.dosname,16,"$a0");
467
468     fprintf(outputSFile,
469         ".word 0\n\t\t.byte %i\n\t\t.byte %i\n\t\t.byte %i, %i, %i, %i, %i\n\n\t\t"
470         ".word 0\n\t\t.byte \"PRG formatted GEOS file V1.0\"\n\n\t\t.res $c4\n\n\t\t"
471         ".byte 3, 21, 63 | $80\n\t\t",
472         myHead.structure, myHead.geostype, myHead.year, myHead.month, myHead.day,
473         myHead.hour, myHead.min);
474
475     for (a=0;a!=63;a=a+3) {
476         fprintf(outputSFile,
477              ".byte %%%s, %%%s, %%%s\n\t\t",
478              bintos(icon1[a], i1), bintos(icon1[a+1], i2), bintos(icon1[a+2], i3)); };
479
480     fprintf(outputSFile,
481             "\n\t\t.byte %i, %i, %i\n\t\t.word $0400, $0400-1, $0400\n\n\t\t",
482             myHead.dostype, myHead.geostype, myHead.structure);
483
484     fillOut(myHead.classname,12,"$20");
485
486     fillOut(myHead.version,4,"0");
487
488     fprintf(outputSFile,
489             ".byte 0, 0, 0\n\t\t.byte %i\n\n\t\t", myHead.mode);
490
491     setLen(myHead.author,62);
492     fprintf(outputSFile,
493             ".byte \"%s\"\n\t\t.byte 0\n\t\t.res (63-%i)\n\n\t\t",
494             myHead.author, (int) (strlen(myHead.author)+1));
495
496     setLen(myHead.info, 95);
497     fprintf(outputSFile,
498             ".byte \"%s\"\n\t\t.byte 0\n\t\t.res (96-%i)\n\n",
499             myHead.info, (int) (strlen(myHead.info)+1));
500
501     if (fclose (outputSFile)!=0)
502                 AbEnd("error closing %s: %s\n",outputSName,strerror (errno));
503
504 }
505
506 void DoVLIR (void) {
507
508 char *token;
509 char *headname;
510 int i,numchains,vlirbase;
511 struct vlirentry {
512     char *chainname;
513     int exist;
514 };
515
516 struct vlirentry vlirtable[127];
517
518     openVFile();
519
520     headname = nextWord();
521
522     vlirbase = strtol(nextWord(),NULL,0);
523
524     if (strcmp(nextWord(),"{")!=0)
525                 AbEnd ("VLIR description has no opening bracket!\n");
526
527     numchains=0;
528
529     do {
530         token=nextWord();
531         if (strcmp(token, "}")==0) break;
532         numchains++;
533         if (numchains>127) {
534             AbEnd("Too many VLIR chains!\n");
535         }
536         vlirtable[numchains].chainname=token;
537
538         /* for first chain - name header */
539         if (numchains==1) {
540           fprintf(outputVFile,"MEMORY {\n\tHEADER: start = $204, size = 508, file = \"%s\";\n"
541             "\tVLIR0: start = $0400, size = $5C00, file = \"%s\";\n",headname,token);
542         } else {
543         /* for all other - segment */
544           /* ignore non-existing segments */
545           vlirtable[numchains].exist=1;
546           if ( (strcmp(token,"blank")==0) || (strcmp(token,"noexist")==0) ) {
547             vlirtable[numchains].exist=0;
548             fprintf(outputVFile,"#");
549           }
550           fprintf(outputVFile,"\tVLIR%i: start = $%x, size = $%x, file = \"%s\";\n",
551             numchains-1,vlirbase,0x5c00-vlirbase,token);
552         }
553
554     } while (strcmp(token, "}")!=0);
555     fprintf(outputVFile,"}\n\n");
556
557     if (numchains==0) {
558         AbEnd("There must be at least one VLIR chain.\n");
559     };
560
561     /* now put segments info */
562     fprintf(outputVFile,"SEGMENTS {\n\tHEADER: load = HEADER, type = ro;\n"
563         "\tCODE: load = VLIR0, type = ro;\n"
564         "\tRODATA: load = VLIR0, type = ro;\n"
565         "\tDATA: load = VLIR0, type = rw;\n"
566         "\tBSS: load = VLIR0, type = bss, define = yes;\n\n");
567
568     for (i=2;i<=numchains;i++) {
569         if (vlirtable[i].exist==0) {
570             fprintf(outputVFile,"#");
571         }
572         fprintf(outputVFile,"\tVLIR%i: load = VLIR%i, type = rw, define = yes;\n",i-1,i-1);
573     }
574     fprintf(outputVFile,"}\n");
575
576     /* now put usage info */
577     fprintf(outputVFile,"\n# ld65 -o output.cvt -C %s file1.o file2.o ...",outputVName);
578     fprintf(outputVFile,"\n# grc -vlir outputname %s",headname);
579     for (i=1;i<=numchains;i++) {
580         fprintf(outputVFile," %s",vlirtable[i].chainname);
581     }
582     fprintf(outputVFile,"\n");
583
584     if (fclose (outputVFile)!=0)
585                 AbEnd("error closing %s: %s\n",outputVName,strerror (errno));
586 }
587
588 char *filterInput (FILE *F, char *tbl) {
589 /* loads file into buffer filtering it out */
590 int a, prevchar=-1, i=0, bracket=0, quote=1;
591
592     while (1) {
593         a = getc(F);
594         if ((a=='\n')||(a=='\015')) a = ' ';
595         if (a==',') a = ' ';
596         if (a=='\042') quote=!quote;
597         if (quote) {
598             if ((a=='{')||(a=='(')) bracket++;
599             if ((a=='}')||(a==')')) bracket--;
600         }
601         if (a==EOF) { tbl[i]='\0'; realloc(tbl, i+1); break; };
602         if (IsSpace(a)) {
603             if ((prevchar!=' ') && (prevchar!=-1)) { tbl[i++]=' '; prevchar=' '; }
604         } else {
605             if (a==';' && quote) { do { a = getc (F); } while (a!='\n'); fseek(F,-1,SEEK_CUR); }
606                 else {
607                     tbl[i++]=a; prevchar=a; }
608         }
609     }
610
611     if (bracket!=0) AbEnd("there are unclosed brackets!\n");
612
613     return tbl;
614 }
615
616 void processFile (const char *filename) {
617
618 FILE *F;
619
620 char *str;
621 char *token;
622
623 int head=0;     /* number of processed HEADER sections */
624 int vlir=0;     /* number of processed VLIR sections */
625
626     if ((F = fopen (filename,"r"))==0)
627                 AbEnd("can't open file %s for reading: %s\n",filename,strerror (errno));
628
629     str=filterInput(F, malloc(BLOODY_BIG_BUFFER));
630
631     token = strtok (str," ");
632
633     do {
634         if (str!=NULL) {
635             switch (findToken (mainToken, token)) {
636
637             case 0: DoMenu(); break;
638             case 1:
639                 if (++head!=1) {
640                         AbEnd ("more than one HEADER section, aborting.\n");
641                     } else {
642                         DoHeader();
643                     }
644                 break;
645             case 2: break;      /* icon not implemented yet */
646             case 3: break;      /* dialog not implemented yet */
647             case 4:
648                 if (++vlir!=1) {
649                         AbEnd ("more than one VLIR section, aborting.\n");
650                 } else {
651                         DoVLIR();
652                 }
653                 break;
654             default: AbEnd ("unknown section %s.\n",token); break;
655             }
656         }
657         token = nextWord();
658     } while (token!=NULL);
659 }
660
661 int main(int argc, char *argv[]) {
662
663 int ffile=0, i=1;
664
665     ProgName = argv[0];
666     while (i < argc) {
667         const char *arg = argv[i];
668         if (arg[0] == '-') {
669             switch (arg[1]) {
670                 case 'f':
671                     forceFlag=1;
672                     break;
673                 case 'o':
674                     outputCName=argv[++i];
675                     break;
676                 case 's':
677                     outputSName=argv[++i];
678                     break;
679                 case 'l':
680                     outputVName=argv[++i];
681                     break;
682                 case 'h':
683                 case '?':
684                     printUsage();
685                     exit (EXIT_SUCCESS);
686                     break;
687                 case 'v':
688                     if (strcmp(arg,"-vlir")==0) {
689                         VLIRLinker(argc,argv);
690                         exit (EXIT_SUCCESS);
691                         break;
692                     } else {
693                         AbEnd("unknown option %s\n",arg);
694                         break;
695                     }
696                 default: AbEnd("unknown option %s\n",arg);
697             }
698         } else {
699             ffile++;
700
701                 if (outputCName==NULL)
702                         outputCName = MakeFilename(arg,".h");
703                 if (outputSName==NULL)
704                         outputSName = MakeFilename(arg,".s");
705                 if (outputVName==NULL)
706                         outputVName = MakeFilename(arg,".cfg");
707
708             processFile(arg);
709
710             }
711         i++;
712         }
713     if (ffile==0) AbEnd("no input file\n");
714
715     return EXIT_SUCCESS;
716 }