home *** CD-ROM | disk | FTP | other *** search
/ Piper's Pit BBS/FTP: ibm 0020 - 0029 / ibm0020-0029 / ibm0028.tar / ibm0028 / INSTALL2.TD0 / SOURCES.LIF / COMPILE.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-09-27  |  61.3 KB  |  2,629 lines

  1. /*=============================================================================
  2.  
  3.      The INSTALL program source code, object code, sample  script files,
  4.      executable  program,  and  documentation  are  subject to copyright
  5.      protection under the laws of the United States and other countries.
  6.  
  7.      This software is licensed, not sold, and may only be  redistributed
  8.      in  executable format and only in accordance with the provisions of
  9.      the INSTALL Source Code License Agreement.
  10.  
  11.         INSTALL is Copyright(C) 1987-1990 by Knowledge Dynamics Corp
  12.        Highway Contract 4 Box 185-H, Canyon Lake, TX (USA) 78133-3508
  13.               512-964-3994 (Voice)   512-964-3958 (24-hr FAX)
  14.  
  15.                       All rights reserved worldwide.
  16.  
  17. ===============================================================================
  18.  
  19. FILENAME:
  20.     compile.c
  21.  
  22. AUTHOR:
  23.     eric jon heflin
  24.  
  25. PUBLIC FUNCTIONS:
  26.     compile()       -  master function
  27.     lookup_var()    -  lookup var name and return its pointer or NULL
  28.  
  29. LOCAL FUNCTIONS:
  30.     get_file()      -  interpret @File verb
  31.     ignore_out()    -  ignore @Out for files not installed
  32.     get_out_drive() -  interpret @GetOutDrive block
  33.     get_subdir()    -  interpret @GetSubdir block
  34.     get_groups()    -  interpret @GetGroup block
  35.     get_string()    -  interpret @GetString block
  36.     get_option()    -  interpret @GetOption block
  37.     get_welcome()   -  display @Welcome/@Display block
  38.     get_autoexec()  -  interpret @SetAutoexec block
  39.     get_config()    -  interpret @SetConfig block
  40.     get_disk()      -  interpret @DefineDisk block
  41.     get_project()   -  interpret @DefineProject block
  42.     parse_out()     -  interpret @Out verb
  43.     get_fixed()     -  prompt for output disk removability
  44.     execute_if()    -  interpret @If/@ElseIf block
  45.     execute_verb()  -  evaluate expression
  46.     get_vars()      -  parse @DefineVars block
  47.     insert_var()    -  insert a polymorphic var into the lookup table
  48.     trim_dir()      -  trim the trailing \\ from a subdirectory string
  49.  
  50. DESCRIPTION:
  51.     This file contains the high-level functions to implement an interpreter
  52.     for the INSTALL script language.
  53.  
  54. REVISION HISTORY:
  55.     DATE:    AUTHOR:        DESCRIPTION OF CHANGES:
  56.     891101    ejh            Addition of user defined vars.
  57.     891213    ea jordan    Documentation.
  58.     900102    ejh            Cosmetic changes.
  59.     900301    ejh            Added code to handle var lookup for subdir type
  60.                         vars after an @Out.
  61.  
  62. ==============================================================================*/
  63.  
  64. #include <ctype.h>
  65. #include "install.h"
  66.  
  67. #if defined(InstantC)
  68. #    define assert(c) if(!(c))_()
  69. #else
  70. #    include <stdlib.h>
  71. #    include <assert.h>
  72. #endif
  73.  
  74. #if defined(__TURBOC__)
  75. #   include <dir.h>
  76. #   include <mem.h>
  77. #    include <process.h>
  78. #    include <io.h>
  79. #    include <sys\types.h>
  80. #    include <sys\stat.h>
  81. #elif defined (__MICROSOFTC__)
  82. #    include <direct.h>
  83. #    include <process.h>
  84. #    include <io.h>
  85. #    include <sys\types.h>
  86. #    include <sys\stat.h>
  87. #elif defined(LATTICE)
  88. #    include <types.h>
  89. #    include <stat.h>
  90. #endif
  91.  
  92. #include "switches.h"
  93. #include <fcntl.h>
  94. #include <stdio.h>
  95. #include <string.h>
  96. #include "tokens.h"
  97. #include "control.h"
  98.  
  99. extern int line;        /* defined in getoken.c */
  100. extern long max_script;    /*    "    "     "      */
  101. extern struct token_s tokens[MAX_TOKEN];
  102. extern byte string[255];
  103. extern int token_type;
  104.  
  105. static long start;
  106. long restart;
  107. int old_line;
  108. /*
  109.  *    Label on first input disk - used if there is a @Finish block, in which
  110.  *    case we need to prompt for the first disk again.  Future versions of
  111.  *    INSTALL will probably have an option to read the @Finish block into RAM
  112.  *    and thus not require re-reading of the first disk.
  113.  */
  114. byte *first_disk_label = NULL;        /* used by finish() and opendata() */
  115. static byte *disk_label;    /* label returned by ignore_out */
  116.  
  117. static bool know_fixed = FALSE;    /* TRUE after fixed/removable is known */
  118. static byte temp[500];    /* local scope scratch buf */
  119.  
  120. #if defined(DEMO)
  121. static files_read = 0;
  122. #endif
  123.  
  124. static file_t *get_file(int in, project_t *project);
  125. static void ignore_out(int in, project_t *project, int skip);
  126. static void get_out_drive(int in, project_t *project);
  127. static void get_subdir(int in, project_t *project);
  128. static void get_string(int in, project_t *project);
  129. static void get_integer(int in, project_t *project);
  130. static void get_groups(int in, project_t *project);
  131. static void get_options(int in, project_t *project);
  132. static void get_welcome(int in, project_t *project);
  133. static void get_disk(int in, project_t *project);
  134. static void get_project(int in, project_t *project);
  135. static void parse_out(int in, project_t *project, file_t *file, byte *dflt);
  136. static void get_fixed(project_t *);
  137. void compile(int in, project_t *project);
  138. static void get_autoexec(int, project_t *);
  139. static void get_config(int, project_t *);
  140. static word execute_if(int in, project_t *project, int token);
  141. static word execute_verb(int in, project_t *project, int token);
  142. var_t *lookup_var(project_t *project, byte *string);
  143. static void get_vars(int in, project_t *project);
  144. static var_t *insert_var(project_t *project, byte *name, var_e type);
  145. static byte *trim_dir(byte *dir);
  146.  
  147.  
  148. /*
  149.  *    Lookup a var name and return its pointer.  A NULL pointer is returned
  150.  *    if the var was not found in the linked list.
  151.  */
  152.  
  153. var_t *lookup_var(project_t *project, byte *string)
  154.     {
  155.     var_t *v;
  156.  
  157.     for (v = project->vars;  v != NULL;  v = v->next)
  158.         if (stricmp(string, v->name) == 0)
  159.             break;
  160.     return v;
  161.     }                   /* lookup_var */
  162.  
  163.  
  164. /*
  165.  *    If token is an rvalue, display it on tty_window and return TRUE,
  166.  *    otherwise, put token text into string and return FALSE.
  167.  */
  168.  
  169. static word execute_verb(int in, project_t *project, int token)
  170.     {
  171.     var_t *v;
  172.  
  173.     if (in)                /* quiet lint */
  174.         in = in;
  175.     switch (token)
  176.         {
  177.     case ABORT:
  178.         say_bye();
  179.         return FALSE;    /* quiet lint */
  180.     case AT:
  181.         sputch('@');
  182.         return TRUE;
  183.     case CLS:
  184.         cls();
  185.         return TRUE;
  186.     case PAUSE:
  187.         pause();
  188.         return TRUE;
  189.     case SUBDIR:
  190.         wputs(tty_w, trim_dir(project->subdir));
  191.         return TRUE;
  192.     case OUTDRIVE:
  193.         wputs(tty_w, "%c", project->out_drive);
  194.         return TRUE;
  195.     case INDRIVE:
  196.         wputs(tty_w, "%c", project->in_drive);
  197.         return TRUE;
  198.     case NAME:
  199.         wputs(tty_w, project->name);
  200.         return TRUE;
  201.     case VERSION:
  202.         wputs(tty_w, project->version);
  203.         return TRUE;
  204.     case ILLEGAL:
  205.         /* attempt to parse a user-defined variable */
  206.         if ((v = lookup_var(project, string)) == NULL)
  207.             return FALSE;
  208.         /* match - display appropriately */
  209.         switch (v->type)
  210.             {
  211.         case byte_var_type:
  212.             wputs(tty_w, "%c", (byte)v->integer);
  213.         case dir_var_type:
  214.             wputs(tty_w, trim_dir(v->string));
  215.             break;
  216.         case qstring_var_type:
  217.             wputs(tty_w, v->string);
  218.             break;
  219.         case drive_var_type:
  220.             wputs(tty_w, "%c", v->drive);
  221.             break;
  222.         case integer_var_type:
  223.             wputs(tty_w, "%lu", v->integer);
  224.             break;
  225.             }
  226.         return TRUE;
  227.         }                /* switch */
  228.     if (tokens[token].type == NUMBER_T)
  229.         {
  230.         unget_token(token);
  231.         wputs(tty_w, "%lu", (ulong) parse_rvalue(project));
  232.         return TRUE;
  233.         }
  234.     return FALSE;
  235.     }                   /* execute_verb */
  236.  
  237.  
  238. static word execute_if(int in, project_t *project, int token)
  239.     {
  240.     switch (token)
  241.         {
  242.     case IF:
  243.         parse_if(in, project);
  244.         return TRUE;
  245.     case ELSE:
  246.     case ELSEIF:
  247.         skip_if(in);
  248.         /* intentional fall-through */
  249.     case ENDIF:
  250.         return TRUE;
  251.         }
  252.     return FALSE;
  253.     }                   /* execute_if */
  254.  
  255.  
  256. /*
  257.  *    Get a string from the end-user.
  258.  */
  259.  
  260. static void get_string(in, project)
  261. int in;
  262. project_t *project;
  263.     {                    /* get_string */
  264.     var_t *v = NULL;
  265.     int token, c;
  266.     byte prompt[100];
  267.     byte str[256], *s;
  268.  
  269.     prompt[0] = str[0] = 0;
  270.  
  271.     /* display and ignore all leading white space */
  272.     while ((c = readch(in, TRUE)) != EOF && isspace(c))
  273.         sputch((byte) c);
  274.  
  275.     /* ensure first non-whitespace token is an valid string-type var */
  276.     if (c != '@')
  277.         expected("a string variable name");
  278.  
  279.     unreadch(c);
  280.     token = get_token(in, project);
  281.     if (token == ILLEGAL && (v = lookup_var(project, string)) != NULL && v->type == qstring_var_type)
  282.         {
  283.         /* valid string-type var */
  284.         /* assign default */
  285.         strcpy(str, v->string);
  286.         }
  287.     else
  288.         /* invalid variable name */
  289.         unexpected(string);
  290.  
  291.     forever
  292.         {
  293.         while ((c = readch(in, TRUE)) != EOF && c != '@')
  294.             sputch((byte) c);
  295.  
  296.         if (c == EOF)
  297.             unexpected("end of file");
  298.  
  299.         unreadch(c);
  300.         token = get_token(in, project);
  301.  
  302.         if (token == ENDSTRING)
  303.             break;
  304.  
  305.         switch (token)
  306.             {
  307.         case DEFAULT:
  308.             match(in, "=");
  309.             s = get_str(in, project);
  310.             strcpy(str, s);
  311.             free(s);
  312.             continue;
  313.         case PROMPT:
  314.             match(in, "=");
  315.             strcpy(prompt, get_str(in, project));
  316.             continue;
  317.         default:
  318.             if (execute_verb(in, project, token))
  319.                 continue;
  320.             if (execute_if(in, project, token))
  321.                 continue;
  322.             unexpected(string);
  323.             }    /* switch */
  324.         }    /* forever */
  325.  
  326.     if (!prompt[0])
  327.         strcpy(prompt, " Enter Text: ");
  328.  
  329.     /* now, display a one-line edit box */
  330.     if (put_prompt(prompt, str, 70, 0) == ESC)
  331.         {
  332.         /* end-user hit [Esc] - abort install */
  333.         say_bye();
  334.         }
  335.  
  336.     /* get mem for dynamically calloced RAM */
  337.     if(strlen(str) == 0)
  338.         v->string = "";
  339.     else
  340.         {
  341.         v->string = (byte *) smart_calloc(1, strlen(str) + 5, "get_string");
  342.         strcpy(v->string, str);
  343.         }
  344.     cls();
  345.     }                    /* get_string */
  346.  
  347.  
  348. /*
  349.  *    Get a number from the end-user.
  350.  */
  351.  
  352. static void get_integer(in, project)
  353. int in;
  354. project_t *project;
  355.     {                    /* get_integer */
  356.     var_t *v = NULL;
  357.     int token, c;
  358.     byte prompt[100], str[55], *s;
  359.     long number;
  360.  
  361.     prompt[0] = 0;
  362.  
  363.     /* display and ignore all leading white space */
  364.     while ((c = readch(in, TRUE)) != EOF && isspace(c))
  365.         sputch((byte) c);
  366.  
  367.     /* ensure first non-whitespace token is an valid integer-type var */
  368.     if (c != '@')
  369.         expected("a integer variable name");
  370.  
  371.     unreadch(c);
  372.     token = get_token(in, project);
  373.     if (token == ILLEGAL && (v = lookup_var(project, string)) != NULL && v->type == integer_var_type)
  374.         {
  375.         /* valid integer-type var */
  376.         /* assign default */
  377.         number = v->integer;
  378.         }
  379.     else
  380.         /* invalid variable name */
  381.         unexpected(string);
  382.  
  383.     forever
  384.         {
  385.         while ((c = readch(in, TRUE)) != EOF && c != '@')
  386.             sputch((byte) c);
  387.  
  388.         if (c == EOF)
  389.             unexpected("end of file");
  390.  
  391.         unreadch(c);
  392.         token = get_token(in, project);
  393.  
  394.         if (token == ENDINTEGER)
  395.             break;
  396.  
  397.         switch (token)
  398.             {
  399.         case DEFAULT:
  400.             match(in, "=");
  401.             number = get_long(in);
  402.             continue;
  403.         case PROMPT:
  404.             match(in, "=");
  405.             s = get_str(in, project);
  406.             strcpy(prompt, s);
  407.             free(s);
  408.             continue;
  409.         default:
  410.             if (execute_verb(in, project, token))
  411.                 continue;
  412.             if (execute_if(in, project, token))
  413.                 continue;
  414.             unexpected(string);
  415.             }    /* switch */
  416.         }    /* forever */
  417.  
  418.     if (!prompt[0])
  419.         strcpy(prompt, " Enter Number: ");
  420.  
  421.     /* now, display a one-line edit box */
  422.     sprintf(str, "%lu", number);
  423.     if (put_prompt(prompt, str, 17, 1) == ESC)
  424.         {
  425.         /* end-user hit [Esc] - abort install */
  426.         say_bye();
  427.         }
  428.  
  429.     /* get mem for dynamically calloced RAM */
  430.     if(strlen(str) == 0)
  431.         v->integer = 0L;
  432.     else
  433.         v->integer = atol(str);
  434.     cls();
  435.     }                    /* get_integer */
  436.  
  437.  
  438. static void get_options(in, project)
  439. int in;
  440. project_t *project;
  441.     {                    /* get_options */
  442.     int c, token, current = 0;
  443.     word i;
  444.     bool no_option = TRUE;
  445.     unsigned max_cnt = 0;
  446.     int ret;
  447.     option_t *o;
  448.     word *number = (word *) smart_calloc(250, sizeof(word), "get_option 1");
  449.     byte **option = (byte **) smart_calloc(250, sizeof(byte *), "get_option 2");
  450.     bool checkbox = FALSE;
  451.     byte *s;            /* temp string pointer */
  452.  
  453.     for (;;)
  454.         {
  455.         while ((c = readch(in, TRUE)) != EOF && c != '@')
  456.             sputch((byte) c);
  457.  
  458.         if (c == EOF)
  459.             unexpected("end of file");
  460.  
  461.         unreadch(c);
  462.         token = get_token(in, project);
  463.  
  464.         if (token == ENDOPTION)
  465.             break;
  466.  
  467.         switch (token)
  468.             {
  469.         case CHECKBOX:
  470.             checkbox = TRUE;
  471.             continue;
  472.         case OPTION:
  473.             number[current] = get_num(in);
  474.             match(in, "=");
  475.             s = get_str(in, project);
  476.             if (strlen(s) > 76 || (checkbox && strlen(s) > 73))
  477.                 error("string too long");
  478.             option[current] = (byte *) smart_calloc(1, 80, "get_options");
  479.             strcpy(option[current], s);
  480.             if (strlen(s) > max_cnt)
  481.                 max_cnt = strlen(s);
  482.             ++current;
  483.             no_option = FALSE;
  484.             skip_white(in);
  485.             continue;
  486.         default:
  487.             if (execute_verb(in, project, token))
  488.                 continue;
  489.             if (execute_if(in, project, token))
  490.                 continue;
  491.             unexpected(string);
  492.             }
  493.         }
  494.  
  495.     if (no_option)
  496.         expected(tokens[OPTION].text);
  497.  
  498.     max_cnt += 4;
  499.     if (checkbox)
  500.         max_cnt += 4;
  501.  
  502.     if (max_cnt >= 76)
  503.         error("%s string too long", tokens[OPTION].text);
  504.     if (current <= 12)
  505.         i = current - 1;
  506.     else
  507.         i = 12;
  508.  
  509.     if ((ret = select_m(option, 5, (80 - max_cnt) / 2, 5 + i, (80 - max_cnt) / 2 + max_cnt, current - 1, 0, checkbox)) == -1)
  510.         say_bye();
  511.  
  512.     /* go to end of option linked-list */
  513.     if (project->option == NULL)
  514.         o = project->option = (option_t *) smart_calloc(1, sizeof(option_t), "get_option 3");
  515.     else
  516.         {
  517.         for (o = project->option;  ;o = o->next)
  518.             {
  519.             if (o->next == NULL)
  520.                 {
  521.                 /* pre allocate next option struct */
  522.                 o->next = (option_t *) smart_calloc(1, sizeof(option_t), "get_option 4");
  523.                 o = o->next;
  524.                 break;
  525.                 }
  526.             }            /* for */
  527.         }                /* else */
  528.  
  529.     if (!checkbox)
  530.         {
  531.         o->number = number[ret];
  532.         }
  533.     else
  534.         {
  535.         /* now set each option selected */
  536.         for (i = 0;  i < current;  ++i)
  537.             {
  538.             if (is_set(i))
  539.                 {
  540.                 o->number = number[i];
  541.                 /* pre allocate next option struct */
  542.                 o->next = (option_t *) smart_calloc(1, sizeof(option_t), "get_option 4");
  543.                 o = o->next;
  544.                 }
  545.             }
  546.         }
  547.  
  548.     /* free options */
  549.     for (i = 0;  option[i] != NULL && i < 250;  ++i)
  550.         free(option[i]);
  551.     free(option);
  552.     free(number);
  553.     cls();
  554.     return;
  555.     }                    /* get_options */
  556.  
  557.  
  558. /*
  559.  *    Parse a @DefineVars/@EndVars block.  Internally, each var is stored
  560.  *    as an element of a linked list with the head "project->vars".  Type
  561.  *    checking is strict.
  562.  */
  563.  
  564. static void get_vars(int in, project_t *project)
  565.     {                    /* get_vars */
  566.     int token;
  567.     int no_var = TRUE;
  568.     var_t *new_var;
  569.     byte *name;
  570.     byte *s;
  571.     int c;
  572.  
  573.     forever
  574.         {
  575.         token = get_token(in, project);
  576.         if (token == ENDVARS || token == EOF)
  577.             break;
  578.  
  579.         if ((name = get_text(in)) == NULL)
  580.             expected("variable identifier");
  581.         strupr(name);
  582.         match(in, "=");
  583.  
  584.         switch (token)
  585.             {
  586.         case DIR_TYPE:
  587.             new_var = insert_var(project, name, dir_var_type);
  588.             s = get_str(in, project);
  589.             new_var->string = smart_calloc(1, strlen(s) + 5, "get_vars");
  590.             strupr(s);
  591.             strcpy(new_var->string, s);
  592.             /*
  593.              *    the next few lines insure that the path starts and ends with
  594.              *    a backslash
  595.              */
  596.             if (new_var->string[0] != '\\')
  597.                 {
  598.                 memmove(new_var->string + 1, new_var->string, strlen(new_var->string));
  599.                 new_var->string[0] = '\\';
  600.                 }
  601.             if (new_var->string[strlen(new_var->string) - 1] != '\\')
  602.                 strcat(new_var->string, "\\");
  603.             no_var = FALSE;
  604.             continue;
  605.         case QSTRING_TYPE:
  606.             new_var = insert_var(project, name, qstring_var_type);
  607.             new_var->string = get_str(in, project);
  608.             no_var = FALSE;
  609.             continue;
  610.         case BYTE_TYPE:
  611.             new_var = insert_var(project, name, byte_var_type);
  612.             new_var->integer = get_char(in);
  613.             no_var = FALSE;
  614.             continue;
  615.         case INTEGER_TYPE:
  616.             new_var = insert_var(project, name, integer_var_type);
  617.             new_var->integer = get_long(in);
  618.             no_var = FALSE;
  619.             continue;
  620.         case DRIVE_TYPE:
  621.             new_var = insert_var(project, name, drive_var_type);
  622.             c = (byte) get_char(in);
  623.             if (c == '@')
  624.                 {
  625.                 var_t *v;
  626.                 unreadch(c);
  627.                 token = get_token(in, project);
  628.                 if (token == OUTDRIVE)
  629.                     new_var->drive = project->out_drive;
  630.                 else if ((v = lookup_var(project, string)) != NULL)
  631.                     new_var->drive = v->drive;
  632.                 else
  633.                     unexpected(string);
  634.                 }
  635.             else
  636.                 new_var->drive = (byte) toupper(c);
  637.  
  638.             /* the following code ensures that the output drive exists */
  639.             {
  640.             byte *drvs = getdrives();
  641.  
  642.             /* use the specified drive letter as the ceiling */
  643.             for (; new_var->drive >= 'A' && drvs[new_var->drive-'A'] == 0; --new_var->drive)
  644.                 ;
  645.             }
  646.             no_var = FALSE;
  647.             continue;
  648.         default:
  649.             if (execute_if(in, project, token))
  650.                 continue;
  651.             unexpected(string);
  652.             }            /* switch */
  653.         }                /* forever */
  654.  
  655.     if (token == EOF)
  656.         unexpected("End of file");
  657.  
  658.     if (no_var)
  659.         expected("a variable definition");
  660.     }                    /* get_vars */
  661.  
  662.  
  663. /*
  664.  *    insert a polymorphic var into the lookup table.
  665.  */
  666.  
  667. static var_t *insert_var(project_t *project, byte *name, var_e type)
  668.     {                    /* insert_var */
  669.     var_t *new_var;
  670.  
  671.     /* go to end of linked-list of vars */
  672.     if (project->vars == NULL)
  673.         new_var = project->vars = (var_t *) smart_calloc(1, sizeof(var_t), "get_vars 1");
  674.     else
  675.         {
  676.         for (new_var = project->vars;  ;new_var = new_var->next)
  677.             {
  678.             if (new_var->next == NULL)
  679.                 {
  680.                 /* pre allocate next option struct */
  681.                 new_var->next = (var_t *) smart_calloc(1, sizeof(var_t), "get_vars 3");
  682.                 new_var = new_var->next;
  683.                 break;
  684.                 }
  685.             }            /* for */
  686.         }                /* else */
  687.  
  688.     /*
  689.      *    At this point v points to location for a new var.  Now, parse each
  690.      *    new var definition and add to the linked list of vars including
  691.      *    type type.
  692.      */
  693.  
  694.     new_var->name = (byte *) smart_calloc(1, strlen(name) + 2, "insert_var");
  695.     strcpy(new_var->name, name);
  696.     strupr(new_var->name);
  697.     new_var->type = type;
  698.     return new_var;
  699.     }                    /* get_vars */
  700.  
  701.  
  702. /*
  703.  *     TRUE if we are between @BeginLib and @EndLib, that is, the current files
  704.  *    are to be extracted from a library file.
  705.  */
  706.  
  707. static byte in_lib = FALSE;
  708.  
  709. static file_t *get_file(in, project)
  710. int in;
  711. project_t *project;
  712.     {
  713.     file_t *file;
  714.     option_t *o;
  715.     unsigned long min_val;
  716.     int token;
  717.     byte *dflt = NULL;
  718.     unsigned i, j;
  719.     byte *groups = NULL;
  720.     long option = -1l;
  721.     byte od = project->out_drive;
  722.  
  723. #    ifdef DEMO
  724.     if (files_read > 3 && (strcmp(project->name, "INSTALL") != 0))
  725.         error("This restricted version of INSTALL will only install 3 files");
  726.     ++files_read;
  727. #    endif
  728.  
  729.     file = (file_t *) smart_calloc(1, sizeof(file_t), "get_file");
  730.     file->next = NULL;
  731.     file->out_fname = NULL;
  732.     file->out_disk = NULL;
  733.     file->sub = NULL;
  734.     file->abs = FALSE;
  735.     file->damaged = FALSE;
  736.     file->overwrite = OVERWRITE;
  737.     if ((file->in_fname = get_fname(in)) == NULL)
  738.         expected("a file name");
  739.     strupr(file->in_fname);
  740.     if (strchr(file->in_fname, '?') != NULL || strchr(file->in_fname, '*') != NULL)
  741.         {
  742.         if (!in_lib)
  743.             error("Ambiguous filenames may only be used for files in a library");
  744.         file->wildcard = TRUE;
  745.         }
  746.  
  747.     file->type = NORMAL;
  748.     if (in_lib)
  749.         {
  750.         /* we are w/i a @BeginLib ... @EndLib block */
  751.         file->type = CHILD;
  752.         file->verify = TRUE;
  753.         }
  754.  
  755.     forever
  756.         {
  757.         token = get_token(in, project);
  758.         switch (token)
  759.             {
  760.         case ASKOVERWRITE:
  761.         case NOOVERWRITE:
  762.         case OVERWRITE:
  763.             file->overwrite = (word) token;
  764.             continue;
  765.         case CRC:
  766.             if (file->type == CHILD)
  767.                 {
  768.                 get_num(in);
  769.                 continue;
  770.                 }
  771.             if ((file->c_crc = file->o_crc = get_num(in)) == 0)
  772.                 expected("CRC number");
  773.             file->verify = TRUE;
  774.             continue;
  775.         case SETMACRO:
  776.             match(in, "=");
  777.             if ((dflt = get_str(in, project)) == NULL)
  778.                 expected("macro substitution text");
  779.             strupr(dflt);
  780.             continue;
  781.         case GROUP:
  782.             if ((groups = get_text(in)) == NULL)
  783.                 expected("group letter(s)");
  784.             strupr(groups);
  785.             continue;
  786.         case OPTION:
  787.             option = get_long(in);
  788.             continue;
  789.         case DECOMPRESS:
  790.             if (file->type == CHILD)
  791.                 continue;
  792.             file->expand = TRUE;
  793.             continue;
  794.         case OUTABS:
  795.             file->abs = TRUE;
  796.             skip_white(in);
  797.             if (peekch(in) == '@')
  798.                 {
  799.                 switch (get_token(in, project))
  800.                     {
  801.                 case IF:
  802.                     parse_if(in, project);
  803.                     break;
  804.                 case ELSE:
  805.                 case ELSEIF:
  806.                 case ENDIF:
  807.                     skip_if(in);
  808.                     break;
  809.                 case OUTDRIVE:
  810.                     unreadch(project->out_drive);
  811.                     break;
  812.                 default:
  813.                     unexpected(string);
  814.                     }
  815.                 }
  816.             if ((file->out_fname = get_fname(in)) == NULL)
  817.                 expected("output filename");
  818.             if (file->out_fname[2] != '\\')
  819.                 expected("output path");
  820.             strupr(file->out_fname);
  821.             continue;
  822.         case APPENDTO:
  823.             file->append = TRUE;
  824.             /* intentional fall-through to next case */
  825.         case OUT:
  826.         case OUT0K:
  827.             min_val = 0L;
  828.             break;
  829.         case OUT128K:
  830.             min_val = 128L;
  831.             break;
  832.         case OUT360K:
  833.             min_val = 350L;
  834.             break;
  835.         case OUT512K:
  836.             min_val = 512L;
  837.             break;
  838.         case OUT720K:
  839.             min_val = 710L;
  840.             break;
  841.         case OUT1M:
  842.             min_val = 1000L;
  843.             break;
  844.         case OUT1440K:
  845.             min_val = 1430L;
  846.             break;
  847.         case OUT5M:
  848.             min_val = 5000L;
  849.             break;
  850.         case OUT10M:
  851.             min_val = 10000L;
  852.             break;
  853.         case OUT20M:
  854.             min_val = 20000L;
  855.             break;
  856.         case OUT30M:
  857.             min_val = 30000L;
  858.             break;
  859.         case SIZE:
  860.             if ((file->c_size = file->o_size = get_long(in)) == 0L)
  861.                 expected("a file size");
  862.             continue;
  863. /*        case SYSTEM:*/
  864.         case IFILE:
  865.         case ENDDISK:
  866.         case BEGINLIB:
  867.         case ENDLIB:
  868.             unget_token(token);
  869. #            ifndef STRICT
  870.             /*
  871.              *    This file is not selected for installation, save output disk
  872.              *    label, if one was specified
  873.              */
  874.             if (file->out_disk != NULL)
  875.                 disk_label = file->out_disk;
  876.             else
  877.                 return NULL;
  878. #            endif
  879.  
  880.             /* see if at least a filename was specified */
  881.             if (file->in_fname == NULL)
  882.                 expected("a file name");
  883.  
  884.             /* define output filename */
  885.             if (file->out_fname == NULL)
  886.                 {
  887.                 /* none was specified, use input name */
  888.                 file->out_fname = (byte *) smart_calloc(1, strlen(file->in_fname) + 3, "get_file 2");
  889.                 if (od != project->out_drive)
  890.                     {
  891.                     sprintf(file->out_fname, "%c:%s", od, file->in_fname);
  892.                     file->abs = TRUE;
  893.                     }
  894.                 else 
  895.                     strcpy(file->out_fname, file->in_fname);
  896.                 }
  897.  
  898.             /* see if this file is in selected @Option or @Group */
  899.             if (groups == NULL && option == -1l)
  900.                 return file;    /* always install this file */
  901.  
  902.             if (groups != NULL)
  903.                 {
  904.                 /* see if this file is in specified group(s) */
  905.                 for (i = 0;  i < strlen(project->groups);  ++i)
  906.                     {
  907.                     for (j = 0;  j < strlen(groups);  ++j)
  908.                         {
  909.                         if (project->groups[i] == groups[j])
  910.                             {
  911.                             /* group checks out */
  912.                             if (option == -1l)
  913.                                 return file;
  914.                             /* no option for this file */
  915.                             /* do option test */
  916.                             for (o = project->option;  o != NULL;  o = o->next)
  917.                                 if (project->option->number == (word) option)
  918.                                     return file;
  919.                             /*
  920.                              *    This file is not selected for installation,
  921.                              *    save output disk label, if one was
  922.                              *    specified
  923.                              */
  924.                             if (file->out_disk != NULL)
  925.                                 disk_label = file->out_disk;
  926.                             return NULL;
  927.                             }
  928.                         }    /* for sub loop */
  929.                     }    /* for loop to test groups */
  930.                     /*
  931.                      *    This file is not selected for installation, save output disk
  932.                      *    label, if one was specified
  933.                      */
  934.                 if (file->out_disk != NULL)
  935.                     disk_label = file->out_disk;
  936.                 return NULL;
  937.                 }        /* if group != NULL */
  938.  
  939.             /* this file had no @Group, but it did have an @Option */
  940.             for (o = project->option;  o != NULL;  o = o->next)
  941.                 if (o->number == (word) option)
  942.                     return file;
  943.             /*
  944.              *    This file is not selected for installation, save output disk
  945.              *    label, if one was specified
  946.              */
  947.             if (file->out_disk != NULL)
  948.                 disk_label = file->out_disk;
  949.  
  950.             /* free allocated mem and clear structure to zeros */
  951. #            ifdef FREE
  952.             if (file->in_fname != NULL)
  953.                 free(file->in_fname);
  954.             if (file->out_disk != NULL)
  955.                 free(file->out_disk);
  956.             if (file->out_fname != NULL)
  957.                 free(file->out_fname);
  958.             free((byte *) file);
  959. #            endif
  960.             return NULL;
  961.         default:
  962.             if (execute_if(in, project, token))
  963.                 continue;
  964.             unexpected(string);
  965.             }            /* switch (token) */
  966.  
  967.         /* processing only gets to this point after an @OUTxxx command */
  968.         if (project->out_size >= min_val * 1024L)
  969.             parse_out(in, project, file, dflt);
  970.         else
  971.             ignore_out(in, project, FALSE);
  972.         }                /* forever */
  973.     }
  974.  
  975.  
  976. static void parse_out(in, project, file, dflt)
  977. int in;
  978. project_t *project;
  979. file_t *file;
  980. byte *dflt;                /* current macro substitution text */
  981.     {                    /* parse_out */
  982.     unsigned i = 0;
  983.     int c;
  984.     int token;
  985.     var_t *v;
  986.  
  987.     memset(temp, 0, sizeof(temp));
  988.  
  989.     if (dflt != NULL)
  990.         dflt[0] = dflt[0];    /* quiet lint */
  991.     /* release any memory allocated to this field */
  992.     if (file->out_disk != NULL)
  993.         {
  994. #        ifdef FREE
  995.         free(file->out_disk);
  996. #        endif
  997.         file->out_disk = NULL;
  998.         }
  999.     if (disk_label != NULL)
  1000.         {
  1001.         file->out_disk = disk_label;
  1002. #        ifdef FREE
  1003.         free(disk_label);
  1004. #        endif
  1005.         disk_label = NULL;
  1006.         }
  1007.  
  1008.     skip_white(in);
  1009.     forever
  1010.         {
  1011.         if (i >= sizeof (temp))
  1012.             error("String to long: parse_out");
  1013.  
  1014.         c = readch(in, TRUE);
  1015.         c = toupper(c);
  1016.         switch (c)
  1017.             {
  1018.         case EOF:
  1019.             unreadch(c);
  1020.             return;
  1021.         case '\"':        /* output disk label */
  1022.             if (project->removable == TRUE)
  1023.                 {
  1024.                 unreadch('"');
  1025.                 file->out_disk = get_str(in, project);
  1026.                 }
  1027.             else
  1028.                 {
  1029.                 /* ignore label since destination is a fixed disk */
  1030.                 while ((c = readch(in, TRUE)) != '"' && c != EOF)
  1031.                     ;
  1032.                 }
  1033.             i = 0;
  1034.             match(in, ":");
  1035.             break;
  1036.         case '@':        /* must be subdir or default or '@' */
  1037.             {
  1038.             unreadch(c);
  1039.             token = get_token(in, project);
  1040.             switch (token)
  1041.                 {
  1042.             case AT:
  1043.                 temp[i++] = '@';
  1044.                 continue;
  1045.             case SUBDIR:
  1046.                 strcpy(string, "SUBDIR");
  1047.                 /* intentional fall through */
  1048.             case ILLEGAL:
  1049.                 if ((v = lookup_var(project, string)) == NULL)
  1050.                     unexpected(string);
  1051.                 if (v->type == dir_var_type)
  1052.                     {
  1053.                     if (i == 0)
  1054.                         {
  1055.                         /*
  1056.                          *    format: @Out @Subdir
  1057.                          *
  1058.                          *    @Subdirxx is the first @out token
  1059.                          */
  1060. /*                        temp[0] = project->out_drive;
  1061.                         temp[1] = ':';
  1062.                         strcpy(temp+2,v->string);
  1063. */
  1064.                         if (v->string[0] == '\\')
  1065.                             strcpy(temp, v->string + 1);
  1066.                         else
  1067.                             strcpy(temp, v->string);
  1068.                         }
  1069.                     else if (i >= 2 && temp[1] != ':')
  1070.                         {
  1071.                         int shift;
  1072.                         /*
  1073.                          *    format: @Out STRING\@SUBDIR
  1074.                          *        or: @Out @Subdirxxx\...\@Subdiryyy
  1075.                          *
  1076.                          *    Dir was not previously absolutly specified, need
  1077.                          *    to move some chars to the right and insert out_drive
  1078.                          *    and subdir (to change this destination to an
  1079.                          *    abs.
  1080.                          */
  1081.                         shift = 2;    /* space for the "D:" */
  1082.                         /* shift += i; space for current chars */
  1083.                         shift += strlen(project->subdir);
  1084.                         /* space for subdir */
  1085.                         memmove(temp + shift, temp, shift);
  1086.                         memmove(temp + 2, project->subdir, strlen(project->subdir));
  1087.                         temp[0] = project->out_drive;
  1088.                         temp[1] = ':';
  1089.                         strcpy(temp+i+shift-1, v->string);
  1090.                         file->abs = TRUE;
  1091.                         }
  1092.                     else
  1093.                         {
  1094.                         /*
  1095.                          *    format:
  1096.                          *        @Out D:\STRING\@Subdir
  1097.                          *        @Out @Drive:\@Subdir...
  1098.                          *
  1099.                          *    Dir already is already absolute - just append
  1100.                          *    this subdir to end of existing subdir.
  1101.                          */
  1102.                         if (temp[i - 1] == '\\' && v->string[0] == '\\')
  1103.                             --i;
  1104.                         strcpy(temp + i, v->string);
  1105.                         file->abs = TRUE;
  1106.                         }
  1107.                     }
  1108.                 else if (v->type == drive_var_type)
  1109.                     {
  1110.                     match(in, ":");
  1111.                     if (i != 0)
  1112.                         error("output drive override must be first");
  1113.                     /*
  1114.                      *    format: @Out @OutDrivexx
  1115.                      *
  1116.                      *    Override the default output drive.
  1117.                      */
  1118.                     temp[0] = v->drive;
  1119.                     temp[1] = ':';
  1120.                     temp[2] = 0;    /* for strlen() purposes */
  1121.                     file->abs = TRUE;
  1122.                     }
  1123.                 else
  1124.                     unexpected(string);
  1125.                 i = strlen(temp);
  1126.                 continue;
  1127.             default:
  1128.                 unexpected(string);
  1129.                 }
  1130.             }
  1131.             continue;
  1132.         case '*':
  1133.             if (i > 0 && temp[i - 1] == '.')
  1134.                 {
  1135.                 /* use the input filename extension, if any */
  1136.                 parse_file(file->in_fname, NULL, NULL, NULL, temp + i);
  1137.                 i = strlen(temp);
  1138.                 }
  1139.             else
  1140.                 {
  1141.                 /* use the input filename node */
  1142.                 parse_file(file->in_fname, NULL, NULL, temp + i, NULL);
  1143.                 i = strlen(temp);
  1144.                 }
  1145.             continue;
  1146.         case '\\':        /* test for already existing separator */
  1147.             if (i > 0 && temp[i - 1] != '\\')
  1148.                 {
  1149.                 temp[i++] = '\\';
  1150.                 }
  1151.             else if (i == 0)    /* subdir override */
  1152.                 {
  1153.                 file->abs = TRUE;
  1154.                 temp[i++] = project->out_drive;
  1155.                 temp[i++] = ':';
  1156.                 temp[i++] = '\\';
  1157.                 }
  1158.             continue;
  1159.         default:
  1160.             if (execute_if(in, project, token))
  1161.                 continue;
  1162.             if (isalnum(c) || c == '$' || c == '&' || c == '#' || c == '!' ||
  1163.                 c == '%' || c == '\'' || c == '`' || c == '(' || c == ')' ||
  1164.                 c == '{' || c == '}' || c == '_' || c == '.' || c == '-')
  1165.                 {
  1166.                 if (file->abs)
  1167.                     {
  1168.                     /*
  1169.                      *    handle:
  1170.                      *        @File afile @Out @drive:afile
  1171.                      *
  1172.                      *    which is an abs file that goes to the default
  1173.                      *    output subdir
  1174.                      */
  1175.                     if (i == 2)
  1176.                         {
  1177.                         /* no subdir yet */
  1178.                         strcat(temp, project->subdir);
  1179.                         i = strlen(temp);
  1180.                         }
  1181.                     temp[i++] = (byte) c;
  1182.                     }
  1183.                 else
  1184.                     temp[i++] = (byte) c;
  1185.                 }
  1186.             else
  1187.                 {
  1188.                 temp[i] = 0;
  1189.                 file->out_fname = (byte *) smart_calloc(1, strlen(temp) + 2, "parse_out");
  1190.                 strcpy(file->out_fname, temp);
  1191.                 i = 0;
  1192.                 return;
  1193.                 }
  1194.             }            /* switch(c) */
  1195.         }                /* while */
  1196.     }                    /* parse_out */
  1197.  
  1198. /*
  1199.  *    Ignore the rest of the file since it is not selected for installation ...
  1200.  *    EXCEPT, the output disk label must be retained if destination is not
  1201.  *    a fixed disk.
  1202.  */
  1203.  
  1204. static void ignore_out(in, project, skip_label)
  1205. int in;
  1206. project_t *project;
  1207. int skip_label;
  1208.     {                    /* ignore_out */
  1209.     int c;
  1210.     int token;
  1211.  
  1212.     disk_label = NULL;
  1213.     skip_white(in);
  1214.     forever
  1215.         {
  1216.         c = readch(in, TRUE);
  1217.         c = toupper(c);
  1218.         switch (c)
  1219.             {
  1220.         case EOF:
  1221.             unreadch(c);
  1222.             return;
  1223.         case '\"':        /* output disk label */
  1224.             if (project->removable == FALSE || skip_label)
  1225.                 {
  1226.                 /* ignore label since destination is a fixed disk */
  1227.                 while ((c = readch(in, TRUE)) != '"' && c != EOF)
  1228.                     ;
  1229.                 }
  1230.             else
  1231.                 {
  1232.                 /*
  1233.                  *    we may need this label if this file is not selected for
  1234.                  *    installation but the @Out### is valid for this output
  1235.                  *    disk size.
  1236.                  */
  1237.                 unreadch('"');
  1238. #                ifdef FREE
  1239.                 free(get_str(in, project));
  1240. #                else
  1241.                 disk_label = get_str(in, project);
  1242. #                endif
  1243.                 }
  1244.             match(in, ":");
  1245.             break;
  1246.         case '@':        /* must be subdir or default or '@' */
  1247.             unreadch(c);
  1248.             token = get_token(in, project);
  1249.             switch (token)
  1250.                 {
  1251. /*            case SUBDIR:
  1252.             case MACRO:*/
  1253.             case AT:
  1254.                 continue;
  1255.             default:
  1256.                 if (execute_if(in, project, token))
  1257.                     continue;
  1258.                 unexpected(string);
  1259.                 }
  1260.             continue;
  1261.         case ':':
  1262.         case '*':
  1263.         case '\\':
  1264.             continue;
  1265.         default:
  1266.             if (execute_if(in, project, token))
  1267.                 continue;
  1268.             if (isalnum(c) || c == '$' || c == '&' || c == '#' || c == '!' ||
  1269.                 c == '%' || c == '\'' || c == '`' || c == '(' || c == ')' ||
  1270.                 c == '{' || c == '}' || c == '_' || c == '.' || c == '-')
  1271.                 ;
  1272.             else
  1273.                 return;
  1274.             }            /* switch(c) */
  1275.         }                /* while */
  1276.     }                    /* ignore_out */
  1277.  
  1278.  
  1279. /*
  1280.  *    This is the master function in this file.  All other blocks are parsed
  1281.  *    by routines called by compile().
  1282.  */
  1283.  
  1284. void compile(in, project)
  1285. int in;
  1286. project_t *project;
  1287.     {                    /* compile */
  1288.     int token;
  1289.     bool no_disk = TRUE;
  1290.     static bool first = TRUE;    /* TRUE first time compile() is called */
  1291.     byte *s;
  1292.     bool first_token = TRUE;
  1293. #    ifdef ABSORB
  1294.     long end, templ;
  1295. #    endif
  1296.  
  1297.     start = tell(in);    /* see where we are w/i script file */
  1298. #    ifdef ABSORB
  1299.     end = start;
  1300. #    endif
  1301.  
  1302.     restart = 0l;
  1303.     disk_label = NULL;
  1304.     token = get_token(in, project);
  1305.  
  1306. #    ifdef ABSORB
  1307.     /* verify that COMBINE.EXE was run on this file */
  1308.     if(token == SCRIPTSIZE)
  1309.         {
  1310.         templ = get_long(in);
  1311.         assert(templ > 30L);
  1312.         end = start + templ;
  1313.         max_script = templ - (0x7FFFFFFl - max_script); /* reduce by bytes already read */
  1314.         token = get_token(in, project);
  1315.         }
  1316.     else
  1317.         {
  1318.         /* this file has not been properly processed */
  1319.         if(token == STRING && strncmp(string, "MZ", 2) == 0)
  1320.             {
  1321.             wputs(error_w, "Before you run the self-extracting version of INSTALL.EXE");
  1322.             wputs(error_w, "you must combine INSTALL.EXE INSTALL.DAT and a library file");
  1323.             wputs(error_w, "using COMBINE.EXE");
  1324.             wputs(error_w, NULL);
  1325.             }
  1326.         else
  1327.             {
  1328.             wputs(error_w, "This self-extracting (SFX) file is corrupted.");
  1329.             }
  1330.         put_error(error_w);
  1331.         say_bye();
  1332.         }
  1333. #    endif /* ABSORB */
  1334.  
  1335.     if (first && token != DEFINEPROJECT)
  1336.         expected(tokens[DEFINEPROJECT].text);
  1337.  
  1338.     first = FALSE;
  1339.     unget_token(token);
  1340.     /*
  1341.      *    parse blocks
  1342.      */
  1343.     while ((token = get_token(in, project)) != EOF)
  1344.         {
  1345.         switch (token)
  1346.             {
  1347.         case SETAUTOEXEC:
  1348.             get_autoexec(in, project);
  1349.             continue;
  1350.         case SETCONFIG:
  1351.             get_config(in, project);
  1352.             continue;
  1353.         case WELCOME:
  1354.         case DISPLAY:
  1355.             get_welcome(in, project);
  1356.             first_token = FALSE;
  1357.             continue;
  1358.         case DEFINEPROJECT:
  1359.             get_project(in, project);
  1360.             first_token = FALSE;
  1361.             continue;
  1362.         case DEFINEVARS:
  1363.             get_vars(in, project);
  1364.             first_token = FALSE;
  1365.             continue;
  1366.         case GETSTRING:
  1367.             get_string(in, project);
  1368.             first_token = FALSE;
  1369.             continue;
  1370.         case GETINTEGER:
  1371.             get_integer(in, project);
  1372.             first_token = FALSE;
  1373.             continue;
  1374.         case GETOUTDRIVE:
  1375.             get_out_drive(in, project);
  1376.             first_token = FALSE;
  1377.             continue;
  1378.         case GETSUBDIR:
  1379.             get_subdir(in, project);
  1380.             first_token = FALSE;
  1381.             continue;
  1382.         case GETGROUPS:
  1383. #            ifdef STRICT
  1384.             if (!no_disk)
  1385.                 error("%s blocks must appear before any %s blocks: compile",
  1386.                     tokens[GETGROUPS].text, tokens[DEFINEDISK].text);
  1387. #            endif
  1388.             get_groups(in, project);
  1389.             first_token = FALSE;
  1390.             continue;
  1391.         case GETOPTION:
  1392. #            ifdef STRICT
  1393.             if (!no_disk)
  1394.                 error("%s blocks must appear before any %s blocks: compile",
  1395.                     tokens[GETOPTION].text, tokens[DEFINEDISK].text);
  1396. #            endif
  1397.             get_options(in, project);
  1398.             first_token = FALSE;
  1399.             continue;
  1400.         case DEFINEDISK:
  1401.             if (no_disk)
  1402.                 {
  1403.                 /* first disk */
  1404.                 if (!know_fixed)
  1405.                     /* script file did not specify prompting end-user for out drive */
  1406.                     get_fixed(project);
  1407.                 if (project->out_size == 0L)
  1408.                     {
  1409.                     if (project->out_drive == 0)
  1410.                         error("The output drive letter was not specified");
  1411.                     project->out_free = disk_free(project->out_drive);
  1412.                     project->out_size = disk_size(project->out_drive);
  1413.                     }
  1414.                 }
  1415.             get_disk(in, project);
  1416.             /*
  1417.              *    If this is the first disk, save name of disk label
  1418.              *    in case there is a @Finish block, and we need to prompt
  1419.              *    end-user for the first disk once all other files are
  1420.              *    copied.
  1421.              */
  1422.             no_disk = FALSE;
  1423.             first_token = FALSE;
  1424.             continue;
  1425.         case FINISH:
  1426.             /*
  1427.              *    If there is adequate RAM, read @Finish block into memory.
  1428.              *    Otherwise, remember its starting location within the
  1429.              *    script file, and prompt end-user to replace the disk in
  1430.              *    @InDrive with the first distribution disk.
  1431.              */
  1432. #            ifdef STRICT
  1433.             if (no_disk)
  1434.                 expected(tokens[DEFINEDISK].text);
  1435. #            endif
  1436.             restart = get_pos(in);
  1437.             old_line = line;
  1438. #            ifndef ABSORB
  1439.             close(in);
  1440.             in = -1;
  1441. #            endif
  1442.             break;
  1443.         default:
  1444.             if (execute_if(in, project, token))
  1445.                 break;
  1446.             unexpected(string);
  1447.             }
  1448.         if (token == FINISH)
  1449.             break;
  1450.         }                /* while token != EOF */
  1451.     if (no_disk)
  1452.         expected(tokens[DEFINEDISK].text);
  1453.  
  1454.     s = getdrives();
  1455.     while (!s[project->out_drive - 'A'])
  1456.         {
  1457.         if (project->out_drive == 0)
  1458.             error("Invalid output drive letter");
  1459.         --project->out_drive;
  1460.         }
  1461.     if (!know_fixed)    /* script file did not specify prompting end-user for out drive */
  1462.         get_fixed(project);
  1463.     if (project->out_size == project->out_free && project->out_free == 0)
  1464.         {
  1465.         if (project->out_drive == 0)
  1466.             error("The output drive letter was not specified");
  1467.         project->out_free = disk_free(project->out_drive);
  1468.         project->out_size = disk_size(project->out_drive);
  1469.         }
  1470.     if (project->in_drive == project->out_drive)
  1471.         error("The output drive cannot be the same as the input drive");
  1472.     if (first_token)
  1473.         ;                /* quiet lint */
  1474. #    ifdef ABSORB
  1475.     /* make sure pointer is at beginning of lib file */
  1476.     lseek(in, end, 0);
  1477. #    else
  1478.     if (in != -1)
  1479.         close(in);
  1480. #    endif
  1481.     }                    /* compile */
  1482.  
  1483.  
  1484.  
  1485. static void get_out_drive(in, project)
  1486. int in;
  1487. project_t *project;
  1488.     {                    /* get_out_drive */
  1489.     /* get dest drive letter */
  1490.     unsigned i, dflt;
  1491.     int token, c;
  1492.     int ret;
  1493.     int max_drive;
  1494.     byte *drive_v[27];
  1495.     byte *drvs;
  1496.     byte *s = (byte *) "Drive %c:";
  1497.     byte out_drive;
  1498.     int d;
  1499.     var_t *v;
  1500. #    ifdef UNUSED_CODE
  1501.     long fpos = get_pos(in);    /* remember where we are in case we need to
  1502.                                    re-read this block */
  1503. #    endif
  1504.  
  1505.     out_drive = project->out_drive;
  1506.  
  1507.     drvs = getdrives();
  1508.  
  1509.     /* display and ignore all leading white space */
  1510.     while ((c = readch(in, TRUE)) != EOF && isspace(c))
  1511.         sputch((byte) c);
  1512.  
  1513.     /* see if first non-whitespace token is a valid drive-type */
  1514.     if (c == '@')
  1515.         {
  1516.         unreadch(c);
  1517.         token = get_token(in, project);
  1518.         if (token == ILLEGAL)
  1519.             {
  1520.             if ((v = lookup_var(project, string)) == NULL || v->type != drive_var_type)
  1521.                 {
  1522.                 v = NULL;
  1523.                 unreadstr(string);
  1524.                 out_drive = project->out_drive;
  1525.                 }
  1526.             else
  1527.                 {
  1528.                 /* valid subdir variable */
  1529.                 out_drive = v->drive;
  1530.                 }
  1531.             }
  1532.         else
  1533.             {
  1534.             unreadstr(tokens[token].text);
  1535.             v = NULL;
  1536.             }
  1537.         }
  1538.  
  1539.     forever
  1540.         {
  1541.         while ((c = readch(in, TRUE)) != EOF && c != '@')
  1542.             sputch((byte) c);
  1543.  
  1544.         if (c == EOF)
  1545.             unexpected("end of file");
  1546.  
  1547.         /* c == @ */
  1548.         unreadch(c);
  1549.         token = get_token(in, project);
  1550.  
  1551.         if (token == ENDOUTDRIVE)
  1552.             break;
  1553.  
  1554.         switch (token)
  1555.             {
  1556.         case SUPPRESS:
  1557.             d = get_char(in);
  1558.             d = toupper(d);
  1559.             if (d >= 'A' && d <= 'Z')
  1560.                 drvs[d - 'A'] = 0;    /* hide this drive letter */
  1561.             else
  1562.                 expected("a drive ID letter");
  1563.             break;
  1564.         case PROMPT:
  1565.             match(in, "=");
  1566.             get_str(in, project);
  1567.             break;
  1568.         default:
  1569.             if (execute_verb(in, project, token))
  1570.                 continue;
  1571.             if (execute_if(in, project, token))
  1572.                 continue;
  1573.             unexpected(string);
  1574.             }
  1575.         }
  1576.  
  1577.     dflt = 0;
  1578.     for (i = 0,  max_drive = 0;  i < 26;  ++i)
  1579.         {
  1580.         if (drvs[i] == 1    /* && i + 'A' != project->in_drive */
  1581.             )
  1582.             {
  1583.             drive_v[max_drive] = (byte *) smart_calloc(1, strlen(s) + 2, "get_out_drive");
  1584.             sprintf(drive_v[max_drive++], s, i + 'A');
  1585.             if (i + 'A' <= out_drive)
  1586.                 dflt = max_drive - 1;
  1587.             }
  1588.         }
  1589.  
  1590.     --max_drive;
  1591.     if (max_drive < 10)
  1592.         i = max_drive;
  1593.     else
  1594.         i = 10;
  1595.  
  1596.     if (out_drive - 'A' > (byte) max_drive)
  1597.         out_drive = (byte) max_drive + 'A';
  1598.  
  1599.     /*
  1600.      *    The following loop is repeated until a valid output drive is
  1601.      *    selected or until the end-user aborts (hits [Esc]).
  1602.      */
  1603.     forever
  1604.         {
  1605.         if ((ret = select_m(drive_v, 5, 34, i + 5, 45, max_drive, dflt, FALSE)) == -1)
  1606.             say_bye();
  1607.  
  1608.         /* v == NULL only if we are getting the default output drive */
  1609.         if (v == NULL)
  1610.             project->out_drive = out_drive = drive_v[ret][6];
  1611.         else
  1612.             v->drive = out_drive = drive_v[ret][6];
  1613.  
  1614.         /*
  1615.          *     This is the most intuitive place to determine the output disk
  1616.          *    removability status.
  1617.          */
  1618.         if (v != NULL)
  1619.             /* this is a var, skip drive characteristic test */
  1620.             break;
  1621.  
  1622.         get_fixed(project);
  1623.         know_fixed = TRUE;    /* don't ask twice */
  1624.         /* this is the primary output drive, get disk size/space */
  1625.         if (((project->out_free = disk_free(project->out_drive)) != -1L) &&
  1626.             ((project->out_size = disk_size(project->out_drive)) != -1L))
  1627.             break;    /* all ok, exit forever loop */
  1628.  
  1629.         /*
  1630.          *    If execution gets here, there was an error.
  1631.          */
  1632.         wputs(message_w, "You do not appear to have the minimum access rights to");
  1633.         wputs(message_w, "drive %c: which are required for installation.", out_drive);
  1634.         wputs(message_w, NULL);
  1635.         wputs(message_w, "Press the [Esc] key to abort, any other key to continue...");
  1636.         put_message(message_w);
  1637.         }                /* forever */
  1638.  
  1639.     /*
  1640.      *    When execution gets here, an output disk has been successfully
  1641.      *    selected.
  1642.      */
  1643.     cls();
  1644.     }                    /* get_out_drive */
  1645.  
  1646. byte *type[3] =
  1647.     {
  1648.     "Hard Disk",
  1649.     "Floppy Disk",
  1650.     NULL
  1651.     };
  1652.  
  1653. /*
  1654.  *    Determine if the output drive is removable or non-removable.
  1655.  */
  1656.  
  1657. static void get_fixed(project)
  1658. project_t *project;
  1659.     {                    /* get_out_drive */
  1660.     /* get dest drive letter */
  1661.     int start;
  1662.     int ret;
  1663.  
  1664.     /* see if there is any work to be done */
  1665.     if (know_fixed)
  1666.         {
  1667.         /*
  1668.          *    Either this function has already been called or @HardDisk was
  1669.          *    used in INSTALL.DAT script file.
  1670.          */
  1671.         return;
  1672.         }
  1673.  
  1674.     know_fixed = TRUE;
  1675.     /*
  1676.      *    Ask DOS for removability status, if it knows.
  1677.      */
  1678.     ret = ckremovable(project->out_drive - 'A' + 1);
  1679.  
  1680.     if (ret > 0)
  1681.         {
  1682.         /* removable */
  1683.         project->removable = TRUE;
  1684.         return;
  1685.         }
  1686.     else if (ret == 0)
  1687.         {
  1688.         /* non-removable */
  1689.         project->removable = FALSE;
  1690.         return;
  1691.         }
  1692.  
  1693.     cls();
  1694.     /* could not determine (probably and old device driver or old DOS) */
  1695.  
  1696.     start = 0;            /* assume fixed */
  1697.  
  1698.     move(2, 18);
  1699.     wputs(tty_w, "  Please select the disk type of output disk %c:", project->out_drive);
  1700.     move(10, 1);
  1701.     wputs(tty_w, "  If you are installing onto a hard disk, RAM disk, or other NON-REMOVABLE\n");
  1702.     wputs(tty_w, "  disk please select \"Hard Disk\".  If you are installing onto a 5.25\"\n");
  1703.     wputs(tty_w, "  floppy diskette, 3.5\" microfloppy diskette, or other REMOVABLE disk\n");
  1704.     wputs(tty_w, "  please select \"Floppy Disk\".");
  1705.     if ((ret = select_m(type, 5, 33, 6, 47, 1, start, FALSE)) == -1)
  1706.         say_bye();
  1707.     project->removable = (unsigned) ret;
  1708.     cls();
  1709.     }                    /* get_fixed */
  1710.  
  1711.  
  1712. /*
  1713.  *    Get a subdirectory string from the end-user.
  1714.  */
  1715.  
  1716. static void get_subdir(in, project)
  1717. int in;
  1718. project_t *project;
  1719.     {                    /* get_subdir */
  1720.     var_t *v = NULL;
  1721.     int token, c;
  1722.     byte prompt[100];
  1723.     byte path[100];
  1724.     byte *dflt = NULL, *ret = NULL;
  1725.     unsigned len;
  1726.     prompt[0] = 0;
  1727.  
  1728.     /* display and ignore all leading white space */
  1729.     while ((c = readch(in, TRUE)) != EOF && isspace(c))
  1730.         sputch((byte) c);
  1731.  
  1732.     /* see if first non-whitespace token is an valid dir-type */
  1733.     if (c == '@')
  1734.         {
  1735.         unreadch(c);
  1736.         token = get_token(in, project);
  1737.         if (token == ILLEGAL)
  1738.             {
  1739.             if ((v = lookup_var(project, string)) == NULL || v->type != dir_var_type)
  1740.                 {
  1741.                 v = NULL;
  1742.                 dflt = project->subdir;
  1743.                 unreadstr(string);
  1744.                 }
  1745.             else
  1746.                 {
  1747.                 /* valid subdir variable */
  1748.                 dflt = v->string;
  1749.                 }
  1750.             }
  1751.         else
  1752.             {
  1753.             unreadstr(tokens[token].text);
  1754.             dflt = project->subdir;
  1755.             v = NULL;
  1756.             }
  1757.         }
  1758.  
  1759.     forever
  1760.         {
  1761.         while ((c = readch(in, TRUE)) != EOF && c != '@')
  1762.             sputch((byte) c);
  1763.  
  1764.         if (c == EOF)
  1765.             unexpected("end of file");
  1766.  
  1767.         unreadch(c);
  1768.         token = get_token(in, project);
  1769.  
  1770.         if (token == ENDSUBDIR)
  1771.             break;
  1772.  
  1773.         switch (token)
  1774.             {
  1775.         case DEFAULT:
  1776.             match(in, "=");
  1777.             dflt = get_str(in, project);
  1778.             if (v != NULL)
  1779.                 v->string = dflt;
  1780.             strupr(dflt);
  1781.             continue;
  1782.         case PROMPT:
  1783.             match(in, "=");
  1784.             strcpy(prompt, get_str(in, project));
  1785.             continue;
  1786.         default:
  1787.             if (execute_verb(in, project, token))
  1788.                 continue;
  1789.             if (execute_if(in, project, token))
  1790.                 continue;
  1791.             unexpected(string);
  1792.             }
  1793.         }
  1794.     if (!prompt[0])
  1795.         strcpy(prompt, " Please Enter The Subdirectory Name: ");
  1796.  
  1797.     /* remove trailing \\ */
  1798.     if (dflt != NULL)
  1799.         /* default to specified output dir */
  1800.         strcpy(path, trim_dir(dflt));
  1801.     else
  1802.         strcpy(path, "\\");
  1803.  
  1804.  
  1805.     /* now, display a one-line edit box */
  1806.     if (put_prompt(prompt, path, 65, 6) == ESC)
  1807.         {
  1808.         /* end-user hit [Esc] - abort install */
  1809.         say_bye();
  1810.         }
  1811.  
  1812.     strupr(path);
  1813.     len = strlen(path);
  1814.     if (len == 0)
  1815.         {
  1816.         /* a zero-length string was returned - use default */
  1817.         len = strlen(dflt);
  1818.         strcpy(path, "\\");
  1819.         }
  1820.  
  1821.     if (path[len - 1] != '\\')
  1822.         {
  1823.         /* ensure path ends with a '\\' */
  1824.         path[len++] = '\\';
  1825.         path[len++] = '\0';
  1826.         }
  1827.  
  1828.     /* get mem for dynamically calloced RAM */
  1829.     ret = (byte *) smart_calloc(1, len + 5, "get_subdir");
  1830.     if (path[0] != '\\')
  1831.         strcpy(ret, "\\");    /* insert leading \\ char */
  1832.     strcat(ret, path);
  1833.     if (v == NULL)
  1834.         {
  1835.         project->subdir = ret;
  1836.         v = lookup_var(project, "SUBDIR");
  1837.         }
  1838.     v->string = ret;
  1839.     cls();
  1840.     }                    /* get_subdir */
  1841.  
  1842.  
  1843.  
  1844. /*
  1845.  *    Parse a @GetGroup/@EndGroup block.
  1846.  */
  1847.  
  1848. static void get_groups(in, project)
  1849. int in;
  1850. project_t *project;
  1851.     {                    /* get_groups */
  1852.     /* level indicates multiple get_groups calls */
  1853.     static int level = 0;
  1854.     int c, token, i;    /* temp vars */
  1855.     byte *group[26];    /* strings for each group */
  1856.     byte letter[26];    /* letter corresponding to each group */
  1857.     unsigned long req[26];
  1858.     bool no_groups = TRUE;    /* diagnostic flag */
  1859.     unsigned max_len;    /* max string len */
  1860.     int current = 0;    /* current group being input */
  1861.     int ret;            /* return value */
  1862.     byte *s;            /* temp storage */
  1863.     bool checkbox = FALSE;    /* TRUE if multiple items may be selected */
  1864.     int l;
  1865. #    define MAX_STRING (76)    /* max quoted string size */
  1866.  
  1867.     for (i = 0;  i < 26;  ++i)
  1868.         {
  1869.         group[i] = (byte *) smart_calloc(1, 80, "get_groups");
  1870.         letter[i] = 0;
  1871.         req[i] = 0l;
  1872.         }
  1873.  
  1874.     for (;;)
  1875.         {
  1876.         while ((c = readch(in, TRUE)) != EOF && c != '@')
  1877.             sputch((byte) c);
  1878.  
  1879.         if (c == EOF)
  1880.             unexpected("end of file");
  1881.  
  1882.         unreadch(c);
  1883.         token = get_token(in, project);
  1884.  
  1885.         if (token == ENDGROUPS)
  1886.             break;
  1887.  
  1888.         switch (token)
  1889.             {
  1890.         case CHECKBOX:
  1891.             checkbox = TRUE;
  1892.             continue;
  1893.         case PROMPT:    /* ignore prompt */
  1894.             match(in, "=");
  1895.             get_str(in, project);
  1896.             continue;
  1897. /*
  1898.         case REQUIRES:
  1899.             if (current <= 0)
  1900.                 expected(tokens[SET].text);
  1901.             if ((req[current - 1] = get_long(in)) == 0l)
  1902.                 expected("a group file space requirement number");
  1903.             continue;
  1904. */
  1905.         case SET:
  1906.             c = get_char(in);
  1907.             c = toupper(c);
  1908.             if (! isalpha(c))
  1909.                 expected("a group designation letter");
  1910.             if (current >= 26)
  1911.                 error("only 26 groups are allowed");
  1912.             letter[current] = (byte) c;
  1913.             match(in, "=");
  1914.             s = get_str(in, project);
  1915.             if (strlen(s) > MAX_STRING || (checkbox && strlen(s) + 3 > MAX_STRING))
  1916.                 error("string too long");
  1917.             strcpy(group[current++], s);
  1918.             free(s);
  1919.             no_groups = FALSE;
  1920.             skip_white(in);
  1921.             continue;
  1922.         default:
  1923.             if (execute_verb(in, project, token))
  1924.                 continue;
  1925.             if (execute_if(in, project, token))
  1926.                 continue;
  1927.             unexpected(string);
  1928.             }
  1929.         }
  1930.  
  1931.     if (no_groups)
  1932.         expected(tokens[SET].text);
  1933.  
  1934.     max_len = 0;
  1935.     for (i = 0;  i < current;  ++i)
  1936.         {
  1937.         if (group[i] != NULL && strlen(group[i]) > max_len)
  1938.             max_len = strlen(group[i]) - 1;
  1939.         }
  1940.     max_len += 4;
  1941.     if (checkbox)
  1942.         max_len += 4;
  1943.     if (max_len >= MAX_STRING)
  1944.         {
  1945.         wputs(message_w, "WARNING: %s string to long", tokens[SET].text);
  1946.         put_message(message_w);
  1947.         }
  1948.     if (current <= 12)
  1949.         i = current - 1;
  1950.     else 
  1951.         i = 12;
  1952.  
  1953.     if ((ret = select_m(group, 5, (80 - max_len) / 2, 5 + i, (80 - max_len) / 2 + max_len, current - 1, 0, checkbox)) == -1)
  1954.         {
  1955.         wputs(message_w, "Aborting installation");
  1956.         put_message(message_w);
  1957.         bye();
  1958.         }
  1959.  
  1960.     if (level == 0)
  1961.         {
  1962.         /* first call to get_groups, overwrite any default */
  1963.         project->groups = (byte *) smart_calloc(27, 1, "get_groups");
  1964.         l = 0;
  1965.         if (!checkbox)
  1966.             {
  1967.             project->groups[l++] = letter[ret];
  1968.             }
  1969.         else {
  1970.             for (i = 0;  i < current;  ++i)
  1971.                 {
  1972.                 if (is_set(i))
  1973.                     project->groups[l++] = letter[i];
  1974.                 }
  1975.             }
  1976.         project->groups[l] = '\0';
  1977. /*        project->requirement = req[ret]; */
  1978.         }
  1979.     else 
  1980.         {
  1981.         /*
  1982.          *    Second or later call to get_groups, add this group to all
  1983.          *    previously selected groups.
  1984.          */
  1985.         byte *s = (byte *) smart_calloc(27, 1, "get_groups");
  1986.         if (project->groups != NULL)
  1987.             strcpy(s, project->groups);
  1988.         project->groups = s;
  1989.  
  1990.         l = strlen(s);
  1991.         if (!checkbox)
  1992.             {
  1993.             s[l++] = letter[ret];
  1994.             }
  1995.         else {
  1996.             for (i = 0;  i < current;  ++i)
  1997.                 {
  1998.                 if (is_set(i))
  1999.                     s[l++] = letter[i];
  2000.                 }
  2001.             }
  2002.         s[l] = '\0';
  2003. /*        project->requirement += req[ret]; */
  2004.         }
  2005.     ++level;
  2006.  
  2007.     for (i = 0;  i < 26;  ++i)
  2008.         {
  2009.         if (group[i] != NULL)
  2010.             free(group[i]);
  2011.         }
  2012.     cls();
  2013.     return;
  2014.     }                    /* get_groups */
  2015.  
  2016.  
  2017. static void get_welcome(in, project)
  2018. int in;
  2019. project_t *project;
  2020.     {                    /* get_welcome */
  2021.     int token;
  2022.     int c;
  2023.  
  2024.     forever
  2025.         {
  2026.         while ((c = readch(in, TRUE)) != EOF && c != '@')
  2027.             sputch((byte) c);
  2028.  
  2029.         if (c == EOF)
  2030.             unexpected("end of file");
  2031.  
  2032.         unreadch(c);
  2033.         token = get_token(in, project);
  2034.  
  2035.         if (token == ENDWELCOME || token == ENDDISPLAY)
  2036.             {
  2037.             cls();
  2038.             return;
  2039.             }
  2040.         if (execute_verb(in, project, token))
  2041.             continue;
  2042.         if (execute_if(in, project, token))
  2043.             continue;
  2044.         unexpected(string);
  2045.         }                /* for(;;) */
  2046.     }                    /* get_welcome */
  2047.  
  2048. #ifdef DEMO
  2049. static word disks_read = 0;
  2050. #endif
  2051.  
  2052. static void get_disk(in, project)
  2053. int in;
  2054. project_t *project;
  2055.     {                    /* get_disk */
  2056.     byte *t;
  2057.     byte n[30];            /* node */
  2058.     byte e[10];            /* extension */
  2059.     byte p[100];        /* path */
  2060. /*
  2061.     int no_files = TRUE;
  2062. */
  2063.     disk_t *disk;
  2064.     disk_t *last;
  2065.     file_t *last_file = NULL, *new_file, *last_normal;
  2066.     int token;
  2067.     int no_label = TRUE;
  2068.  
  2069. #    ifdef DEMO
  2070.     if (++disks_read > 3)
  2071.         error("This restricted version of INSTALL will only read 3 disks");
  2072. #    endif
  2073.  
  2074.     disk = (disk_t *) smart_calloc(1, sizeof(disk_t), "get_disk disk");
  2075.  
  2076.     if (project->disk == NULL)
  2077.         project->disk = disk;
  2078.     else
  2079.         {
  2080.         last = project->disk;
  2081.         while (last->next != NULL)
  2082.             last = last->next;
  2083.         last->next = disk;
  2084.         }
  2085.  
  2086.     for (;;)
  2087.         {
  2088.         token = get_token(in, project);
  2089.  
  2090.         switch (token)
  2091.             {
  2092.         case LABEL:
  2093.             match(in, "=");
  2094.             disk->label = get_str(in, project);
  2095.             /*
  2096.              *    If this is the first disk, save name of disk label
  2097.              *    in case there is a @Finish block, and we need to prompt
  2098.              *    end-user for the first disk once all other files are
  2099.              *    copied.
  2100.              */
  2101.             if(first_disk_label == NULL)
  2102.                 {
  2103.                 first_disk_label = (byte *)smart_calloc(1, strlen(disk->label) + 3, "get_dsk_label");
  2104.                 strcpy(first_disk_label, disk->label);
  2105.                 }
  2106.             no_label = FALSE;
  2107.             continue;
  2108.         case BEGINLIB:
  2109.             /* first get parent info */
  2110.             if (in_lib)
  2111.                 unexpected(tokens[BEGINLIB].text);
  2112.             new_file = (file_t *) smart_calloc(1, sizeof(file_t), "get_disk");
  2113.             new_file->type = PARENT;
  2114.             if ((t = get_fname(in)) == NULL)
  2115.                 expected("a library filename");
  2116.             strupr(t);
  2117.             parse_file(t, NULL, p, n, e);
  2118.             /* default extension is .LIF */
  2119.             if (!e[0])
  2120.                 strcpy(e, "LIF");
  2121.             new_file->in_fname = (byte *) smart_calloc(1, strlen(n) + strlen(p) + 5, "get_disk 2");
  2122.             sprintf(new_file->in_fname, "%s%s.%s", p, n, e);
  2123.             new_file->type = PARENT;
  2124.             new_file->verify = TRUE;
  2125.             in_lib = TRUE;
  2126.             /* now link-in parent */
  2127.             if (last_file == NULL)
  2128.                 disk->file = new_file;
  2129.             else 
  2130.                 last_file->next = new_file;
  2131.             last_file = new_file;
  2132.             last_normal = new_file;
  2133.             continue;
  2134.         case ENDLIB:
  2135.             if (!in_lib)
  2136.                 unexpected(tokens[ENDLIB].text);
  2137.             last_file = last_normal;
  2138.             in_lib = FALSE;
  2139.             continue;
  2140.         case IFILE:
  2141. /*
  2142.             no_files = FALSE;
  2143. */
  2144.             if ((new_file = get_file(in, project)) != NULL)
  2145.                 {
  2146.                 if (in_lib && last_file->type == PARENT)
  2147.                     last_file->sub = new_file;
  2148.                 else if (!in_lib && disk->file == NULL)
  2149.                     disk->file = new_file;
  2150.                 else 
  2151.                     last_file->next = new_file;
  2152.                 last_file = new_file;
  2153.                 }
  2154.             continue;
  2155.         case ENDDISK:
  2156. /*
  2157.             if (no_files)
  2158.                 expected(tokens[IFILE].text);
  2159. */
  2160.             if (in_lib)
  2161.                 expected(tokens[ENDLIB].text);
  2162.             if (no_label)
  2163.                 expected(tokens[LABEL].text);
  2164.             return;
  2165.         default:
  2166.             if (execute_if(in, project, token))
  2167.                 continue;
  2168.             unexpected(string);
  2169.             }
  2170.         }
  2171.     }                    /* get_disk */
  2172.  
  2173.  
  2174. /*
  2175.  *     Compile a @DefineProject/@EndProject block.
  2176.  */
  2177.  
  2178. static void get_project(in, project)
  2179. int in;
  2180. project_t *project;
  2181.     {                    /* get_project */
  2182.     int token;
  2183.     int c;
  2184.     byte *s;
  2185.  
  2186.     /*
  2187.      *        get project info.
  2188.      */
  2189.     forever
  2190.         {
  2191.         token = get_token(in, project);
  2192.         switch (token)
  2193.             {
  2194.         case FORMATALLOWED:
  2195. #            ifdef FORMAT_ALLOWED
  2196.             project->format_ok = TRUE;
  2197. #            else
  2198.             error("This version of INSTALL does not allow formatting");
  2199. #            endif
  2200.             continue;
  2201.         case INDISKBELL:
  2202.             project->in_bell = TRUE;
  2203.             continue;
  2204.         case OUTDISKBELL:
  2205.             project->out_bell = TRUE;
  2206.             continue;
  2207.         case VERIFY:
  2208.             project->verify = TRUE;
  2209.             continue;
  2210.         case TERSE:
  2211.             project->terse = TRUE;
  2212.             continue;
  2213.         case REQUIRES:
  2214.             c = get_char(in);
  2215.             unreadch(c);
  2216.             if (c == '@')
  2217.                 {
  2218.                 token = get_token(in, project);
  2219.                 if (token != HARDDISK)
  2220.                     unexpected(string);
  2221.                 project->removable = FALSE;
  2222.                 /* arbitrarily set outsize to 2 gigabytes */
  2223.                 project->out_size = 0x7FFFFFFFL;
  2224.                 know_fixed = TRUE;
  2225.                 }
  2226.             else if ((project->requirement = get_long(in)) == 0l)
  2227.                 expected("a project file space requirement number");
  2228.             continue;
  2229.         case INDRIVE:
  2230.             match(in, "=");
  2231.             c = get_char(in);
  2232.             c = toupper(c);
  2233.             if (! isalpha(c))
  2234.                 expected("a drive letter");
  2235.             project->in_drive = (byte) c;
  2236.             continue;
  2237.         case OUTDRIVE:
  2238.             match(in, "=");
  2239.             c = get_char(in);
  2240.             c = toupper(c);
  2241.             if (! isalpha(c))
  2242.                 expected("a drive letter");
  2243.             project->out_drive = (byte) c;
  2244.             s = getdrives();
  2245.             while (!s[project->out_drive - 'A'])
  2246.                 {
  2247.                 if (project->out_drive == 0)
  2248.                     error("Invalid output drive letter");
  2249.                 --project->out_drive;
  2250.                 }
  2251.             continue;
  2252.         case GROUP:
  2253.             match(in, "=");
  2254.             if ((project->groups = get_str(in, project)) == NULL)
  2255.                 expected("the project default group(s)");
  2256.             strupr(project->groups);
  2257.             continue;
  2258.         case NAME:
  2259.             match(in, "=");
  2260.             if ((project->name = get_str(in, project)) == NULL)
  2261.                 expected("the project textual name");
  2262.             if (strlen(project->name) > 30)
  2263.                 error("The product name must be less than 30 chars long");
  2264.             continue;
  2265.         case VERSION:
  2266.             match(in, "=");
  2267.             if ((project->version = get_str(in, project)) == NULL)
  2268.                 expected("the project version number");
  2269.             continue;
  2270.         case SUBDIR:
  2271.             match(in, "=");
  2272.             skip_white(in);
  2273.             if ((c = readch(in, TRUE)) == '@')
  2274.                 {
  2275.                 unreadch(c);
  2276.                 if ((token = get_token(in, project)) == GETENV)
  2277.                     {
  2278.                     byte *s = get_str(in, project);
  2279.                     if (s == NULL)
  2280.                         expected("an environment variable name");
  2281.                     if ((s = (byte *) getenv(s)) == NULL)
  2282.                         /* default to the root */
  2283.                         s = "\\";
  2284.                     usr_11();
  2285.                     project->subdir = (byte *) smart_calloc(1, strlen(s) + 5, "get_project");
  2286.                     strcpy(project->subdir, s);
  2287.                     }
  2288.                 else
  2289.                     unexpected(tokens[token].text);
  2290.                 }
  2291.             else
  2292.                 {
  2293.                 unreadch(c);
  2294.                 if ((s = get_str(in, project)) == NULL)
  2295.                     expected("the default subdirectory");
  2296.                 project->subdir = (byte *) smart_calloc(1, strlen(s) + 5, "get_project");
  2297.                 strcpy(project->subdir, s);
  2298.                 }
  2299.             strupr(project->subdir);
  2300.             /*
  2301.              *    the next few lines insure that the path starts and ends with
  2302.              *    a back slash.
  2303.              */
  2304.             if (project->subdir[0] != '\\')
  2305.                 {
  2306.                 memmove(project->subdir + 1, project->subdir, strlen(project->subdir));
  2307.                 project->subdir[0] = '\\';
  2308.                 }
  2309.             if (project->subdir[strlen(project->subdir) - 1] != '\\')
  2310.                 strcat(project->subdir, "\\");
  2311.             insert_var(project, "SUBDIR", dir_var_type)->string = project->subdir;
  2312.             continue;
  2313.         case ENDPROJECT:
  2314.             if (project->out_drive == 0)
  2315.                 expected(tokens[OUTDRIVE].text);
  2316.             if (project->subdir == NULL)
  2317.                 expected(tokens[SUBDIR].text);
  2318.             if (project->name == NULL)
  2319.                 expected(tokens[NAME].text);
  2320.             if (project->version == NULL)
  2321.                 expected(tokens[VERSION].text);
  2322.             return;
  2323.         default:
  2324.             if (execute_if(in, project, token))
  2325.                 continue;
  2326.             unexpected(string);
  2327.             }
  2328.         }
  2329.     }                    /* get_project */
  2330.  
  2331. /*
  2332.  *     Compile a @SetAutoexec/@EndAutoexec block.
  2333.  */
  2334.  
  2335. static void get_autoexec(int in, project_t *project)
  2336.     {                    /* get_autoexec */
  2337.     int token;
  2338.     byte *s;
  2339.     int pnum = 0;        /* number of path nodes */
  2340.     int vnum = 0;        /* number of verbatim lines */
  2341.     patch_t *m;
  2342.     int l;
  2343.     int c;
  2344.  
  2345.     if (project->autoexec == NULL)
  2346.         project->autoexec = smart_calloc(1, sizeof(patch_t), "get_autoexec 1");
  2347.  
  2348.     m = project->autoexec;
  2349.  
  2350.     /* default behavior values */
  2351.     m->action = SCREENPROTO;
  2352.     m->verbose = TRUE;
  2353.     forever
  2354.         {
  2355.         token = get_token(in, project);
  2356.         switch (token)
  2357.             {
  2358.         case ENDAUTOEXEC:
  2359.             return;
  2360.         case TERSE:
  2361.             m->verbose = FALSE;
  2362.             continue;
  2363.         case OVERWRITE:
  2364.         case SCREENPROTO:
  2365.         case DISKPROTO:
  2366.         case ASKOVERWRITE:
  2367.             m->action = token;
  2368.             continue;
  2369.         case PATH:
  2370.             if (pnum >= 2)
  2371.                 error("only 1 PATH command allowed");
  2372.             c = get_char(in);
  2373.             if (c == '\"')
  2374.                 unreadch('"');
  2375.             else if (c != '=')
  2376.                 expected("=");
  2377.             if ((s = get_str(in, project)) == NULL)
  2378.                 expected("a PATH node");
  2379.             /* break out each node of the path */
  2380.             for (m->path[pnum++] = s;  *s;  )
  2381.                 {
  2382.                 if ((s = (byte *) strchr(s, ';')) == NULL)
  2383.                     {
  2384.                     l = strlen(m->path[pnum - 1]) - 1;
  2385.                     if (m->path[pnum - 1][l] == '\\' && m->path[pnum - 1][l - 1] != ':')
  2386.                         m->path[pnum - 1][l] = 0;
  2387.                     break;    /* done */
  2388.                     }
  2389.                 if (*(s - 1) == '\\' && *(s - 2) != ':')
  2390.                     *(s - 1) = 0;    /* eat \\ to conform with DOS path requirements */
  2391.                 else if (*s == ';')
  2392.                     *s = '\0';    /* make a string */
  2393.                 ++s;
  2394.                 /* store new node if one actually exists */
  2395.                 if (*s)
  2396.                     m->path[pnum++] = s;
  2397.                 }
  2398.             m->path[pnum] = NULL;
  2399.             continue;
  2400.         case VERBATIM:
  2401.             if (vnum >= 254)
  2402.                 error("only 255 %s commands allowed", tokens[VERBATIM].text);
  2403.             if ((m->verbatim[vnum++] = get_str(in, project)) == NULL)
  2404.                 expected("a quoted text string");
  2405.             m->verbatim[vnum] = NULL;
  2406.             continue;
  2407.         default:
  2408.             if (execute_if(in, project, token))
  2409.                 continue;
  2410.             unexpected(string);
  2411.             }
  2412.         }
  2413.     }                    /* get_autoexec */
  2414.  
  2415. /*
  2416.  *     Compile a @SetConfig/@EndConfig block.
  2417.  */
  2418.  
  2419. static void get_config(int in, project_t *project)
  2420.     {                    /* get_config */
  2421.     int dnum = 0;        /* number of device= lines */
  2422.     patch_t *m;
  2423.     int token;
  2424.  
  2425.     if (project->config == NULL)
  2426.         project->config = smart_calloc(1, sizeof(patch_t), "get_config 1");
  2427.     m = project->config;
  2428.     m->last_drive = 0;
  2429.  
  2430.     /* set the default behavior */
  2431.     m->verbose = TRUE;
  2432.     m->action = SCREENPROTO;
  2433.  
  2434.     while (TRUE)
  2435.         {
  2436.         token = get_token(in, project);
  2437.         switch (token)
  2438.             {
  2439.         case ENDCONFIG:
  2440.             return;
  2441.         case TERSE:
  2442.             m->verbose = FALSE;
  2443.             continue;
  2444.         case OVERWRITE:
  2445.         case SCREENPROTO:
  2446.         case DISKPROTO:
  2447.         case ASKOVERWRITE:
  2448.             m->action = token;
  2449.             continue;
  2450.         case FILES:
  2451.             match(in, "=");
  2452.             if ((m->files = get_num(in)) < 8 || m->files > 255)
  2453.                 expected("a number from 8 and 255");
  2454.             continue;
  2455.         case LASTDRIVE:
  2456.             match(in, "=");
  2457.             skip_white(in);
  2458.             m->last_drive = (byte) readch(in, TRUE);
  2459.             m->last_drive = toupper(m->last_drive);
  2460.             if (m->last_drive < 'A' || m->last_drive > 'Z')
  2461.                 expected("a letter from A and Z");
  2462.             continue;
  2463.         case BUFFERS:
  2464.             match(in, "=");
  2465.             if ((m->buffers = get_num(in)) < 1 || m->buffers > 99)
  2466.                 expected("a number from 1 and 99");
  2467.             continue;
  2468.         case DEVICE:
  2469.             match(in, "=");
  2470.             if ((m->device[dnum++] = get_str(in, project)) == NULL)
  2471.                 expected("a DEVICE name");
  2472.             m->device[dnum] = NULL;
  2473.             continue;
  2474. /*        case SHELL:
  2475.             match(in,"/E:");
  2476.             if((m->env_size = get_num(in)) < 160 || m->env_size>32768)
  2477.                 expected("a environment size number between 160 and 32768");
  2478.             continue;*/
  2479.         default:
  2480.             if (execute_if(in, project, token))
  2481.                 continue;
  2482.             unexpected(string);
  2483.             }
  2484.         }
  2485. #    if defined(__MICROSOFTC__)
  2486.     m->device[dnum] = NULL;    /* quiet microsoft's lint */
  2487. #    endif
  2488.     }                    /* get_config */
  2489.  
  2490.  
  2491. void finish(project_t *project, byte *name)
  2492.     {                    /* finish */
  2493.     int token;
  2494.     int c, len;
  2495.     byte *p, *a, *s;
  2496.     int in;
  2497.     project_t local;
  2498.     byte *os = (byte *) "an operating system command string";
  2499.  
  2500.     /* see if a @finish block is in script file */
  2501.     if (restart == 0L)
  2502.         return;
  2503.  
  2504.     /* make local copy of these vars since free_mem will trash them */
  2505.     memmove((byte *) &local, (byte *) project, sizeof(project_t));
  2506.  
  2507.     in = open_data(&local, name);
  2508.     clr_readch();
  2509.  
  2510.     if (lseek(in, restart, 0) == -1)
  2511.         error("Unable to reopen script file \"INSTALL.DAT\"");
  2512.     line = old_line;
  2513.     vrestore();
  2514.     free_mem();
  2515.  
  2516.     for (;;)
  2517.         {
  2518.         while ((c = readch(in, TRUE)) != EOF && c != '@')
  2519.             sputch((byte) c);
  2520.  
  2521.         if (c == EOF)
  2522.             unexpected("end of file");
  2523.  
  2524.         unreadch(c);
  2525.         token = get_token(in, project);
  2526.  
  2527.         if (token == ENDFINISH)
  2528.             {
  2529.             cls();
  2530.             return;
  2531.             }
  2532.  
  2533.         switch (token)
  2534.             {
  2535.         case CHDRIVE:
  2536.             c = get_char(in);
  2537.             if (c == '@')
  2538.                 {
  2539.                 unreadch(c);
  2540.                 token = get_token(in, project);
  2541.                 switch (token)
  2542.                     {
  2543.                 case OUTDRIVE:
  2544.                     c = project->out_drive;
  2545.                     break;
  2546.                 case INDRIVE:
  2547.                     c = project->in_drive;
  2548.                     break;
  2549.                 default:
  2550.                     if (!execute_if(in, project, token))
  2551.                         unexpected((byte *) string);
  2552.                     }
  2553.                 }
  2554.             if (! isalpha(c))
  2555.                 expected("a drive letter");
  2556.             setdisk(toupper(c) - 'A');
  2557.             continue;
  2558.         case CHDIR:
  2559.             if ((s = get_str(in, &local)) == NULL)
  2560.                 expected("a directory");
  2561.             strupr(s);
  2562.             len = strlen(s);
  2563.             if (len > 1 && s[len - 1] == '\\')
  2564.                 s[len - 1] = '\0';
  2565.             chdir(s);
  2566.             continue;
  2567.         case PAUSE:
  2568.             sputs("\nPress any key to continue ...");
  2569.             if (sgetch() == ESC)
  2570.                 say_bye();
  2571.             sputs("\n");
  2572.             continue;
  2573.         case SYSTEM:
  2574.             if ((p = get_str(in, &local)) == NULL)
  2575.                 expected(os);
  2576.             if (system(p) == -1)
  2577.                 error("Unable to execute @System command");
  2578.             continue;
  2579.         case EXECUTE:
  2580.             if ((p = get_str(in, &local)) == NULL)
  2581.                 expected(os);
  2582.             match(in, ",");
  2583.             if ((a = get_str(in, &local)) == NULL)
  2584.                 expected(os);
  2585. #            ifdef LATTICE
  2586.             if (system(p) == -1)
  2587.                 error("Unable to execute @System command");
  2588. #            else
  2589.             execlp(p, p, a, NULL);
  2590.             sputs(dosprob(NULL));
  2591.             sputs("Unable to execute @Execute command");
  2592.             sgetch();
  2593. #            endif            
  2594.             continue;
  2595.         default:
  2596.             if (execute_verb(in, &local, token))
  2597.                 continue;
  2598.             if (execute_if(in, project, token))
  2599.                 continue;
  2600.             unexpected(string);
  2601.             }
  2602.         }                /* forever */
  2603.     }                    /* finish */
  2604.  
  2605.  
  2606. /*
  2607.  *    Remove trailing \\ from a subdir string.
  2608.  */
  2609.  
  2610. static byte *trim_dir(byte *dir)
  2611.     {
  2612.     int len;
  2613.     static byte local_copy[255];
  2614.  
  2615.     if (dir == NULL)
  2616.         return dir;
  2617.  
  2618.     if ((len = strlen(dir)) > 255)
  2619.         error("internal error: trim");
  2620.  
  2621.     strcpy(local_copy, dir);
  2622.     if (len > 1 && local_copy[len - 1] == '\\')
  2623.         local_copy[len - 1] = 0;    /* skip \ */
  2624.  
  2625.     return local_copy;
  2626.     }
  2627.  
  2628. /* end-of-file */
  2629.