char *ckzv = "File support, 5A(084), 16 Aug 94"; /* COPYRIGHT NOTICE: Copyright (C) 1985, 1994, 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. */ /* Most recent update: 5A(084), 16 Aug 94, fdc. 1. Fixed zoutdump() to work on Mac System 7.1 by removing "flush_file_in_background()" call == PBFlushFile(). 2. Fixed zopeni() to account for ZRFILE and to return an error if it is called with an unknown file number, rather than pretending it succeeded. */ /* Version 0.91-99: Many changes since those below by many people, 1988-1994: Matthias Aebi, Paul Placeway, Rick Watson, Frank da Cruz, ... */ /* Version 0.9(37) - Paul Placeway at Ohio State, Jan 1988 */ /* reformatted all of the code so that it would be 79 or fewer colums */ /* Version 0.8(35) - Jim Noble at Planning Research Corporation, June 1987. */ /* Ported to Megamax native Macintosh C compiler. */ /* Original work done by Bill Catchings and Bill Schilit at Columbia Univ */ /* using the SUMACC cross compiler, 1984, adapted from UNIX ckufio.c... */ /* File ckmfio -- Kermit file system support for the Macintosh */ /* Definitions of some Unix system commands */ #include "ckuver.h" /* Version herald */ char *ckzsys = HERALD; #define DIRCMDSTR "ls" #define DELCMDSTR "rm" #define SPCCMDSTR "sp" char *DIRCMD = DIRCMDSTR; /* For directory listing */ char *DIRCM2 = DIRCMDSTR; /* For long directory listing */ char *DELCMD = DELCMDSTR; /* For file deletion */ char *SPACMD = SPCCMDSTR; /* Space for all available volumes */ char *TYPCMD = ""; /* For typing a file */ char *SPACM2 = ""; /* For space in specified directory */ char *WHOCMD = ""; /* For seeing who's logged in */ long mac_znextlen = 0L; /* Functions (n is one of the predefined file numbers from ckermi.h): zopeni(n,name) -- Opens an existing file for input. zopeno(n,name) -- Opens a new file for output. zclose(n) -- Closes a file. zchin(n) -- Gets the next character from an input file. zinfill() -- (re) fill file input buffer, return the first character zsout(n,s) -- Write a null-terminated string to output file, buffered. zsoutl(n,s) -- Like zsout, but appends a line terminator. zsoutx(n,s,x) -- Write x characters to output file, unbuffered. zchout(n,c) -- Add a character to an output file, unbuffered. zchki(name) -- Check if named file exists and is readable, return size. zchko(name) -- Check if named file can be created. znewn(name,s) -- Make a new unique file name based on the given name. zdelet(name) -- Delete the named file. zxpand(string) -- Expands the given wildcard string into a list of files. znext(string) -- Returns the next file from the list in "string". zxcmd(cmd) -- Execute the command in a lower fork. zclosf() -- Close input file associated with zxcmd()'s lower fork. zrtol(n1,n2) -- Convert remote filename into local form. zltor(n1,n2) -- Convert local filename into remote form. zchdir(dirnam) -- Change working directory. zhome() -- Return pointer to home directory name string. zkself() -- Kill self, log out own job. */ #include "ckcdeb.h" /* Debug() and tlog() defs */ #include "ckcasc.h" /* ASCII character symbols */ #include "ckcker.h" /* Kermit definitions */ #include "ckmdef.h" /* Common Mac module definitions */ #include "ckmres.h" /* Resource defs */ #include "ckmasm.h" /* Assembler code */ #include "ckmptp.h" /* ckm* Prototypes */ #include "ckmwin.h" #ifdef MPW32 #define hfileInfo hFileInfo /* for compat with newer .h files */ #endif /* MPW32 */ /* (PWP) external def. of things used in buffered file input and output */ extern char *zinbuffer, *zoutbuffer; extern char *zinptr, *zoutptr; extern int zincnt, zoutcnt, what; static long zcnt_written; /* These should all be settable by the File Settings Menu */ #define FS_WIND 1 /* file is a text edit buffer */ #define FS_OPEN 2 /* file has been opened */ #define FS_RSRC 4 /* opened in resource fork */ #define FS_DATA 8 #define FS_PIPE 16 /* file is a memory buffer */ #define FS_MACB 32 /* we are sending a file in MacBinary format */ MACFILE fp[ZNFILS] = { /* File information */ {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL} }; static long iflen = -1; /* Input file length */ static long oflen = -1; /* Output file length */ void mac_setfilflg(binary) int binary; { switch (binary) { case XYFT_M: /* Macbinary */ filargs.filflg &= ~FIL_TEXT; filargs.filflg |= FIL_BINA | FIL_RSRC | FIL_DATA; break; case XYFT_B: /* Binary */ filargs.filflg &= ~FIL_TEXT; filargs.filflg |= FIL_BINA; break; case XYFT_T: /* Text */ filargs.filflg &= ~FIL_BINA; filargs.filflg |= FIL_TEXT; break; } } /* mac_setfilflg() */ mac_setfildflg(binary) int binary; { switch (binary) { case XYFT_M: /* Macbinary */ filargs.filflg &= ~FIL_TEXT; filargs.fildflg &= ~FIL_TEXT; filargs.filflg |= FIL_BINA | FIL_RSRC | FIL_DATA; filargs.fildflg |= FIL_BINA | FIL_RSRC | FIL_DATA; break; case XYFT_B: /* Binary */ filargs.filflg &= ~FIL_TEXT; filargs.fildflg &= ~FIL_TEXT; filargs.filflg |= FIL_BINA; filargs.fildflg |= FIL_BINA; break; case XYFT_T: /* Text */ filargs.filflg &= ~FIL_BINA; filargs.fildflg &= ~FIL_BINA; filargs.filflg |= FIL_TEXT; filargs.fildflg |= FIL_TEXT; break; } } /* mac_setfildflg() */ char printfbuf[256]; /****************************************************************************/ /* Z O P E N I -- Open an existing file for input. * * The file name has been returned from and the volume reference * number set by SFGetFile. * * Returns: * TRUE: file opened ok * FALSE: some error. */ /****************************************************************************/ zopeni (n, name) int n; char *name; { int err; register MACFILE *fpp; if (chkfn(n)) { printerr ("At zopeni file is already open ", n); return (FALSE); } zincnt = 0; /* (PWP) clear buffer input count */ fpp = &fp[n]; if (n == ZCTERM) { /* Terminal open? */ if (chkfn (ZIFILE)) /* Check current ZOFILE */ printerr ("ZIFILE already open...: ", n); fp[ZIFILE].fstatus = FS_WIND; /* redirect... here it is */ fpp->fstatus = FS_WIND; /* Indicate this is open too */ return (conopen()); /* Return from low level open */ } if (n == ZSYSFN) /* trying to open a pipe? */ return (zmxcmd(name)); /* yes... */ if (n == ZIFILE) { /* Opening input file? */ mac_setfilflg(binary); if ((filargs.filflg & (FIL_RSRC|FIL_DATA)) == (FIL_RSRC|FIL_DATA)) { err = macbinopen(name, fpp); /* MacBinary */ if (err == noErr) return (TRUE); else return (ioutil (err)); } else { if (filargs.filflg & FIL_RSRC) /* and they said resource? */ err = OpenRF_rdonly(c2p_tmp(name), filargs.filvol, &fpp->frefnum); else /* else some other channel or data */ err = FSOpen_rdonly (c2p_tmp(name), filargs.filvol, &fpp->frefnum); if (err != noErr) /* check for open error */ return(ioutil (err)); /* failed... */ } } else if (n == ZRFILE) { err = FSOpen_rdonly(c2p_tmp(name), filargs.filvol, &fpp->frefnum); if (err != noErr) /* check for open error */ return(ioutil (err)); /* failed... */ } else return(FALSE); /* Some other kind we don't support */ /* Set flags */ fpp->fstatus = FS_OPEN | ((filargs.filflg & FIL_RSRC) ? FS_RSRC : FS_DATA); GetEOF(fpp->frefnum, &filargs.filsiz); /* Set size for screen */ return (TRUE); /* Return success */ } /* zopeni */ MBHead filHead; static char MBname[64]; void bzero (b, n) register char *b; register int n; { while (n-- > 0) *b++ = 0; } void bcopy (a, b, n) register char *a, *b; register int n; { while (n-- > 0) *b++ = *a++; } /* these next three taken from NCSA Telnet 2.2 */ int GetFileInfo(short vol, char *name, ParamBlockRec *iop) { char thename[64]; strncpy (thename, name, 64); /* make my own copy of this */ thename[63] = '\0'; c2pstr(thename); iop->fileParam.ioNamePtr = (StringPtr) thename; iop->fileParam.ioVRefNum=vol; iop->fileParam.ioFVersNum=iop->fileParam.ioFDirIndex=0; PBGetFInfo(iop, FALSE); return (iop->fileParam.ioResult); } int SetFileInfo(short vol, char *name, ParamBlockRec *iop) { char thename[64]; strncpy (thename, name, 64); /* make my own copy of this */ thename[63] = '\0'; c2pstr(thename); iop->fileParam.ioNamePtr = (StringPtr) thename; iop->fileParam.ioVRefNum=vol; iop->fileParam.ioFVersNum=iop->fileParam.ioFDirIndex=0; PBSetFInfo(iop, FALSE); return (iop->fileParam.ioResult); } int MakeTextFile(short vol, char *name, ParamBlockRec *iop) { GetFileInfo(vol,name,iop); iop->fileParam.ioFlFndrInfo.fdType='TEXT'; iop->fileParam.ioFlFndrInfo.fdCreator='EDIT'; SetFileInfo(vol,name,iop); return (iop->fileParam.ioResult); } int macbinopen(name, fpp) char *name; MACFILE *fpp; { ParamBlockRec finfo; int err; /* open first fork of name for reading and fill input buffer with MacBinary header */ /* save file name for later */ strncpy (MBname, name, 64); MBname[64] = 0; /* clear out the header */ bzero ((char *) &filHead, sizeof(MBHead)); /* put the name into place */ strncpy (&filHead.name[0], name, 64); filHead.name[63] = '\0'; c2pstr(filHead.name); /* get the file info */ if ((err = GetFileInfo( filargs.filvol, name, &finfo)) != noErr) { printerr("macbinopen: problem with GetFileInfo", err); return (err); } bcopy((char *) &finfo.fileParam.ioFlFndrInfo, &filHead.type[0], sizeof(FInfo) ); filHead.protected = (filHead.zero2 & 0x40)?1:0; filHead.zero2 = 0; bcopy((char *) &finfo.fileParam.ioFlLgLen, &filHead.dflen[0], 4); bcopy((char *) &finfo.fileParam.ioFlRLgLen, &filHead.rflen[0], 4); bcopy((char *) &finfo.fileParam.ioFlCrDat, &filHead.cdate[0], 4); bcopy((char *) &finfo.fileParam.ioFlMdDat, &filHead.mdate[0], 4); filargs.filsiz=finfo.fileParam.ioFlLgLen; filargs.rsrcsiz=finfo.fileParam.ioFlRLgLen; #ifdef COMMENT fsize = ((filargs.filsiz + 127) & ~127) + ((filargs.rsrcsiz + 127) & ~127) + 128; #endif bcopy ((char *) &filHead, zinbuffer, 128); /* put header in xfer buffer */ zincnt = 128; /* init buffer to 128 to send */ zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */ if (filargs.filsiz <= 0) { /* no data fork, open the resource fork */ if ((err = OpenRF_rdonly(c2p_tmp(name), filargs.filvol, &fpp->frefnum)) != noErr) { printerr("macbinopen: problem with OpenRF", err); return(err); } fpp->fstatus = FS_OPEN | FS_RSRC | FS_MACB; } else { /* We do the data fork first, then the resource fork */ if ((err = FSOpen_rdonly (c2p_tmp(name), filargs.filvol, &fpp->frefnum)) != noErr) { printerr("macbinopen: problem with FSOpen", err); return(err); } fpp->fstatus = FS_OPEN | FS_DATA | FS_MACB; } return (noErr); } /* is this a MacBinary I header -- see standard-macbinary-ii.txt (on Sumex) */ is_macbinary(h) MBHead h; { long l; if ((h.zero1 != 0) || (h.zero2 != 0) || (h.zero3 != 0)) { printerr("zeros", 0); return (0); } if ((h.name[0] == 0) || (h.name[0] & 0xc0)) { printerr("name length", h.name[0]); return (0); } bcopy (&h.dflen[0], (char *) &l, 4); if ((l < 0) || (l > 0x7fffff)) { printerr("data length", l); return (0); } bcopy (&h.rflen[0], (char *) &l, 4); if ((l < 0) || (l > 0x7fffff)) { printerr("rsrc length", l); return (0); } #ifdef COMMENT /* MacBinary II uses this area for more stuff, so don't check it */ for (i = 2; i < 27; i++) { if (h.filler[i] != 0) { printerr("filler index:", i); return (0); } } #endif /* COMMENT */ return (1); } /****************************************************************************/ /* Z O P E N O -- Open a new file for output. * * Returns: * TRUE: File opened ok * FALSE: some error has occured or channel occupied. * */ /****************************************************************************/ short zopo_vrefnum = 0; /* a hack for where to put the file */ zopeno (n, name, zz, fcb) int n; char *name; struct zattr *zz; struct filinfo *fcb; { #pragma unused (zz, fcb) OSType forktext, authortext; int err; FInfo finfo; register MACFILE *fpp; short the_vrefnum; #ifdef notdef if ((zz != NULL) || (fcb != NULL)) { printerr ("zopeno: zz or fcb not NULL", 0); return(FALSE); } #endif if (chkfn(n)) { printerr ("zopeno - file is already open: ", n); return(FALSE); } fpp = &fp[n]; /* We should also allow a real file here -- the debugging window doesn't do much good if it disappears when Mac Kermit crashes... */ debug(F101,"zopeno n","",n); if (n == ZDFILE) { /* debugging open? */ if (chkfn(n)) /* Check current ZDFILE */ printerr ("Console already open...: ", n); fp[n].fstatus = FS_WIND; /* yes, redirect... here it is */ return(conopen()); /* Return from low level open */ } if (n == ZCTERM || n == ZSTDIO) { /* Terminal open? */ if (chkfn (ZOFILE)) /* Check current ZOFILE */ printerr ("ZOFILE already open...: ", n); fp[ZOFILE].fstatus = FS_WIND; /* yes, redirect... here it is */ fpp->fstatus = FS_WIND; /* Indicate this is open too */ zoutcnt = 0; /* (PWP) reset output buffer */ zoutptr = zoutbuffer; return (conopen ()); /* Return from low level open */ } if (n == ZOFILE) { zoutcnt = 0; /* (PWP) reset output buffer */ zoutptr = zoutbuffer; mac_setfilflg(binary); if ((filargs.filflg & (FIL_RSRC|FIL_DATA)) == (FIL_RSRC|FIL_DATA)) { /* if MacBinary */ debug(F100,"zopeno macbinary","",0); forktext = 'TEXT'; /* a text file, because we don't */ authortext = '????'; /* know what else to do with it. */ } else if (filargs.filflg & FIL_RSRC) { debug(F100,"zopeno resource","",0); forktext = 'APPL'; authortext = '????'; } else { debug(F100,"zopeno data","",0); forktext = 'TEXT'; /* Make fork reflect fork choice */ authortext = 'ttxt'; /* Set creator to TeachText */ /* Was 'MACA' for MacWrite */ } the_vrefnum = filargs.filvol; debug(F101,"zopeno ZOFILE the_vrefnum","",the_vrefnum); } else { /* file, but not the transfer output file */ if (zopo_vrefnum) the_vrefnum = zopo_vrefnum; else the_vrefnum = filargs.filvol; /* same as for transfers */ zopo_vrefnum = 0; /* we've now used this */ debug(F101,"zopeno not ZOFILE the_vrefnum","",the_vrefnum); } if (n == ZSFILE || n == ZPFILE || n == ZTFILE || n == ZDFILE) { forktext = 'TEXT'; authortext = 'ttxt'; } err = Create (c2p_tmp(name), the_vrefnum, authortext, forktext); debug(F101,"zopeno Create err","",err); if (err == dupFNErr) { /* duplicate file? */ debug(F101,"zopeno duplicate file","",err); if (!ioutil (FSDelete (c2p_tmp(name), /* Try to delete it */ the_vrefnum))) { /* checking for failure */ debug(F100,"zopeno delete duplicate failed","",0); return(FALSE); /* failed... */ } /* recreate */ err = Create (c2p_tmp(name), the_vrefnum, authortext, forktext); debug(F101,"zopeno Create 2 err","",err); } if (err != noErr) /* some error? */ return(ioutil(err)); /* yes, do message and return */ if (n == ZOFILE) { /* is it our transferred file? */ /* set file's folder from filargs.filfldr which is either the */ /* applications folder or the settings file folder */ /* read current finder info */ GetFInfo (c2p_tmp(name), the_vrefnum, &finfo); finfo.fdFldr = filargs.filfldr; /* Set new folder */ /* and tell system about it */ SetFInfo (c2p_tmp(name), the_vrefnum, &finfo); debug(F100,"zopeno GetFInfo and SetFInfo done","",0); zcnt_written = 0; /* if we are doing MacBinary format */ if ((filargs.filflg & (FIL_RSRC|FIL_DATA)) == (FIL_RSRC|FIL_DATA)) { /* save file name for later */ bzero (MBname, 64); /* clear name buffer */ strncpy (MBname, name, 64); MBname[64] = 0; /* we delay opening any forks until we have the MacBin header */ fp[n].fstatus = FS_OPEN | FS_MACB; /* neither DATA nor RSRC yet */ return (TRUE); /* done ok */ } else { /* not MacBinary */ if (filargs.filflg & FIL_RSRC) /* Resource fork... */ err = OpenRF (c2p_tmp(name), the_vrefnum, &fpp->frefnum); else /* Data fork or some other file */ err = FSOpen (c2p_tmp(name), the_vrefnum, &fpp->frefnum); } } else err = FSOpen (c2p_tmp(name), the_vrefnum, &fpp->frefnum); debug(F101,"zopeno FSOpen err","",err); if (err != noErr) /* able to open? */ return (ioutil(err)); /* no. fail return now */ if (n == ZOFILE) { /* is it our transferred file? */ fp[n].fstatus = FS_OPEN | ((filargs.filflg & FIL_RSRC) ? FS_RSRC : FS_DATA); } else { fp[n].fstatus = FS_OPEN | FS_DATA; } return(TRUE); /* done ok */ } /* zopeno */ /*********************************************************************** * mbcl_cleanup -- do stuff after closing an open (for recipt) macbinary file. */ mbcl_cleanup() { int err = noErr, e2 = 0; ParamBlockRec finfo; Point old_location; if (!cxseen && !czseen && (zcnt_written > ((filargs.rsrcsiz + 127) & ~127)) || (zcnt_written < filargs.rsrcsiz)) { sprintf(printfbuf, "Resource fork size mismatch: should be %d is %d", filargs.rsrcsiz, zcnt_written); printerr(printfbuf, 0); } if ((err = GetFileInfo(filargs.filvol, MBname, &finfo)) != noErr) { sprintf(printfbuf, "Could not GetFileInfo on \"%s\": error %d", MBname, err); printerr(printfbuf,0); } else { old_location = finfo.fileParam.ioFlFndrInfo.fdLocation; filHead.protected &= 01; /* nuke all but low order bit */ bcopy(&filHead.type[0], (char *) &finfo.fileParam.ioFlFndrInfo, sizeof(FInfo)); bcopy(&filHead.cdate[0], (char *) &finfo.fileParam.ioFlCrDat, 4); bcopy(&filHead.mdate[0], (char *) &finfo.fileParam.ioFlMdDat, 4); /* As per the MacBinary II doc, I clear the following flags: * * 0 - Set if file/folder is on the desktop (Finder 5.0 and later) * 1 - bFOwnAppl (used internally) * 8 - Inited (seen by Finder) * 9 - Changed (used internally by Finder) * 10 - Busy (copied from File System busy bit) */ finfo.fileParam.ioFlFndrInfo.fdFlags &= 0xf8fc; /* set new folder */ /* finfo.fileParam.ioFlFndrInfo.fdFldr = filargs.filfldr; */ finfo.fileParam.ioFlFndrInfo.fdFldr = 0; /* set new folder */ /* old_location */ finfo.fileParam.ioFlFndrInfo.fdLocation = old_location; if (finfo.fileParam.ioFlLgLen != filargs.filsiz) { sprintf(printfbuf, "%s: Data fork size mismatch: should be %d is %d", MBname, filargs.filsiz, finfo.fileParam.ioFlLgLen); printerr(printfbuf, 0); } if (finfo.fileParam.ioFlRLgLen != filargs.rsrcsiz) { sprintf(printfbuf, "%s: Resource fork size mismatch: should be %d is %d", MBname, filargs.rsrcsiz, finfo.fileParam.ioFlRLgLen); printerr(printfbuf, 0); } /* finfo.fileParam.ioFlRLgLen=out->rlen; */ /* finfo.fileParam.ioFlLgLen =out->dlen; */ if ((err = SetFileInfo(filargs.filvol, MBname, &finfo)) != noErr) printerr("Could not SetFileInfo:", err); /* make sure the new data got to disk */ /* try to give it the name encoded in the MacBinary header */ err = Rename(c2p_tmp(MBname), filargs.filvol, c2p_tmp2(filHead.name)); if (err != noErr) screen(SCR_WM, 0, 0l, "Can't rename file to its MacBinary name."); err = FlushVol (NILPTR, filargs.filvol); } return (err); } /****************************************************************************/ /* Z C L O S E -- Close the given file. * * Returns: * TRUE: file closed ok. * FLASE: some error has occured. * */ /****************************************************************************/ zclose (n) int n; { int err = noErr, e2 = 0; register MACFILE *fpp; debug(F101,"zclose n","",n); if (!chkfn(n)) /* is it opened? */ return (FALSE); /* no return now */ debug(F101,"zclose zoutcnt","",zoutcnt); if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */ e2 = zoutdump(); fpp = &fp[n]; if (fpp->fstatus == FS_WIND) { /* is this a window? */ fp[ZCTERM].fstatus = 0; /* yes, clear ZCTERM */ } else if (fpp->fstatus == FS_PIPE) { /* is this a pipe? */ fp[ZSYSFN].fstatus = 0; /* yes, no pipe now, clear ZSYSFN */ } else if (n == ZIFILE || n == ZRFILE) { /* Regular input file */ err = FSClose (fpp->frefnum); /* Use the OS close */ if (err != noErr) printerr("zclose(): problem closing input file:", err); } else if (n == ZOFILE) { /* recieving this file */ debug(F100,"zclose ZOFILE","",0); if (fpp->fstatus & FS_MACB) { /* if MacBinary format */ debug(F100,"zclose ZOFILE macbinary","",0); if (fpp->fstatus & FS_OPEN) { printerr( "zclose(): MacBinary botched: this file should NOT still be open", 0); err = FSClose (fpp->frefnum); /* close it just to be safe */ if (err == noErr) /* and if that worked */ /* PWP: the above if should be ==, NOT != !!!!! */ /* flush buffers in case write worked */ err = FlushVol (NILPTR, filargs.filvol); if (err != noErr) printerr("zclose(): problem closing/flushing output file:", err); } } else { debug(F100,"zclose ZOFILE not macbinary","",0); err = FSClose (fpp->frefnum); /* else use OS close */ debug(F101,"zclose ZOFILE FSClose err","",err); if (err != noErr) printerr("zclose(): problem closing output file:", err); /* flush buffers in case write worked */ err = FlushVol (NILPTR, filargs.filvol); debug(F101,"zclose ZOFILE FlushVol err","",err); if (err != noErr) printerr("zclose(): problem flushing output disk:", err); } if (fpp->fstatus & FS_MACB) { /* if MacBinary format */ err = mbcl_cleanup(); } } else if (n == ZWFILE || /* Output file opened by command */ n == ZPFILE || n == ZSFILE || n == ZTFILE) { if ((err = FSClose (fpp->frefnum)) != noErr) printerr("zclose(): problem closing output file:", err); debug(F100,"zclose calling FlushVol...","",0); /* Flush buffers... */ err = FlushVol(NILPTR, filargs.filvol); /* Doesn't FSClose do this? */ debug(F101,"zclose FlushVol err","",err); if (err != noErr) printerr("zclose(): problem flushing output disk:", err); } else printerr("zclose(): Unknown file number:", n); fpp->fstatus = 0; /* clear out status word */ if (n == ZOFILE || n == ZIFILE) /* turn off both flags */ filargs.filflg &= ~(FIL_RSRC | FIL_DATA); iflen = -1; /* Invalidate file length */ debug(F101,"zclose done e2","",e2); if (e2 < 0) { (void) ioutil (err); return(FALSE); } else return(ioutil (err)); /* Return according to IO operations */ } /* zclose */ /****************************************************************************/ /* Z C H I N -- Get a character from the input file. * * Returns: * 0: Ok * -1: EOF (or other error). * */ /****************************************************************************/ zchin (n, c) int n; char *c; { int err; long rdcnt; /* pascal long */ register MACFILE *fpp; register MACPIPE *pipe; if (n == ZIFILE && (fp[n].fstatus != FS_PIPE)) return (zminchar()); /* (PWP) go through the macro */ if (!chkfn (n)) return (0); fpp = &fp[n]; if (fpp->fstatus == FS_WIND) { /* a window? */ printerr ("zchin called for FS_WIND file: ", n); return (0); } if (fpp->fstatus == FS_PIPE) { /* a pipe? */ pipe = fpp->fpipe; if (pipe->charsleft <= 0) { /* test for characters left */ pipe->currptr = pipe->pipebuf; /* restart at beginning of buffer */ if (pipe->refill != NILPROC) { /* refill the pipe if possible */ /* YUCK - why do a call the hard way when we can just call (pipe->refill)(); */ #ifdef COMMENT saveA0 (); /* push content of A0 to stack */ /* load the content of refill to A0 */ loadA0 ((char *) *(pipe->refill)); execute (); /* call the refill procedure */ restoreA0 (); /* get A0 back from the stack */ #endif /* COMMENT */ (*(pipe->refill))(); /* call the refill procedure */ } else { *(pipe->currptr) = '\0'; /* make it end otherwise */ } } if (*(pipe->currptr) == '\0') /* is this eo-pipe? */ return (-1); /* yes, fail return */ *c = *(pipe->currptr)++; /* read character */ (pipe->charsleft)--; /* decrement characters left */ return (0); /* success */ } rdcnt = 1; err = FSRead (fpp->frefnum, &rdcnt, c); debug(F101,"zchin FSRead err","",err); if (err == eofErr) return (-1); /* Failure return */ return (ioutil(err) ? 0 : -1); /* success or unknown failure */ } /* zchin */ /* 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. */ int zsinl(n,s,x) int n, x; char *s; { int z = 0; char a; debug(F101,"zsinl n","",n); if (chkfn(n) < 1) { /* Make sure file is open */ return(-1); } debug(F101,"zsinl x","",x); 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); } /* * (PWP) (re)fill the buffered input buffer with data. All file * input should go through this routine, usually by calling the * zminchar() macro */ zinfill() { int err; long rdcnt; /* pascal long */ register MACFILE *fpp; char *cp; fpp = &fp[ZIFILE]; /* if not an open file; just get one character */ if (!(fpp->fstatus & FS_OPEN)) { zincnt = 0; if (zchin (ZIFILE, zinbuffer) < 0) return (-1); return (zinbuffer[0] & 0xff); /* so 8-bit chars work */ } rdcnt = INBUFSIZE; err = FSRead (fpp->frefnum, &rdcnt, zinbuffer); zincnt = rdcnt; /* set number actually read */ /* check for any errors */ if ((err != noErr) && (err != eofErr) && (!ioutil(err))) return (-1); /* * PWP: FSRead will return eofErr when it reads the last * partial block of data. If rdcnt > 0, then we still have * the last bit of the file to send out... */ if (err == eofErr) { if (rdcnt == 0) { /* if actual EOF */ return (-1); /* EOF return */ } else { /* last real block */ if (fpp->fstatus & FS_MACB) { /* if MacBinary format */ /* pad out things */ rdcnt = (128 - (filargs.filsiz % 128)) % 128; /* for (cp = &zinbuffer[zincnt]; rdcnt > 0; rdcnt--) */ for (cp = zinbuffer + zincnt; rdcnt > 0; rdcnt--) *cp++ = 0; zincnt = (zincnt + 127) & ~127; /* pad out to 128 bytes */ if (fpp->fstatus & FS_DATA) { /* if we were doing data fork */ fpp->fstatus &= ~FS_DATA; if (filargs.rsrcsiz != 0) { /* if a resource fork */ if ((err = FSClose (fpp->frefnum)) != noErr) { printerr("zinfill: trouble closing data fork:", err); return (-1); } /* open the resource fork 'cause we do that next */ if ((err = OpenRF_rdonly (c2p_tmp(MBname), filargs.filvol, &fpp->frefnum)) != noErr) { printerr("zinfill: trouble opening rsrc fork:", err); return (-1); } /* in case anyone else needs to know */ fpp->fstatus |= FS_RSRC; } } } } } 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. * * Returns: * 0: OK * -1: Error * */ /****************************************************************************/ zsout (n, s) int n; char *s; { long wrcnt; /* pascal long */ if (n == ZCTERM || fp[n].fstatus == FS_WIND) return (conol (s)); wrcnt = (long) strlen (s); return (ioutil (FSWrite (fp[n].frefnum, &wrcnt, s)) ? 0 : -1); } /* zsout */ /****************************************************************************/ /* Z S O U T L -- Write string to file, with line terminator, buffered. * * Returns: * 0: OK * -1: Error * */ /****************************************************************************/ zsoutl (n, s) int n; char *s; { long wrcnt; /* pascal long */ int err; if (n == ZCTERM || fp[n].fstatus == FS_WIND) return (conoll (s)); wrcnt = (long) strlen (s); err = FSWrite (fp[n].frefnum, &wrcnt, s); if (err == noErr) { wrcnt = 2; err = FSWrite (fp[n].frefnum, &wrcnt, "\015"); } return (ioutil (err) ? 0 : -1); } /* zsoutl */ /****************************************************************************/ /* Z S O U T X -- Write x characters to file, unbuffered. * * Returns: * 0: OK * -1: Error */ /****************************************************************************/ zsoutx (n, s, x) int n, x; char *s; { long size; if (n == ZCTERM || fp[n].fstatus == FS_WIND) return (conxo (x, s)); size = x; return (ioutil (FSWrite (fp[n].frefnum, &size, s)) ? 0 : -1); } /* zsoutx */ /****************************************************************************/ /* Z C H O U T -- Add a character to the given file. */ /* */ /* Returns: */ /* 0: OK */ /* -1: Error */ /****************************************************************************/ zchout (int n, char c) { long wrcnt; /* pascal long */ int err; if (n == ZCTERM || fp[n].fstatus == FS_WIND) { conoc (c); /* Then send to console routine */ return (0); /* Then send to console routine */ } if (n == ZOFILE) /* (PWP) just in case */ return (zmchout(c)); wrcnt = 1; err = FSWrite (fp[n].frefnum, &wrcnt, &c); if (err != noErr) /* error occured? */ sstate = 'a'; /* yes, abort protocol */ return (ioutil (err) ? 0 : -1); /* else return code */ } /* zchout */ #ifdef COMMENT /* fdc */ /**************************************************************************** * (PWP) Tell the mac to actually write the contents of the file to disk. * this avoids the problem of the Mac waiting around until it has filled * the disk cache and then flushing all of it at once (which takes several * seconds). */ void flush_file_in_background(int frefnum) { int err; ParamBlockRec info; info.ioParam.ioRefNum = frefnum; /* file descriptor */ info.ioParam.ioCompletion = NULL; /* no completion routine to call */ err = PBFlushFile (&info, TRUE); /* tell system to flush that file */ debug(F101,"flush_file_in_background err","",err); if (err != noErr) /* any error? */ printerr ("flush_file_in_background failed: ", err); /* tell me about it */ } #endif /* COMMENT */ /* (PWP) buffered character output routine to speed up file IO */ zoutdump() { long wrcnt, tmpcnt; /* pascal long */ int err; char *outp; debug(F101," zoutdump: zoutcnt","",zoutcnt); if ((zoutcnt < 0) || (zoutcnt > OBUFSIZE)) { printerr("zoutdump(): zoutcnt out of range", zoutcnt); zoutcnt = 0; zoutptr = zoutbuffer; return (-1); } wrcnt = (long) zoutcnt; if (wrcnt <= 0) return (0); /* nothing to do */ if (fp[ZOFILE].fstatus == FS_WIND) { /* if console output */ conxo(zoutcnt, zoutbuffer); zoutcnt = 0; zoutptr = zoutbuffer; return(0); } outp = zoutbuffer; if (fp[ZOFILE].fstatus & FS_MACB) { /* if MacBinary format */ /* if done with both Data and Rsrc */ if (!(fp[ZOFILE].fstatus & FS_OPEN)) { /* keep track of how much leftover we got */ zcnt_written += wrcnt; zoutcnt = 0; zoutptr = zoutbuffer; return (0); /* toss 'em */ } /* looking for header */ if (!(fp[ZOFILE].fstatus & (FS_RSRC | FS_DATA))) { if (zoutcnt < 128) /* we don't have all of the header yet */ return (0); bcopy(zoutbuffer, (char *) &filHead, 128); if (!is_macbinary(filHead)) { screen(SCR_WM, 0, 0l, "Not a MacBinary file, reverting to binary, data fork"); screen(SCR_AN,0,0l,MBname); /* stop saying MacBinary mode */ err = FSOpen (c2p_tmp(MBname), filargs.filvol, &fp[ZOFILE].frefnum); if (err != noErr) { printerr("zoutdump(): trouble with FSOpen:", err); (void) ioutil(err); return (-1); } fp[ZOFILE].fstatus = FS_OPEN | FS_DATA; /* unset FS_MACB */ goto norm_file; } /* adjust pointers */ wrcnt = zoutcnt - 128; outp = &zoutbuffer[128]; /* get the sizes from header */ bcopy(&filHead.dflen[0], (char *) &filargs.filsiz, 4); bcopy(&filHead.rflen[0], (char *) &filargs.rsrcsiz, 4); if (filargs.filsiz > 0) { /* is there a data fork? */ err = FSOpen (c2p_tmp(MBname), filargs.filvol, &fp[ZOFILE].frefnum); if (err != noErr) printerr("zoutdump(): trouble with FSOpen:", err); fp[ZOFILE].fstatus |= FS_DATA; } else { /* else data, or some other file */ err = OpenRF (c2p_tmp(MBname), filargs.filvol, &fp[ZOFILE].frefnum); if (err != noErr) printerr("zoutdump(): trouble with OpenRF:", err); fp[ZOFILE].fstatus |= FS_RSRC; } if (err != noErr) { sstate = 'a'; /* yes, abort protocol */ return (ioutil (err) ? 0 : -1); } zcnt_written = 0; /* screen(SCR_AN,0,0l,MBname); */ /* Make screen say MacBinary */ if (wrcnt <= 0) { /* if nothing to write */ zoutcnt = 0; zoutptr = zoutbuffer; return (0); /* we are done this time */ } } /* can't be "else if" here, above if may feed this if */ if (fp[ZOFILE].fstatus & FS_DATA) { /* if doing data fork */ if (zcnt_written + wrcnt >= filargs.filsiz) { tmpcnt = wrcnt; /* save old amount of data */ /* figure how how much we really should write */ wrcnt = filargs.filsiz - zcnt_written; err = FSWrite (fp[ZOFILE].frefnum, &wrcnt, outp); zcnt_written += wrcnt; outp += wrcnt; wrcnt = tmpcnt - wrcnt; /* adjust to reflect the write */ /* close data fork, open rsrc fork */ if (err == noErr) err = FSClose (fp[ZOFILE].frefnum); if (err == noErr) /* and if that worked */ err = FlushVol (NILPTR, /* flush OS buffers */ filargs.filvol); if (err == noErr) err = OpenRF (c2p_tmp(MBname), filargs.filvol, /* yes... */ &fp[ZOFILE].frefnum); if (err != noErr) { /* error occured? */ zoutcnt = 0; zoutptr = zoutbuffer; sstate = 'a'; /* yes, cancel protocol */ return (ioutil (err) ? 0 : -1); /* else return code */ } fp[ZOFILE].fstatus &= ~FS_DATA; fp[ZOFILE].fstatus |= FS_RSRC; /* * tmpcnt is now the amount of extra buffer characters we * need to see in order to fill out this 128 byte "block". */ tmpcnt = ((zcnt_written + 127) & ~127) - zcnt_written; if (wrcnt < tmpcnt) { /* mark more padding still needed */ zcnt_written = -tmpcnt + wrcnt; zoutcnt = 0; zoutptr = zoutbuffer; return (0); /* we are done this time */ } /* all the padding is here -- skip over it */ wrcnt -= tmpcnt; outp += tmpcnt; zcnt_written = 0; /* reset for RSRC fork */ /* fall through to RSRC fork if(), below... */ } } /* * Also can't be "else if" here, because above if just might * feed this if too. */ if (fp[ZOFILE].fstatus & FS_RSRC) { /* if doing data fork */ if (zcnt_written < 0) { /* if we need to skip more padding */ outp += -zcnt_written; wrcnt -= -zcnt_written; zcnt_written = 0; } if (zcnt_written + wrcnt >= filargs.rsrcsiz) { tmpcnt = wrcnt; /* save old amount of data */ /* figure how how much we really should write */ wrcnt = filargs.rsrcsiz - zcnt_written; err = FSWrite (fp[ZOFILE].frefnum, &wrcnt, outp); zcnt_written += wrcnt; outp += wrcnt; wrcnt = tmpcnt - wrcnt; /* adjust to reflect the write */ /* close rsrc fork, set flags to toss output */ if (err == noErr) err = FSClose (fp[ZOFILE].frefnum); if (err == noErr) /* and if that worked */ err = FlushVol (NILPTR, /* flush OS buffers */ filargs.filvol); if (err != noErr) { /* error occured? */ zoutcnt = 0; zoutptr = zoutbuffer; sstate = 'a'; /* yes, abort protocol */ return (ioutil (err) ? 0 : -1); /* else return code */ } /* say this file is no longer open */ fp[ZOFILE].fstatus &= ~(FS_OPEN | FS_DATA | FS_RSRC); zcnt_written += wrcnt; /* we keep track of leftovers */ zoutcnt = 0; zoutptr = zoutbuffer; return (0); /* we are done this time */ } } } norm_file: debug(F101," zoutdump: normal file, writing","",wrcnt); err = FSWrite (fp[ZOFILE].frefnum, &wrcnt, outp); debug(F101," zoutdump: FSWrite returned","",err); debug(F101," zoutdump: wrote","",wrcnt); zcnt_written += wrcnt; zoutcnt = 0; zoutptr = zoutbuffer; if (err != noErr) { /* error occured? */ sstate = 'a'; /* yes, abort protocol */ return (ioutil (err) ? 0 : -1); /* else return code */ } #ifdef COMMENT /* Aha! This is what has been crashing Mac Kermit on Power Mac, Mac 660 AV, etc. It is probably something more to do with some particular new System Release, like 7.1.2, than the underlying machine architecture. Removing this seems to do no harm, and allows downloads to work once again. fdc, 16 Aug 94. */ flush_file_in_background(fp[ZOFILE].frefnum); #endif /* COMMENT */ return (0); /* no problems */ } /****************************************************************************/ /* C H K F N -- Internal function to verify file number is ok. * * Returns: * TRUE - file is open * FALSE - file is not open * * Issues an error message if the file number is not in range. * */ /****************************************************************************/ chkfn (n) int n; { if (n < ZNFILS) return ((fp[n].fstatus != 0)); /* if open, fstatus is nonzero */ debug (F101, "chkfn: file number out of range", "", n); printerr ("chkfn - file number not in range: ", n); return (FALSE); /* ugh */ } /* chkfn */ /****************************************************************************/ /* 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 err; ParamBlockRec info; if (strcmp (name, "stdin") == 0) /* stdin is a pipe */ return (PIPESIZE); /* Return size of buffer */ c2pstr (name); /* Convert to a pascal string */ info.fileParam.ioFVersNum = 0; /* No version number */ info.fileParam.ioFDirIndex = 0; /* Use the file name */ info.fileParam.ioNamePtr = (StringPtr) name; /* Point to the file name */ info.fileParam.ioVRefNum = filargs.filvol; /* Volume number */ err = PBGetFInfo (&info, FALSE); /* Get info on file */ p2cstr (name); /* Put the name back */ if (err == fnfErr) /* File not found? */ return (-1); /* Then that is what they want */ if (err != noErr) { /* Any other error? */ printerr ("zchki failed: ", err); /* Tell me about it */ return (-1); } /* if we are doing MacBinary format transfer */ if ((filargs.filflg & (FIL_RSRC|FIL_DATA)) == (FIL_RSRC|FIL_DATA)) { /* * MacBinary size is: * 128 byte header * + data fork, rounded up to the next 128 boundry * + resource fork, rounded up to the next 128 boundry */ iflen = ((info.fileParam.ioFlLgLen + 127) & ~127) + ((info.fileParam.ioFlRLgLen + 127) & ~127) + 128; } else { /* normal (non-MacBinary) file */ iflen = (filargs.filflg & FIL_RSRC) ? /* If thinking about RSRC */ info.fileParam.ioFlRLgLen : /* return that size, */ info.fileParam.ioFlLgLen; /* else DATA */ } /* Kludge to make total length available to outside world */ /* Used (e.g.) by DIRECTORY command */ mac_znextlen = info.fileParam.ioFlRLgLen + info.fileParam.ioFlLgLen; return(iflen); /* Did ok */ } /* zchki */ /****************************************************************************/ /* Z C H K O -- Check if output file can be created. * * Returns * 0: Write OK * -1: write permission for the file should be denied. */ /****************************************************************************/ zchko (name) char *name; /* unused in this */ { #pragma unused (name) /* $$$ but SHOULD it be unused? */ /* No it shouldn't be, because it might include a directory or volume name whose access should be checked. No big deal -- it just means we die ignominiously later on, rather than gracefully right away. */ Str255 volname; ParamBlockRec info; info.volumeParam.ioVolIndex = 0; /* Use the vol ref num only */ info.volumeParam.ioNamePtr = volname; /* Pointer to the volume name */ info.volumeParam.ioVRefNum = filargs.filvol; /* Volume reference number */ if (!ioutil (PBGetVInfo (&info, 0))) /* Get info on vol, synchronously */ return (-1); /* failed... */ if ((info.volumeParam.ioVAtrb & 0x8000) != 0) /* Write locked? */ return(-1); /* yes... */ return(0); /* else success */ } /* zchko */ /****************************************************************************/ /* Z D E L E T -- Delete the named file. * return 0 if successful, -1 if not. */ /****************************************************************************/ int zdelet (name) char *name; { int err; err = remove(name); debug(F101,"zdelet","",err); return(err == 0 ? 0 : -1); } /****************************************************************************/ /* Z R T O L -- Convert remote filename into local form. * * Check here to see if this should go into the resource fork (.rsrc) * or into the data fork (.data). * */ /****************************************************************************/ VOID zrtol (name, name2) char *name, *name2; { strcpy (name2, name); /* copy name to destination */ if (filargs.filflg & (FIL_DODLG)) /* selected by user? */ return; /* won't be called but... */ filargs.filflg &= ~(FIL_RBDT); /* clear out flags */ filargs.filflg |= sfprtol (name2); /* convert name2 and set flags */ #ifdef COMMENT binary = (filargs.filflg & FIL_BINA); /* selected binary mode? */ #endif /* COMMENT */ return; } /* zrtol */ /****************************************************************************/ /* 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[100]; /* buffer for use by zstrip */ VOID zstrip(name,name2) char *name, **name2; { char *cp, *pp; debug(F110,"zstrip before",name,0); pp = work; 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 L T O R -- Convert filename from local format to common form. */ /****************************************************************************/ void zltor(name, name2) char *name, *name2; { int dc = 0; /* Dot counter */ char c; /* For each character from name */ char *pp; /* Pointer to work buffer */ pp = work; /* Point to start of work buffer */ while ((c = *name++) != '\0') { /* Go thru original name */ if (c == ' ') /* Change space to underscore */ *pp++ = '_'; else if (c == ':') /* Colon is the directory seperator */ pp = work; /* Strip pathname */ else if ((c == '.') && (++dc > 1)) *pp++ = 'X'; /* Just 1 dot allowed */ else /* Convert letters to uppercase */ *pp++ = (islower(c)) ? toupper(c) : c; } *pp = '\0'; /* Deposit final null */ pp = work; /* Back to beginning of work buffer */ if (*pp == '.') /* If it starts with a dot, */ *name2++ = 'X'; /* insert an X. */ strcpy(name2,work); /* Copy result to destination. */ if (strlen(name2) == 0) /* Make sure something is there. */ strcpy(name2,"X"); debug(F110," name2",name2,0); /* (debug) */ return; } /* zltor */ static char *mgbufp = NULL; /* extern char *malloc(); */ #ifdef COMMENT #ifndef NOMSEND /* Multiple SEND */ #define MSENDMAX 100 char *msfiles[MSENDMAX]; #endif /* F N P A R S E -- */ /* Argument is a character string containing one or more filespecs. This function breaks the string apart into an array of pointers, one to each filespec, and returns the number of filespecs. Used by server when it receives a GET command to allow it to process multiple file specifications in one transaction. Sets cmlist to point to a list of file pointers, exactly as if they were command line arguments. This version of fnparse treats spaces as filename separators. If your operating system allows spaces in filenames, you'll need a different separator. This version of fnparse mallocs a string buffer to contain the names. It cannot assume that the string that is pointed to by the argument is safe. */ #ifdef MAC /* Filename separator */ #define FNSEP ',' #else #define FNSEP SP #endif fnparse(string) char *string; { char *p, *s; int r = 0; /* Return code */ extern char **cmlist; if (mgbufp) free(mgbufp); /* Free this from last time. */ mgbufp = malloc(strlen(string)+2); if (!mgbufp) { debug(F100,"fnparse malloc error","",0); return(0); } strcpy(mgbufp,string); /* Make a safe copy */ p = s = mgbufp; /* Point to the copy */ r = 0; /* Initialize our return code */ while (*p == SP) p++,s++; /* Skip leading spaces */ while (1) { /* Loop through rest of string */ if (*s == FNSEP || *s == NUL) { /* Look for separator or terminator */ msfiles[r] = p; /* Add this filename to the list */ debug(F111,"fnparse",msfiles[r],r); r++; /* Count it */ if (*s == NUL) break; /* End of string? */ *s++ = NUL; /* No, turn space to NUL */ while (*s == SP) s++; /* Skip repeated spaces */ p = s; /* Start of next name */ continue; } s++; /* Otherwise keep scanning */ } debug(F101,"fnparse r","",r); cmlist = msfiles; return(r); } #endif /* COMMENT */ /****************************************************************************/ /* 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. */ #pragma unused (f, n) return(1); /* Always say OK. */ } /****************************************************************************/ /* Z R E N A M E -- Rename a file */ /****************************************************************************/ /* Call with old and new names */ /* Returns 0 on success, -1 on failure. */ int zrename(old,new) char *old, *new; { int err; err = rename(old,new); debug(F101,"zrenam","",err); return(err == 0 ? 0 : -1); } /****************************************************************************/ /* Z C H D I R -- Change directory or volumes */ /****************************************************************************/ int zchdir (char *dirnam) { int err; int volnum; WDPBRec vinfo; short *FSFCBLen = (short *)0x3F6; if (*FSFCBLen < 0) { /* If no HFS ROM's */ err = SetVol(c2p_tmp(dirnam), 0); volnum = 0; } else { /* Use HFS calls */ c2pstr(dirnam); vinfo.ioVRefNum = 0; /* Open a workimg directory */ vinfo.ioWDDirID = 0; vinfo.ioWDProcID = 'ERIK'; vinfo.ioNamePtr = (StringPtr) dirnam; err = PBOpenWD (&vinfo, FALSE); p2cstr(dirnam); debug(F111,"zchdir dirnam",dirnam,err); if (err != noErr) return (FALSE); err = SetVol(NIL, vinfo.ioVRefNum); volnum = vinfo.ioVRefNum; } if (err == noErr) /* Set default volume */ filargs.filvol = volnum; /* Make it our default */ /* NOTE: If this routine is called from the command window, and the directory-change fails, a bunch of windows pop up on the screen saying "Writing to the console in applications is NOT supported!". Removing the following code makes no difference. Btw, if a CD command is given at the prompt for a valid folder within the current folder, no error boxes appear on the screen, but it's not obvious that any change took place, since the Set Directory dialog still shows the previous folder... */ if (what == W_SEND || what == W_RECV || what == W_REMO) { if (err == noErr) /* show new default volume */ screen (SCR_TN, 0, 0l, dirnam); else screen (SCR_TN, 0, 0l, "Can't set directory"); } return(err == noErr); /* Return ok or fail */ } /* zchdir */ /****************************************************************************/ /* Z H O M E -- Return pointer to user's home directory */ /****************************************************************************/ /* This one should return the name of the boot volume. */ char HomeDir[256]; char * zhome(void) { #ifdef COMMENT short vRef; long Free; int err; err = GetVInfo(3, HomeDir, &vRef, &Free); if (err != 0) return(""); c2pstr(HomeDir); return(HomeDir); #else /* From Rick Watson */ OSErr s; ParamBlockRec pb; pb.ioParam.ioCompletion = 0; pb.ioParam.ioVRefNum = -1; /* -1 == System drive */ pb.ioParam.ioNamePtr = HomeDir; pb.volumeParam.ioVolIndex = 0; /* Use ioVRefNum only */ if ((s = PBGetVInfo(&pb, false)) == noErr) { p2cstr(HomeDir); return(HomeDir); } return(""); #endif /* COMMENT */ } /****************************************************************************/ /* Z G T D I R -- Return pointer to user's current directory */ /****************************************************************************/ char curDir[1025]; /* * fullPath - Rick Watson, U of Texas, August 1994. * Return a full path spec for a directory. * * vref and dirid should specify a directory. * result should point to a char array large enough to store the result. * * WARNING: Under most circumstances, path specs of more than 255 chars won't * work on the Mac. However, it is possible for a full path spec to exceed * 255 chars. This is why you should never use full paths on the Mac. * * This routine is currently coded to handle paths of 1024 chars and * returns -1 if the path is too long. */ static OSErr fullPath (short vref, long dirid, char *result) { OSErr s; char folder[1024]; char path[1024]; CInfoPBRec pb; /* Back up the directory tree until we reach the top... */ path[0] = 0; for (;;) { pb.hFileInfo.ioCompletion = 0; pb.hFileInfo.ioNamePtr = folder; pb.hFileInfo.ioVRefNum = vref; pb.hFileInfo.ioFDirIndex = -1; /* Info about ioDirID */ pb.hFileInfo.ioDirID = dirid; s = PBGetCatInfo(&pb, false); if (s) /*** Assume end of path (fnfErr?), should really check. ***/ break; p2cstr(folder); if (((int) strlen(folder) + (int) strlen(path) + 1 + 1) > sizeof(path)) return -1; /* Path is too long */ strcat(folder, ":"); strcat(folder, path); strcpy(path, folder); dirid = pb.hFileInfo.ioFlParID; } #ifdef COMMENT c2pstr(path); /* Leave it in C format.. */ #endif /* COMMENT */ strcpy(result, path); return(noErr); } char * zgtdir(void) { /* Courtesy of Rick Watson */ OSErr s; /* University of Texas */ short vref; long dirid; #ifdef COMMENT /* This version gets the name of the current folder only ... */ CInfoPBRec ci; if ((s = HGetVol(0, &vref, &dirid)) == noErr) { bzero(&ci, sizeof(ci)); ci.hFileInfo.ioNamePtr = curDir; ci.hFileInfo.ioVRefNum = vref; ci.hFileInfo.ioFDirIndex = -1; ci.hFileInfo.ioDirID = dirid; if ((s = PBGetCatInfo(&ci, false)) == noErr) { p2cstr(curDir); /* Convert to C string */ return(curDir); } } return(""); #else /* This one gets the full path... */ if ((s = HGetVol(0, &vref, &dirid)) == noErr) s = fullPath(vref, dirid, curDir); return((s == noErr) ? curDir : ""); #endif /* COMMENT */ } /****************************************************************************/ /* initialize the fields of a pipe */ /****************************************************************************/ void zinitpipe (MACPIPE *pipe, PFV_NA refillproc) { pipe->refill = refillproc; pipe->currptr = pipe->pipebuf; pipe->charsleft = 0; *(pipe->currptr) = '\0'; } /* zinitpipe */ /****************************************************************************/ /* fill the pipe; last is TRUE if it is the */ /* last time the pipe has to be filled */ /****************************************************************************/ void zfillpipe (MACPIPE *pipe, char *str, Boolean last) { int len; len = strlen (str); if (last) len++; if (len > PIPESIZE) { len = PIPESIZE; if (last) str[PIPESIZE - 1] = '\0'; /* make sure we keep the eop * character */ printerr ("pipe overflow! characters may be lost", 0); } memcpy (pipe->pipebuf, str, len); pipe->charsleft = len; } /* zfillpipe */ /****************************************************************************/ /* sprintf uses 12 kByte. This is the reason to use a simpler formatter here */ /* formatnum returns a right adjusted numberstring padded with fillc */ /* Numbers which do not fit into width are truncated on the left. */ /* Make sure str is at least 'width+1' bytes wide */ /****************************************************************************/ formatnum (long num, char fillc, int width, char *str) { int i; char numstr[12]; /* -2147483647 is the longest string */ /* that can be returned from NumToString */ NumToString (num, numstr); p2cstr(numstr); i = strlen (numstr); while ((i >= 0) && (width >= 0)) str[width--] = numstr[i--]; while (width >= 0) str[width--] = fillc; } /* formatnum */ MACPIPE cmdpipe; int volindex; char spaceheader[60] = "\ Free Name\n\ --------- --------------------\n"; /****************************************************************************/ /* loop through all available volumes and display the space left */ /****************************************************************************/ void zlspace() { int err; Str255 name; long free; char outstr[60]; ParamBlockRec vinfo; name[0] = 0; /* name.length = 0; */ vinfo.volumeParam.ioVolIndex = volindex; vinfo.volumeParam.ioNamePtr = name; err = PBGetVInfo (&vinfo, FALSE); if (err == noErr) { free = vinfo.volumeParam.ioVFrBlk * vinfo.volumeParam.ioVAlBlkSiz; formatnum (free, ' ', 9, outstr); strcat (outstr, " "); p2cstr (&name); debug(F110,"zlspace",name,0); strcat (outstr, (char *) name); strcat (outstr, "\n"); volindex++; zfillpipe (&cmdpipe, outstr, FALSE); } else { /* out of entries */ volindex = 0; zfillpipe (&cmdpipe, "", TRUE); } } /* zlspace */ int fileindex; char dirheader[100] = "\ Size Type Crea Last Modification Name\n\ ------- ---- ---- ----------------- --------------------\n"; /****************************************************************************/ /* loop through all the files on the current volume / directory */ /****************************************************************************/ void zldir() { int err; CInfoPBRec info; WDPBRec vinfo; Str255 name; DateTimeRec dtrec; char outstr[60]; char type[10]; char yearstr[5]; char monthstr[3]; char daystr[3]; char hourstr[3]; char minutestr[3]; unsigned long secs; long size; short *FSFCBLen = (short *) 0x3F6; if (*FSFCBLen < 0) { /* errpkt ("Sorry, the server uses 64 kByte ROM's"); */ errpkt ((CHAR *) "Sorry, the server is not running on an HFS disk"); zfillpipe (&cmdpipe, "", TRUE); return; } PBHGetVol (&vinfo, FALSE); /* loop through all the files starting at the first one */ strcpy(outstr, ""); name[0] = 0; /* name.length = 0; */ info.hFileInfo.ioFDirIndex = fileindex; /* Get next file name */ info.hFileInfo.ioNamePtr = name; /* Point to the empty file name */ info.hFileInfo.ioVRefNum = vinfo.ioWDVRefNum; /* Directory / Volume info.hFileInfo.ioDirID = vinfo.ioWDDirID; /* Directory / Volume number */ err = PBGetCatInfo (&info, FALSE); /* Get info on file number */ if (err == noErr) { if (info.hFileInfo.ioFlAttrib & ioDirMask) { /* a directory */ secs = info.dirInfo.ioDrMdDat; strcpy (type, "#########"); strcpy (outstr, " "); } else { /* a file otherwise */ secs = info.hFileInfo.ioFlMdDat; size = info.hFileInfo.ioFlLgLen + info.hFileInfo.ioFlRLgLen; strcpy (type, " "); memcpy (type, &info.hFileInfo.ioFlFndrInfo.fdType, 4); memcpy (type + 5, &info.hFileInfo.ioFlFndrInfo.fdCreator, 4); formatnum (size, ' ', 7, outstr); } Secs2Date(secs, &dtrec); if(dtrec.year >= 2000) formatnum(dtrec.year-2000, '0', 2, yearstr); else formatnum(dtrec.year-1900, '0', 2, yearstr); formatnum(dtrec.month, '0', 2, monthstr); formatnum(dtrec.day, '0', 2, daystr); formatnum(dtrec.hour, ' ', 2, hourstr); formatnum(dtrec.minute, '0', 2, minutestr); p2cstr(&name); strcat(outstr, " "); strcat(outstr, type); strcat(outstr, " "); strcat(outstr, yearstr); strcat(outstr, "-"); strcat(outstr, monthstr); strcat(outstr, "-"); strcat(outstr, daystr); strcat(outstr, " "); strcat(outstr, hourstr); strcat(outstr, ":"); strcat(outstr, minutestr); strcat(outstr, " "); strcat(outstr, (char *) name); strcat(outstr, "\n"); fileindex++; zfillpipe(&cmdpipe, outstr, FALSE); } else { /* out of entries */ fileindex = 0; zfillpipe(&cmdpipe, "", TRUE); } } /* zldir */ #define CMD_RSRC 1 #define CMD_DATA 2 #define CMD_TEXT 3 #define CMD_BINA 4 #define CMD_DIR 5 #define CMD_DEL 6 #define CMD_SPC 7 #define CMD_UNK 255 static char *maccmdtab[] = { "fork rsrc", "fork data", "mode binary", "mode text", DIRCMDSTR, DELCMDSTR, SPCCMDSTR }; static int mactoktab[] = { CMD_RSRC, CMD_DATA, CMD_BINA, CMD_TEXT, CMD_DIR, CMD_DEL, CMD_SPC }; #define NTOKS (sizeof (mactoktab)/sizeof(int)) /****************************************************************************/ /* Z X C M D -- Run a system command so its output can be read like a file. * * Used on the MAC to implement MAC settings commands -- commands from a * remote system when in server mode that change internal variables. * */ /****************************************************************************/ int zmxcmd (comand) char *comand; { int sc; char theStr[120]; int retCd; fp[ZIFILE].fstatus = FS_PIPE; /* set input from pipe */ fp[ZIFILE].fpipe = &cmdpipe;/* init pointer to command pipe */ switch (sc = getcmd (comand)) { case CMD_RSRC: case CMD_DATA: zinitpipe (&cmdpipe, (PFV_NA) 0); zfillpipe (&cmdpipe, "Default Fork set OK\n", TRUE); filargs.filflg &= ~(FIL_RSRC | FIL_DATA); /* turn off */ filargs.filflg |= (sc == CMD_RSRC) ? FIL_RSRC : FIL_DATA; return (TRUE); /* ok */ case CMD_TEXT: case CMD_BINA: zinitpipe (&cmdpipe, (PFV_NA) 0); zfillpipe (&cmdpipe, "Default Mode set OK\n", TRUE); filargs.filflg &= ~(FIL_TEXT | FIL_BINA); filargs.filflg |= (sc == CMD_BINA) ? FIL_BINA : FIL_TEXT; return (TRUE); /* ok */ case CMD_DIR: fileindex = 1; /* start at the first file on */ zinitpipe (&cmdpipe, zldir); zfillpipe (&cmdpipe, dirheader, FALSE); /* the current volume / directory */ return (TRUE); /* always ok */ case CMD_DEL: strcpy (theStr, comand + strlen (DELCMDSTR)); /* the filename */ /* immediately */ retCd = zdelet (theStr); /* follows the command name */ if (retCd) { zinitpipe (&cmdpipe, (PFV_NA) 0); strcat (theStr, " deleted."); zfillpipe (&cmdpipe, theStr, true); } return (retCd); case CMD_SPC: volindex = 1; /* start with the first volume */ zinitpipe (&cmdpipe, zlspace); /* init pipe for space listing */ zfillpipe (&cmdpipe, spaceheader, FALSE); /* copy the header to * the pipe */ return (TRUE); /* always ok */ default: return (FALSE); /* fail, unknown */ } } /* zxcmd */ /****************************************************************************/ /* Z X C M D - Run a command and send the result back through the "pipe". */ /****************************************************************************/ zxcmd(int filnum, char *comand) { #pragma unused (filnum) return(zmxcmd(comand)); } /****************************************************************************/ /****************************************************************************/ getcmd (cmd) char *cmd; { int k; for (k = 0; k < NTOKS; k++) if (strncmp (maccmdtab[k], cmd, strlen (maccmdtab[k])) == 0) return (mactoktab[k]); /* and return ID */ return (CMD_UNK); /* else unknown */ } /* getcmd */ /****************************************************************************/ /* Z C L O S F - wait for the child fork to terminate and close the pipe. */ /****************************************************************************/ zclosf () { return; } /* zclosf */ int zindex; int zfiles; char *zname; static char znm_storage[64]; /****************************************************************************/ /* Z X P A N D -- Expand a wildcard string into an array of strings * * Returns the number of files that match fn, with data structures set up * so that first file (if any) will be returned by the next znext() call. */ /****************************************************************************/ zxpand (fn) char *fn; { int err; ParamBlockRec info; zfiles = 1; debug(F110," zxpand fn",fn, 0); if ((filargs.filflg & FIL_ALLFL) || /* all files check box on or */ (strcmp (fn, ":") == 0)) { /* server 'get' with filname = ':' */ /* the ioVNmFls field of the VolumeParam returns the number of files */ /* * !and! directories after PBGetInfo. This is why we have to count * here. */ info.fileParam.ioFVersNum = 0; /* No version number */ info.fileParam.ioNamePtr = NIL; /* Point to the file name */ info.fileParam.ioVRefNum = filargs.filvol; /* Volume number */ do { info.fileParam.ioFDirIndex = zfiles; /* Get next file name */ err = PBGetFInfo (&info, FALSE); /* Get info on file */ zfiles++; } while (err == noErr); zname = NIL; /* no specific file */ zfiles -= 2; /* we counted 2 too high */ } else { strncpy(znm_storage, fn, 63); /* copy fn to local storage */ znm_storage[63] = '\0'; /* string paranoia */ zname = znm_storage; /* keep a pointer to that name */ } zindex = 0; /* init the files sent counter */ return (zfiles); } /* zxpand */ /****************************************************************************/ /* 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; { int err; Str255 name; ParamBlockRec info; zindex++; /* next file */ if (zindex > zfiles) return (0); /* no more files */ if (zname != NIL) strcpy (fn, zname); /* Get the file's name */ else { info.fileParam.ioFVersNum = 0; /* No version number */ info.fileParam.ioFDirIndex = zindex; /* Get next file name */ info.fileParam.ioNamePtr = name; /* Point to the file name */ /* VolRefNum of the selected folder */ info.fileParam.ioVRefNum = filargs.filvol; err = PBGetFInfo (&info, FALSE); /* Get info on file */ if (err == noErr) { p2cstr (&name); strcpy (fn, (char *) name); /* Return the file's name */ *filargs.filrem = '\0'; /* reset remote name for folder * transfer */ } else { printerr ("Error on reading next file name: ", err); return (0); } } return (1); /* fn contains the next file */ } /* znext */ /****************************************************************************/ /* Z N E W N -- Make a new name for the given file */ /****************************************************************************/ VOID znewn (fn, s) char *fn, **s; { char *extp, *tmpnam(char *); int ver; static char buf[70]; /* Enough for a Mac filename */ char *p; p = buf; strncpy(p, fn, 59); /* copy in the name, no long names! */ extp = p + strlen(p); /* find position of extension */ *extp++ = '.'; /* add in the dot now */ for (ver = 0; ver < 99; ver++) { /* I'll try this many names */ NumToString ((long) ver, extp); /* add in the number */ p2cstr(extp); if (zchki(p) == -1) { /* is this file known? */ *s = buf; /* no, we can use this name. */ return; } } p = buf; /* Failed, use tmpnam(). */ *s = tmpnam(p); return; } /* znewn */ /* I S W I L D */ /* * return true (1) if the file spec contains any Kermit wild cards */ iswild(filespec) char *filespec; { #pragma unused (filespec) return (0); /* MacKermit doesn't do wild cards yet */ } /* 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; { long k; k = iflen % 1024L; /* File length in K */ 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 = ""; xx->date.len = 0; /* File creation date */ 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 = "A3"; /* (A3: Apple Macintosh) */ 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); } /* Find initialization file. */ zkermini(line,rcflag, kermrc) char *line; int rcflag; char *kermrc; { /* nothing yet... this function added for benefit of VMS Kermit. */ #pragma unused (rcflag, kermrc) short vRefNum; short takeFRefNum; /* file reference number of the take file */ Str255 volName; OSErr err; GetVol (&volName, &vRefNum); err = FSOpen ("\pKermit Takefile", vRefNum, &takeFRefNum); /* try to open the take file */ if (err == noErr) { err = FSClose (takeFRefNum); /* else use OS close */ if (err != noErr) printerr("zclose(): problem closing Kermit Takefile:", err); else strcpy(line, "Kermit Takefile"); } return(0); } /****************************************************************************/ /* zkself() - Kill self (reboot). On other machines does a logout. * Flush volumes and reboot. Called by remote BYE. * */ /****************************************************************************/ zkself () { DrvQEl *drvqe; Str255 vname; long vfreeb; short vrefnum; int err; /* handle on drive q */ for (drvqe = (DrvQEl *) ((QHdr *) GetDrvQHdr ())->qHead; drvqe != NULL; /* while still something */ drvqe = (DrvQEl *) drvqe->qLink) { /* step to next for each drive */ err = GetVInfo (drvqe->dQDrive, &vname, &vrefnum, &vfreeb); if (err == noErr) err = FlushVol (NILPTR, vrefnum); /* flush the volume given * refnum */ else if (err != nsvErr) screen (SCR_TN,0,0l,"Remote cmd: GetVinfo returned unknown code"); } doclean (); /* clean up before leaving */ doexit(GOOD_EXIT, -1); } /* zkself */ #ifdef COMMENT /* now in ckuusx.c */ char optbuf[50]; /* used for MAIL and REMOTE PRINT options */ #endif /* COMMENT */ zmail(p,f) char *p; char *f; { /* Send file f as mail to address p */ #pragma unused (p, f) screen (SCR_WM,0,0l,"There is no mail support in Mac Kermit."); } zprint(p,f) char *p; char *f; { /* Print file f with options p */ #pragma unused (p, f) screen (SCR_WM,0,0l,"There is no printer support in Mac Kermit yet."); } struct { int errnum; char *errstr; } ioerrs[] = { { dirFulErr, "Directory is full" }, { dskFulErr, "Disk is full" }, { wPrErr, "Diskette is write protected" }, { fLckdErr, "File is software locked" }, { vLckdErr, "Volume is software locked" }, { fBsyErr, "File is busy" }, { opWrErr, "File is already open with write permission" }, { fnfErr, "File does not exist" }, { 0, NILPTR } }; /****************************************************************************/ /* ioutil - handle the result from an IO call, checking for an * error return and displaying an appropriate error * message. Returns TRUE if no error occured, FALSE * otherwise. */ /****************************************************************************/ int ioutil (err) int err; { int e; if (err == noErr) return (TRUE); for (e = 0; ioerrs[e].errnum != 0 && ioerrs[e].errnum != err; e++); if (ioerrs[e].errstr == NILPTR) /* anything there? */ printerr ("Unknown IO error: ", err); else printerr (ioerrs[e].errstr, 0); return (FALSE); } /* ioutil */ /**************************************************************************** * OpenRF_rdonly() -- An interface just like OpenRF, but open for read only. ****************************************************************************/ OSErr OpenRF_rdonly (ConstStr255Param fileName, short vRefNum, short *refNum) { ParamBlockRec pb; if (!fileName) DebugStr("\pfileName == NULL"); if (!refNum) DebugStr("\prefNum == NULL"); pb.ioParam.ioCompletion = (ProcPtr) 0; pb.ioParam.ioNamePtr = (StringPtr) fileName; pb.ioParam.ioVRefNum = vRefNum; pb.ioParam.ioVersNum = 0; pb.ioParam.ioPermssn = fsRdPerm; pb.ioParam.ioMisc = (Ptr) 0; PBOpenRF (&pb, 0); *refNum = pb.ioParam.ioRefNum; return pb.ioParam.ioResult; } /* OpenRF_rdonly */ /**************************************************************************** * FSOpen_rdonly() -- An interface just like FSOpen, but open for read only. ****************************************************************************/ OSErr FSOpen_rdonly (ConstStr255Param fileName, short vRefNum, short *refNum) { ParamBlockRec pb; if (!fileName) DebugStr("\pfileName == NULL"); if (!refNum) DebugStr("\prefNum == NULL"); pb.ioParam.ioCompletion = (ProcPtr) 0; pb.ioParam.ioNamePtr = (StringPtr) fileName; pb.ioParam.ioVRefNum = vRefNum; pb.ioParam.ioVersNum = 0; pb.ioParam.ioPermssn = fsRdPerm; pb.ioParam.ioMisc = (Ptr) 0; PBOpen (&pb, 0); *refNum = pb.ioParam.ioRefNum; return pb.ioParam.ioResult; } /* FSOpen_rdonly */ extern short dfltVol; short tlogfile; char tlogname[] = "Kermit Transaction"; /****************************************************************************/ /****************************************************************************/ opentlog() { return (openlogfile("Transaction log name:", tlogname, &tlogfile, ZTFILE)); } /****************************************************************************/ /****************************************************************************/ closetlog() { FSClose (tlogfile); FlushVol (NIL, dfltVol); tlogfile = 0; } /* closetlog */ #ifdef COMMENT /****************************************************************************/ /* This is exactly like the REAL tlog() in ckuusx.c, except it uses "\r" instead of "\n". It would be a good idea to merge the two... */ /****************************************************************************/ #ifdef TLOG #define TBUFL 300 /* T L O G -- Log a record in the transaction file */ /* Call with a format and 3 arguments: two strings and a number: f - Format, a bit string in range 0-7, bit x is on, arg #x is printed. s1,s2 - String arguments 1 and 2. n - Int, argument 3. */ VOID tlog(f,s1,s2,n) int f; long n; char *s1, *s2; { char s[TBUFL]; char *sp = s; int x; if (!tralog) return; /* If no transaction log, don't */ switch (f) { case F000: /* 0 (special) "s1 n s2" */ if (strlen(s1) + strlen(s2) + 15 > TBUFL) sprintf(sp,"?T-Log string too long\r"); else sprintf(sp,"%s %ld %s\r",s1,n,s2); if (zsout(ZTFILE,s) < 0) tralog = 0; break; case F001: /* 1, " n" */ sprintf(sp," %ld\r",n); if (zsout(ZTFILE,s) < 0) tralog = 0; break; case F010: /* 2, "[s2]" */ x = strlen(s2); if (s2[x] == '\n' || s2[x] == '\r') s2[x] = '\0'; if (x + 6 > TBUFL) sprintf(sp,"?T-Log string too long\r"); else sprintf(sp,"[%s]\r",s2); if (zsout(ZTFILE,"") < 0) tralog = 0; break; case F011: /* 3, "[s2] n" */ x = strlen(s2); if (s2[x] == '\n' || s2[x] == '\r') s2[x] = '\0'; if (x + 6 > TBUFL) sprintf(sp,"?T-Log string too long\r"); else sprintf(sp,"[%s] %ld\r",s2,n); if (zsout(ZTFILE,s) < 0) tralog = 0; break; case F100: /* 4, "s1" */ if (zsoutl(ZTFILE,s1) < 0) tralog = 0; break; case F101: /* 5, "s1: n" */ if (strlen(s1) + 15 > TBUFL) sprintf(sp,"?T-Log string too long\r"); else sprintf(sp,"%s: %ld\r",s1,n); if (zsout(ZTFILE,s) < 0) tralog = 0; break; case F110: /* 6, "s1 s2" */ x = strlen(s2); if (s2[x] == '\n' || s2[x] == '\r') s2[x] = '\0'; if (strlen(s1) + x + 4 > TBUFL) sprintf(sp,"?T-Log string too long\r"); else sprintf(sp,"%s %s\r",s1,s2); if (zsout(ZTFILE,s) < 0) tralog = 0; break; case F111: /* 7, "s1 s2: n" */ x = strlen(s2); if (s2[x] == '\n' || s2[x] == '\r') s2[x] = '\0'; if (strlen(s1) + x + 15 > TBUFL) sprintf(sp,"?T-Log string too long\r"); else sprintf(sp,"%s %s: %ld\r",s1,s2,n); if (zsout(ZTFILE,s) < 0) tralog = 0; break; default: sprintf(sp,"\r?Invalid format for tlog() - %ld\r",n); if (zsout(ZTFILE,s) < 0) tralog = 0; } } #endif /* TLOG */ #endif /* COMMENT */ short slogfile; char slogname[] = "Kermit Session"; /****************************************************************************/ /****************************************************************************/ openslog () { return (openlogfile("Session log name:", slogname, &slogfile, ZSFILE)); } /****************************************************************************/ /****************************************************************************/ openlogfile(prompt, name, fdp, n) char *prompt, *name; short *fdp; int n; /* openlogfile */ { int err; SFReply sfr; /* holds file info */ Point where; SetPt(&where, 75, 80); SFPutFile (where, c2p_tmp(prompt), c2p_tmp2(name), (DlgHookProcPtr) NILPROC, &sfr); if (!sfr.good) /* if canceled */ return (0); err = Create (&sfr.fName, sfr.vRefNum, 'ttxt', 'TEXT'); if (err != dupFNErr) if (!ioutil (err)) return (0); err = FSOpen (&sfr.fName, sfr.vRefNum, fdp); /* open the logfile */ if (!ioutil (err)) return (0); fp[n].frefnum = *fdp; /* let normal kermit z- routines know about */ /* the file descriptor */ SetFPos (*fdp, fsFromLEOF, 0); /* set file pointer to eof */ return (1); } /* openslog */ /****************************************************************************/ /****************************************************************************/ closeslog() { int err; err = FSClose (slogfile); if (!ioutil (err)) return (0); FlushVol (NIL, dfltVol); slogfile = 0; } /* closeslog */ /****************************************************************************/ /* write a maximum of n characters from s to the session log file */ /* skip all trailing blanks */ /****************************************************************************/ slog (s, n) char *s; int n; { char *c; long count; short fn; fn = slogfile ? slogfile : fp[ZSFILE].frefnum; if (!fn) return; /* skip all non visible characters */ for (c = s + n - 1; (*c <= ' ') && (c >= s); c--); /* adjust count and write to file */ count = (long) (c - s + 1); FSWrite (fn, &count, s); /* write a cr at end of line */ count = 1; FSWrite (fn, &count, "\015"); } /* slog */ char plogname[] = "Kermit Packets"; /****************************************************************************/ /****************************************************************************/ openplog() { return (openlogfile("Packet log name:", plogname, &fp[ZPFILE].frefnum, ZPFILE)); } /****************************************************************************/ /****************************************************************************/ closeplog () { FSClose (fp[ZPFILE].frefnum); FlushVol (NIL, dfltVol); } /* closeplog */ /****************************************************************************/ /****************************************************************************/ opendlog () { /* debugging output goes to the console for the moment */ return (zopeno (ZDFILE, "Debugging log file", NULL, NULL)); } /****************************************************************************/ /****************************************************************************/ closedlog () { return (zclose(ZDFILE)); } /* closedlog */ /****************************************************************************/ /****************************************************************************/ char * tilde_expand(dirname) char *dirname; { #pragma unused (dirname) /* there really isn't any concept of "user's home dir" on the Mac */ return(""); } /* 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. */ zstime(f,yy,x) char *f; struct zattr *yy; int x; { /* $$$ Fill me in */ } #ifdef COMMENT /* This might work... */ int zfseek(long pos) { debug(F101,"zfseek","",pos); return(fseek(fp[ZIFILE], pos, SEEK_SET)); /* Or maybe... SetFPos(refnum, (int) pos, fsFromStart); But pos has to be an int, so how do we handle huge numbers?... */ } #endif /* COMMENT */ #define WINDOW_MAGIC 0xFFF0 /* hopefully unused value */ #define WINDOW_REFNUM ((short) -6) /* refnum -6 should be unused since it is the modem port */ /* * mac_fopen * Open a window if special filename. * * In MPW, we use these fields in the FILE struct: * _ptr - pointer to cmdw * _size - character offset of current position * _file - holds our magic refnum (WINDOW_REFNUM) * _flag - holds our magic flag identifier (WINDOW_MAGIC) * * But in Think C, we use these fields in the FILE struct: * ptr - pointer to cmdw * size - character offset of current position * refnum - holds our magic refnum (WINDOW_REFNUM) * window - holds our magic flag identifier (WINDOW_MAGIC) */ FILE *mac_fopen (char *filename, char *mode) { int id; struct cmdw *cmdw; extern struct cmdw *cmdwl; FILE *stream; if (strncmp(filename, "%%%", 3) != 0) /* if normal open */ return(fopen(filename, mode)); /* * Find the window */ id = atoi(&filename[3]); for (cmdw = cmdwl; cmdw; cmdw = cmdw->next) if (id == cmdw->id) break; if (!cmdw) return NULL; /* * Allocate and preset FILE struct */ stream = (FILE *)malloc(sizeof(FILE)); if (!stream) { printfalert("mac_fopen: no memory for FILE"); return NULL; } #ifdef MPW stream->_flag = WINDOW_MAGIC; stream->_file = WINDOW_REFNUM; stream->_size = 0; stream->_ptr = (unsigned char *)cmdw; #else /* !MPW */ #ifdef THINK_C stream->window = (void *) WINDOW_MAGIC; stream->refnum = WINDOW_REFNUM; stream->size = 0; stream->ptr = (unsigned char *)cmdw; #else /* THINK_C */ YOU MUST FIGURE OUT HOW TO DO THE SAME THING FOR NON-MPW ENVIRONMENTS!!! #endif /* THINK_C */ #endif /* MPW */ cmdw->flags |= CMDWF_FOPEN; return(stream); } /* * mac_fclose */ int mac_fclose (FILE *stream) { struct cmdw *cmdw; #ifdef MPW if ((stream->_flag != WINDOW_MAGIC) || (stream->_file != WINDOW_REFNUM)) return(fclose(stream)); cmdw = (struct cmdw *)stream->_ptr; #else /* !MPW */ #ifdef THINK_C if ((stream->window != (void *) WINDOW_MAGIC) || (stream->refnum != WINDOW_REFNUM)) return(fclose(stream)); cmdw = (struct cmdw *)stream->ptr; #else /* THINK_C */ YOU MUST FIGURE OUT HOW TO DO THE SAME THING FOR NON-MPW ENVIRONMENTS!!! #endif /* THINK_C */ #endif /* MPW */ cmdw->flags &= ~CMDWF_FOPEN; /* clear file open */ free(stream); return 0; } /* * mac_rewind */ void mac_rewind (FILE *stream) { #ifdef MPW if ((stream->_flag != WINDOW_MAGIC) || (stream->_file != WINDOW_REFNUM)) { rewind(stream); return; } stream->_size = 0; #else /* !MPW */ #ifdef THINK_C if ((stream->window != (void *) WINDOW_MAGIC) || (stream->refnum != WINDOW_REFNUM)) { rewind(stream); return; } stream->size = 0; #else /* THINK_C */ YOU MUST FIGURE OUT HOW TO DO THE SAME THING FOR NON-MPW ENVIRONMENTS!!! #endif /* THINK_C */ #endif /* MPW */ } /* * mac_fgets */ char *mac_fgets (char *s, int n, FILE *stream) { struct cmdw *cmdw; CharsHandle h; long len; char *cp; unsigned short fpos; int nstored; #ifdef MPW if ((stream->_flag != WINDOW_MAGIC) || (stream->_file != WINDOW_REFNUM)) return (fgets(s, n, stream)); cmdw = (struct cmdw *)stream->_ptr; fpos = stream->_size; /* file position */ #else /* !MPW */ #ifdef THINK_C if ((stream->window != (void *) WINDOW_MAGIC) || (stream->refnum != WINDOW_REFNUM)) return (fgets(s, n, stream)); cmdw = (struct cmdw *)stream->ptr; fpos = stream->size; /* file position */ #else /* THINK_C */ YOU MUST FIGURE OUT HOW TO DO THE SAME THING FOR NON-MPW ENVIRONMENTS!!! #endif /* THINK_C */ #endif /* MPW */ h = TEGetText(cmdw->teh); len = (*cmdw->teh)->teLength; cp = (char *)*h; /* pointer to chars in window */ if (fpos >= len) /* if already eof */ return NULL; /* * Transfer characters until n-1 chars have been transfered, * a newline has been transferred, or we have reached the * end of the window. Terminate the buffer with a null. */ nstored = 0; while (fpos < len) { if (nstored >= n-1) break; s[nstored] = cp[fpos++]; if (s[nstored++] == '\n') break; } s[nstored++] = '\0'; /* terminate string */ #ifdef MPW stream->_size = fpos; /* remember position */ #else /* !MPW */ #ifdef THINK_C stream->size = fpos; /* remember position */ #else /* THINK_C */ YOU MUST FIGURE OUT HOW TO DO THE SAME THING FOR NON-MPW ENVIRONMENTS!!! #endif /* THINK_C */ #endif /* MPW */ return s; } /* * mac_feof */ int mac_feof (FILE *stream) { struct cmdw *cmdw; long len; #ifdef MPW if ((stream->_flag != WINDOW_MAGIC) || (stream->_file != WINDOW_REFNUM)) return(feof(stream)); cmdw = (struct cmdw *)stream->_ptr; #else /* !MPW */ #ifdef THINK_C if ((stream->window != (void *) WINDOW_MAGIC) || (stream->refnum != WINDOW_REFNUM)) return(feof(stream)); cmdw = (struct cmdw *)stream->ptr; #else /* THINK_C */ YOU MUST FIGURE OUT HOW TO DO THE SAME THING FOR NON-MPW ENVIRONMENTS!!! #endif /* THINK_C */ #endif /* MPW */ len = (*cmdw->teh)->teLength; #ifdef MPW if (stream->_size >= len) return TRUE; else return FALSE; #else /* !MPW */ #ifdef THINK_C if (stream->size >= len) return TRUE; else return FALSE; #else /* THINK_C */ YOU MUST FIGURE OUT HOW TO DO THE SAME THING FOR NON-MPW ENVIRONMENTS!!! #endif /* THINK_C */ #endif /* MPW */ } /* * Junk so Emacs will set local variables to be compatible with Mac/MPW. * Should be at end of file. * This module uses 8 char tabs * * Local Variables: * tab-width: 8 * End: */