Pristine Ack-5.5
[Ack-5.5.git] / util / ack / trans.c
1 /*
2  * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
3  * See the copyright notice in the ACK home directory, in the file "Copyright".
4  *
5  */
6
7 #include "ack.h"
8 #include "list.h"
9 #include "trans.h"
10 #include "grows.h"
11 #include "data.h"
12
13 #ifndef NORCSID
14 static char rcs_id[] = "$Id: trans.c,v 2.10 1994/06/24 10:13:15 ceriel Exp $" ;
15 static char rcs_trans[] = RCS_TRANS ;
16 #endif
17
18 /****************************************************************************/
19 /*      Routines for transforming from one file type to another             */
20 /****************************************************************************/
21
22 static growstring head ;
23 static int        touch_head= NO ;
24 static growstring tail ;
25 static int        touch_tail= NO ;
26
27 char *headvar(),*tailvar() ;
28
29 int transform(phase) register trf *phase ; {
30         int ok ;
31
32         if ( !setfiles(phase) ) {
33                 disc_files(phase) ;
34                 return 0 ;
35         }
36         getcallargs(phase) ;
37         ok= runphase(phase) ;
38         if ( !ok ) rmfile(&out) ;
39         /* Free the space occupied by the arguments,
40            except for the linker, since we are bound to exit soon
41            and do not foresee further need of memory space */
42         if ( !phase->t_linker ) discardargs(phase) ;
43         disc_files(phase) ;
44         return ok ;
45 }
46
47 getmapflags(phase) register trf *phase ; {
48         register path *l_in ;
49         register list_elem *elem ;
50         int scanned ;
51         register char *ptr ;
52
53         scanlist(l_first(flags),elem) {
54                 scanned= *(l_content(*elem))&NO_SCAN ;
55                 *(l_content(*elem)) &= ~NO_SCAN ;
56                 if ( mapflag(&(phase->t_mapf),l_content(*elem)) ) {
57                         scanned=NO_SCAN ;
58 #ifdef DEBUG
59                         if ( debug >=4 ) {
60                                 vprint("phase %s, added mapflag for %s\n",
61                                         phase->t_name,
62                                         l_content(*elem) ) ;
63                         }
64 #endif
65                 }
66                 *(l_content(*elem)) |= scanned ;
67         }
68         if ( phase->t_linker ) {
69                 scanlist(l_first(phase->t_inputs),elem) {
70                         l_in = p_cont(*elem) ;
71                         if ( mapflag(&(phase->t_mapf),l_in->p_path) ) {
72                                 ptr= keeps(getvar(LIBVAR)) ;
73                                 clr_noscan(ptr) ;
74 #ifdef DEBUG
75                                 if ( debug >=4 ) {
76                                         vprint("phase %s, library %s(%s)\n",
77                                            phase->t_name,l_in->p_path,ptr) ;
78                                 }
79 #endif
80                                 if ( l_in->p_keeps) throws(l_in->p_path) ;
81                                 l_in->p_path= ptr ;
82                                 l_in->p_keeps=YES ;
83                         }
84                 }
85                 scanlist(l_first(flags),elem) {
86                         /* Get the flags remaining for the loader,
87                            That is: all the flags neither eaten by ack nor
88                            one of the subprograms called so-far.
89                            The last fact is indicated by the NO_SCAN bit
90                            in the first character of the flag.
91                         */
92                         if ( !( *(l_content(*elem))&NO_SCAN ) ) {
93                                 l_add(&(phase->t_flags),l_content(*elem)) ;
94 #ifdef DEBUG
95                                 if ( debug >=4 ) {
96                                         vprint("phase %s, added flag %s\n",
97                                                 phase->t_name,
98                                                 l_content(*elem) ) ;
99                                 }
100 #endif
101                         }
102                 }
103         }
104 }
105
106
107 do_Rflag(argp) char *argp ; {
108         l_add(&R_list,argp) ;
109 }
110
111 char *headvar() {
112         if ( !touch_head) return "" ;
113         return gr_start(head) ;
114 }
115
116 add_head(str) char *str; {
117         if ( !touch_head) {
118                 gr_init(&head) ;
119                 touch_head=YES ;
120         }
121         gr_cat(&head,str) ;
122 }
123
124 char *tailvar() {
125         if ( !touch_tail ) return "" ;
126         return gr_start(tail) ;
127 }
128
129 add_tail(str) char *str ; {
130         if ( !touch_tail ) {
131                 gr_init(&tail) ;
132                 touch_tail=YES ;
133         }
134         gr_cat(&tail,str) ;
135 }
136
137
138 transini() {
139         register list_elem *elem ;
140         register trf *phase ;
141
142         scanlist(l_first(tr_list), elem) {
143                 phase = t_cont(*elem) ;
144                 if ( !phase->t_linker ) getmapflags(phase);
145         }
146         scanlist(l_first(R_list), elem) {
147                 set_Rflag(l_content(*elem)) ;
148         }
149         l_clear(&R_list) ;
150         setpvar(keeps(HEAD),headvar) ;
151         setpvar(keeps(TAIL),tailvar) ;
152 }
153
154 set_Rflag(argp) register char *argp ; {
155         register char *eos ;
156         register list_elem *prog ;
157         register int length ;
158         char *eq, *colon ;
159
160         eos= strindex(&argp[2],'-');
161         eq= strindex(&argp[2],EQUAL) ;
162         colon= strindex(&argp[2],':');
163         if ( !eos ) {
164                 eos= eq ;
165         } else {
166                 if ( eq && eq<eos ) eos= eq ;
167         }
168         if ( colon && ( !eos || eos>colon ) ) eos= colon ;
169         if ( !eos ) {
170                 if ( !(argp[0]&NO_SCAN) ) werror("Incorrect use of -R flag") ;
171                 return ;
172         }
173         length= eos - &argp[2] ;
174         scanlist(l_first(tr_list), prog) {
175                 if ( strncmp(t_cont(*prog)->t_name, &argp[2], length )==0 &&
176                      t_cont(*prog)->t_name[length]==0  /* Same name length */) {
177                         if ( *eos=='-' ) {
178                                 if ( !(argp[0]&NO_SCAN) ) {
179                                         /* If not already taken by a mapflag */
180                                         l_add(&(t_cont(*prog)->t_flags),eos) ;
181                                 }
182                         } else
183                         if ( *eos=='=' ) {
184                                 t_cont(*prog)->t_prog= eos+1 ;
185                         } else {
186                                 t_cont(*prog)->t_priority= atoi(eos+1) ;
187                         }
188                         argp[0] |= NO_SCAN ;
189                         return ;
190                 }
191         }
192         if ( !(argp[0]&NO_SCAN) ) werror("Cannot find program for %s",argp) ;
193         return ;
194 }
195
196 /**************************************************************************/
197 /*                                                                        */
198 /*           The creation of arguments for exec for a transformation      */
199 /*                                                                        */
200 /**************************************************************************/
201
202 growstring scanb(line) char *line ; {
203         /* Scan a line for backslashes, setting the NO_SCAN bit in characters
204            preceded by a backslash.
205         */
206         register char *in_c ;
207         register int  token ;
208         growstring result ;
209         enum { TEXT, ESCAPED } state = TEXT ;
210
211         gr_init(&result) ;
212         for ( in_c= line ; *in_c ; in_c++ ) {
213                 token= *in_c&0377 ;
214                 switch( state ) {
215                 case TEXT :
216                         if ( token==BSLASH ) {
217                                 state= ESCAPED ;
218                         } else {
219                                 gr_add(&result,token) ;
220                         }
221                         break ;
222                 case ESCAPED :
223                         gr_add(&result,token|NO_SCAN) ;
224                         state=TEXT ;
225                         break ;
226                 }
227         }
228         gr_add(&result,0) ;
229         if ( state!=TEXT ) werror("flag line ends with %c",BSLASH) ;
230         return result ;
231 }
232
233 growstring scanvars(line) char *line ; {
234         /* Scan a line variable replacements started by S_VAR.
235            Two sequences exist: S_VAR name E_VAR, S_VAR name A_VAR text E_VAR.
236            neither name nor text may contain further replacements.
237            In the first form an error message is issued if the name is not
238            present in the variables, the second form produces text
239            in that case.
240            The sequence S_VAR S_VAR is transformed into S_VAR.
241            This to allow later recognition in mapflags, where B_SLASH
242            would be preventing any recognition.
243         */
244         register char *in_c ;
245         register int  token ;
246         growstring result ;
247         growstring name ;
248         register char *tr ;
249         enum { TEXT, FIRST, NAME, SKIP, COPY } state = TEXT ;
250
251         gr_init(&result) ; gr_init(&name) ;
252         for ( in_c= line ; *in_c ; in_c++ ) {
253                 token= *in_c&0377 ;
254                 switch( state ) {
255                 case TEXT :
256                         if ( token==S_VAR ) {
257                                 state= FIRST ;
258                         } else {
259                                 gr_add(&result,token) ;
260                         }
261                         break ;
262                 case FIRST :
263                         switch ( token ) {
264                         case S_VAR :
265                                 state= TEXT ;
266                                 gr_add(&result,token) ;
267                                 break ;
268                         case A_VAR :
269                         case C_VAR :
270                                 fatal("empty string variable name") ;
271                         default :
272                                 state=NAME ;
273                                 gr_add(&name,token) ;
274                                 break ;
275                         }
276                         break ;
277                 case NAME:
278                         switch ( token ) {
279                         case A_VAR :
280                                 gr_add(&name,0) ;
281                                 if ( tr=getvar(gr_start(name)) ) {
282                                         while ( *tr ) {
283                                                 gr_add(&result,*tr++) ;
284                                         }
285                                         state=SKIP ;
286                                 } else {
287                                         state=COPY ;
288                                 }
289                                 gr_throw(&name) ;
290                                 break ;
291                         case C_VAR :
292                                 gr_add(&name,0) ;
293                                 if ( tr=getvar(gr_start(name)) ) {
294                                         while ( *tr ) {
295                                            gr_add(&result,*tr++);
296                                         }
297                                 } else {
298                                         werror("No definition for %s",
299                                                 gr_start(name)) ;
300                                 }
301                                 state=TEXT ;
302                                 gr_throw(&name) ;
303                                 break ;
304                         default:
305                                 gr_add(&name,token) ;
306                                 break ;
307                         }
308                         break ;
309                 case SKIP :
310                         if ( token==C_VAR ) state= TEXT ;
311                         break ;
312                 case COPY :
313                         if ( token==C_VAR ) state= TEXT ; else {
314                                 gr_add(&result,token) ;
315                         }
316                         break ;
317                 }
318         }
319         gr_add(&result,0) ;
320         if ( state!=TEXT ) {
321                 werror("flag line misses %c",C_VAR) ;
322                 gr_throw(&name) ;
323         }
324         return result ;
325 }
326
327 growstring scanexpr(line) char *line ; {
328         /* Scan a line for conditional or flag expressions,
329            dependent on the type. The format is
330            S_EXPR suflist M_EXPR suflist T_EXPR tail C_EXPR
331            the head and tail are passed to treat, together with the
332            growstring for futher treatment.
333            Nesting is not allowed.
334         */
335         register char *in_c ;
336         char *heads ;
337         register int  token ;
338         growstring sufs, tailval ;
339         growstring result ;
340         static list_head fsuff, lsuff ;
341         enum { TEXT, FDOT, FSUF, LDOT, LSUF, FTAIL } state = TEXT ;
342
343         gr_init(&result) ; gr_init(&sufs) ; gr_init(&tailval) ;
344         for ( in_c= line ; *in_c ; in_c++ ) {
345                 token= *in_c&0377 ;
346                 switch( state ) {
347                 case TEXT :
348                         if ( token==S_EXPR ) {
349                                 state= FDOT ;
350                                 heads=in_c ;
351                         } else gr_add(&result,token) ;
352                         break ;
353                 case FDOT :
354                         if ( token==M_EXPR ) {
355                                 state=LDOT ;
356                                 break ;
357                         }
358                         token &= ~NO_SCAN ;
359                         if ( token!=SUFCHAR ) {
360                                 error("Missing %c in expression",SUFCHAR) ;
361                         }
362                         gr_add(&sufs,token) ; state=FSUF ;
363                         break ;
364                 case FSUF :
365                         if ( token==M_EXPR || (token&~NO_SCAN)==SUFCHAR) {
366                                 gr_add(&sufs,0) ;
367                                 l_add(&fsuff,gr_final(&sufs)) ;
368                         }
369                         if ( token==M_EXPR ) {
370                                 state=LDOT ;
371                         } else gr_add(&sufs,token&~NO_SCAN) ;
372                         break ;
373                 case LDOT :
374                         if ( token==T_EXPR ) {
375                                 state=FTAIL ;
376                                 break ;
377                         }
378                         token &= ~NO_SCAN ;
379                         if ( token!=SUFCHAR ) {
380                                 error("Missing %c in expression",SUFCHAR) ;
381                         }
382                         gr_add(&sufs,token) ; state=LSUF ;
383                         break ;
384                 case LSUF :
385                         if ( token==T_EXPR || (token&~NO_SCAN)==SUFCHAR) {
386                                 gr_add(&sufs,0) ;
387                                 l_add(&lsuff,gr_final(&sufs)) ;
388                         }
389                         if ( token==T_EXPR ) {
390                                 state=FTAIL ;
391                         } else gr_add(&sufs,token&~NO_SCAN) ;
392                         break ;
393                 case FTAIL :
394                         if ( token==C_EXPR ) {
395                                 /* Found one !! */
396                                 gr_add(&tailval,0) ;
397                                 condit(&result,&fsuff,&lsuff,gr_start(tailval)) ;
398                                 l_throw(&fsuff) ; l_throw(&lsuff) ;
399                                 gr_throw(&tailval) ;
400                                 state=TEXT ;
401                         } else gr_add(&tailval,token) ;
402                         break ;
403                 }
404         }
405         gr_add(&result,0) ;
406         if ( state!=TEXT ) {
407                 l_throw(&fsuff) ; l_throw(&lsuff) ; gr_throw(&tailval) ;
408                 werror("flag line has unclosed expression starting with %6s",
409                         heads) ;
410         }
411         return result ;
412 }
413
414 condit(line,fsuff,lsuff,tailval) growstring *line ;
415         list_head *fsuff, *lsuff;
416         char *tailval ;
417 {
418         register list_elem *first ;
419         register list_elem *last ;
420
421 #ifdef DEBUG
422         if ( debug>=4 ) vprint("Conditional for %s, ",tailval) ;
423 #endif
424         scanlist( l_first(*fsuff), first ) {
425                 scanlist( l_first(*lsuff), last ) {
426                         if ( strcmp(l_content(*first),l_content(*last))==0 ) {
427                                 /* Found */
428 #ifdef DEBUG
429                                 if ( debug>=4 ) vprint(" matched\n") ;
430 #endif
431                                 while ( *tailval) gr_add(line,*tailval++ ) ;
432                                 return ;
433                         }
434                 }
435         }
436 #ifdef DEBUG
437         if ( debug>=4) vprint(" non-matched\n") ;
438 #endif
439 }
440
441 int mapflag(maplist,cflag) list_head *maplist ; char *cflag ; {
442         /* Expand a flag expression */
443         /* The flag "cflag" is checked for each of the mapflags.
444            A mapflag entry has the form
445                 -text NAME=replacement or -text*text NAME=replacement
446            The star matches anything as in the shell.
447            If the entry matches the assignment will take place
448            This replacement is subjected to argument matching only.
449            When a match took place the replacement is returned
450            when not, (char *)0.
451            The replacement sits in stable storage.
452         */
453         register list_elem *elem ;
454
455         scanlist(l_first(*maplist),elem) {
456                 if ( mapexpand(l_content(*elem),cflag) ) {
457                         return 1 ;
458                 }
459         }
460         return 0 ;
461 }
462
463 int mapexpand(mapentry,cflag)
464         char *mapentry, *cflag ;
465 {
466         register char *star ;
467         register char *ptr ;
468         register char *space ;
469         int length ;
470
471         star=strindex(mapentry,STAR) ;
472         space=firstblank(mapentry) ;
473         if ( star >space ) star= (char *)0 ;
474         if ( star ) {
475                 length= space-star-1 ;
476                 if ( strncmp(mapentry,cflag,star-mapentry) ||
477                      strncmp(star+1,cflag+strlen(cflag)-length,length) ) {
478                         return 0 ;
479                 }
480                 /* Match */
481                 /* Now set star to the first char of the star
482                    replacement and length to its length
483                 */
484                 length=strlen(cflag)-(star-mapentry)-length ;
485                 if ( length<0 ) return 0 ;
486                 star=cflag+(star-mapentry) ;
487 #ifdef DEBUG
488                 if ( debug>=6 ) {
489                         vprint("Starmatch (%s,%s) %.*s\n",
490                                 mapentry,cflag,length,star) ;
491                 }
492 #endif
493         } else {
494                 if ( strncmp(mapentry,cflag,space-mapentry)!=0 ||
495                      cflag[space-mapentry] ) {
496                         return 0 ;
497                 }
498         }
499         ptr= skipblank(space) ;
500         if ( *ptr==0 ) return 1 ;
501         doassign(ptr,star,length) ;
502         return 1 ;
503 }
504
505 doassign(line,star,length) char *line, *star ; {
506         growstring varval, name, temp ;
507         register char *ptr ;
508
509         gr_init(&varval) ;
510         gr_init(&name) ;
511         ptr= line ;
512         for ( ; *ptr && *ptr!=SPACE && *ptr!=TAB && *ptr!=EQUAL ; ptr++ ) {
513                 gr_add(&name,*ptr) ;
514         }
515         ptr= strindex(ptr,EQUAL) ;
516         if ( !ptr ) {
517                 error("Missing %c in assignment %s",EQUAL,line);
518                 return ;
519         }
520         temp= scanvars(ptr+1) ;
521         for ( ptr=gr_start(temp); *ptr; ptr++ ) switch ( *ptr ) {
522         case STAR :
523                 if ( star ) {
524                         while ( length-- ) gr_add(&varval,*star++|NO_SCAN) ;
525                         break ;
526                 }
527         default :
528                 gr_add(&varval,*ptr) ;
529                 break ;
530         }
531         gr_throw(&temp) ;
532         gr_add(&name,0) ; gr_add(&varval,0) ;
533         setsvar(gr_final(&name),gr_final(&varval)) ;
534 }
535
536 #define ISBLANK(c) ( (c)==SPACE || (c)==TAB )
537
538 unravel(line,action) char *line ; int (*action)() ; {
539         /* Unravel the line, get arguments a la shell */
540         /* each argument is handled to action */
541         /* The input string is left intact */
542         register char *in_c ;
543         register int  token ;
544         enum { BLANK, ARG } state = BLANK ;
545         growstring argum ;
546
547         in_c=line ;
548         for (;;) {
549                 token= *in_c&0377 ;
550                 switch ( state ) {
551                 case BLANK :
552                         if ( token==0 ) break ;
553                         if ( !ISBLANK(token) ) {
554                                 state= ARG ;
555                                 gr_init(&argum) ;
556                                 gr_add(&argum,token&~NO_SCAN) ;
557                         }
558                         break ;
559                 case ARG :
560                         if ( ISBLANK(token) || token==0 ) {
561                                 gr_add(&argum,0) ;
562                                 (*action)(gr_start(argum)) ;
563                                 gr_throw(&argum) ;
564                                 state=BLANK ;
565                         } else {
566                                 gr_add(&argum,token&~NO_SCAN) ;
567                         }
568                         break ;
569                 }
570                 if ( token == 0 ) break ;
571                 in_c++ ;
572         }
573 }
574
575 char *c_rep(string,place,rep) char *string, *place, *rep ; {
576         /* Produce a string in stable storage produced from 'string'
577            with the character at place replaced by rep
578         */
579         growstring name ;
580         register char *nc ;
581         register char *xc ;
582
583         gr_init(&name) ;
584         for ( nc=string ; *nc && nc<place ; nc++ ) {
585                 gr_add(&name,*nc) ;
586         }
587 #ifdef DEBUG
588         if ( *nc==0 ) fatal("Place is not in string") ;
589 #endif
590         for ( xc=rep ; *xc ; xc++ ) gr_add(&name,*xc|NO_SCAN) ;
591         gr_add(&name,0) ;
592         gr_cat(&name,nc+1) ;
593         return gr_final(&name) ;
594 }
595
596 static list_head *curargs ;
597 static list_head *comb_args ;
598
599 addargs(string) char *string ; {
600         register char *temp, *repc ;
601         register list_elem *elem ;
602
603         repc=strindex(string,C_IN) ;
604         if ( repc ) {
605                 /* INPUT FILE TOKEN seen, replace it and scan further */
606                 if ( repc==string && string[1]==0 ) {
607                         if ( in.p_path ) { /* All but combiner */
608                                 l_add(curargs,keeps(in.p_path)) ;
609                         } else {
610                                 scanlist( l_first(*comb_args), elem ) {
611                                         l_add(curargs,
612                                               keeps(p_cont(*elem)->p_path)) ;
613                                 }
614                         }
615                         return ;
616                 }
617                 if ( in.p_path ) { /* Not for the combiners */
618                         temp=c_rep(string,repc,in.p_path) ;
619                         addargs(temp) ;
620                         throws(temp) ;
621                 } else {           /* For the combiners */
622                         scanlist( l_first(*comb_args), elem ) {
623                                 temp=c_rep(string,repc,p_cont(*elem)->p_path);
624                                 addargs(temp) ;
625                                 throws(temp) ;
626                         }
627                 }
628                 return ;
629         }
630         repc=strindex(string,C_OUT) ;
631         if ( repc ) {
632                 /* replace the outfile token as with the infile token */
633 #ifdef DEBUG
634                 if ( !out.p_path ) fatal("missing output filename") ;
635 #endif
636                 temp=c_rep(string,repc,out.p_path) ;
637                 addargs(temp) ;
638                 throws(temp) ;
639                 return ;
640         }
641         temp= keeps(string) ;
642         clr_noscan(temp) ;
643         l_add(curargs,temp) ;
644 }
645
646 getcallargs(phase) register trf *phase ; {
647         growstring arg1, arg2 ;
648
649         arg1= scanvars(phase->t_argd) ;
650 #ifdef DEBUG
651         if ( debug>=3 ) { vprint("\tvars: ") ; prns(gr_start(arg1)) ; }
652 #endif
653         arg2= scanexpr(gr_start(arg1)) ;
654 #ifdef DEBUG
655         if ( debug>=3 ) { vprint("\texpr: ") ; prns(gr_start(arg2)) ; }
656 #endif
657         gr_throw(&arg1) ;
658         curargs= &phase->t_args ;
659         if (phase->t_combine) comb_args = &phase->t_inputs ;
660         unravel( gr_start(arg2), addargs ) ;
661         gr_throw(&arg2) ;
662 }
663
664 discardargs(phase) register trf *phase ; {
665         l_throw(&phase->t_args) ;
666 }