Synchronise with NetBSD: ANSIfy.
[dragonfly.git] / usr.bin / xlint / xlint / xlint.c
1 /*      $NetBSD: xlint.c,v 1.3 1995/10/23 14:29:30 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  * $FreeBSD: src/usr.bin/xlint/xlint/xlint.c,v 1.8 2000/01/14 09:25:31 sheldonh Exp $
34  * $DragonFly: src/usr.bin/xlint/xlint/xlint.c,v 1.8 2004/07/07 12:13:27 asmodai Exp $
35  */
36
37 #include <sys/param.h>
38 #include <sys/wait.h>
39 #include <sys/stat.h>
40 #include <sys/utsname.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <signal.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <fcntl.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <paths.h>
50
51 #include "lint.h"
52 #include "pathnames.h"
53
54 /* directory for temporary files */
55 static  const   char *tmpdir;
56
57 /* path name for cpp output */
58 static  char    *cppout;
59
60 /* files created by 1st pass */
61 static  char    **p1out;
62
63 /* input files for 2nd pass (without libraries) */
64 static  char    **p2in;
65
66 /* library which will be created by 2nd pass */
67 static  char    *p2out;
68
69 /* flags always passed to cpp */
70 static  char    **cppflags;
71
72 /* flags for cpp, controled by sflag/tflag */
73 static  char    **lcppflgs;
74
75 /* flags for lint1 */
76 static  char    **l1flags;
77
78 /* flags for lint2 */
79 static  char    **l2flags;
80
81 /* libraries for lint2 */
82 static  char    **l2libs;
83
84 /* default libraries */
85 static  char    **deflibs;
86
87 /* additional libraries */
88 static  char    **libs;
89
90 /* search path for libraries */
91 static  char    **libsrchpath;
92
93 /* flags */
94 static  int     iflag, oflag, Cflag, sflag, tflag, Fflag, Sflag;
95
96 /* print the commands executed to run the stages of compilation */
97 static  int     Vflag;
98
99 /* filename for oflag */
100 static  char    *outputfn;
101
102 /* reset after first .c source has been processed */
103 static  int     first = 1;
104
105 /*
106  * name of a file which is currently written by a child and should
107  * be removed after abnormal termination of the child
108  */
109 static  const   char *currfn;
110
111
112 static  void    appstrg(char ***, char *);
113 static  void    appcstrg(char ***, const char *);
114 static  void    applst(char ***, char *const *);
115 static  void    freelst(char ***);
116 static  char    *concat2(const char *, const char *);
117 static  char    *concat3(const char *, const char *, const char *);
118 static  void    terminate(int);
119 static  const   char *basename(const char *, int);
120 static  void    appdef(char ***, const char *);
121 static  void    usage(void);
122 static  void    fname(const char *, int);
123 static  void    runchild(const char *, char *const *, const char *);
124 static  void    findlibs(char *const *);
125 static  int     rdok(const char *);
126 static  void    lint2(void);
127 static  void    cat(char *const *, const char *);
128
129 /*
130  * Some functions to deal with lists of strings.
131  * Take care that we get no surprises in case of asyncron signals.
132  */
133 static void
134 appstrg(char ***lstp, char *s)
135 {
136         char    **lst, **olst;
137         int     i;
138
139         olst = *lstp;
140         for (i = 0; olst[i] != NULL; i++) ;
141         lst = xmalloc((i + 2) * sizeof (char *));
142         (void)memcpy(lst, olst, i * sizeof (char *));
143         lst[i] = s;
144         lst[i + 1] = NULL;
145         *lstp = lst;
146 }       
147
148 static void
149 appcstrg(char ***lstp, const char *s)
150 {
151         appstrg(lstp, xstrdup(s));
152 }
153
154 static void
155 applst(char ***destp, char *const *src)
156 {
157         int     i, k;
158         char    **dest, **odest;
159
160         odest = *destp;
161         for (i = 0; odest[i] != NULL; i++) ;
162         for (k = 0; src[k] != NULL; k++) ;
163         dest = xmalloc((i + k + 1) * sizeof (char *));
164         (void)memcpy(dest, odest, i * sizeof (char *));
165         for (k = 0; src[k] != NULL; k++)
166                 dest[i + k] = xstrdup(src[k]);
167         dest[i + k] = NULL;
168         *destp = dest;
169         free(odest);
170 }
171
172 static void
173 freelst(char ***lstp)
174 {
175         char    *s;
176         int     i;
177
178         for (i = 0; (*lstp)[i] != NULL; i++) ;
179         while (i-- > 0) {
180                 s = (*lstp)[i];
181                 (*lstp)[i] = NULL;
182                 free(s);
183         }
184 }
185
186 static char *
187 concat2(const char *s1, const char *s2)
188 {
189         char    *s;
190
191         s = xmalloc(strlen(s1) + strlen(s2) + 1);
192         (void)strcpy(s, s1);
193         (void)strcat(s, s2);
194
195         return (s);
196 }
197
198 static char *
199 concat3(const char *s1, const char *s2, const char *s3)
200 {
201         char    *s;
202
203         s = xmalloc(strlen(s1) + strlen(s2) + strlen(s3) + 1);
204         (void)strcpy(s, s1);
205         (void)strcat(s, s2);
206         (void)strcat(s, s3);
207
208         return (s);
209 }
210
211 /*
212  * Clean up after a signal.
213  */
214 static void
215 terminate(int signo)
216 {
217         int     i;
218
219         if (cppout != NULL)
220                 (void)remove(cppout);
221
222         if (p1out != NULL) {
223                 for (i = 0; p1out[i] != NULL; i++)
224                         (void)remove(p1out[i]);
225         }
226
227         if (p2out != NULL)
228                 (void)remove(p2out);
229
230         if (currfn != NULL)
231                 (void)remove(currfn);
232
233         exit(signo != 0 ? 1 : 0);
234 }
235
236 /*
237  * Returns a pointer to the last component of strg after delim.
238  * Returns strg if the string does not contain delim.
239  */
240 static const char *
241 basename(const char *strg, int delim)
242 {
243         const   char *cp, *cp1, *cp2;
244
245         cp = cp1 = cp2 = strg;
246         while (*cp != '\0') {
247                 if (*cp++ == delim) {
248                         cp2 = cp1;
249                         cp1 = cp;
250                 }
251         }
252         return (*cp1 == '\0' ? cp2 : cp1);
253 }
254
255 static void
256 appdef(char ***lstp, const char *def)
257 {
258         appstrg(lstp, concat2("-D__", def));
259         appstrg(lstp, concat3("-D__", def, "__"));
260 }
261
262 static void
263 usage(void)
264 {
265         (void)printf("lint [-abceghprvxzHFS] [-s|-t] [-i|-nu] [-Dname[=def]] [-Uname]\n");
266         (void)printf("     [-Idirectory] [-Ldirectory] [-llibrary] [-ooutputfile] file ...\n");
267         (void)printf("\n");
268         (void)printf("lint [-abceghprvzHFS] [-s|-t] -Clibrary [-Dname[=def]]\n");
269         (void)printf("     [-Idirectory] [-Uname] file ...\n");
270         terminate(-1);
271 }
272
273 int
274 main(int argc, char *argv[])
275 {
276         int     c;
277         char    flgbuf[3], *tmp, *s;
278         size_t  len;
279         struct  utsname un;
280
281         if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) {
282                 tmpdir = xstrdup(_PATH_TMP);
283         } else {
284                 s = xmalloc(len + 2);
285                 (void)sprintf(s, "%s%s", tmp, tmp[len - 1] == '/' ? "" : "/");
286                 tmpdir = s;
287         }
288
289         cppout = xmalloc(strlen(tmpdir) + sizeof ("lint0.XXXXXX"));
290         (void)sprintf(cppout, "%slint0.XXXXXX", tmpdir);
291         if (mktemp(cppout) == NULL) {
292                 warn("can't make temp");
293                 terminate(-1);
294         }
295
296         p1out = xcalloc(1, sizeof (char *));
297         p2in = xcalloc(1, sizeof (char *));
298         cppflags = xcalloc(1, sizeof (char *));
299         lcppflgs = xcalloc(1, sizeof (char *));
300         l1flags = xcalloc(1, sizeof (char *));
301         l2flags = xcalloc(1, sizeof (char *));
302         l2libs = xcalloc(1, sizeof (char *));
303         deflibs = xcalloc(1, sizeof (char *));
304         libs = xcalloc(1, sizeof (char *));
305         libsrchpath = xcalloc(1, sizeof (char *));
306
307         appcstrg(&cppflags, "-lang-c");
308         appcstrg(&cppflags, "-$");
309         appcstrg(&cppflags, "-C");
310         appcstrg(&cppflags, "-Wcomment");
311 #ifdef __DragonFly__
312         appcstrg(&cppflags, "-D__DragonFly__=" __XSTRING(__DragonFly__));
313 #else
314 #       error "This ain't NetBSD.  You lose!"
315         appcstrg(&cppflags, "-D__NetBSD__");
316 #endif
317         appcstrg(&cppflags, "-Dlint");          /* XXX don't def. with -s */
318         appdef(&cppflags, "lint");
319         appdef(&cppflags, "unix");
320
321         appcstrg(&lcppflgs, "-Wtraditional");
322
323         if (uname(&un) == -1)
324                 err(1, "uname");
325         appdef(&cppflags, un.machine);
326         appstrg(&lcppflgs, concat2("-D", un.machine));
327
328 #ifdef MACHINE_ARCH
329         if (strcmp(un.machine, MACHINE_ARCH) != 0) {
330                 appdef(&cppflags, MACHINE_ARCH);
331                 appstrg(&lcppflgs, concat2("-D", MACHINE_ARCH));
332         }
333 #endif
334         
335         appcstrg(&deflibs, "c");
336
337         if (signal(SIGHUP, terminate) == SIG_IGN)
338                 (void)signal(SIGHUP, SIG_IGN);
339         (void)signal(SIGINT, terminate);
340         (void)signal(SIGQUIT, terminate);
341         (void)signal(SIGTERM, terminate);
342
343         while (argc > optind) {
344
345                 argc -= optind;
346                 argv += optind;
347                 optind = 0;
348
349                 c = getopt(argc, argv, "abceghil:no:prstuvxzC:D:FHI:L:SU:V");
350
351                 switch (c) {
352
353                 case 'a':
354                 case 'b':
355                 case 'c':
356                 case 'e':
357                 case 'g':
358                 case 'r':
359                 case 'v':
360                 case 'z':
361                         (void)sprintf(flgbuf, "-%c", c);
362                         appcstrg(&l1flags, flgbuf);
363                         break;
364
365                 case 'F':
366                         Fflag = 1;
367                         /* FALLTHROUGH */
368                 case 'u':
369                 case 'h':
370                         (void)sprintf(flgbuf, "-%c", c);
371                         appcstrg(&l1flags, flgbuf);
372                         appcstrg(&l2flags, flgbuf);
373                         break;
374
375                 case 'i':
376                         if (Cflag)
377                                 usage();
378                         iflag = 1;
379                         break;
380
381                 case 'n':
382                         freelst(&deflibs);
383                         break;
384
385                 case 'p':
386                         appcstrg(&l1flags, "-p");
387                         appcstrg(&l2flags, "-p");
388                         if (*deflibs != NULL) {
389                                 freelst(&deflibs);
390                                 appcstrg(&deflibs, "c");
391                         }
392                         break;
393
394                 case 's':
395                         if (tflag)
396                                 usage();
397                         freelst(&lcppflgs);
398                         appcstrg(&lcppflgs, "-trigraphs");
399                         appcstrg(&lcppflgs, "-Wtrigraphs");
400                         appcstrg(&lcppflgs, "-pedantic");
401                         appcstrg(&lcppflgs, "-D__STRICT_ANSI__");
402                         appcstrg(&l1flags, "-s");
403                         appcstrg(&l2flags, "-s");
404                         sflag = 1;
405                         break;
406
407                 case 'S':
408                         if (tflag)
409                                 usage();
410                         appcstrg(&l1flags, "-S");
411                         Sflag = 1;
412
413                 case 't':
414                         if (sflag)
415                                 usage();
416                         freelst(&lcppflgs);
417                         appcstrg(&lcppflgs, "-traditional");
418                         appstrg(&lcppflgs, concat2("-D", MACHINE));
419 #ifdef MACHINE_ARCH
420                         appstrg(&lcppflgs, concat2("-D", MACHINE_ARCH));
421 #endif
422                         appcstrg(&l1flags, "-t");
423                         appcstrg(&l2flags, "-t");
424                         tflag = 1;
425                         break;
426
427                 case 'x':
428                         appcstrg(&l2flags, "-x");
429                         break;
430
431                 case 'C':
432                         if (Cflag || oflag || iflag)
433                                 usage();
434                         Cflag = 1;
435                         appstrg(&l2flags, concat2("-C", optarg));
436                         p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg));
437                         (void)sprintf(p2out, "llib-l%s.ln", optarg);
438                         freelst(&deflibs);
439                         break;
440
441                 case 'D':
442                 case 'I':
443                 case 'U':
444                         (void)sprintf(flgbuf, "-%c", c);
445                         appstrg(&cppflags, concat2(flgbuf, optarg));
446                         break;
447
448                 case 'l':
449                         appcstrg(&libs, optarg);
450                         break;
451
452                 case 'o':
453                         if (Cflag || oflag)
454                                 usage();
455                         oflag = 1;
456                         outputfn = xstrdup(optarg);
457                         break;
458
459                 case 'L':
460                         appcstrg(&libsrchpath, optarg);
461                         break;
462
463                 case 'H':
464                         appcstrg(&l2flags, "-H");
465                         break;
466
467                 case 'V':
468                         Vflag = 1;
469                         break;
470
471                 case '?':
472                         usage();
473                         /* NOTREACHED */
474
475                 case -1:
476                         /* filename */
477                         fname(argv[0], argc == 1);
478                         first = 0;
479                         optind = 1;
480                 }
481
482         }
483
484         if (first)
485                 usage();
486
487         if (iflag)
488                 terminate(0);
489
490         if (!oflag) {
491                 if ((s = getenv("LIBDIR")) == NULL || strlen(s) == 0)
492                         s = PATH_LINTLIB;
493                 appcstrg(&libsrchpath, s);
494                 findlibs(libs);
495                 findlibs(deflibs);
496         }
497
498         (void)printf("Lint pass2:\n");
499         lint2();
500
501         if (oflag)
502                 cat(p2in, outputfn);
503
504         if (Cflag)
505                 p2out = NULL;
506
507         terminate(0);
508         /* NOTREACHED */
509         return 0;
510 }
511
512 /*
513  * Read a file name from the command line
514  * and pass it through lint1 if it is a C source.
515  */
516 static void
517 fname(const char *name, int last)
518 {
519         const   char *bn, *suff;
520         char    **args, *ofn, *path;
521         size_t  len;
522
523         bn = basename(name, '/');
524         suff = basename(bn, '.');
525
526         if (strcmp(suff, "ln") == 0) {
527                 /* only for lint2 */
528                 if (!iflag)
529                         appcstrg(&p2in, name);
530                 return;
531         }
532
533         if (strcmp(suff, "c") != 0 &&
534             (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) {
535                 warnx("unknown file type: %s\n", name);
536                 return;
537         }
538
539         if (!iflag || !first || !last)
540                 (void)printf("%s:\n", Fflag ? name : bn);
541
542         /* build the name of the output file of lint1 */
543         if (oflag) {
544                 ofn = outputfn;
545                 outputfn = NULL;
546                 oflag = 0;
547         } else if (iflag) {
548                 ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2));
549                 len = bn == suff ? strlen(bn) : (suff - 1) - bn;
550                 (void)sprintf(ofn, "%.*s", (int)len, bn);
551                 (void)strcat(ofn, ".ln");
552         } else {
553                 ofn = xmalloc(strlen(tmpdir) + sizeof ("lint1.XXXXXX"));
554                 (void)sprintf(ofn, "%slint1.XXXXXX", tmpdir);
555                 if (mktemp(ofn) == NULL) {
556                         warn("can't make temp");
557                         terminate(-1);
558                 }
559         }
560         if (!iflag)
561                 appcstrg(&p1out, ofn);
562
563         args = xcalloc(1, sizeof (char *));
564
565         /* run cpp */
566
567         path = xmalloc(strlen(PATH_USRBIN) + sizeof ("/cpp"));
568         (void)sprintf(path, "%s/cpp", PATH_USRBIN);
569
570         appcstrg(&args, path);
571         applst(&args, cppflags);
572         applst(&args, lcppflgs);
573         appcstrg(&args, name);
574         appcstrg(&args, cppout);
575
576         runchild(path, args, cppout);
577         free(path);
578         freelst(&args);
579
580         /* run lint1 */
581
582         path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1"));
583         (void)sprintf(path, "%s/lint1", PATH_LIBEXEC);
584
585         appcstrg(&args, path);
586         applst(&args, l1flags);
587         appcstrg(&args, cppout);
588         appcstrg(&args, ofn);
589
590         runchild(path, args, ofn);
591         free(path);
592         freelst(&args);
593
594         appcstrg(&p2in, ofn);
595         free(ofn);
596
597         free(args);
598 }
599
600 static void
601 runchild(const char *path, char *const *args, const char *crfn)
602 {
603         int     status, rv, signo, i;
604
605         if (Vflag) {
606                 for (i = 0; args[i] != NULL; i++)
607                         (void)printf("%s ", args[i]);
608                 (void)printf("\n");
609         }
610
611         currfn = crfn;
612
613         (void)fflush(stdout);
614
615         switch (fork()) {
616         case -1:
617                 warn("cannot fork");
618                 terminate(-1);
619                 /* NOTREACHED */
620         default:
621                 /* parent */
622                 break;
623         case 0:
624                 /* child */
625                 (void)execv(path, args);
626                 warn("cannot exec %s", path);
627                 exit(1);
628                 /* NOTREACHED */
629         }
630
631         while ((rv = wait(&status)) == -1 && errno == EINTR) ;
632         if (rv == -1) {
633                 warn("wait");
634                 terminate(-1);
635         }
636         if (WIFSIGNALED(status)) {
637                 signo = WTERMSIG(status);
638                 warnx("%s got SIG%s", path, sys_signame[signo]);
639                 terminate(-1);
640         }
641         if (WEXITSTATUS(status) != 0)
642                 terminate(-1);
643         currfn = NULL;
644 }
645
646 static void
647 findlibs(char *const *liblst)
648 {
649         int     i, k;
650         const   char *lib, *path;
651         char    *lfn;
652         size_t  len;
653
654         lfn = NULL;
655
656         for (i = 0; (lib = liblst[i]) != NULL; i++) {
657                 for (k = 0; (path = libsrchpath[k]) != NULL; k++) {
658                         len = strlen(path) + strlen(lib);
659                         lfn = xrealloc(lfn, len + sizeof ("/llib-l.ln"));
660                         (void)sprintf(lfn, "%s/llib-l%s.ln", path, lib);
661                         if (rdok(lfn))
662                                 break;
663                         lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln"));
664                         (void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib);
665                         if (rdok(lfn))
666                                 break;
667                 }
668                 if (path != NULL) {
669                         appstrg(&l2libs, concat2("-l", lfn));
670                 } else {
671                         warnx("cannot find llib-l%s.ln", lib);
672                 }
673         }
674
675         free(lfn);
676 }
677
678 static int
679 rdok(const char *path)
680 {
681         struct  stat sbuf;
682
683         if (stat(path, &sbuf) == -1)
684                 return (0);
685         if ((sbuf.st_mode & S_IFMT) != S_IFREG)
686                 return (0);
687         if (access(path, R_OK) == -1)
688                 return (0);
689         return (1);
690 }
691
692 static void
693 lint2(void)
694 {
695         char    *path, **args;
696
697         args = xcalloc(1, sizeof (char *));
698
699         path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2"));
700         (void)sprintf(path, "%s/lint2", PATH_LIBEXEC);
701         
702         appcstrg(&args, path);
703         applst(&args, l2flags);
704         applst(&args, l2libs);
705         applst(&args, p2in);
706
707         runchild(path, args, p2out);
708         free(path);
709         freelst(&args);
710         free(args);
711 }
712
713 static void
714 cat(char *const *srcs, const char *dest)
715 {
716         int     ifd, ofd, i;
717         char    *src, *buf;
718         ssize_t rlen;
719
720         if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
721                 warn("cannot open %s", dest);
722                 terminate(-1);
723         }
724
725         buf = xmalloc(MBLKSIZ);
726
727         for (i = 0; (src = srcs[i]) != NULL; i++) {
728                 if ((ifd = open(src, O_RDONLY)) == -1) {
729                         free(buf);
730                         warn("cannot open %s", src);
731                         terminate(-1);
732                 }
733                 do {
734                         if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) {
735                                 free(buf);
736                                 warn("read error on %s", src);
737                                 terminate(-1);
738                         }
739                         if (write(ofd, buf, (size_t)rlen) == -1) {
740                                 free(buf);
741                                 warn("write error on %s", dest);
742                                 terminate(-1);
743                         }
744                 } while (rlen == MBLKSIZ);
745                 (void)close(ifd);
746         }
747         (void)close(ofd);
748         free(buf);
749 }
750