home *** CD-ROM | disk | FTP | other *** search
- /*=============================================================================
-
- The INSTALL program source code, object code, sample script files,
- executable program, and documentation are subject to copyright
- protection under the laws of the United States and other countries.
-
- This software is licensed, not sold, and may only be redistributed
- in executable format and only in accordance with the provisions of
- the INSTALL Source Code License Agreement.
-
- INSTALL is Copyright(C) 1987-1990 by Knowledge Dynamics Corp
- Highway Contract 4 Box 185-H, Canyon Lake, TX (USA) 78133-3508
- 512-964-3994 (Voice) 512-964-3958 (24-hr FAX)
-
- All rights reserved worldwide.
-
- ===============================================================================
-
- FILENAME:
- compile.c
-
- AUTHOR:
- eric jon heflin
-
- PUBLIC FUNCTIONS:
- compile() - master function
- lookup_var() - lookup var name and return its pointer or NULL
-
- LOCAL FUNCTIONS:
- get_file() - interpret @File verb
- ignore_out() - ignore @Out for files not installed
- get_out_drive() - interpret @GetOutDrive block
- get_subdir() - interpret @GetSubdir block
- get_groups() - interpret @GetGroup block
- get_string() - interpret @GetString block
- get_option() - interpret @GetOption block
- get_welcome() - display @Welcome/@Display block
- get_autoexec() - interpret @SetAutoexec block
- get_config() - interpret @SetConfig block
- get_disk() - interpret @DefineDisk block
- get_project() - interpret @DefineProject block
- parse_out() - interpret @Out verb
- get_fixed() - prompt for output disk removability
- execute_if() - interpret @If/@ElseIf block
- execute_verb() - evaluate expression
- get_vars() - parse @DefineVars block
- insert_var() - insert a polymorphic var into the lookup table
- trim_dir() - trim the trailing \\ from a subdirectory string
-
- DESCRIPTION:
- This file contains the high-level functions to implement an interpreter
- for the INSTALL script language.
-
- REVISION HISTORY:
- DATE: AUTHOR: DESCRIPTION OF CHANGES:
- 891101 ejh Addition of user defined vars.
- 891213 ea jordan Documentation.
- 900102 ejh Cosmetic changes.
- 900301 ejh Added code to handle var lookup for subdir type
- vars after an @Out.
-
- ==============================================================================*/
-
- #include <ctype.h>
- #include "install.h"
-
- #if defined(InstantC)
- # define assert(c) if(!(c))_()
- #else
- # include <stdlib.h>
- # include <assert.h>
- #endif
-
- #if defined(__TURBOC__)
- # include <dir.h>
- # include <mem.h>
- # include <process.h>
- # include <io.h>
- # include <sys\types.h>
- # include <sys\stat.h>
- #elif defined (__MICROSOFTC__)
- # include <direct.h>
- # include <process.h>
- # include <io.h>
- # include <sys\types.h>
- # include <sys\stat.h>
- #elif defined(LATTICE)
- # include <types.h>
- # include <stat.h>
- #endif
-
- #include "switches.h"
- #include <fcntl.h>
- #include <stdio.h>
- #include <string.h>
- #include "tokens.h"
- #include "control.h"
-
- extern int line; /* defined in getoken.c */
- extern long max_script; /* " " " */
- extern struct token_s tokens[MAX_TOKEN];
- extern byte string[255];
- extern int token_type;
-
- static long start;
- long restart;
- int old_line;
- /*
- * Label on first input disk - used if there is a @Finish block, in which
- * case we need to prompt for the first disk again. Future versions of
- * INSTALL will probably have an option to read the @Finish block into RAM
- * and thus not require re-reading of the first disk.
- */
- byte *first_disk_label = NULL; /* used by finish() and opendata() */
- static byte *disk_label; /* label returned by ignore_out */
-
- static bool know_fixed = FALSE; /* TRUE after fixed/removable is known */
- static byte temp[500]; /* local scope scratch buf */
-
- #if defined(DEMO)
- static files_read = 0;
- #endif
-
- static file_t *get_file(int in, project_t *project);
- static void ignore_out(int in, project_t *project, int skip);
- static void get_out_drive(int in, project_t *project);
- static void get_subdir(int in, project_t *project);
- static void get_string(int in, project_t *project);
- static void get_integer(int in, project_t *project);
- static void get_groups(int in, project_t *project);
- static void get_options(int in, project_t *project);
- static void get_welcome(int in, project_t *project);
- static void get_disk(int in, project_t *project);
- static void get_project(int in, project_t *project);
- static void parse_out(int in, project_t *project, file_t *file, byte *dflt);
- static void get_fixed(project_t *);
- void compile(int in, project_t *project);
- static void get_autoexec(int, project_t *);
- static void get_config(int, project_t *);
- static word execute_if(int in, project_t *project, int token);
- static word execute_verb(int in, project_t *project, int token);
- var_t *lookup_var(project_t *project, byte *string);
- static void get_vars(int in, project_t *project);
- static var_t *insert_var(project_t *project, byte *name, var_e type);
- static byte *trim_dir(byte *dir);
-
-
- /*
- * Lookup a var name and return its pointer. A NULL pointer is returned
- * if the var was not found in the linked list.
- */
-
- var_t *lookup_var(project_t *project, byte *string)
- {
- var_t *v;
-
- for (v = project->vars; v != NULL; v = v->next)
- if (stricmp(string, v->name) == 0)
- break;
- return v;
- } /* lookup_var */
-
-
- /*
- * If token is an rvalue, display it on tty_window and return TRUE,
- * otherwise, put token text into string and return FALSE.
- */
-
- static word execute_verb(int in, project_t *project, int token)
- {
- var_t *v;
-
- if (in) /* quiet lint */
- in = in;
- switch (token)
- {
- case ABORT:
- say_bye();
- return FALSE; /* quiet lint */
- case AT:
- sputch('@');
- return TRUE;
- case CLS:
- cls();
- return TRUE;
- case PAUSE:
- pause();
- return TRUE;
- case SUBDIR:
- wputs(tty_w, trim_dir(project->subdir));
- return TRUE;
- case OUTDRIVE:
- wputs(tty_w, "%c", project->out_drive);
- return TRUE;
- case INDRIVE:
- wputs(tty_w, "%c", project->in_drive);
- return TRUE;
- case NAME:
- wputs(tty_w, project->name);
- return TRUE;
- case VERSION:
- wputs(tty_w, project->version);
- return TRUE;
- case ILLEGAL:
- /* attempt to parse a user-defined variable */
- if ((v = lookup_var(project, string)) == NULL)
- return FALSE;
- /* match - display appropriately */
- switch (v->type)
- {
- case byte_var_type:
- wputs(tty_w, "%c", (byte)v->integer);
- case dir_var_type:
- wputs(tty_w, trim_dir(v->string));
- break;
- case qstring_var_type:
- wputs(tty_w, v->string);
- break;
- case drive_var_type:
- wputs(tty_w, "%c", v->drive);
- break;
- case integer_var_type:
- wputs(tty_w, "%lu", v->integer);
- break;
- }
- return TRUE;
- } /* switch */
- if (tokens[token].type == NUMBER_T)
- {
- unget_token(token);
- wputs(tty_w, "%lu", (ulong) parse_rvalue(project));
- return TRUE;
- }
- return FALSE;
- } /* execute_verb */
-
-
- static word execute_if(int in, project_t *project, int token)
- {
- switch (token)
- {
- case IF:
- parse_if(in, project);
- return TRUE;
- case ELSE:
- case ELSEIF:
- skip_if(in);
- /* intentional fall-through */
- case ENDIF:
- return TRUE;
- }
- return FALSE;
- } /* execute_if */
-
-
- /*
- * Get a string from the end-user.
- */
-
- static void get_string(in, project)
- int in;
- project_t *project;
- { /* get_string */
- var_t *v = NULL;
- int token, c;
- byte prompt[100];
- byte str[256], *s;
-
- prompt[0] = str[0] = 0;
-
- /* display and ignore all leading white space */
- while ((c = readch(in, TRUE)) != EOF && isspace(c))
- sputch((byte) c);
-
- /* ensure first non-whitespace token is an valid string-type var */
- if (c != '@')
- expected("a string variable name");
-
- unreadch(c);
- token = get_token(in, project);
- if (token == ILLEGAL && (v = lookup_var(project, string)) != NULL && v->type == qstring_var_type)
- {
- /* valid string-type var */
- /* assign default */
- strcpy(str, v->string);
- }
- else
- /* invalid variable name */
- unexpected(string);
-
- forever
- {
- while ((c = readch(in, TRUE)) != EOF && c != '@')
- sputch((byte) c);
-
- if (c == EOF)
- unexpected("end of file");
-
- unreadch(c);
- token = get_token(in, project);
-
- if (token == ENDSTRING)
- break;
-
- switch (token)
- {
- case DEFAULT:
- match(in, "=");
- s = get_str(in, project);
- strcpy(str, s);
- free(s);
- continue;
- case PROMPT:
- match(in, "=");
- strcpy(prompt, get_str(in, project));
- continue;
- default:
- if (execute_verb(in, project, token))
- continue;
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- } /* switch */
- } /* forever */
-
- if (!prompt[0])
- strcpy(prompt, " Enter Text: ");
-
- /* now, display a one-line edit box */
- if (put_prompt(prompt, str, 70, 0) == ESC)
- {
- /* end-user hit [Esc] - abort install */
- say_bye();
- }
-
- /* get mem for dynamically calloced RAM */
- if(strlen(str) == 0)
- v->string = "";
- else
- {
- v->string = (byte *) smart_calloc(1, strlen(str) + 5, "get_string");
- strcpy(v->string, str);
- }
- cls();
- } /* get_string */
-
-
- /*
- * Get a number from the end-user.
- */
-
- static void get_integer(in, project)
- int in;
- project_t *project;
- { /* get_integer */
- var_t *v = NULL;
- int token, c;
- byte prompt[100], str[55], *s;
- long number;
-
- prompt[0] = 0;
-
- /* display and ignore all leading white space */
- while ((c = readch(in, TRUE)) != EOF && isspace(c))
- sputch((byte) c);
-
- /* ensure first non-whitespace token is an valid integer-type var */
- if (c != '@')
- expected("a integer variable name");
-
- unreadch(c);
- token = get_token(in, project);
- if (token == ILLEGAL && (v = lookup_var(project, string)) != NULL && v->type == integer_var_type)
- {
- /* valid integer-type var */
- /* assign default */
- number = v->integer;
- }
- else
- /* invalid variable name */
- unexpected(string);
-
- forever
- {
- while ((c = readch(in, TRUE)) != EOF && c != '@')
- sputch((byte) c);
-
- if (c == EOF)
- unexpected("end of file");
-
- unreadch(c);
- token = get_token(in, project);
-
- if (token == ENDINTEGER)
- break;
-
- switch (token)
- {
- case DEFAULT:
- match(in, "=");
- number = get_long(in);
- continue;
- case PROMPT:
- match(in, "=");
- s = get_str(in, project);
- strcpy(prompt, s);
- free(s);
- continue;
- default:
- if (execute_verb(in, project, token))
- continue;
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- } /* switch */
- } /* forever */
-
- if (!prompt[0])
- strcpy(prompt, " Enter Number: ");
-
- /* now, display a one-line edit box */
- sprintf(str, "%lu", number);
- if (put_prompt(prompt, str, 17, 1) == ESC)
- {
- /* end-user hit [Esc] - abort install */
- say_bye();
- }
-
- /* get mem for dynamically calloced RAM */
- if(strlen(str) == 0)
- v->integer = 0L;
- else
- v->integer = atol(str);
- cls();
- } /* get_integer */
-
-
- static void get_options(in, project)
- int in;
- project_t *project;
- { /* get_options */
- int c, token, current = 0;
- word i;
- bool no_option = TRUE;
- unsigned max_cnt = 0;
- int ret;
- option_t *o;
- word *number = (word *) smart_calloc(250, sizeof(word), "get_option 1");
- byte **option = (byte **) smart_calloc(250, sizeof(byte *), "get_option 2");
- bool checkbox = FALSE;
- byte *s; /* temp string pointer */
-
- for (;;)
- {
- while ((c = readch(in, TRUE)) != EOF && c != '@')
- sputch((byte) c);
-
- if (c == EOF)
- unexpected("end of file");
-
- unreadch(c);
- token = get_token(in, project);
-
- if (token == ENDOPTION)
- break;
-
- switch (token)
- {
- case CHECKBOX:
- checkbox = TRUE;
- continue;
- case OPTION:
- number[current] = get_num(in);
- match(in, "=");
- s = get_str(in, project);
- if (strlen(s) > 76 || (checkbox && strlen(s) > 73))
- error("string too long");
- option[current] = (byte *) smart_calloc(1, 80, "get_options");
- strcpy(option[current], s);
- if (strlen(s) > max_cnt)
- max_cnt = strlen(s);
- ++current;
- no_option = FALSE;
- skip_white(in);
- continue;
- default:
- if (execute_verb(in, project, token))
- continue;
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- }
- }
-
- if (no_option)
- expected(tokens[OPTION].text);
-
- max_cnt += 4;
- if (checkbox)
- max_cnt += 4;
-
- if (max_cnt >= 76)
- error("%s string too long", tokens[OPTION].text);
- if (current <= 12)
- i = current - 1;
- else
- i = 12;
-
- if ((ret = select_m(option, 5, (80 - max_cnt) / 2, 5 + i, (80 - max_cnt) / 2 + max_cnt, current - 1, 0, checkbox)) == -1)
- say_bye();
-
- /* go to end of option linked-list */
- if (project->option == NULL)
- o = project->option = (option_t *) smart_calloc(1, sizeof(option_t), "get_option 3");
- else
- {
- for (o = project->option; ;o = o->next)
- {
- if (o->next == NULL)
- {
- /* pre allocate next option struct */
- o->next = (option_t *) smart_calloc(1, sizeof(option_t), "get_option 4");
- o = o->next;
- break;
- }
- } /* for */
- } /* else */
-
- if (!checkbox)
- {
- o->number = number[ret];
- }
- else
- {
- /* now set each option selected */
- for (i = 0; i < current; ++i)
- {
- if (is_set(i))
- {
- o->number = number[i];
- /* pre allocate next option struct */
- o->next = (option_t *) smart_calloc(1, sizeof(option_t), "get_option 4");
- o = o->next;
- }
- }
- }
-
- /* free options */
- for (i = 0; option[i] != NULL && i < 250; ++i)
- free(option[i]);
- free(option);
- free(number);
- cls();
- return;
- } /* get_options */
-
-
- /*
- * Parse a @DefineVars/@EndVars block. Internally, each var is stored
- * as an element of a linked list with the head "project->vars". Type
- * checking is strict.
- */
-
- static void get_vars(int in, project_t *project)
- { /* get_vars */
- int token;
- int no_var = TRUE;
- var_t *new_var;
- byte *name;
- byte *s;
- int c;
-
- forever
- {
- token = get_token(in, project);
- if (token == ENDVARS || token == EOF)
- break;
-
- if ((name = get_text(in)) == NULL)
- expected("variable identifier");
- strupr(name);
- match(in, "=");
-
- switch (token)
- {
- case DIR_TYPE:
- new_var = insert_var(project, name, dir_var_type);
- s = get_str(in, project);
- new_var->string = smart_calloc(1, strlen(s) + 5, "get_vars");
- strupr(s);
- strcpy(new_var->string, s);
- /*
- * the next few lines insure that the path starts and ends with
- * a backslash
- */
- if (new_var->string[0] != '\\')
- {
- memmove(new_var->string + 1, new_var->string, strlen(new_var->string));
- new_var->string[0] = '\\';
- }
- if (new_var->string[strlen(new_var->string) - 1] != '\\')
- strcat(new_var->string, "\\");
- no_var = FALSE;
- continue;
- case QSTRING_TYPE:
- new_var = insert_var(project, name, qstring_var_type);
- new_var->string = get_str(in, project);
- no_var = FALSE;
- continue;
- case BYTE_TYPE:
- new_var = insert_var(project, name, byte_var_type);
- new_var->integer = get_char(in);
- no_var = FALSE;
- continue;
- case INTEGER_TYPE:
- new_var = insert_var(project, name, integer_var_type);
- new_var->integer = get_long(in);
- no_var = FALSE;
- continue;
- case DRIVE_TYPE:
- new_var = insert_var(project, name, drive_var_type);
- c = (byte) get_char(in);
- if (c == '@')
- {
- var_t *v;
- unreadch(c);
- token = get_token(in, project);
- if (token == OUTDRIVE)
- new_var->drive = project->out_drive;
- else if ((v = lookup_var(project, string)) != NULL)
- new_var->drive = v->drive;
- else
- unexpected(string);
- }
- else
- new_var->drive = (byte) toupper(c);
-
- /* the following code ensures that the output drive exists */
- {
- byte *drvs = getdrives();
-
- /* use the specified drive letter as the ceiling */
- for (; new_var->drive >= 'A' && drvs[new_var->drive-'A'] == 0; --new_var->drive)
- ;
- }
- no_var = FALSE;
- continue;
- default:
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- } /* switch */
- } /* forever */
-
- if (token == EOF)
- unexpected("End of file");
-
- if (no_var)
- expected("a variable definition");
- } /* get_vars */
-
-
- /*
- * insert a polymorphic var into the lookup table.
- */
-
- static var_t *insert_var(project_t *project, byte *name, var_e type)
- { /* insert_var */
- var_t *new_var;
-
- /* go to end of linked-list of vars */
- if (project->vars == NULL)
- new_var = project->vars = (var_t *) smart_calloc(1, sizeof(var_t), "get_vars 1");
- else
- {
- for (new_var = project->vars; ;new_var = new_var->next)
- {
- if (new_var->next == NULL)
- {
- /* pre allocate next option struct */
- new_var->next = (var_t *) smart_calloc(1, sizeof(var_t), "get_vars 3");
- new_var = new_var->next;
- break;
- }
- } /* for */
- } /* else */
-
- /*
- * At this point v points to location for a new var. Now, parse each
- * new var definition and add to the linked list of vars including
- * type type.
- */
-
- new_var->name = (byte *) smart_calloc(1, strlen(name) + 2, "insert_var");
- strcpy(new_var->name, name);
- strupr(new_var->name);
- new_var->type = type;
- return new_var;
- } /* get_vars */
-
-
- /*
- * TRUE if we are between @BeginLib and @EndLib, that is, the current files
- * are to be extracted from a library file.
- */
-
- static byte in_lib = FALSE;
-
- static file_t *get_file(in, project)
- int in;
- project_t *project;
- {
- file_t *file;
- option_t *o;
- unsigned long min_val;
- int token;
- byte *dflt = NULL;
- unsigned i, j;
- byte *groups = NULL;
- long option = -1l;
- byte od = project->out_drive;
-
- # ifdef DEMO
- if (files_read > 3 && (strcmp(project->name, "INSTALL") != 0))
- error("This restricted version of INSTALL will only install 3 files");
- ++files_read;
- # endif
-
- file = (file_t *) smart_calloc(1, sizeof(file_t), "get_file");
- file->next = NULL;
- file->out_fname = NULL;
- file->out_disk = NULL;
- file->sub = NULL;
- file->abs = FALSE;
- file->damaged = FALSE;
- file->overwrite = OVERWRITE;
- if ((file->in_fname = get_fname(in)) == NULL)
- expected("a file name");
- strupr(file->in_fname);
- if (strchr(file->in_fname, '?') != NULL || strchr(file->in_fname, '*') != NULL)
- {
- if (!in_lib)
- error("Ambiguous filenames may only be used for files in a library");
- file->wildcard = TRUE;
- }
-
- file->type = NORMAL;
- if (in_lib)
- {
- /* we are w/i a @BeginLib ... @EndLib block */
- file->type = CHILD;
- file->verify = TRUE;
- }
-
- forever
- {
- token = get_token(in, project);
- switch (token)
- {
- case ASKOVERWRITE:
- case NOOVERWRITE:
- case OVERWRITE:
- file->overwrite = (word) token;
- continue;
- case CRC:
- if (file->type == CHILD)
- {
- get_num(in);
- continue;
- }
- if ((file->c_crc = file->o_crc = get_num(in)) == 0)
- expected("CRC number");
- file->verify = TRUE;
- continue;
- case SETMACRO:
- match(in, "=");
- if ((dflt = get_str(in, project)) == NULL)
- expected("macro substitution text");
- strupr(dflt);
- continue;
- case GROUP:
- if ((groups = get_text(in)) == NULL)
- expected("group letter(s)");
- strupr(groups);
- continue;
- case OPTION:
- option = get_long(in);
- continue;
- case DECOMPRESS:
- if (file->type == CHILD)
- continue;
- file->expand = TRUE;
- continue;
- case OUTABS:
- file->abs = TRUE;
- skip_white(in);
- if (peekch(in) == '@')
- {
- switch (get_token(in, project))
- {
- case IF:
- parse_if(in, project);
- break;
- case ELSE:
- case ELSEIF:
- case ENDIF:
- skip_if(in);
- break;
- case OUTDRIVE:
- unreadch(project->out_drive);
- break;
- default:
- unexpected(string);
- }
- }
- if ((file->out_fname = get_fname(in)) == NULL)
- expected("output filename");
- if (file->out_fname[2] != '\\')
- expected("output path");
- strupr(file->out_fname);
- continue;
- case APPENDTO:
- file->append = TRUE;
- /* intentional fall-through to next case */
- case OUT:
- case OUT0K:
- min_val = 0L;
- break;
- case OUT128K:
- min_val = 128L;
- break;
- case OUT360K:
- min_val = 350L;
- break;
- case OUT512K:
- min_val = 512L;
- break;
- case OUT720K:
- min_val = 710L;
- break;
- case OUT1M:
- min_val = 1000L;
- break;
- case OUT1440K:
- min_val = 1430L;
- break;
- case OUT5M:
- min_val = 5000L;
- break;
- case OUT10M:
- min_val = 10000L;
- break;
- case OUT20M:
- min_val = 20000L;
- break;
- case OUT30M:
- min_val = 30000L;
- break;
- case SIZE:
- if ((file->c_size = file->o_size = get_long(in)) == 0L)
- expected("a file size");
- continue;
- /* case SYSTEM:*/
- case IFILE:
- case ENDDISK:
- case BEGINLIB:
- case ENDLIB:
- unget_token(token);
- # ifndef STRICT
- /*
- * This file is not selected for installation, save output disk
- * label, if one was specified
- */
- if (file->out_disk != NULL)
- disk_label = file->out_disk;
- else
- return NULL;
- # endif
-
- /* see if at least a filename was specified */
- if (file->in_fname == NULL)
- expected("a file name");
-
- /* define output filename */
- if (file->out_fname == NULL)
- {
- /* none was specified, use input name */
- file->out_fname = (byte *) smart_calloc(1, strlen(file->in_fname) + 3, "get_file 2");
- if (od != project->out_drive)
- {
- sprintf(file->out_fname, "%c:%s", od, file->in_fname);
- file->abs = TRUE;
- }
- else
- strcpy(file->out_fname, file->in_fname);
- }
-
- /* see if this file is in selected @Option or @Group */
- if (groups == NULL && option == -1l)
- return file; /* always install this file */
-
- if (groups != NULL)
- {
- /* see if this file is in specified group(s) */
- for (i = 0; i < strlen(project->groups); ++i)
- {
- for (j = 0; j < strlen(groups); ++j)
- {
- if (project->groups[i] == groups[j])
- {
- /* group checks out */
- if (option == -1l)
- return file;
- /* no option for this file */
- /* do option test */
- for (o = project->option; o != NULL; o = o->next)
- if (project->option->number == (word) option)
- return file;
- /*
- * This file is not selected for installation,
- * save output disk label, if one was
- * specified
- */
- if (file->out_disk != NULL)
- disk_label = file->out_disk;
- return NULL;
- }
- } /* for sub loop */
- } /* for loop to test groups */
- /*
- * This file is not selected for installation, save output disk
- * label, if one was specified
- */
- if (file->out_disk != NULL)
- disk_label = file->out_disk;
- return NULL;
- } /* if group != NULL */
-
- /* this file had no @Group, but it did have an @Option */
- for (o = project->option; o != NULL; o = o->next)
- if (o->number == (word) option)
- return file;
- /*
- * This file is not selected for installation, save output disk
- * label, if one was specified
- */
- if (file->out_disk != NULL)
- disk_label = file->out_disk;
-
- /* free allocated mem and clear structure to zeros */
- # ifdef FREE
- if (file->in_fname != NULL)
- free(file->in_fname);
- if (file->out_disk != NULL)
- free(file->out_disk);
- if (file->out_fname != NULL)
- free(file->out_fname);
- free((byte *) file);
- # endif
- return NULL;
- default:
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- } /* switch (token) */
-
- /* processing only gets to this point after an @OUTxxx command */
- if (project->out_size >= min_val * 1024L)
- parse_out(in, project, file, dflt);
- else
- ignore_out(in, project, FALSE);
- } /* forever */
- }
-
-
- static void parse_out(in, project, file, dflt)
- int in;
- project_t *project;
- file_t *file;
- byte *dflt; /* current macro substitution text */
- { /* parse_out */
- unsigned i = 0;
- int c;
- int token;
- var_t *v;
-
- memset(temp, 0, sizeof(temp));
-
- if (dflt != NULL)
- dflt[0] = dflt[0]; /* quiet lint */
- /* release any memory allocated to this field */
- if (file->out_disk != NULL)
- {
- # ifdef FREE
- free(file->out_disk);
- # endif
- file->out_disk = NULL;
- }
- if (disk_label != NULL)
- {
- file->out_disk = disk_label;
- # ifdef FREE
- free(disk_label);
- # endif
- disk_label = NULL;
- }
-
- skip_white(in);
- forever
- {
- if (i >= sizeof (temp))
- error("String to long: parse_out");
-
- c = readch(in, TRUE);
- c = toupper(c);
- switch (c)
- {
- case EOF:
- unreadch(c);
- return;
- case '\"': /* output disk label */
- if (project->removable == TRUE)
- {
- unreadch('"');
- file->out_disk = get_str(in, project);
- }
- else
- {
- /* ignore label since destination is a fixed disk */
- while ((c = readch(in, TRUE)) != '"' && c != EOF)
- ;
- }
- i = 0;
- match(in, ":");
- break;
- case '@': /* must be subdir or default or '@' */
- {
- unreadch(c);
- token = get_token(in, project);
- switch (token)
- {
- case AT:
- temp[i++] = '@';
- continue;
- case SUBDIR:
- strcpy(string, "SUBDIR");
- /* intentional fall through */
- case ILLEGAL:
- if ((v = lookup_var(project, string)) == NULL)
- unexpected(string);
- if (v->type == dir_var_type)
- {
- if (i == 0)
- {
- /*
- * format: @Out @Subdir
- *
- * @Subdirxx is the first @out token
- */
- /* temp[0] = project->out_drive;
- temp[1] = ':';
- strcpy(temp+2,v->string);
- */
- if (v->string[0] == '\\')
- strcpy(temp, v->string + 1);
- else
- strcpy(temp, v->string);
- }
- else if (i >= 2 && temp[1] != ':')
- {
- int shift;
- /*
- * format: @Out STRING\@SUBDIR
- * or: @Out @Subdirxxx\...\@Subdiryyy
- *
- * Dir was not previously absolutly specified, need
- * to move some chars to the right and insert out_drive
- * and subdir (to change this destination to an
- * abs.
- */
- shift = 2; /* space for the "D:" */
- /* shift += i; space for current chars */
- shift += strlen(project->subdir);
- /* space for subdir */
- memmove(temp + shift, temp, shift);
- memmove(temp + 2, project->subdir, strlen(project->subdir));
- temp[0] = project->out_drive;
- temp[1] = ':';
- strcpy(temp+i+shift-1, v->string);
- file->abs = TRUE;
- }
- else
- {
- /*
- * format:
- * @Out D:\STRING\@Subdir
- * @Out @Drive:\@Subdir...
- *
- * Dir already is already absolute - just append
- * this subdir to end of existing subdir.
- */
- if (temp[i - 1] == '\\' && v->string[0] == '\\')
- --i;
- strcpy(temp + i, v->string);
- file->abs = TRUE;
- }
- }
- else if (v->type == drive_var_type)
- {
- match(in, ":");
- if (i != 0)
- error("output drive override must be first");
- /*
- * format: @Out @OutDrivexx
- *
- * Override the default output drive.
- */
- temp[0] = v->drive;
- temp[1] = ':';
- temp[2] = 0; /* for strlen() purposes */
- file->abs = TRUE;
- }
- else
- unexpected(string);
- i = strlen(temp);
- continue;
- default:
- unexpected(string);
- }
- }
- continue;
- case '*':
- if (i > 0 && temp[i - 1] == '.')
- {
- /* use the input filename extension, if any */
- parse_file(file->in_fname, NULL, NULL, NULL, temp + i);
- i = strlen(temp);
- }
- else
- {
- /* use the input filename node */
- parse_file(file->in_fname, NULL, NULL, temp + i, NULL);
- i = strlen(temp);
- }
- continue;
- case '\\': /* test for already existing separator */
- if (i > 0 && temp[i - 1] != '\\')
- {
- temp[i++] = '\\';
- }
- else if (i == 0) /* subdir override */
- {
- file->abs = TRUE;
- temp[i++] = project->out_drive;
- temp[i++] = ':';
- temp[i++] = '\\';
- }
- continue;
- default:
- if (execute_if(in, project, token))
- continue;
- if (isalnum(c) || c == '$' || c == '&' || c == '#' || c == '!' ||
- c == '%' || c == '\'' || c == '`' || c == '(' || c == ')' ||
- c == '{' || c == '}' || c == '_' || c == '.' || c == '-')
- {
- if (file->abs)
- {
- /*
- * handle:
- * @File afile @Out @drive:afile
- *
- * which is an abs file that goes to the default
- * output subdir
- */
- if (i == 2)
- {
- /* no subdir yet */
- strcat(temp, project->subdir);
- i = strlen(temp);
- }
- temp[i++] = (byte) c;
- }
- else
- temp[i++] = (byte) c;
- }
- else
- {
- temp[i] = 0;
- file->out_fname = (byte *) smart_calloc(1, strlen(temp) + 2, "parse_out");
- strcpy(file->out_fname, temp);
- i = 0;
- return;
- }
- } /* switch(c) */
- } /* while */
- } /* parse_out */
-
- /*
- * Ignore the rest of the file since it is not selected for installation ...
- * EXCEPT, the output disk label must be retained if destination is not
- * a fixed disk.
- */
-
- static void ignore_out(in, project, skip_label)
- int in;
- project_t *project;
- int skip_label;
- { /* ignore_out */
- int c;
- int token;
-
- disk_label = NULL;
- skip_white(in);
- forever
- {
- c = readch(in, TRUE);
- c = toupper(c);
- switch (c)
- {
- case EOF:
- unreadch(c);
- return;
- case '\"': /* output disk label */
- if (project->removable == FALSE || skip_label)
- {
- /* ignore label since destination is a fixed disk */
- while ((c = readch(in, TRUE)) != '"' && c != EOF)
- ;
- }
- else
- {
- /*
- * we may need this label if this file is not selected for
- * installation but the @Out### is valid for this output
- * disk size.
- */
- unreadch('"');
- # ifdef FREE
- free(get_str(in, project));
- # else
- disk_label = get_str(in, project);
- # endif
- }
- match(in, ":");
- break;
- case '@': /* must be subdir or default or '@' */
- unreadch(c);
- token = get_token(in, project);
- switch (token)
- {
- /* case SUBDIR:
- case MACRO:*/
- case AT:
- continue;
- default:
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- }
- continue;
- case ':':
- case '*':
- case '\\':
- continue;
- default:
- if (execute_if(in, project, token))
- continue;
- if (isalnum(c) || c == '$' || c == '&' || c == '#' || c == '!' ||
- c == '%' || c == '\'' || c == '`' || c == '(' || c == ')' ||
- c == '{' || c == '}' || c == '_' || c == '.' || c == '-')
- ;
- else
- return;
- } /* switch(c) */
- } /* while */
- } /* ignore_out */
-
-
- /*
- * This is the master function in this file. All other blocks are parsed
- * by routines called by compile().
- */
-
- void compile(in, project)
- int in;
- project_t *project;
- { /* compile */
- int token;
- bool no_disk = TRUE;
- static bool first = TRUE; /* TRUE first time compile() is called */
- byte *s;
- bool first_token = TRUE;
- # ifdef ABSORB
- long end, templ;
- # endif
-
- start = tell(in); /* see where we are w/i script file */
- # ifdef ABSORB
- end = start;
- # endif
-
- restart = 0l;
- disk_label = NULL;
- token = get_token(in, project);
-
- # ifdef ABSORB
- /* verify that COMBINE.EXE was run on this file */
- if(token == SCRIPTSIZE)
- {
- templ = get_long(in);
- assert(templ > 30L);
- end = start + templ;
- max_script = templ - (0x7FFFFFFl - max_script); /* reduce by bytes already read */
- token = get_token(in, project);
- }
- else
- {
- /* this file has not been properly processed */
- if(token == STRING && strncmp(string, "MZ", 2) == 0)
- {
- wputs(error_w, "Before you run the self-extracting version of INSTALL.EXE");
- wputs(error_w, "you must combine INSTALL.EXE INSTALL.DAT and a library file");
- wputs(error_w, "using COMBINE.EXE");
- wputs(error_w, NULL);
- }
- else
- {
- wputs(error_w, "This self-extracting (SFX) file is corrupted.");
- }
- put_error(error_w);
- say_bye();
- }
- # endif /* ABSORB */
-
- if (first && token != DEFINEPROJECT)
- expected(tokens[DEFINEPROJECT].text);
-
- first = FALSE;
- unget_token(token);
- /*
- * parse blocks
- */
- while ((token = get_token(in, project)) != EOF)
- {
- switch (token)
- {
- case SETAUTOEXEC:
- get_autoexec(in, project);
- continue;
- case SETCONFIG:
- get_config(in, project);
- continue;
- case WELCOME:
- case DISPLAY:
- get_welcome(in, project);
- first_token = FALSE;
- continue;
- case DEFINEPROJECT:
- get_project(in, project);
- first_token = FALSE;
- continue;
- case DEFINEVARS:
- get_vars(in, project);
- first_token = FALSE;
- continue;
- case GETSTRING:
- get_string(in, project);
- first_token = FALSE;
- continue;
- case GETINTEGER:
- get_integer(in, project);
- first_token = FALSE;
- continue;
- case GETOUTDRIVE:
- get_out_drive(in, project);
- first_token = FALSE;
- continue;
- case GETSUBDIR:
- get_subdir(in, project);
- first_token = FALSE;
- continue;
- case GETGROUPS:
- # ifdef STRICT
- if (!no_disk)
- error("%s blocks must appear before any %s blocks: compile",
- tokens[GETGROUPS].text, tokens[DEFINEDISK].text);
- # endif
- get_groups(in, project);
- first_token = FALSE;
- continue;
- case GETOPTION:
- # ifdef STRICT
- if (!no_disk)
- error("%s blocks must appear before any %s blocks: compile",
- tokens[GETOPTION].text, tokens[DEFINEDISK].text);
- # endif
- get_options(in, project);
- first_token = FALSE;
- continue;
- case DEFINEDISK:
- if (no_disk)
- {
- /* first disk */
- if (!know_fixed)
- /* script file did not specify prompting end-user for out drive */
- get_fixed(project);
- if (project->out_size == 0L)
- {
- if (project->out_drive == 0)
- error("The output drive letter was not specified");
- project->out_free = disk_free(project->out_drive);
- project->out_size = disk_size(project->out_drive);
- }
- }
- get_disk(in, project);
- /*
- * If this is the first disk, save name of disk label
- * in case there is a @Finish block, and we need to prompt
- * end-user for the first disk once all other files are
- * copied.
- */
- no_disk = FALSE;
- first_token = FALSE;
- continue;
- case FINISH:
- /*
- * If there is adequate RAM, read @Finish block into memory.
- * Otherwise, remember its starting location within the
- * script file, and prompt end-user to replace the disk in
- * @InDrive with the first distribution disk.
- */
- # ifdef STRICT
- if (no_disk)
- expected(tokens[DEFINEDISK].text);
- # endif
- restart = get_pos(in);
- old_line = line;
- # ifndef ABSORB
- close(in);
- in = -1;
- # endif
- break;
- default:
- if (execute_if(in, project, token))
- break;
- unexpected(string);
- }
- if (token == FINISH)
- break;
- } /* while token != EOF */
- if (no_disk)
- expected(tokens[DEFINEDISK].text);
-
- s = getdrives();
- while (!s[project->out_drive - 'A'])
- {
- if (project->out_drive == 0)
- error("Invalid output drive letter");
- --project->out_drive;
- }
- if (!know_fixed) /* script file did not specify prompting end-user for out drive */
- get_fixed(project);
- if (project->out_size == project->out_free && project->out_free == 0)
- {
- if (project->out_drive == 0)
- error("The output drive letter was not specified");
- project->out_free = disk_free(project->out_drive);
- project->out_size = disk_size(project->out_drive);
- }
- if (project->in_drive == project->out_drive)
- error("The output drive cannot be the same as the input drive");
- if (first_token)
- ; /* quiet lint */
- # ifdef ABSORB
- /* make sure pointer is at beginning of lib file */
- lseek(in, end, 0);
- # else
- if (in != -1)
- close(in);
- # endif
- } /* compile */
-
-
-
- static void get_out_drive(in, project)
- int in;
- project_t *project;
- { /* get_out_drive */
- /* get dest drive letter */
- unsigned i, dflt;
- int token, c;
- int ret;
- int max_drive;
- byte *drive_v[27];
- byte *drvs;
- byte *s = (byte *) "Drive %c:";
- byte out_drive;
- int d;
- var_t *v;
- # ifdef UNUSED_CODE
- long fpos = get_pos(in); /* remember where we are in case we need to
- re-read this block */
- # endif
-
- out_drive = project->out_drive;
-
- drvs = getdrives();
-
- /* display and ignore all leading white space */
- while ((c = readch(in, TRUE)) != EOF && isspace(c))
- sputch((byte) c);
-
- /* see if first non-whitespace token is a valid drive-type */
- if (c == '@')
- {
- unreadch(c);
- token = get_token(in, project);
- if (token == ILLEGAL)
- {
- if ((v = lookup_var(project, string)) == NULL || v->type != drive_var_type)
- {
- v = NULL;
- unreadstr(string);
- out_drive = project->out_drive;
- }
- else
- {
- /* valid subdir variable */
- out_drive = v->drive;
- }
- }
- else
- {
- unreadstr(tokens[token].text);
- v = NULL;
- }
- }
-
- forever
- {
- while ((c = readch(in, TRUE)) != EOF && c != '@')
- sputch((byte) c);
-
- if (c == EOF)
- unexpected("end of file");
-
- /* c == @ */
- unreadch(c);
- token = get_token(in, project);
-
- if (token == ENDOUTDRIVE)
- break;
-
- switch (token)
- {
- case SUPPRESS:
- d = get_char(in);
- d = toupper(d);
- if (d >= 'A' && d <= 'Z')
- drvs[d - 'A'] = 0; /* hide this drive letter */
- else
- expected("a drive ID letter");
- break;
- case PROMPT:
- match(in, "=");
- get_str(in, project);
- break;
- default:
- if (execute_verb(in, project, token))
- continue;
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- }
- }
-
- dflt = 0;
- for (i = 0, max_drive = 0; i < 26; ++i)
- {
- if (drvs[i] == 1 /* && i + 'A' != project->in_drive */
- )
- {
- drive_v[max_drive] = (byte *) smart_calloc(1, strlen(s) + 2, "get_out_drive");
- sprintf(drive_v[max_drive++], s, i + 'A');
- if (i + 'A' <= out_drive)
- dflt = max_drive - 1;
- }
- }
-
- --max_drive;
- if (max_drive < 10)
- i = max_drive;
- else
- i = 10;
-
- if (out_drive - 'A' > (byte) max_drive)
- out_drive = (byte) max_drive + 'A';
-
- /*
- * The following loop is repeated until a valid output drive is
- * selected or until the end-user aborts (hits [Esc]).
- */
- forever
- {
- if ((ret = select_m(drive_v, 5, 34, i + 5, 45, max_drive, dflt, FALSE)) == -1)
- say_bye();
-
- /* v == NULL only if we are getting the default output drive */
- if (v == NULL)
- project->out_drive = out_drive = drive_v[ret][6];
- else
- v->drive = out_drive = drive_v[ret][6];
-
- /*
- * This is the most intuitive place to determine the output disk
- * removability status.
- */
- if (v != NULL)
- /* this is a var, skip drive characteristic test */
- break;
-
- get_fixed(project);
- know_fixed = TRUE; /* don't ask twice */
- /* this is the primary output drive, get disk size/space */
- if (((project->out_free = disk_free(project->out_drive)) != -1L) &&
- ((project->out_size = disk_size(project->out_drive)) != -1L))
- break; /* all ok, exit forever loop */
-
- /*
- * If execution gets here, there was an error.
- */
- wputs(message_w, "You do not appear to have the minimum access rights to");
- wputs(message_w, "drive %c: which are required for installation.", out_drive);
- wputs(message_w, NULL);
- wputs(message_w, "Press the [Esc] key to abort, any other key to continue...");
- put_message(message_w);
- } /* forever */
-
- /*
- * When execution gets here, an output disk has been successfully
- * selected.
- */
- cls();
- } /* get_out_drive */
-
- byte *type[3] =
- {
- "Hard Disk",
- "Floppy Disk",
- NULL
- };
-
- /*
- * Determine if the output drive is removable or non-removable.
- */
-
- static void get_fixed(project)
- project_t *project;
- { /* get_out_drive */
- /* get dest drive letter */
- int start;
- int ret;
-
- /* see if there is any work to be done */
- if (know_fixed)
- {
- /*
- * Either this function has already been called or @HardDisk was
- * used in INSTALL.DAT script file.
- */
- return;
- }
-
- know_fixed = TRUE;
- /*
- * Ask DOS for removability status, if it knows.
- */
- ret = ckremovable(project->out_drive - 'A' + 1);
-
- if (ret > 0)
- {
- /* removable */
- project->removable = TRUE;
- return;
- }
- else if (ret == 0)
- {
- /* non-removable */
- project->removable = FALSE;
- return;
- }
-
- cls();
- /* could not determine (probably and old device driver or old DOS) */
-
- start = 0; /* assume fixed */
-
- move(2, 18);
- wputs(tty_w, " Please select the disk type of output disk %c:", project->out_drive);
- move(10, 1);
- wputs(tty_w, " If you are installing onto a hard disk, RAM disk, or other NON-REMOVABLE\n");
- wputs(tty_w, " disk please select \"Hard Disk\". If you are installing onto a 5.25\"\n");
- wputs(tty_w, " floppy diskette, 3.5\" microfloppy diskette, or other REMOVABLE disk\n");
- wputs(tty_w, " please select \"Floppy Disk\".");
- if ((ret = select_m(type, 5, 33, 6, 47, 1, start, FALSE)) == -1)
- say_bye();
- project->removable = (unsigned) ret;
- cls();
- } /* get_fixed */
-
-
- /*
- * Get a subdirectory string from the end-user.
- */
-
- static void get_subdir(in, project)
- int in;
- project_t *project;
- { /* get_subdir */
- var_t *v = NULL;
- int token, c;
- byte prompt[100];
- byte path[100];
- byte *dflt = NULL, *ret = NULL;
- unsigned len;
- prompt[0] = 0;
-
- /* display and ignore all leading white space */
- while ((c = readch(in, TRUE)) != EOF && isspace(c))
- sputch((byte) c);
-
- /* see if first non-whitespace token is an valid dir-type */
- if (c == '@')
- {
- unreadch(c);
- token = get_token(in, project);
- if (token == ILLEGAL)
- {
- if ((v = lookup_var(project, string)) == NULL || v->type != dir_var_type)
- {
- v = NULL;
- dflt = project->subdir;
- unreadstr(string);
- }
- else
- {
- /* valid subdir variable */
- dflt = v->string;
- }
- }
- else
- {
- unreadstr(tokens[token].text);
- dflt = project->subdir;
- v = NULL;
- }
- }
-
- forever
- {
- while ((c = readch(in, TRUE)) != EOF && c != '@')
- sputch((byte) c);
-
- if (c == EOF)
- unexpected("end of file");
-
- unreadch(c);
- token = get_token(in, project);
-
- if (token == ENDSUBDIR)
- break;
-
- switch (token)
- {
- case DEFAULT:
- match(in, "=");
- dflt = get_str(in, project);
- if (v != NULL)
- v->string = dflt;
- strupr(dflt);
- continue;
- case PROMPT:
- match(in, "=");
- strcpy(prompt, get_str(in, project));
- continue;
- default:
- if (execute_verb(in, project, token))
- continue;
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- }
- }
- if (!prompt[0])
- strcpy(prompt, " Please Enter The Subdirectory Name: ");
-
- /* remove trailing \\ */
- if (dflt != NULL)
- /* default to specified output dir */
- strcpy(path, trim_dir(dflt));
- else
- strcpy(path, "\\");
-
-
- /* now, display a one-line edit box */
- if (put_prompt(prompt, path, 65, 6) == ESC)
- {
- /* end-user hit [Esc] - abort install */
- say_bye();
- }
-
- strupr(path);
- len = strlen(path);
- if (len == 0)
- {
- /* a zero-length string was returned - use default */
- len = strlen(dflt);
- strcpy(path, "\\");
- }
-
- if (path[len - 1] != '\\')
- {
- /* ensure path ends with a '\\' */
- path[len++] = '\\';
- path[len++] = '\0';
- }
-
- /* get mem for dynamically calloced RAM */
- ret = (byte *) smart_calloc(1, len + 5, "get_subdir");
- if (path[0] != '\\')
- strcpy(ret, "\\"); /* insert leading \\ char */
- strcat(ret, path);
- if (v == NULL)
- {
- project->subdir = ret;
- v = lookup_var(project, "SUBDIR");
- }
- v->string = ret;
- cls();
- } /* get_subdir */
-
-
-
- /*
- * Parse a @GetGroup/@EndGroup block.
- */
-
- static void get_groups(in, project)
- int in;
- project_t *project;
- { /* get_groups */
- /* level indicates multiple get_groups calls */
- static int level = 0;
- int c, token, i; /* temp vars */
- byte *group[26]; /* strings for each group */
- byte letter[26]; /* letter corresponding to each group */
- unsigned long req[26];
- bool no_groups = TRUE; /* diagnostic flag */
- unsigned max_len; /* max string len */
- int current = 0; /* current group being input */
- int ret; /* return value */
- byte *s; /* temp storage */
- bool checkbox = FALSE; /* TRUE if multiple items may be selected */
- int l;
- # define MAX_STRING (76) /* max quoted string size */
-
- for (i = 0; i < 26; ++i)
- {
- group[i] = (byte *) smart_calloc(1, 80, "get_groups");
- letter[i] = 0;
- req[i] = 0l;
- }
-
- for (;;)
- {
- while ((c = readch(in, TRUE)) != EOF && c != '@')
- sputch((byte) c);
-
- if (c == EOF)
- unexpected("end of file");
-
- unreadch(c);
- token = get_token(in, project);
-
- if (token == ENDGROUPS)
- break;
-
- switch (token)
- {
- case CHECKBOX:
- checkbox = TRUE;
- continue;
- case PROMPT: /* ignore prompt */
- match(in, "=");
- get_str(in, project);
- continue;
- /*
- case REQUIRES:
- if (current <= 0)
- expected(tokens[SET].text);
- if ((req[current - 1] = get_long(in)) == 0l)
- expected("a group file space requirement number");
- continue;
- */
- case SET:
- c = get_char(in);
- c = toupper(c);
- if (! isalpha(c))
- expected("a group designation letter");
- if (current >= 26)
- error("only 26 groups are allowed");
- letter[current] = (byte) c;
- match(in, "=");
- s = get_str(in, project);
- if (strlen(s) > MAX_STRING || (checkbox && strlen(s) + 3 > MAX_STRING))
- error("string too long");
- strcpy(group[current++], s);
- free(s);
- no_groups = FALSE;
- skip_white(in);
- continue;
- default:
- if (execute_verb(in, project, token))
- continue;
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- }
- }
-
- if (no_groups)
- expected(tokens[SET].text);
-
- max_len = 0;
- for (i = 0; i < current; ++i)
- {
- if (group[i] != NULL && strlen(group[i]) > max_len)
- max_len = strlen(group[i]) - 1;
- }
- max_len += 4;
- if (checkbox)
- max_len += 4;
- if (max_len >= MAX_STRING)
- {
- wputs(message_w, "WARNING: %s string to long", tokens[SET].text);
- put_message(message_w);
- }
- if (current <= 12)
- i = current - 1;
- else
- i = 12;
-
- if ((ret = select_m(group, 5, (80 - max_len) / 2, 5 + i, (80 - max_len) / 2 + max_len, current - 1, 0, checkbox)) == -1)
- {
- wputs(message_w, "Aborting installation");
- put_message(message_w);
- bye();
- }
-
- if (level == 0)
- {
- /* first call to get_groups, overwrite any default */
- project->groups = (byte *) smart_calloc(27, 1, "get_groups");
- l = 0;
- if (!checkbox)
- {
- project->groups[l++] = letter[ret];
- }
- else {
- for (i = 0; i < current; ++i)
- {
- if (is_set(i))
- project->groups[l++] = letter[i];
- }
- }
- project->groups[l] = '\0';
- /* project->requirement = req[ret]; */
- }
- else
- {
- /*
- * Second or later call to get_groups, add this group to all
- * previously selected groups.
- */
- byte *s = (byte *) smart_calloc(27, 1, "get_groups");
- if (project->groups != NULL)
- strcpy(s, project->groups);
- project->groups = s;
-
- l = strlen(s);
- if (!checkbox)
- {
- s[l++] = letter[ret];
- }
- else {
- for (i = 0; i < current; ++i)
- {
- if (is_set(i))
- s[l++] = letter[i];
- }
- }
- s[l] = '\0';
- /* project->requirement += req[ret]; */
- }
- ++level;
-
- for (i = 0; i < 26; ++i)
- {
- if (group[i] != NULL)
- free(group[i]);
- }
- cls();
- return;
- } /* get_groups */
-
-
- static void get_welcome(in, project)
- int in;
- project_t *project;
- { /* get_welcome */
- int token;
- int c;
-
- forever
- {
- while ((c = readch(in, TRUE)) != EOF && c != '@')
- sputch((byte) c);
-
- if (c == EOF)
- unexpected("end of file");
-
- unreadch(c);
- token = get_token(in, project);
-
- if (token == ENDWELCOME || token == ENDDISPLAY)
- {
- cls();
- return;
- }
- if (execute_verb(in, project, token))
- continue;
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- } /* for(;;) */
- } /* get_welcome */
-
- #ifdef DEMO
- static word disks_read = 0;
- #endif
-
- static void get_disk(in, project)
- int in;
- project_t *project;
- { /* get_disk */
- byte *t;
- byte n[30]; /* node */
- byte e[10]; /* extension */
- byte p[100]; /* path */
- /*
- int no_files = TRUE;
- */
- disk_t *disk;
- disk_t *last;
- file_t *last_file = NULL, *new_file, *last_normal;
- int token;
- int no_label = TRUE;
-
- # ifdef DEMO
- if (++disks_read > 3)
- error("This restricted version of INSTALL will only read 3 disks");
- # endif
-
- disk = (disk_t *) smart_calloc(1, sizeof(disk_t), "get_disk disk");
-
- if (project->disk == NULL)
- project->disk = disk;
- else
- {
- last = project->disk;
- while (last->next != NULL)
- last = last->next;
- last->next = disk;
- }
-
- for (;;)
- {
- token = get_token(in, project);
-
- switch (token)
- {
- case LABEL:
- match(in, "=");
- disk->label = get_str(in, project);
- /*
- * If this is the first disk, save name of disk label
- * in case there is a @Finish block, and we need to prompt
- * end-user for the first disk once all other files are
- * copied.
- */
- if(first_disk_label == NULL)
- {
- first_disk_label = (byte *)smart_calloc(1, strlen(disk->label) + 3, "get_dsk_label");
- strcpy(first_disk_label, disk->label);
- }
- no_label = FALSE;
- continue;
- case BEGINLIB:
- /* first get parent info */
- if (in_lib)
- unexpected(tokens[BEGINLIB].text);
- new_file = (file_t *) smart_calloc(1, sizeof(file_t), "get_disk");
- new_file->type = PARENT;
- if ((t = get_fname(in)) == NULL)
- expected("a library filename");
- strupr(t);
- parse_file(t, NULL, p, n, e);
- /* default extension is .LIF */
- if (!e[0])
- strcpy(e, "LIF");
- new_file->in_fname = (byte *) smart_calloc(1, strlen(n) + strlen(p) + 5, "get_disk 2");
- sprintf(new_file->in_fname, "%s%s.%s", p, n, e);
- new_file->type = PARENT;
- new_file->verify = TRUE;
- in_lib = TRUE;
- /* now link-in parent */
- if (last_file == NULL)
- disk->file = new_file;
- else
- last_file->next = new_file;
- last_file = new_file;
- last_normal = new_file;
- continue;
- case ENDLIB:
- if (!in_lib)
- unexpected(tokens[ENDLIB].text);
- last_file = last_normal;
- in_lib = FALSE;
- continue;
- case IFILE:
- /*
- no_files = FALSE;
- */
- if ((new_file = get_file(in, project)) != NULL)
- {
- if (in_lib && last_file->type == PARENT)
- last_file->sub = new_file;
- else if (!in_lib && disk->file == NULL)
- disk->file = new_file;
- else
- last_file->next = new_file;
- last_file = new_file;
- }
- continue;
- case ENDDISK:
- /*
- if (no_files)
- expected(tokens[IFILE].text);
- */
- if (in_lib)
- expected(tokens[ENDLIB].text);
- if (no_label)
- expected(tokens[LABEL].text);
- return;
- default:
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- }
- }
- } /* get_disk */
-
-
- /*
- * Compile a @DefineProject/@EndProject block.
- */
-
- static void get_project(in, project)
- int in;
- project_t *project;
- { /* get_project */
- int token;
- int c;
- byte *s;
-
- /*
- * get project info.
- */
- forever
- {
- token = get_token(in, project);
- switch (token)
- {
- case FORMATALLOWED:
- # ifdef FORMAT_ALLOWED
- project->format_ok = TRUE;
- # else
- error("This version of INSTALL does not allow formatting");
- # endif
- continue;
- case INDISKBELL:
- project->in_bell = TRUE;
- continue;
- case OUTDISKBELL:
- project->out_bell = TRUE;
- continue;
- case VERIFY:
- project->verify = TRUE;
- continue;
- case TERSE:
- project->terse = TRUE;
- continue;
- case REQUIRES:
- c = get_char(in);
- unreadch(c);
- if (c == '@')
- {
- token = get_token(in, project);
- if (token != HARDDISK)
- unexpected(string);
- project->removable = FALSE;
- /* arbitrarily set outsize to 2 gigabytes */
- project->out_size = 0x7FFFFFFFL;
- know_fixed = TRUE;
- }
- else if ((project->requirement = get_long(in)) == 0l)
- expected("a project file space requirement number");
- continue;
- case INDRIVE:
- match(in, "=");
- c = get_char(in);
- c = toupper(c);
- if (! isalpha(c))
- expected("a drive letter");
- project->in_drive = (byte) c;
- continue;
- case OUTDRIVE:
- match(in, "=");
- c = get_char(in);
- c = toupper(c);
- if (! isalpha(c))
- expected("a drive letter");
- project->out_drive = (byte) c;
- s = getdrives();
- while (!s[project->out_drive - 'A'])
- {
- if (project->out_drive == 0)
- error("Invalid output drive letter");
- --project->out_drive;
- }
- continue;
- case GROUP:
- match(in, "=");
- if ((project->groups = get_str(in, project)) == NULL)
- expected("the project default group(s)");
- strupr(project->groups);
- continue;
- case NAME:
- match(in, "=");
- if ((project->name = get_str(in, project)) == NULL)
- expected("the project textual name");
- if (strlen(project->name) > 30)
- error("The product name must be less than 30 chars long");
- continue;
- case VERSION:
- match(in, "=");
- if ((project->version = get_str(in, project)) == NULL)
- expected("the project version number");
- continue;
- case SUBDIR:
- match(in, "=");
- skip_white(in);
- if ((c = readch(in, TRUE)) == '@')
- {
- unreadch(c);
- if ((token = get_token(in, project)) == GETENV)
- {
- byte *s = get_str(in, project);
- if (s == NULL)
- expected("an environment variable name");
- if ((s = (byte *) getenv(s)) == NULL)
- /* default to the root */
- s = "\\";
- usr_11();
- project->subdir = (byte *) smart_calloc(1, strlen(s) + 5, "get_project");
- strcpy(project->subdir, s);
- }
- else
- unexpected(tokens[token].text);
- }
- else
- {
- unreadch(c);
- if ((s = get_str(in, project)) == NULL)
- expected("the default subdirectory");
- project->subdir = (byte *) smart_calloc(1, strlen(s) + 5, "get_project");
- strcpy(project->subdir, s);
- }
- strupr(project->subdir);
- /*
- * the next few lines insure that the path starts and ends with
- * a back slash.
- */
- if (project->subdir[0] != '\\')
- {
- memmove(project->subdir + 1, project->subdir, strlen(project->subdir));
- project->subdir[0] = '\\';
- }
- if (project->subdir[strlen(project->subdir) - 1] != '\\')
- strcat(project->subdir, "\\");
- insert_var(project, "SUBDIR", dir_var_type)->string = project->subdir;
- continue;
- case ENDPROJECT:
- if (project->out_drive == 0)
- expected(tokens[OUTDRIVE].text);
- if (project->subdir == NULL)
- expected(tokens[SUBDIR].text);
- if (project->name == NULL)
- expected(tokens[NAME].text);
- if (project->version == NULL)
- expected(tokens[VERSION].text);
- return;
- default:
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- }
- }
- } /* get_project */
-
- /*
- * Compile a @SetAutoexec/@EndAutoexec block.
- */
-
- static void get_autoexec(int in, project_t *project)
- { /* get_autoexec */
- int token;
- byte *s;
- int pnum = 0; /* number of path nodes */
- int vnum = 0; /* number of verbatim lines */
- patch_t *m;
- int l;
- int c;
-
- if (project->autoexec == NULL)
- project->autoexec = smart_calloc(1, sizeof(patch_t), "get_autoexec 1");
-
- m = project->autoexec;
-
- /* default behavior values */
- m->action = SCREENPROTO;
- m->verbose = TRUE;
- forever
- {
- token = get_token(in, project);
- switch (token)
- {
- case ENDAUTOEXEC:
- return;
- case TERSE:
- m->verbose = FALSE;
- continue;
- case OVERWRITE:
- case SCREENPROTO:
- case DISKPROTO:
- case ASKOVERWRITE:
- m->action = token;
- continue;
- case PATH:
- if (pnum >= 2)
- error("only 1 PATH command allowed");
- c = get_char(in);
- if (c == '\"')
- unreadch('"');
- else if (c != '=')
- expected("=");
- if ((s = get_str(in, project)) == NULL)
- expected("a PATH node");
- /* break out each node of the path */
- for (m->path[pnum++] = s; *s; )
- {
- if ((s = (byte *) strchr(s, ';')) == NULL)
- {
- l = strlen(m->path[pnum - 1]) - 1;
- if (m->path[pnum - 1][l] == '\\' && m->path[pnum - 1][l - 1] != ':')
- m->path[pnum - 1][l] = 0;
- break; /* done */
- }
- if (*(s - 1) == '\\' && *(s - 2) != ':')
- *(s - 1) = 0; /* eat \\ to conform with DOS path requirements */
- else if (*s == ';')
- *s = '\0'; /* make a string */
- ++s;
- /* store new node if one actually exists */
- if (*s)
- m->path[pnum++] = s;
- }
- m->path[pnum] = NULL;
- continue;
- case VERBATIM:
- if (vnum >= 254)
- error("only 255 %s commands allowed", tokens[VERBATIM].text);
- if ((m->verbatim[vnum++] = get_str(in, project)) == NULL)
- expected("a quoted text string");
- m->verbatim[vnum] = NULL;
- continue;
- default:
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- }
- }
- } /* get_autoexec */
-
- /*
- * Compile a @SetConfig/@EndConfig block.
- */
-
- static void get_config(int in, project_t *project)
- { /* get_config */
- int dnum = 0; /* number of device= lines */
- patch_t *m;
- int token;
-
- if (project->config == NULL)
- project->config = smart_calloc(1, sizeof(patch_t), "get_config 1");
- m = project->config;
- m->last_drive = 0;
-
- /* set the default behavior */
- m->verbose = TRUE;
- m->action = SCREENPROTO;
-
- while (TRUE)
- {
- token = get_token(in, project);
- switch (token)
- {
- case ENDCONFIG:
- return;
- case TERSE:
- m->verbose = FALSE;
- continue;
- case OVERWRITE:
- case SCREENPROTO:
- case DISKPROTO:
- case ASKOVERWRITE:
- m->action = token;
- continue;
- case FILES:
- match(in, "=");
- if ((m->files = get_num(in)) < 8 || m->files > 255)
- expected("a number from 8 and 255");
- continue;
- case LASTDRIVE:
- match(in, "=");
- skip_white(in);
- m->last_drive = (byte) readch(in, TRUE);
- m->last_drive = toupper(m->last_drive);
- if (m->last_drive < 'A' || m->last_drive > 'Z')
- expected("a letter from A and Z");
- continue;
- case BUFFERS:
- match(in, "=");
- if ((m->buffers = get_num(in)) < 1 || m->buffers > 99)
- expected("a number from 1 and 99");
- continue;
- case DEVICE:
- match(in, "=");
- if ((m->device[dnum++] = get_str(in, project)) == NULL)
- expected("a DEVICE name");
- m->device[dnum] = NULL;
- continue;
- /* case SHELL:
- match(in,"/E:");
- if((m->env_size = get_num(in)) < 160 || m->env_size>32768)
- expected("a environment size number between 160 and 32768");
- continue;*/
- default:
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- }
- }
- # if defined(__MICROSOFTC__)
- m->device[dnum] = NULL; /* quiet microsoft's lint */
- # endif
- } /* get_config */
-
-
- void finish(project_t *project, byte *name)
- { /* finish */
- int token;
- int c, len;
- byte *p, *a, *s;
- int in;
- project_t local;
- byte *os = (byte *) "an operating system command string";
-
- /* see if a @finish block is in script file */
- if (restart == 0L)
- return;
-
- /* make local copy of these vars since free_mem will trash them */
- memmove((byte *) &local, (byte *) project, sizeof(project_t));
-
- in = open_data(&local, name);
- clr_readch();
-
- if (lseek(in, restart, 0) == -1)
- error("Unable to reopen script file \"INSTALL.DAT\"");
- line = old_line;
- vrestore();
- free_mem();
-
- for (;;)
- {
- while ((c = readch(in, TRUE)) != EOF && c != '@')
- sputch((byte) c);
-
- if (c == EOF)
- unexpected("end of file");
-
- unreadch(c);
- token = get_token(in, project);
-
- if (token == ENDFINISH)
- {
- cls();
- return;
- }
-
- switch (token)
- {
- case CHDRIVE:
- c = get_char(in);
- if (c == '@')
- {
- unreadch(c);
- token = get_token(in, project);
- switch (token)
- {
- case OUTDRIVE:
- c = project->out_drive;
- break;
- case INDRIVE:
- c = project->in_drive;
- break;
- default:
- if (!execute_if(in, project, token))
- unexpected((byte *) string);
- }
- }
- if (! isalpha(c))
- expected("a drive letter");
- setdisk(toupper(c) - 'A');
- continue;
- case CHDIR:
- if ((s = get_str(in, &local)) == NULL)
- expected("a directory");
- strupr(s);
- len = strlen(s);
- if (len > 1 && s[len - 1] == '\\')
- s[len - 1] = '\0';
- chdir(s);
- continue;
- case PAUSE:
- sputs("\nPress any key to continue ...");
- if (sgetch() == ESC)
- say_bye();
- sputs("\n");
- continue;
- case SYSTEM:
- if ((p = get_str(in, &local)) == NULL)
- expected(os);
- if (system(p) == -1)
- error("Unable to execute @System command");
- continue;
- case EXECUTE:
- if ((p = get_str(in, &local)) == NULL)
- expected(os);
- match(in, ",");
- if ((a = get_str(in, &local)) == NULL)
- expected(os);
- # ifdef LATTICE
- if (system(p) == -1)
- error("Unable to execute @System command");
- # else
- execlp(p, p, a, NULL);
- sputs(dosprob(NULL));
- sputs("Unable to execute @Execute command");
- sgetch();
- # endif
- continue;
- default:
- if (execute_verb(in, &local, token))
- continue;
- if (execute_if(in, project, token))
- continue;
- unexpected(string);
- }
- } /* forever */
- } /* finish */
-
-
- /*
- * Remove trailing \\ from a subdir string.
- */
-
- static byte *trim_dir(byte *dir)
- {
- int len;
- static byte local_copy[255];
-
- if (dir == NULL)
- return dir;
-
- if ((len = strlen(dir)) > 255)
- error("internal error: trim");
-
- strcpy(local_copy, dir);
- if (len > 1 && local_copy[len - 1] == '\\')
- local_copy[len - 1] = 0; /* skip \ */
-
- return local_copy;
- }
-
- /* end-of-file */