Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / xlint / lint1 / emit1.c
1 /*      $NetBSD: emit1.c,v 1.4 1995/10/02 17:21:28 jpo Exp $    */
2
3 /*
4  * Copyright (c) 1994, 1995 Jochen Pohl
5  * All Rights Reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Jochen Pohl for
18  *      The NetBSD Project.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 static char rcsid[] = "$NetBSD: emit1.c,v 1.4 1995/10/02 17:21:28 jpo Exp $";
36 #endif
37
38 #include <ctype.h>
39
40 #include "lint1.h"
41
42 static  void    outtt __P((sym_t *, sym_t *));
43 static  void    outfstrg __P((strg_t *));
44
45 /*
46  * Write type into the output buffer.
47  * The type is written as a sequence of substrings, each of which describes a
48  * node of type type_t
49  * a node is coded as follows:
50  *      char                    C
51  *      signed char             s C
52  *      unsigned char           u C
53  *      short                   S
54  *      unsigned short          u S
55  *      int                     I
56  *      unsigned int            u I
57  *      long                    L
58  *      unsigned long           u L
59  *      long long               Q
60  *      unsigned long long      u Q
61  *      float                   s D
62  *      double                  D
63  *      long double             l D
64  *      void                    V
65  *      *                       P
66  *      [n]                     A n
67  *      ()                      F
68  *      (void)                  F 0
69  *      (n arguments)           F n arg1 arg2 ... argn
70  *      (n arguments, ...)      F n arg1 arg2 ... argn-1 E
71  *      (a, b, c, ...)          f n arg1 arg2 ...
72  *      enum tag                e T tag_or_typename
73  *      struct tag              s T tag_or_typename
74  *      union tag               u T tag_or_typename
75  *
76  *      tag_or_typename         0                       no tag or type name
77  *                              1 n tag                 Tag
78  *                              2 n typename            only type name
79  *
80  * spaces are only for better readability
81  * additionaly it is possible to prepend the characters 'c' (for const)
82  * and 'v' (for volatile)
83  */
84 void
85 outtype(tp)
86         type_t  *tp;
87 {
88         int     t, s, na;
89         sym_t   *arg;
90         tspec_t ts;
91
92         while (tp != NULL) {
93                 if ((ts = tp->t_tspec) == INT && tp->t_isenum)
94                         ts = ENUM;
95                 switch (ts) {
96                 case CHAR:      t = 'C';        s = '\0';       break;
97                 case SCHAR:     t = 'C';        s = 's';        break;
98                 case UCHAR:     t = 'C';        s = 'u';        break;
99                 case SHORT:     t = 'S';        s = '\0';       break;
100                 case USHORT:    t = 'S';        s = 'u';        break;
101                 case INT:       t = 'I';        s = '\0';       break;
102                 case UINT:      t = 'I';        s = 'u';        break;
103                 case LONG:      t = 'L';        s = '\0';       break;
104                 case ULONG:     t = 'L';        s = 'u';        break;
105                 case QUAD:      t = 'Q';        s = '\0';       break;
106                 case UQUAD:     t = 'Q';        s = 'u';        break;
107                 case FLOAT:     t = 'D';        s = 's';        break;
108                 case DOUBLE:    t = 'D';        s = '\0';       break;
109                 case LDOUBLE:   t = 'D';        s = 'l';        break;
110                 case VOID:      t = 'V';        s = '\0';       break;
111                 case PTR:       t = 'P';        s = '\0';       break;
112                 case ARRAY:     t = 'A';        s = '\0';       break;
113                 case FUNC:      t = 'F';        s = '\0';       break;
114                 case ENUM:      t = 'T';        s = 'e';        break;
115                 case STRUCT:    t = 'T';        s = 's';        break;
116                 case UNION:     t = 'T';        s = 'u';        break;
117                 default:
118                         lerror("outtyp() 1");
119                 }
120                 if (tp->t_const)
121                         outchar('c');
122                 if (tp->t_volatile)
123                         outchar('v');
124                 if (s != '\0')
125                         outchar(s);
126                 outchar(t);
127                 if (ts == ARRAY) {
128                         outint(tp->t_dim);
129                 } else if (ts == ENUM) {
130                         outtt(tp->t_enum->etag, tp->t_enum->etdef);
131                 } else if (ts == STRUCT || ts == UNION) {
132                         outtt(tp->t_str->stag, tp->t_str->stdef);
133                 } else if (ts == FUNC && tp->t_proto) {
134                         na = 0;
135                         for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt)
136                                         na++;
137                         if (tp->t_vararg)
138                                 na++;
139                         outint(na);
140                         for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt)
141                                 outtype(arg->s_type);
142                         if (tp->t_vararg)
143                                 outchar('E');
144                 }
145                 tp = tp->t_subt;
146         }
147 }
148
149 /*
150  * type to string
151  * used for debugging output
152  *
153  * it uses its own output buffer for conversion
154  */
155 const char *
156 ttos(tp)
157         type_t  *tp;
158 {
159         static  ob_t    tob;
160         ob_t    tmp;
161
162         if (tob.o_buf == NULL) {
163                 tob.o_len = 64;
164                 tob.o_buf = tob.o_nxt = xmalloc(tob.o_len);
165                 tob.o_end = tob.o_buf + tob.o_len;
166         }
167
168         tmp = ob;
169         ob = tob;
170         ob.o_nxt = ob.o_buf;
171         outtype(tp);
172         outchar('\0');
173         tob = ob;
174         ob = tmp;
175
176         return (tob.o_buf);
177 }
178
179 /*
180  * write the name of a tag or typename
181  *
182  * if the tag is named, the name of the
183  * tag is written, otherwise, if a typename exists which
184  * refers to this tag, this typename is written
185  */
186 static void
187 outtt(tag, tdef)
188         sym_t   *tag, *tdef;
189 {
190         if (tag->s_name != unnamed) {
191                 outint(1);
192                 outname(tag->s_name);
193         } else if (tdef != NULL) {
194                 outint(2);
195                 outname(tdef->s_name);
196         } else {
197                 outint(0);
198         }
199 }
200
201 /*
202  * write information about an global declared/defined symbol
203  * with storage class extern
204  *
205  * informations about function definitions are written in outfdef(),
206  * not here
207  */
208 void
209 outsym(sym, sc, def)
210         sym_t   *sym;
211         scl_t   sc;
212         def_t   def;
213 {
214         /*
215          * Static function declarations must also be written to the output
216          * file. Compatibility of function declarations (for both static
217          * and extern functions) must be checked in lint2. Lint1 can't do
218          * this, especially not, if functions are declared at block level
219          * before their first declaration at level 0.
220          */
221         if (sc != EXTERN && !(sc == STATIC && sym->s_type->t_tspec == FUNC))
222                 return;
223
224         /* reset buffer */
225         outclr();
226
227         /*
228          * line number of .c source, 'd' for declaration, Id of current
229          * source (.c or .h), and line in current source.
230          */
231         outint(csrc_pos.p_line);
232         outchar('d');
233         outint(getfnid(sym->s_dpos.p_file));
234         outchar('.');
235         outint(sym->s_dpos.p_line);
236
237         /* flags */
238
239         switch (def) {
240         case DEF:
241                 /* defined */
242                 outchar('d');
243                 break;
244         case TDEF:
245                 /* tentative defined */
246                 outchar('t');
247                 break;
248         case DECL:
249                 /* declared */
250                 outchar('e');
251                 break;
252         default:
253                 lerror("outsym() 2");
254         }
255         if (llibflg && def != DECL) {
256                 /*
257                  * mark it as used so we get no warnings from lint2 about
258                  * unused symbols in libraries.
259                  */
260                 outchar('u');
261         }
262
263         if (sc == STATIC)
264                 outchar('s');
265
266         /* name of the symbol */
267         outname(sym->s_name);
268
269         /* type of the symbol */
270         outtype(sym->s_type);
271 }
272
273 /*
274  * write information about function definition
275  *
276  * this is also done for static functions so we are able to check if
277  * they are called with proper argument types
278  */
279 void
280 outfdef(fsym, posp, rval, osdef, args)
281         sym_t   *fsym, *args;
282         pos_t   *posp;
283         int     rval, osdef;
284 {
285         int     narg;
286         sym_t   *arg;
287
288         /* reset the buffer */
289         outclr();
290
291         /*
292          * line number of .c source, 'd' for declaration, Id of current
293          * source (.c or .h), and line in current source
294          *
295          * we are already at the end of the function. If we are in the
296          * .c source, posp->p_line is correct, otherwise csrc_pos.p_line
297          * (for functions defined in header files).
298          */
299         if (posp->p_file == csrc_pos.p_file) {
300                 outint(posp->p_line);
301         } else {
302                 outint(csrc_pos.p_line);
303         }
304         outchar('d');
305         outint(getfnid(posp->p_file));
306         outchar('.');
307         outint(posp->p_line);
308
309         /* flags */
310
311         /* both SCANFLIKE and PRINTFLIKE imply VARARGS */
312         if (prflstrg != -1) {
313                 nvararg = prflstrg;
314         } else if (scflstrg != -1) {
315                 nvararg = scflstrg;
316         }
317
318         if (nvararg != -1) {
319                 outchar('v');
320                 outint(nvararg);
321         }
322         if (scflstrg != -1) {
323                 outchar('S');
324                 outint(scflstrg);
325         }
326         if (prflstrg != -1) {
327                 outchar('P');
328                 outint(prflstrg);
329         }
330         nvararg = prflstrg = scflstrg = -1;
331
332         outchar('d');
333
334         if (rval)
335                 /* has return value */
336                 outchar('r');
337
338         if (llibflg)
339                 /*
340                  * mark it as used so lint2 does not complain about
341                  * unused symbols in libraries
342                  */
343                 outchar('u');
344
345         if (osdef)
346                 /* old style function definition */
347                 outchar('o');
348
349         if (fsym->s_scl == STATIC)
350                 outchar('s');
351
352         /* name of function */
353         outname(fsym->s_name);
354
355         /* argument types and return value */
356         if (osdef) {
357                 narg = 0;
358                 for (arg = args; arg != NULL; arg = arg->s_nxt)
359                         narg++;
360                 outchar('f');
361                 outint(narg);
362                 for (arg = args; arg != NULL; arg = arg->s_nxt)
363                         outtype(arg->s_type);
364                 outtype(fsym->s_type->t_subt);
365         } else {
366                 outtype(fsym->s_type);
367         }
368 }
369
370 /*
371  * write out all information necessary for lint2 to check function
372  * calls
373  *
374  * rvused is set if the return value is used (asigned to a variable)
375  * rvdisc is set if the return value is not used and not ignored
376  * (casted to void)
377  */
378 void
379 outcall(tn, rvused, rvdisc)
380         tnode_t *tn;
381         int     rvused, rvdisc;
382 {
383         tnode_t *args, *arg;
384         int     narg, n, i;
385         quad_t  q;
386         tspec_t t;
387
388         /* reset buffer */
389         outclr();
390
391         /*
392          * line number of .c source, 'c' for function call, Id of current
393          * source (.c or .h), and line in current source
394          */
395         outint(csrc_pos.p_line);
396         outchar('c');
397         outint(getfnid(curr_pos.p_file));
398         outchar('.');
399         outint(curr_pos.p_line);
400
401         /*
402          * flags; 'u' and 'i' must be last to make sure a letter
403          * is between the numeric argument of a flag and the name of
404          * the function
405          */
406         narg = 0;
407         args = tn->tn_right;
408         for (arg = args; arg != NULL; arg = arg->tn_right)
409                 narg++;
410         /* informations about arguments */
411         for (n = 1; n <= narg; n++) {
412                 /* the last argument is the top one in the tree */
413                 for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) ;
414                 arg = arg->tn_left;
415                 if (arg->tn_op == CON) {
416                         if (isityp(t = arg->tn_type->t_tspec)) {
417                                 /*
418                                  * XXX it would probably be better to
419                                  * explizitly test the sign
420                                  */
421                                 if ((q = arg->tn_val->v_quad) == 0) {
422                                         /* zero constant */
423                                         outchar('z');
424                                 } else if (msb(q, t, 0) == 0) {
425                                         /* positive if casted to signed */
426                                         outchar('p');
427                                 } else {
428                                         /* negative if casted to signed */
429                                         outchar('n');
430                                 }
431                                 outint(n);
432                         }
433                 } else if (arg->tn_op == AMPER &&
434                            arg->tn_left->tn_op == STRING &&
435                            arg->tn_left->tn_strg->st_tspec == CHAR) {
436                         /* constant string, write all format specifiers */
437                         outchar('s');
438                         outint(n);
439                         outfstrg(arg->tn_left->tn_strg);
440                 }
441
442         }
443         /* return value discarded/used/ignored */
444         outchar(rvdisc ? 'd' : (rvused ? 'u' : 'i'));
445
446         /* name of the called function */
447         outname(tn->tn_left->tn_left->tn_sym->s_name);
448
449         /* types of arguments */
450         outchar('f');
451         outint(narg);
452         for (n = 1; n <= narg; n++) {
453                 /* the last argument is the top one in the tree */
454                 for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) ;
455                 outtype(arg->tn_left->tn_type);
456         }
457         /* expected type of return value */
458         outtype(tn->tn_type);
459 }
460
461 /*
462  * extracts potential format specifiers for printf() and scanf() and
463  * writes them, enclosed in "" and qouted if necessary, to the output buffer
464  */
465 static void
466 outfstrg(strg)
467         strg_t  *strg;
468 {
469         int     c, oc, first;
470         u_char  *cp;
471
472         if (strg->st_tspec != CHAR)
473                 lerror("outfstrg() 1");
474
475         cp = strg->st_cp;
476
477         outchar('"');
478
479         c = *cp++;
480
481         while (c != '\0') {
482
483                 if (c != '%') {
484                         c = *cp++;
485                         continue;
486                 }
487
488                 outqchar('%');
489                 c = *cp++;
490
491                 /* flags for printf and scanf and *-fieldwidth for printf */
492                 while (c != '\0' && (c == '-' || c == '+' || c == ' ' ||
493                                      c == '#' || c == '0' || c == '*')) {
494                         outqchar(c);
495                         c = *cp++;
496                 }
497
498                 /* numeric field width */
499                 while (c != '\0' && isdigit(c)) {
500                         outqchar(c);
501                         c = *cp++;
502                 }
503
504                 /* precision for printf */
505                 if (c == '.') {
506                         outqchar(c);
507                         if ((c = *cp++) == '*') {
508                                 outqchar(c);
509                                 c = *cp++;
510                         } else {
511                                 while (c != '\0' && isdigit(c)) {
512                                         outqchar(c);
513                                         c = *cp++;
514                                 }
515                         }
516                 }
517
518                 /* h, l, L and q flags fpr printf and scanf */
519                 if (c == 'h' || c == 'l' || c == 'L' || c == 'q') {
520                         outqchar(c);
521                         c = *cp++;
522                 }
523
524                 /*
525                  * The last character. It is always written so we can detect
526                  * invalid format specifiers.
527                  */
528                 if (c != '\0') {
529                         outqchar(c);
530                         oc = c;
531                         c = *cp++;
532                         /*
533                          * handle [ for scanf. [-] means that a minus sign
534                          * was found at an undefined position.
535                          */
536                         if (oc == '[') {
537                                 if (c == '^')
538                                         c = *cp++;
539                                 if (c == ']')
540                                         c = *cp++;
541                                 first = 1;
542                                 while (c != '\0' && c != ']') {
543                                         if (c == '-') {
544                                                 if (!first && *cp != ']')
545                                                         outqchar(c);
546                                         }
547                                         first = 0;
548                                         c = *cp++;
549                                 }
550                                 if (c == ']') {
551                                         outqchar(c);
552                                         c = *cp++;
553                                 }
554                         }
555                 }
556
557         }
558
559         outchar('"');
560 }
561
562 /*
563  * writes a record if sym was used
564  */
565 void
566 outusg(sym)
567         sym_t   *sym;
568 {
569         /* reset buffer */
570         outclr();
571
572         /*
573          * line number of .c source, 'u' for used, Id of current
574          * source (.c or .h), and line in current source
575          */
576         outint(csrc_pos.p_line);
577         outchar('u');
578         outint(getfnid(curr_pos.p_file));
579         outchar('.');
580         outint(curr_pos.p_line);
581
582         /* necessary to delimit both numbers */
583         outchar('x');
584
585         /* Den Namen des Symbols ausgeben */
586         outname(sym->s_name);
587 }