char *ckzv = "OS-9 file support, 5A(09) 4 Oct 94"; char *ckzsys = " OS-9/68000"; /* c k 9 F I O -- Kermit file system support for OS-9/68k systems */ /* Author: Peter Scholz, Ruhr University Bochum, Department for Analytical Chemistry, Federal Republic of Germany, February 1987 04/30/87 Robert Larson Cleanup, merge with standard C-kermit 04/07/89 Robert Larson Update for ckermit 4f(77) 07/16/89 Robert Larson 4f(85) Edition: 5A(01) 06/21/91 Chris Hemsing general adaption to 5A(171) miscellaneous bug fixes, utilization of initial file size. 07/25/91 Chris Hemsing minor bug fixes, changes for gnu (ansi) C 01/14/92 Chris Hemsing uct/localtime bug fix 05/26/92 Chris Hemsing zltor and zrtol bug fix Edition: 5A(05) 06/30/92 Chris Hemsing pipeopen stderr bug fix,dir shows filesize Edition: 5A(06) 07/09/92 Chris Hemsing removed all chaining which would save a process but f$chain does not look in PATH Edition: 5A(07) 07/10/92 Chris Hemsing remove whole process tree of a lower fork Edition: 5A(08) 12/01/92 Chris Hemsing zgtdir bugs repaired: upmost dir not closed closedir of non-open dir could be called adapted from Unix C-Kermit Author: Frank da Cruz (SY.FDC@CU20B), Columbia University Center for Computing Activities, January 1985. Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New York. Permission is granted to any individual or institution to use this software as long as it is not sold for profit. This copyright notice must be retained. This software may not be included in commercial products without written permission of Columbia University. */ /* Includes */ #include "ckcdeb.h" /* Typedefs, debug formats, etc */ #include "ckcker.h" /* Kermit definitions */ #include /* Directory structure */ #include #include #include #include #include #include /* Definitions of some system commands */ char *DIRCMD = "dir -e "; /* For directory listing with filename*/ char *DIRCM2 = "dir -e "; /* For directory listing without filename*/ char *DELCMD = "del "; /* For file deletion */ char *TYPCMD = "list "; /* For typing a file */ char *PWDCMD = "pd "; /* For saying where I am */ char *SPACMD = "free "; char *SPACM2 = "free "; /* For space in specified directory */ char *WHOCMD = "procs -e "; /* we have no who yet*/ /* Functions (n is one of the predefined file numbers from ckermi.h): zopeni(n,name) -- Opens an existing file for input. zopeno(n,name,attr,fcb) -- 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 to create new file, name. 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(struc zattr *) -- Return attributes for file which is being sent. */ /* Some systems define these in include files, others don't... */ #define MAXWLD 500 /* Declarations */ #define MAXPATH 256 /* not really an OS-9 restriction */ FILE *fp[ZNFILS] = { /* File pointers */ NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* (PWP) external def. of things used in buffered file input and output */ #ifdef DYNAMIC extern CHAR *zinbuffer, *zoutbuffer; #else extern CHAR zinbuffer[], zoutbuffer[]; #endif /* DYNAMIC */ extern CHAR *zinptr, *zoutptr; extern int zincnt, zoutcnt; struct fildes cur_in_fd; /* current input filedesc */ static int cur_in_size = -1; /* current input file length */ static int pipe_pid; /* pid of child fork */ static int fcount; /* Number of files in wild group */ char *malloc(), *getenv(), *strcpy(); /* System functions */ extern errno; /* System error code */ char *mtchs[MAXWLD], /* Matches found for filename */ **mtchptr; /* Pointer to current match */ /* Z K S E L F -- Kill Self: log out own job, if possible. */ zkself() { /* For "bye" */ procid pbuf; _get_process_desc(getpid(),sizeof(pbuf),&pbuf); if (kill((int)pbuf._pid,SIGKILL)==0) doexit(GOOD_EXIT,-1);/*kill parent process*/ else doexit(errno,-1); } char *zgtdir() { /* I'm sure this can be done better... */ static char cwd[MAXPATH]; char backpath[MAXPATH]; char *bpp = backpath, *path = cwd; DIR *dirp; struct direct *dp; long inode, inode2; char dots[MAXPATH]; if((dirp = opendir(".")) == NULL) return NULL; if((readdir(dirp) == NULL) || (dp = readdir(dirp)) == NULL) { closedir(dirp); return (char *)NULL; } inode = dp->d_addr; *path++ = '/'; _gs_devn(dirp->dd_fd, path); path += strlen(path); closedir(dirp); strcpy(dots, ".."); for(;;) { if((dirp = opendir(dots)) == NULL) return NULL; if((readdir(dirp) == NULL) || (dp = readdir(dirp)) == NULL) { closedir(dirp); return (char *)NULL; } inode2 = dp->d_addr; if(inode == inode2) break; /* .. and . are same, we are done */ do { if((dp = readdir(dirp)) == NULL) { closedir(dirp); return (char *)NULL; } } while(dp->d_addr != inode); *bpp++ = '/'; strcpy(bpp, dp->d_name); bpp += strlen(bpp); closedir(dirp); inode = inode2; strcat(dots, "/.."); } closedir(dirp); /* close upmost directory */ while(bpp > backpath) { *bpp = '\0'; while(*--bpp != '/') {} strcpy(path, bpp); path += strlen(path); } return cwd; } /* 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); zincnt = 0; /* Reset input buffer */ if (n == ZSYSFN) { /* Input from a system function? */ /*** Note, this function should not be called with ZSYSFN ***/ /*** Always call zxcmd() directly, and give it the real file number ***/ /*** you want to use. ***/ debug(F110,"zopeni called with ZSYSFN, failing!",name,0); return(0); /* fail. */ } 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) /* zopeno */ int n; char *name; struct zattr *zz; struct filinfo *fcb; { /* As of Version 5A, the attribute structure and the file information */ /* structure are included in the arglist. */ char *p; int pn; debug(F111,"zopeno",name,n); if (chkfn(n) != 0) return(0); zoutcnt = 0; zoutptr = zoutbuffer; 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 == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */ fp[ZOFILE] = stdout; debug(F101," fp[]=stdout", "", (int) fp[n]); return(1); } /* A real file. Open it in desired mode (create or append). */ p = "w"; /* Assume write/create mode */ if (fcb) { /* If called with an FCB... */ if (fcb->dsp == XYFZ_A) /* Does it say Append? */ p = "a"; /* Yes. */ } if (access(name,0) == 0) /* does file exist ? */ { if ((fp[n] = fopen(name,p)) == NULL) /* Simply open the file */ { perror("zopeno can't open"); return(0); } debug(F111, " exist:fp[n]",p, (int) fp[n]); } else /* file does not exist, we can open with initial size */ /* this is very important for long files not to be too fragmented */ /* so that after a long transmission OS-9 may finish saying that */ /* the segment allocation table is full */ { if ((zz) && ((zz->length > 0) || (zz->lengthk > 0))) /* length estimate available ? */ { if ((pn = create(name,S_IREAD+S_IWRITE+S_ISIZE, S_IREAD+S_IWRITE, (zz->length > 0) ? zz->length : (zz->lengthk*1024L))) != -1) { debug(F111, " create with init. size:",p, (zz->length > 0) ? zz->length : (zz->lengthk*1024L)); if ((fp[n] = fdopen(pn,"w")) == NULL) /* get filepntr for pn */ { perror("zopeno can't open"); return(0); } debug(F111, " create with init. size:fp[n]",p, (int) fp[n]); } else { perror("zopeno can't open"); return(0); } } else /* no length available */ { if ((fp[n] = fopen(name,p)) == NULL) /* Simply open the file */ { perror("zopeno can't open"); return(0); } debug(F111, " create without init size fp[n]",p, (int) fp[n]); } } if((zz) && (zz->systemid.val[0] == 'U') && ( zz->systemid.val[1] == 'D')) { debug(F101, "zopeno:file originated from OS-9","",zz->lprotect.val[0]); if (zz->lprotect.len != 0) { if (_ss_attr(fp[n]->_fd,(zz->lprotect.val[0]&0x7f)|S_IWRITE) == -1) { debug(F101, "zopeno:can't set file attr","",errno); return(0); } } } if (n == ZDFILE) setbuf(fp[n],(char *)NULL); /* Debug file unbuffered */ return(1); } /* Z C L O S E -- Close the given file. */ /* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */ zclose(n) int n; { int x, 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; /* Initialize return code */ if (fp[ZSYSFN]) { /* If file is really pipe */ x = zclosf(n); /* do it specially */ } else { if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]); fp[n] = NULL; } if (x == EOF) /* if we got a close error */ return (-1); else if (x2 < 0) /* or an error flushing the last buffer */ return (-1); /* then return an error */ 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 */ zchin(n,c) int n; int *c; { int a; /* (PWP) Just in case this gets called when it shoudn't */ if (n == ZIFILE) return (zminchar()); /* if (chkfn(n) < 1) return(-1); */ a = getc(fp[n]); if (a == EOF) return(-1); *c = a & 0377; return(0); } /* Z I N F I L L -- Get a character from the input file. * (PWP) (re)fill the buffered input buffer with data. All file input * should go through this routine, usually by calling the zminchar() * macro (in ckcker.h). */ zinfill() { zincnt = fread((char *)zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]); if (zincnt == 0) return (-1); /* end of file */ zinptr = zinbuffer; /* set pointer 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. */ zsout(n,s) int n; char *s; { if (chkfn(n) < 1) return(-1); fputs(s,fp[n]); return(0); } /* Z S O U T L -- Write string to file, with line terminator, buffered */ zsoutl(n,s) int n; char *s; { /* if (chkfn(n) < 1) return(-1); */ fputs(s,fp[n]); fputs("\n",fp[n]); return(0); } /* Z S O U T X -- Write x characters to file, unbuffered. */ zsoutx(n,s,x) int n, x; char *s; { /* if (chkfn(n) < 1) return(-1); */ return(write(fileno(fp[n]),s,(unsigned int)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) */ int #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])); /* Check to make sure */ } else return(0); /* There was no error. */ } } /* (PWP) buffered character output routine to speed up file IO */ zoutdump() { if (zoutcnt == 0) return (0); /* nothing to output */ if (zoutcnt < 0) return (-1); /* unexpected */ zoutptr = zoutbuffer; /* Reset buffer pointer in all cases */ if (fwrite ((char *)zoutbuffer, 1, zoutcnt, fp[ZOFILE])) { fflush(fp[ZOFILE]); zoutcnt = 0; /* reset output buffer */ zoutptr = zoutbuffer; return(0); /* things worked OK */ } zoutcnt = 0; /* reset output buffer */ return(ferror(fp[ZOFILE])?-1:0); /* Check to make sure */ } /* 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. */ long zchki(name) char *name; { int x; if (access(name,0) < 0) { if(access(name,S_IFDIR)>=0) { debug(F111,"zchki skipping:",name,errno); return(-2); } debug(F111,"zchki can't access",name,errno); return(-1); } if ((x = access(name,S_IREAD)) < 0) { /* Is the file accessible? */ debug(F111," access failed:",name,x); /* No */ return(-3); } debug(F110," access ok:",name,0); if((x = open(name,S_IREAD)) < 0) { debug(F111,"zchki can't open:",name,errno); return(-2); } debug(F111,"zchki stat ok:",name,x); cur_in_size = _gs_size(x); /* remember size */ _gs_gfd(x,&cur_in_fd,sizeof(cur_in_fd)); /* remember filedespr. */ close(x); debug(F111," access ok:",name,cur_in_size); return( (cur_in_size > -1) ? cur_in_size : 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; if(access(name,S_IFDIR)>=0) return -1; /* it's a directory */ 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(!isalnum(s[i-1])) { switch(s[i-1]) { case '.': case '$': case '_': continue; case '/': break; default: return -1; /* bad character */ } break; } } debug(F101," i","",i); if (i == 0) /* If no path, use current directory */ strcpy(s,"."); else { /* Otherwise, use given one. */ if(i==x) return -1; /* no filename! */ s[--i] = '\0'; } x = access(s,S_IFDIR|S_IWRITE); /* Check access of path. */ if (x < 0) { fprintf(stderr,"access failed %s errno:%d\n",s,errno); debug(F111,"zchko access failed:",s,errno); return(-1); } else { debug(F111,"zchko access ok:",s,x); return(0); } } /* 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. */ zchkspa(f,n) char *f; long n; { /* Just dummy for now. */ return(1); /* Always say OK. */ } /* Z D E L E T -- Delete the named file. */ zdelet(name) char *name; { return(unlink(name)); } /* Z R T O L -- Convert remote filename into local form */ /* For OS9, this means changing uppercase letters to lowercase and making shure only a-z,A-Z,0-9,$,_,. are used */ VOID zrtol(name,name2) char *name, *name2; { for ( ; *name != '\0'; name++) { if (isupper(*name)) *name2++ = tolower(*name); else if (islower(*name) || isdigit(*name) || *name == '$' || *name == '.' || *name == '_') *name2++ = *name; else *name2++ = '_'; /* otherwise use '_' */ } *name2 = '\0'; debug(F110,"zrtol:",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; for (cp = name; *cp != '\0'; cp++) { /* strip path name */ if (*cp == '/') { dc = 0; pp = work; } else if (islower(*cp)) *pp++ = toupper(*cp); /* Uppercase letters */ else if (*cp == '$') *pp++ = 'X'; /* Change '$' 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() { return(getenv("HOME")); } /* Z X C M D -- Run a system command so its output can be read like a file */ zxcmd(filnum,comand) int filnum;char *comand; { FILE *pipeopen(),*filedesc; if (chkfn(filnum) < 0) return(-1); /* Need a valid kermit file number */ if (filnum == ZSTDIO || filnum == ZCTERM) return(0); if (filnum == ZIFILE || filnum == ZRFILE) /* kermit wants to read */ { debug(F111," zxcmd read:",comand,filnum); if ((filedesc = pipeopen(comand,"r")) != (FILE *)NULL) { fp[filnum] = filedesc; fp[ZSYSFN] = fp[filnum]; /* Remember. */ zincnt = 0; zinptr = zinbuffer; } else return(0); } else { debug(F111," zxcmd write:",comand,filnum); if ((filedesc = pipeopen(comand,"w")) != (FILE *)NULL) { fp[filnum] = filedesc; fp[ZSYSFN] = fp[filnum]; /* Remember. */ zoutcnt = 0; zoutptr = zoutbuffer; } else return(0); } return(1); /* return success if we ever get here */ } /* KILL_TREE recursively kill all processes of a forked tree */ static void kill_tree(root) unsigned int root; { struct { unsigned short _id, /* process id */ _pid, /* parent's id */ _sid, /* sibling's id */ _cid; /* child's id */ } prdesc; /* NOT whole process descriptor! too much stack needed */ unsigned int child; if(_get_process_desc(root,sizeof(prdesc),&prdesc)==(int)-1) return; if ((child = prdesc._cid) != 0) kill_tree(child); /* kill child's tree */ if ((child = prdesc._sid) != 0) kill_tree(child); /* kill siblings tree */ kill(root,SIGKILL); } /* Z C L O S F - kill the child(action may have aborted and close the pipe.*/ zclosf(filnum) int filnum;{ int wstat; debug(F101," zclosf filnum:","",filnum); fclose(fp[filnum]); fp[filnum] = fp[ZSYSFN] = NULL; if (pipe_pid == 0) return(-1); kill_tree(pipe_pid); /*kill all lower processes in case things have been aborted */ debug(F101," zclosf killed:","",pipe_pid); while ((wstat = wait((unsigned int *)0)) != pipe_pid && wstat != -1) ; pipe_pid = 0; return(1); } /* Z X P A N D -- Expand a wildcard string into an array of strings */ /* Returns the number of files that match fn1, with data structures set up so that first file (if any) will be returned by the next znext() call. */ zxpand(fn) char *fn; { fcount = fgen(fn,mtchs,MAXWLD); /* Look up the file. */ if (fcount > 0) { mtchptr = mtchs; /* Save pointer for next. */ } debug(F111,"zxpand",mtchs[0],fcount); return(fcount); } /* Z N E X T -- Get name of next file from list created by zxpand(). */ /* Returns >0 if there's another file,with its name copied into the arg string or 0 if no more files in list. */ znext(fn) char *fn; { if (fcount-- > 0) strcpy(fn,*mtchptr++); else *fn = '\0'; debug(F111,"znext",fn,fcount+1); return(fcount+1); } /* Z N E W N -- Make a new name for the given file */ VOID znewn(fn,s) char *fn, **s; { static char buf[256]; char *bp, *xp; int len = 0, n = 0, d = 0, t, i, power = 1; #ifdef MAXNAMLEN int max = MAXNAMLEN; #else int max = 14; #endif 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 < 4; i++) { /* Try up to 999 times */ power *= 10; *bp++ = '*'; /* Put a star on the end */ *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... */ } /* Z S A T T R */ /* Fills in a Kermit file attribute structure for the file which is to be sent. Returns 0 on success with the structure filled in, or -1 on failure. If any string member is null, then it should be ignored. If any numeric member is -1, then it should be ignored. */ zsattr(xx) struct zattr *xx; { int k; static char protection[2]; static char * get_gmtime(); k = cur_in_size % 1024L; /* File length in K */ if (k != 0L) k = 1L; xx->lengthk = (cur_in_size / 1024L) + k; /* if ((cur_in_fd.fd_att & S_IEXEC) || (cur_in_fd.fd_att & S_IOEXEC)) { xx->type.len = 1; xx->type.val = "B"; } else { xx->type.len = 0; xx->type.val = ""; } */ xx->type.len = 0; /* better let the user decide */ xx->type.val = ""; xx->date.val = get_gmtime(&cur_in_fd); /* get UCT time, if possible */ xx->date.len = strlen(xx->date.val); debug(F111,"zsattr date",xx->date.val,xx->date.len); 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 = ""; protection[0] = cur_in_fd.fd_att & 0x7f; protection[1] = 0; xx->lprotect.val = protection; xx->lprotect.len = 1; debug(F101,"zsattr lprotect","",xx->lprotect.val[0]); xx->gprotect.len = 0; /* Generic protection */ xx->gprotect.val = ""; xx->systemid.len = 2; /* System ID */ xx->systemid.val = "UD"; xx->recfm.len = 0; /* Record format */ xx->recfm.val = ""; xx->sysparam.len = 0; /* System-dependent parameters */ xx->sysparam.val = ""; debug(F101,"zsattr size","",cur_in_size); xx->length = cur_in_size; /* Length */ return(0); } zmail(p,f) char *p; char *f; { /* Send file f as mail to address p; no mail on OS-9 */ return(-1); } zprint(p,f) char *p; char *f; { /* Print file f with options p; no unique print device on OS-9 */ return(-1); } /* 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 2000 /* 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","",(int)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); } /* * 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; { VOID traverse(); struct path *head; char scratch[100+MAXNAMLEN],*sptr; head = splitpath(pat); if (*pat == '/') { scratch[0] = '/'; sptr = scratch+1; *sptr = '\0'; } else { strcpy(scratch,"./"); sptr = scratch+2; } /* init buffer correctly */ numfnd = 0; /* none found yet */ freeptr = sspace; /* this is where matches are copied */ resptr = resarry; /* static copies of these so*/ remlen = len; /* recursive calls can alter them */ traverse(head,scratch,sptr); /* go walk the directory tree */ while (head != NULL) { struct path *next = head -> fwd; free((char *)head); head = next; } return(numfnd); /* and return the number of 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. */ VOID traverse(pl,sofar,endcur) struct path *pl; register char *sofar,*endcur; { DIR *fd, *opendir(); VOID addresult(); struct direct *dirbuf; debug(F110,"traverse ",sofar,0); if (pl == NULL) { *--endcur = '\0'; /* end string, overwrite trailing / */ addresult(sofar); return; } if (!iswild(pl -> npart)) { strcpy(endcur,pl -> npart); endcur += strlen(endcur); if (access(sofar,S_IFDIR) == 0) { /* if current piece exists & dir */ *endcur++ = '/'; /* add slash to end */ *endcur = '\0'; /* and end the string */ traverse(pl -> fwd,sofar,endcur); } else if(access(sofar,0) == 0 && pl->fwd==NULL) { addresult(sofar); } return; } /* segment contains wildcards, have to search directory */ *--endcur = '\0'; /* end current string */ if (access(sofar,S_IFDIR) < 0) { debug(F111,"traverse can't access directory",sofar,errno); return; /* doesn't exist, forget it */ } if ((fd = opendir(sofar)) == NULL) { debug(F111,"traverse can't open directory",sofar,errno); return; /* can't open, forget it */ } *endcur++ = '/'; while (dirbuf = readdir(fd)) { if (dirbuf->d_addr != 0) { strncpy(endcur,dirbuf->d_name,MAXNAMLEN); /* Get a null terminated copy!!! */ if(match(pl -> npart,endcur)) { char *eos; eos = endcur + strlen(endcur); *eos++ = '/'; /* end this segment */ *eos = '\0'; traverse(pl -> fwd,sofar,eos); } } } closedir(fd); } /* * 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. */ VOID addresult(str) register char *str; { register 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++; } iswild(str) register char *str; { register char c; while ((c = *str++) != '\0') if (c == '*' || c == '?') return(1); return(0); } /* * 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. * Input: a string and a wildcard pattern. * Returns: 1 if match, 0 if no match. */ match(pattern,string) char *pattern,*string; { int i; i = _cmpnam(string,pattern,strlen(pattern))==0; debug(F111,"Match ",string,i); return i; } /* emulate unix perror function */ perror(string) char *string; { extern int errno; fprintf(stderr,"%s ERRNO: %d\n",string,errno); } #ifdef DTILDE char * tilde_expand(dirname) register char *dirname; { static char *home = NULL; static char temp[MAXNAMLEN]; debug(F111,"tilde_expand dirname", dirname, dirname[0]); if(*dirname++ != '~' || (*dirname != '\0' && *dirname != '/')) return --dirname; if(home == NULL && (home = getenv("HOME")) == NULL) return --dirname; if(*dirname == '\0') return home; strcpy(temp, home); strcat(temp, dirname); return temp; } #endif /* 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 [yy]yymmdd[ 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. */ zstime(f,yy,x) char *f; register struct zattr *yy; int x; { extern int errno; int r = -1; /* return code */ int path; register char *point; register char save; char year,month,day,hour,minute,second; struct fildes buffer; unsigned char long_year=0,hour_min=0,err=0; time_t mktime(),file_date; static VOID convert_to_local_time(); struct tm tp; debug(F110,"zstime",f,0); switch (yy->date.len) { case 0: err = 1; break; case 6: /* yymmdd */ break; case 8: /* yyyymmdd */ long_year = 1; break; case 12: /* yymmdd hh:mm */ hour_min = 1; break; case 14: /* yyyymmdd hh:mm */ long_year = 1; hour_min = 1; break; case 15: /* yymmdd hh:mm:ss */ hour_min = 1; break; case 17: /* yyyymmdd hh:mm::ss */ long_year = 1; hour_min = 1; break; default: err = 1; break; } if (err) { debug(F111,"Bad creation date ",yy->date.val,yy->date.len); return(-1); } point = yy->date.val; if (long_year) { save = *(point+4); *(point+4) = '\0'; year = atoi(point); *(point+4) = save; point += 4; year -= 1900; /* OS-9 only has year since 1900 */ } else { save = *(point+2); *(point+2) = '\0'; year = atoi(point); point += 2; /* don't add 1900: OS-9 only has year since 1900 anyway */ } save = *(point+2); *(point+2) = '\0'; month = atoi(point); *(point+2) = save; point += 2; save = *(point+2); *(point+2) = '\0'; day = atoi(point); *(point+2) = save; if (hour_min) { point += 3; /* skip the blank */ save = *(point+2); *(point+2) = '\0'; hour = atoi(point); *(point+2) = save; point += 3; /* skip the : */ save = *(point+2); *(point+2) = '\0'; minute = atoi(point); *(point+2) = save; second = 0; /* no OS-9 seconds */ } else /* no hours and minutes given */ { hour = minute = second = 0; } err = 0; if (year >99) err = 1; if ((month >12) || (month < 1)) err = 1; if ((day >31) || (day < 1)) err = 1; if (hour >23) err = 1; if (minute >59) err = 1; if (err) { debug(F111,"Bad creation date ",yy->date.val,yy->date.len); return(-1); } convert_to_local_time(&year,&month,&day,&hour,&minute,&second); if ((path = open(f,0)) == -1) /* mode 0 is sufficient for _gs_gfd */ { debug(F110,"Can't open file to get fdesc:",f,0); return(r); } if (_gs_gfd(path,&buffer,sizeof(buffer)) == -1) { debug(F111,"Can't get descriptor for file:",f,errno); close(path); return(r); } close(path); switch (x) /* Execute desired function */ { case 0: /* Set the creation and modification date of the file */ buffer.fd_date[0] = year; buffer.fd_date[1] = month; buffer.fd_date[2] = day; buffer.fd_date[3] = hour; buffer.fd_date[4] = minute; buffer.fd_dcr[0] = year; buffer.fd_dcr[1] = month; buffer.fd_dcr[2] = day; if ((path = open(f,S_IWRITE)) == -1) /* _ss_pfd needs writing */ { debug(F110,"Can't open file for write:",f,0); return(r); } if (_ss_pfd(path,&buffer) == -1) { debug(F111,"Can't update descriptor for file:",f,errno); } else { debug(F110,"Creat. and mod. time is set for file: ",f,0); r = 0; } close(path); break; case 1: /* Compare the dates */ tp.tm_sec = 0; tp.tm_min = buffer.fd_date[4]; tp.tm_hour = buffer.fd_date[3]; tp.tm_mday = buffer.fd_date[2]; tp.tm_mon = buffer.fd_date[1]-1; tp.tm_year = buffer.fd_date[0]; file_date = mktime(&tp); tp.tm_min = minute; tp.tm_hour = hour; tp.tm_mday = day; tp.tm_mon = month-1; tp.tm_year = year; if (mktime(&tp) > file_date) r=0; else r=1; break; default: /* Error */ break; } close(path); return(r); } /* Z R E N A M E -- Rename a file */ /* Call with old and new names */ /* Returns 0 on success, -1 on failure. */ zrename(old,new) register char *old, *new; { char buffer[256]; sprintf(buffer,"rename %s %s",old,new); if (zsyscmd(buffer) != 0) { debug(F111,"Can't rename:",old,errno); return(-1); } return(0); } /* Functions for executing system commands. zsyscmd() executes the system command in the normal, default way for the system. In UNIX, it does what system() does. Thus, its results are always predictable. zshcmd() executes the command using the user's preferred shell. */ zsyscmd(s) char *s; { unsigned int status; extern int os9forkc(); extern char **environ; char *(argblk[3]); int pid,wstat; debug(F110,"zsyscmd:try:",s,0); /* chaining (shell ex command) would save a process but */ argblk[2] = 0; /* f$chain does not use PATH */ argblk[1] = s; argblk[0] = "shell"; if ((pid = os9exec(os9forkc,argblk[0],argblk,environ,0,0,3)) > 0) { while ((wstat = wait(&status)) != pid && wstat != -1) ; if (status != 0) { debug(F111,"zsyscmd:can't execute:",s,status); /* return(-1); */ } } else { debug(F111,"zsyscmd:can't fork:",s,errno); /* return(-1); */ } debug(F110,"zsyscmd: success:",s,0); return(0); } zshcmd(s) register char *s; { unsigned int status; extern int os9forkc(); extern char **environ; char *(argblk[3]); int pid,wstat; debug(F110,"zshcmd:try:",s,0); /* chaining (shell ex command) would save a process but */ argblk[2] = 0; /* f$chain does not use PATH */ argblk[1] = s; argblk[0] = getenv("SHELL"); if (argblk[0] == (char *)0) argblk[0] = "shell"; if ((pid = os9exec(os9forkc,argblk[0],argblk,environ,0,0,3)) > 0) { while ((wstat = wait(&status)) != pid && wstat != -1) ; if (status != 0) { debug(F111,"zshcmd:can't execute:",s,status); /* return(-1); */ } } else { debug(F111,"zshcmd:can't fork:",s,errno); /* return(-1); */ } return(0); } /* Z S T R I P -- Strip device & directory name from file specification */ /* Strip pathname from filename "name", return pointer to result in name2 */ char work[100]; /* buffer for use by zstrip and zltor */ VOID zstrip(name,name2) char *name, **name2; { char *cp, *pp; debug(F110,"zstrip before",name,0); pp = work; #ifdef DTILDE if (*name == '~') name++; #endif for (cp = name; *cp != '\0'; cp++) { if (*cp == '/') pp = work; else *pp++ = *cp; } *pp = '\0'; /* Terminate the string */ *name2 = work; debug(F110,"zstrip after",*name2,0); } /* Z S I N L -- Read a line from a file */ /* Writes the line into the address provided by the caller. n is the Kermit "channel number". Writing terminates when newline is encountered, newline is not copied. Writing also terminates upon EOF or if length x is exhausted. Returns 0 on success, -1 on EOF or error. */ zsinl(n,s,x) int n, x; char *s; { int a, z = 0; if (chkfn(n) < 1) { /* Make sure file is open */ return(-1); } a = -1; while (x--) { if (zchin(n,&a) < 0) { /* Read a character from the file */ z = -1; break; } if ( a == (char) NLCHAR) break; /* Single-character line terminator */ *s = a; s++; } *s = '\0'; return(z); } static char *get_gmtime(file_desc) struct fildes *file_desc; /* returns UCT/GMT yyyymmdd hh:mm:00 time out of the file descriptor if im possible to get GMT it takes the time from the file descriptor without converion */ { static char datbuf[18]; /* must be static !!! */ struct tm *gmt,tp; time_t local_file_date; unsigned char we_know_gmt=0; while(1) { if (getenv("TZ") == (char *)NULL) /* if TZ not defined send modification date literally */ break; tp.tm_sec = 0; tp.tm_min = file_desc->fd_date[4]; tp.tm_hour = file_desc->fd_date[3]; tp.tm_mday = file_desc->fd_date[2]; tp.tm_mon = file_desc->fd_date[1]-1; tp.tm_year = file_desc->fd_date[0]; local_file_date = mktime(&tp); if (local_file_date == (time_t)-1) /* take time literally */ break; gmt = gmtime(&local_file_date); if (gmt == (struct tm*)NULL) /* take time literally */ break; we_know_gmt = 1; break; } /* end while (1) */ if (we_know_gmt) sprintf(datbuf,"%4d%02d%02d %02d:%02d:%02d", 1900L+(unsigned int)gmt->tm_year, gmt->tm_mon+1,gmt->tm_mday,gmt->tm_hour,gmt->tm_min,0); else sprintf(datbuf,"%4d%02d%02d %02d:%02d:%02d", 1900L+(unsigned int)file_desc->fd_date[0], file_desc->fd_date[1],file_desc->fd_date[2], file_desc->fd_date[3],file_desc->fd_date[4],0); return(datbuf); } static VOID convert_to_local_time(year,month,day,hour,minute,second) char *year,*month,*day,*hour,*minute,*second; /* will convert to local time if timezone information is available assuming UCT/GMT time as input , otherwise just return. year must be since 1900 */ { struct tm *tp; time_t cal_date; int time,date; char *getenv(); if (getenv("TZ") == (char *)NULL) return; date = ((1900+*year)<<16)+(*month<<8)+*day; time = (*hour<<16)+(*minute<<8)+*second; if (_julian(&time,&date) == -1) return; cal_date = (date-2440587)*86400+time; /* 2440587 = 1st of Jan 1970 */ tp = localtime(&cal_date); if (tp == (struct tm*)NULL) return; *second = tp->tm_sec; *minute = tp->tm_min; *hour = tp->tm_hour; *day = tp->tm_mday; *month = tp->tm_mon+1; *year = tp->tm_year; } FILE * pipeopen(command,access_type) char *command,*access_type; { extern int os9forkc(); extern char **environ; int duped,dupederr,stdinout; FILE *pipe; char *argv[3]; if (*access_type=='r') stdinout = 1; /* redirect stdout and stderr*/ else if (*access_type=='w') stdinout = 0; /* redirect stdin */ else return((FILE *)NULL); if((duped = dup(stdinout)) <= 0) return((FILE *)NULL); if(stdinout == 1) { if((dupederr = dup(2)) <= 0) { close(duped); return((FILE *)NULL); } } if((pipe = fopen("/pipe", "r+")) == NULL) { close(duped); if(stdinout == 1) close(dupederr); return((FILE *)NULL); } close(stdinout); dup(fileno(pipe)); if (stdinout == 1) /* also dupe stderr */ { close(2); dup(fileno(pipe)); } argv[0] = "shell"; argv[1] = command; argv[2] = (char *)NULL; if((pipe_pid = os9exec(os9forkc,argv[0],argv,environ,0,0,3)) < 0) { pipe_pid = 0; fclose(pipe); close(stdinout); dup(duped); /* restore old stdinout */ close(duped); if(stdinout == 1) /* also restore old sterr */ { close(2); dup(dupederr); close(dupederr); } return (FILE *)NULL; } debug(F101," pipeopen success id:","",pipe_pid); close(stdinout); dup(duped); close(duped); if(stdinout == 1) { close(2); dup(dupederr); close(dupederr); } return(pipe); }