1 /* vasm.c  main module for vasm */
2 /* (c) in 2002-2018 by Volker Barthelmann */
3 
4 #include <stdlib.h>
5 #include <stdio.h>
6 
7 #include "vasm.h"
8 #include "stabs.h"
9 
10 #define _VER "vasm 1.8c"
11 char *copyright = _VER " (c) in 2002-2018 Volker Barthelmann";
12 #ifdef AMIGA
13 static const char *_ver = "$VER: " _VER " " __AMIGADATE__ "\r\n";
14 #endif
15 
16 #define SRCREADINC (64*1024)  /* extend buffer in these steps when reading */
17 
18 /* The resolver will run another pass over the current section as long as any
19    label location or atom size has changed. It gives up at MAXPASSES, which
20    hopefully will never happen.
21    During the first FASTOPTPHASE passes all instructions of a section will be
22    optimized at the same time. After that the resolver enters a safe mode,
23    where only a single instruction per pass is changed. */
24 #define MAXPASSES 1000
25 #define FASTOPTPHASE 200
26 
27 source *cur_src=NULL;
28 char *filename,*debug_filename;
29 section *current_section;
30 char *inname,*outname,*listname;
31 taddr inst_alignment;
32 int secname_attr;
33 int unnamed_sections;
34 int ignore_multinc;
35 int nocase;
36 int no_symbols;
37 int pic_check;
38 int done,final_pass,debug;
39 int exec_out;
40 int chklabels;
41 int warn_unalloc_ini_dat;
42 int listena,listformfeed=1,listlinesperpage=40,listnosyms;
43 listing *first_listing,*last_listing,*cur_listing;
44 struct stabdef *first_nlist,*last_nlist;
45 char *output_format="test";
46 unsigned long long taddrmask;
47 char emptystr[]="";
48 char vasmsym_name[]="__VASM";
49 
50 static int produce_listing;
51 static char **listtitles;
52 static int *listtitlelines;
53 static int listtitlecnt;
54 
55 static FILE *outfile=NULL;
56 
57 static int depend,depend_all;
58 #define DEPEND_LIST     1
59 #define DEPEND_MAKE     2
60 struct deplist {
61   struct deplist *next;
62   char *filename;
63 };
64 static struct deplist *first_depend,*last_depend;
65 
66 static section *first_section,*last_section;
67 #if NOT_NEEDED
68 static section *prev_sec=NULL,*prev_org=NULL;
69 #endif
70 
71 /* MNEMOHTABSIZE should be defined by cpu module */
72 #ifndef MNEMOHTABSIZE
73 #define MNEMOHTABSIZE 0x1000
74 #endif
75 hashtable *mnemohash;
76 
77 static int verbose=1,auto_import=1;
78 static struct include_path *first_incpath=NULL;
79 static struct include_path *first_source=NULL;
80 
81 static char *output_copyright;
82 static void (*write_object)(FILE *,section *,symbol *);
83 static int (*output_args)(char *);
84 
85 
leave(void)86 void leave(void)
87 {
88   section *sec;
89   symbol *sym;
90 
91   if(outfile){
92     fclose(outfile);
93     if (errors)
94       remove(outname);
95   }
96 
97   if(debug){
98     fprintf(stdout,"Sections:\n");
99     for(sec=first_section;sec;sec=sec->next)
100       print_section(stdout,sec);
101 
102     fprintf(stdout,"Symbols:\n");
103     for(sym=first_symbol;sym;sym=sym->next){
104       print_symbol(stdout,sym);
105       fprintf(stdout,"\n");
106     }
107   }
108 
109   if(errors)
110     exit(EXIT_FAILURE);
111   else
112     exit(EXIT_SUCCESS);
113 }
114 
115 /* Convert all labels from an offset-section into absolute expressions. */
convert_offset_labels(void)116 static void convert_offset_labels(void)
117 {
118   symbol *sym;
119 
120   for (sym=first_symbol; sym; sym=sym->next) {
121     if (sym->type==LABSYM && sym->sec!=NULL && (sym->sec->flags&UNALLOCATED)) {
122       sym->type = EXPRESSION;
123       sym->expr = number_expr(sym->pc);
124       sym->sec = NULL;
125     }
126   }
127 }
128 
129 /* Removes all unallocated (offset) sections from the list. */
remove_unalloc_sects(void)130 static void remove_unalloc_sects(void)
131 {
132   section *prev,*sec;
133 
134   for (sec=first_section,prev=NULL; sec; sec=sec->next) {
135     if (sec->flags&UNALLOCATED) {
136       if (prev)
137         prev->next = sec->next;
138       else
139         first_section = sec->next;
140     }
141     else
142       prev = sec;
143   }
144 }
145 
146 /* append a new stabs (nlist) symbol/debugging definition */
new_stabdef(aoutnlist * nlist,section * sec)147 static void new_stabdef(aoutnlist *nlist,section *sec)
148 {
149   struct stabdef *new = mymalloc(sizeof(struct stabdef));
150 
151   new->next = NULL;
152   new->name.ptr = nlist->name;
153   new->type = nlist->type;
154   new->other = nlist->other;
155   new->desc = nlist->desc;
156   new->base = NULL;
157   if (nlist->value == NULL)
158     new->value = 0;
159   else if (!eval_expr(nlist->value,&new->value,sec,sec->pc)) {
160     int btype = find_base(nlist->value,&new->base,sec,sec->pc);
161     if (btype==BASE_ILLEGAL || btype==BASE_PCREL) {
162        new->base = NULL;
163        general_error(38);  /* illegal relocation */
164     }
165     else if (new->base != NULL)
166       new->base->flags |= REFERENCED;
167   }
168   if (last_nlist)
169     last_nlist = last_nlist->next = new;
170   else
171     first_nlist = last_nlist = new;
172 }
173 
174 
resolve_section(section * sec)175 static void resolve_section(section *sec)
176 {
177   taddr rorg_pc,org_pc;
178   int fastphase=FASTOPTPHASE;
179   int pass=0;
180   int extrapass,rorg;
181   size_t size;
182   atom *p;
183 
184   do{
185     done=1;
186     rorg=0;
187     if (++pass>=MAXPASSES){
188       general_error(7,sec->name);
189       break;
190     }
191     extrapass=pass<=fastphase;
192     if(debug)
193       printf("resolve_section(%s) pass %d%s",sec->name,pass,
194              pass<=fastphase?" (fast)\n":"\n");
195     sec->pc=sec->org;
196     for(p=sec->first;p;p=p->next){
197       sec->pc=pcalign(p,sec->pc);
198       cur_src=p->src;
199       cur_src->line=p->line;
200 #if HAVE_CPU_OPTS
201       if(p->type==OPTS){
202         cpu_opts(p->content.opts);
203       }
204       else
205 #endif
206       if(p->type==RORG){
207         if(rorg)
208           general_error(43);  /* reloc org is already set */
209         rorg_pc=*p->content.rorg;
210         org_pc=sec->pc;
211         sec->pc=rorg_pc;
212         sec->flags|=ABSOLUTE;
213         rorg=1;
214       }
215       else if(p->type==RORGEND&&rorg){
216         sec->pc=org_pc+(sec->pc-rorg_pc);
217         rorg_pc=0;
218         sec->flags&=~ABSOLUTE;
219         rorg=0;
220       }
221       else if(p->type==LABEL){
222         symbol *label=p->content.label;
223         if(label->type!=LABSYM)
224           ierror(0);
225         if(label->pc!=sec->pc){
226           if(debug)
227             printf("moving label %s from %lu to %lu\n",label->name,
228                    (unsigned long)label->pc,(unsigned long)sec->pc);
229           done=0;
230           label->pc=sec->pc;
231         }
232       }
233       if(pass>fastphase&&!done&&p->type==INSTRUCTION){
234         /* entered safe mode: optimize only one instruction every pass */
235         sec->pc+=p->lastsize;
236         continue;
237       }
238       if(p->changes>MAXSIZECHANGES){
239         /* atom changed size too frequently, set warning flag */
240         if(debug)
241           printf("setting resolve-warning flag for atom type %d at %lu\n",
242                  p->type,(unsigned long)sec->pc);
243         sec->flags|=RESOLVE_WARN;
244         size=atom_size(p,sec,sec->pc);
245         sec->flags&=~RESOLVE_WARN;
246       }
247       else
248         size=atom_size(p,sec,sec->pc);
249       if(size!=p->lastsize){
250         if(debug)
251           printf("modify size of atom type %d at %lu from %lu to %lu\n",
252                  p->type,(unsigned long)sec->pc,(unsigned long)p->lastsize,
253                  (unsigned long)size);
254         done=0;
255         if(pass>fastphase)
256           p->changes++;  /* now count size modifications of atoms */
257         else if(size>p->lastsize)
258           extrapass=0;   /* no extra pass, when an atom became larger */
259         p->lastsize=size;
260       }
261       sec->pc+=size;
262     }
263     if(rorg){
264       sec->pc=org_pc+(sec->pc-rorg_pc);
265       sec->flags&=~ABSOLUTE;  /* workaround for misssing RORGEND */
266     }
267     /* Extend the fast-optimization phase, when there was no atom which
268        became larger than in the previous pass. */
269     if(extrapass) fastphase++;
270   }while(errors==0&&!done);
271 }
272 
resolve(void)273 static void resolve(void)
274 {
275   section *sec;
276   final_pass=0;
277   if(debug)
278     printf("resolve()\n");
279   for(sec=first_section;sec;sec=sec->next)
280     resolve_section(sec);
281 }
282 
assemble(void)283 static void assemble(void)
284 {
285   section *sec;
286   taddr basepc,rorg_pc,org_pc;
287   atom *p;
288   int bss,rorg;
289 
290   convert_offset_labels();
291   final_pass=1;
292   rorg=0;
293   for(sec=first_section;sec;sec=sec->next){
294     source *lasterrsrc=NULL;
295     utaddr oldpc;
296     int lasterrline=0,ovflw=0;
297     sec->pc=sec->org;
298     bss=strchr(sec->attr,'u')!=NULL;
299     for(p=sec->first;p;p=p->next){
300       basepc=sec->pc;
301       sec->pc=pcalign(p,sec->pc);
302       cur_src=p->src;
303       cur_src->line=p->line;
304       if(p->list&&p->list->atom==p){
305         p->list->sec=sec;
306         p->list->pc=sec->pc;
307       }
308       if(p->changes>MAXSIZECHANGES)
309         sec->flags|=RESOLVE_WARN;
310       /* print a warning on auto-aligned instructions or data */
311       if(sec->pc!=basepc){
312         atom *aa;
313         if (p->type==LABEL&&p->next!=NULL&&p->next->line==p->line)
314           aa=p->next; /* next atom in same line, look at it instead of label */
315         else
316           aa=p;
317         if (aa->type==INSTRUCTION)
318           general_error(50);  /* instruction has been auto-aligned */
319         else if (aa->type==DATA||aa->type==DATADEF)
320           general_error(57);  /* data has been auto-aligned */
321       }
322       if(p->type==RORG){
323         rorg_pc=*p->content.rorg;
324         org_pc=sec->pc;
325         sec->pc=rorg_pc;
326         sec->flags|=ABSOLUTE;
327         rorg=1;
328       }
329       else if(p->type==RORGEND){
330         if(rorg){
331           sec->pc=org_pc+(sec->pc-rorg_pc);
332           rorg_pc=0;
333           sec->flags&=~ABSOLUTE;
334           rorg=0;
335         }
336         else
337           general_error(44);  /* reloc org was not set */
338       }
339       else if(p->type==INSTRUCTION){
340         dblock *db;
341         cur_listing=p->list;
342         db=eval_instruction(p->content.inst,sec,sec->pc);
343         if(pic_check)
344           do_pic_check(db->relocs);
345         cur_listing=0;
346         if(debug){
347           if(db->size!=(p->content.inst->code>=0?
348                         instruction_size(p->content.inst,sec,sec->pc):0))
349             ierror(0);
350         }
351         /*FIXME: sauber freigeben */
352         myfree(p->content.inst);
353         p->content.db=db;
354         p->type=DATA;
355       }
356       else if(p->type==DATADEF){
357         dblock *db;
358         cur_listing=p->list;
359         db=eval_data(p->content.defb->op,p->content.defb->bitsize,sec,sec->pc);
360         if(pic_check)
361           do_pic_check(db->relocs);
362         cur_listing=0;
363         /*FIXME: sauber freigeben */
364         myfree(p->content.defb);
365         p->content.db=db;
366         p->type=DATA;
367       }
368       else if(p->type==ROFFS){
369         sblock *sb;
370         taddr space;
371         if(eval_expr(p->content.roffs,&space,sec,sec->pc)){
372           space=sec->org+space-sec->pc;
373           if (space>=0){
374             sb=new_sblock(number_expr(space),1,0);
375             p->content.sb=sb;
376             p->type=SPACE;
377           }
378           else
379             general_error(20);  /* rorg is lower than current pc */
380         }
381         else
382           general_error(30);  /* expression must be constant */
383       }
384 #if HAVE_CPU_OPTS
385       else if(p->type==OPTS)
386         cpu_opts(p->content.opts);
387 #endif
388       else if(p->type==PRINTTEXT&&!depend)
389         printf("%s",p->content.ptext);
390       else if(p->type==PRINTEXPR&&!depend)
391         atom_printexpr(p->content.pexpr,sec,sec->pc);
392       else if(p->type==ASSERT){
393         assertion *ast=p->content.assert;
394         taddr val;
395         if(ast->assert_exp!=NULL) {
396           eval_expr(ast->assert_exp,&val,sec,sec->pc);
397           if(val==0)
398             general_error(47,ast->expstr,ast->msgstr?ast->msgstr:emptystr);
399         }
400         else /* ASSERT without expression, used for user-FAIL directives */
401           general_error(19,ast->msgstr?ast->msgstr:emptystr);
402       }
403       else if(p->type==NLIST)
404         new_stabdef(p->content.nlist,sec);
405       if(p->type==DATA&&bss){
406         if(lasterrsrc!=p->src||lasterrline!=p->line){
407           if(sec->flags&UNALLOCATED){
408             if(warn_unalloc_ini_dat)
409             general_error(54);  /* initialized data in offset section */
410           }
411           else
412             general_error(31);  /* initialized data in bss */
413           lasterrsrc=p->src;
414           lasterrline=p->line;
415         }
416       }
417       oldpc=sec->pc;
418       sec->pc+=atom_size(p,sec,sec->pc);
419       if((utaddr)sec->pc!=oldpc){
420         if((utaddr)(sec->pc-1)<oldpc||ovflw)
421           general_error(45);  /* address space overflow */
422         ovflw=sec->pc==0;
423       }
424       sec->flags&=~RESOLVE_WARN;
425     }
426     /* leave RORG-mode, when section ends */
427     if(rorg){
428       sec->pc=org_pc+(sec->pc-rorg_pc);
429       rorg_pc=0;
430       sec->flags&=~ABSOLUTE;
431       rorg=0;
432     }
433   }
434   remove_unalloc_sects();
435 }
436 
undef_syms(void)437 static void undef_syms(void)
438 {
439   symbol *sym;
440 
441   for(sym=first_symbol;sym;sym=sym->next){
442     if (!auto_import&&sym->type==IMPORT&&!(sym->flags&(EXPORT|COMMON|WEAK)))
443       general_error(22,sym->name);
444     else if (sym->type==IMPORT&&!(sym->flags&REFERENCED))
445       general_error(61,sym->name);
446   }
447 }
448 
fix_labels(void)449 static void fix_labels(void)
450 {
451   symbol *sym,*base;
452   taddr val;
453 
454   for(sym=first_symbol;sym;sym=sym->next){
455     /* turn all absolute mode labels into absolute symbols */
456     if((sym->flags&ABSLABEL)&&sym->type==LABSYM){
457       sym->type=EXPRESSION;
458       sym->flags&=~(TYPE_MASK|COMMON);
459       sym->sec=NULL;
460       sym->size=NULL;
461       sym->align=0;
462       sym->expr=number_expr(sym->pc);
463     }
464     /* expressions which are based on a label are turned into a new label */
465     else if(sym->type==EXPRESSION){
466       if(!eval_expr(sym->expr,&val,NULL,0)){
467         if(find_base(sym->expr,&base,NULL,0)==BASE_OK){
468           /* turn into an offseted label symbol from the base's section */
469           sym->type=base->type;
470           sym->sec=base->sec;
471           sym->pc=val;
472           sym->align=1;
473         }else
474           general_error(53,sym->name);  /* non-relocatable expr. in equate */
475       }
476     }
477   }
478 }
479 
statistics(void)480 static void statistics(void)
481 {
482   section *sec;
483   unsigned long long size;
484 
485   printf("\n");
486   for(sec=first_section;sec;sec=sec->next){
487     size=ULLTADDR(ULLTADDR(sec->pc)-ULLTADDR(sec->org));
488     printf("%s(%s%lu):\t%12llu byte%c\n",sec->name,sec->attr,
489            (unsigned long)sec->align,size,size==1?' ':'s');
490   }
491 }
492 
init_output(char * fmt)493 static int init_output(char *fmt)
494 {
495   if(!strcmp(fmt,"test"))
496     return init_output_test(&output_copyright,&write_object,&output_args);
497   if(!strcmp(fmt,"elf"))
498     return init_output_elf(&output_copyright,&write_object,&output_args);
499   if(!strcmp(fmt,"bin"))
500     return init_output_bin(&output_copyright,&write_object,&output_args);
501   if(!strcmp(fmt,"srec"))
502     return init_output_srec(&output_copyright,&write_object,&output_args);
503   if(!strcmp(fmt,"vobj"))
504     return init_output_vobj(&output_copyright,&write_object,&output_args);
505   if(!strcmp(fmt,"hunk"))
506     return init_output_hunk(&output_copyright,&write_object,&output_args);
507   if(!strcmp(fmt,"aout"))
508     return init_output_aout(&output_copyright,&write_object,&output_args);
509   if(!strcmp(fmt,"hunkexe")){
510     exec_out=1;  /* executable format */
511     return init_output_hunk(&output_copyright,&write_object,&output_args);
512   }
513   if(!strcmp(fmt,"tos")){
514     exec_out=1;  /* executable format */
515     return init_output_tos(&output_copyright,&write_object,&output_args);
516   }
517   return 0;
518 }
519 
init_main(void)520 static int init_main(void)
521 {
522   size_t i;
523   char *last;
524   hashdata data;
525   mnemohash=new_hashtable(MNEMOHTABSIZE);
526   i=0;
527   while(i<mnemonic_cnt){
528     data.idx=i;
529     last=mnemonics[i].name;
530     add_hashentry(mnemohash,mnemonics[i].name,data);
531     do{
532       i++;
533     }while(i<mnemonic_cnt&&!strcmp(last,mnemonics[i].name));
534   }
535   if(debug){
536     if(mnemohash->collisions)
537       printf("*** %d mnemonic collisions!!\n",mnemohash->collisions);
538   }
539   new_include_path(".");
540   taddrmask=MAKEMASK(bytespertaddr<<3);
541   inst_alignment=INST_ALIGN;
542   return 1;
543 }
544 
set_default_output_format(char * fmt)545 void set_default_output_format(char *fmt)
546 {
547   output_format=fmt;
548 }
549 
set_input_name(void)550 static void set_input_name(void)
551 {
552   if(inname){
553     char *p,c;
554     if((p=strrchr(inname,'/'))!=NULL||
555        (p=strrchr(inname,'\\'))!=NULL||
556        (p=strrchr(inname,':'))!=NULL){
557       /* source text not in current dir., add an include path for it */
558       p++;
559       c=*p;
560       *p='\0';
561       new_include_path(inname);
562       *p=c;
563     }
564     setfilename(inname);
565     setdebugname(inname);
566     include_source(inname);
567   }else
568     general_error(15);
569 }
570 
write_depends(FILE * f)571 static void write_depends(FILE *f)
572 {
573   struct deplist *d = first_depend;
574 
575   if (depend==DEPEND_MAKE && d!=NULL && outname!=NULL)
576     fprintf(f,"%s:",outname);
577 
578   while (d != NULL) {
579     switch (depend) {
580       case DEPEND_LIST:
581         fprintf(f,"%s\n",d->filename);
582         break;
583       case DEPEND_MAKE:
584         if (str_is_graph(d->filename))
585           fprintf(f," %s",d->filename);
586         else
587           fprintf(f," \"%s\"",d->filename);
588         break;
589       default:
590         ierror(0);
591     }
592     d = d->next;
593   }
594 
595   if (depend == DEPEND_MAKE)
596     fputc('\n',f);
597 }
598 
main(int argc,char ** argv)599 int main(int argc,char **argv)
600 {
601   int i;
602   for(i=1;i<argc;i++){
603     if(argv[i][0]=='-'&&argv[i][1]=='F'){
604       output_format=argv[i]+2;
605       argv[i][0]=0;
606     }
607     if(!strcmp("-quiet",argv[i])){
608       verbose=0;
609       argv[i][0]=0;
610     }
611     if(!strcmp("-debug",argv[i])){
612       debug=1;
613       argv[i][0]=0;
614     }
615   }
616   if(!init_output(output_format))
617     general_error(16,output_format);
618   if(!init_main())
619     general_error(10,"main");
620   if(!init_symbol())
621     general_error(10,"symbol");
622   if(verbose)
623     printf("%s\n%s\n%s\n%s\n",copyright,cpu_copyright,syntax_copyright,output_copyright);
624   for(i=1;i<argc;i++){
625     if(argv[i][0]==0)
626       continue;
627     if(argv[i][0]!='-'){
628       if(inname)
629         general_error(11);
630       inname=argv[i];
631       continue;
632     }
633     if(!strcmp("-o",argv[i])&&i<argc-1){
634       if(outname)
635         general_error(28,'o');
636       outname=argv[++i];
637       continue;
638     }
639     if(!strcmp("-L",argv[i])&&i<argc-1){
640       if(listname)
641         general_error(28,'L');
642       listname=argv[++i];
643       produce_listing=1;
644       set_listing(1);
645       continue;
646     }
647     if(!strcmp("-Lnf",argv[i])){
648       listformfeed=0;
649       continue;
650     }
651     if(!strcmp("-Lns",argv[i])){
652       listnosyms=1;
653       continue;
654     }
655     if(!strncmp("-Ll",argv[i],3)){
656       sscanf(argv[i]+3,"%i",&listlinesperpage);
657       continue;
658     }
659     if(!strncmp("-D",argv[i],2)){
660       char *def=NULL;
661       expr *val;
662       if(argv[i][2])
663         def=&argv[i][2];
664       else if (i<argc-1)
665         def=argv[++i];
666       if(def){
667         char *s=def;
668         if(ISIDSTART(*s)){
669           s++;
670           while(ISIDCHAR(*s))
671             s++;
672           def=cnvstr(def,s-def);
673           if(*s=='='){
674             s++;
675             val=parse_expr(&s);
676           }
677           else
678             val=number_expr(1);
679           if(*s)
680             general_error(23,'D');  /* trailing garbage after option */
681           new_abs(def,val);
682           myfree(def);
683           continue;
684         }
685       }
686     }
687     if(!strncmp("-I",argv[i],2)){
688       char *path=NULL;
689       if(argv[i][2])
690         path=&argv[i][2];
691       else if (i<argc-1)
692         path=argv[++i];
693       if(path){
694         new_include_path(path);
695         continue;
696       }
697     }
698     if(!strncmp("-depend=",argv[i],8) || !strncmp("-dependall=",argv[i],11)){
699       depend_all=argv[i][7]!='=';
700       if(!strcmp("list",&argv[i][depend_all?11:8])){
701         depend=DEPEND_LIST;
702         continue;
703       }
704       else if(!strcmp("make",&argv[i][depend_all?11:8])){
705         depend=DEPEND_MAKE;
706         continue;
707       }
708     }
709     if(!strcmp("-unnamed-sections",argv[i])){
710       unnamed_sections=1;
711 
712       continue;
713     }
714     if(!strcmp("-ignore-mult-inc",argv[i])){
715       ignore_multinc=1;
716       continue;
717     }
718     if(!strcmp("-nocase",argv[i])){
719       nocase=1;
720       continue;
721     }
722     if(!strcmp("-nosym",argv[i])){
723       no_symbols=1;
724       continue;
725     }
726     if(!strncmp("-nowarn=",argv[i],8)){
727       int wno;
728       sscanf(argv[i]+8,"%i",&wno);
729       disable_warning(wno);
730       continue;
731     }
732     else if(!strcmp("-w",argv[i])){
733       no_warn=1;
734       continue;
735     }
736     if(!strncmp("-maxerrors=",argv[i],11)){
737       sscanf(argv[i]+11,"%i",&max_errors);
738       continue;
739     }
740     else if(!strcmp("-pic",argv[i])){
741       pic_check=1;
742       continue;
743     }
744     else if(!strncmp("-maxmacrecurs=",argv[i],14)) {
745       sscanf(argv[i]+14,"%i",&maxmacrecurs);
746       continue;
747     }
748     else if(!strcmp("-unsshift",argv[i])){
749       unsigned_shift=1;
750       continue;
751     }
752     else if(!strcmp("-chklabels",argv[i])){
753       chklabels=1;
754       continue;
755     }
756     else if(!strcmp("-noialign",argv[i])) {
757       inst_alignment=1;
758       continue;
759     }
760     if(cpu_args(argv[i]))
761       continue;
762     if(syntax_args(argv[i]))
763       continue;
764     if(output_args(argv[i]))
765       continue;
766     if(!strcmp("-esc",argv[i])){
767       esc_sequences=1;
768       continue;
769     }
770     if(!strcmp("-noesc",argv[i])){
771       esc_sequences=0;
772       continue;
773     }
774     if (!strncmp("-x",argv[i],2)){
775       auto_import=0;
776       continue;
777     }
778     general_error(14,argv[i]);
779   }
780   set_input_name();
781   internal_abs(vasmsym_name);
782   if(!init_parse())
783     general_error(10,"parse");
784   if(!init_syntax())
785     general_error(10,"syntax");
786   if(!init_cpu())
787     general_error(10,"cpu");
788   parse();
789   if(errors==0||produce_listing)
790     resolve();
791   if(errors==0||produce_listing)
792     assemble();
793   cur_src=NULL;
794   if(errors==0)
795     undef_syms();
796   fix_labels();
797   if(produce_listing){
798     if(!listname)
799       listname="a.lst";
800     write_listing(listname);
801   }
802   if(errors==0){
803     if(!depend){
804       if(verbose)
805         statistics();
806       if(!outname)
807         outname="a.out";
808       outfile=fopen(outname,"wb");
809       if(!outfile)
810         general_error(13,outname);
811       else
812         write_object(outfile,first_section,first_symbol);
813     }else
814       write_depends(stdout);
815   }
816   leave();
817   return 0; /* not reached */
818 }
819 
add_depend(char * name)820 static void add_depend(char *name)
821 {
822   if (depend) {
823     struct deplist *d = first_depend;
824 
825     /* check if an entry with the same file name already exists */
826     while (d != NULL) {
827       if (!strcmp(d->filename,name))
828         return;
829       d = d->next;
830     }
831 
832     /* append new dependency record */
833     d = mymalloc(sizeof(struct deplist));
834     d->next = NULL;
835     if (name[0]=='.'&&(name[1]=='/'||name[1]=='\\'))
836       name += 2;  /* skip "./" in paths */
837     d->filename = mystrdup(name);
838     if (last_depend)
839       last_depend = last_depend->next = d;
840     else
841       first_depend = last_depend = d;
842   }
843 }
844 
locate_file(char * filename,char * mode)845 FILE *locate_file(char *filename,char *mode)
846 {
847   char pathbuf[MAXPATHLEN];
848   struct include_path *ipath;
849   FILE *f;
850 
851   if (*filename=='.' || abs_path(filename)) {
852     /* file name is absolute, then don't use any include paths */
853     /* @@@ FIXME: '.' is currently stripped by convert_path() */
854     if (f = fopen(filename,mode)) {
855       if (depend_all)
856         add_depend(pathbuf);
857       return f;
858     }
859   }
860   else {
861     /* locate file name in all known include paths */
862     for (ipath=first_incpath; ipath; ipath=ipath->next) {
863       if (strlen(ipath->path) + strlen(filename) + 1 <= MAXPATHLEN) {
864         strcpy(pathbuf,ipath->path);
865         strcat(pathbuf,filename);
866         if (f = fopen(pathbuf,mode)) {
867           if (depend_all || !abs_path(pathbuf))
868             add_depend(pathbuf);
869           return f;
870         }
871       }
872     }
873   }
874   general_error(12,filename);
875   return NULL;
876 }
877 
include_source(char * inname)878 void include_source(char *inname)
879 {
880   char *filename;
881   struct include_path **nptr = &first_source;
882   struct include_path *name;
883   FILE *f;
884 
885   filename = convert_path(inname);
886 
887   /* check whether this source was already included */
888   while (name = *nptr) {
889 #if defined(AMIGA) || defined(MSDOS) || defined(ATARI) || defined(_WIN32)
890     if (!stricmp(name->path,filename)) {
891 #else
892     if (!strcmp(name->path,filename)) {
893 #endif
894       myfree(filename);
895       if (!ignore_multinc) {
896         filename = name->path;
897         /* reuse already read file from cache? */
898       }
899       nptr = NULL;  /* ignore including this source */
900       break;
901     }
902     nptr = &name->next;
903   }
904   if (nptr) {
905     name = mymalloc(sizeof(struct include_path));
906     name->next = NULL;
907     name->path = filename;
908     *nptr = name;
909   }
910   else if (ignore_multinc)
911     return;  /* ignore multiple inclusion of this source completely */
912 
913   if (f = locate_file(filename,"r")) {
914     char *text;
915     size_t size;
916 
917     for (text=NULL,size=0; ; size+=SRCREADINC) {
918       size_t nchar;
919       text = myrealloc(text,size+SRCREADINC);
920       nchar = fread(text+size,1,SRCREADINC,f);
921       if (nchar < SRCREADINC) {
922         size += nchar;
923         break;
924       }
925     }
926     if (feof(f)) {
927       if (size > 0) {
928         cur_src = new_source(filename,myrealloc(text,size+2),size+1);
929         *(cur_src->text+size) = '\n';
930         *(cur_src->text+size+1) = '\0';
931       }
932       else {
933         myfree(text);
934         cur_src = new_source(filename,"\n",1);
935       }
936     }
937     else
938       general_error(29,filename);
939     fclose(f);
940   }
941 }
942 
943 /* searches a section by name and attr (if secname_attr set) */
944 section *find_section(char *name,char *attr)
945 {
946   section *p;
947   if(secname_attr){
948     for(p=first_section;p;p=p->next){
949       if(!strcmp(name,p->name) && !strcmp(attr,p->attr))
950         return p;
951     }
952   }
953   else{
954     for(p=first_section;p;p=p->next){
955       if(!strcmp(name,p->name))
956         return p;
957     }
958   }
959   return 0;
960 }
961 
962 /* create a new source text instance, which has cur_src as parent */
963 source *new_source(char *filename,char *text,size_t size)
964 {
965   static unsigned long id = 0;
966   source *s = mymalloc(sizeof(source));
967   char *p;
968   size_t i;
969 
970   /* scan source for strange characters */
971   for (p=text,i=0; i<size; i++,p++) {
972     if (*p == 0x1a) {
973       /* EOF character - replace by newline and ignore rest of source */
974       *p = '\n';
975       size = i + 1;
976       break;
977     }
978   }
979 
980   s->parent = cur_src;
981   s->parent_line = cur_src ? cur_src->line : 0;
982   s->name = mystrdup(filename);
983   s->text = text;
984   s->size = size;
985   s->macro = NULL;
986   s->repeat = 1;        /* read just once */
987   s->irpname = NULL;
988   s->cond_level = clev; /* remember level of conditional nesting */
989   s->num_params = -1;   /* not a macro, no parameters */
990   s->param[0] = emptystr;
991   s->param_len[0] = 0;
992   s->id = id++;	        /* every source has unique id - important for macros */
993   s->srcptr = text;
994   s->line = 0;
995   s->bufsize = INITLINELEN;
996   s->linebuf = mymalloc(INITLINELEN);
997 #ifdef CARGSYM
998   s->cargexp = NULL;
999 #endif
1000 #ifdef REPTNSYM
1001   /* -1 outside of a repetition block */
1002   s->reptn = cur_src ? cur_src->reptn : -1;
1003 #endif
1004   return s;
1005 }
1006 
1007 /* quit parsing the current source instance, leave macros, repeat loops
1008    and restore the conditional assembly level */
1009 void end_source(source *s)
1010 {
1011   if(s){
1012     s->srcptr=s->text+s->size;
1013     s->repeat=1;
1014     clev=s->cond_level;
1015   }
1016 }
1017 
1018 /* set current section, remember last */
1019 void set_section(section *s)
1020 {
1021 #if NOT_NEEDED
1022   if (current_section!=NULL && !(current_section->flags & UNALLOCATED)) {
1023     if (current_section->flags & ABSOLUTE)
1024       prev_org = current_section;
1025     else
1026       prev_sec = current_section;
1027   }
1028 #endif
1029 #if HAVE_CPU_OPTS
1030   if (!(s->flags & UNALLOCATED))
1031     cpu_opts_init(s);  /* set initial cpu opts before the first atom */
1032 #endif
1033   current_section = s;
1034 }
1035 
1036 /* creates a new section with given attributes and alignment;
1037    does not switch to this section automatically */
1038 section *new_section(char *name,char *attr,int align)
1039 {
1040   section *p;
1041   if(unnamed_sections)
1042     name=emptystr;
1043   if(p=find_section(name,attr))
1044     return p;
1045   p=mymalloc(sizeof(*p));
1046   p->next=0;
1047   p->name=mystrdup(name);
1048   p->attr=mystrdup(attr);
1049   p->first=p->last=0;
1050   p->align=align;
1051   p->org=p->pc=0;
1052   p->flags=0;
1053   p->memattr=0;
1054   memset(p->pad,0,MAXPADBYTES);
1055   p->padbytes=1;
1056   if(last_section)
1057     last_section=last_section->next=p;
1058   else
1059     first_section=last_section=p;
1060   return p;
1061 }
1062 
1063 /* create a dummy code section for each new ORG directive */
1064 section *new_org(taddr org)
1065 {
1066   char buf[16];
1067   section *sec;
1068 
1069   sprintf(buf,"seg%llx",ULLTADDR(org));
1070   sec = new_section(buf,"acrwx",1);
1071   sec->org = sec->pc = org;
1072   sec->flags |= ABSOLUTE;  /* absolute destination address */
1073   return sec;
1074 }
1075 
1076 /* switches current section to the section with the specified name */
1077 void switch_section(char *name,char *attr)
1078 {
1079   section *p;
1080   if(unnamed_sections)
1081     name=emptystr;
1082   p=find_section(name,attr);
1083   if(!p)
1084     general_error(2,name);
1085   else
1086     set_section(p);
1087 }
1088 
1089 /* Switches current section to an offset section. Create a new section when
1090    it doesn't exist yet or needs a different offset. */
1091 void switch_offset_section(char *name,taddr offs)
1092 {
1093   static unsigned long id;
1094   char unique_name[14];
1095   section *sec;
1096 
1097   if (!name) {
1098     if (offs != -1)
1099       ++id;
1100     sprintf(unique_name,"OFFSET%06lu",id);
1101     name = unique_name;
1102   }
1103   sec = new_section(name,"u",1);
1104   sec->flags |= UNALLOCATED;
1105   if (offs != -1)
1106     sec->org = sec->pc = offs;
1107   set_section(sec);
1108 }
1109 
1110 /* returns current_section or the syntax module's default section,
1111    when undefined */
1112 section *default_section(void)
1113 {
1114   section *sec = current_section;
1115 
1116   if (!sec && defsectname && defsecttype) {
1117     sec = new_section(defsectname,defsecttype,1);
1118     switch_section(defsectname,defsecttype);
1119   }
1120   return sec;
1121 }
1122 
1123 #if NOT_NEEDED
1124 /* restore last relocatable section */
1125 section *restore_section(void)
1126 {
1127   if (prev_sec)
1128     return prev_sec;
1129   if (defsectname && defsecttype)
1130     return new_section(defsectname,defsecttype,1);
1131   return NULL;  /* no previous section or default section defined */
1132 }
1133 
1134 /* restore last absolute section */
1135 section *restore_org(void)
1136 {
1137   if (prev_org)
1138     return prev_org;
1139   return new_org(0);  /* no previous org: default to ORG 0 */
1140 }
1141 #endif /* NOT_NEEDED */
1142 
1143 /* end a relocated ORG block */
1144 int end_rorg(void)
1145 {
1146   section *s = default_section();
1147 
1148   if (s == NULL) {
1149     general_error(3);
1150     return 0;
1151   }
1152   if (s->flags & IN_RORG) {
1153     add_atom(s,new_rorgend_atom());
1154     if (s->flags & PREVABS)
1155       s->flags |= ABSOLUTE;
1156     else
1157       s->flags &= ~ABSOLUTE;
1158     s->flags &= ~IN_RORG;
1159     return 1;
1160   }
1161   general_error(44);  /* no Rorg block to end */
1162   return 0;
1163 }
1164 
1165 /* end a relocated ORG block when currently active */
1166 void try_end_rorg(void)
1167 {
1168   if (current_section!=NULL && (current_section->flags&IN_RORG))
1169     end_rorg();
1170 }
1171 
1172 /* start a relocated ORG block */
1173 void start_rorg(taddr rorg)
1174 {
1175   section *s = default_section();
1176 
1177   if (s == NULL) {
1178     general_error(3);
1179     return;
1180   }
1181   if (s->flags & IN_RORG)
1182     end_rorg();  /* we are already in a ROrg-block, so close it first */
1183   add_atom(s,new_rorg_atom(rorg));
1184   s->flags |= IN_RORG;
1185   if (!(s->flags & ABSOLUTE)) {
1186     s->flags &= ~PREVABS;
1187     s->flags |= ABSOLUTE;  /* make section absolute during the ROrg-block */
1188   }
1189   else
1190     s->flags |= PREVABS;
1191 }
1192 
1193 void print_section(FILE *f,section *sec)
1194 {
1195   atom *p;
1196   taddr pc=sec->org;
1197   fprintf(f,"section %s (attr=<%s> align=%llu):\n",
1198           sec->name,sec->attr,ULLTADDR(sec->align));
1199   for(p=sec->first;p;p=p->next){
1200     pc=pcalign(p,pc);
1201     fprintf(f,"%8llx: ",ULLTADDR(pc));
1202     print_atom(f,p);
1203     fprintf(f,"\n");
1204     pc+=atom_size(p,sec,pc);
1205   }
1206 }
1207 
1208 void new_include_path(char *pathname)
1209 {
1210   struct include_path *new = mymalloc(sizeof(struct include_path));
1211   struct include_path *ipath;
1212   char *newpath = convert_path(pathname);
1213   int len = strlen(newpath);
1214 
1215 #if defined(AMIGA)
1216   if (len>0 && newpath[len-1]!='/' && newpath[len-1]!=':') {
1217     pathname = mymalloc(len+2);
1218     strcpy(pathname,newpath);
1219     pathname[len] = '/';
1220     pathname[len+1] = '\0';
1221   }
1222 #elif defined(MSDOS) || defined(ATARI) || defined(_WIN32)
1223   if (len>0 && newpath[len-1]!='\\' && newpath[len-1]!=':') {
1224     pathname = mymalloc(len+2);
1225     strcpy(pathname,newpath);
1226     pathname[len] = '\\';
1227     pathname[len+1] = '\0';
1228   }
1229 #else
1230   if (len>0 && newpath[len-1] != '/') {
1231     pathname = mymalloc(len+2);
1232     strcpy(pathname,newpath);
1233     pathname[len] = '/';
1234     pathname[len+1] = '\0';
1235   }
1236 #endif
1237   else
1238     pathname = mystrdup(newpath);
1239   myfree(newpath);
1240   new->next = NULL;
1241   new->path = pathname;
1242 
1243   if (ipath = first_incpath) {
1244     while (ipath->next)
1245       ipath = ipath->next;
1246     ipath->next = new;
1247   }
1248   else
1249     first_incpath = new;
1250 }
1251 
1252 void set_listing(int on)
1253 {
1254   listena = on && produce_listing;
1255 }
1256 
1257 void set_list_title(char *p,int len)
1258 {
1259   listtitlecnt++;
1260   listtitles=myrealloc(listtitles,listtitlecnt*sizeof(*listtitles));
1261   listtitles[listtitlecnt-1]=mymalloc(len+1);
1262   strncpy(listtitles[listtitlecnt-1],p,len);
1263   listtitles[listtitlecnt-1][len]=0;
1264   listtitlelines=myrealloc(listtitlelines,listtitlecnt*sizeof(*listtitlelines));
1265   listtitlelines[listtitlecnt-1]=cur_src->line;
1266 }
1267 
1268 static void print_list_header(FILE *f,int cnt)
1269 {
1270   if(cnt%listlinesperpage==0){
1271     if(cnt!=0&&listformfeed)
1272       fprintf(f,"\f");
1273     if(listtitlecnt>0){
1274       int i,t;
1275       for(i=0,t=-1;i<listtitlecnt;i++){
1276         if(listtitlelines[i]<=cnt+listlinesperpage)
1277           t=i;
1278       }
1279       if(t>=0){
1280         int sp=(120-strlen(listtitles[t]))/2;
1281         while(--sp)
1282           fprintf(f," ");
1283         fprintf(f,"%s\n",listtitles[t]);
1284       }
1285       cnt++;
1286     }
1287     fprintf(f,"Err  Line Loc.  S Object1  Object2  M Source\n");
1288   }
1289 }
1290 
1291 #if VASM_CPU_OIL
1292 void write_listing(char *listname)
1293 {
1294   FILE *f;
1295   int nsecs,i,cnt=0,nl;
1296   section *secp;
1297   listing *p;
1298   atom *a;
1299   symbol *sym;
1300   taddr pc;
1301   char rel;
1302 
1303   if(!(f=fopen(listname,"w"))){
1304     general_error(13,listname);
1305     return;
1306   }
1307   for(nsecs=0,secp=first_section;secp;secp=secp->next)
1308     secp->idx=nsecs++;
1309   for(p=first_listing;p;p=p->next){
1310     if(!p->src||p->src->id!=0)
1311       continue;
1312     print_list_header(f,cnt++);
1313     if(p->error!=0)
1314       fprintf(f,"%04d ",p->error);
1315     else
1316       fprintf(f,"     ");
1317     fprintf(f,"%4d ",p->line);
1318     a=p->atom;
1319     while(a&&a->type!=DATA&&a->next&&a->next->line==a->line&&a->next->src==a->src)
1320       a=a->next;
1321     if(a&&a->type==DATA){
1322       int size=a->content.db->size;
1323       char *dp=a->content.db->data;
1324       pc=p->pc;
1325       fprintf(f,"%05lX %d ",(unsigned long)pc,(int)(p->sec?p->sec->idx:0));
1326       for(i=0;i<8;i++){
1327         if(i==4)
1328           fprintf(f," ");
1329         if(i<size){
1330           fprintf(f,"%02X",(unsigned char)*dp++);
1331           pc++;
1332         }else
1333           fprintf(f,"  ");
1334         /* append following atoms with align 1 directly */
1335         if(i==size-1&&i<7&&a->next&&a->next->align<=a->align&&a->next->type==DATA&&a->next->line==a->line&&a->next->src==a->src){
1336           a=a->next;
1337           size+=a->content.db->size;
1338           dp=a->content.db->data;
1339         }
1340       }
1341       fprintf(f," ");
1342       if(a->content.db->relocs){
1343         symbol *s=((nreloc *)(a->content.db->relocs->reloc))->sym;
1344         if(s->type==IMPORT)
1345           rel='X';
1346         else
1347           rel='0'+p->sec->idx;
1348       }else
1349         rel='A';
1350       fprintf(f,"%c ",rel);
1351     }else
1352       fprintf(f,"                           ");
1353 
1354     fprintf(f," %-.77s",p->txt);
1355 
1356     /* bei laengeren Daten den Rest ueberspringen */
1357     /* Block entfernen, wenn alles ausgegeben werden soll */
1358     if(a&&a->type==DATA&&i<a->content.db->size){
1359       pc+=a->content.db->size-i;
1360       i=a->content.db->size;
1361     }
1362 
1363     /* restliche DATA-Zeilen, wenn noetig */
1364     while(a){
1365       if(a->type==DATA){
1366         int size=a->content.db->size;
1367         char *dp=a->content.db->data+i;
1368 
1369         if(i<size){
1370           for(;i<size;i++){
1371             if((i&7)==0){
1372               fprintf(f,"\n");
1373               print_list_header(f,cnt++);
1374               fprintf(f,"          %05lX %d ",(unsigned long)pc,(int)(p->sec?p->sec->idx:0));
1375             }else if((i&3)==0)
1376               fprintf(f," ");
1377             fprintf(f,"%02X",(unsigned char)*dp++);
1378             pc++;
1379             /* append following atoms with align 1 directly */
1380             if(i==size-1&&a->next&&a->next->align<=a->align&&a->next->type==DATA&&a->next->line==a->line&&a->next->src==a->src){
1381               a=a->next;
1382               size+=a->content.db->size;
1383               dp=a->content.db->data;
1384             }
1385           }
1386           i=8-(i&7);
1387           if(i>=4)
1388             fprintf(f," ");
1389           while(i--){
1390             fprintf(f,"  ");
1391           }
1392           fprintf(f," %c",rel);
1393         }
1394         i=0;
1395       }
1396       if(a->next&&a->next->line==a->line&&a->next->src==a->src){
1397         a=a->next;
1398         pc=pcalign(a,pc);
1399         if(a->type==DATA&&a->content.db->relocs){
1400           symbol *s=((nreloc *)(a->content.db->relocs->reloc))->sym;
1401           if(s->type==IMPORT)
1402             rel='X';
1403           else
1404             rel='0'+p->sec->idx;
1405         }else
1406           rel='A';
1407       }else
1408         a=0;
1409     }
1410     fprintf(f,"\n");
1411   }
1412   fprintf(f,"\n\nSections:\n");
1413   for(secp=first_section;secp;secp=secp->next)
1414     fprintf(f,"%d  %s\n",(int)secp->idx,secp->name);
1415   if(!listnosyms){
1416     fprintf(f,"\n\nSymbols:\n");
1417     {
1418       symbol *last=0,*cur,*symo;
1419       for(symo=first_symbol;symo;symo=symo->next){
1420         cur=0;
1421         for(sym=first_symbol;sym;sym=sym->next){
1422           if(!last||stricmp(sym->name,last->name)>0)
1423             if(!cur||stricmp(sym->name,cur->name)<0)
1424               cur=sym;
1425         }
1426         if(cur){
1427           print_symbol(f,cur);
1428           fprintf(f,"\n");
1429           last=cur;
1430         }
1431       }
1432     }
1433   }
1434   if(errors==0)
1435     fprintf(f,"\nThere have been no errors.\n");
1436   else
1437     fprintf(f,"\nThere have been %d errors!\n",errors);
1438   fclose(f);
1439   for(p=first_listing;p;){
1440     listing *m=p->next;
1441     myfree(p);
1442     p=m;
1443   }
1444 }
1445 #else
1446 void write_listing(char *listname)
1447 {
1448   FILE *f;
1449   int nsecs,i,maxsrc=0;
1450   section *secp;
1451   listing *p;
1452   atom *a;
1453   symbol *sym;
1454   taddr pc;
1455 
1456   if(!(f=fopen(listname,"w"))){
1457     general_error(13,listname);
1458     return;
1459   }
1460   for(nsecs=1,secp=first_section;secp;secp=secp->next)
1461     secp->idx=nsecs++;
1462   for(p=first_listing;p;p=p->next){
1463     char err[6];
1464     if(p->error!=0)
1465       sprintf(err,"E%04d",p->error);
1466     else
1467       sprintf(err,"     ");
1468     if(p->src&&p->src->id>maxsrc)
1469       maxsrc=p->src->id;
1470     fprintf(f,"F%02d:%04d %s %s",(int)(p->src?p->src->id:0),p->line,err,p->txt);
1471     a=p->atom;
1472     pc=p->pc;
1473     while(a){
1474       if(a->type==DATA){
1475         int size=a->content.db->size;
1476         for(i=0;i<size&&i<32;i++){
1477           if((i&15)==0)
1478             fprintf(f,"\n               S%02d:%08lX: ",(int)(p->sec?p->sec->idx:0),(unsigned long)(pc));
1479           fprintf(f," %02X",(unsigned char)a->content.db->data[i]);
1480           pc++;
1481         }
1482         if(a->content.db->relocs)
1483           fprintf(f," [R]");
1484       }
1485       if(a->next&&a->next->line==a->line&&a->next->src==a->src){
1486         a=a->next;
1487         pc=pcalign(a,pc);
1488       }else
1489         a=0;
1490     }
1491     fprintf(f,"\n");
1492   }
1493   fprintf(f,"\n\nSections:\n");
1494   for(secp=first_section;secp;secp=secp->next)
1495     fprintf(f,"S%02d  %s\n",(int)secp->idx,secp->name);
1496   fprintf(f,"\n\nSources:\n");
1497   for(i=0;i<=maxsrc;i++){
1498     for(p=first_listing;p;p=p->next){
1499       if(p->src&&p->src->id==i){
1500         fprintf(f,"F%02d  %s\n",i,p->src->name);
1501         break;
1502       }
1503     }
1504   }
1505   fprintf(f,"\n\nSymbols:\n");
1506   for(sym=first_symbol;sym;sym=sym->next){
1507     print_symbol(f,sym);
1508     fprintf(f,"\n");
1509   }
1510   if(errors==0)
1511     fprintf(f,"\nThere have been no errors.\n");
1512   else
1513     fprintf(f,"\nThere have been %d errors!\n",errors);
1514   fclose(f);
1515   for(p=first_listing;p;){
1516     listing *m=p->next;
1517     myfree(p);
1518     p=m;
1519   }
1520 }
1521 #endif
1522