506 lines
11 KiB
C
506 lines
11 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
* Copyright (c) 1989, Mark Pizzolato (mark@infopiz.uucp)
|
|
*
|
|
* This source code is released for free distribution under the terms of the
|
|
* GNU General Public License.
|
|
*
|
|
* Provided by Stephen P. Wall <swall@redcom.com>
|
|
* Extracted from the VMS port of GNU patch-2.1.
|
|
*
|
|
* This module provides redirection support for the VAX DECC port of
|
|
* Exuberant Ctags.
|
|
*/
|
|
/*
|
|
* @(#)argproc.c 1.0 89/02/01 Mark Pizzolato (mark@infopiz.uucp)
|
|
*/
|
|
|
|
#ifndef lint
|
|
char argproc_version [] = "@(#)argproc.c VMS uucp Version infopiz-1.0";
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#include <descrip.h>
|
|
#include <dvidef.h>
|
|
#include <errno.h>
|
|
#include <iodef.h>
|
|
#include <lib$routines.h>
|
|
#include <starlet.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <syidef.h> /* System Information Definitions */
|
|
|
|
#define EXIT_OK 1 /* image exit code */
|
|
#define EXIT_ERR 0x10000000 /* image exit code */
|
|
|
|
/*
|
|
* getredirection() is intended to aid in porting C programs
|
|
* to VMS (Vax-11 C) which does not support '>' and '<'
|
|
* I/O redirection, along with a command line pipe mechanism
|
|
* using the '|' AND background command execution '&'.
|
|
* The piping mechanism will probably work with almost any 'filter' type
|
|
* of program. With suitable modification, it may useful for other
|
|
* portability problems as well.
|
|
*
|
|
* Author: Mark Pizzolato mark@infopiz.UUCP
|
|
* Mods: Steve Wall Don't return a full path unless the
|
|
* original filename included a path.
|
|
*/
|
|
struct list_item
|
|
{
|
|
struct list_item *next;
|
|
char *value;
|
|
};
|
|
|
|
static expand_wild_cards ();
|
|
static char *pipe_and_fork ();
|
|
|
|
int
|
|
getredirection (ac, av)
|
|
int *ac;
|
|
char ***av;
|
|
/*
|
|
* Process vms redirection arg's. Exit if any error is seen.
|
|
* If getredirection() processes an argument, it is erased
|
|
* from the vector. getredirection () returns a new argc and argv value.
|
|
* In the event that a background command is requested (by a trailing "&"),
|
|
* this routine creates a background subprocess, and simply exits the program.
|
|
*
|
|
* Warning: do not try to simplify the code for vms. The code
|
|
* presupposes that getredirection() is called before any data is
|
|
* read from stdin or written to stdout.
|
|
*
|
|
* Normal usage is as follows:
|
|
*
|
|
* main (argc, argv)
|
|
* int argc;
|
|
* char *argv [];
|
|
* {
|
|
* getredirection (&argc, &argv);
|
|
* }
|
|
*/
|
|
{
|
|
int argc = *ac; /* Argument Count */
|
|
char **argv = *av; /* Argument Vector */
|
|
char *ap; /* Argument pointer */
|
|
int j; /* argv [] index */
|
|
extern int errno; /* Last vms i/o error */
|
|
int item_count = 0; /* Count of Items in List */
|
|
struct list_item *list_head = 0; /* First Item in List */
|
|
struct list_item *list_tail; /* Last Item in List */
|
|
char *in = NULL; /* Input File Name */
|
|
char *out = NULL; /* Output File Name */
|
|
char *outmode = "w"; /* Mode to Open Output File */
|
|
int cmargc = 0; /* Piped Command Arg Count */
|
|
char **cmargv = NULL;/* Piped Command Arg Vector */
|
|
|
|
/*
|
|
* First handle the case where the last thing on the line ends with
|
|
* a '&'. This indicates the desire for the command to be run in a
|
|
* subprocess, so we satisfy that desire.
|
|
*/
|
|
{
|
|
extern background_process ();
|
|
ap = argv [argc-1];
|
|
if (0 == strcmp ("&", ap))
|
|
exit (background_process (--argc, argv));
|
|
if ('&' == ap [strlen (ap)-1])
|
|
{
|
|
ap [strlen (ap)-1] = '\0';
|
|
exit (background_process (argc, argv));
|
|
}
|
|
}
|
|
/*
|
|
* Now we handle the general redirection cases that involve '>', '>>',
|
|
* '<', and pipes '|'.
|
|
*/
|
|
for (j = 0; j < argc; ++j)
|
|
{
|
|
if (0 == strcmp ("<", argv [j]))
|
|
{
|
|
if (j+1 >= argc)
|
|
{
|
|
errno = EINVAL;
|
|
perror ("No input file");
|
|
exit (EXIT_ERR);
|
|
}
|
|
in = argv [++j];
|
|
continue;
|
|
}
|
|
if ('<' == *(ap = argv [j]))
|
|
{
|
|
in = 1 + ap;
|
|
continue;
|
|
}
|
|
if (0 == strcmp (">", ap))
|
|
{
|
|
if (j+1 >= argc)
|
|
{
|
|
errno = EINVAL;
|
|
perror ("No output file");
|
|
exit (EXIT_ERR);
|
|
}
|
|
out = argv [++j];
|
|
continue;
|
|
}
|
|
if ('>' == *ap)
|
|
{
|
|
if ('>' == ap [1])
|
|
{
|
|
outmode = "a";
|
|
if ('\0' == ap [2])
|
|
out = argv [++j];
|
|
else
|
|
out = 2 + ap;
|
|
}
|
|
else
|
|
out = 1 + ap;
|
|
continue;
|
|
}
|
|
if (0 == strcmp ("|", argv [j]))
|
|
{
|
|
if (j+1 >= argc)
|
|
{
|
|
errno = EPIPE;
|
|
perror ("No command to Pipe to");
|
|
exit (EXIT_ERR);
|
|
}
|
|
cmargc = argc- (j+1);
|
|
cmargv = &argv [j+1];
|
|
argc = j;
|
|
continue;
|
|
}
|
|
if ('|' == *(ap = argv [j]))
|
|
{
|
|
++argv [j];
|
|
cmargc = argc-j;
|
|
cmargv = &argv [j];
|
|
argc = j;
|
|
continue;
|
|
}
|
|
expand_wild_cards (ap, &list_head, &list_tail, &item_count);
|
|
}
|
|
/*
|
|
* Allocate and fill in the new argument vector, Some Unix's terminate
|
|
* the list with an extra null pointer.
|
|
*/
|
|
argv = *av = calloc (item_count+1, sizeof (char *));
|
|
for (j = 0; j < item_count; ++j, list_head = list_head->next)
|
|
argv [j] = list_head->value;
|
|
*ac = item_count;
|
|
if (cmargv != NULL)
|
|
{
|
|
char subcmd [1024];
|
|
|
|
if (out != NULL)
|
|
{
|
|
errno = EINVAL;
|
|
perror ("Invalid '|' and '>' specified");
|
|
exit (EXIT_ERR);
|
|
}
|
|
strcpy (subcmd, cmargv [0]);
|
|
for (j = 1; j < cmargc; ++j)
|
|
{
|
|
strcat (subcmd, " \"");
|
|
strcat (subcmd, cmargv [j]);
|
|
strcat (subcmd, "\"");
|
|
}
|
|
out = pipe_and_fork (subcmd);
|
|
}
|
|
if ((in != NULL) && (NULL == freopen (in, "r", stdin, "mbc=32", "mbf=2")))
|
|
{
|
|
perror (in); /* Can't find file */
|
|
exit (EXIT_ERR); /* Is a fatal error */
|
|
}
|
|
if ((out != NULL) && (NULL == freopen (out, outmode, stdout, "mbc=32", "mbf=2")))
|
|
{
|
|
perror (ap); /* Error, can't write or append */
|
|
exit (EXIT_ERR); /* Is a fatal error */
|
|
}
|
|
#ifdef DEBUG
|
|
fprintf (stderr, "Arglist:\n");
|
|
for (j = 0; j < *ac; ++j)
|
|
fprintf (stderr, "argv[%d] = '%s'\n", j, argv [j]);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static add_item (head, tail, value, count)
|
|
struct list_item **head;
|
|
struct list_item **tail;
|
|
char *value;
|
|
int *count;
|
|
{
|
|
if (*head == 0)
|
|
{
|
|
if (NULL == (*head = calloc (1, sizeof (**head))))
|
|
{
|
|
errno = ENOMEM;
|
|
perror ("");
|
|
exit (EXIT_ERR);
|
|
}
|
|
*tail = *head;
|
|
}
|
|
else
|
|
if (NULL == ((*tail)->next = calloc (1, sizeof (**head))))
|
|
{
|
|
errno = ENOMEM;
|
|
perror ("");
|
|
exit (EXIT_ERR);
|
|
}
|
|
else
|
|
*tail = (*tail)->next;
|
|
(*tail)->value = value;
|
|
++ (*count);
|
|
}
|
|
|
|
static expand_wild_cards (item, head, tail, count)
|
|
char *item;
|
|
struct list_item **head;
|
|
struct list_item **tail;
|
|
int *count;
|
|
{
|
|
int expcount = 0;
|
|
int context = 0;
|
|
int status;
|
|
int status_value;
|
|
char *had_version;
|
|
int had_path;
|
|
$DESCRIPTOR (filespec, item);
|
|
/*$DESCRIPTOR (defaultspec, "SYS$DISK:[]*.*;");*/
|
|
$DESCRIPTOR (defaultspec, "");
|
|
$DESCRIPTOR (resultspec, "");
|
|
|
|
if (strcspn (item, "*%") == strlen (item))
|
|
{
|
|
add_item (head, tail, item, count);
|
|
return;
|
|
}
|
|
resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
|
|
resultspec.dsc$b_class = DSC$K_CLASS_D;
|
|
resultspec.dsc$a_pointer = NULL;
|
|
filespec.dsc$w_length = strlen (item);
|
|
/*
|
|
* Only return version specs, if the caller specified a version
|
|
*/
|
|
had_version = strchr (item, ';');
|
|
/*
|
|
* Only return full path if the caller specified a path
|
|
*/
|
|
had_path = (strchr (item, ']') || strchr (item, ':'));
|
|
while (1 == (1&lib$find_file (&filespec, &resultspec, &context,
|
|
&defaultspec, 0, &status_value, &0)))
|
|
{
|
|
char *string;
|
|
char *c;
|
|
|
|
if (NULL == (string = calloc (1, resultspec.dsc$w_length+1)))
|
|
{
|
|
errno = ENOMEM;
|
|
perror ("");
|
|
exit (EXIT_ERR);
|
|
}
|
|
strncpy (string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
|
|
string [resultspec.dsc$w_length] = '\0';
|
|
if (NULL == had_version)
|
|
*((char *) strrchr (string, ';')) = '\0';
|
|
if (!had_path) {
|
|
char *s = strrchr (string, ']');
|
|
if ( s == NULL ) s = strrchr (string, ':');
|
|
if ( s != NULL ) strcpy (string, s+1);
|
|
}
|
|
/*
|
|
* Be consistent with what the C RTL has already done to the rest of
|
|
* the argv items and lowercase all of these names.
|
|
*/
|
|
for (c = string; *c; ++c)
|
|
if (isupper (*c))
|
|
*c = tolower (*c);
|
|
add_item (head, tail, string, count);
|
|
++expcount;
|
|
}
|
|
if (expcount == 0)
|
|
add_item (head, tail, item, count);
|
|
lib$sfree1_dd (&resultspec);
|
|
lib$find_file_end (&context);
|
|
}
|
|
|
|
static int child_st [2]; /* Event Flag set when child process completes */
|
|
|
|
static short child_chan;/* I/O Channel for Pipe Mailbox */
|
|
|
|
static exit_handler (status)
|
|
int *status;
|
|
{
|
|
short iosb [4];
|
|
|
|
if (0 == child_st [0])
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf (stderr, "Waiting for Child Process to Finnish . . .\n");
|
|
#endif
|
|
sys$qiow (0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
sys$dassgn (child_chan);
|
|
fclose (stdout);
|
|
sys$synch (0, child_st);
|
|
}
|
|
}
|
|
|
|
|
|
static sig_child (chan)
|
|
int chan;
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf (stderr, "Child Completion AST\n");
|
|
#endif
|
|
if (child_st [0] == 0)
|
|
child_st [0] = 1;
|
|
}
|
|
|
|
static struct exit_control_block
|
|
{
|
|
struct exit_control_block *flink;
|
|
int (*exit_routine) ();
|
|
int arg_count;
|
|
int *status_address;
|
|
int exit_status;
|
|
} exit_block =
|
|
{
|
|
0,
|
|
exit_handler,
|
|
1,
|
|
&exit_block.exit_status,
|
|
0
|
|
};
|
|
|
|
static char *pipe_and_fork (cmd)
|
|
char *cmd;
|
|
{
|
|
$DESCRIPTOR (cmddsc, cmd);
|
|
static char mbxname [64];
|
|
$DESCRIPTOR (mbxdsc, mbxname);
|
|
short iosb [4];
|
|
int status;
|
|
int pid;
|
|
struct
|
|
{
|
|
short dna_buflen;
|
|
short dna_itmcod;
|
|
char *dna_buffer;
|
|
unsigned short *dna_retlen;
|
|
int listend;
|
|
} itmlst =
|
|
{
|
|
sizeof (mbxname),
|
|
DVI$_DEVNAM,
|
|
mbxname,
|
|
&mbxdsc.dsc$w_length,
|
|
0
|
|
};
|
|
int mbxsize;
|
|
struct
|
|
{
|
|
short mbf_buflen;
|
|
short mbf_itmcod;
|
|
int *mbf_maxbuf;
|
|
unsigned short *mbf_retlen;
|
|
int listend;
|
|
} syiitmlst =
|
|
{
|
|
sizeof (mbxsize),
|
|
SYI$_MAXBUF,
|
|
&mbxsize,
|
|
0,
|
|
0
|
|
};
|
|
|
|
cmddsc.dsc$w_length = strlen (cmd);
|
|
/*
|
|
* Get the SYSGEN parameter MAXBUF, and the smaller of it and 2048 as
|
|
* the size of the 'pipe' mailbox.
|
|
*/
|
|
if (1 == (1& (vaxc$errno = sys$getsyiw (0, 0, 0, &syiitmlst, iosb, 0, 0, 0))))
|
|
vaxc$errno = iosb [0];
|
|
if (0 == (1&vaxc$errno))
|
|
{
|
|
errno = EVMSERR;
|
|
perror ("Can't get SYSGEN parameter value for MAXBUF");
|
|
exit (EXIT_ERR);
|
|
}
|
|
if (mbxsize > 2048)
|
|
mbxsize = 2048;
|
|
if (0 == (1& (vaxc$errno = sys$crembx (0, &child_chan, mbxsize, mbxsize, 0, 0, 0))))
|
|
{
|
|
errno = EVMSERR;
|
|
perror ("Can't create pipe mailbox");
|
|
exit (EXIT_ERR);
|
|
}
|
|
if (1 == (1& (vaxc$errno = sys$getdviw (0, child_chan, 0, &itmlst, iosb,
|
|
0, 0, 0))))
|
|
vaxc$errno = iosb [0];
|
|
if (0 == (1&vaxc$errno))
|
|
{
|
|
errno = EVMSERR;
|
|
perror ("Can't get pipe mailbox device name");
|
|
exit (EXIT_ERR);
|
|
}
|
|
mbxname [mbxdsc.dsc$w_length] = '\0';
|
|
#ifdef DEBUG
|
|
fprintf (stderr, "Pipe Mailbox Name = '%s'\n", mbxname);
|
|
#endif
|
|
if (0 == (1& (vaxc$errno = lib$spawn (&cmddsc, &mbxdsc, 0, &1,
|
|
0, &pid, child_st, &0, sig_child,
|
|
&child_chan))))
|
|
{
|
|
errno = EVMSERR;
|
|
perror ("Can't spawn subprocess");
|
|
exit (EXIT_ERR);
|
|
}
|
|
#ifdef DEBUG
|
|
fprintf (stderr, "Subprocess's Pid = %08X\n", pid);
|
|
#endif
|
|
sys$dclexh (&exit_block);
|
|
return (mbxname);
|
|
}
|
|
|
|
background_process (argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
char command [2048] = "$";
|
|
$DESCRIPTOR (value, command);
|
|
$DESCRIPTOR (cmd, "BACKGROUND$COMMAND");
|
|
$DESCRIPTOR (null, "NLA0:");
|
|
int pid;
|
|
|
|
strcat (command, argv [0]);
|
|
while (--argc)
|
|
{
|
|
strcat (command, " \"");
|
|
strcat (command, *(++argv));
|
|
strcat (command, "\"");
|
|
}
|
|
value.dsc$w_length = strlen (command);
|
|
if (0 == (1& (vaxc$errno = lib$set_symbol (&cmd, &value))))
|
|
{
|
|
errno = EVMSERR;
|
|
perror ("Can't create symbol for subprocess command");
|
|
exit (EXIT_ERR);
|
|
}
|
|
if (0 == (1& (vaxc$errno = lib$spawn (&cmd, &null, 0, &17, 0, &pid))))
|
|
{
|
|
errno = EVMSERR;
|
|
perror ("Can't spawn subprocess");
|
|
exit (EXIT_ERR);
|
|
}
|
|
#ifdef DEBUG
|
|
fprintf (stderr, "%s\n", command);
|
|
#endif
|
|
fprintf (stderr, "%08X\n", pid);
|
|
return (EXIT_OK);
|
|
}
|
|
|
|
/* vi:set tabstop=4 shiftwidth=4: */
|