home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / c / maek.c < prev    next >
C/C++ Source or Header  |  1994-03-04  |  18KB  |  609 lines

  1. /*    make - do minimum work to update a file */
  2.  
  3. #include <stdio.h>
  4.  
  5. /*---------------------------------------------------------------------------*/
  6. #ifdef NEVER
  7. #define DEBUG 1    /* include for debug diagnostics in make() */
  8. #endif
  9.  
  10. #define MAXLINE     (80*10)        /* maximum input line length            */
  11. #define    MAXBLOCK     64            /* max number of lines in an action        */
  12. #define MAXDEP        32            /* max number of dependancies            */
  13. #define COMMENT     '#'            /* delimits a comment                    */
  14. #define MAKEFILE    "mkfile"    /* name of makefile                        */
  15. #define OPEN        0x3d        /* dos function call to open a file        */
  16. #define CLOSE        0x3e        /* dos function call to close a file    */
  17. #define DATETIME    0x57        /* " to get/set file's date & time        */
  18. #define DEFTIME        0x0            /* the default time returned by
  19.                                     gtime when a file doesn't exist        */
  20.  
  21. /*---------------------------------------------------------------------------
  22.     iswhite(c)    evaluates true if c is white space.
  23.     skipwhite(s)    skips the character pointer s past any white space
  24.     skipnonwhite(s) skips s past any non-white characters.                    */
  25.  
  26. #define iswhite(c)            ((c)==' '||(c)=='\t')
  27. #define skipwhite(s)        while(iswhite(*s) ) ++s;
  28. #define skipnonwhite(s)        while(*s&& !iswhite(*s) ) ++s;
  29.  
  30. /*---------------------------------------------------------------------------
  31.     The entire makefile is read into memory before it's processed. 
  32.     It's stored in a binary tree composed of the following structures:
  33.     depends_on and do_this are argv-like arrays of pointers to
  34.     character pointers.  The arrays are null terminated so no count is
  35.     required.  The time field is a 32 bit long consisting of the date
  36.     and time fields returned from a DOS 0x57 call.  The date and time
  37.     are concataneted with the date in the most significant 16 bits and
  38.     the time in the least significant.  This way they can be compared
  39.     as a single number.
  40. */
  41.  
  42. typedef struct _tn
  43.     {struct _tn    *lnode;            /* pointer to left sub-tree            */
  44.     struct _tn    *rnode;            /* pointer to right sub-tree        */
  45.     char        *being_made;    /* name of file being made            */
  46.     char        **depends_on;    /* names of dependant files            */
  47.     char        **do_this;        /* Actions to be done to make file    */
  48.     long        time;            /* time & date last modified        */
  49.     }    TNODE;
  50.  
  51. /*---------------------------------------------------------------------------*/
  52.  
  53. static    TNODE    *Root        =0    ;    /* root of file-name tree        */
  54. static    FILE    *Makefile        ;    /* pointer to opened makefile    */
  55. static    int        Inputline    =0    ;    /* current input line number    */
  56. static     char    *First        =""    ;    /* default file to make            */
  57.  
  58. extern    char    *malloc()        ;    /* from standard library        */
  59. extern    char    **getblock()    ;    /* declared in this module        */
  60. extern    char    *getline()        ;    /* declared in this module        */
  61. extern    TNODE    *find()            ;    /* declared in this module        */
  62.  
  63. /*---------------------------------------------------------------------------*/
  64.  
  65. char    *gmem( numbytes )
  66. {
  67.     /* Get numbytes from malloc.  Print an error message and abort if
  68.         malloc fails, otherwise return a pointer to the memory.                */
  69.     extern    char    *calloc();
  70.     char    *p;
  71.  
  72.     if( !( p=calloc(1,numbytes) ))
  73.         err("Out of memory");
  74.     return p;
  75. }
  76.  
  77. /*---------------------------------------------------------------------------*/
  78.  
  79. char    **stov(str, maxvect )
  80. char    *str;
  81. {
  82.     /* "Str" is a string of words separated from each other by white
  83.         space.  Stov returns an argv-like array of pointers to
  84.         character pointers, one to each word in the original string. 
  85.         The white-space in the original string is replaced with nulls. 
  86.         The array of pointers is null-terminated.  "Maxvect" is the
  87.         number of vectors in the returned array.  The program is
  88.         aborted if it can't get memory.
  89.     */
  90.  
  91.     char    **vect, **vp;
  92.  
  93.     vp = vect = (char **) gmem( (maxvect + 1) * sizeof(str) );
  94.     while( *str && --maxvect >= 0 )
  95.         {skipwhite(str);
  96.         *vp++ = str;
  97.         skipnonwhite(str);
  98.         if( *str ) *str++=0;
  99.         }
  100.     *vp=0;
  101.     return (vect);
  102. }
  103.  
  104. /*---------------------------------------------------------------------------*/
  105.  
  106. long    gtime( file )    char *file;
  107. {
  108.     /*    Return the time and date for a file.
  109.  
  110.         The DOS time and date are concatenated to form one large
  111.         number.  Note that the high bit of this number will be set to 1
  112.         for all dates after 2043, which will cause the date comparisons
  113.         done in make() to fail.  THIS ROUTINE IS NOT PORTABLE (because
  114.         it assumes a 32 bit long).
  115.     */
  116.     extern unsigned _rax,_rbx,_rcx,_rdx,_rsi,_rdi,_res,_rds,_doint();
  117.     extern char _carryf,_zerof;
  118.     short    handle    =    0    ;    /* place to remember file handle */
  119.     long    time            ;
  120.  
  121.     _rds=-1;
  122.     _rax=(OPEN<<8)|0;                /* open the file */
  123.     _rdx=(short) file;
  124.     _doint(0x21);
  125.     if(_carryf) return DEFTIME;        /* file doesn't exist */
  126.     handle=_rbx=_rax;
  127.     _rax=(DATETIME<<8)|0;            /* get the time */
  128.     _doint(0x21);
  129.     if(_carryf) err("DOS returned error from date/time request");
  130.     time=(((long)(_rdx))<<16) | ((long)(_rcx)&0xffffL);
  131.     _rax=CLOSE<<8;                    /* close the file */
  132.     _doint(0x21);
  133.     if(_carryf) err("DOS returned error from file close request");
  134.     return time;
  135. }
  136.  
  137. /*--------------------------------------------------------------------------*/
  138.  
  139. TNODE *makenode()
  140. {
  141.     /*    Create a TNODE, filling it from the makefile and returning a
  142.         pointer to it.  Return NULL if there are no more objects in the
  143.         makefile.                                                              */
  144.  
  145.     char        *line, *lp;
  146.     TNODE        *nodep;
  147.     unsigned    date, time;
  148.  
  149.     /*    First, skip past any blank lines or comment lines.
  150.         Return NULL if we reach end of file.                                */
  151.  
  152.     do
  153.         {if( (line=getline(MAXLINE,Makefile)) == NULL)  return (NULL);
  154.         }    while ( *line == 0 || *line == COMMENT );
  155.  
  156.     /*    At this point we've gotten what should be the dependancy
  157.         line.  Position lp to point at the colon.                        */
  158.     
  159.     for( lp = line; *lp && *lp != ':' ; lp++)  {}
  160.  
  161.     /*    If we find the colon position, adjust lp to point at the first
  162.         non-white character following the colon.                          */
  163.  
  164.     if( *lp != ':' ) {err( "missing ':'" );}    /* This will abort the program */
  165.     else for( *lp++ = 0; iswhite(*lp) ; lp++) {}
  166.  
  167.     /*    Allocate and initialize the TNODE    */
  168.  
  169.     nodep                    = (TNODE *) gmem( sizeof(TNODE) );
  170.     nodep->being_made        = line;
  171.     nodep->time                = gtime( line );
  172.     nodep->depends_on        = stov( lp, MAXDEP );
  173.     nodep->do_this            = getblock( Makefile );
  174. #ifdef DEBUG
  175.     printf("making...\n"); pnode(nodep); getchar();
  176. #endif
  177.     return (nodep);
  178. }
  179.  
  180. /*--------------------------------------------------------------------------*/
  181.  
  182. dependancies()
  183. {
  184.     /*    Manufacture the binary tree of objects to make.  First is a
  185.         pointer to the first target file listed in the makefile (i.e. 
  186.         the one to make if one isn't explicitly given on the command
  187.         line.  Root is the tree's root pointer.                             */
  188.  
  189.     TNODE    *node;
  190.  
  191.     if( node = makenode() )
  192.         {First = node->being_made ;
  193.         if( !tree(node, &Root) )
  194.             err("Can't insert first node into tree !!!\n");
  195.         while( node = makenode() )
  196.             if( !tree( node, &Root ) )
  197.                 free( node );
  198.         return 1;
  199.         }
  200.     return 0;
  201. }
  202.  
  203. /*---------------------------------------------------------------------------*/
  204.  
  205. char    *getline( maxline, fp )
  206. FILE    *fp;
  207. {
  208.     /*    Get a line from the stream pointed to by fp.  "Maxline" is the
  209.         maximum input line size (including the terminating null).  A \
  210.         at the end of line is recognized as a line continuation, (the
  211.         lines are concatenated).  Buffer space is gotten from malloc. 
  212.         If a line is longer than maxline, it is truncated (i.e.  all
  213.         characters from the maxlineth until a \n or EOF is encountered
  214.         are discarded.                                                  */
  215.  
  216.     static        char    *buf    ;
  217.     register    char    *bp        ;
  218.     register    int        c, lastc;
  219.  
  220.     /*    Two buffers are used. Here, we are getting a worst-case buffer
  221.         that will hold the longest possible line. Later on we'll copy
  222.         the string into a buffer that's the correct size.                */
  223.  
  224.     if( !(bp = buf = malloc(maxline)) )
  225.         return NULL;
  226.  
  227.     while(1)
  228.         {/*    Get the line from fp. Terminate after maxline characters
  229.             and ignore \n following a \                                    */
  230.  
  231.         Inputline++;                    /* update input line number        */
  232.         for( lastc=0; (c = filter(fp)) != EOF && c!='\n'; lastc = c)
  233.             if( --maxline > 0)
  234.                 *bp++ = c;
  235.         if( !( c == '\n' && lastc == '\\' ) )
  236.             break;
  237.         else if( maxline > 0)                            /* erase the \ */
  238.             --bp;
  239.         }
  240.     *bp=0;
  241.  
  242.     if( (c == EOF && bp == buf) || !(bp = malloc((bp-buf)+1)) )
  243.         {
  244.             /*    If EOF was the first character on the line or
  245.                 malloc fails when we try to get a buffer, quit.    */
  246.         
  247.         free(buf);
  248.         return (NULL);
  249.         }
  250.  
  251.     strcpy ( bp, buf );        /*    Copy the worst-case buffer to the one
  252.                                 that is the correct size and...            */
  253.     free ( buf );            /*    free the original, worst-case buffer,    */
  254. #ifdef DEBUG
  255. /*    printf("line %d: \n",Inputline); */
  256. #endif
  257.     return ( bp );            /*    returning a pointer to the copy.        */
  258. }
  259.  
  260. /*    With DeSmet C, the program hiccups unless CRs are filtered out */
  261. filter(fp) FILE *fp;
  262. {    int c;
  263.     while((c=fgetc(fp))=='\015') {}
  264.     return c;
  265. }
  266.  
  267. /*---------------------------------------------------------------------------*/
  268.  
  269. char    **getblock( fp )
  270. FILE    *fp;
  271. {
  272.     /*    Get a block from standard input.  A block is a sequence of lines
  273.         terminated by a blank line.  The block is returned as an array
  274.         of pointers to strings.  At most MAXBLOCK lines can be in a
  275.         block.  Leading white space is stripped.                          */
  276.  
  277.     char    *p, *lines[MAXBLOCK], **blockv=lines ;
  278.     int        blockc = 0;
  279.  
  280.     do    {if( !( p = getline(MAXLINE,Makefile) ))
  281.             break;
  282.         skipwhite(p);
  283.         if( ++blockc <= MAXBLOCK )
  284.             *blockv++ = p;
  285.         else
  286.             err("action too long (max = %d lines)", MAXBLOCK);
  287.         }    while ( *p );
  288.  
  289.     /*    Copy the blockv array into a safe place.  Since the array
  290.         returned by getblock is NULL terminated, we need to increment
  291.         blockc first.                                                      */
  292.  
  293.     blockv = (char **) gmem( (blockc + 1) * sizeof(blockv[0]) );
  294.     movmem( lines, blockv, blockc * sizeof(blockv[0]) );
  295.     blockv[blockc]=NULL;
  296.  
  297.     return blockv;
  298. }
  299.  
  300. /*    move n bytes from s to t */
  301. movmem(s,t,n) char *s,*t; int n;
  302. {    while(n--) *t++=*s++;
  303. }
  304. /*---------------------------------------------------------------------------*/
  305.  
  306. err( msg, param)
  307. char *msg;
  308. {
  309.     /*    Print the error message and exit the program.                    */
  310.  
  311.     fprintf(stderr,"Mk (%s line %d): ",MAKEFILE, Inputline );
  312.     fprintf(stderr, msg, param );
  313.     exit(1);
  314. }
  315.  
  316. serr( msg, param )
  317. char    *msg, *param;
  318. {
  319.     /*    Same as err() except the parameter is a string pointer
  320.         instead of an int.                                                */
  321.  
  322.     fprintf(stderr,"Mk (%s line %d): ", MAKEFILE, Inputline );
  323.     fprintf(stderr, msg, param );
  324.     exit(1);
  325. }
  326.  
  327. /*---------------------------------------------------------------------------*/
  328.  
  329. make(what)
  330. char    *what;
  331. {
  332.     /*    Actually do the make.  The dependancy tree is descended
  333.         recursively and if required, the dependancies are adjusted. 
  334.         Return 1 if anything was done, 0 otherwise.                  */
  335.  
  336.     TNODE    *snode                        ;    /* source file node pointer        */
  337.     TNODE    *dnode                        ;    /* dependant file node pointer    */
  338.     int        doaction    =    0            ;    /* if true do the action        */
  339.     static char    *zero    =    (char *) 0    ;
  340.     char    **linev        =    &zero        ;
  341.  
  342. #ifdef    DEBUG
  343.     static    int        recurlev = 0        ;    /* recursion level    */
  344.     printf("make (lev %d): making <%s>\n",recurlev, what);
  345. #endif
  346.  
  347.     if( !(snode = find(what, Root)) )
  348.         serr("Don't know how to make source <%s>\n", what );
  349.  
  350.     if( !*(linev = snode->depends_on))    /*    If no dependancies        */
  351.         doaction++;                        /*    always do the action    */
  352.  
  353.     for( ; *linev ; linev++ )            /*    Process each dependancy    */
  354.         {
  355. #ifdef    DEBUG
  356.         recurlev++;
  357. #endif
  358.         make( *linev );
  359. #ifdef    DEBUG
  360.         recurlev--;
  361. #endif
  362.         if( !(dnode = find(*linev, Root)) )
  363.             serr("Don't know how to make dependent <%s>\n", *linev );
  364. #ifdef    DEBUG
  365.         printf("make (lev %d):    source file ",recurlev);
  366.         ptime( what, snode->time);
  367.         printf("make (lev %d): dependant file ",recurlev);
  368.         ptime( *linev, dnode->time );
  369. #endif
  370.         if( snode->time <= dnode->time )
  371.             {
  372.                 /*    If source node is older than (time is less than)
  373.                     dependant node, do something.  If the times are
  374.                     equal, assume that neither file exists but that
  375.                     the action will create them, and do the action    */
  376. #ifdef DEBUG
  377.             printf("make (lev %d): %s older than %s\n",
  378.             recurlev, what, *linev );
  379. #endif
  380.             doaction++;
  381.             }
  382. #ifdef DEBUG
  383.             else printf("make (lev %d): %s younger than %s\n",
  384.             recurlev, what, *linev);
  385. #endif
  386.         }
  387.     if (doaction )
  388.         {
  389. #ifdef DEBUG
  390.         printf("make (lev %d): doing action:\n",
  391.         recurlev, *linev, what);
  392. #endif
  393.         for( linev = snode->do_this; *linev; linev++ )
  394.             {printf("%s\n", *linev); /* echo action to screen */
  395.             if( system(*linev) )
  396.                 serr("Can't process <%s>\n", *linev );
  397.             /*    Change the source file's time to
  398.                 reflect any modification                        */
  399.             snode->time = gtime( snode->being_made );
  400.             }
  401.         }
  402. #ifdef DEBUG
  403.     printf("make (lev %d): exiting\n", recurlev );
  404. #endif
  405.     return doaction;
  406. }
  407.  
  408. /*    system() isn't supplied in the DeSmet library.    
  409.     This implementation by J. R. Van Zandt            */
  410. static char *(extension[])={".COM",".EXE",".BAT"};
  411.  
  412. system(s) char *s;
  413. {    int i;
  414.     char program[40],directory[30],path[200];
  415.     char *tail,*dp,*pp;
  416.     FILE file;
  417.  
  418.     tail=s;
  419.     skipnonwhite(tail);
  420.     if(*tail)
  421.         {*tail++=0;
  422.         envsearch("path",path);
  423.         pp=path;
  424.         dp=directory;
  425.         while(1)
  426.             {*dp=0;
  427. /*    We don't permit i=2 because exec() can't execute a .BAT file    */
  428.             for (i=0; i<2; i++)
  429.                 {program[0]=0;
  430.                 if(directory[0])
  431.                     {strcat(program,directory);
  432.                     strcat(program,"\\");
  433.                     }
  434.                 strcat(program,s);
  435.                 strcat(program,extension[i]);
  436.                 if(file=fopen(program,"r")) break;
  437.                 }        
  438.             if(file)break;
  439.             if( !(*pp) ) return (-1);
  440.             dp=directory;
  441.             while(*pp && *pp!=';') *dp++=*pp++; /* copy next directory in path */
  442.             if(*pp) pp++;
  443.             }
  444.         fclose(file);
  445.         return exec(program,tail);
  446.         }
  447.     else return 0;
  448. }
  449.  
  450. /*    envsearch - search environment for given string
  451.  
  452.     usage...
  453.         char buf[25];
  454.         envsearch("ALPHA",buf);        puts value of the environment
  455.                         variable ALPHA into buf
  456. */
  457.  
  458. envsearch(target,value) char *target,*value;
  459. {    char buf[100],*s,t[25],*env;
  460.     int nt,offset;
  461.  
  462.     s=t;
  463.     while(*target) *s++=toupper(*target++);
  464.     *s++= '='; *s=0;
  465.     nt = strlen(t);
  466.     offset=0;
  467.  
  468. /* DeSmet C sets up cs register to point 100H past the Program Segment
  469.    Prefix.  The word at offset 44 in the PSP points to the segment with
  470.    the environment */
  471.  
  472.     _lmove(2,44,_showcs()-0x10,&env,_showds()); /* get env. pointer */
  473.     while(1)
  474.         {_lmove(100,offset,env,buf,_showds()); /* get (part of) env. */
  475.         s=buf;
  476.         if(*s)
  477.             {/* printf("examining entry: %s \n",s); getchar(); */
  478.             if (strncmp(t,s,nt)==0) return (strcpy(value,s+nt));
  479.             }
  480.         else
  481.             {*value=0;    /* no value found */
  482.             return;
  483.             }
  484.         offset+=strlen(buf)+1;
  485.         }
  486. }
  487.     
  488. /*--------------------------------------------------------------------------*/
  489.  
  490. /*    Tree routines:                                                            */
  491.  
  492. TNODE    *find( key, root )
  493. char    *key;
  494. TNODE    *root;
  495. {
  496.     /*    If key is in the tree pointed to by root, return
  497.         a pointer to it, else return 0.                                        */
  498.     
  499.     register    int        notequal    ;
  500.     register    TNODE    *rval        ;
  501.  
  502.     if( !root )
  503.         return 0;
  504.     if( !(notequal = strcmp(root->being_made,key)) )
  505.         return( root );
  506.     return( find( key, (notequal > 0) ? root->lnode : root->rnode) );
  507. }
  508.  
  509. /*---------------------------------------------------------------------------*/
  510.  
  511. tree( node, rootp )
  512. TNODE    *node, **rootp ;
  513. {
  514.     /* If node's key is in the tree pointed to by rootp, return 0
  515.         else put it into the tree and return 1.                          */
  516.  
  517.     register    int        notequal    ;
  518.     register    TNODE    *rval        ;
  519.  
  520.     if( *rootp == NULL )
  521.         {*rootp = node;
  522.         return 1;
  523.         }
  524.     if( !(notequal = strcmp( (*rootp)->being_made, node->being_made)))
  525.         return 0;
  526.     return( tree( node, notequal > 0 ? &(*rootp)->lnode
  527.                                      : &(*rootp)->rnode ) );
  528. }
  529.  
  530. /*--------------------------------------------------------------------------*/
  531.  
  532. main( argc, argv )
  533. int argc;
  534. char **argv;
  535. {    /*    A stupid version of the Unix make facility                            */
  536.  
  537.     if( !(Makefile = fopen(MAKEFILE, "r")) )
  538.         err("can't open %s\n", MAKEFILE );
  539.     if( !dependancies() )
  540.         err("Nothing to make");
  541.     else
  542.         make( argc > 1 ? argv[1] : First );
  543. }
  544.  
  545.  
  546.  
  547. #ifdef DEBUG
  548. /*--------------------------------------------------------------------------*/
  549.  
  550. /*    Misc. debugging routines                                                */
  551.  
  552. ptime( file, t )
  553. char *file;
  554. long    t;
  555. {
  556.     /*    Print out the time and date field of a TNODE as
  557.         "mm-dd-yy hh:mm:ss"
  558.         File is the file name.                                                */
  559.  
  560.     int date, time;
  561.  
  562.     date = (t >> 16) & 0xffffL ;
  563.     time = t & 0xffffL ;
  564.     printf("%s: file: ",file);
  565.     printf("%02d-%02d-%02d, ", (date >> 5 ) & 0x0f, date & 0x1f,
  566.                 80 + ((date >> 9) & 0x7f) );
  567.     printf ("%02d:%02d:%02d, ", (time >> 11) & 0x1f, (time >> 5) & 0x3f,
  568.                 (time << 1) & 0x3d );
  569.     printf("\n");
  570. }
  571.  
  572. /*--------------------------------------------------------------------------*/
  573.  
  574. pnode( node )
  575. TNODE *node;
  576. {
  577.     /*    Print out the tree node pointed to by "node"                        */
  578.  
  579.     char **linev;
  580.     printf("+-----------------------------\n"                             );
  581.     printf("|  node at 0x%x\n",                                      node );
  582.     printf("+-----------------------------\n"                             );
  583.     printf("| lnode  = 0x%x,  rnode = 0x%x\n"     ,node->lnode,node->rnode);
  584.     printf("| time   = 0x%lx =\n"                              , node->time );
  585.     ptime( "", node->time  );
  586.     printf("| target = <%s>\n"                         , node->being_made );
  587.     printf("| dependancies:\n"                                            );
  588.     for( linev = node->depends_on; *linev; printf("|\t<%s>\n", *linev++)) {}
  589.     printf("| actions:\n"                                                 );
  590.     for( linev = node->do_this; *linev; printf("|\t<%s>\n", *linev++)) {}
  591.     printf("+-----------------------------\n"                             );
  592. }
  593.  
  594. /*--------------------------------------------------------------------------*/
  595.  
  596. trav( root )
  597. TNODE    *root;
  598. {
  599.     /*    Do an in-order traversal of the tree, printing the node's 
  600.         contents as you go                                                    */
  601.  
  602.     if( root == NULL )
  603.         return;
  604.     trav( root->lnode );
  605.     pnode( root );
  606.     trav( root->rnode );
  607. }
  608. #endif
  609.