char *ckzv = "Data General file support, 5A(065) 12 May 93"; /* C K D F I O -- Kermit file system support for AOS/VS & derivatives */ /* Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET), Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 1993, Trustees of Columbia University in the City of New York. The C-Kermit software may not be, in whole or in part, licensed or sold for profit as a software product itself, nor may it be included in or distributed with commercial products or otherwise distributed by commercial concerns to their clients or customers without written permission of the Office of Kermit Development and Distribution, Columbia University. This copyright notice must not be removed, altered, or obscured. Adapted to Data General computers in 1985 by: Phil Julian, SAS Institute, Inc., Box 8000, Cary, NC 27512-8000, with additional help: For using sys_gnfn() in fgen(), instead of my kludge: Victor Johansen, Micro_rel, 2343 W 10th Place, Tempe AZ 85281 For using dg_open() for the pipe in zxcmd(), and also for using sys_proc() in start_cli(): Richard Lamb, MIT, 77 Mass. Ave., Room 35-437, Cambridge MA 02139 Adapted to C-Kermit 5A by Eugenia Harris (ENH) at Data General, who thanks Larry McCoskery, also at Data General, for many library routines and much debugging assistance. */ /* Support for DG/UX and other non-AOS/VS implementations has been removed -- only exception is MV/UX which has been left in but has not been tested and probably doesn't work. DG/UX Kermit can be built with the cku* modules from Columbia University. --ENH */ /* Includes */ #nolist #include "ckcdeb.h" /* Typedefs, debug formats, etc */ #include "ckcker.h" /* Kermit definitions */ #include /* Character types */ #include /* Standard i/o */ #include #include #include #include #include #include #include #include /* Used for ?GNFN */ #include #include #include /* Directory structure */ #include /* File status */ #list /* The DG compiler version 3.21 has a bug in the get*id() functions, which * cause the program to go into an infinite loop. These functions should * return -1 unless the system is in a UNIX environment, so I made the * appropriate kludges. -- Phil Julian, 8 April 87 */ #define getgid() -1 #define getuid() -1 #define geteuid() -1 #define MAXNAMLEN ($MXFN-1) #define MAXPATH ($MXPL-1) /* both from system PARU.32 */ #define DIRSEP ':' #define ISDIRSEP(c) (((c)==':') || ((c)=='^') || ((c)=='=')) #define TIMESTAMP #define MAXWLD 500 #define fork() vfork() char *ckzsys = " Data General AOS/VS"; /* Definitions of system commands for AOS/VS */ char *DIRCMD = "filestatus/sort/assortment "; /* For directory listing */ char *DIRCM2 = "filestatus/sort/assortment"; /* For full directory list */ char *DELCMD = "delete/v "; /* For file deletion */ char *TYPCMD = "type "; /* For typing a file */ char *PWDCMD = "directory"; /* For saying where I am */ /* SPACMD now returns space in current directory rather than home dir. */ char *SPACMD = "space ="; /* For space in current directory */ char *SPACM2 = "space "; /* For space in specified directory */ char *WHOCMD = "who [!sons op:exec]"; /* 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,&c) -- 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. zchkspa(name,n) -- Check if n bytes available for file 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() -- Kill self, log out own job. zsattr(struct zattr *) -- Return attributes for file which is being sent. zstime(f, struct zattr *, x) - Set file creation date from attribute packet. zrename(old, new) -- Rename a file. */ /* Some systems define these in include files, others don't... */ #ifndef R_OK #define R_OK 4 /* For access */ #endif /* ifndef R_OK */ #ifndef W_OK #define W_OK 2 #endif /* ifndef W_OK */ #ifdef UXIII #include #define MAXNAMLEN DIRSIZ #endif #ifndef O_RDONLY #define O_RDONLY 000 #endif /* ifndef O_RDONLY */ /* Declarations */ int wildcarlb; /* Wild card ^ or # */ int maxnam = MAXNAMLEN; /* Available to the outside */ int maxpath = MAXPATH; FILE *fp[ZNFILS] = { /* File pointers */ NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* Buffers and pointers used in buffered file input */ #ifdef DYNAMIC extern char *zinbuffer, *zoutbuffer; #else extern char zinbuffer[], zoutbuffer[]; #endif /* ifdef DYNAMIC */ extern char *zinptr, *zoutptr; extern int zincnt, zoutcnt; extern int wildxpand; static long iflen = -1L; /* Input file length */ static PID_T pid; /* pid of child fork */ #ifdef SASMOD /* For remote Kermit command */ static int savout, saverr; /* saved stdout and stderr streams */ #endif /* ifdef SASMOD */ static int fcount; /* Number of files in wild group */ static char nambuf[MAXNAMLEN+1]; /* Buffer for a filename */ char *malloc(), *getenv(), *strcpy(); /* System functions */ extern errno; /* System error code */ char *mtchs[MAXWLD]; /* Matches found for filename */ static char **mtchptr; /* Pointer to current match */ static char zmbuf[200]; /* Z K S E L F -- Kill Self: log out own job, if possible. */ zkself() { /* For "bye", but no guarantee! */ /* sys_term works better than kill() on the DG, but does not log off. */ char *msg ="bye "; int ac2; ac2 = (int) msg; return(sys_term(getpid(),0,ac2)); } /* 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 (chkfn(n) != 0) return(0); if (n == ZSYSFN) { /* Input from a system function? */ debug(F110," invoking zxcmd",name,0); return(zxcmd(name)); /* Try to fork the command */ } if (n == ZSTDIO) { /* Standard input? */ if (isatty(0)) { ermsg("Terminal input not allowed"); debug(F110,"zopeni: attempts input from unredirected stdin","",0); return(0); } fp[ZIFILE] = stdin; return(1); } fp[n] = fopen(name,"r"); /* Real file. */ debug(F111," zopeni", name, (int) fp[n]); if (fp[n] == NULL) perror("zopeni"); return((fp[n] != NULL) ? 1 : 0); } /* Z O P E N O -- Open a new file for output. */ zopeno(n,name,zz,fcb) int n; char *name; struct zattr *zz; struct filinfo *fcb; { char p[3]; char myname[] = "zopeno()"; long validate(); debug(F111," zopeno",name,n); if (fcb) { debug(F101,"zopeno fcb disp","",fcb->dsp); debug(F101,"zopeno fcb type","",fcb->typ); debug(F101,"zopeno fcb char","",fcb->cs); } else { debug(F100,"zopeno fcb is NULL","",0); } if (n != ZDFILE) debug(F111," zopeno",name,n); if (chkfn(n) != 0) return(0); if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */ fp[ZOFILE] = stdout; if (n != ZDFILE) debug(F101," fp[]=stdout", "",fp[n]); zoutcnt = 0; zoutptr = zoutbuffer; return(1); } /* ENH - in VS, the creation time can only be set on the */ /* create, so if the file exists and a creation date is specified */ /* we will delete the existing file and create the new with requested */ /* creation date. If no date is given, or it's invalid, we will */ /* not bother creating the file, as it will be created implicitly */ /* on the open. Note that we ignore errors from the routine to */ /* set the creation date (and create the file), the assumption */ /* being that if it fails, the file is not created, but there's */ /* still a possibility that it might be created on the open. If */ /* not, the error will be caught there. Note that this whole pro- */ /* cedure is only invoked when the file collsion action is "update". */ /* That is the only time we will get to this point and find that a */ /* file of the same name already exists. */ strcpy(p,"w"); if (fcb) { /* If called with an FCB... */ if (fcb->dsp == XYFZ_A) { /* Does it say Append? */ debug(F101,"zopeno opening for append access",name,0); strcpy(p,"a"); /* If so, open for append access */ } else if (file_exists(name) != 0) { /* If it exists, delete it */ if (zdelet(name) != 0) /* and create w/incoming date */ debug(F101,"zopeno error deleting existing file",name,0); else debug(F101,"zopeno existing file deleted",name,0); if (zz->date.len > 0) { /* if creation date specified */ debug(F101,"zopeno creating file with incoming date", name,0); set_creation_date(name,zz->date.val); /* set it */ strcpy(p,"r+"); } } } fp[n] = fopen(name,p); /* A real file, try to open */ if (fp[n] == NULL) { perror("zopeno can't open"); } else { if (n == ZDFILE) setbuf(fp[n],NULL); /* Debugging file unbuffered */ } zoutcnt = 0; /* (PWP) reset output buffer */ zoutptr = zoutbuffer; if (n != ZDFILE) 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. */ int zclose(n) int n; { int x, x2; if (chkfn(n) < 1) return(0); /* Check range of n */ if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */ x2 = zoutdump(); else x2 = 0; x = 0; if (fp[ZSYSFN]) { /* If system function */ x = zclosf(n); /* do it specially */ } else { if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]); fp[n] = NULL; } iflen = -1L; /* invalidate file length */ if (x == EOF) return (-1); else if (x2 < 0) return (-1); else return (1); } /* Z C H I N -- Get a character from the input file. */ /* Returns -1 if EOF, 0 otherwise with character returned in argument */ int zchin(n,c) int n; int *c; { int a, x; /* (PWP) Just in case this gets called when it shouldn't. */ if (n == ZIFILE) { x = zminchar(); *c = x; return(x); } /* if (chkfn(n) < 1) return(-1); */ a = getc(fp[n]); if (a == EOF) return(-1); *c = (CHAR) a & 0377; return(0); } /* Z S I N L -- Reads a line from file number n */ /* Reads line from file number n and writes it to address provided by caller. Writing terminates when a newline is read, but the newline is discarded. Writing also terminates on EOF or if length x is exhausted. Returns 0 on success and -1 on EOF */ int zsinl(n,s,x) int n, x; char *s; { int a, z = 0; /* lifted verbatim from ckufio.c */ if (chkfn(n) < 1) { /* Make sure file is open */ return(-1); } a = -1; while (x--) { #ifndef NLCHAR int old; old = a; /* Previous character */ #endif if (zchin(n,&a) < 0) { /* Read a character from the file */ z = -1; break; } #ifdef NLCHAR if (a == (char) NLCHAR) break; /* Single-character line terminator */ #else if (a == '\r') continue; /* CRLF line terminator */ if (old == '\r') { if (a == '\n') break; else *s++ = '\r'; } #endif /* NLCHAR */ *s = a; s++; } *s = '\0'; return(z); } /* Z I N F I L L -- Reads characters from ZIFILE when sending files. */ /* * Suggestion: if fread() returns 0, call ferror to find out what the * problem was. If it was not EOF, then return -2 instead of -1. * Upper layers (getpkt function in ckcfns.c) should set cxseen flag * if it gets -2 return from zminchar macro. */ int zinfill() { int x; errno = 0; zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]); #ifdef COMMENT debug(F101,"zinfill fp","",fp[ZIFILE]); debug(F101,"zinfill zincnt","",zincnt); #endif if (zincnt == 0) { #ifdef ferror x = ferror(fp[ZIFILE]); debug(F101,"zinfill errno","",errno); debug(F101,"zinfill ferror","",x); if (x) return(-2); #endif /* ferror */ x = feof(fp[ZIFILE]); debug(F101,"zinfill errno","",errno); debug(F101,"zinfill feof","",x); if (!x && ferror(fp[ZIFILE])) return(-2); return(-1); } zinptr = zinbuffer; /* set ptr to beginning, (== &zinbuffer[0]) */ zincnt--; /* one less char in buffer */ return((int)(*zinptr++) & 0377); /* because we return the first */ } /* Z S O U T -- Write a string to the given file, buffered. */ int zsout(n,s) int n; char *s; { if (chkfn(n) < 1) return(-1); /* Keep this here, prevents memory faults */ #ifdef COMMENT while (*s) { /* (unbuffered for debugging) */ write(fileno(fp[n]),s,1); ++s; } return(fputs(s,fp[n]) == EOF ? -1 : 0); #else if (n == ZSFILE) return(write(fileno(fp[n]),s,(int)strlen(s))); else return((fputs(s,fp[n]) == EOF) ? -1 : 0); #endif } /* Z S O U T L -- Write string to file, with line terminator, buffered */ int zsoutl(n,s) int n; char *s; { /* if (chkfn(n) < 1) return(-1); */ if (fputs(s,fp[n]) == EOF) return(-1); fputs("\n",fp[n]); return(0); } /* Z S O U T X -- Write x characters to file, unbuffered. */ int zsoutx(n,s,x) int n, x; char *s; { #ifdef COMMENT if (chkfn(n) < 1) return(-1); return(write(fp[n]->_file,s,x)); #endif return(write(fileno(fp[n]),s,x)); } /* Z C H O U T -- Add a character to the given file. */ /* Should return 0 or greater on success, -1 on failure (e.g. disk full) */ #ifdef CK_ANSIC zchout (register int n, char c) #else zchout(n,c) register int n; char c; #endif /* CK_ANSIC */ /* zchout() */ { /* if (chkfn(n) < 1) return(-1); */ if (n == ZSFILE) return(write(fileno(fp[n]),&c,1)); /* Use unbuffered for session log */ else { /* Buffered for everything else */ if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */ return(ferror(fp[n])?-1:0); /* Check to make sure */ else /* Otherwise... */ return(0); /* There was no error. */ } } zoutdump() { int x; zoutptr = zoutbuffer; /* Reset buffer pointer in all cases */ debug(F101,"zoutdump chars","",zoutcnt); if (zoutcnt == 0) { /* Nothing to output */ return(0); } else if (zoutcnt < 0) { /* Unexpected negative argument */ zoutcnt = 0; /* Reset output buffer count */ return(-1); /* and fail. */ } /* Frank Prindle suggested that replacing this fwrite() by an fflush() */ /* followed by a write() would improve the efficiency, especially when */ /* writing to stdout. Subsequent tests showed a 5-fold improvement! */ /* if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) { */ fflush(fp[ZOFILE]); if ((x = write(fileno(fp[ZOFILE]),zoutbuffer,zoutcnt)) == zoutcnt) { debug(F101,"zoutdump write ok","",zoutcnt); zoutcnt = 0; /* Reset output buffer count */ return(0); /* write() worked OK */ } else { debug(F101,"zoutdump write error","",errno); debug(F101,"zoutdump write returns","",x); zoutcnt = 0; /* Reset output buffer count */ return(-1); /* write() failed */ } } /* 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: case ZSYSFN: case ZRFILE: case ZWFILE: 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; { /* For some reason, the DG croaks intermittently with a call to stat(), * and goes into an infinite loop. This routine needs to see if the * file is okay to look at and access, so I will just call ?fstat to * do that. The only files that cannot be accessed by normal i/o routines * are directories. Other files are fair game. */ int x; long y; P_FSTAT buf; /* struct stat buf; */ int ac0,ac2; char * temp; /* ENH - in case we have to prepend an = */ /* because ?fstat looks in searchlist */ temp = alloc (strlen(name)+2); if ((*name != '^') && (*name != '@') && (*name != ':') && (*name != '=')) { strcpy (temp+1,name); *temp = '='; } else strcpy (temp,name); ac0 = (int) temp; ac2 = (int) &buf; x = sys_fstat(ac0,0,ac2); free(temp); if (x != 0) { debug(F111,"zchki sys_fstat fails",name,ac0); return(-1); } x = buf.styp_type; /* Isolate file format field */ iflen = buf.sefm; if ((x >= $LDIR) && (x <= $HDIR)) { debug(F111,"zchki skipping DIR type:",name,x); return(-2); } debug(F111,"zchki stat ok:",name,x); if ((x = access(name,R_OK)) < 0) { /* Is the file accessible? */ debug(F111," access failed:",name,x); /* No */ return(-3); } else { y = buf.sefm; debug(F111," access ok:",name,(int) y); /* Yes */ return( (y > -1) ? y : 0 ); } } /* 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; { int i, x; char s[50], *sp; sp = s; /* Make a copy, get length */ x = 0; while ((*sp++ = *name++) != '\0') x++; if (x == 0) return(-1); /* If no filename, fail. */ debug(F101," length","",x); for (i = x; i > 0; i--) /* Strip filename. */ if (ISDIRSEP(s[i-1])) break; debug(F101," i","",i); if (i == 0) /* If no path, use current directory */ strcpy(s,"="); else /* Otherwise, use given one. */ s[i] = '\0'; x = access(s,W_OK); /* Check access of path. */ if (x < 0) { debug(F111,"zchko access failed:",s,errno); return(-1); } else { debug(F111,"zchko access ok:",s,x); return(0); } } /* Z D E L E T -- Delete the named file. */ zdelet(name) char *name; { if (*name == '\0') { debug(F111,"zdelet passed null filename","",0); return(-1); } return(unlink(name)); } /* Z R T O L -- Convert remote filename into local form */ /* For AOS/VS, we don't care about case -- we only care about certain chars */ zrtol(name,name2) char *name, *name2; { char *p; int flag; if (!name || !name2) return; debug(F110,"zrtol original name ",name,0); p = name2; /* Output pointer to new name */ for ( ; *name != '\0'; name++) { if (*name > ' ') flag = 1; /* Strip leading blanks and controls */ if (flag == 0 && *name < '!') continue; if (*name == '-') *p = '_'; /* Change dash to underscore */ else if (*name == '^' || /* Change template characters to 'X' */ *name == '*' || *name == '+' || *name == '#' || *name == '\\' ) *p = 'X'; else *p = *name; /* Other characters are just copied */ p++; } *p-- = '\0'; /* Terminate */ while (*p < '!' && p > name2) /* Strip trailing blanks & controls */ *p-- = '\0'; if (*name2 == '\0') strcpy(name2,"NONAME"); debug(F110,"zrtol translated name ",name2,0); /* Done */ } /* Z S T R I P -- Strip device & directory name from file specification */ /* Strip pathname from filename "name", return pointer to result in name2 */ static char work[257]; /* buffer for use by zstrip and zltor */ VOID zstrip(name,name2) char *name, **name2; { char *cp, *pp, *p2; debug(F110,"zstrip before",name,0); pp = work; for (cp = name; *cp != '\0'; cp++) { if (ISDIRSEP(*cp)) pp = work; else *pp++ = *cp; } *pp = '\0'; /* Terminate the string */ *name2 = work; debug(F110,"zstrip after",*name2,0); } /* Z L T O R -- Local TO Remote */ /* Convert filename from local format to common (remote) form. */ VOID zltor(name,name2) char *name, *name2; { char work[100], *cp, *pp; int dc = 0; debug(F110,"zltor",name,0); pp = work; /* Strip off the DG directory prefix */ for (cp=name; *cp != '\0'; cp++) { if (ISDIRSEP(*cp)) { dc = 0; pp = work; } else if (islower(*cp)) *pp++ = toupper(*cp); /* Uppercase letters */ else if (*cp == '~') *pp++ = 'X'; /* Change tilde to 'X' */ else if (*cp == '#') *pp++ = 'X'; /* Change number sign to 'X' */ else if (*cp == '$') *pp++ = 'X'; /* Change dollar sign to 'X' */ else if (*cp == '?') *pp++ = 'X'; /* Change question mark to 'X' */ else if ((*cp == '.') && (++dc > 1)) *pp++ = 'X'; /* & extra dots */ else *pp++ = *cp; } *pp = '\0'; /* Tie it off. */ cp = name2; /* If nothing before dot, */ if (*work == '.') *cp++ = 'X'; /* insert 'X' */ strcpy(cp,work); debug(F110," name2",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() { char *result,*ptr; static char *buf; static int homeflag = 0; if (homeflag) return (buf); /* if homeflag, result already */ result = (getenv("HOME")); /* in buf - don't do it again */ if (result != NULL) { buf = malloc (strlen (result) + 2); /* 1 for null, 1 for final : */ if (buf == NULL) { debug(F111,"zhome can't allocate memory","",0); return(NULL); } for (ptr = buf ; *result != '\0'; ptr++,result++) if (*result == '/') *ptr = DIRSEP; else *ptr = toupper(*result); *ptr++ = DIRSEP; *ptr = '\0'; homeflag = 1; } return(buf); } /* Z G T D I R -- Return pointer to user's current directory */ #ifdef MAXPATH #define CWDBL MAXPATH #else #define CWDBL 100 #endif static char cwdbuf[CWDBL+1]; char * zgtdir() { char *buf; int error; buf = cwdbuf; if ((error = get_dir(buf)) != 0) { debug(F111,"zgtdir can't get dir name, VS error","",error); return(NULL); } else return(buf); } /********************************************************************* P I P E _ C A L L Split off the arguments from a command to the system, and pass the command and arguments to the system routine. Return the name of the temp file which stored the results of the command. This routine assumes that the "l=" option is available on all commands, which is true of AOS/VS CLI. Other DG systems may need another method, if the l= option is not available. Returns 0 if the system() function succeeds (>1 status) 1 if the system() function fails. I would have deleted this routine, except that I use it in zxpand() if a # is used as a wild card character. I used to depend on this routine for zxcmd() as well as zxpand(), but I now use sys_proc and sys_gnfn() respectively. *********************************************************************/ int pipe_call(command,output_fname) char *command,output_fname[]; { /* Look for the first semi-colon, if any, and strip up to that point, plus any blanks following it. Because the system() command refers to the initial working directory, a "dir xxx;" is usually pre-pended. */ char *arguments, syscommand[512], locommand[512]; char *insert,*fname,*cp,*tempname; FILE *dummy; strcpy(locommand,command); /* Find the first ;, if any */ for (insert=locommand; ((*insert) && (*insert!=';')); insert++); if (*insert) arguments = insert+1; else arguments = locommand; /* No ; found */ /* Skip leading spaces */ for (; ((*arguments) && isspace(*arguments)); arguments++); /* Find the end of the command to CLI */ for (; ((*arguments) && (*arguments!=',') && !isspace(*arguments)); arguments++); /* If *arguments is NULL, there are no parameters to the command. Otherwise, null-terminate command and address the arguments */ if (*arguments) *arguments++ = '\0'; /* create the @OUTPUT file name -- a unique file name */ fname = getenv("HOME"); for (cp=fname; *cp; cp++) if (*cp == '/') *cp=':'; strcpy(syscommand,fname); strcat(syscommand,":?kermit_pipe_123456"); fname = &syscommand[0]; mktemp(fname); strcpy(output_fname,fname); /* Create the file to establish its file type first */ close(dummy=open(output_fname,O_WRONLY)); /* Append an L= (listing =) switch. All CLI commands have this. */ sprintf(syscommand,"%s/l=%s%s",locommand,output_fname,arguments); debug(F101,"syscommand",syscommand,0); /* now execute the command */ if (system(syscommand) > 1) { perror("system"); return(1); } return(0); } /* Z X C M D -- Run a system command so its output can be read like a file */ zxcmd(filenum,comand) int filenum; char *comand; { /******** Start of Rick Lamb's addition to Kermit history. ****************/ /* I modified Rick's code to use a tempname type of file. -- Phil Julian. */ /* Can open and start a task in AOS using their dg_open command * and a system call "sys_proc" in "start_cli". R.H.Lamb 12/86 */ FILE *piped; char temp[256]; char *tempfile,*cp,pipename[256]; char *shell, *savep, *comand2; int i; /* Create a unique pipe file in the users home directory, because * the actual working directory could be where the user has no * write privelages, or two users may collide in the same directory. */ debug(F000,"Entering zxcmd","",0); if (chkfn(filenum) < 0) return(-1); tempfile = getenv("HOME"); for (cp = tempfile; *cp; cp++) if (*cp == '/') *cp=':'; /* Make sure and copy the NULL also */ memcpy (pipename, tempfile, strlen(tempfile)+1); strcat (pipename,":?kermit_pipe_123456"); mktemp(pipename); /* The command interpreter for AOS is "cli.pr" * for MV/UX its :bin:sh:pr */ if ((piped=dg_open(pipename,$ICRF+$OFIN+$OFCR+$RTDY,$FPIP))==NULL) { perror("Trouble creating a pipe in zxcmd\n"); fprintf(stderr,"Pipe filename was [%s]\n",pipename); return(0); } debug(F000," pipe file created","",0); debug(F110," zxcmd: received command = ",comand,0); savep = strchr(comand,' '); /* enh - find blank and if there */ if (savep != NULL) { /* is one, split comand in 2 */ *savep = '\0'; comand2 = ++savep; /* comand2 points to 1st arg */ } else comand2 = NULL; /* we're doing all this so we can */ debug(F110," zxcmd: command = ",comand,0); /* put the L= switch on*/ if (comand2 != NULL) /* the command rather than the proc */ debug(F110," zxcmd: arguments = ",comand2,0); else debug(F110," zxcmd: arguments = none","",0); #ifdef mvux shell = ":bin:sh.pr"; #else shell = ":cli.pr"; #endif if (comand2 != NULL) sprintf(temp,"%s,%s/L,%s",shell,comand,comand2); else sprintf(temp,"%s,%s/L",shell,comand); debug(F110," zxcmd: revised command = ",temp,0); if (start_cli(temp,pipename)) {perror("Can't execute command in zxcmd\n"); return(0);} fp[filenum]=piped; fp[ZSYSFN]=fp[filenum]; zincnt = 0; zinptr = zinbuffer; return(1); } #ifdef SASMOD /* For remote Kermit command */ /* Z X L O G -- redirect stderr and stdout for logging. */ zxlog() { FILE *tmpf, *tmpfile(); if (chkfn(ZSYSFN) != 0) return(0); /* Unix magic to redirect stdout and stderr to temporary file */ fflush(stdout); fflush(stderr); /* synchronize */ if ((tmpf = tmpfile()) == NULL) return(0); if ((savout = dup(1)) < 0 || (saverr = dup(2)) < 0) return(0); dup2(fileno(tmpf), 1); dup2(fileno(tmpf), 2); fp[ZSYSFN] = tmpf; return(1); } /* Z X U N L O G -- restore stderr and stdout from logging. */ zxunlog() { /* restore stdout and stderr */ fflush(stdout); fflush(stderr); /* synchronize */ dup2(savout, 1); close(savout); dup2(saverr, 2); close(saverr); /* rewind to start of temporary file */ rewind(fp[ZSYSFN]); fp[ZIFILE] = fp[ZSYSFN]; pid = 0; return(1); } #endif SASMOD /* Z C L O S F - wait for the child fork to terminate and close the pipe. */ zclosf() { int wstat; fclose(fp[ZIFILE]); fp[ZIFILE] = fp[ZSYSFN] = NULL; while ((wstat = wait(0)) != pid && wstat != -1) ; return(1); } /******** More of Rick Lamb's addition to Kermit history. */ /* S T A R T _ C L I - starts a command as another concurrent task. * The command is equivalent to the CLI command of * "proc/def/output=pipename :cli command" */ start_cli(command,pipename) char *command,*pipename; { P_PROC packet; P_ISEND message; int len,pid,err; short int *string = (short int *)command; debug(F110,"Start_cli: command = ",command,0); len = strlen(command); message.isfl = (short) 0; /* System flags */ message.iufl = (short) $RFCF; /* User flags */ message.idph = (long) 0; /* Destination port number */ message.iopn = (short) 0; /* Local origin port number */ message.ilth = (short) (len / 2 + 1); /* Length (in words) of message */ message.iptr = string; /* Pointer to message buffer */ packet.pflg = (short) 0; /* Flags for process creation */ packet.ppri = (short) -1; /* Process priority -- was 3 */ packet.psnm = ":cli.pr"; /* Byte pointer to program name */ packet.pipc = &message; /* Pointer to initial msg. or -1 */ packet.pnm = (char *) -1; /* Byte ptr to process name or -1 */ packet.pmem = (long) -1; /* Maximum memory pages or -1 */ packet.pdir = (char *) 0; /* Byte ptr to initial dir. or -1/0 */ packet.pcon = (char *) 0; /* Byte ptr to console name or -1/0 */ packet.pcal = (short) -1; /* Max concurrent system calls or -1 */ packet.pwss = (short) -1; /* Max working set size or -1 */ packet.punm = -1; /* Byte ptr to username or -1 */ /* Note that $PVPC (unlimited sons) causes privelage violations * for users that are not royally endowed. Anyway, following are * the privileges bits. -- Phil Julian */ packet.pprv = (short) ( /* $PVPC+ */ $PVWS+$PVEX+$PVIP); packet.ppcr = (short) -1; /* Maximum sons or -1 */ packet.pwmi = (short) -1; /* Working set minimum or -1 */ /* reserved */ packet.pifp = "@Null"; /* Byte ptr to @INPUT or -1/0 */ packet.pofp = "@Null"; /* Byte ptr to @OUTPUT or -1/0 */ packet.plfp = pipename; /* Byte ptr to @LIST or -1/0 */ packet.pdfp = (char *) 0; /* Byte ptr to @DATA or -1/0 */ packet.smch = (_ulong) -1; /* Max CPU time or -1 */ if (err = sys_proc(&packet,&pid)) { perror("Start_cli: sys_proc "); fprintf(stderr,"Start_cli: Error in sys_proc = %#o\n",err); return(1); } else return(0); } /******** End of Rick Lamb's addition to Kermit history. */ /* 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; { /* Victor Johansen's code requires no change in zxpand(). * However, it is difficult to expand wild card strings that * contain some ^ or #. For a #, you must check the file type for * being a directory, then open up the directory, and keep searching * for sub-directories, etc. However, an simple kludge can be used * in this case, which saves me the programming effort. Anyone * using the # should pay an execution time penalty for using it. * So when a string contains the #, we use the original kludge that * I used before getting Victor's code. Victor Johansen's code is more * efficient because it uses sys_gnfn(), but I have to do more work * in some cases. With the ^'s, the directory name must be parsed * and adjusted, before opening the correct directory. */ char tempname[256],command[256]; int pipe_call(); FILE *sysout; char *curptr, *saveptr, buffer[257], *pos, *end; int n; wildcarlb = 0; for (curptr = fn; *curptr; curptr++) if (*curptr == '#') { wildcarlb = 0; goto nonvictor; } wildcarlb = 1; goto victor; nonvictor: curptr = command; sprintf(curptr, "filestatus/cpl=16/nheader %s", fn); curptr = tempname; pipe_call(command,curptr); /* Read the file of filenames, and parse out a universal name */ sysout = fopen(tempname,"r"); for (fcount=0; n=dg_fgets(buffer,256,sysout); ) { curptr = (char *) malloc(min(256,strlen(buffer))); mtchs[fcount]=curptr; /* delete leading spaces, leading directory name, and and trailing LF */ if (iscntrl(*(pos = &buffer[strlen(buffer)-1]))) *pos-- = '\0'; /* First char will be =, if working dir, or : or @ if not. Delete the =, but keep others. */ for (pos=buffer; *pos; pos++) { if (*pos == '=') break; if ((*pos == ':') || (*pos == '@')) { pos--; break; } } strcpy(mtchs[fcount],pos+1); fcount++; } fclose(sysout); zdelet(tempname); victor: if (wildcarlb) 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) { /* Victor Johansen's code requires no change in znext(), but my * code does. The flag, wildcardlb is 1, if Victor's code is * in effect. */ strcpy(fn,*mtchptr++); if (wildcarlb == 0) /* My old code: Phil Julian */ free (*(mtchptr-1)); } else *fn = '\0'; debug(F111,"znext",fn,fcount+1); return(fcount+1); } /* Z C H K S P A -- Check if there is enough space to store the file */ /* Call with file specification f, size n in bytes. Returns -1 on error, 0 if not enough space, 1 if enough space. */ int zchkspa(f,n) char *f; long n; { /* ENH - this is not feasible in VS */ return(1); /* where the space returned is an */ } /* allocation limit rather than a */ /* physical one - so always return 1*/ /* Z N E W N -- Make a new name for the given file */ znewn(fn,s) char *fn, **s; { static char buf[100]; #define ZNEWNMD 4 /* Max digits for version number */ char *bp, *xp; int len = 0, n = 0, d = 0, t, i, power = 1; int j, k; int max = MAXNAMLEN; bp = buf; while (*fn) { /* Copy name into buf */ *bp++ = *fn++; len++; } if (len > max-2) { /* Don't let it get too long */ bp = buf + max-2; len = max - 2; } for (i = 1; i < (ZNEWNMD - 1); i++) { power *= 10; *bp++ = '+'; *bp-- = '\0'; n = zxpand(buf); /* Expand the resulting wild name */ while (n-- > 0) { /* Find any existing name~d files */ xp = *mtchptr++; xp += len; if (*xp == '.') { t = atoi(xp+1); if (t > d) d = t; /* Get maximum d */ } } if (d < power-1) { sprintf(bp,".%d",d+1); /* Make name~(d+1) */ *s = buf; return; } bp--; len--; } /* If we ever get here, we'll overwrite the xxx~100 file... */ } #ifndef NOFRILLS int zmail(p,f) char *p, *f; { /* Send f as mail to addr p */ /* Returns 0 on success 2 if mail delivered but temp file can't be deleted -2 if mail can't be delivered The UNIX version always returns 0 because it can't get a good return code from zsyscmd. *zmbuf = '\0'; /* Not implemented in AOS/VS Kermit */ return(-2); } #endif /* NOFRILLS */ #ifndef NOFRILLS int zprint(p,f) char *p; char *f; { /* Print file f with options p */ sprintf(zmbuf,"qprint%s %s",p,f); /* ENH - options must be specified */ zsyscmd(zmbuf); /* by user as /