Category : C Source Code
Archive   : OHLMAKE.ZIP
Filename : SPAWN.C

 
Output of file : SPAWN.C contained in archive : OHLMAKE.ZIP
/* spawn.c - replacements for MSC spawn*() functions
Copyright (C) 1990 by Thorsten Ohl, [email protected]

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

IMPORTANT:

This code is not an official part of the GNU project and the
author is not affiliated to the Free Software Foundation.
He just likes their code and spirit. */

static char RCS_id[] =
"$Header: e:/gnu/make/RCS/spawn.c'v 0.18 90/07/22 14:42:38 tho Exp $";


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include


#define MAX_MSDOS_CMDLINE 126
#define MAX_MSDOS_PATH 144

/* IMPORTED DECLARATIONS */

enum swapping_mode
{
none, disk, ems, xms
};

extern int
spawn_child (enum swapping_mode mode, char *path, char *cmdline, char *env,
int len, char *swap_file);


/* PRIVATE DECLARATIONS */

static void swapping_mode_hook (enum swapping_mode * swapping_ptr, char *dev);
static int commandline_hook (char *cmd, char **argv, char **env0,
char **respond_file_name_ptr);
static char *putenv_cmdline (char *cmd, char **argv);
static int build_environment (char _far ** envbuf, char *cmdline,
char **envvec);
static char *build_link_respond_file (char **argv);
static char *build_lib_respond_file (char **argv);
static char *build_zip_respond_file (char **argv);
static int build_cmdline (char **argv);
static char *expand_path (char *name, char *env, char *(*select) (char *));
static char *executable_p (char *path);
extern void *xmalloc (size_t size);
extern void *xrealloc (void *ptr, size_t size);

static char *extensions[] =/* extensions recognized during */
{ /* path search */
".bat", /* this will be passed to the shell */
".exe",
".com",
NULL,
};

#define PATH_SEP ';'
#define FSLASH '/'
#define BSLASH '\\'


/* PUBLIC DECLARATIONS */

extern char *mktmpname (char *prefix);


/* The main entry points, work just like spawnvpe?() from the standard MSC
library. */

int swap_and_spawnvp (char *device, char *cmd, char **argv);
int swap_and_spawnvpe (char *device, char *cmd, char **argv, char **envvec);

int
swap_and_spawnvp (char *device, char *cmd, char **argv)
{
return swap_and_spawnvpe (device, cmd, argv, NULL);
}

int
swap_and_spawnvpe (char *device, char *cmd, char **argv, char **envvec)
{
int len;
int rc;
char _far *env = (char _far *) 0;
char *env0 = NULL;
char *respond_file_name = NULL;
char *swap_file_name = mktmpname ("vm");
enum swapping_mode swapping = xms;

if (device != NULL)
swapping_mode_hook (&swapping, device);

argv[0] = expand_path (cmd, "PATH", executable_p);
if (argv[0] == NULL)
return ENOENT;

rc = commandline_hook (cmd, argv, &env0, &respond_file_name);
if (rc)
return rc;

len = build_environment (&env, env0, (envvec == NULL) ? environ : envvec);
if (!len)
return E2BIG;

rc = spawn_child (swapping, argv[0], argv[1], env, len, swap_file_name);

if (env0)
free (env0);
if (env)
free (env);
if (*argv[1])
free (argv[1]);
free (swap_file_name);

if (respond_file_name) /* clean up disk */
unlink (respond_file_name);

return rc;
}


/* Look if DEV corresponds to a known swapping device and set *SWAPPING_PTR
accordingly; */

void
swapping_mode_hook (enum swapping_mode * swapping_ptr, char *dev)
{
if (dev)
{
strlwr (dev);
if (!strcmp ("xms", dev))
*swapping_ptr = xms;
else if (!strcmp ("ems", dev))
{
fprintf (stderr, "Swapping to expanded memory is not supported yet, "
"will try extended memory.\n");
*swapping_ptr = xms;
}
else if (!strcmp ("disk", dev))
*swapping_ptr = disk;
else if (!strcmp ("none", dev))
{
fprintf (stderr, "No swapping is not supported yet, "
"will swap to disk.\n");
*swapping_ptr = disk;
}
else
{
fprintf (stderr, "Can't swap to `%s' (no such device), "
"will try extended memory.\n", dev);
*swapping_ptr = xms;
}
}
}

/* Look if CMD allows or requires special treatment, manipulate ARGV
accordingly. If necessary, create ENV0 and RESPOND_FILE_NAME_PTR.
In any case, merge ARGV[1], ... into ARGV[1].
~~~~~~~~~~~
Note that the special treatment can be switched off by giving CMD
uppercase! */

int
commandline_hook (char *cmd, char **argv, char **env0,
char **respond_file_name_ptr)
{
if (!strcmp ("cl", cmd) || !strcmp ("fl", cmd) || !strcmp ("masm", cmd))
*env0 = putenv_cmdline (cmd, argv);
else if (!strcmp ("link", cmd))
{
*respond_file_name_ptr = build_link_respond_file (argv);
if (*respond_file_name_ptr == NULL)
return E2BIG;
}
else if (!strcmp ("lib", cmd))
{
*respond_file_name_ptr = build_lib_respond_file (argv);
if (*respond_file_name_ptr == NULL)
return E2BIG;
}
else if (!strcmp ("pkzip", cmd) || !strcmp ("pkunzip", cmd))
{
*respond_file_name_ptr = build_zip_respond_file (argv);
if (*respond_file_name_ptr == NULL)
return E2BIG;
}
else if (!build_cmdline (argv))
return E2BIG;

return 0; /* success */
}


/* Manipulating the environment. */

/* Merge CMDLINE, ENVVEC[0], ... into ENVBUF as a well formed MS-DOS
environment (will be malloc()'d). Returns the length (incl. the
trailing '\0's) of the resulting environment. */

int
build_environment (char _far ** envbuf, char *cmdline, char **envvec)
{
char *p;
char **envptr = envvec;
int len = 1;

if (envvec == NULL && cmdline == NULL)
return 0;

if (cmdline != NULL)
len += strlen (cmdline) + 1;
if (envvec != NULL)
while (*envptr)
len += strlen (*envptr++) + 1;

p = *envbuf = (char *) xmalloc (len);

if (cmdline != NULL)
{
strcpy (p, cmdline);
p += strlen (cmdline) + 1;
}
envptr = envvec;
if (envvec != NULL)
while (*envptr)
{
strcpy (p, *envptr);
p += strlen (*envptr++) + 1;
}
*p++ = '\0'; /* end of environment! */

return (len);
}


/* Manipulating the commandline. */

/* The following functions for manipulating the commandline do NOT free
the storage of the ARGV[] strings, since they can't know how it was
allocated (separately or as single block). */

/* Build an environment entry for passing options to the Microsoft C
and Fortran Compilers and Macro Assembler. CMD is the command to
execute. ARGV[1],... will be moved to an environment entry "CMD" and
we will have ARGV[1] == "". */

char *
putenv_cmdline (char *cmd, char **argv)
{
int nologo_flag = 0;
char **av = argv + 1;
char *p;
char *env;
int len = 3 + strlen (cmd);

/* `fl' and `cl' understand the "-nologo" option to suppress the banner,
`masm' not. */
if (!strcmp (cmd, "fl") || !strcmp (cmd, "cl"))
{
nologo_flag = 1;
len += 8;
}

while (*av)
len += strlen (*av++) + 1;

p = env = (char *) xmalloc (len);

strcpy (p, strupr (cmd));
p += strlen (cmd);
*p++ = '=';

if (nologo_flag)
{
strcpy (p, "-nologo");
p += 7;
}

av = argv + 1;
while (*av) /* paste arguments together */
{
*p++ = ' ';
strcpy (p, *av);
p += strlen (*av++);
}

*argv[1] = '\0'; /* commandline terminated */

return env;
}


/* Build a respondfile for the Microsoft linker from ARGV[0]. Returns the
name of the generated file on success, NULL otherwise. The new
commandline ARGV[1] holds the proper command for instructing the linker
to use this respondfile. */

char *
build_link_respond_file (char **argv)
{
int len = 0;
char **av = argv;
FILE *respond_file;
char *respond_file_name = mktmpname ("lk");

if (!(respond_file = fopen (respond_file_name, "w")))
{
fprintf (stderr, "can't open link respond_file %s.\n",
respond_file_name);
return NULL;
}

while (*++av)
{
char *cp;

if (strlen (*av) + len > 50)
{
fprintf (respond_file, "+\n");
len = 0;
}

while (cp = strchr (*av, ',')) /* new respond group? */
{
*cp = '\n';
len = *av - cp - 1; /* precompensate */
}

len += fprintf (respond_file, "%s ", *av);
}

fprintf (respond_file, ";\n");/* avoid prompts! */

fclose (respond_file);

argv[1] = (char *) xmalloc (strlen (respond_file_name) + 9);
sprintf (argv[1], "/batch @%s", respond_file_name);

return respond_file_name;
}


/* Build a respondfile for the Microsoft library manager from ARGV[0].
Returns the name of the generated file on success, NULL otherwise. The
new commandline ARGV[1] holds the proper command for instructing the
library manager to use this respondfile. A module without command
inherits the one of it's predecessor. This allows constructions like
`lib foo.lib -+ $?' in the makefiles (idea stolen from Kneller's
`ndmake'). */

char *
build_lib_respond_file (char **argv)
{
int len = 0;
char **av = argv;
FILE *respond_file;
char *respond_file_name = mktmpname ("lb");
static char lib_action[3] = "+";

if (!(respond_file = fopen (respond_file_name, "w")))
{
fprintf (stderr, "can't open lib respond_file %s.\n",
respond_file_name);
return NULL;
}

fprintf (respond_file, " %s\n ", *++av); /* easy: the lib file */

if (access (*av, 0)) /* new library */
fprintf (respond_file, "y\n "); /* create it! */

while (*++av)
{
char *cp;

if (strchr ("+-*", **av))
{
lib_action[0] = *(*av)++;

if (strchr ("+-*", **av))
lib_action[1] = *(*av)++;
else
lib_action[1] = '\0';
}

if (**av)
{
if (strlen (*av) + len > 50)
{
fprintf (respond_file, "&\n ");
len = 0;
}

len += fprintf (respond_file, "%s", lib_action) + 1;

while (cp = strchr (*av, ',')) /* new respond group? */
{
*cp = '\n';
len = *av - cp - 1; /* precompensate */
lib_action[0] = '\0'; /* no more actions! */
}

len += fprintf (respond_file, "%s ", *av) + 1;
}
}

fprintf (respond_file, ";\n");/* avoid prompts! */

fclose (respond_file);

argv[1] = (char *) xmalloc (strlen (respond_file_name) + 10);
sprintf (argv[1], "/nologo @%s", respond_file_name);

return respond_file_name;
}


/* Build a respondfile for the pk(un)?zip archive managers from ARGV[0].
Returns the name of the generated file on success, NULL otherwise. The
new commandline ARGV[1] holds the proper command for instructing
pk(un)?zip to use this respondfile. */

char *
build_zip_respond_file (char **argv)
{
char *cmdline = (char *) xmalloc (127 * sizeof (char));
char *ptr = cmdline;
char **av = argv + 1;
int len = 0;
FILE *respond_file = NULL;
char *respond_file_name = mktmpname ("zi");

respond_file = fopen (respond_file_name, "w");
if (respond_file == NULL)
{
fprintf (stderr, "can't open zip respondfile %s.\n", respond_file_name);
return NULL;
}

while (**av == '-') /* leave options on the commandline */
{
if (ptr - cmdline + strlen (*av) > 126)
{
free (cmdline);
return NULL;
}
strcpy (ptr, *av);
ptr += strlen (*av);
*ptr++ = ' ';
av++;
}

if (*av == NULL /* missing zipfilename? */
|| ptr - cmdline + strlen (*av) + strlen (respond_file_name) > 124)
{
free (cmdline);
return NULL;
}

sprintf (ptr, "%s @%s", *av++, respond_file_name);

while (*av)
fprintf (respond_file, "%s\n", *av++);

fprintf (respond_file, "\n");
fclose (respond_file);

argv[1] = cmdline;

return respond_file_name;
}


/* Build a MS-DOS commandline from the supplied argument vector ARGV and
put it into ARGV[1]. Storage of the old argv[1], ... is NOT free()'d,
since we can't know how it was allocated (separately or as single
block)! ARGV[0] is assumed to be a fully expanded pathname incl
extension. If ARGV[0] ends int ".bat", we will pass the command to the
shell. Returns 1 on success, 0 if the commandline is too long. */

int
build_cmdline (char **argv)
{
char *cmdline = (char *) xmalloc (128 * sizeof (char));
char *p = cmdline;
char *tmp;
char **av;

if ((tmp = strrchr (argv[0], '.')) /* .bat files will be passed */
&& !strcmp (tmp + 1, "bat")) /* to the shell ... */
{
*tmp = '\0'; /* terminate */

strcpy (p, "/c "); /* return after execution */
p += 3;

if ((tmp = strrchr (argv[0], BSLASH)) /* strip path */
|| (tmp = strrchr (argv[0], FSLASH)))
strcpy (p, tmp + 1);
else
strcpy (p, argv[0]);

p += strlen (p);
*p++ = ' ';

if (!(argv[0] = getenv ("COMSPEC")))
{
fprintf (stderr, "command processor not found!");
exit (1);
}
}

av = argv; /* paste arguments together */
while (*++av)
{
strcpy (p, *av);
p += strlen (*av);
*p++ = ' ';
if ((p - cmdline) > 126)
return (0); /* commandline to long for MeSy-DOS */
}

*p = '\0';
argv[1] = cmdline;

return (1); /* success */
}



/* $PATH search. */

/* Look for NAME in the directories from $ENV ($PATH, if ENV is NULL),
satisfying SELECT. */

char *
expand_path (char *name, char *env, char *(*select) (char *))
{
size_t name_len = strlen (name) + 6; /* SELECT may append 4 chars! */
char *exp = (char *) xmalloc (name_len);

strcpy (exp, name);

if ((*select) (exp)) /* first look in current directory. */
return exp;

/* If not an absolute path, scan $PATH */

if (exp[1] != ':' && *exp != BSLASH && *exp != FSLASH)
{
char *ptr = getenv ((env == NULL) ? "PATH" : env);
if (ptr != NULL)
{
char *path = (char *) alloca (strlen (ptr) + 1);
strcpy (path, ptr); /* get a copy strtok() can butcher. */

ptr = strtok (path, ";");

while (ptr != NULL)
{
exp = (char *) xrealloc (exp, strlen (ptr) + name_len);
if ((*select) (strcat (strcat (strcpy (exp, ptr), "/"), name)))
return exp;
ptr = strtok (NULL, ";");
}
}
}

free (exp);
return NULL; /* We've failed! */
}


/* Return the expanded path with extension iff PATH is an executable MS-DOS
program, NULL otherwise. */

static char *
executable_p (char *path)
{
char *base;
const char **ext = extensions;

base = strrchr (strrchr (path, BSLASH), FSLASH);
if (base == NULL)
base = path;

if (strchr (base, '.')) /* explicit extension? */
{
if (!access (path, 0))
return path;
}
else
{
while (*base) /* point to the end */
*base++;

while (*ext) /* try all extensions */
{
strcpy (base, *ext++);
if (!access (path, 0))
return path;
}
}

return NULL; /* failed */
}


/* Get a unique filename in the temporary directory from the environment
entry TMP or TEMP, if this fails, use current directory. (replacement
for tempnam(), whish is *too* touchy about trailing path separators!) */

char *
mktmpname (char *prefix)
{
register int len;
register char *ptr;
register char *tmpname;
static char default_prefix[3] = "vm";

if (!prefix[0] || !prefix[1]) /* did the user supply a prefix? */
prefix = default_prefix;

if (!(ptr = getenv ("TMP")) && !(ptr = getenv ("TEMP")))
{

ptr = ".";
len = 1;
}
else
{
len = strlen (ptr) - 1;
if ((ptr[len] == FSLASH) || (ptr[len] == BSLASH))
ptr[len] = '\0';
}

tmpname = xmalloc (len + 10);

sprintf (tmpname, "%s\\%2sXXXXXX", ptr, prefix);
mktemp (tmpname);

return tmpname;
}


#ifdef TEST

extern void main (int ac, char **av);

void *
xmalloc (size_t size)
{
void *result = malloc (size);
if (result == NULL)
{
fprintf (stderr, "virtual memory exhausted");
abort ();
}
return result;
}

void *
xrealloc (void *ptr, size_t size)
{
void *result = realloc (ptr, size);
if (result == NULL)
{
fprintf (stderr, "virtual memory exhausted");
abort ();
}
return result;
}

void
main (int ac, char **av)
{
int n = 0;
int j = 2;
char **argv;

argv = (char **) xmalloc ((ac - 1) * sizeof (char **));

while (j < ac)
{
argv[n] = (char *) xmalloc (strlen (av[j]) + 1);
strcpy (argv[n++], av[j++]);
}
argv[n] = NULL;

swap_and_spawnvp (av[1], *argv, argv);
}

#endif /* TEST */


/*
* Local Variables:
* mode:C
* ChangeLog:ChangeLog
* compile-command:cl -DTEST -W3 vmspawn.c swapcore
* End:
*/


  3 Responses to “Category : C Source Code
Archive   : OHLMAKE.ZIP
Filename : SPAWN.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/