home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / makdep4d.zip / MAKDPWIN.ZIP / MAKEDEP.C next >
C/C++ Source or Header  |  1995-05-25  |  16KB  |  746 lines

  1. // ---------------------------------------------------------------------------
  2. //
  3. //    Program:    MakeDeps
  4. //    File:        MakeDep.c
  5. //    By:            David M. Miller
  6. //                Business Visions, Inc.
  7. //    Date:        11-May-95
  8. //
  9. //    Description:
  10. //        Generate a dependency list for C, (Windows) RC, and ASM files for MAKE
  11. //
  12. //    Notes:
  13. //
  14. // ---------------------------------------------------------------------------
  15.  
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <ctype.h>
  19. #include <io.h>
  20. #include <dos.h>
  21. #include <string.h>
  22. #include <malloc.h>
  23. #include <limits.h>
  24.  
  25.  
  26. #define ELEMENTS(array)        (sizeof array/sizeof array[0])
  27.  
  28. typedef enum { FT_UNK = 1, FT_C, FT_ASM, FT_RC } FTYPE;
  29. typedef enum { ERR_LOGO, ERR_MEMORY, ERR_RESP, ERR_USAGE, ERR_LOC, ERR_NOT,
  30.         ERR_OPT } ERRCODE;
  31.  
  32. typedef struct tagFCACHE
  33. {
  34.     char *foundat;
  35.     FTYPE ft;
  36. }
  37. FCACHE;
  38.  
  39. static char logo[] =
  40.     "MakeDep2 dependency generator (c) 1995, Business Visions, Inc.\n\n";
  41. static char usage[] =
  42.     "\nusage: makedeps [-options] {target | @respfile} [target ..]\n"        \
  43.     "where:\n"                                                                \
  44.     "    -l             suppress logo\n"                                    \
  45.     "    -ipath[;path]  include file search directories\n"                    \
  46.     "    -spath[;path]  source file search directories\n"                    \
  47.     "    target         specifies source file name (wildcards permitted)\n"    \
  48.     "                   (examples: *.cpp file.c file.asm file.rc)\n"        \
  49.     "    respfile       contains targets and/or options.\n"                    \
  50.     "    -?             this message\n"                                        \
  51.     "\n"                                                                    \
  52.     "INCLUDE environment variable will be used to locate quoted include\n"    \
  53.     "files not in the current directory. -I flag adds directories to search\n";
  54.  
  55. static char wseol[] = " \t\n\r";        // white space with EOLs
  56. static char ws[] = " \t";                // white space
  57.  
  58. int nologo = 0;
  59.  
  60. // file types that produce .OBJs
  61. char *ctargets[] = {"c", "cpp", "cxx" };
  62. char *asmtargets[] = {"a", "asm" };
  63.  
  64. // file types that produce .RESs
  65. char *rctargets[] = {"rc", "dlg", "ver"};
  66.  
  67. // file types that won't have includes within them
  68. char *rcdepsonly[] = {".ico", ".bmp", ".dib"};
  69.  
  70. char **sps = NULL;                        // source paths
  71. int nsps = 0;                            // count
  72.  
  73. char **incs = NULL;                        // include paths
  74. int nincs = 0;                            // count
  75.  
  76. char **targs = NULL;                    // targets
  77. int ntargs = 0;                            // count
  78.  
  79. char **depends = NULL;                    // dependencies
  80. int ndepends = 0;                        // count
  81.  
  82. FCACHE *findcache = NULL;
  83. int nfound = 0;
  84.  
  85. char _drv[_MAX_DRIVE];
  86. char _dir[_MAX_DIR];
  87. char _fnm[_MAX_FNAME];
  88. char _ext[_MAX_EXT];
  89. char pathname[_MAX_PATH];
  90.  
  91.  
  92. void scan_targets(void);
  93. void scan_file(char *target, FTYPE ft);
  94. void print_list(char *target);
  95. char *is_c_include(char *buf);
  96. char *is_rc_include(char *buf);
  97. char *is_asm_include(char *buf);
  98. FTYPE find_file(char *target, char **paths, int npaths, char *pathname);
  99. FTYPE get_filetype(char *ext);
  100. int get_args(int argc, char **argv);
  101. int expargv(int *pargc, char ***pargv);
  102. int add_list_to_array(char *p, char ***parray, int *psize);
  103. int add_to_array(char *p, char ***parray, int *psize);
  104. int in_array(char *p, char **array, int size);
  105. void free_array(char **array, int size);
  106. FCACHE *in_cache(char *target);
  107. FCACHE *en_cache(char *pathname);
  108. char *strip_path(char *fn);
  109. void error(ERRCODE errcode, char *p);
  110.  
  111.  
  112. int main(int argc, char *argv[])
  113. {
  114.     int i;
  115.     char *p;
  116.  
  117.     switch (expargv(&argc, &argv))
  118.     {
  119.     case -1:
  120.         error(ERR_MEMORY, "loading response file");
  121.         return 1;
  122.     case -2:
  123.         error(ERR_RESP, "loading response file");
  124.         return 1;
  125.     }
  126.  
  127.     if (argc < 2)
  128.     {
  129.         error(ERR_USAGE, NULL);
  130.         return 1;
  131.     }
  132.  
  133.     if (get_args(argc, argv))            // examine options
  134.         return 1;
  135.  
  136.     error(ERR_LOGO, NULL);                // print logo
  137.  
  138.     // add directories in the INCLUDE environment variable to the list of
  139.     // directories in which to search for include files
  140.     if ((p = getenv("INCLUDE")) && add_list_to_array(strdup(p), &incs, &nincs))
  141.     {
  142.         error(ERR_MEMORY, "expanding dependencies");
  143.         return 1;
  144.     }
  145.  
  146.     // off we go ...
  147.     scan_targets();
  148.  
  149.     free_array(sps, nsps);
  150.     free_array(incs, nincs);
  151.     free_array(targs, ntargs);
  152.     if (nfound)
  153.     {
  154.         for (i = 0; i < nfound; ++i)
  155.             free(findcache[i].foundat);
  156.         free(findcache);
  157.     }
  158.     return 0;
  159. }
  160.  
  161. void scan_targets()
  162. {
  163.     int i;
  164.     FTYPE ft;
  165.     char pathname[_MAX_PATH];
  166.  
  167.     for (i = 0; i < ntargs; ++i)
  168.     {
  169.         if ((ft = find_file(targs[i], sps, nsps, pathname)) > FT_UNK)
  170.         {
  171.             scan_file(pathname, ft);
  172.             print_list(pathname);
  173.         }
  174.         else
  175.             error(ERR_NOT, pathname);
  176.  
  177.         free_array(depends, ndepends);
  178.         depends = NULL;
  179.         ndepends = 0;
  180.     }
  181. }
  182.  
  183.  
  184. void scan_file(char *target, FTYPE ft)
  185. {
  186.     FILE *fp;
  187.     int i, start;
  188.     char *p;
  189.     char buf[512];
  190.     char outfile[_MAX_PATH];
  191.  
  192.     if ((fp = fopen(target, "rt")) == NULL)
  193.     {
  194.         error(ERR_LOC, target);
  195.         exit(1);
  196.     }
  197.  
  198.     if (add_to_array(target, &depends, &ndepends))
  199.     {
  200.         error(ERR_MEMORY, "expanding dependencies");
  201.         exit(1);
  202.     }
  203.  
  204.     start = ndepends;
  205.     while (fgets(buf, sizeof buf, fp))
  206.     {
  207.         p = NULL;
  208.         switch (ft)
  209.         {
  210.         case FT_C:
  211.             p = is_c_include(buf);
  212.             break;
  213.         case FT_RC:
  214.             p = is_rc_include(buf);
  215.             break;
  216.         case FT_ASM:
  217.             p = is_asm_include(buf);
  218.             break;
  219.         }
  220.  
  221.         // search for included file
  222.         if (p && find_file(p, incs, nincs, outfile))
  223.         {
  224.             if (add_to_array(strdup(outfile), &depends, &ndepends))
  225.             {
  226.                 error(ERR_MEMORY, "expanding dependencies");
  227.                 exit(1);
  228.             }
  229.         }
  230.     }
  231.     fclose(fp);
  232.  
  233.     // any new dependencies?
  234.     if (ndepends - start)
  235.     {
  236.         // scan them too
  237.         for (i = start; i < ndepends; ++i)
  238.         {
  239.             if (ft == FT_RC)
  240.             {
  241.                 // make sure dependent file is not an icon or a bitmap
  242.                 _splitpath(depends[start], NULL, NULL, NULL, _ext);
  243.                 if (in_array(_ext, rcdepsonly, ELEMENTS(rcdepsonly)))
  244.                     continue;
  245.             }
  246.             scan_file(depends[i], ft);
  247.         }
  248.     }
  249. }
  250.  
  251. void print_list(char *target)
  252. {
  253.     int i, len;
  254.     char *ext;
  255.     char buf[512];
  256.  
  257.     if (ndepends)
  258.     {
  259.         // print out the list of dependencies
  260.         _splitpath(target, NULL, NULL, _fnm, _ext);
  261.         // get the file extension, without the period
  262.         ext = _ext+(_ext[0] == '.');
  263.         if (in_array(ext, ctargets, ELEMENTS(ctargets)) ||
  264.                 in_array(ext, asmtargets, ELEMENTS(asmtargets)))
  265.             _makepath(buf, NULL, NULL, _fnm, "obj");
  266.         else if (in_array(ext, rctargets,
  267.                 ELEMENTS(rctargets)))
  268.             _makepath(buf, NULL, NULL, _fnm, "res");
  269.         else
  270.             strcpy(buf, target);    // don't know what else to do with it
  271.  
  272.         // print all of the dependencies
  273.         strcat(buf, ":");
  274.         for (len = strlen(buf), i = 0; i < ndepends; ++i)
  275.         {
  276.             if (len + 1 + strlen(depends[i]) > 78)
  277.             {
  278.                 strcat(buf, "\\\n");
  279.                 len = 0;
  280.             }
  281.             strcat(buf, " ");
  282.             strcat(buf, depends[i]);
  283.             len += 1 + strlen(depends[i]);
  284.         }
  285.         puts(buf);
  286.     }
  287. }
  288.  
  289.  
  290. char *is_c_include(char *buf)
  291. {
  292.     int len;
  293.     char *p = buf + strspn(buf, ws);    // skip to first non-whitespace
  294.  
  295.     if (*p == '\0')
  296.         return NULL;
  297.     if (*p != '#')
  298.         return NULL;
  299.     // some compilers allow whitespace here
  300.     p += 1 + strspn(p+1, ws);
  301.     if (strncmp(p, "include", 7))        // must be lowercase
  302.         return NULL;
  303.     // found #include, now get filename and length
  304.     p += 7 + strspn(p+7, ws);
  305.     len = strcspn(p, wseol);
  306.  
  307.     if (*p != '"' || *(p+len-1) != '"')
  308.         return NULL;
  309.  
  310.     *(p+len-1) = '\0';
  311.  
  312.     return ++p;
  313. }
  314.  
  315.  
  316. char *is_rc_include(char *buf)
  317. {
  318.     char *p = is_c_include(buf);        // if it's not a c style #include
  319.  
  320.     if (!p)                                // check whether it's rc-style
  321.     {
  322.         // examine first word for "RCINCLUDE"
  323.         int is_rcinclude = (stricmp(strtok(buf, ws), "rcinclude") == 0);
  324.  
  325.         // is there a second word? then, if first word not "RCINCLUDE"        
  326.         if ((p = strtok(NULL, ws)) && !is_rcinclude)
  327.         {
  328.             // if second word is not "ICON" or "BITMAP"
  329.             if (stricmp(p, "icon") && stricmp(p, "bitmap"))
  330.                 return NULL;            // fail
  331.  
  332.             // for "ICON" or "BITMAP": the last word on the line is the
  333.             // filename
  334.             do                            // loop
  335.             {
  336.                 buf = p;                // save current word
  337.                 p = strtok(NULL, ws);    // get next word
  338.             }
  339.             while (p);                    // until no more words
  340.  
  341.             p = buf;                    // saved pointer is last word
  342.         }
  343.     }
  344.  
  345.     return p;
  346. }
  347.  
  348.  
  349. char *is_asm_include(char *buf)
  350. {
  351.     char *p = buf + strspn(buf, ws);    // skip to first non-whitespace
  352.  
  353.     if (*p == '\0')
  354.         return NULL;
  355.     
  356.     if (strnicmp(p, "include", 7))
  357.         return NULL;
  358.  
  359.     // found include, now get filename and length
  360.     p += 7 + strspn(p+7, ws);
  361.  
  362.     *(p+strcspn(p, ws)) = '\0';
  363.  
  364.     return p;
  365. }
  366.  
  367.  
  368. FTYPE find_file(char *target, char **paths, int npaths, char *pathname)
  369. {
  370.     int i = 0;
  371.     char origpath[_MAX_PATH];
  372.     char *p;
  373.     struct find_t find;
  374.     FCACHE *ff;
  375.  
  376.     if ((ff = in_cache(target)) != NULL)
  377.     {
  378.         strcpy(pathname, ff->foundat);
  379.         _splitpath(pathname, _drv, _dir, _fnm, _ext);
  380.         return ff->ft;
  381.     }
  382.  
  383.     _splitpath(target, _drv, _dir, _fnm, _ext);
  384.     _makepath(origpath, _drv, _dir, NULL, NULL);
  385.     p = origpath;
  386.  
  387.     // first time through, p points at original pathname that came with
  388.     // target.  subsequently, all paths in sps are attempted.  if found,
  389.     // the pathname by which it was found is left in pathname.
  390.     do
  391.     {
  392.         _makepath(pathname, NULL, p, _fnm, _ext);
  393.         if (!_dos_findfirst(pathname, _A_ARCH|_A_RDONLY|_A_HIDDEN, &find))
  394.         {
  395.             if ((ff = en_cache(pathname)) == NULL)
  396.                 exit(1);
  397.  
  398.             return ff->ft;
  399.         }
  400.         p = paths[i];
  401.     }
  402.     while (i++ < npaths);
  403.  
  404.     return 0;
  405. }
  406.  
  407.  
  408. FTYPE get_filetype(char *ext)
  409. {
  410.     int i;
  411.  
  412.     if (*ext == '.')
  413.         ++ext;
  414.  
  415.     for (i = 0; i < ELEMENTS(ctargets); ++i)
  416.         if (stricmp(ext, ctargets[i]) == 0)
  417.             return FT_C;
  418.     for (i = 0; i < ELEMENTS(rctargets); ++i)
  419.         if (stricmp(ext, rctargets[i]) == 0)
  420.             return FT_RC;
  421.     for (i = 0; i < ELEMENTS(asmtargets); ++i)
  422.         if (stricmp(ext, asmtargets[i]) == 0)
  423.             return FT_ASM;
  424.     return FT_UNK;
  425. }
  426.  
  427.  
  428. int get_args(int argc, char **argv)
  429. {
  430.     int i;
  431.     char *p;
  432.  
  433.     for (i = 1; i < argc; ++i)
  434.     {
  435.         if (*argv[i] == '-' || *argv[i] == '/')
  436.         {
  437.             ++argv[i];
  438.             switch (tolower(*argv[i]))
  439.             {
  440.             case '?':
  441.                 puts(usage);
  442.                 exit(0);
  443.  
  444.             case 'l':                    // no logo
  445.                 nologo = 1;
  446.                 break;
  447.  
  448.             case 'i':                    // INCLUDE path
  449.                 // see if path starts immediately after argument
  450.                 if (*(p = ++argv[i]) == '\0')
  451.                     p = argv[++i];        // if not, look at next argument
  452.                 if (p && add_list_to_array(strdup(p), &incs, &nincs))
  453.                 {
  454.                     error(ERR_MEMORY, "expanding include direectories");
  455.                     return 1;
  456.                 }
  457.                 break;
  458.  
  459.             case 's':                    // source path
  460.                 // see if path starts immediately after argument
  461.                 if (*(p = ++argv[i]) == '\0')
  462.                     p = argv[++i];        // if not, look at next argument
  463.                 if (p && add_list_to_array(strdup(p), &sps, &nsps))
  464.                 {
  465.                     error(ERR_MEMORY, "expanding source directories");
  466.                     return 1;
  467.                 }
  468.                 break;
  469.  
  470.             default:
  471.                 error(ERR_OPT, argv[i]);
  472.                 return 1;
  473.             }
  474.         }
  475.         else
  476.             add_to_array(strlwr(argv[i]), &targs, &ntargs);
  477.     }
  478.     return 0;
  479. }
  480.  
  481.  
  482. int expargv(int *pargc, char ***pargv)
  483. {
  484.     int i;
  485.     int nargc;
  486.     int expanded = 0;
  487.     char **nargv;
  488.  
  489.     // allocate a new argv array
  490.     if ((nargv = (char **)malloc((*pargc + 1) * sizeof(char *))) == NULL)
  491.         return -1;
  492.  
  493.     // copy all pargv elements to nargv, expanding response files
  494.     for (i = nargc = 0; i < *pargc; ++i)
  495.     {
  496.         char **argv = *pargv;
  497.  
  498.         // if argument doesn't start with '@', copy it
  499.         if (*argv[i] != '@')
  500.             nargv[nargc++] = argv[i];
  501.  
  502.         // else expand response file
  503.         else
  504.         {
  505.             FILE *fp;
  506.             int nargs = 0;
  507.             long filesize;
  508.             size_t bytes;
  509.             char *buf;
  510.             char **tmp;
  511.             char *p;
  512.  
  513.             if ((fp = fopen(argv[i]+1, "rt")) == NULL)
  514.             {
  515.                 free(nargv);
  516.                 return -2;
  517.             }
  518.             if ((filesize = filelength(fileno(fp))) >= UINT_MAX)
  519.             {
  520.                 fclose(fp);
  521.                 free(nargv);
  522.                 return -2;
  523.             }
  524.             if ((buf = (char *)malloc((size_t)filesize + 1)) == NULL)
  525.             {
  526.                 fclose(fp);
  527.                 free(nargv);
  528.                 return -2;
  529.             }
  530.             bytes = fread(buf, 1, (size_t)filesize, fp);
  531.             if (bytes != (size_t)filesize && ferror(fp))
  532.             {
  533.                 free(buf);
  534.                 fclose(fp);
  535.                 free(nargv);
  536.                 return -2;
  537.             }
  538.             fclose(fp);
  539.             buf[bytes] = '\0';
  540.  
  541.             // count arguments in file
  542.             for (p = buf; *p; )
  543.             {
  544.                 // skip whitespace
  545.                 p += strspn(p, wseol);
  546.                 if (*p)
  547.                     ++nargs;
  548.                 // skip to next whitespace
  549.                 p += strcspn(p, wseol);
  550.             }
  551.  
  552.             // reallocate nargv[] array to hold new items
  553.             if ((tmp = (char **)realloc(nargv, ((*pargc + 1) + nargs) *
  554.                     sizeof(char *))) == NULL)
  555.             {
  556.                 free(buf);
  557.                 free(nargv);
  558.                 return -1;
  559.             }
  560.             nargv = tmp;
  561.  
  562.             // collect new items from file arguments
  563.             for (p = strtok(buf, wseol); p; p = strtok(NULL, wseol))
  564.                 nargv[nargc++] = p;
  565.  
  566.             expanded = 1;
  567.         }
  568.     }
  569.  
  570.     if (expanded)
  571.     {
  572.         nargv[nargc] = NULL;
  573.         *pargc = nargc;
  574.         *pargv = nargv;
  575.     }
  576.     else
  577.         free(nargv);
  578.  
  579.     return 0;
  580. }
  581.  
  582.  
  583. int add_list_to_array(char *p, char ***parray, int *psize)
  584. {
  585.     if (p == NULL)
  586.         return -1;
  587.  
  588.     if ((p = strtok(p, ";")) != NULL)
  589.         do
  590.         {
  591.             if (add_to_array(p, parray, psize))
  592.                 return -1;
  593.         }
  594.         while (p = strtok(NULL, ";"));
  595.  
  596.     return 0;
  597. }
  598.  
  599.  
  600. int add_to_array(char *p, char ***parray, int *psize)
  601. {
  602.     char **narray;
  603.  
  604.     if (!p)
  605.         return -1;
  606.  
  607.     // don't add it if it's already there
  608.     if (!in_array(p, *parray, *psize))
  609.     {
  610.         // increase the size of the array by 1
  611.         // NOTE: this expects realloc(NULL, ...) to behave like malloc(...) !!
  612.         narray = (char **)realloc(*parray, (1 + *psize) * sizeof(char *));
  613.         if (narray == NULL)                    // realloc failed, leave old array
  614.             return -1;
  615.  
  616.         narray[(*psize)++] = p;                // add string to array
  617.         *parray = narray;
  618.     }
  619.  
  620.     return 0;
  621. }
  622.  
  623.  
  624. int in_array(char *p, char **array, int size)
  625. {
  626.     int i;
  627.  
  628.     for (i = 0; i < size; ++i)
  629.         if (stricmp(p, array[i]) == 0)
  630.             break;
  631.     return (i < size);
  632. }
  633.  
  634.  
  635. void free_array(char **array, int size)
  636. {
  637.     int i;
  638.  
  639.     if (array)
  640.     {
  641.         for (i = 0; i < size; ++i)
  642.             if (array[i])
  643.                 free(array[i]);
  644.         free(array);
  645.     }
  646. }
  647.  
  648.  
  649. FCACHE *in_cache(char *target)
  650. {
  651.     FCACHE *ff;
  652.  
  653.     target = strip_path(target);
  654.     for (ff = findcache; ff && ff - findcache < nfound; ++ff)
  655.     {
  656.         if (stricmp(target, strip_path(ff->foundat)) == 0)
  657.             return ff;
  658.     }
  659.     return NULL;
  660. }
  661.  
  662.  
  663. FCACHE *en_cache(char *pathname)
  664. {
  665.     int i;
  666.     char *p;
  667.     FCACHE *ff = realloc(findcache, (1 + nfound) * sizeof *ff);
  668.  
  669.     if (ff == NULL)
  670.     {
  671.         error(ERR_MEMORY, "expanding dependencies");
  672.         return NULL;
  673.     }
  674.  
  675.     findcache = ff;
  676.     p = strip_path(pathname);
  677.     for (i = 0; i < nfound; ++i, ++ff)
  678.         if (stricmp(p, strip_path(ff->foundat)) < 0)
  679.         {
  680.             memmove(ff+1, ff, (nfound - i) * sizeof *ff);
  681.             break;
  682.         }
  683.  
  684.     ff->foundat = strdup(pathname);
  685.     ff->ft = get_filetype(_ext);
  686.     ++nfound;
  687.  
  688.     return ff;
  689. }
  690.  
  691.  
  692. char *strip_path(char *fn)
  693. {
  694.     int i;
  695.     char *p;
  696.     static char parts[] = "\\:/";
  697.  
  698.     for (i = 0; i < sizeof parts - 1; ++i)
  699.         if (p = strrchr(fn, parts[i]))
  700.             return p+1;
  701.  
  702.     return fn;
  703. }
  704.  
  705.  
  706. void error(ERRCODE errcode, char *p)
  707. {
  708.     if (!nologo)
  709.     {
  710.         fputs(logo, stderr);
  711.         nologo = 1;
  712.     }
  713.  
  714.     if (errcode == ERR_LOGO)
  715.         return;
  716.  
  717.     switch (errcode)
  718.     {
  719.     case ERR_RESP:
  720.         perror(p);
  721.         return;
  722.     case ERR_MEMORY:
  723.         fputs("Out of memory", stderr);
  724.         fputs(p, stderr);
  725.         fputc('\n', stderr);
  726.         break;
  727.     case ERR_NOT:
  728.         fputs("Not a valid target file (.C??, .RC, .ASM): ", stderr);
  729.         fputs(p, stderr);
  730.         fputc('\n', stderr);
  731.         break;
  732.     case ERR_LOC:
  733.         fputs("Unable to locate target file: ", stderr);
  734.         fputs(p, stderr);
  735.         fputc('\n', stderr);
  736.         break;
  737.     case ERR_OPT:
  738.         fputs("Unknown option: -", stderr);
  739.         fputs(p, stderr);
  740.         fputc('\n', stderr);
  741.         // fall through
  742.     case ERR_USAGE:
  743.         fputs(usage, stderr);
  744.     }
  745. }
  746.