diff options
Diffstat (limited to 'lib/libpbmvms.c')
-rw-r--r-- | lib/libpbmvms.c | 734 |
1 files changed, 734 insertions, 0 deletions
diff --git a/lib/libpbmvms.c b/lib/libpbmvms.c new file mode 100644 index 00000000..8a2a54f8 --- /dev/null +++ b/lib/libpbmvms.c @@ -0,0 +1,734 @@ +/*************************************************************************** + This file contains library routines needed to build Netpbm for VMS. + However, as of 2000.05.26, when these were split out of libpbm1.c + (where they were all switched by #ifdef VMS), there is no evidence + that anyone is building Netpbm for VMS. +****************************************************************************/ + +/* + * @(#)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 "includes.h" /* System include files, system dependent */ + + +/* + * getredirection() is intended to aid in porting C programs + * to VMS (Vax-11 C) which does not have '>' 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 + */ +struct list_item + { + struct list_item *next; + char *value; + }; + +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 */ + int new_file; /* flag, true if '>' used */ + 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 */ + stat_t statbuf; /* fstat buffer */ + + /* + * 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. + */ + 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 (new_file = 0, 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]; + new_file = 1; + continue; + } + if ('>' == *ap) + { + if ('>' == ap[1]) + { + outmode = "a"; + if ('\0' == ap[2]) + out = argv[++j]; + else + out = 2 + ap; + } + else + { out = 1 + ap; new_file = 1; } + 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; + outmode = "wb"; /* pipes are binary mode devices */ + continue; + } + if ('|' == *(ap = argv[j])) + { + ++argv[j]; + cmargc = argc-j; + cmargv = &argv[j]; + argc = j; + outmode = "wb"; /* pipes are binary mode devices */ + 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]; + static char *pipe_and_fork(); + + 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); + outmode = "wb"; + } + + /* Check for input from a pipe (mailbox) */ + + if(fstat(0, &statbuf) == 0){ + if(strncmp(statbuf.st_dev, "MB", 2) == 0 || + strncmp(statbuf.st_dev, "_MB", 3) == 0){ + + /* Input from a pipe, reopen it in binary mode to disable */ + /* carriage control processing. */ + + if (in != NULL){ + errno = EINVAL; + perror("Invalid '|' and '<' specified"); + exit(EXIT_ERR); + } + freopen(statbuf.st_dev, "rb", stdin); + } + } + else { + perror("fstat failed"); + exit(EXIT_ERR); + } + +#ifdef __ALPHA + /*, "mbc=32", "mbf=2"))) blows up on the ALPHA cbm 11/08/92 */ + if ((in != NULL) && (NULL == freopen(in, "r", stdin))) + { +#else + if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2"))) + { +#endif + perror(in); /* Can't find file */ + exit(EXIT_ERR); /* Is a fatal error */ + } +#ifdef __ALPHA + if ((out != NULL) && (NULL == freopen(out, outmode, stdout))) + { +#else + if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2"))) + { +#endif + perror(ap); /* Error, can't write or append */ + exit(EXIT_ERR); /* Is a fatal error */ + } + + if ( new_file ) { + /* + * We are making an explicit output file, fstat the file and + * declare exit handler to be able change the file to fixed length + * records if necessary. + */ + char fname[256]; + static int outfile_rundown(), check_outfile_filetype(); + static stat_t ofile_stat; + static struct exit_control_block { + struct exit_control_block *flink; + int (*exit_routine)(); + int arg_count; + int *status_address; /* arg 1 */ + stat_t *stat; /* arg 2 */ + int exit_status; + int skew[128]; + } exit_block = + { 0, outfile_rundown, 2, &exit_block.exit_status, &ofile_stat, 0 }; + + if ( fstat ( fileno(stdout), &ofile_stat ) == 0 ) + sys$dclexh ( &exit_block ); + else fprintf(stderr,"Error fstating stdout - %s\n", + strerror(errno,vaxc$errno) ); + + if ( fgetname ( stdout, fname, 0 ) ) check_outfile_filetype ( fname ); + } +#ifdef DEBUG + fprintf(stderr, "Arglist:\n"); + for (j = 0; j < *ac; ++j) + fprintf(stderr, "argv[%d] = '%s'\n", j, argv[j]); +#endif +} + +static int binary_outfile = 0; +void set_outfile_binary() { binary_outfile = 1; return; } + +/* + * Check if output file should be set to binary (fixed 512) on exit based + * upon the filetype. + */ +static int check_outfile_filetype ( name ) + char *name; +{ + char *binary_filetypes, *ext, *p, *t; + binary_filetypes = (char *) getenv ( "PBM_BINARY_FILETYPES" ); + if ( binary_filetypes == NULL ) return 0; + + ext = strchr ( name, '.' ); if ( ext == NULL ) return 0; + ext++; + t = strrchr ( name, '.' ); if ( t != NULL ) *t = '\0'; + + for ( p = binary_filetypes; *p != '\0'; p++ ) { + for ( t = p; + (*p != '\0' ) && (strchr ( ", ", *p ) == NULL); + p++ ) *p = toupper(*p); + *p = '\0'; + + if ( strcmp ( t, ext ) == 0 ) { + binary_outfile = 1; + break; + } + } + return binary_outfile; +} + + +/* + * Exit handler to set output file to binary on image exit. + */ +static int outfile_rundown ( reason, statbuf ) + int *reason; + stat_t *statbuf; +{ + int code, channel, status, LIB$GETDVI(), sys$assign(), sys$qiow(); + long int fib_desc[2], devchar; + short int iosb[4]; + $DESCRIPTOR(device, statbuf->st_dev); + struct fibdef fib; /* File information block (XQP) */ + struct atrdef atr[2]; + struct fat { + unsigned char rtype, ratattrib; + unsigned short int rsize, hiblk[2], efblk[2], ffbyte, maxrec, defext, gbc; + unsigned short int reserved[4], versions; + } rat; + + if ( !binary_outfile ) return 1; /* leave file alone */ + /* + * Assign channel to device listed in stat block and test if it is + * a directory structured device, returning if not. + */ + device.dsc$w_length = strlen ( statbuf->st_dev ); + status = sys$assign ( &device, &channel, 0, 0 ); + if ((status & 1) == 0) { fprintf(stderr, + "assign error to %s (%d)\n", device.dsc$a_pointer, status); + return status; } + code = DVI$_DEVCHAR; + status = LIB$GETDVI ( &code, &channel, 0, &devchar ); + if ((status & 1) == 0) { fprintf(stderr, "getdvi error: %d\n", status); + return status; } + if ( (devchar & DEV$M_DIR) == 0 ) return 1; + /* + * Build File Information Block and Atrribute block. + */ +#ifdef __ALPHA + fib.fib$w_fid[0] = statbuf->st_ino[0]; + fib.fib$w_fid[1] = statbuf->st_ino[1]; + fib.fib$w_fid[2] = statbuf->st_ino[2]; +#else + fib.fib$r_fid_overlay.fib$w_fid[0] = statbuf->st_ino[0]; + fib.fib$r_fid_overlay.fib$w_fid[1] = statbuf->st_ino[1]; + fib.fib$r_fid_overlay.fib$w_fid[2] = statbuf->st_ino[2]; +#endif + + atr[0].atr$w_size = sizeof(rat); + atr[0].atr$w_type = ATR$C_RECATTR; + atr[0].atr$l_addr = &rat; + atr[1].atr$w_size = 0; atr[1].atr$w_type = 0; + /* + * Perform access function with read_attributes sub-function. + */ + freopen ( "SYS$OUTPUT:", "a", stdout ); + fib_desc[0] = 10; fib_desc[1] = (long int) &fib; +#ifdef __ALPHA + fib.fib$l_acctl = FIB$M_WRITE; +#else + fib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_WRITE; +#endif + status = sys$qiow ( 0, channel, IO$_ACCESS|IO$M_ACCESS, + &iosb, 0, 0, &fib_desc, 0, 0, 0, &atr, 0 ); + /* + * If status successful, update record byte and perform a MODIFY. + */ + if ( (status&1) == 1 ) status = iosb[0]; + if ( (status&1) == 1 ) { + rat.rtype = 1; /* fixed length records */ + rat.rsize = 512; /* 512 byte block recrods */ + rat.ratattrib = 0; /* Record attributes: none */ + + status = sys$qiow + ( 0, channel, IO$_MODIFY, &iosb, 0, 0, &fib_desc, 0, 0, 0, &atr, 0 ); + if ( (status&1) == 1 ) status = iosb[0]; + } + sys$dassgn ( channel ); + if ( (status & 1) == 0 ) fprintf ( stderr, + "Failed to convert output file to binary format, status: %d\n", status); + return status; +} + + +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 ltem_list **head; +struct ltem_list **tail; +int *count; +{ +int expcount = 0; +int context = 0; +int status; +int status_value; +int had_version; +$DESCRIPTOR(filespec, item); +$DESCRIPTOR(defaultspec, "SYS$DISK:[]*.*;"); +$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, ';'); + 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'; + /* + * 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 + fflush(stdout); /* Have to flush pipe for binary data to */ + /* terminate properly -- <tp@mccall.com> */ +#ifdef DEBUG + fprintf(stderr, " stdout flushed. . .\n"); +#endif + sys$qio(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0); +#ifdef DEBUG + fprintf(stderr, " Pipe terminated. . .\n"); +#endif + fclose(stdout); +#ifdef DEBUG + fprintf(stderr, " stdout closed. . .\n"); +#endif + sys$synch(0, child_st); + sys$dassgn(child_chan); + } +#ifdef DEBUG + fprintf(stderr, " sync done. . .\n"); +#endif +} + +#include <syidef> /* System Information Definitions */ + +static sig_child(chan) +int chan; +{ +#ifdef DEBUG + fprintf(stderr, "Child Completion AST, st: %x\n", child_st[0] ); +#endif + if (child_st[0] == 0) + { child_st[0] = 1; } + sys$setef ( 0 ); +} + +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], ef = 0; + $DESCRIPTOR(mbxdsc, mbxname); + short iosb[4]; + int status; + int pid; + struct + { + short dna_buflen; + short dna_itmcod; + char *dna_buffer; + 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; + 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, &ef, 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); +} + +/* got this off net.sources */ + +#ifdef VMS +#define index strchr +#endif /*VMS*/ + +/* + * get option letter from argument vector + */ +int opterr = 1, /* useless, never set or used */ + optind = 1, /* index into parent argv vector */ + optopt; /* character checked for validity */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define EMSG "" +#define tell(s) fputs(progname,stderr);fputs(s,stderr); \ + fputc(optopt,stderr);fputc('\n',stderr);return(BADCH); + +getopt(nargc,nargv,ostr) +int nargc; +char **nargv, + *ostr; +{ + static char *place = EMSG; /* option letter processing */ + register char *oli; /* option letter list index */ + char *index(); + char *progname; + + if(!*place) { /* update scanning pointer */ + if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF); + if (*place == '-') { /* found "--" */ + ++optind; + return(EOF); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) { + if(!*place) ++optind; +#ifdef VMS + progname = strrchr(nargv[0],']'); +#else + progname = rindex(nargv[0],'/'); +#endif + if (!progname) progname = nargv[0]; else progname++; + tell(": illegal option -- "); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) ++optind; + } + else { /* need an argument */ + if (*place) optarg = place; /* no white space */ + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; +#ifdef VMS + progname = strrchr(nargv[0],']'); +#else + progname = rindex(nargv[0],'/'); +#endif + if (!progname) progname = nargv[0]; else progname++; + tell(": option requires an argument -- "); + } + else optarg = nargv[optind]; /* white space */ + place = EMSG; + ++optind; + } + return(optopt); /* dump back option letter */ +} |