home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
kermit.columbia.edu
/
kermit.columbia.edu.tar
/
kermit.columbia.edu
/
old
/
ckermit5a190
/
ckdfio.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-06-24
|
72KB
|
2,196 lines
char *ckzv = "Data General file support, 5A(066) 25 Jun 94";
/* 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 <ctype.h> /* Character types */
#include <stdio.h> /* Standard i/o */
#include <time.h>
#include <packets:create.h>
#include <packets:common.h>
#include <memory.h>
#include <dglib.h>
#include <sys_calls.h>
#include <packets:process.h>
#include <packets/filestatus.h> /* Used for ?GNFN */
#include <paru.h>
#include <bit.h>
#include <sys/dir.h> /* Directory structure */
#include <sys/stat.h> /* 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 <fcntl.h>
#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 /<option>/<option>.. */
return(0);
}
#endif /* NOFRILLS */
/* Z R E N A M E -- changes the name of old to new */
/*
Returns -1 on failure, 0 on success
*/
zrename(old,new) char *old, *new; {
int ac0,ac1,ac2,result;
ac0 = (char *) old;
ac1 = (char *) new;
ac2 = 0;
result = sys($RENAME,&ac0,&ac1,&ac2);
if (result > 0) {
debug(F110," rename fails",old,result); /* last arg is zero in unix */
return(-1); /* - using result may not */
} /* work */
if (result < 0) {
debug (F110," negative return from rename system call",old,result);
return(-1);
}
return(0);
}
/* Z F C D A T -- Get file creation date */
/*
Call with pointer to filename.
On success, returns pointer to creation date in yyyymmdd hh:mm:ss format.
On failure, returns pointer to null string.
*/
static char datbuf[40];
/* static */ /* (===OS2 change===) */
char *
zfcdat(name) char *name; {
#ifdef TIMESTAMP
P_FSTAT fs_pak; /* for AOS/VS only */
int year,month,day,hours,minutes,seconds;
int error;
int ac0,ac1,ac2; /* registers for fstat system call */
long date;
ac0 = name; /* ptr to filename */
ac1 = 0; /* resolve links */
ac2 = (P_FSTAT *) &fs_pak;
error = sys ($FSTAT, &ac0, &ac1, &ac2); /* get modification date */
if (error != 0) {
debug(F110,"zfcdat stat failed",name,error);
return("");
}
date = fs_pak.stmh.long_time; /* last modification date in seconds */
error = dg_date(fs_pak.stmh.short_time[_DATE],&day,&month,&year);
if (error != 0) {
debug(F110,"zfcdat call to dg_date() failed","",error);
return("");
}
error = dg_time(fs_pak.stmh.short_time[_TIME],&hours,&minutes,&seconds);
if (error != 0) {
debug(F110,"zfcdat call to dg_time() failed","",error);
return("");
}
if (year < 100)
year += 1900;
if (year < 0 || year > 2100) { /* Make sure year is ok */
debug(F110,"zfcdat date failed",name,0);
return("");
}
if (month < 1 || month > 12)
return("");
if (day < 0 || day > 31)
return("");
if (hours < 0 || hours > 23)
return("");
if (minutes < 0 || minutes > 59)
return("");
if (seconds < 0 || seconds > 59)
return("");
sprintf(datbuf,"%04d%02d%02d %02d:%02d:%02d",year,month,day,hours,
minutes,seconds);
debug(F111,"zfcdat",datbuf,strlen(datbuf));
return(datbuf);
#else
return ("");
#endif /* timestamp */
}
/* Z S T I M E -- Set creation date for incoming file */
/*
Call with:
f = pointer to name of existing file.
yy = pointer to a Kermit file attribute structure in which yy->date.val
is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
x = is a function code: 0 means to set the file's creation date as given.
1 means compare the given date with the file creation date.
Returns:
-1 on any kind of error.
0 if x is 0 and the file date was set successfully.
0 if x is 1 and date from attribute structure <= file creation date.
1 if x is 1 and date from attribute structure > file creation date.
*/
int
zstime(f,yy,x) char *f; struct zattr *yy; int x; {
int r = -1; /* return code */
int error;
long tm; /* UNIX scalar form of date/time */
char myname[] = "zstime()";
long validate();
int vs_existing_datetime, vs_incoming_datetime;
char existing_kdate[20], *incoming_kdate = yy->date.val;
void convert_kdate_to_aosvs(),convert_aosvs_to_kdate();
if ((x != 0) && (x != 1)) {
debug(F111,"Bad argument",yy->date.val,yy->date.len);
return (-1);
}
tm = validate(yy,myname); /* tm not used unless error */
if (tm == -1) {
debug(F111,"zstime fails on date check",incoming_kdate,yy->date.len);
return (-1);
}
if ( error = fs_get_modification_date (f,&vs_existing_datetime) ) {
debug(F110,"Can't stat file:",f,0);
return(-1);
}
debug(F111,"zstime FSTAT of existing file okay",f,vs_existing_datetime);
convert_kdate_to_aosvs(incoming_kdate,&vs_incoming_datetime);
debug(F111,"zstime date of incoming file","",vs_incoming_datetime);
/* ENH - modified for VS - we set the creation date in zopeno() */
/* because the only straightforward way of setting it in VS is on the */
/* create; so, if x is 0 here we don't do anything */
switch (x) { /* Execute desired function */
case 0: /* Set the creation date of the file */
debug(F110,"VS creation date intentionally not set in zstime(): ",f,0);
r = 0;
break;
case 1: /* Compare the dates */
convert_aosvs_to_kdate(existing_kdate,vs_existing_datetime);
debug(F111,"zstime compare existing file date",existing_kdate,0);
debug(F111,"zstime compare incoming file date",incoming_kdate,0);
if (vs_incoming_datetime < vs_existing_datetime) r = 1; else r = 0;
break;
default: /* Error */
r = -1;
}
return (r);
}
/* Z S A T T R -- fills in Kermit file attribute structure */
/*
Returns 0 on success or -1 on falure. Any string members that are null
should be ignored by the caller; likewise, any numeric members that are
-1 should be ignored. (Actually, there is no error return right now.)
*/
int
zsattr(xx) struct zattr *xx; {
long k;
if (iflen > 0)
k = iflen % 1024L; /* file length in k bytes */
else k = 0L;
if (k != 0L) k = 1L;
xx->lengthk = (iflen / 1024L) + k;
xx->type.len = 0; /* file type can't be filled in here */
xx->type.val = "";
if (*nambuf) {
xx->date.val = zfcdat(nambuf); /* File creation (mod. in VS) date */
xx->date.len = (int)strlen(xx->date.val);
} else {
xx->date.len = 0;
xx->date.val = "";
}
xx->creator.len = 0; /* File creator */
xx->creator.val = "";
xx->account.len = 0; /* File account */
xx->account.val = "";
xx->area.len = 0; /* File area */
xx->area.val = "";
xx->password.len = 0; /* Area password */
xx->password.val = "";
xx->blksize = -1L; /* File blocksize */
xx->access.len = 0; /* File access */
xx->access.val = "";
xx->encoding.len = 0; /* Transfer syntax */
xx->encoding.val = 0;
xx->disp.len = 0; /* Disposition upon arrival */
xx->disp.val = "";
xx->lprotect.len = 0; /* Local protection */
xx->lprotect.val = "";
xx->gprotect.len = 0; /* Generic protection */
xx->gprotect.val = "";
xx->systemid.len = 2; /* System ID */
xx->systemid.val = "F3";
xx->recfm.len = 0; /* Record format */
xx->recfm.val = "";
xx->sysparam.len = 0; /* System-dependent parameters */
xx->sysparam.val = "";
xx->length = iflen; /* Length */
return(0);
}
/* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */
/*
* The path structure is used to represent the name to match.
* Each slash-separated segment of the name is kept in one
* such structure, and they are linked together, to make
* traversing the name easier.
*/
struct path {
char npart[MAXNAMLEN]; /* name part of path segment */
struct path *fwd; /* forward ptr */
};
#define SSPACE 10000 /* size of string-generating buffer */
static char sspace[SSPACE]; /* buffer to generate names in */
static char *freeptr,**resptr; /* copies of caller's arguments */
static int remlen; /* remaining length in caller's array*/
static int numfnd; /* number of matches found */
/*
* splitpath:
* takes a string and splits the slash-separated portions into
* a list of path structures. Returns the head of the list. The
* structures are allocated by malloc, so they must be freed.
* Splitpath is used internally by the filename generator.
*
* Input: A string.
* Returns: A linked list of the slash-separated segments of the input.
*/
struct path *
splitpath(p)
char *p;
{
struct path *head,*cur,*prv;
int i;
head = prv = NULL;
if (*p == '/') p++; /* skip leading slash */
while (*p != '\0')
{
cur = (struct path *) malloc(sizeof (struct path));
debug(F101,"splitpath malloc","",cur);
if (cur == NULL) fatal("malloc fails in splitpath()");
cur -> fwd = NULL;
if (head == NULL) head = cur;
else prv -> fwd = cur; /* link into chain */
prv = cur;
for (i=0; i < MAXNAMLEN && *p != '/' && *p != '\0'; i++)
cur -> npart[i] = *p++;
cur -> npart[i] = '\0'; /* end this segment */
if (i >= MAXNAMLEN) while (*p != '/' && *p != '\0') p++;
if (*p == '/') p++;
}
return(head);
}
/* F G E N -- finds filenames that match string */
/*
* fgen:
* This is the actual name generator. It is passed a string,
* possibly containing wildcards, and an array of character pointers.
* It finds all the matching filenames and stores them into the array.
* The returned strings are allocated from a static buffer local to
* this module (so the caller doesn't have to worry about deallocating
* them); this means that successive calls to fgen will wipe out
* the results of previous calls. This isn't a problem here
* because we process one wildcard string at a time.
*
* Input: a wildcard string, an array to write names to, the
* length of the array.
* Returns: the number of matches. The array is filled with filenames
* that matched the pattern. If there wasn't enough room in the
* array, -1 is returned.
* By: Jeff Damens, CUCCA, 1984.
*/
fgen(pat,resarry,len)
char *pat,*resarry[];
int len;
{
/* Victor Johannsen helped out with this addition to Kermit. The use of
* sys_gnfn() is the proper way to solve this problem. But, I would prefer
* not building up the static string space. This can be a problem when a
* long, full-qualified directory name pattern is used, since this directory
* name would be pre-pended to each file name.
* A later fix would actually not build a list, but call sys_gnfn() within
* znext(), and update the counters appropriately. Several systems support
* this model, which seems much more flexible and not at all space-limited.
* -- Phil Julian, 30 April 1987
*/
P_GNFN packet; /* packet for gnfn call */
char prefix[256];
char dirname[256];
int chan; /* Channel for open the DIR */
FILE *Ftemp;
char *DIRptr; /* DIR name */
int i,off;
int got_dir = 0;
char *pattern,*cp;
/* If a directory prefix is passed in, and possibly some wild cards, we
* will need to parse off the directory to open. We do not assume that
* directories can be descended ad infinitum.
*/
i = strlen(pat); zero(prefix,min(i+1,256));
for (cp = pat+i-1; i-- >= 0; cp--)
if ((*cp == ':') || (*cp == '^') || (*cp == '=') || (*cp == '@')) {
memcpy(prefix,pat,off = i+1);
memcpy(dirname,prefix,off-1), dirname[off-1] = 0;
DIRptr = dirname, pattern = cp+1;
got_dir = 1;
/* Parse ^ for moving up a directory */
if (*pat == '^') {
/* From the tail of the current dir, back up to : */
char *kp,*dp;
int pos = 0;
i = strlen(dp = getdir()) - 1;
for (kp = pat; *kp == '^'; kp++,i--,pos++)
for (;dp[i] != ':';i--);
i++;
if (strlen(DIRptr)) { /* Less ^s */
memcpy(prefix,DIRptr+pos,strlen(DIRptr)-pos);
prefix[strlen(DIRptr)-pos] = 0; /* Terminate */
} else prefix[0] = 0;
memcpy(dirname,dp,i); /* The ^'d DIR */
off -= pos;
if (pos = strlen(prefix)) {
memcpy(dirname+i,":",1); /* Dir separator */
memcpy(dirname+i+1,prefix,pos); /* Rest of dir name */
}
dirname[i+off] = 0; /* Null terminate */
memcpy(prefix,dirname,off = strlen(dirname));
prefix[off] = ':'; prefix[++off] = 0;
}
break;
}
if (got_dir == 0) {
DIRptr = getdir();
pattern = pat;
}
if ( (Ftemp = fopen(DIRptr,"r")) == NULL) return(0);
chan = fchannel(Ftemp);
numfnd = 0;
freeptr = sspace;
resptr = resarry;
remlen = len;
packet.nfky = 0;
packet.nftp = pattern;
packet.nfnm = nambuf;
while (i = !sys_gnfn(chan,&packet)) {
if (got_dir == 0) addresult( nambuf );
else {
memcpy(prefix+off,nambuf,strlen(nambuf)+1);
addresult( prefix );
}
}
fclose(Ftemp);
return(numfnd); /* and return the number of matches */
}
/* T R A V E R S E -- searches directory for matches */
/* traverse:
* Walks the directory tree looking for matches to its arguments.
* The algorithm is, briefly:
* If the current pattern segment contains no wildcards, that
* segment is added to what we already have. If the name so far
* exists, we call ourselves recursively with the next segment
* in the pattern string; otherwise, we just return.
*
* If the current pattern segment contains wildcards, we open the name
* we've accumulated so far (assuming it is really a directory), then read
* each filename in it, and, if it matches the wildcard pattern segment, add
* that filename to what we have so far and call ourselves recursively on the
* next segment.
*
* Finally, when no more pattern segments remain, we add what's accumulated
* so far to the result array and increment the number of matches.
*
* Input: a pattern path list (as generated by splitpath), a string
* pointer that points to what we've traversed so far (this
* can be initialized to "/" to start the search at the root
* directory, or to "./" to start the search at the current
* directory), and a string pointer to the end of the string
* in the previous argument.
* Returns: nothing.
*/
traverse(pl,sofar,endcur)
struct path *pl;
char *sofar,*endcur;
{
int fd;
struct direct dir_entry;
struct direct *dirbuf = &dir_entry;
struct stat statbuf;
if (pl == NULL)
{
*--endcur = '\0'; /* end string, overwrite trailing / */
addresult(sofar);
return;
}
if (!iswild(pl -> npart))
{
strcpy(endcur,pl -> npart);
endcur += (int)strlen(pl -> npart);
*endcur = '\0'; /* end current string */
if (stat(sofar,&statbuf) == 0) /* if current piece exists */
{
*endcur++ = DIRSEP;
*endcur = '\0';
traverse(pl -> fwd,sofar,endcur);
}
return;
}
/* cont'd... */
/*...traverse, cont'd */
/* segment contains wildcards, have to search directory */
*endcur = '\0'; /* end current string */
if (stat(sofar,&statbuf) == -1) return; /* doesn't exist, forget it */
if ((statbuf.st_mode & S_IFDIR) == 0) return; /* not a directory, skip */
if ((fd = open(sofar,O_RDONLY)) < 0) return; /* can't open, forget it */
while ( read(fd,dirbuf,sizeof dir_entry) )
{
strncpy(nambuf,dirbuf->d_name,MAXNAMLEN); /* Get a null terminated copy!!! */
nambuf[MAXNAMLEN] = '\0';
if (dirbuf->d_ino != 0 && match(pl -> npart,nambuf)) {
char *eos;
strcpy(endcur,nambuf);
eos = endcur + strlen(nambuf);
*eos = '/'; /* end this segment */
traverse(pl -> fwd,sofar,eos+1);
}
}
close(fd);
}
/* A D D R E S U L T -- adds result string to result array */
/*
* addresult:
* Adds a result string to the result array. Increments the number
* of matches found, copies the found string into our string
* buffer, and puts a pointer to the buffer into the caller's result
* array. Our free buffer pointer is updated. If there is no
* more room in the caller's array, the number of matches is set to -1.
* Input: a result string.
* Returns: nothing.
*/
addresult(str)
char *str;
{
int l;
if (strncmp(str,"./",2) == 0) str += 2;
if (--remlen < 0) {
numfnd = -1;
return;
}
l = strlen(str) + 1; /* size this will take up */
if ((freeptr + l) > &sspace[SSPACE]) {
numfnd = -1; /* do not record if not enough space */
return;
}
strcpy(freeptr,str);
*resptr++ = freeptr;
freeptr += l;
numfnd++;
}
/* Z S Y S C M D -- Supposed to execute a "system" command. */
/*
Code copied verbatim from zshcmd for now
*/
int
zsyscmd(s) char *s; {
if (priv_chk()) return(1);
if (*s == '\0') /* Interactive shell requested? */
#ifdef mvux
system("/bin/sh ");
#else
system("x :cli prefix Kermit_Baby:");
#endif /* mvux */
else /* Otherwise, */
system(s); /* Best for aos/vs?? */
return (0); /* Assume no errors */
}
/* Z S H C M D -- Start a CLI */
int
zshcmd(s) char *s; { /* ENH - new function */
if (priv_chk()) return(1);
if (*s == '\0') /* Interactive shell requested? */
#ifdef mvux
system("/bin/sh ");
#else
system("x :cli prefix Kermit_CLI:");
#endif /* mvux */
else /* Otherwise, */
system(s); /* Best for aos/vs?? */
return (0); /* Assume no errors */
}
/* I S W I L D -- Check if filespec is "wild */
/*
Returns 0 if it is a single file, 1 if it contains wildcard characters.
Note: must match the algorithm used by match(), hence no [a-z], etc
*/
int
iswild(filespec)
char *filespec;
{
char c; int x; char *p;
if (wildxpand) {
if ((x = zxpand(filespec)) > 1) return(1);
p = malloc(MAXNAMLEN + 20);
znext(p);
x = (strcmp(filespec,p) != 0);
free(p);
return(x);
} else {
while ((c = *filespec++) != '\0')
if (c == '*' || c == '?') return(1);
return(0);
}
}
/* M A T C H -- see if pattern matches string */
/*
* match:
* pattern matcher. Takes a string and a pattern possibly containing
* the wildcard characters '*' and '?'. Returns true if the pattern
* matches the string, false otherwise.
* by: Jeff Damens, CUCCA
*
* Input: a string and a wildcard pattern.
* Returns: 1 if match, 0 if no match.
*/
match(pattern,string) char *pattern,*string; {
char *psave,*ssave; /* back up pointers for failure */
psave = ssave = NULL;
while (1) {
for (; *pattern == *string; pattern++,string++) /* skip first */
if (*string == '\0') return(1); /* end of strings, succeed */
if (*string != '\0' && *pattern == '?') {
pattern++; /* '?', let it match */
string++;
} else if (*pattern == '*') { /* '*' ... */
psave = ++pattern; /* remember where we saw it */
ssave = string; /* let it match 0 chars */
} else if (ssave != NULL && *ssave != '\0') { /* if not at end */
/* ...have seen a star */
string = ++ssave; /* skip 1 char from string */
pattern = psave; /* and back up pattern */
} else return(0); /* otherwise just fail */
}
}
/* C H E C K _ K E R M I T _ D A T E -- see if Kermit date is valid */
/*
This function is new in the VS version of Kermit only. It replaces code
that was previously in-line in zstime() that checks the validity of the
date found in an argument passed in, and if it's valid, converts it to a
scalar, stored in tm, which is returned. It takes a second argument, which
is the name of the calling function. This is there so that entries to
log will report the accurate Kermit function. It returns the value of
tm if the date is valid and a -1 if not. --ENH
*/
long
validate (yy,caller) struct zattr *yy; char *caller; {
int r = -1; /* return code */
long tm, days;
int i, n, isleapyear;
/* J F M A M J J A S O N D */
/* 31 28 31 30 31 30 31 31 30 31 30 31 */
static
int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
char s[5];
struct stat sb;
debug(F111,caller," calling validate()",0);
if ((yy->date.len == 0)
|| (yy->date.len != 17)
|| (yy->date.val[8] != ' ')
|| (yy->date.val[11] != ':')
|| (yy->date.val[14] != ':') ) {
debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
return(-1);
}
debug(F111,"Date check 1",yy->date.val,yy->date.len);
for(i = 0; i < 8; i++) {
if (!isdigit(yy->date.val[i])) {
debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
return(-1);
}
}
debug(F111,"Date check 2",yy->date.val,yy->date.len);
i++;
for (; i < 16; i += 3) {
if ((!isdigit(yy->date.val[i])) || (!isdigit(yy->date.val[i + 1]))) {
debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
return(-1);
}
}
debug(F111,"Date check 3",yy->date.val,yy->date.len);
debug(F100,"So far so good","",0);
s[4] = '\0';
for (i = 0; i < 4; i++) /* Fix the year */
s[i] = yy->date.val[i];
debug(F110,"year",s,0);
n = atoi(s);
debug(F111,"year",s,n);
/* Previous year's leap days. This won't work after year 2100, */
/* I don't care about that! */
isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
days = (long) (n - 1970) * 365;
days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
s[2] = '\0';
for (i = 4 ; i < 16; i += 2) {
s[0] = yy->date.val[i];
s[1] = yy->date.val[i + 1];
n = atoi(s);
debug(F110,"zstime entering switch",s,0);
switch (i) {
case 4: /* MM: month */
if ((n < 1 ) || ( n > 12)) {
debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
return(-1);
}
days += monthdays [n];
if (isleapyear && n > 2)
++days;
continue;
case 6: /* DD: day */
if ((n < 1 ) || ( n > 31)) {
debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
return(-1);
}
tm = (days + n - 1) * 24L * 60L * 60L;
i++; /* Skip the space */
continue;
case 9: /* hh: hour */
if ((n < 0 ) || ( n > 23)) {
debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
return(-1);
}
tm += n * 60L * 60L;
i++; /* Skip the colon */
continue;
case 12: /* mm: minute */
if ((n < 0 ) || ( n > 59)) {
debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
return(-1);
}
tm += n * 60L;
i++; /* Skip the colon */
continue;
case 15: /* ss: second */
if ((n < 0 ) || ( n > 59)) {
debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
return(-1);
}
tm += n;
}
if (localtime(&tm)->tm_isdst)
tm -= 60L * 60L; /* Adjust for daylight savings time */
}
debug(F111,"Attribute creation date ok ",yy->date.val,yy->date.len);
return(tm);
}
/* F I L E _ E X I S T S -- returns zero if filename already exists */
/*
Takes a pointer to a filename as an argument and determines whether that
file exists. Returns 1 if it does, 0 if not, -1 on other error.
*/
int
file_exists(name) char *name; {
P_FSTAT fs_pak;
int ac0,ac1,ac2,error;
ac0 = name;
ac1 = 0; /* resolve links */
ac2 = (P_FSTAT *) &fs_pak;
error = sys($FSTAT,&ac0,&ac1,&ac2);
if (error == ERFDE) /* file doesn't exist */
return (0);
if (error == 0) /* file does exist */
return (1);
else /* some other error */
return (-1);
}
/* S E T _ C R E A T I O N _ D A T E -- set creation date on VS file */
/*
Takes a pointer to a filename and a timestamp in Kermit format and sets
the creation date/time of the file -- returns 0 on success, -1 on failure
*/ /* Contributed by: */
int /* Larry McCoskery, Data General */
set_creation_date( filename, date_time )
char * filename;
char * date_time;
{
int ac0,ac1,ac2,error;
int vs_datetime; /* VS scalar */
P_CREATE crepak;
P_CTIM timepak;
void convert_kdate_to_aosvs();
/*
* First, convert the silly date. EH assures me that it's ok
*/
convert_kdate_to_aosvs ( date_time, &vs_datetime);
/*
* Build the time packet. Set TCR, TLM, and TLA to specified date
*/
timepak.tcth.long_time =
timepak.tath.long_time =
timepak.tmth.long_time = vs_datetime;
/*
* Build the create packet
*/
crepak.cftyp_format = 0; /* record format */
crepak.cftyp_entry = $FUDF; /* entry type */
crepak.ccps = 0; /* file control parameters */
crepak.ctim = &timepak; /* address of time block */
crepak.cacp = -1; /* address of ACL */
crepak.cdeh = 0; /* reserved */
crepak.cdel = -1; /* file element size */
crepak.cmil = -1; /* maximum index levels */
crepak.cmrs = 0; /* reserved */
/*
* Create it
*/
ac0 = (char *) filename;
ac1 = 0; /* reserved */
ac2 = (P_CREATE *) &crepak;
error = sys( $CREATE, &ac0, &ac1, &ac2 );
if( error != NULL ) {
debug(F111, "can't create file - VS error",filename,error);
error = -1; /* unix style "hide the error" */
}
else debug(F111,"file created",filename,0);
return error;
}
void
convert_kdate_to_aosvs( kermit_date, vs_scalar )
char * kermit_date;
int * vs_scalar;
{
int month, day, year, hour, minute, second;
int date, time;
char c;
short * sp = vs_scalar;
sscanf( kermit_date,"%4d%2d%2d%c%2d%c%2d%c%2d",
&year,&month,&day,&c,&hour,&c,&minute,&c,&second );
convert_date_to_dg( day, month, year, &date );
convert_time_to_dg( hour, minute, second, &time );
sp[0] = (short) date;
sp[1] = (short) time;
}
void
convert_aosvs_to_kdate( string, aosvs_scalar )
char * string;
int aosvs_scalar;
{
int day,month,year,hours,minutes,seconds;
short * sp = &aosvs_scalar;
dg_date( (int) sp[0], &day, &month, &year );
dg_time( (int) sp[1], &hours, &minutes, &seconds );
sprintf( string, "%4d%02d%02d %02d:%02d:%02d",
year,month,day,hours,minutes,seconds );
}
int
convert_date_to_dg( day, month, year, scalar )
int day,month,year,*scalar;
{
int ac0,ac1,ac2,error;
ac0 = day;
ac1 = month;
ac2 = year - 1900;
error = sys( $FDAY, &ac0, &ac1, &ac2 );
*scalar = ac0;
return error;
}
int
convert_time_to_dg( hours, mins, secs, scalar )
int hours, mins, secs, *scalar;
{
int ac0,ac1,ac2,error;
ac0 = secs;
ac1 = mins;
ac2 = hours;
error = sys( $FTOD, &ac0, &ac1, &ac2 );
*scalar = ac0;
return error;
}
int
dg_date( scalar, day, month, year )
int scalar, *day, *month, *year;
{
int ac0,ac1,ac2,error;
ac0 = scalar;
ac1 = ac2 = 0;
error = sys( $CDAY, &ac0, &ac1, &ac2 );
*day = ac0;
*month = ac1;
*year = ac2 + 1900;
return error;
}
int
dg_time( scalar, hours, mins, secs )
int scalar, *hours, *mins, *secs;
{
int ac0,ac1,ac2,error;
ac0 = scalar;
ac1 = ac2 = 0;
error = sys( $CTOD, &ac0, &ac1, &ac2 );
*hours = ac2;
*mins = ac1;
*secs = ac0;
return error;
}
fs_get_modification_date( filename, date )
char * filename;
int * date;
{
P_FSTAT fs_pak;
int ac0,ac1,ac2,error;
ac0 = filename;
ac1 = 0; /* resolve links */
ac2 = (P_FSTAT *) &fs_pak;
error = sys($FSTAT,&ac0,&ac1,&ac2);
*date = fs_pak.stmh.long_time;
return error;
}
int
get_dir(dirbuf)
char *dirbuf;
{
int ac0,ac1,ac2,error;
ac0 = (char *) "=";
ac1 = (char *) dirbuf;
ac2 = MAXPATH+1;
error = sys($GNAME,&ac0,&ac1,&ac2);
if (error == NULL)
*(dirbuf+ac2) = '\000'; /* null terminator */
return(error);
}
/* Z F S E E K -- Position input file pointer */
/*
Call with:
Long int, 0-based, indicating desired position.
Returns:
0 on success.
-1 on failure.
*/
#ifndef NORESEND
int
#ifdef CK_ANSIC
zfseek(long pos)
#else
zfseek(pos) long pos;
#endif /* CK_ANSIC */
/* zfseek */ {
debug(F101,"zfseek","",pos);
return(fseek(fp[ZIFILE], pos, 0));
}
#endif /* NORESEND */