home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / C / CLFEB88.ZIP / DEPENDS.C < prev    next >
Encoding:
Text File  |  1988-02-04  |  15.9 KB  |  471 lines

  1. An Incremental Compilation Package in C by Dave Taylor 
  2.  
  3. /**                             depends.c                               **/
  4.  
  5. /** This program tweaks with the last modified dates of the files in the 
  6.     current directory based on what files include what other files.  The end 
  7.     result of this is that the 'create' program can recompile as needed based 
  8.     on the modification of not just the source file but also the included 
  9.     files too!
  10.  
  11.         Depends allows the following flags;
  12.          -d             show a dependency tree similar to "make' expects
  13.          -m             generate a real 'makefile'
  14.          -n             no execute.  Display commands to be used only.
  15.          -s             show times.  Display last modified times of files.
  16.          -v             verbose.  Running commentary during file parsing.
  17.  
  18.     (C) Copyright 1986, 1987 by Dave Taylor
  19. **/
  20.  
  21. #include <stdio.h>
  22. #include <ndir.h>                       /* directory stuff           */
  23. #include <errno.h>                      /* system error              */
  24. #include <sys/types.h>                  /* more types!!!             */
  25. #include <sys/stat.h>                   /* stat stuff...             */
  26.  
  27. #define MAXFILES        250             /* max files total in dir    */
  28. #define FNAMELEN        20              /* max file name length      */
  29. #define SLEN            80              /* regular length string...  */
  30.  
  31. DIR *dirp;                              /* directory structure...    */
  32. struct direct *dp;                      /* file entry in directory   */
  33.  
  34. struct dir_rec {
  35.         char name[FNAMELEN];            /* name of the file          */
  36.         long time;                      /* last modified time        */
  37.         int  changed;                   /* has it changed?           */
  38.         int  checked;                   /* has it been checked?      */
  39.        } directory[MAXFILES];   
  40.  
  41. int  count      = 0,                    /* how many files in directory? */
  42.      no_execute = 0,                    /* -n (no execute) flag set     */
  43.      verbose    = 0,                    /* -v (verbose) flag set...     */
  44.      dependencies = 0,                  /* -d (dependencies) flag set.. */
  45.      makefile   = 0,                    /* -m (makefile) flag set..     */
  46.      show_times = 0;                    /* -s (show times) flag set     */
  47.  
  48. extern int errno;                       /* system error number          */
  49.  
  50. char *makefile_header[] = { 
  51.         "# Custom makefile generated by the 'depends' program",
  52.         "#",
  53.         "# The following might need to be customized...",
  54.         " ",
  55.         "CC     = cc",
  56. è        "CFLAGS = -O",
  57.         "PC     = pc",
  58.         "PFLAGS = -O",
  59.         "LIBS   =",
  60.         "DEFS   =",
  61.         " ",
  62.         "# The following should be changed to the final name of the binary",
  63.         "TARGET = a.out",
  64.         " ",
  65.         "",
  66.         };
  67.  
  68. char *cc_line   = {"\n\t$(CC) $(CFLAGS) $(DEFS) %s\n\n" };
  69. char *pc_line   = {"\n\t$(PC) $(PFLAGS) $(DEFS) %s\n\n" };
  70. char *link_line = {"\t$(CC) $(CFLAGS) $(DEFS) $(OBJS) -o $(TARGET) $(LIBS)\n" };
  71.  
  72. char compile_command[SLEN] = { "" };
  73.  
  74. int   strcmp(), compare();
  75. char *strcpy(), *ctime(), *strncpy();
  76. void  qsort(), exit();
  77.  
  78. main(argc, argv)
  79. int argc;
  80. char *argv[];
  81. {
  82.         register int i;
  83.         
  84.         parse_arguments(argc, argv);    /* starting arguments... */
  85.  
  86.         initially_read_in_directory();                         /* start up stuff        */
  87.  
  88.         get_file_modification_dates();                    /* read file dates       */
  89.  
  90.         /** if the user wants to see the dates spin through and 
  91.             spit 'em all out! **/
  92.  
  93.         if (show_times) 
  94.           for (i=0; i < count; i++)
  95.             if (suffix(".c", directory[i].name) || 
  96.                 suffix(".h", directory[i].name) || 
  97.                 suffix(".p", directory[i].name))   /* a legit file? */
  98.                 printf("%-15s %s", directory[i].name,
  99.                        ctime(&directory[i].time));
  100.  
  101.         /** now let's go through and check all the source files **/
  102.  
  103.         for (i=0; i < count; i++)
  104.           if (suffix(".c", directory[i].name) || 
  105.               suffix(".p", directory[i].name))   /* a source file? */
  106.             figure_out_includes(i, 1);
  107.           
  108.         change_file_dates();    /* based on internal modifications... */
  109.  
  110.         fini();                 /* all done! */
  111. è
  112.         exit(0);
  113. }
  114.  
  115. parse_arguments(argc, argv)
  116. int argc;
  117. char *argv[];
  118. {
  119.         /** parse the starting arguments setting the flags etc as specified...
  120.             fail from this routine if bad args! **/
  121.         
  122.         int c;
  123.         extern int optind, opterr;
  124.  
  125.         opterr = 0;     /* supress getopt error message! */
  126.  
  127.         while ((c = getopt(argc, argv, "dhmnvs")) != EOF) 
  128.           switch (c) {
  129.             case 'd' : dependencies++;                  break;
  130.             case 'm' : makefile++; no_execute++;        break;
  131.             case 'n' : no_execute++;                    break;
  132.             case 'v' : verbose++;                       break;
  133.             case 's' : show_times++;                    break;
  134.             case 'h' :
  135.             default  : fprintf(stderr, "Usage: %s [-dhmnvs]\n", argv[0]);
  136.                        fprintf(stderr, "where:\n\
  137.    -d\t\tdepends - show dependencies in 'make'-style format\n\
  138.    -h\t\thelp - give some help on the starting flags\n\
  139.    -m\t\tmake - actually generate a real makefile\n\
  140.    -n\t\tno-action - just list what would be done\n\
  141.    -v\t\tverbose - show all includes in verbose format\n\
  142.    -s\t\tshow times of all files checked\n");
  143.                        exit(1);
  144.           }
  145.  
  146.         if ((makefile && verbose) || (dependencies && verbose)) {
  147.           fprintf(stderr, "That combination of flags doesn't make sense.\n");
  148.           exit(1);
  149.         }
  150. }
  151.         
  152. initially_read_in_directory()
  153. {       
  154.         /* initialize the system variables and read in and sort the current 
  155.            directory... */
  156.  
  157.         dirp = opendir(".");    /* current directory */
  158.  
  159.         while (read_directory(directory[count++].name) && count < MAXFILES)
  160.                 directory[count-1].changed = 0;
  161.  
  162.         if (count >= MAXFILES) {
  163.           fprintf(stderr,
  164.         "*** Warning: read more files than this program can deal with! ***\n");
  165.           fprintf(stderr,
  166. è        "***          Depends continuing, but it might be wrong!       ***\n");
  167.         }
  168.  
  169.         qsort(directory, (unsigned) --count, sizeof ( directory[0] ), compare);
  170.  
  171.         if (makefile) 
  172.           initialize_makefile();
  173. }
  174.  
  175. fini()
  176. {
  177.         /* close everything and let's leave! */
  178.  
  179.         if (makefile)
  180.           finish_makefile();
  181.  
  182.         closedir(dirp);
  183. }
  184.  
  185. initialize_makefile()
  186. {
  187.         /** outputs all the leading Makefile information as appropriate **/
  188.  
  189.         /* first off, let's dump the makefile_header stuff... */
  190.  
  191.         register int i, len;
  192.         char     buffer[SLEN];
  193.  
  194.         for (i=0; strlen(makefile_header[i]) > 0; i++)
  195.           puts(makefile_header[i]);
  196.  
  197.         /* next, we'll need to output "HDRS", "SRCS" and "OBJS" ... */
  198.  
  199.         printf("HDRS  = ");
  200.  
  201.         for (len=8, i=0; i < count; i++)
  202.           if (suffix(".h", directory[i].name)) {
  203.             if (strlen(directory[i].name) + len > 75) {
  204.                printf("  \\n\t");
  205.                len = 8;
  206.             }
  207.             printf("%s ", directory[i].name);
  208.             len += strlen(directory[i].name)+1;
  209.           }
  210.         
  211.         putchar('\n');
  212.  
  213.         printf("SRCS  = ");
  214.  
  215.         for (len = 8, i=0; i < count; i++)
  216.           if (suffix(".c", directory[i].name)||suffix(".p", directory[i].name)){
  217.             if (strlen(directory[i].name) + len > 75) {
  218.                printf("  \\n\t");
  219.                len = 8;
  220.             }
  221. è            printf("%s ", directory[i].name);
  222.             len += strlen(directory[i].name)+1;
  223.           }
  224.         
  225.         putchar('\n');
  226.  
  227.         printf("OBJS  = ");
  228.  
  229.         for (len = 8, i=0; i < count; i++)
  230.           if (suffix(".c", directory[i].name)||suffix(".p", directory[i].name)){
  231.             if (strlen(directory[i].name) + len > 75) {
  232.                printf("  \\n\t");
  233.                len = 8;
  234.             }
  235.             strcpy(buffer, directory[i].name);
  236.             buffer[strlen(buffer)-1] = 'o';     /* make it a '.o' file! */
  237.             printf("%s ", buffer);
  238.             len += strlen(buffer)+1;
  239.           }
  240.         
  241.         printf("\n\n");
  242.  
  243.         /* and the default binary target... */
  244.  
  245.         printf("$(TARGET): $(OBJS) $(SRCS) $(HDRS)\n%s\n\n",
  246.                 link_line);
  247.  
  248. }
  249.  
  250. finish_makefile()
  251. {
  252.         /** adds some standard stuff to the end of the makefile **/
  253.  
  254.         printf(compile_command);
  255.         printf("clean: $(OBJS)\n\trm -f $(OBJS)\n\n");
  256.         printf("lint:\n\tlint $(SRCS) > LINT.OUT\n\n");
  257.         printf("listing:\n\tlpr $(SRCS)\n");
  258. }
  259.  
  260. get_file_modification_dates()
  261. {
  262.         /** do a 'stat' on each file in this directory, saving the last 
  263.             modified date of each in the directory structure... **/
  264.  
  265.         struct stat buffer;
  266.         register int i;
  267.  
  268.         for (i = 0; i < count ; i++) 
  269.           if ((stat(directory[i].name, &buffer)) != 0) {
  270.             fprintf(stderr,"** could not stat %s [%d] **\n", 
  271.                     directory[i].name, errno);
  272.             exit(errno);
  273.           }
  274.           else {
  275.             directory[i].time = buffer.st_mtime;
  276. è          }
  277. }
  278.  
  279. figure_out_includes(index, cnt)
  280. int index, cnt;
  281. {
  282.         /** read the specified file, get all the files that this fellow 
  283.             includes, then change the 'time' of the file entry in the 
  284.             'directory' structure based on the times of the files it includes.
  285.             'cnt' is the nesting depth that we're currently at (for verbose 
  286.              output and other I/O miscellany).
  287.         **/
  288.  
  289.         FILE *thefile;
  290.         char buffer[SLEN];
  291.         int  findex, i;
  292.         
  293.         if (verbose)
  294.           if (cnt == 1)
  295.             printf("Checking file \"%s\"\n", directory[index].name);
  296.           else {
  297.             for (i=0;i<cnt;i++) 
  298.               printf("  ");
  299.             printf("includes file \"%s\"\n", directory[index].name);
  300.           }
  301.          
  302.         if (cnt == 1 && makefile) {
  303.           if (strlen(compile_command) > 0)
  304.             printf(compile_command);
  305.           if (suffix(".c", directory[index].name))
  306.             sprintf(compile_command, cc_line, directory[index].name);
  307.           else
  308.             sprintf(compile_command, pc_line, directory[index].name);
  309.         }
  310.           
  311.         if (dependencies || (makefile && cnt > 1))
  312.           printf("%s%s ", directory[index].name, cnt==1?":":"");
  313.         else if (makefile && cnt == 1) {
  314.           strcpy(buffer, directory[index].name);
  315.           buffer[strlen(buffer)-1] = 'o';
  316.           printf("%s: %s ", buffer, directory[index].name);
  317.         }
  318.  
  319.         if (!verbose && !dependencies && !makefile && directory[index].checked)
  320.           return;
  321.  
  322.         if ((thefile = fopen(directory[index].name,"r")) == NULL) {
  323.           fprintf(stderr,"*** could not open file %s for reading [%d] ! ***\n",
  324.                   directory[index].name, errno);
  325.           exit(errno);
  326.         }
  327.         
  328.         /** okay, now let's loop through this thing and try to get all the
  329.             #include lines... **/
  330.  
  331. è        while (fgets(buffer, SLEN, thefile) != NULL) {
  332.           if (buffer[0] == '#') /* hmmm...a compiler directive... */
  333.             if ((findex = check_for_include(buffer)) != -1) {
  334.               figure_out_includes(findex, cnt+1);       /* recurse... */ 
  335.               if (directory[index].time < directory[findex].time) { 
  336.                  directory[index].time = directory[findex].time; 
  337.                  directory[index].changed++;
  338.               }
  339.             }
  340.         }
  341.         
  342.         directory[index].checked++;
  343.  
  344.         if (dependencies && cnt==1) printf("\n");
  345.  
  346.         (void) fclose(thefile);
  347. }
  348.           
  349. change_file_dates()
  350. {
  351.         /** Change the files that have the 'changed' bit set, meaning that their
  352.             modified times are wrong based on the files they include... **/
  353.  
  354.         register int i;
  355.         struct utimbuf {
  356.                 long    actime;         /* last accessed time */
  357.                 long    modtime;        /* last modified time */
  358.                } time_struct;
  359.         
  360.         for (i=0; i < count; i++) 
  361.           if (directory[i].changed) {
  362.             printf("\ttouch %s\n", directory[i].name);
  363.             if (! no_execute) {
  364.               time_struct.actime = directory[i].time;
  365.               time_struct.modtime= directory[i].time;
  366.               if (utime(directory[i].name, &time_struct) != 0) {
  367.                 fprintf(stderr,"*** could not change file times [%d] ***\n",
  368.                                errno);
  369.                 exit(errno);
  370.               }
  371.             }
  372.           }
  373. }
  374.  
  375. int
  376. check_for_include(line)
  377. char *line;
  378. {
  379.         /** Line is an m4 directive line - this routine figures out if it is 
  380.             an 'include' line, and if so, what file is being included.  If the 
  381.             file included is contained within this directory, then this routine
  382.             will return the index, otherwise it will return -1 if an error.
  383.         **/
  384.  
  385.         char *line_ptr, *word, *strtok();
  386. è        int  i;
  387.  
  388.         line[0] = ' ';  /* remove the '#' */
  389.  
  390.         /* this first section is so we can have "# include" as well
  391.            as "#include" ... we simply get the first word token via
  392.            calls to 'strtok()' */
  393.  
  394.         line_ptr = (char *) line;       /* gets the address */
  395.         
  396.         if ((word = strtok(line_ptr," \t")) != NULL) {
  397.           if (strcmp(word, "include") != 0) 
  398.             return(-1);
  399.         }
  400.         else
  401.           return(-1);
  402.  
  403.         /** to get to here, it must be an include line and the internal strtok 
  404.             pointer must be pointing at the filename surrounded by quotes or 
  405.             '<>' characters... (note that the " in the strtok call will also
  406.             suffice to remove the quotes from the filename too)  **/
  407.  
  408.         if ((word = strtok(NULL, "\t \"")) != NULL) {
  409.           if (word[0] == '<') 
  410.             return(-1);
  411.         }
  412.         else
  413.           return(-1);
  414.  
  415.         /** to get to here, it must have included the file that is specified 
  416.             as 'word' currently, and that file must be a specified file in 
  417.             the local directory. **/
  418.  
  419.         for (i=0; i < strlen(word); i++)
  420.           if (word[i] == '/') 
  421.             return(-1);
  422.  
  423.         /** now, finally, we know that 'word' must be a file in the
  424.             current directory, so we merely need to find it's index
  425.             into the directory structure of this program and return
  426.             it! **/
  427.         
  428.         for (i=0; i < count; i++)
  429.           if (strcmp(word, directory[i].name) == 0) 
  430.             return(i); 
  431.  
  432.         /* it wasn't in there??? */
  433.  
  434.         fprintf(stderr,"*** couldn't find %s in directory! ***\n", 
  435.                         word);
  436.         return(-1);
  437. }
  438.  
  439. int
  440. read_directory(buffer)
  441. èchar *buffer;
  442. {
  443.         /** return the next name in the directory... returns zero when 
  444.             we're out of entries. **/
  445.  
  446.         if ((dp = readdir(dirp)) != NULL) 
  447.           strncpy(buffer, dp->d_name, FNAMELEN);
  448.         
  449.         return(dp != NULL? 1 : 0);
  450. }
  451.  
  452. int
  453. suffix(sf, string)
  454. char *sf, *string;
  455. {
  456.         /** returns true iff the suffix of 'string' is 'sf' **/
  457.  
  458.         register int i, j;
  459.  
  460.         i = strlen(string);
  461.         j = strlen(sf);
  462.  
  463.         while (string[i] == sf[j] && j > 0) {
  464.           i--;
  465.           j--;
  466.         }
  467.  
  468.         return(sf[0] == string[i] && j == 0);
  469. }
  470.  
  471. int
  472. compare(a, b)
  473. struct dir_rec *a, *b;
  474. {
  475.         /** strcmp on name field (for sort routine) **/
  476.  
  477.         return( strcmp( a->name, b->name ));
  478. }
  479.