1/* ************************************************************************ *\
2 *                                                                          *
3 * File:        md.c                                                        *
4 *                                                                          *
5 *      Updates makefiles from the .n dependency files generated by the     *
6 *      -MD option to "cc" (and "cpp").                                     *
7 *                                                                          *
8 * Abstract:                                                                *
9 *                                                                          *
10 *      Basically, "md" does two things:                                    *
11 *      1) It processes the raw dependency files produced by the cpp -MD     *
12 *         option.  There is one line in the file for every #include        *
13 *         encountered, but there are repeats and patterns like             *
14 *         .../dir1/../dir2 appear which should reduce to .../dir2          *
15 *         Md canonicalizes and flushes repeats from the dependency         *
16 *         list.  It also sorts the file names and "fills" them to a 78     *
17 *         character line.                                                  *
18 *      2) Md also updates the makefile directly with the dependency        *
19 *         information, so the .d file can be thrown away (-- -d option)    *
20 *         This is done to save space.  Md assumes that dependency          *
21 *         information in the makefile is sorted by .o file name and it     *
22 *         procedes to merge in (add/or replace [as appropriate])  the new  *
23 *         dependency lines that it has generated.  For time effeciency,    *
24 *         Md assumes that any .d files it is given that were created       *
25 *         before the creation date of the "makefile" were processed        *
26 *         already.  It ignores them unless the force flag (-f) is given.   *
27 *                                                                          *
28 * Arguments:                                                               *
29 *                                                                          *
30 *      -d      delete the .d file after it is processed                    *
31 *      -f      force an update of the dependencies in the makefile         *
32 *              even though the makefile is more recent than the .n file    *
33 *              (This implies that md has been run already.)                *
34 *      -m      specify the makefile to be upgraded.  The defaults are      *
35 *              "makefile" and then "Makefile".                             *
36 *      -u      like -m above, but the file will be created if necessary    *
37 *      -o      specify an output file for the dependencies other than a    *
38 *              makefile                                                    *
39 *      -v      set the verbose flag                                        *
40 *      -x      expunge old dependency info from makefile                   *
41 *      -D      subswitch for debugging.  can be followed by any of         *
42 *              "c", "d", "m", "o", "t", "D" meaning:                       *
43 *              c       show file contents                                  *
44 *              d       show new dependency crunching                       *
45 *              m       show generation of makefile                         *
46 *              o       show files being opened                             *
47 *              t       show time comparisons                               *
48 *              D       show very low level debugging                       *
49 *                                                                          *
50 * Author:      Robert V. Baron                                             *
51 *              Copyright (c) 1986 by Robert V. Baron                       *
52 *                                                                          *
53 * HISTORY                                                                  *
54 * 29-Apr-87  Robert Baron (rvb) at Carnegie-Mellon University
55 *      If specified -u file does not exist, assume it is empty and
56 *      generate one.  As a sanity check, it must be possible to create
57 *      the output file.
58 *      Also, generalized fix below to handle any case of . as a
59 *      file name.
60 *
61 * 25-Mar-87  Mary Thompson (mrt) at Carnegie Mellon
62 *      Fixed up pathnamecanonicalization to recognize .// and
63 *      drop the second / as well. mmax cpp generates this form.
64 *
65 *  6-Jan-87  Robert Baron (rvb) at Carnegie-Mellon University
66 *      Fixed up pathname canonicalization to that ../../, etc would be
67 *      handled correctly.
68 *      Also made "force" on by default.
69 *
70 * 16-Mar-86  Robert Baron (rvb) at Carnegie-Mellon University
71 *              Created 4/16/86                                             *
72 *                                                                          *
73\* ************************************************************************ */
74
75
76#include <sys/types.h>
77#include <sys/stat.h>
78#include <stdio.h>
79#include <stdlib.h>
80#include <string.h>
81
82#define LINESIZE 65536  //  NeXT_MOD
83
84#define OUTLINELEN 79
85#define IObuffer 50000
86#define SALUTATION "# Dependencies for File:"
87#define SALUTATIONLEN (sizeof SALUTATION - 1)
88#define OLDSALUTATION "# DO NOT DELETE THIS LINE"
89#define OLDSALUTATIONLEN (sizeof OLDSALUTATION - 1)
90
91char file_array[IObuffer];      /* read file and store crunched names */
92char dep_line[LINESIZE];        /* line being processed */
93char dot_o[LINESIZE];           /* <foo.o>: prefix */
94char *path_component[100];      /* stores components for a path while being
95                                   crunched */
96
97struct dep {                    /* stores paths that a file depends on */
98        int len;
99        char *str;
100} dep_files[1000];
101int dep_file_index;
102
103qsort_strcmp(a, b)
104struct dep *a, *b;
105{
106extern int strcmp();
107        return strcmp(a->str, b->str);
108}
109
110char *outfile = (char *) 0;     /* generate dependency file */
111FILE *out;
112
113char *makefile = (char *) 0;    /* user supplied makefile name */
114char *real_mak_name;            /* actual makefile name (if not supplied) */
115char shadow_mak_name[LINESIZE]; /* changes done here then renamed */
116FILE *mak;                      /* for reading makefile */
117FILE *makout;                   /* for writing shadow */
118char makbuf[LINESIZE];          /* one line buffer for makefile */
119struct stat makstat;            /* stat of makefile for time comparisons */
120int mak_eof = 0;                        /* eof seen on makefile */
121FILE *find_mak(), *temp_mak();
122
123int delete = 0;                 /* -d delete dependency file */
124int debug = 0;
125int     D_contents = 0;         /* print file contents */
126int     D_depend = 0;           /* print dependency processing info */
127int     D_make = 0;             /* print makefile processing info */
128int     D_open = 0;             /* print after succesful open */
129int     D_time = 0;             /* print time comparison info */
130int force = 1;                  /* always update dependency info */
131int update = 0;                 /* it's ok if the -m file does not exist */
132int verbose = 0;                /* tell me something */
133int expunge = 0;                /* first flush dependency stuff from makefile */
134
135
136char *name;
137
138static void scan_mak(FILE *, FILE *, char *);
139static void finish_mak(FILE *, FILE *);
140
141main(argc,argv)
142register char **argv;
143{
144int size;
145
146        name = *argv;
147        {register char *cp =name;
148                while (*cp) if (*cp++ == '/') name = cp;
149        }
150
151        for ( argv++ ; --argc ; argv++ ) { register char *token = *argv;
152                if (*token++ != '-' || !*token)
153                        break;
154                else { register int flag;
155                        for ( ; flag = *token++ ; ) {
156                                switch (flag) {
157                                case 'd':
158                                        delete++;
159                                        break;
160                                case 'f':
161                                        force++;
162                                        break;
163                                case 'u':
164                                        update++;
165                                case 'm':
166                                        makefile = *++argv;
167                                        if (--argc < 0) goto usage;
168                                        break;
169                                case 'o':
170                                        outfile = *++argv;
171                                        if (--argc < 0) goto usage;
172                                        break;
173                                case 'v':
174                                        verbose++;
175                                        break;
176                                case 'x':
177                                        expunge++;
178                                        break;
179                                case 'D':
180                                        for ( ; flag = *token++ ; )
181                                                switch (flag) {
182                                                case 'c':
183                                                        D_contents++;
184                                                        break;
185                                                case 'd':
186                                                        D_depend++;
187                                                        break;
188                                                case 'm':
189                                                        D_make++;
190                                                        break;
191                                                case 'o':
192                                                        D_open++;
193                                                        break;
194                                                case 't':
195                                                        D_time++;
196                                                        break;
197                                                case 'D':
198                                                        debug++;
199                                                        break;
200                                                default:
201                                                        goto letters;
202                                                }
203                                        goto newtoken;
204                                default:
205                                        goto usage;
206                                }
207letters: ;
208                        }
209                }
210newtoken: ;
211        }
212
213        if (!expunge && argc < 1) goto usage;
214        if ((int) outfile && (int) makefile)    /* not both */
215                goto usage;
216
217        if ((int) outfile) {
218                /*
219                 * NeXT_MOD, For SGS stuff, in case still linked to master version
220                 */
221                unlink(outfile);
222
223                if ((out = fopen(outfile, "w")) == NULL) {
224                        fprintf(stderr, "%s: outfile = \"%s\" ", name, outfile);
225                        perror("fopen");
226                        fflush(stdout), fflush(stderr);
227                        exit(1);
228                } else if (D_open)
229                        printf("%s: opened outfile \"%s\"\n", name, outfile);
230        } else if (mak = find_mak(makefile)) {
231                makout = temp_mak();
232                out = makout;
233                if (expunge)
234                        expunge_mak(mak, makout);
235                else
236                        skip_mak(mak, makout);
237        } else if (mak_eof &&  /* non existent file == mt file */
238                   (int)(makout = temp_mak())) { /* but we need to be able */
239                out = makout;                    /* to write here */
240        } else if (makefile) {
241                fprintf(stderr, "%s: makefile \"%s\" can not be opened or stat'ed\n",
242                        name, makefile);
243                exit(2);
244        }
245
246        for (; argc--; argv++) {
247                dep_file_index = 0;
248
249                if (size = read_dep(*argv)) {
250
251                        save_dot_o();
252                        if (D_depend) printf("%s: dot_o = \"%s\"\n", name, dot_o);
253
254                        parse_dep();
255                        if (mak) scan_mak(mak, makout, dot_o);
256                        if (out) output_dep(out);
257
258                        if (delete)
259                                unlink(*argv);
260                }
261        }
262
263        if (mak) finish_mak(mak, makout);
264        rename(shadow_mak_name, real_mak_name);
265        exit(0);
266usage:
267        fprintf(stderr, "usage: md -f -Dcdmot -m makefile -o outputfile -v <file1> ... <filen>\n");
268        exit(1);
269}
270
271
272read_dep(file)
273register char *file;
274{
275register int fd;
276register int size;
277struct stat statbuf;
278
279        if ((fd = open(file, 0)) < 0) {
280                fprintf(stderr, "%s: file = \"%s\" ", name, file);
281                perror("open");
282                fflush(stdout), fflush(stderr);
283                return 0;
284        }
285        if (D_open)
286                printf("%s: opened dependency file \"%s\"\n", name, file);
287
288        if (fstat(fd, &statbuf) < 0) {
289                fprintf(stderr, "%s: file = \"%s\" ", name, file);
290                perror("stat");
291                fflush(stdout), fflush(stderr);
292                goto out;
293        }
294        switch(statbuf.st_mode & S_IFMT) {
295        case S_IFREG:
296                if (D_time)
297                        printf("%s: file time = %d\n", name, statbuf.st_mtime);
298
299                if (statbuf.st_size > IObuffer) {
300                        fprintf(stderr, "%s: file \"%s\" tooo big for IObuffer\n",
301                                name, file);
302                        goto out;
303                } else if (force)
304                        break;
305                else if ((int) mak && statbuf.st_mtime < makstat.st_mtime) {
306                        if (verbose || D_time)
307                                fprintf(stderr, "%s: skipping \"%s\" %d < %d \"%s\"\n",
308                                        name, file, statbuf.st_mtime, makstat.st_mtime,
309                                        real_mak_name);
310                        goto out;
311                } else /* >=   =>ok */
312                        break;
313        case S_IFDIR:
314        case S_IFLNK:
315        case S_IFCHR:
316        case S_IFBLK:
317        case S_IFSOCK:
318        default:
319                fprintf(stderr, "%s: bad mode: 0%o on \"%s\"\n",
320                        name, statbuf.st_mode, file);
321                fflush(stdout), fflush(stderr);
322                goto out;
323        }
324
325        if ((size = read(fd, file_array, sizeof (file_array))) < 0) {
326                fprintf(stderr, "%s: file = \"%s\" ", name, file);
327                perror("read");
328                fflush(stdout), fflush(stderr);
329                goto out;
330        }
331        file_array[size] = 0;
332
333        if (close(fd) < 0) {
334                fprintf(stderr, "%s: file = \"%s\" ", name, file);
335                perror("close");
336                fflush(stdout), fflush(stderr);
337                return 0;
338        }
339
340        if (D_depend && D_contents)
341                printf("file_array: \"%s\"\n", file_array);
342        return size;
343out: ;
344        close(fd);
345        return 0;
346}
347
348save_dot_o()
349{
350register char *cp = file_array;
351register char *svp = dot_o;
352register int c;
353
354        while ((*svp++ = (c = *cp++)) && c != ':');
355        *svp = 0;
356}
357
358parse_dep()
359{
360register char *lp = file_array;
361register int c;
362
363        while (*lp) {register char *tlp = lp;
364                     register char *cp = dep_line;
365                     register int i = 0;
366                     int abspath = 0;
367                     char oldc;
368                     char *oldcp;
369
370                        /* get a line to process */
371                while ((c = *lp++) && c != '\n')
372                  {
373                    if (c == '\\')
374                      lp++;             /* skip backslash newline */
375                    else
376                      *cp++ = c;
377                  }
378                  if (!c)
379                    break;
380                *cp = 0;
381                cp = dep_line;
382                lp[-1] = 0;
383                        /* skip .o file name */
384                while ((c = *cp++) && c != ':'); if (!c) continue;
385next_filename:
386                i = 0;
387                abspath = 0;
388                while ((c = *cp) && (c == ' ' || c == '\t')) cp++; if (!c) continue;
389
390                        /* canonicalization processing */
391
392                                        /* initial / is remembered */
393                if (c == '/')
394                        abspath++;
395
396                while (c && c != ' ' && c != '\t') {
397                        if (D_depend) printf("i = %d going \"%s\"\n", i, cp);
398                                        /* kill \'s */
399                        while ((c = *cp) && c == '/') cp++; if (!c) break;
400                        path_component[i] = cp;
401                                        /* swallow chars till next / or null */
402                        while ((c = *cp++) && c != '/' && c != ' ' && c != '\t');
403                        if (c) cp[-1]=0;/* end component C style */
404
405                                        /* ignore . */;
406                        if (!strcmp(path_component[i], "."))
407                                ;       /* if "component" != .. */
408                        else            /* don't reduce /component/.. to nothing */
409                                i++;    /* there could be symbolic links! */
410                }
411                        /* reassemble components */
412                oldc = c;               /* save c */
413                oldcp = cp;             /* save cp */
414                cp = tlp;               /* overwrite line in buffer */
415                if (abspath)
416                        *cp++ = '/';
417                for (c=0; c<i; c++) {register char *ccp = path_component[c];
418                        while (*cp++ = *ccp++);
419                        *--cp = '/';
420                        cp++;
421                }
422                *--cp = 0;
423
424                c=dep_file_index++;
425                dep_files[c].str = tlp;
426                dep_files[c].len = cp - tlp;
427                if (D_depend)
428                        printf("%s: dep_file[%d] = \"%s\" Len %d\n",
429                                name, dep_file_index - 1, tlp, cp - tlp);
430                tlp = cp + 1;
431                if (oldc)
432                  {
433                     cp = oldcp;
434                     goto next_filename;
435                  }
436        }
437}
438
439output_dep(out)
440FILE *out;
441{
442register int j;
443register int size = 1000;
444register int dot_o_len = strlen(dot_o);
445register struct dep *dp = dep_files;
446int written = 0;
447
448        if (D_depend && debug)
449                for(j = 0; j < dep_file_index; j++) {
450                        printf("dep_files[%d] = %s\n", j, dep_files[j].str);
451                }
452
453        qsort(dep_files, dep_file_index, sizeof (struct dep), qsort_strcmp);
454
455        if (D_depend && debug)
456                for(j = 0; j < dep_file_index; j++) {
457                        printf("dep_files[%d] = %s\n", j, dep_files[j].str);
458                }
459
460        fprintf(out, "%s %s", SALUTATION, dot_o);
461        for(j = 0; j < dep_file_index; j++, dp++)
462                                        {register int len = dp->len;
463                                         register char *str = dp->str;
464                if (j && len == (dp-1)->len && !strcmp(str, (dp-1)->str))
465                        continue;
466                written++;
467                if (size + len + 1 > OUTLINELEN) {
468                        fprintf(out, "\n%s %s", dot_o, str);
469                        size = dot_o_len + len + 1;
470                } else {
471                        fprintf(out, " %s", str);
472                        size += len + 1;
473                }
474        }
475        fprintf(out, "\n");
476        if (verbose)
477                fprintf(stdout, "%s: \"%s\" %d => %d\n", name, dot_o, dep_file_index, written);
478}
479
480                /* process makefile */
481FILE *
482find_mak(file)
483char *file;
484{
485FILE *mak;
486
487        if ((int) file) {
488                if ((mak = fopen(file, "r")) != NULL) {
489                        real_mak_name = file;
490                } else if (update) {
491                        mak_eof = 1;
492                        real_mak_name = file;
493                        return NULL;
494                } else {
495                        fprintf(stderr, "%s: file = \"%s\" ", name, file);
496                        perror("fopen");
497                        fflush(stdout), fflush(stderr);
498                        return NULL;
499                }
500        } else {
501                if ((mak = fopen("makefile", "r")) != NULL) {
502                        real_mak_name = "makefile";
503                } else if ((mak = fopen("Makefile", "r")) != NULL) {
504                        real_mak_name = "Makefile";
505                } else return NULL;
506        }
507
508        if (fstat(fileno(mak), &makstat) < 0) {
509                fprintf(stderr, "%s: file = \"%s\" ", name, real_mak_name);
510                perror("stat");
511                fflush(stdout), fflush(stderr);
512                return NULL;
513        }
514        if (D_open)
515                printf("%s: opened makefile \"%s\"\n", name, real_mak_name);
516        if (D_time)
517                printf("%s: makefile time = %d\n", name, makstat.st_mtime);
518
519        return mak;
520}
521
522FILE *
523temp_mak()
524{
525FILE *mak;
526
527        strcpy(shadow_mak_name, real_mak_name);
528        strcat(shadow_mak_name, ".md");
529
530        /*
531         * For SGS stuff, in case still linked to master version
532         */
533        unlink(shadow_mak_name);
534        if ((mak = fopen(shadow_mak_name, "w")) == NULL) {
535                fprintf(stderr, "%s: file = \"%s\" ", name, shadow_mak_name);
536                perror("fopen");
537                fflush(stdout), fflush(stderr);
538                return NULL;
539        }
540        if (D_open)
541                printf("%s: opened makefile.md \"%s\"\n", name, shadow_mak_name);
542
543        return mak;
544}
545
546skip_mak(makin, makout)
547register FILE *makin, *makout;
548{
549register int len = SALUTATIONLEN;
550
551        if (D_make)
552                printf("skipping in \"%s\"  ", real_mak_name);
553
554        while (fgets(makbuf, LINESIZE, makin) != NULL) {
555                if (D_make && D_contents)
556                        printf("%s: \"%s\"\n", real_mak_name, makbuf);
557                if (strncmp(makbuf, SALUTATION, len)) {
558                        fputs(makbuf, makout);
559                } else
560                        break;
561        }
562        mak_eof = feof(makin);
563        if (mak_eof)
564                fclose(makin);
565        if (D_make)
566                printf("eof = %d str = \"%s\"", mak_eof, makbuf);
567}
568
569expunge_mak(makin, makout)
570register FILE *makin, *makout;
571{
572register int len = SALUTATIONLEN;
573register int oldlen = OLDSALUTATIONLEN;
574
575        if (D_make)
576                printf("expunging in \"%s\"  ", real_mak_name);
577
578        while (fgets(makbuf, LINESIZE, makin) != NULL) {
579                if (D_make && D_contents)
580                        printf("%s: \"%s\"\n", real_mak_name, makbuf);
581                if (! strncmp(makbuf, SALUTATION, len) ||
582                    ! strncmp(makbuf, OLDSALUTATION, oldlen))
583                        break;
584                else
585                        fputs(makbuf, makout);
586        }
587        mak_eof = 1;
588        if (mak_eof)
589                fclose(makin);
590        if (D_make)
591                printf("eof = %d str = \"%s\"", mak_eof, makbuf);
592}
593
594static void
595scan_mak(FILE *makin, FILE *makout, char *file)
596{
597register char *cp = &makbuf[SALUTATIONLEN+1];
598register int len = strlen(file);
599register int ret;
600
601        if (D_make)
602                printf("scanning in \"%s\" for \"%s\"\n", real_mak_name, file);
603
604        do {
605                if (mak_eof)            /* don't scan any more */
606                        return;
607
608                ret = strncmp(cp, file, len);
609                if (D_make)
610                        printf("saw \"%s\" ret = %d\n", cp, ret);
611
612                if (ret < 0) {          /* skip forward till match or greater */
613                        fputs(makbuf, makout);          /* line we're looking at */
614                        while (fgets(makbuf, LINESIZE, makin) != NULL) {
615                                if (strncmp(makbuf, SALUTATION, SALUTATIONLEN)) {
616                                        fputs(makbuf, makout);
617                                } else
618                                        break;
619                        }
620                        mak_eof = feof(makin);
621                        if (mak_eof)
622                                fclose(makin);
623                        continue;
624                } else if (ret == 0) {  /* flush match */
625                        while (fgets(makbuf, LINESIZE, makin) != NULL) {
626                                if (strncmp(makbuf, SALUTATION, SALUTATIONLEN)) {
627                                        ;       /* flush old stuff */
628                                } else
629                                        break;
630                        }
631                        mak_eof = feof(makin);
632                        if (mak_eof)
633                                fclose(makin);
634                        break;
635                } else {                /* no luck this time */
636                        break;
637                }
638        } while (1);
639}
640
641static void
642finish_mak(FILE *makin, FILE *makout)
643{
644        if (mak_eof)            /* don't scan any more */
645                return;
646
647        if (D_make)
648                printf("finishing in \"%s\"\n", real_mak_name);
649
650        fputs(makbuf, makout);          /* line we're looking at */
651        while (fgets(makbuf, LINESIZE, makin) != NULL) {
652                fputs(makbuf, makout);
653        }
654}
655