char *ckzv = "VMS file support, 1.0(010), 24 Jan 88"; char *ckzsys = " Vax/VMS"; /* C K V F I O -- Kermit file system support for VAX/VMS */ /* Stew Rubenstein, Harvard University Chemical Labs */ /* (c) 1985 President and Fellows of Harvard College */ /* Based on CKZUNX.C, 4.1(015) 28 Feb 85 */ /* Also, Martin Minow (MM), Digital Equipment Corporation, Maynard MA */ /* Also, Dan Schullman (DS), Digital Equipment Corporation, Maynard MA */ /* Adapted from ckufio.c, by... */ /* F. da Cruz (FdC), Columbia University Center for Computing Activities */ /* Edit history * 003 20-Mar-85 MM fixed fprintf bug in zsout.c * 004 21-Mar-84 MM create text files in variable-stream. * 005 8-May-85 MM filled in zkself (not tested), fixed other minor bugs * 006 5-Jul-85 DS handle version number in zltor, zrtol * 007 11-Jul-85 FdC fix zclose() to give return codes * 008 19-Mar-86 FdC Fix system() for "!", zopeni() for REMOTE commands. * 008 17-Sep-87 FdC Define PWDCMD. * 090 (???) * 010 24-Jan-88 FdC Add zgtdir() function, even tho it doesn't work... */ /* Definitions of some VMS system commands */ char *DIRCMD = "DIRECTORY "; /* For directory listing */ char *DELCMD = "DELETE "; /* For file deletion */ char *TYPCMD = "TYPE "; /* For typing a file */ char *SPACMD = "DIRECTORY/TOTAL"; /* Space/quota of current directory */ char *SPACM2 = "DIRECTORY/TOTAL "; /* Space/quota of current directory */ char *WHOCMD = "SHOW USERS"; /* For seeing who's logged in */ char *PWDCMD = "SHOW DEFAULT"; /* For seeing current directory */ /* Functions (n is one of the predefined file numbers from ckermi.h): zopeni(n,name) -- Opens an existing file for input. zopeno(n,name) -- Opens a new file for output. zclose(n) -- Closes a file. zchin(n) -- Gets the next character from an input file. zsout(n,s) -- Write a null-terminated string to output file, buffered. zsoutl(n,s) -- Like zsout, but appends a line terminator. zsoutx(n,s,x) -- Write x characters to output file, unbuffered. zchout(n,c) -- Add a character to an output file, unbuffered. zchki(name) -- Check if named file exists and is readable, return size. zchko(name) -- Check if named file can be created. znewn(name,s) -- Make a new unique file name based on the given name. zdelet(name) -- Delete the named file. zxpand(string) -- Expands the given wildcard string into a list of files. znext(string) -- Returns the next file from the list in "string". zxcmd(cmd) -- Execute the command in a lower fork. zclosf() -- Close input file associated with zxcmd()'s lower fork. zrtol(n1,n2) -- Convert remote filename into local form. zltor(n1,n2) -- Convert local filename into remote form. zchdir(dirnam) -- Change working directory. zhome() -- Return pointer to home directory name string. zkself() -- Log self out */ /* Includes */ #include "ckcker.h" #include "ckcdeb.h" #include #include #include #include #include #include #include #define MAXWLD 500 /* Maximum wildcard filenames */ /* Declarations */ FILE *fp[ZNFILS] = { /* File pointers */ NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static int fcount; /* Number of files in wild group */ char *getenv(), *strcpy(); /* For finding home directory */ static char *mtchs[MAXWLD], /* Matches found for filename */ **mtchptr; /* Pointer to current match */ /*** Z K S E L F -- Log self out ***/ /*** (someone please check if this works in VMS) ***/ zkself() { return (kill(0,9)); } /* Z O P E N I -- Open an existing file for input. */ zopeni(n,name) int n; char *name; { debug(F111," zopeni",name,n); debug(F101," fp","",(int) fp[n]); if (n == ZSYSFN) { /* Input from a system function? */ return(zxcmd(name)); /* Try to fork the command */ } if (n == ZSTDIO) { /* Standard input? */ if (isatty(0)) { fprintf(stderr,"?Terminal input not allowed\n"); debug(F110,"zopeni: attempts input from unredirected stdin","",0); return(0); } fp[ZIFILE] = stdin; return(1); } if (chkfn(n) != 0) return(0); fp[n] = fopen(name,"r"); /* Real file. */ debug(F111," zopeni", name, (int) fp[n]); if (fp[n] == NULL) perror(name); /* +1, want a useful message */ return((fp[n] != NULL) ? 1 : 0); } /* Z O P E N O -- Open a new file for output. */ zopeno(n,name) int n; char *name; { int fildes; extern int binary; debug(F111," zopeno",name,n); if (chkfn(n) != 0) return(0); if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */ fp[ZOFILE] = stdout; debug(F101," fp[]=stdout", "", (int) fp[n]); return(1); } /* * Create "binary" output files as fixed-block 512 byte records. * This should permit copying task images. It is rumored that * Vax C will null-fill an incomplete final block. * * Create all debugging files (and normal output files) in * "vanilla" RMS -- variable length, implicit carriage control. * This way, old brain-damaged programs aren't suprised by * bizarre Unix-styled files. */ if (n == ZOFILE && binary != 0) fildes = creat(name, 0, "mrs=512", "rfm=fix"); else fildes = creat(name, 0, "rat=cr", "rfm=var"); fp[n] = (fildes == -1) ? NULL : fdopen(fildes, "w"); if (fp[n] == NULL) perror(name); /* +1, print useful msg */ if (n == ZDFILE && isatty(fileno(fp[n]))) setbuf(fp[n],NULL); /* Make debugging file unbuffered */ debug(F101, " fp[n]", "", (int) fp[n]); return((fp[n] != NULL) ? 1 : 0); } /* Z C L O S E -- Close the given file. */ /* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */ zclose(n) int n; { int x; if (chkfn(n) < 1) return(0); if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]); fp[n] = NULL; return((x == EOF) ? -1 : 1); } /* Z C H I N -- Get a character from the input file. */ static int subprocess_input = 0, sub_count; static int input_mbxchn, output_mbxchn, child_pid = 0; static char *sub_ptr, sub_buf[200]; get_subprc_line() { struct { short status, size, trm, trmsize; } subiosb; if ((SYS$QIOW(0, output_mbxchn, IO$_READVBLK, &subiosb, 0, 0, sub_buf, sizeof sub_buf, 0, 0, 0, 0) & 7) != 1 || (subiosb.status & 7) != 1) return(-1); if (subiosb.size == 29 && strncmp(sub_buf, ">>> END OF KERMIT COMMAND <<<", subiosb.size) == 0) { subprocess_input = 0; return(-1); } sub_buf[subiosb.size] = '\n'; sub_buf[subiosb.size + 1] = '\0'; sub_count = subiosb.size; sub_ptr = sub_buf; return(0); } zchin(n,c) int n; char *c; { int a; if (n == ZIFILE && subprocess_input) { if (--sub_count < 0) if (get_subprc_line()) return(-1); a = *sub_ptr++; } else { if (chkfn(n) < 1) return(-1); a = getc(fp[n]); } if (a == EOF) return(-1); *c = (a & 0377); return(0); } /* Z S O U T -- Write a string to the given file, buffered. */ zsout(n,s) int n; char *s; { if (chkfn(n) < 1) return(-1); fputs(s, fp[n]); /* Don't use fprintf here MM */ return(0); } /* Z S O U T L -- Write string to file, with line terminator, buffered */ zsoutl(n,s) int n; char *s; { if (chkfn(n) < 1) return(-1); fputs(s, fp[n]); /* Don't use fprintf MM */ putc('\n', fp[n]); return(0); } /* Z S O U T X -- Write x characters to file, unbuffered. */ zsoutx(n,s,x) int n, x; char *s; { if (chkfn(n) < 1) return(-1); return(write(fp[n]->_file,s,x)); } /* Z C H O U T -- Add a character to the given file. */ zchout(n,c) int n; char c; { if (chkfn(n) < 1) return(-1); if (n == ZSFILE) return(write(fp[n]->_file,&c,1)); /* Use unbuffered for session log */ else { if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */ return(ferror(fp[n])); /* Check to make sure */ else /* Otherwise... */ return(0); /* There was no error. */ } } /* C H K F N -- Internal function to verify file number is ok */ /* Returns: -1: File number n is out of range 0: n is in range, but file is not open 1: n in range and file is open */ chkfn(n) int n; { switch (n) { case ZCTERM: case ZSTDIO: case ZIFILE: case ZOFILE: case ZDFILE: case ZTFILE: case ZPFILE: case ZSFILE: break; default: debug(F101,"chkfn: file number out of range","",n); fprintf(stderr,"?File number out of range - %d\n",n); return(-1); } return( (fp[n] == NULL) ? 0 : 1 ); } /* Z C H K I -- Check if input file exists and is readable */ /* Returns: >= 0 if the file can be read (returns the size). -1 if file doesn't exist or can't be accessed, -2 if file exists but is not readable (e.g. a directory file). -3 if file exists but protected against read access. */ /* For Berkeley Unix, a file must be of type "regular" to be readable. Directory files, special files, and symbolic links are not readable. */ long zchki(name) char *name; { int x; long pos; x = open(name, 0); if (x < 0) { debug(F111,"zchki stat fails",name,errno); return(-1); } pos = lseek(x, 0, 2); close(x); return(pos); } /* Z C H K O -- Check if output file can be created */ /* Returns -1 if write permission for the file would be denied, 0 otherwise. */ zchko(name) char *name; { return(0); /* Always creates new version */ } /* Z D E L E T -- Delete the named file. */ zdelet(name) char *name; { delete(name); } /* Z R T O L -- Convert remote filename into local form */ /* For VMS, we eliminate all special characters and truncate. */ /* Doesn't allow the longer filespecs that VMS V4 supports. */ /* Assumes version number delimited by semicolon, not period. */ /* Should really use RMS to parse filespec components. -- DS */ zrtol(name,name2) char *name, *name2; { int count; char *cp; count = 9; for ( cp = name2; *name != '\0'; name++ ) { switch (*name) { case '.': /* File type */ count = 3; /* Max length for this field */ *cp++ = '.'; break; case ';': /* Version */ count = 5; *cp++ = ';'; break; default: if (count > 0 && isalnum(*name)) { --count; *cp++ = islower(*name) ? toupper(*name) : *name; } break; } } *cp = '\0'; /* End of name */ debug(F110,"zrtol: ",name2,0); } /* Z L T O R -- Convert filename from local format to common form. */ zltor(name,name2) char *name, *name2; { char *cp, *pp; for (cp = pp = name; *cp != '\0'; cp++) { /* strip path name */ if (*cp == ']' || *cp == ':') { pp = cp; pp++; } } for ( ; --cp >= pp; ) { /* From end to beginning */ if (!isdigit(*cp)) { /* if not numeric, then */ if (*cp == '-') --cp; /* if minus sign, skip over, or */ if (*cp == ';') *cp = '\0'; /* if version delim, make end */ break; } } cp = name2; /* If nothing before dot, */ if (*pp == '.') *cp++ = 'X'; /* insert 'X' */ strcpy(cp,pp); debug(F110,"zltor: ",name2,0); } /* Z C H D I R -- Change directory */ zchdir(dirnam) char *dirnam; { char *hd; if (*dirnam == '\0') hd = getenv("HOME"); else hd = dirnam; return((chdir(hd) == 0) ? 1 : 0); } /* Z H O M E -- Return pointer to user's home directory */ char * zhome() { return(getenv("HOME")); } /* Z G T D I R -- Return pointer to user's current directory */ char * zgtdir() { /* char *getcwd(); /* char cwdbuf[100]; /* char *buf; /* buf = cwdbuf; /* return(getcwd(buf,100)); */ return(""); /* Can't seem to make LINK find getcwd()... */ } /* Z X C M D -- Run a system command so its output can be read like a file */ zxcmd(comand) char *comand; { char input_mbxnam[10], output_mbxnam[10]; char cmdbuf[200]; if (child_pid == 0) { struct dsc$descriptor_s inpdsc, outdsc; struct { short buflen, code; char *bufadr; short *retlen; } itmlst[2]; SYS$CREMBX(0, &input_mbxchn, 0, 0, 0, 0, 0); itmlst[0].buflen = sizeof input_mbxnam; itmlst[0].code = DVI$_DEVNAM; itmlst[0].bufadr = input_mbxnam; itmlst[0].retlen = 0; itmlst[1].buflen = 0; itmlst[1].code = 0; SYS$GETDVI(0, input_mbxchn, 0, itmlst, 0, 0, 0, 0); SYS$WAITFR(0); SYS$CREMBX(0, &output_mbxchn, 0, 0, 0, 0, 0); itmlst[0].buflen = sizeof output_mbxnam; itmlst[0].bufadr = output_mbxnam; SYS$GETDVI(0, output_mbxchn, 0, itmlst, 0, 0, 0, 0); SYS$WAITFR(0); inpdsc.dsc$w_length = strlen(input_mbxnam); inpdsc.dsc$b_dtype = DSC$K_DTYPE_T; inpdsc.dsc$b_class = DSC$K_CLASS_S; inpdsc.dsc$a_pointer = input_mbxnam; outdsc.dsc$w_length = strlen(output_mbxnam); outdsc.dsc$b_dtype = DSC$K_DTYPE_T; outdsc.dsc$b_class = DSC$K_CLASS_S; outdsc.dsc$a_pointer = output_mbxnam; LIB$SPAWN(0, &inpdsc, &outdsc, &1, 0, &child_pid); SYS$QIOW(0, input_mbxchn, IO$_WRITEVBLK | IO$M_NOW, 0, 0, 0, "$ SET NOON", 10, 0, 0, 0, 0); } strcpy(cmdbuf, "$ "); strcat(cmdbuf, comand); SYS$QIOW(0, input_mbxchn, IO$_WRITEVBLK | IO$M_NOW, 0, 0, 0, cmdbuf, strlen(cmdbuf), 0, 0, 0, 0); SYS$QIOW(0, input_mbxchn, IO$_WRITEVBLK | IO$M_NOW, 0, 0, 0, "$ WRITE SYS$OUTPUT \">>> END OF KERMIT COMMAND <<<\"", 50, 0, 0, 0, 0); subprocess_input = 1; sub_count = 0; return(1); } /* Z C L O S F - close the suprocess output file. */ zclosf() { } /* Z K I L L F - kill the subprocess used for host commands */ /* The return value is 1 if the subprocess was killed successfully. */ /* -1 if there was no subprocess to kill. */ zkillf() { if (child_pid == 0) return(-1); return((SYS$DELPRC(&child_pid) & 7) == 1); } /* Z X P A N D -- Expand a wildcard string into an array of strings */ /* Returns the number of files that match fn1, with data structures set up so that first file (if any) will be returned by the next znext() call. */ zxpand(fn) char *fn; { fcount = fgen(fn,mtchs,MAXWLD); /* Look up the file. */ if (fcount > 0) { mtchptr = mtchs; /* Save pointer for next. */ } debug(F111,"zxpand",mtchs[0],fcount); return(fcount); } /* Z N E X T -- Get name of next file from list created by zxpand(). */ /* Returns >0 if there's another file, with its name copied into the arg string, or 0 if no more files in list. */ znext(fn) char *fn; { if (fcount-- > 0) strcpy(fn,*mtchptr++); else *fn = '\0'; debug(F111,"znext",fn,fcount+1); return(fcount+1); } /* Z N E W N -- Make a new name for the given file */ znewn(fn,s) char *fn, **s; { static char buf[100]; char *bp, *xp; int len = 0, n = 0, d = 0, t; strcpy(buf, fn); /* Version numbers are handled by OS */ *s = buf; } /* Wildcard expansion for VMS is easy; we just use a run-time library call. */ fgen(pat,resarry,len) char *pat,*resarry[]; int len; { struct dsc$descriptor_s file_spec, result, deflt; long context; int count, slen, status, plen; char *pp, *rp, result_string[256], *strchr(); file_spec.dsc$w_length = strlen(pat); file_spec.dsc$b_dtype = DSC$K_DTYPE_T; file_spec.dsc$b_class = DSC$K_CLASS_S; file_spec.dsc$a_pointer = pat; result.dsc$w_length = sizeof result_string; result.dsc$b_dtype = DSC$K_DTYPE_T; result.dsc$b_class = DSC$K_CLASS_S; result.dsc$a_pointer = result_string; deflt.dsc$w_length = 3; deflt.dsc$b_dtype = DSC$K_DTYPE_T; deflt.dsc$b_class = DSC$K_CLASS_S; deflt.dsc$a_pointer = "*.*"; count = 0; context = 0; pp = strchr(pat, ']'); if (pp == 0) pp = strchr(pat, ':'); if (pp == 0) plen = 0; else plen = pp - pat + 1; while (count < len && (status = LIB$FIND_FILE(&file_spec, &result, &context, &deflt)) == RMS$_NORMAL) { rp = strchr(result_string, ']') + 1; slen = strchr(rp, ' ') - rp; resarry[count] = malloc(slen + plen + 1); if (plen != 0) strncpy(resarry[count], pat, plen); strncpy(resarry[count] + plen, rp, slen); resarry[count][slen + plen] = '\0'; ++count; } #ifdef DVI$_ALT_HOST_TYPE lib$find_file_end(&context); /* Only on V4 and later */ #endif if (status == RMS$_FNF) return(0); if (status == RMS$_NMF) return(count); return(-1); } system(s) char *s; { struct dsc$descriptor_s cmd; if ( *s ) { zxcmd(s); while (!get_subprc_line()) fputs(sub_buf, stdout); putchar('\n'); } else { LIB$SPAWN(); } }