home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / ddjmag / ddj8911.zip / STREICH.LST < prev   
File List  |  1989-10-04  |  49KB  |  1,540 lines

  1. _A PARALLEL MAKE WITH DESQVIEW_
  2. by Mark Steich
  3.  
  4. [LISTING ONE]
  5. /*---------------------------------------------------------------------
  6.  * DVMAKE, adapted by Mark Streich
  7.  * Original Mk by Allen Holub, Doctor Dobb's Journal, August 1985
  8.  *
  9.  * Some functions may be specific to Borland Int'l. Turbo C 2.0.
  10.  * DESQview 2.01 (or later), and API C Library required.
  11.  *
  12.  * Compile with byte alignment ON.
  13.  *
  14.  * Run from within a non-swappable DOS window with at least 64K
  15.  * of memory, 15K of system memory, runs in the background and
  16.  * shares the cpu when in the foreground.  On non-386 systems,
  17.  * you will have to say that it does not write directly to the screen.
  18.  */
  19.  
  20. #include <stdio.h>        /* for printf(), sprintf(), etc.   */
  21. #include <io.h>           /* for open(), close(), getftime() */
  22. #include <fcntl.h>        /* for O_RDONLY used in open()     */
  23. #include <string.h>       /* for various string functions    */
  24. #include <stdarg.h>       /* for variable argument err()     */
  25. #include "dvapi.h"        /* provided by DESQview API        */
  26.  
  27. /*----------------------------------------------- DEFINES -----------------*/
  28.  
  29. #define MAXLINE  127      /* Maximum DOS command line length    */
  30. #define MAXBLOCK 16       /* Max number of lines in an action   */
  31. #define MAXDEP   32       /* Max number of dependencies         */
  32. #define MAXFNM   64       /* Max length of file name/dir        */
  33. #define COMMENT  '#'      /* Delimits a comment                 */
  34. #define MAKEFILE "mkfile" /* Name of makefile                   */
  35. #define OLDTIME  0x0      /* the Beginning of Time (very old)   */
  36. #define NEWTIME  0xFFFFFFFFL /* the End of Time    (very young) */
  37.  
  38. #define DV_VER   0x201    /* DESQview version required: 2.01    */
  39. #define STKSIZE  512      /* size of tasks' local stack         */
  40. #define NORMAL   0        /* normal status state                */
  41. #define ACCESSDV 1        /* access DESQview, so no new tasks   */
  42. #define ABORT    2        /* kill of all processes              */
  43.  
  44. /*----------------------------------------------------------------------
  45.  *    iswhite(c)        evaluates true if c is white space.
  46.  *    skipwhite(s)      skips the character pointer s past any white space.
  47.  *    skipnonwhite(s)   skips s past any non-white characters.
  48.  *    waitwhile(event)  gives up task's time slice while event is true.
  49.  */
  50.  
  51. #define iswhite(c)       ((c)==' ' || (c)=='\t')
  52. #define skipwhite(s)     while( iswhite(*s) )        ++s;
  53. #define skipnonwhite(s)  while( *s && !iswhite(*s) ) ++s;
  54. #define waitwhile(event) while( event ) api_pause();
  55.  
  56. /*----------------------------------------------- TYPEDEFS ----------------
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  * The entire mkfile is read into memory before it's processed.  It's
  70.  * stored in a binary tree composed of the following structures:
  71.  * depends_on and do_this are argv-like arrays of pointers to character
  72.  * pointers. The arrays are null terminated so no count is required.
  73.  * The time field is a 32 bit ulong consisting of the date and time
  74.  * fields returned from DOS. The date and time are concatanated with
  75.  * the date in the most significant bits and the time in the least
  76.  * significant. This way they can be compared as a single number.
  77.  */
  78.  
  79. typedef struct _tn             /* node for dependencies */
  80. {
  81.     struct _tn   *lnode;       /* pointer to left sub-tree         */
  82.     struct _tn   *rnode;       /* pointer to right sub-tree        */
  83.     char         *being_made;  /* name of file being made          */
  84.     char         **depends_on; /* names of dependent files         */
  85.     char         **do_this;    /* Actions to be done to make file  */
  86.     ulong        time;         /* time & date last modified        */
  87.     ulong        apphan;       /* what app is making this item     */
  88.     char         made;         /* flag indicating made or not      */
  89.     int          tsknum;       /* what task number was assigned    */
  90. }
  91. TNODE;
  92.  
  93. typedef struct _qn             /* queue of items */
  94. {
  95.     void         *item;        /* item in queue          */
  96.     struct _qn   *next;        /* next item in the queue */
  97. }
  98. QNODE;
  99.  
  100. typedef struct  /* definition of Program Information File (PIF) */
  101. {
  102.     char reserved1[2];
  103.     char prog_title[30];       /* blank filled */
  104.     uint maxmem;               /* max memory size in k-bytes */
  105.     uint minmem;
  106.     char program[64];          /* command to start program, 0-terminated */
  107.     char def_drive;            /* 'A', 'B', ..., or blank */
  108.     char def_dir[64];          /* default directory, 0-terminated */
  109.     char params[64];           /* parameters, 0-terminated */
  110.     byte init_screen;          /* screen mode (0-7) */
  111.     byte text_pages;           /* # of text pages used */
  112.     byte first_intr;           /* # of first interrupt vector to save */
  113.     byte last_intr;            /* # of last interrupt */
  114.     byte logical_rows;         /* logical size of window buffer */
  115.     byte logical_cols;
  116.     byte init_row;             /* initial row to display window */
  117.     byte init_col;
  118.     uint system_mem;           /* system memory in k-bytes */
  119.     char shared_prog[64];      /* shared program file name, 0-terminated */
  120.     char shared_data[64];      /* shared program data, 0-terminated */
  121.     byte control_byte1;        /* control byte 1, encoded as follows:
  122.                                     80H - writes direct to screen
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.                                     40H - foreground only
  136.                                     20H - uses math coprocessor
  137.                                     10H - accesses system keyboard buffer
  138.                                     01H - swappable */
  139.     byte control_byte2;        /* control byte 2, encoded as follows:
  140.                                     40H - uses command line parameters
  141.                                     20H - swaps interrupt vectors */
  142.     char open_keys[2];         /* keys to use for Open Window menu */
  143.     uint script_size;          /* size of script buffer in bytes */
  144.     uint auto_pause;           /* pause after this many tests for input
  145.                                   during one clock tick (normally 0) */
  146.     byte color_mapping;        /* non-zero to disable auto color mapping */
  147.     byte swappable;            /* non-zero if application is swappable */
  148.     char reserved2[3];         /* should be zero */
  149.     byte auto_close;           /* non-zero to automatically close on exit */
  150.     byte disk_reqd;            /* non-zero if diskette required */
  151.     byte reserved3;            /* MUST HAVE VALUE OF 1 */
  152.     byte shared_mem;           /* non-zero if program uses shared system mem
  153. */
  154.     byte physical_rows;        /* initial size of physical window */
  155.     byte physical_cols;
  156.     uint max_expanded_mem;     /* max amount of expanded mem avail to app */
  157.     byte control_byte3;        /* control byte 3, encoded as follows:
  158.                                     80H - automatically assigns position
  159.                                     20H - honor maximum memory value
  160.                                     10H - disallow Close command
  161.                                     08H - foreground-only when in graphics
  162.                                     04H - don't virtualize */
  163.     byte key_conflict;         /* keyboard conflict (0-4, usually 0) */
  164.     byte graphics_pages;       /* # graphics pages used */
  165.     uint system_mem2;          /* system memory - overrides system_mem */
  166.     byte init_mode;            /* initial screen mode, normally 0FFH */
  167.     char reserved4[22];
  168.  
  169. } PIF; /* note that the sizeof(PIF) MUST be 416, or else we've made a typo */???
  170.  
  171. /*----------------------------------------------- GLOBAL VARIABLES --------*/
  172.  
  173. static TNODE   *Root     = NULL;   /* Root of file-name tree     */
  174. static FILE    *Makefile;          /* Pointer to opened makefile */
  175. static int     Inputline = 1;      /* current input line number  */
  176. static char    *First    = NULL;   /* Default file to make       */
  177.  
  178. static char    ShowWin   = 0;      /* Display tasks?             */
  179. static char    Parallel  = 0;      /* Are we multitasking yet?   */
  180. static char    Status    = NORMAL; /* processing status          */
  181.  
  182. static char    CurDir[MAXFNM];     /* Directory called from      */
  183. static char    Error[MAXLINE] = "";/* Error saved for later printing */
  184.  
  185. static int     RunCnt    = 0;      /* how many tasks running     */
  186. static int     MemSize   = 256;    /* default task memory size   */
  187. static int     ReDirLen  = 0;      /* length of redirection file */
  188.  
  189.  
  190.  
  191.  
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201. static QNODE   *MkQueue  = NULL;   /* queue of items to make     */
  202. static QNODE   *TskQueue = NULL;   /* queue of tasks to run      */
  203. static QNODE   *OutQueue = NULL;   /* queue of files to output   */
  204.  
  205. static ulong   TskQueueLock;       /* semaphore for TskQueue     */
  206. static ulong   OutQueueLock;       /* semaphore for OutQueue     */
  207. static ulong   AllocLock;          /* semaphore for malloc/free  */
  208. static ulong   MainWin;            /* handle of Main Window      */
  209. static ulong   MenuTsk;            /* handle of Menu Task        */
  210. static ulong   MakeTsk;            /* handle of Make Task        */
  211.  
  212. static PIF     Pif;                /* default Prog Info File     */
  213. static int     Lpif;               /* length of the PIF          */
  214.  
  215. /*----------------------------------------------- ERROR ROUTINES ----------*/
  216.  
  217. void err( char *msg, ... )
  218. {
  219.     /* print the message and optional parameter and either
  220.      * stop immediately if we haven't started up parallel tasks,
  221.      * or just set our status to ABORT and stop later
  222.      */
  223.  
  224.     static char temp[MAXLINE];
  225.     va_list argptr;
  226.  
  227.     /*  Print the error message, if we haven't already, and abort */
  228.     if (!strlen(Error))
  229.     {
  230.         /* print the location of the error in the mkfile */
  231.         sprintf(Error,"Mk (%s line %d): ", MAKEFILE, Inputline );
  232.  
  233.         va_start(argptr,msg);
  234.         vsprintf(temp,msg,argptr);    /* print the error message */
  235.         va_end(argptr);
  236.  
  237.         strcat(Error,temp);           /* and concatenate the two */
  238.     }
  239.  
  240.     if (Parallel)                     /* are we multitasking? */
  241.     {
  242.         /*  notify everyone that there are problems, but don't stop yet */
  243.         api_beginc();
  244.         Status = ABORT;
  245.         api_endc();
  246.     }
  247.     else                              /* not multitasking, so we can STOP */
  248.     {
  249.         fprintf(stderr,"%s",Error);   /* print the error message */
  250.  
  251.         mal_free(TskQueueLock);       /* get rid of our semaphores */
  252.         mal_free(OutQueueLock);
  253.         mal_free(AllocLock);
  254.  
  255.  
  256.  
  257.  
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.         api_exit();                   /* tell DESQview we're done */
  268.         exit(1);                      /* and STOP */
  269.     }
  270. }
  271.  
  272. /*----------------------------------------------- MEMORY ROUTINES ---------*/
  273.  
  274. void *gmem( int numbytes )
  275. {
  276.    /*     Get numbytes from malloc. Print an error message and
  277.     *     abort if malloc fails, otherwise return a pointer to
  278.     *     the memory.  Uses semaphores because malloc() not re-entrant.
  279.     */
  280.  
  281.     void *p;
  282.     extern void *malloc();
  283.  
  284.     mal_lock(AllocLock);              /* get access to heap  */
  285.     p = malloc(numbytes);             /* grab some memory    */
  286.     mal_unlock(AllocLock);            /* free access to heap */
  287.  
  288.     if (p == NULL)                    /* were we successful  */
  289.         err("Out of memory");
  290.  
  291.     return( p );
  292. }
  293.  
  294. void fmem( void *ptr )
  295. {
  296.     /* Frees memory pointed to by ptr. */
  297.  
  298.     extern void free();
  299.  
  300.     mal_lock(AllocLock);   /* get access to heap */
  301.     if (ptr) free(ptr);    /* free the memory if ptr not NULL */
  302.     mal_unlock(AllocLock); /* free access to heap */
  303. }
  304.  
  305. /*----------------------------------------------- QUEUE ROUTINES ----------*/
  306.  
  307. void enqueue(QNODE **queue,void *item)  /* add item to the queue */
  308. {
  309.     QNODE *qptr;
  310.  
  311.     /* get memory for new node */
  312.     if ( (qptr = (QNODE *) gmem(sizeof(QNODE))) == NULL )
  313.         err("Out of memory");
  314.     else
  315.     {
  316.         qptr->item = item;    /* add the item */
  317.         qptr->next = *queue;  /* point to the next item in the queue, if any
  318. */
  319.         *queue = qptr;        /* point to the new front of the queue */
  320.     }
  321.  
  322.  
  323.  
  324.  
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333. }
  334.  
  335. void *dequeue(QNODE **queue)  /* return an item from the queue */
  336. {
  337.     QNODE *qptr,*qptr2 = NULL;
  338.     void *iptr;
  339.  
  340.     if (*queue == NULL)       /* is the queue empty? */
  341.         return( NULL );
  342.     else
  343.     {
  344.         /* find the end of the queue */
  345.         for (qptr = *queue; qptr->next != NULL; qptr = qptr->next)
  346.             qptr2 = qptr;     /* qptr2 points to qptr's predecessor */
  347.  
  348.         iptr = qptr->item;    /* get the item to return */
  349.  
  350.         fmem( qptr );         /* remove the last item from the queue */
  351.         if (qptr2 != NULL)
  352.             qptr2->next = NULL;
  353.         else
  354.             *queue = NULL;    /* nothing left in queue */
  355.  
  356.         return( iptr );       /* return a pointer to the item removed */
  357.     }
  358. }
  359.  
  360. int inMkQueue(char *being_made)
  361. {
  362.     /* see if "being_made" item is already in the MkQueue */
  363.     QNODE *qptr;
  364.  
  365.     if (MkQueue != NULL)       /* is the queue empty? */
  366.         for (qptr = MkQueue; qptr != NULL; qptr = qptr->next)
  367.             if (strcmp(((TNODE *)qptr->item)->being_made,being_made) == 0)
  368.                 return(1);     /* being_made is in the queue */
  369.  
  370.     return( 0 );               /* MkQueue is empty or being_made not in it */
  371. }
  372.  
  373. /*----------------------------------------------- INITIALIZE PIF ----------*/
  374.  
  375. void init_pif(PIF *pif,int *lenpif,char *title,int rows,int cols)
  376.  
  377.     /* Initialize the Program Information File to start the new application.
  378.      * By default it is just a non-swappable dos window with 256K.
  379.      */
  380. {
  381.     *lenpif = sizeof(PIF);
  382.  
  383.     /* set defaults now, and particulars later */
  384.     memset(pif->reserved1,0,2);
  385.     memset(pif->prog_title,' ',30); /* blank filled */
  386.     strncpy(pif->prog_title,title,strlen(title));
  387.  
  388.  
  389.  
  390.  
  391.  
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.  
  399.     pif->maxmem = MemSize;          /* memory required for app in k-bytes */
  400.     pif->minmem = MemSize;
  401.     strcpy(pif->program,"");        /* command to start program, 0-terminated
  402. */
  403.     pif->def_drive = CurDir[0];     /* ' ', 'A', 'B', ... */
  404.     strcpy(pif->def_dir,CurDir+2);  /* default directory, 0-terminated */
  405.     strcpy(pif->params,"");         /* parameters, 0-terminated */
  406.     pif->init_screen = 0x7F;        /* screen mode (0-7) (??) */
  407.     pif->text_pages = 1;            /* # of text pages used */
  408.     pif->first_intr = 0;            /* # of first interrupt vector to save */
  409.     pif->last_intr = 255;           /* # of last interrupt */
  410.     pif->logical_rows = rows;       /* logical size of window buffer */
  411.     pif->logical_cols = cols;
  412.     pif->init_row = 0;              /* initial row to display window */
  413.     pif->init_col = 0;
  414.     pif->system_mem = 0;            /* system memory in k-bytes */
  415.     pif->shared_prog[0] = 0;        /* shared program file name, 0-terminated
  416. */
  417.     pif->shared_data[0] = 0;        /* shared program data, 0-terminated */
  418.     pif->control_byte1 = 0x20;      /* control byte 1, encoded as follows:
  419.                                          80H - writes direct to screen
  420.                                          40H - foreground onlay
  421.                                          20H - uses math coprocessor
  422.                                          10H - accesses system keyboard buffer
  423.                                          01H - swappable */
  424.     pif->control_byte2 = 0x40|0x20; /* control byte 2, encoded as follows:
  425.                                          40H - uses command line parameters
  426.                                          20H - swaps interrupt vectors */
  427.     memset(pif->open_keys,' ',2);   /* keys to use for Open Window menu */
  428.     pif->script_size = 256;         /* size of script buffer in bytes */
  429.     pif->auto_pause = 0;            /* pause after this many tests for input
  430.                                        during one clock tick (normally 0) */
  431.     pif->color_mapping = 0;         /* non-zero to disable color mapping */
  432.     pif->swappable = 0;             /* non-zero if application is swappable */
  433.     memset(pif->reserved2,0,3);     /* should be zero */
  434.     pif->auto_close = 0;            /* non-zero to close on program exit */
  435.     pif->disk_reqd = 0;             /* non-zero if diskette required */
  436.     pif->reserved3 = 1;             /* MUST HAVE VALUE OF 1 */
  437.     pif->shared_mem = 0;            /* non-zero if prog uses shared memory */
  438.     pif->physical_rows = 0;         /* initial size of physical window */
  439.     pif->physical_cols = 0;         /*   0's allow DV to set */
  440.     pif->max_expanded_mem = 65535u; /* max amt of expanded mem avail to app */
  441.     pif->control_byte3 = 0x80|0x10; /* control byte 3, encoded as follows:
  442.                                          80H - automatically assigns position
  443.                                          20H - honor maximum memory value
  444.                                          10H - disallow Close command
  445.                                          08H - foreground-only when in
  446. graphics
  447.                                          04H - don't virtualize */
  448.     pif->key_conflict = 0;          /* keyboard conflict (0-4, usually 0) */
  449.     pif->graphics_pages = 0;        /* # graphics pages used */
  450.     pif->system_mem2 = 0;           /* system memory - overrides system_mem */
  451.     pif->init_mode = 0xFF;          /* initial screen mode, normally 0FFH */
  452.     memset(pif->reserved4,0,22);
  453.  
  454.  
  455.  
  456.  
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463.  
  464.  
  465. }
  466.  
  467. /*----------------------------------------------- GENERATE TEMP FILE NAME --*/
  468.  
  469. char *gen_name(int tsknum,int cmdnum)
  470. {
  471.     /*    Generate a new output file name, d:\dir\DVMKxxyy.$$$, where
  472.           d:\dir\ is the directory in which the "dvmake" was started,
  473.           xx = task number, and yy = command number (both in hex).
  474.           Returns a pointer to the newly allocated file name.
  475.      */
  476.  
  477.     char *new_name;                   /* generated name */
  478.     char tsk_cmd[5];                  /* task/command number string */
  479.  
  480.     if ( (new_name = (char *) gmem(MAXFNM)) == NULL )
  481.         err("Out of memory");
  482.     else
  483.     {
  484.         strcpy(new_name,CurDir);      /* directory name */
  485.  
  486.         if (CurDir[strlen(CurDir)-1] != '\\')
  487.             strcat(new_name,"\\");    /* add backslash to dir */
  488.  
  489.         strcat(new_name,"DVMK");      /* first 4 chars of name */
  490.  
  491.         sprintf(tsk_cmd,"%02x%02x",tsknum,cmdnum);
  492.         strcat(new_name,tsk_cmd);     /* last 4 chars of name */
  493.  
  494.         strcat(new_name,".$$$");      /* add an extension */
  495.     }
  496.     return( new_name );
  497. }
  498.  
  499. /*----------------------------------------------- MENU TASK ---------------*/
  500.  
  501. int dvmenu( void )
  502. {
  503.     /*    display a menu to control the status of the make, and don't
  504.      *    quit until someone wakes me up with tsk_post(MenuTsk)
  505.      */
  506.  
  507.     ulong kbd,win;  /* handles for keyboard, window */
  508.     ulong whichobj; /* handle of object that has input */
  509.     char *kbuf;     /* message buffer */
  510.     int  klen,      /* message length */
  511.          state;     /* state of selected item */
  512.  
  513. /* this string defines the contents of the menu */
  514. static char mkmenu[] = "\
  515.  Access DV  A \
  516.  Quit       Q ";
  517.  
  518. static char mkmenutbl[] =
  519.  
  520.  
  521.  
  522.  
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530.  
  531. {
  532.  ftab(2,FTH_KEYSELECT+FTH_MODIFIED+FTH_AUTORESET,0,0,9,2),
  533.       0,0,0,13,FTE_SELECT,'A',1,0,
  534.       1,0,1,13,FTE_SELECT,'Q',1,0,
  535. };
  536.  
  537.     win = win_new("DVMAKE",6,2,14);   /* get a new window */
  538.     win_logattr(win,1);               /* and set its logical attributes */
  539.     win_attr(win,1);
  540.     win_disallow(win,ALW_HSIZE);      /* do not allow resizing menu */
  541.     win_disallow(win,ALW_VSIZE);
  542.     win_swrite(win,mkmenu);           /* write the contents and */
  543.     win_stream(win,mkmenutbl);        /* field table to the menu window */
  544.     fld_marker(win,175);              /* set the selected field marker */
  545.  
  546.     kbd = key_new();                  /* get a keyboard for the menu */
  547.     key_open(kbd,win);
  548.     key_addto(kbd,KBF_FIELD);         /* and put it into field mode */
  549.  
  550.     /* put and display the menu in the top right corner of the main window */
  551.     win_poswin(win,MainWin,PSW_LEFT,0,PSW_RIGHT,0,0);
  552.     win_unhide(win);
  553.     win_top (win);                    /* make sure it's the one on top */
  554.  
  555.     /* go until someone wakes me up with tsk_post(MenuTsk) */
  556.     for (whichobj = 0; whichobj != tsk_me(); )
  557.     {
  558.         /* wait for something to show up in our object queue */
  559.         if ((whichobj = obq_read()) == kbd)
  560.         {
  561.             key_read(kbd,&kbuf,&klen);/* see what field was selected */
  562.             state = qry_type(win,(int) *kbuf); /* is it ON or OFF    */
  563.  
  564.             if ((int) *kbuf == 1 &&   /* was "Access DV" toggled?    */
  565.                 Status != ABORT)
  566.             {
  567.                 api_beginc();         /* make sure err() hasn't aborted */
  568.                 if (Status != ABORT)  /*   in the interim */
  569.                     Status = (state == FLT_SELECT ? ACCESSDV : NORMAL);
  570.                 api_endc();
  571.             }
  572.             else                      /* selected "Quit"             */
  573.             {
  574.                 Status = ABORT;
  575.                 fld_reset(win);       /* show only Quit as selected  */
  576.                 fld_type(win,2,FLT_SELECT);
  577.             }
  578.         }
  579.     }
  580.  
  581.     /* get rid of menu window, keyboard */
  582.     key_free(kbd);
  583.     win_free(win);
  584. }
  585.  
  586.  
  587.  
  588.  
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597.  
  598. /*----------------------------------------------- GET TIME ROUTINE --------*/
  599.  
  600. ulong   gtime( char *file )
  601. {
  602.     /*     Return the time and date for file, or if the file
  603.      *     does not exist, assume it is very old
  604.      *
  605.      *     The DOS time and date are concatanated to form one
  606.      *     large number.
  607.      *     THIS ROUTINE IS NOT PORTABLE (because it assumes a 32
  608.      *     bit ulong to provide for the time functions).
  609.      */
  610.  
  611.     short        handle;            /* Place to remember file handle */
  612.     struct ftime time;              /* date/time structure           */
  613.     ulong        utime = 0;         /* use to convert time to ulong  */
  614.     char       xtern *searchpath(); /* search PATH for the file,     */
  615.                                     /*   defined in TURBO C's dir.h  */
  616.  
  617.     if ((handle = open(searchpath(file),O_RDONLY)) == -1)
  618.     {
  619.         /* File doesn't exist. Return a very old date & time */
  620.         return( OLDTIME );
  621.     }
  622.     else
  623.     {
  624.         /* File exists, so get the time */
  625.         if ( getftime(handle,&time) )
  626.             err("DOS returned error from date/time request");
  627.  
  628.         if ( close(handle) )
  629.             err("DOS returned error from file close request");
  630.  
  631.         /* pack the time into an unsigned long for comparisons */
  632.         utime |= (ulong) time.ft_year  << 25;
  633.         utime |= (ulong) time.ft_month << 21;
  634.         utime |= (ulong) time.ft_day   << 16;
  635.         utime |= (ulong) time.ft_hour  << 11;
  636.         utime |= (ulong) time.ft_min   << 5;
  637.         utime |= (ulong) time.ft_tsec;
  638.  
  639.         return( utime );
  640.     }
  641. }
  642.  
  643. /*----------------------------------------------- CHAR STORAGE ------------*/
  644.  
  645. char   **stov( char *str, int maxvect )
  646. {
  647.    /*     "str" is a string of words separated from each other by
  648.     *     white space. Stov returns an argv-like array of pointers
  649.     *     to character pointers, one to each word in the original
  650.     *     string. The white space in the original string is replaced
  651.  
  652.  
  653.  
  654.  
  655.  
  656.  
  657.  
  658.  
  659.  
  660.  
  661.  
  662.  
  663.     *     with nulls. The array of pointers is null-terminated.
  664.     *     "Maxvect" is the number of vectors in the returned
  665.     *     array. The program is aborted if it can't get memory.
  666.     */
  667.  
  668.     char   **vect, **vp;
  669.  
  670.     vp = vect = (char **)  gmem( (maxvect + 1) * sizeof(str) );
  671.     while ( *str && --maxvect >= 0 )
  672.     {
  673.         skipwhite(str);
  674.         *vp++ = str;
  675.         skipnonwhite(str);
  676.         if ( *str )
  677.             *str++ = 0;
  678.     }
  679.  
  680.     *vp = 0;
  681.     return( vect );
  682. }
  683.  
  684. /*----------------------------------------------------------------------*/
  685.  
  686. char *getline( int maxline, FILE *fp )
  687. {
  688.     /*     Get a line from the stream pointed to by fp.
  689.      *     "Maxline" is the maximum input line size (including the
  690.      *     terminating null. A \ at the end of line is
  691.      *     recognized as a line continuation, (the lines
  692.      *     are concatanated). Buffer space is gotten from gmem().
  693.      *     If a line is longer than maxline it is truncated (i.e.
  694.      *     all characters from the maxlineth until a \n or EOF is
  695.      *     encountered are discarded.
  696.      *
  697.      *     Returns: NULL on a malloc failure or end of file.
  698.      *              A pointer to the malloced buffer on success.
  699.      */
  700.  
  701.     static  char    *buf;
  702.     char    *bp;
  703.     int     c, lastc;
  704.  
  705.     /* Two buffers are used. Here, we are getting a worst-case buffer
  706.      * that will hold the longest possible line. Later on we'll copy
  707.      * the string into a buffer that's the correct size.
  708.      */
  709.  
  710.     if ( (bp = buf = (char *) gmem(maxline)) == NULL )
  711.         return( NULL );
  712.  
  713.     while(1)
  714.     {
  715.          /* Get the line from fp. Terminate after maxline
  716.           * characters and ignore \n following a \.
  717.  
  718.  
  719.  
  720.  
  721.  
  722.  
  723.  
  724.  
  725.  
  726.  
  727.  
  728.  
  729.           */
  730.  
  731.         Inputline++;                  /* Update input line number */
  732.  
  733.         for ( lastc=0; (c = fgetc(fp)) != EOF && c!='\n'; lastc=c)
  734.             if ( --maxline > 0 )
  735.                 *bp++ = c;
  736.  
  737.         if ( !( c == '\n' && lastc == '\\') )
  738.             break;
  739.  
  740.         else if ( maxline > 0 )    /* erase the \ */
  741.             --bp;
  742.     }
  743.     *bp = 0;
  744.  
  745.     if ( (c == EOF && bp == buf) ||
  746.          (bp = (char *) gmem((int) (bp-buf)+1)) == NULL )
  747.     {
  748.          /*     If EOF was the first character on the line or
  749.           *     malloc fails when we try to get a buffer, quit/
  750.           */
  751.  
  752.         fmem(buf);
  753.         return( NULL );
  754.     }
  755.  
  756.     strcpy( bp, buf ); /* Copy the worst-case buffer to the one  */
  757.                        /* that is the correct size and ...       */
  758.     fmem( buf );       /* free the original, worst-case buffer,  */
  759.     return( bp  );     /* returning a pointer to the copy.       */
  760. }
  761.  
  762. /*----------------------------------------------------------------------*/
  763.  
  764. char **getblock( FILE *fp )
  765. {
  766.     /* Get a block from standard input. A block is a sequence of
  767.      * lines terminated by a blank line. The block is returned as
  768.      * an array of pointers to strings. At most MAXBLOCK lines can
  769.      * be in a block. Leading white space is stripped.
  770.      */
  771.  
  772.     char *p, *lines[MAXBLOCK], **blockv = lines;
  773.     int  blockc = 0;
  774.  
  775.     do {
  776.         if ( (p = getline(MAXLINE,fp)) == NULL)
  777.             break;
  778.  
  779.         skipwhite(p);
  780.  
  781.         if ( ++blockc <= MAXBLOCK )
  782.             *blockv++ = p;
  783.  
  784.  
  785.  
  786.  
  787.  
  788.  
  789.  
  790.  
  791.  
  792.  
  793.  
  794.  
  795.         else
  796.             err("action too long (max = %d lines)",MAXBLOCK);
  797.  
  798.     } while ( *p );
  799.  
  800.     /*     Copy the blockv array into a safe place. Since the array
  801.      *     returned by getblock is NULL terminated, we need to
  802.      *     increment blockc first.
  803.      */
  804.  
  805.     blockv = (char **) gmem( (blockc + 1) * sizeof(blockv[0]) );
  806.     movmem( lines, blockv, blockc * sizeof(blockv[0]) );
  807.     blockv[blockc] = NULL;
  808.  
  809.     return( blockv );
  810. }
  811.  
  812. /*----------------------------------------------------------------------*/
  813.  
  814. TNODE *makenode( void )
  815. {
  816.     /* Create a TNODE, filling it from the mkfile, and return a
  817.      * pointer to it.  Return NULL if there are no more objects
  818.      * in the makefile.
  819.      */
  820.  
  821.     char     *line, *lp;
  822.     TNODE    *nodep;
  823.  
  824.     /*     First, skip past any blank lines or comment lines.
  825.      *     Return NULL if we reach end of file.
  826.      */
  827.  
  828.     do {
  829.         if ( (line = getline(MAXLINE,Makefile)) == NULL )
  830.             return( NULL );
  831.  
  832.     } while ( *line == 0 || *line == COMMENT );
  833.  
  834.     /*     At this point we've gotten what should be the dependency
  835.      *     line. Position lp to point at the colon.
  836.      */
  837.  
  838.     for ( lp = line; *lp && *lp != ':'; lp++ )
  839.         ;
  840.  
  841.     /*     If we find the colon position, lp to point at the first
  842.      *     non-white character following the colon.
  843.      */
  844.  
  845.     if ( *lp != ':' )
  846.         err( "missing ':'");          /* This will abort the program */
  847.     else
  848.         for ( *lp++ = 0; iswhite(*lp); lp++ )
  849.  
  850.  
  851.  
  852.  
  853.  
  854.  
  855.  
  856.  
  857.  
  858.  
  859.  
  860.  
  861.             ;
  862.  
  863.     /*  Allocate and initialize the TNODE */
  864.  
  865.     nodep             = (TNODE *) gmem( sizeof(TNODE) );
  866.     nodep->lnode      = NULL;
  867.     nodep->rnode      = NULL;
  868.     nodep->being_made = line;
  869.     nodep->time       = gtime( line );
  870.     nodep->depends_on = stov( lp, MAXDEP );
  871.     nodep->do_this    = getblock( Makefile );
  872.     nodep->made       = 1; /* assume has already been made, but later change
  873. */
  874.     nodep->apphan     = 0;
  875.     nodep->tsknum     = 0;
  876.  
  877.     return( nodep );
  878. }
  879.  
  880. /*----------------------------------------------- TREE ROUTINES -----------*/
  881.  
  882. TNODE *find( char *key, TNODE *root )
  883. {
  884.     /* If key is in the tree pointed to by root, return a pointer
  885.      * to it, else return 0.
  886.      */
  887.  
  888.     int   notequal;
  889.  
  890.     if ( !root )
  891.          return( 0 );
  892.  
  893.     if ( (notequal = strcmp(root->being_made,key)) == 0 )
  894.          return( root );
  895.  
  896.     return( find( key, (notequal > 0) ? root->lnode : root->rnode) );
  897. }
  898.  
  899. /*----------------------------------------------------------------------*/
  900.  
  901. int tree( TNODE *node, TNODE **rootp )
  902. {
  903.     /* If node's key is in the tree pointed to by rootp, return 0
  904.      * else put it into the tree and return 1.
  905.      */
  906.  
  907.     int   notequal;
  908.  
  909.     if ( *rootp == NULL )
  910.     {
  911.         *rootp = node;
  912.         return( 1 );
  913.     }
  914.  
  915.  
  916.  
  917.  
  918.  
  919.  
  920.  
  921.  
  922.  
  923.  
  924.  
  925.  
  926.  
  927.     if ( (notequal = strcmp( (*rootp)->being_made, node->being_made)) == 0 )
  928.         return( 0 );
  929.  
  930.     return( tree( node, notequal > 0 ? &(*rootp)->lnode : &(*rootp)->rnode)
  931. );
  932. }
  933.  
  934. /*----------------------------------------------------------------------*/
  935.  
  936. int dependencies( void )
  937. {
  938.     /* Manufacture the binary tree of objects to make. First
  939.      * is a pointer to the first target file listed in the
  940.      * makefile (ie. the one to make if one isn't explicitly
  941.      * given on the command line. Root is the tree's root pointer.
  942.      */
  943.  
  944.     TNODE   *node;
  945.  
  946.     if ( (node = makenode()) != NULL )
  947.     {
  948.         /* has First been assigned a value yet? */
  949.         if (First == NULL)
  950.             First = node->being_made;
  951.  
  952.         if ( !tree(node, &Root) )
  953.             err("Can't insert first node into tree !!!\n");
  954.  
  955.         while ( (node = makenode()) != NULL )
  956.             if ( !tree( node, &Root ) )
  957.                 fmem( node );
  958.         return( 1 );
  959.     }
  960.  
  961.     return( 0 );
  962. }
  963.  
  964. /*----------------------------------------------- CREATE MAKE QUEUE -------*/
  965.  
  966. void make_queue( char *what )
  967. {
  968.     /*     Simulate a sequential make, building up a queue of items to make.
  969.      *     The dependency tree is descended recursively.
  970.      */
  971.  
  972.     TNODE       *snode;               /* Source file node pointer     */
  973.     TNODE       *dnode;               /* dependent file node pointer  */
  974.     int         doaction = 0;         /* If true do the action        */
  975.     static char *zero    = (char *)0;
  976.     char        **linev  = &zero;
  977.  
  978.     if ( (snode = find(what, Root)) == NULL )
  979.          err("Don't know how to make <%s>\n", what );
  980.  
  981.  
  982.  
  983.  
  984.  
  985.  
  986.  
  987.  
  988.  
  989.  
  990.  
  991.  
  992.  
  993.     if ( !*(linev = snode->depends_on)) /* If no dependencies      */
  994.          ++doaction;                    /* always do the action     */
  995.  
  996.     for ( ; *linev; linev++ )           /* Process each dependency  */
  997.     {
  998.         make_queue( *linev );
  999.  
  1000.         if ( (dnode = find(*linev, Root)) == NULL )
  1001.             err("Don't know how to make <%s>\n", *linev );
  1002.  
  1003.         if ( snode->time <= dnode->time )
  1004.         {
  1005.             /* If dependent node is more recent (time is greater)
  1006.              * than the source node, do something. If the times
  1007.              * are equal, assume that neither file exists but that
  1008.              * the action will create them, and do the action.
  1009.              */
  1010.             ++doaction;
  1011.         }
  1012.     }
  1013.  
  1014.     if ( doaction )    /* are we going to do anything */
  1015.     {
  1016.         /* are there any commands, and is this node not in MkQueue */
  1017.         if ( snode->do_this && *snode->do_this && **snode->do_this &&
  1018.              !inMkQueue(snode->being_made) )
  1019.         {
  1020.             snode->time = NEWTIME;    /* Assume the time will change     */
  1021.             snode->made = 0;          /* This item has not been made yet */
  1022.             enqueue(&MkQueue,snode);  /* Add to list of things to make   */
  1023.         }
  1024.     }
  1025. }
  1026.  
  1027. /*----------------------------------------------- DISPATCH TASK -----------*/
  1028.  
  1029. int dispatch( void )
  1030. {
  1031.     /*    Grab a job from the TskQueue, send all of the commands to that
  1032.      *    application through its keyboard, close the app, and return.
  1033.      */
  1034.  
  1035.     TNODE       *snode;               /* Source file node pointer */
  1036.     char        **linev;              /* Command to execute */
  1037.     char        *outfnm;              /* file to redirect output to */
  1038.     int         cmdcnt;               /* how many commands were redirected */
  1039.     ulong       keyhan;               /* handle for keyboard */
  1040.     static int  taskcnt = 0;          /* give each task a unique identifier */
  1041.     void        send_keys(ulong keyhan, char *keys);
  1042.  
  1043.     mal_lock(TskQueueLock);           /* grab the task queue */
  1044.     snode = (TNODE *) dequeue(&TskQueue); /* get a task from the queue */
  1045.     mal_unlock(TskQueueLock);         /* let go of the task queue */
  1046.  
  1047.  
  1048.  
  1049.  
  1050.  
  1051.  
  1052.  
  1053.  
  1054.  
  1055.  
  1056.  
  1057.  
  1058.  
  1059.     keyhan = key_of(snode->apphan);   /* get keyboard handle */
  1060.  
  1061.     api_beginc();
  1062.     snode->tsknum = taskcnt++;        /* assign a new task number */
  1063.     api_endc();
  1064.  
  1065.     /* get window to "escape out of" any commands that creeped in at startup
  1066. */
  1067.     send_keys(keyhan,"\x1B");    /* "\x1B" is the Escape key character */
  1068.  
  1069.     for (linev = snode->do_this, cmdcnt = 0;
  1070.          *linev && **linev && Status != ABORT; linev++)
  1071.     {
  1072.         send_keys(keyhan,*linev);     /* send the command */
  1073.  
  1074.         /* if the command doesn't already redirect output, do so */
  1075.         if (strchr(*linev,'>') == NULL && strlen(*linev)+ReDirLen < MAXLINE)
  1076.         {
  1077.             /* get a new output file */
  1078.             if ((outfnm = gen_name((int) snode->tsknum,cmdcnt++)) != NULL)
  1079.             {
  1080.                 send_keys(keyhan," > ");  /* send a redirect command */
  1081.                 send_keys(keyhan,outfnm);
  1082.                 fmem(outfnm);             /* free up the temp file name */
  1083.             }
  1084.             else
  1085.                 --cmdcnt;             /* we couldn't redirect anything */
  1086.         }
  1087.  
  1088.         send_keys(keyhan,"\r");       /* send return key to run the command */
  1089.     }
  1090.  
  1091.     send_keys(keyhan,"exit\r");       /* make window exit itself when done  */
  1092.     waitwhile(api_isobj(snode->apphan));  /* and wait for task to go away   */
  1093.  
  1094.     snode->made = 1;                  /* show that this item was made */
  1095.  
  1096.     if (cmdcnt > 0)                   /* were any commands redirected? */
  1097.     {
  1098.         mal_lock(OutQueueLock);       /* get access to output queue */
  1099.         enqueue(&OutQueue,snode);     /* add to output list */
  1100.         mal_unlock(OutQueueLock);     /* free access to queue */
  1101.     }
  1102.  
  1103.     api_beginc();                     /* this task is done, so */
  1104.     --RunCnt;                         /*   update # of running tasks */
  1105.     api_endc();
  1106. }
  1107.  
  1108. void send_keys(ulong keyhan, char *keys)
  1109. {
  1110.     /* send string to keyboard, pausing every 5 characters to avoid
  1111.      * overflowing any buffers.  You could try different values, but
  1112.      * 5 seems to be a reasonable compromise between speed and making
  1113.  
  1114.  
  1115.  
  1116.  
  1117.  
  1118.  
  1119.  
  1120.  
  1121.  
  1122.  
  1123.  
  1124.  
  1125.      * sure no keys are lost.
  1126.      */
  1127.  
  1128.     int keyctr;
  1129.  
  1130.     if (keys != NULL)
  1131.         while (*keys)
  1132.         {
  1133.             waitwhile(key_sizeof(keyhan));    /* wait for keyboard space */
  1134.             for (keyctr = 0; *keys && keyctr < 5; keyctr++)
  1135.                 key_write(keyhan,keys++,1,0); /* send a single keystroke */
  1136.         }
  1137. }
  1138.  
  1139. /*----------------------------------------------- MAKE TASK ---------------*/
  1140.  
  1141. int make( void )
  1142. {
  1143.     /*     Actually execute the commands.  Items are removed from the
  1144.      *     MkQueue queue, and placed back on the queue if its dependents
  1145.      *     haven't been made yet.  This task runs in parallel with others.
  1146.      */
  1147.  
  1148.     TNODE *snode;        /* Source file node pointer */
  1149.     char  **linev;       /* Command to execute */
  1150.     char  doaction;      /* Should we do anything? */
  1151.     char  *stack;        /* stack for dispatch() tasks */
  1152.  
  1153.     /* while there are still items to make */
  1154.     while ( (snode = (TNODE *) dequeue(&MkQueue)) != NULL && Status != ABORT )
  1155.     {
  1156.         /* make sure all dependents have been made before acting */
  1157.         for (linev = snode->depends_on, doaction = 1; *linev; linev++ )
  1158.             if (!(find(*linev,Root)->made))
  1159.                 doaction = 0;
  1160.  
  1161.         if (!doaction)
  1162.         {
  1163.             enqueue(&MkQueue,snode);   /* put the item back on the queue */
  1164.             api_pause();               /* and give up our time slice */
  1165.         }
  1166.         else
  1167.         {
  1168.             /* put the item on the task queue, and start up its dispatch */
  1169.             mal_lock(TskQueueLock);    /* grab the task queue */
  1170.             enqueue(&TskQueue,snode);  /* put a task on the queue */
  1171.             mal_unlock(TskQueueLock);  /* let go of the task queue */
  1172.  
  1173.             /* get stack space for the command dispatcher */
  1174.             if ( (stack = (char *) gmem(STKSIZE)) == NULL )
  1175.                 err("Out of memory");
  1176.  
  1177.             /* keep trying to start a new application
  1178.              * unless user aborts or wants to access DESQview menu
  1179.  
  1180.  
  1181.  
  1182.  
  1183.  
  1184.  
  1185.  
  1186.  
  1187.  
  1188.  
  1189.  
  1190.  
  1191.              */
  1192.             while ( Status != NORMAL ||
  1193.                    (snode->apphan = app_start((char *) &Pif,Lpif)) == 0 )
  1194.             {
  1195.                 if (Status == ABORT)   /* get out now! */
  1196.                     break;
  1197.                 else if (RunCnt == 0 && /* can't we even get 1 running? */
  1198.                          Status == NORMAL && snode->apphan == 0)
  1199.                     err("Cannot start a single process");
  1200.                 else
  1201.                     api_pause();       /* just give up our time slice */
  1202.             }
  1203.  
  1204.             if (snode->apphan != 0)
  1205.             {
  1206.                 /* either hide or put the application in the background */
  1207.                 if (ShowWin)
  1208.                     app_goback(snode->apphan);
  1209.                 else
  1210.                     app_hide(snode->apphan);
  1211.  
  1212.                 /* start up another command dispatcher, with no window */
  1213.                 tsk_new(dispatch,stack,STKSIZE,"",0,0,0);
  1214.  
  1215.                 api_beginc();
  1216.                 ++RunCnt;              /* we've started another task */
  1217.                 api_endc();
  1218.             }
  1219.         }
  1220.     }
  1221.  
  1222.     waitwhile(RunCnt > 0);            /* wait for all tasks to finish */
  1223. }
  1224.  
  1225. /*----------------------------------------------- OUTPUT TASK -------------*/
  1226.  
  1227. void output( void )
  1228. {
  1229.     /*     Send files (created by redirecting output to DVMKxxyy.$$$) to
  1230.      *     standard output in same order they were created, keeping all
  1231.      *     output for a given dependency together.
  1232.      */
  1233.     TNODE *onode;      /* Output node pointer          */
  1234.     FILE  *infile;     /* file to read input from      */
  1235.     char  *infnm;      /* file name of input           */
  1236.     char  **linev;     /* pointer to commands          */
  1237.     int   ch,counter;
  1238.  
  1239.     /* loop until everything is finished */
  1240.     while ((api_isobj(MakeTsk) || OutQueue != NULL) && Status != ABORT)
  1241.     {
  1242.         mal_lock(OutQueueLock);       /* get access to output queue */
  1243.  
  1244.         if ( (onode = (TNODE *) dequeue(&OutQueue)) == NULL )
  1245.  
  1246.  
  1247.  
  1248.  
  1249.  
  1250.  
  1251.  
  1252.  
  1253.  
  1254.  
  1255.  
  1256.  
  1257.         {
  1258.             mal_unlock(OutQueueLock); /* free access to queue */
  1259.             api_pause();              /* and give up our time slice */
  1260.         }                             /* because there's nothing to output */
  1261.         else
  1262.         {
  1263.             mal_unlock(OutQueueLock); /* free access to queue */
  1264.  
  1265.             for ( linev = onode->do_this, counter = 0;
  1266.                  *linev && **linev; linev++ )
  1267.             {
  1268.                 printf("\n%s\n",*linev); /* print the command executed */
  1269.  
  1270.                 /* make sure dvmake was able to redirect output */
  1271.                 if (strchr(*linev,'>') == NULL &&
  1272.                     strlen(*linev)+ReDirLen < MAXLINE)
  1273.                 {
  1274.                     /* get the file name and open the file */
  1275.                     infnm = gen_name((int) onode->tsknum,counter++);
  1276.  
  1277.                     /* lock access to memory (fopen() gets memory) */
  1278.                     mal_lock(AllocLock);
  1279.  
  1280.                     if (infnm == NULL || (infile = fopen(infnm,"r")) == NULL)
  1281.                     {
  1282.                         mal_unlock(AllocLock); /* free access to memory */
  1283.  
  1284.                         /* not a drastic error, but tell user */
  1285.                         printf("Can't open %s\n",infnm);
  1286.                     }
  1287.                     else /* we successfully opened the temporary file */
  1288.                     {
  1289.                         mal_unlock(AllocLock); /* free access to memory */
  1290.  
  1291.                         /* copy the file to stdout */
  1292.                         while ((ch = fgetc(infile)) != EOF)
  1293.                             putchar(ch);
  1294.  
  1295.                         /* get access to memory (fclose() releases memory) */
  1296.                         mal_lock(AllocLock);
  1297.  
  1298.                         fclose(infile); /* close, and */
  1299.                         remove(infnm);  /* erase the temporary file */
  1300.  
  1301.                         mal_unlock(AllocLock);  /* free access to memory */
  1302.                     }
  1303.  
  1304.                     fmem(infnm);
  1305.                 }
  1306.             }
  1307.         }
  1308.     }
  1309.  
  1310.     /* if user aborts, get rid of remaining temporary files */
  1311.  
  1312.  
  1313.  
  1314.  
  1315.  
  1316.  
  1317.  
  1318.  
  1319.  
  1320.  
  1321.  
  1322.  
  1323.     if (Status == ABORT)
  1324.     {
  1325.         waitwhile(api_isobj(MakeTsk));/* wait for make() to stop first */
  1326.  
  1327.         mal_lock(OutQueueLock);       /* gain access to queue */
  1328.  
  1329.         while ( (onode = (TNODE *) dequeue(&OutQueue)) != NULL )
  1330.            for ( linev = onode->do_this, counter = 0;
  1331.                  *linev && **linev; linev++ )
  1332.               if (strchr(*linev,'>') == NULL &&   /* could we redirect? */
  1333.                   strlen(*linev)+ReDirLen < MAXLINE)
  1334.                  if ((infnm = gen_name((int) onode->tsknum,counter++)) !=
  1335. NULL)
  1336.                  {
  1337.                     remove(infnm);    /* erase the temporary file */
  1338.                     fmem(infnm);      /* and free up the memory   */
  1339.                  }
  1340.  
  1341.         mal_unlock(OutQueueLock);     /* free access to queue */
  1342.     }
  1343. }
  1344.  
  1345. /*----------------------------------------------- INITIALIZE ROUTINES -----*/
  1346.  
  1347. int controlbrk( void )   /* handles control-break interrupts */
  1348. {
  1349.     return( 1 );   /* return non-zero to continue running */
  1350. }
  1351.  
  1352. /*-------------------------------------------------------------------------*/
  1353.  
  1354. void getoptions(int argc,char *argv[])
  1355. {
  1356.     int i;
  1357.     char *getcwd();   /* defined in dir.h */
  1358.     void ctrlbrk();   /* defined in dos.h */
  1359.  
  1360.     TskQueueLock = mal_new();         /* semaphore for TskQueue     */
  1361.     OutQueueLock = mal_new();         /* semaphore for OutQueue     */
  1362.     AllocLock    = mal_new();         /* semaphore for malloc/free  */
  1363.  
  1364.     /* get the current directory, used to save output files */
  1365.     if (getcwd(CurDir,MAXFNM) == NULL)
  1366.         err("Cannot get current directory");
  1367.  
  1368.     ReDirLen = strlen(CurDir)+16;     /* length of redirection file name */
  1369.  
  1370.     /* initialize the control-break handler to call controlbrk()  */
  1371.     ctrlbrk(controlbrk);
  1372.  
  1373.     for (i = 1; i < argc; i++)        /* get the command line switches */
  1374.     {
  1375.         /* windows switch */
  1376.         if (strcmp(argv[i],"-w") == 0 || strcmp(argv[i],"-W") == 0)
  1377.  
  1378.  
  1379.  
  1380.  
  1381.  
  1382.  
  1383.  
  1384.  
  1385.  
  1386.  
  1387.  
  1388.  
  1389.             ShowWin = 1;
  1390.  
  1391.         /* memory size switch */
  1392.         else if (strcmp(argv[i],"-k") == 0 || strcmp(argv[i],"-K") == 0)
  1393.         {
  1394.             if (i < argc - 1)
  1395.             {
  1396.                 if ((MemSize = atoi(argv[++i])) <= 0)
  1397.                     err("Invalid memory size parameter for -k switch");
  1398.             }
  1399.             else
  1400.                 err("Missing memory size parameter for -k switch");
  1401.         }
  1402.  
  1403.         /* help switch */
  1404.         else if (strcmp(argv[i],"-h") == 0 || strcmp(argv[i],"-H") == 0)
  1405.         {
  1406.             printf("dvmake [-w] [-k nnn] [-h] [target]\n");
  1407.             printf("  -w switch displays windows \n");
  1408.             printf("  -k switch sets task memory to nnn K-bytes \n");
  1409.             printf("  -h switch displays help message \n");
  1410.         }
  1411.  
  1412.         /* anything else with - or / must be mistake */
  1413.         else if (argv[i][0] == '-' || argv[i][0] == '/')
  1414.             err("Invalid command switch: %s",argv[i]);
  1415.  
  1416.         /* anything else must be the first item to make */
  1417.         else
  1418.         {
  1419.             if ( (First = (char *) gmem(strlen(argv[i])+1)) == NULL)
  1420.                 err("Out of memory");
  1421.             strcpy(First,argv[i]);
  1422.         }
  1423.     }
  1424. }
  1425.  
  1426. /*----------------------------------------------- START PARALLEL TASKS ----*/
  1427.  
  1428. void startup( void )
  1429. {
  1430.     char *mkstack,        /* stacks for various tasks */
  1431.          *menustack;
  1432.  
  1433.     MainWin = win_me();   /* get handle of main window  */
  1434.  
  1435.     /* initialize a PIF buffer with appropriate window size */
  1436.     if (ShowWin)
  1437.         init_pif(&Pif,&Lpif," DVMAKE TASK ",25,80);
  1438.     else
  1439.         init_pif(&Pif,&Lpif," DVMAKE TASK ",1,1);
  1440.  
  1441.     /* get stack space for the parallel tasks */
  1442.     if ( (mkstack   = (char *) gmem(STKSIZE)) == NULL ||
  1443.  
  1444.  
  1445.  
  1446.  
  1447.  
  1448.  
  1449.  
  1450.  
  1451.  
  1452.  
  1453.  
  1454.  
  1455.          (menustack = (char *) gmem(STKSIZE)) == NULL )
  1456.         err("Out of memory");
  1457.  
  1458.     /* we are now in Parallel mode */
  1459.     Parallel = 1;
  1460.  
  1461.     /* start a task to track a menu, with no title or window */
  1462.     MenuTsk = tsk_new(dvmenu,menustack,STKSIZE,"",0,0,0);
  1463.  
  1464.     /* start a task to make the items, with no title or window */
  1465.     MakeTsk = tsk_new(make,mkstack,STKSIZE,"",0,0,0);
  1466. }
  1467.  
  1468. /*----------------------------------------------- STOP PARALLEL TASKS -----*/
  1469.  
  1470. void finishup( void )
  1471. {
  1472.     /* get dvmenu() to stop by posting its Object Queue */
  1473.     tsk_post(MenuTsk);
  1474.  
  1475.     /* wait for everything to really finish */
  1476.     waitwhile(api_isobj(MakeTsk) || api_isobj(MenuTsk));
  1477. }
  1478.  
  1479. /*----------------------------------------------- MAIN PROGRAM ------------*/
  1480.  
  1481. main( int argc, char *argv[] )
  1482. {
  1483.     /* if DESQview is not running or version is too low, display a message */
  1484.     if (api_init() < DV_VER)
  1485.     {
  1486.         printf ("dvmake requires DESQview version %d.%02d or later\n",
  1487.                            DV_VER/256,DV_VER%256);
  1488.         return( 1 );
  1489.     }
  1490.     else
  1491.     {
  1492.         api_level(DV_VER);            /* tell DV what extensions to enable */
  1493.  
  1494.         getoptions( argc, argv );     /* get command line arguments */
  1495.  
  1496.         if ( (Makefile = fopen(MAKEFILE, "r")) == NULL )
  1497.              err("can't open %s\n", MAKEFILE );
  1498.  
  1499.         if ( !dependencies() )        /* is there anything in the mkfile */
  1500.         {
  1501.              fclose(Makefile);
  1502.              err("Nothing to make");
  1503.         }
  1504.         else
  1505.         {
  1506.              fclose(Makefile);
  1507.              make_queue( First );     /* simulate the sequential make */
  1508.  
  1509.  
  1510.  
  1511.  
  1512.  
  1513.  
  1514.  
  1515.  
  1516.  
  1517.  
  1518.  
  1519.  
  1520.  
  1521.              if (MkQueue != NULL)     /* does anything need to be made? */
  1522.              {
  1523.                  startup();           /* start parallel tasks */
  1524.                  output();            /* display output from tasks */
  1525.                  finishup();          /* stop parallel tasks */
  1526.              }
  1527.  
  1528.              mal_free(TskQueueLock);  /* get rid of our semaphores */
  1529.              mal_free(OutQueueLock);
  1530.              mal_free(AllocLock);
  1531.  
  1532.              if (Status == ABORT)     /* print the error message */
  1533.                  fprintf(stderr,"%s",Error);
  1534.  
  1535.              api_exit();              /* tell DESQview we're done */
  1536.              return( Status == ABORT ? 1 : 0 );
  1537.         }
  1538.     }
  1539. }
  1540.