char *ckxv = "Communications I/O, 5A(084), 8 Feb 96"; /* C K M T I O -- interrupt, console, and port functions for Mac Kermit */ /* COPYRIGHT NOTICE: Copyright (C) 1985, 1996, 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. */ /* Variables: dftty -- Pointer to default tty name string, like "/dev/tty". dfloc -- 0 if dftty is console, 1 if external line. dfprty -- Default parity dfflow -- Default flow control ckxech -- Flag for who echoes console typein: 1 - The program (system echo is turned off) 0 - The system (or front end, or terminal). functions that want to do their own echoing should check this flag before doing so. Functions for assigned communication line (either external or console tty): ttopen(ttname,local,mdmtyp,timo) -- Open the named tty for exclusive access. ttclos() -- Close & reset the tty, releasing any access lock. ttpkt(speed,flow) -- Put the tty in packet mode and set the speed. ttvt(speed,flow) -- Put the tty in virtual terminal mode. ttinl(dest,max,timo) -- Timed read line from the tty. ttinc(timo) -- Timed read character from tty. ttchk() -- See how many characters in tty input buffer. ttxin(n,buf) -- Read n characters from tty (untimed). ttol(string,length) -- Write a string to the tty. ttoc(c) -- Write a character to the tty. ttflui() -- Flush tty input buffer. Functions for console terminal: congm() -- Get console terminal modes. concb() -- Put the console in single-character wakeup mode with no echo. conbin() -- Put the console in binary (raw) mode. conres() -- Restore the console to mode obtained by congm(). conoc(c) -- Unbuffered output, one character to console. conol(s) -- Unbuffered output, null-terminated string to the console. conxo(n,s) -- Unbuffered output, n characters to the console. conchk() -- Check if characters available at console. coninc() -- Get a character from the console. conint() -- Enable terminal interrupts on the console. connoi() -- Disable terminal interrupts on the console. Time functions msleep(m) -- Millisecond sleep ztime(&s) -- Return pointer to date/time string */ #include #include #include #include "ckcdeb.h" /* Formats for debug() */ #include "ckcker.h" /* kermit defs */ #include "ckmdef.h" /* macintosh defs */ #include "ckcasc.h" #include "ckmwin.h" #include "ckmcon.h" #include "ckmptp.h" /* ckm* Prototypes */ #include "ckuusr.h" #include "ckmres.h" char default_port_name[256]; char *dftty = default_port_name; /* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */ int dfloc = 1; /* Other defaults */ int dfprty = 0; /* Default parity */ int ttprty = 0; /* Parity in use. */ long ttspeed = -1; /* For saving speed */ int dfflow = 1; /* Xon/Xoff flow control */ int ttflow = -9; /* For saving flow */ int fDTR = 0; /* DTR input flow control */ int fCTS = 0; /* CTS output flow control */ int fInX = 0; /* send xoff when buffer almost full */ int backgrd = 0; /* a Mac is allways in foreground */ /* Local variables */ int drop_dtr = 0; /* drop DTR on Quit */ /* buffer and pointer for input processing */ static char *my_input_buf; /* we give this to the serial driver queue */ /* Private buffer for myread() and its companions. Not for use by anything * else. ttflui() is allowed to reset them to initial values. ttchk() is * allowed to read my_count. * * my_item is an index into mybuf[]. Increment it *before* reading mybuf[]. * * A global parity mask variable could be useful too. We could use it to * let myread() strip the parity on its own, instead of stripping sign * bits as it does now. */ /* #define MYBUFLEN 256 */ #define MYBUFLEN 2048 static unsigned char *mybufp; /* Buffer, including push back */ static int my_count = 0; /* Number of chars still in mybuf */ static int my_item = -1; /* Last index read from mybuf[] */ #ifdef COMMENT #define TTBUFL 200 /* good size (it's RBUFL guys!) */ static unsigned char *ttbuf; #endif static char *tto_buf; /* output buffer for ttol() */ static ParamBlockRec iopb; /* paramater block for ttol() */ static long tto_startticks; /* when we started the output */ #define XOFFTIMEO 480L /* a timeout of 8 seconds (in ticks) */ short dfltVol; /* Volume for Take- and Log-File */ extern Boolean have_128roms; /* True if we are a Plus or better */ extern char ttname[]; extern SIGTYP (*alarmfunc)(); extern SIGTYP (*intfunc)(); typedef unsigned long ulong; extern ulong alarmtime; /* Global alarm time */ extern int pflag; static void ucharout (char c); /****************************************************************************/ /* S Y S I N I T -- System-dependent program initialization. */ /****************************************************************************/ sysinit() { ParamBlockRec pb; mac_init (); /* Set up the Mac */ /* Get the default volume reference number for Take- and Log-Files */ pb.volumeParam.ioNamePtr = NIL; PBGetVol (&pb, FALSE); dfltVol = pb.volumeParam.ioVRefNum; findfinderfiles(); /* See if file was selected */ /* * If we did not open a port in findfinderfiles(), select the default port * and open it now. */ if (innum == 0) { default_serial_port(default_port_name); dftty = default_port_name; port_open(dftty); parity = DEFPAR; if (!setserial (innum, outnum, DSPEED, DEFPAR)) /* set speed parity */ macfatal("Couldn't set serial port to default speed",0); (void) sershake(flow); ttres(); /* (PWP) set up flow control for interactive use */ } mac_post_load_init(); /* Show the terminal window etc */ return(0); } /* sysinit */ /* init terminal I/O buffers */ inittiobufs() { #ifdef COMMENT if ((ttbuf = (unsigned char *) NewPtr(TTBUFL + 1)) == NIL) macfatal("Can't allocate ttbuf", 0); #endif if ((mybufp = (unsigned char *) NewPtr(MYBUFLEN + 4)) == NIL) macfatal("Can't allocate mybufp", 0); my_count = 0; /* Number of chars still in mybuf */ my_item = -1; /* Last index read from mybuf[] */ if ((tto_buf = (char *) NewPtr(MAXSP)) == NIL) macfatal("Can't allocate tto_buf", 0); if ((my_input_buf = (char *) NewPtr(MYBUFSIZE)) == NIL) macfatal("Can't allocate my_input_buf", 0); } /****************************************************************************/ /* P O R T _ O P E N -- Open and init a serial port. port is either -6 (for */ /* the modem port) or -8 (for the printer port) */ /****************************************************************************/ #ifdef MPW #define RamSDOpen RAMSDOPEN /* Fix routine name */ #else /* !MPW (Think C) */ #define RamSDOpen(n) (noErr) /* Don't really do anything */ #endif /* !MPW */ port_open (char *port_name) { int err; char scratch[256]; Str255 p_port_name, input_driver_name, output_driver_name; strcpy((char *)p_port_name, port_name); c2pstr(p_port_name); /* Set up IO drivers */ if (!portnameToDriver(p_port_name,input_driver_name, output_driver_name)) { sprintf(scratch, "Could not find the serial port \"%s\"", port_name); printerr(scratch, 0); innum = outnum = 0; return; } /* * See if the port is already open. If so, warn the user before proceeding. */ #if 1 if (!HaveSerialPortArbitration()) { /* if no arbitrator */ if (isOpenDriver(output_driver_name)) { if (checkPortConflict(port_name)) { return; } } } #else if (isOpenDriver(output_driver_name)) { if (checkPortConflict(port_name)) { return; } } #endif if ((err = OpenDriver(input_driver_name, &innum)) != noErr) { if (err == portInUse) { sprintf(scratch, "Output port \"%s\" is in use", port_name); err = 0; } else sprintf(scratch, "Could not open output \"%s\": ", port_name); printerr(scratch, err); innum = outnum = 0; return; } if ((err = OpenDriver(output_driver_name, &outnum)) != noErr) { if (err == portInUse) { sprintf(scratch, "Input port \"%s\" is in use", port_name); err = 0; } else sprintf(scratch, "Could not open input \"%s\": ", port_name); CloseDriver(innum); innum = outnum = 0; return; } /* Make driver use larger buff */ err = SerSetBuf (innum, my_input_buf, MYBUFSIZE); if (err) printerr ("Trouble making IO buffer:", err); strcpy(ttname, port_name); } /*****************************************************************************/ /* P O R T _ C L O S E -- Close down the serial port. */ /*****************************************************************************/ port_close() { int err; ParamBlockRec cpb; if (!innum) return; /* already closed */ err = KillIO(innum); /* Kill off IO drivers */ if (err != noErr) printerr("trouble KillIO-ing serial input driver:",err); err = KillIO(outnum); /* Kill off IO drivers */ if (err != noErr) printerr("trouble KillIO-ing serial output driver:",err); err = SerSetBuf (innum, NULL, 0); /* Make driver default buffer */ if (err != noErr) printerr("trouble resetting serial IO buffer:",err); /* if (usingRAMdriver) { */ if (!drop_dtr) { bzero((char *) &cpb, sizeof(cpb)); cpb.cntrlParam.ioNamePtr = NULL; /* IM-IV p226 */ /* tell the ram driver not to lower DTR on close */ cpb.cntrlParam.csCode = 16; /* misc serial control */ *((unsigned char *) cpb.cntrlParam.csParam) = 0x80; /* don't lower DTR */ cpb.cntrlParam.ioCRefNum = innum; err = PBControl(&cpb, FALSE); if ((err != noErr) && (err != -1)) printerr( "trouble telling RAM serial driver (in) not to lower DTR:", err); bzero((char *) &cpb, sizeof(cpb)); cpb.cntrlParam.ioNamePtr = NULL; /* IM-IV p226 */ cpb.cntrlParam.csCode = 16; /* misc serial control */ /* don't lower DTR */ *((unsigned char *) cpb.cntrlParam.csParam) = 0x80; cpb.cntrlParam.ioCRefNum = outnum; err = PBControl(&cpb, FALSE); if ((err != noErr) && (err != -1)) printerr("\ trouble telling RAM serial driver (out) not to lower DTR:",err); } /* RamSDClose (sPortA); */ /* this "returns" void */ /* } */ err = CloseDriver (innum); if (err != noErr) printerr("trouble closing serial input driver:",err); /* * For some reason or other, doing this close on a 64k ROM machine will cause * the mouse to freeze. Since the input driver is the only one that really * matters, we just close it. */ if (have_128roms) { err = CloseDriver (outnum); if (err != noErr) printerr("trouble closing serial output driver:",err); } /* mark things as closed */ innum = outnum = 0; } /****************************************************************************/ /* T T O P E N -- Open a tty for exclusive access. */ /* */ /* ttname = C string, name of communications device */ /* lcl = (doesn't mean anything in Mac Kermit */ /* modem = 0 if no modem, positive if there is a modem */ /* timeo = (not used in Mac Kermit) */ /* */ /* Returns 0 on success, -1 on failure. */ /****************************************************************************/ int ttopen (ttname, lcl, modem, timeo) char *ttname; int *lcl; int modem; int timeo; #pragma unused (modem, timeo) { int saved_drop_dtr; my_count = 0; /* Initialize myread() stuff */ my_item = -1; if (*lcl < 0) *lcl = 1; /* Always in local mode */ if (mybufp == NIL) macfatal("No mybufp", 0); if (tto_buf == NIL) macfatal("No tto_buf", 0); if (my_input_buf == NIL) macfatal("No my_input_buf", 0); iopb.ioParam.ioResult = 0; /* No pending output */ iopb.ioParam.ioActCount = 0; /* For error checking in ttol() */ iopb.ioParam.ioReqCount = 0; /* Re-open the port if it has changed. */ /* ddddd don't do this if no change in port name ? */ saved_drop_dtr = drop_dtr; drop_dtr = 1; /* So we don't confuse AppleTalk */ port_close(); drop_dtr = saved_drop_dtr; port_open(ttname); if (!setserial (innum, outnum, speed, KPARITY_NONE)) printfalert("Problem setting port speed"); (void) sershake(flow); /* Set flow control */ return (0); } /* ttopen */ /****************************************************************************/ /* T T C L O S -- Close the TTY, releasing any lock. */ /****************************************************************************/ int ttclos () { my_count = 0; /* Initialize myread() stuff */ my_item = -1; return 0; } /* ttclos */ /* * sershake * Set handshake parameters on serial port. */ int sershake (int flow) { int err; ParamBlockRec pb; SerShk *controlparam; /* To change serial driver paramaters */ if (!innum) return 0; /* if port closed, do nothing */ bzero((char *)&pb, sizeof(pb)); controlparam = (SerShk *) &pb.cntrlParam.csParam; /* * Old code used to issue controls to in and out sides. IM-IV * says to use the output driver. */ pb.cntrlParam.ioCRefNum = outnum; if (have_128roms) pb.cntrlParam.csCode = 14; /* SerHShake + DTR */ else pb.cntrlParam.csCode = 10; /* SerHShake */ if (flow) controlparam->fXOn = TRUE; /* obey flow control */ else controlparam->fXOn = FALSE; /* ignore flow control */ controlparam->fCTS = fCTS; /* output flow control */ controlparam->xOn = 17; controlparam->xOff = 19; controlparam->errs = FALSE; controlparam->evts = FALSE; if (flow && fInX) controlparam->fInX = TRUE; /* flow control when almost full */ else controlparam->fInX = FALSE; controlparam->fDTR = fDTR; /* input flow control */ err = PBControl(&pb, FALSE); if (err != noErr) { printfalert("sershake: problem setting handshake: %d", err); return -1; } return 0; } /****************************************************************************/ /* T T P K T -- Condition the communication line for packets. */ /* If called with speed > -1, also set the speed. */ /* Returns 0 on success, -1 on failure. */ /****************************************************************************/ int ttpkt (long spd, int flow, int parity) /* we only care about flow here */ { #pragma unused (spd) ttprty = parity; /* Let other tt functions see these. */ ttspeed = speed; /* $$$ is this correct? */ ttflow = flow; /* Now make this available too. */ fInX = TRUE; return(sershake(flow)); } /****************************************************************************/ /* T T V T -- Condition the communication line for a virtual terminal. */ /* If called with spd > -1, also set the spd. */ /* Returns 0 on success, -1 on failure. */ /****************************************************************************/ int ttvt (spd, flow, parity) /* all ignoreed */ long spd; int flow; int parity; #pragma unused (spd, flow, parity) { (void) ttres(); return (0); } /* ttvt */ /****************************************************************************/ /* T T F L U I -- Flush tty input buffer */ /****************************************************************************/ int ttflui () { int err; my_count = 0; /* Initialize myread() stuff */ my_item = -1; if (!innum) return 0; /* if port closed, do nothing */ err = KillIO (innum); if (err) printerr ("Bad input clear", err); return (0); } /* ttflui */ int ttfluo() /* Flush output buffer */ { int err; if (!innum) return 0; /* if port closed, do nothing */ err = KillIO (outnum); if (err) printerr ("Bad ouput clear", err); return (0); /* (dummy for now) */ } /****************************************************************************/ /* T T S N D B -- Send a break */ /****************************************************************************/ ttsndb() { long finalticks; if (!innum) return 0; /* if port closed, do nothing */ /* delay wants 1/60th units */ SerSetBrk (outnum); /* start breaking */ Delay ((long) 15, &finalticks); /* delay about 250ms */ SerClrBrk (outnum); /* stop breaking */ return 0; } /****************************************************************************/ /* * Flushio: * Initialize some communications constants, and clear screen and * character buffers. */ /****************************************************************************/ flushio () { int err; if (!innum) return 0; /* if port closed, do nothing */ err = KillIO (innum); if (err) printerr ("Bad input clear", err); err = KillIO (outnum); if (err) printerr ("Bad ouput clear", err); return 0; } /* flushio */ /****************************************************************************/ /* sendbreak - sends a break across the communictions line. * * The argument is in units of approximately 0.05 seconds (or 50 * milliseconds). To send a break of duration 250 milliseconds the * argument would be 5; a break of duration 3.5 seconds would be (umm, * lets see now) 70. * */ /****************************************************************************/ sendbreak (msunit) int msunit; { long finalticks; if (!innum) return 0; /* if port closed, do nothing */ /* delay wants 1/60th units. We have 3/60 (50 ms.) units, convert */ msunit = msunit * 3; SerSetBrk (outnum); /* start breaking */ Delay ((long) msunit, &finalticks); /* delay */ SerClrBrk (outnum); /* stop breaking */ return 0; } /* sendbreak */ /****************************************************************************/ /* toggledtr - Turn DTR off, wait a bit, turn it back on. * * the argument is in the same units as sendbreak (see above). */ /****************************************************************************/ toggle_dtr (msunit) int msunit; { long finalticks; ParamBlockRec pb; int err; if (!innum) return 0; /* if port closed, do nothing */ /* delay wants 1/60th units. We have 3/60 (50 ms.) units, convert */ msunit = msunit * 3; pb.cntrlParam.csCode = 18; /* lower DTR */ pb.cntrlParam.ioCRefNum = outnum; err = PBControl (&pb, FALSE); if (err != noErr) printerr ("toggle_dtr() trouble lowering DTR: ", err); Delay ((long) msunit, &finalticks); /* delay */ pb.cntrlParam.csCode = 17; /* raise DTR */ pb.cntrlParam.ioCRefNum = outnum; err = PBControl (&pb, FALSE); if (err != noErr) printerr ("toggle_dtr() trouble raising DTR: ", err); return 0; } /* sendbreak */ /****************************************************************************/ /* do_xon - xon the output port and send an xon (control-Q) character */ /****************************************************************************/ do_xon () { ParamBlockRec pb; int err; if (!innum) return 0; /* if port closed, do nothing */ pb.cntrlParam.csCode = 22; /* clear XOFF for my output */ pb.cntrlParam.ioCRefNum = outnum; err = PBControl (&pb, FALSE); if (err != noErr) printerr ("do_xon() trouble unblocking output port: ", err); pb.cntrlParam.csCode = 24; /* unconditionally send XON */ pb.cntrlParam.ioCRefNum = outnum; err = PBControl (&pb, FALSE); if (err != noErr) printerr ("do_xon() trouble sending XON: ", err); return 0; } /* do_xon */ /****************************************************************************/ /* T T S S P D -- Set tty speed */ /****************************************************************************/ ttsspd (cps) int cps; { if (!innum) return 0; /* if port closed, do nothing */ if (setserial (innum, outnum, cps*10, KPARITY_NONE)) return(0); else return (-1); } /****************************************************************************/ /* T T G S P D -- Get tty speed */ /****************************************************************************/ long ttgspd () { if (speed <= 0L && innum != 0) printerr("Speed got reset, now == ", (int) speed); return (speed); } /* Interrupt Functions */ /****************************************************************************/ /* Set up terminal interrupts on console terminal */ /* Set an interrupt trap. */ /****************************************************************************/ VOID conint(SIGTYP (*f)(int), SIGTYP (*s)(int)) { #pragma unused (f, s) return; } /* conint */ /****************************************************************************/ /* Reset console terminal interrupts */ /****************************************************************************/ VOID connoi () { return; } /* connoi */ /****************************************************************************/ /* writeps - write a pascal string to the serial port. * */ /****************************************************************************/ void writeps (StringPtr s) { long wcnt, w2; StringPtr s2; w2 = wcnt = *s++; /* get count */ for (s2 = s; w2 > 0; w2--, s2++) /* add parity */ *s2 = dopar (*s2); /* ttol will printerr() if it has a problem */ (void) ttol((CHAR *) s, wcnt); return; } /* writeps */ extern Boolean have_multifinder;/* true if running under MF */ extern Boolean in_background; /* becomes true if running MF and in bg */ extern long mf_sleep_time; /* number of 60ths between task time */ /****************************************************************************/ /* T T O L -- Similar to "ttinl", but for writing. */ /****************************************************************************/ ttol (s, n) CHAR *s; int n; { long finalticks; int err; ParamBlockRec cpb; if (!innum) return (-1); /* if port closed, do nothing */ #ifdef COMMENT debug(F101,"ttol n","",n); debug(F101," s","",s); /* * the straight-forward way to write out a buffer: synchronously */ finalticks = n; err = FSWrite(outnum, &finalticks, s); if ((err != noErr) || (finalticks != n)) { printerr("ttol FSWrite error: ", err); return (-1); } return (n); #endif /* * The fancy way to write out strings: do async writes, waiting for the * previous call to finish first (and possibly unsticking the serial * driver) */ /* wait for previous call to finish */ /* while the prev. request is still running */ while (iopb.ioParam.ioResult == 1) { /* if we're running protocol */ if (have_multifinder && protocmd != 0) { miniparser (TRUE); /* keep mac running */ finalticks = TickCount (); } else { /* else terminal emulation */ Delay ((long) 1, &finalticks); /* wait for a bit */ } /* (PWP) If we have waited too long, unblock the output (keep the Mac from freezing up) */ if (finalticks > (tto_startticks + XOFFTIMEO)) { cpb.cntrlParam.csCode = 22; /* clear XOFF for my output */ cpb.cntrlParam.ioCRefNum = outnum; err = PBControl (&cpb, FALSE); if (err != noErr) printerr ("ttol() trouble unblocking output port: ", err); tto_startticks = TickCount (); /* Get starting time */ } } /* check for errors in previous call */ if (iopb.ioParam.ioResult) { printerr ("Error in previous PBWrite:", iopb.ioParam.ioResult); return (-1); } if (iopb.ioParam.ioActCount != iopb.ioParam.ioReqCount) { printerr ("PBWrite to serial didn't write enough: ", iopb.ioParam.ioActCount); printerr ("(asked for:)", iopb.ioParam.ioReqCount); return (-1); } /* the previous call is now done, so we can load in our new information */ if (n > MAXSP) { /* MAXSP == sizeof(tto_buf) */ printerr("ttol asked to write too many chars: ", n); return (-1); } bcopy((char *) s, tto_buf, n); /* in ckmfio.h if nowhere else */ iopb.ioParam.ioCompletion = NULL; iopb.ioParam.ioNamePtr = NULL; iopb.ioParam.ioVRefNum = 0; iopb.ioParam.ioRefNum = outnum; iopb.ioParam.ioVersNum = 0; iopb.ioParam.ioPermssn = 0; iopb.ioParam.ioMisc = NULL; iopb.ioParam.ioBuffer = tto_buf; iopb.ioParam.ioReqCount = (long) n; iopb.ioParam.ioPosMode = 0; iopb.ioParam.ioPosOffset = 0; tto_startticks = TickCount (); /* get starting time */ PBWrite (&iopb, TRUE); /* request an async. write */ if (protocmd != 0) miniparser (TRUE); /* allow other tasks to run */ return (n); /* fake a good run */ } /* ttol */ /* T T O C -- Output a character to the communication line */ ttoc(char c) { char foo[2]; foo[0] = c; foo[1] = '\0'; return (ttol((CHAR *) foo, 1)); } /* myread() -- Efficient read of one character from communications line. * * Uses a private buffer to minimize the number of expensive read() system * calls. Essentially performs the equivalent of read() of 1 character, which * is then returned. By reading all available input from the system buffers * to the private buffer in one chunk, and then working from this buffer, the * number of system calls is reduced in any case where more than one character * arrives during the processing of the previous chunk, for instance high * baud rates or network type connections where input arrives in packets. * If the time needed for a read() system call approaches the time for more * than one character to arrive, then this mechanism automatically compensates * for that by performing bigger read()s less frequently. If the system load * is high, the same mechanism compensates for that too. * * myread() is a macro that returns the next character from the buffer. If the * buffer is empty, mygetbuf() is called. See mygetbuf() for possible error * returns. * * This should be efficient enough for any one-character-at-a-time loops. * For even better efficiency you might use memcpy()/bcopy() or such between * buffers (since they are often better optimized for copying), but it may not * be worth it if you have to take an extra pass over the buffer to strip * parity and check for CTRL-C anyway. * * Note that if you have been using myread() from another program module, you * may have some trouble accessing this macro version and the private variables * it uses. In that case, just add a function in this module, that invokes the * macro. */ #define mac_myread(timeo_ticks,intim) (--my_count < 0 \ ? mac_mygetbuf(timeo_ticks,intim) \ : (int) (mybufp[++my_item])) /* Specification: Push back up to one character onto myread()'s queue. * * This implementation: Push back characters into mybuf. At least one character * must have been read through myread() before myunrd() may be used. After * EOF or read error, again, myunrd() can not be used. Sometimes more than * one character can be pushed back, but only one character is guaranteed. * Since a previous myread() must have read its character out of mybuf[], * that guarantees that there is space for at least one character. If push * back was really needed after EOF, a small addition could provide that. * * myunrd() is currently not called from anywhere inside kermit... */ #ifdef NOTUSED myunrd(ch) CHAR ch; { if (my_item >= 0) { mybufp[my_item--] = ch; ++my_count; } } #endif /* mygetbuf() -- Fill buffer for myread() and return first character. * * This function is what myread() uses when it can't get the next character * directly from its buffer. First, it calls a system dependent myfillbuf() * to read at least one new character into the buffer, and then it returns * the first character just as myread() would have done. This function also * is responsible for all error conditions that myread() can indicate. * * Returns: When OK => a positive character, 0 or greater. * When EOF => -2. * When error => -3, error code in errno. * * Older myread()s additionally returned -1 to indicate that there was nothing * to read, upon which the caller would call myread() again until it got * something. The new myread()/mygetbuf() always gets something. If it * doesn't, then make it do so! Any program that actually depends on the old * behaviour will break. * * The older version also used to return -2 both for EOF and other errors, * and used to set errno to 9999 on EOF. The errno stuff is gone, EOF and * other errors now return different results, although Kermit currently never * checks to see which it was. It just disconnects in both cases. * * Kermit lets the user use the quit key to perform some special commands * during file transfer. This causes read(), and thus also mygetbuf(), to * finish without reading anything and return the EINTR error. This should * be checked by the caller. Mygetbuf() could retry the read() on EINTR, * but if there is nothing to read, this could delay Kermit's reaction to * the command, and make Kermit appear unresponsive. * * The debug() call should be removed for optimum performance. */ /* myfillbuf(): * System-dependent read() into mybuf[], as many characters as possible. * * Returns: OK => number of characters read, always more than zero. * EOF => 0 * Error => -1, error code in errno. * * If there is input available in the system's buffers, all of it should be * read into mybuf[] and the function return immediately. If no input is * available, it should wait for a character to arrive, and return with that * one in mybuf[] as soon as possible. It may wait somewhat past the first * character, but be aware that any such delay lengthens the packet turnaround * time during kermit file transfers. Should never return with zero characters * unless EOF or irrecoverable read error. * * Correct functioning depends on the correct tty parameters being used. * Better control of current parameters is required than may have been the * case in older Kermit releases. For instance, O_NDELAY (or equivalent) can * no longer be sometimes off and sometimes on like it used to, unless a * special myfillbuf() is written to handle that. Otherwise the ordinary * myfillbuf()s may think they have come to EOF. * * If your system has a facility to directly perform the functioning of * myfillbuf(), then use it. If the system can tell you how many characters * are available in its buffers, then read that amount (but not less than 1). * If the system can return a special indication when you try to read without * anything to read, while allowing you to read all there is when there is * something, you may loop until there is something to read, but probably that * is not good for the system load. */ int mac_mygetbuf(long timeo_tics, long intim) { long avail; /* can't be register */ long finaltics; /* can't be register */ int err; extern int tlevel; if (mybufp == NIL) macfatal("No mybufp (in mac_mygetbuf())", 0); if (!innum) return -1; /* if port closed, do nothing */ for (;;) { SerGetBuf (innum, &avail); /* Get available count */ if (avail > 0) break; /* we can get these chars */ /* no chars availiable yet */ if ((protocmd != 0) || (tlevel > -1) || intfunc) { miniparser (TRUE); /* keep mac running */ if (sstate == 'a') { /* abort occured? */ if (intfunc) { sstate = '\0'; (*intfunc)(0); } else return (-1); /* ugh, look like timeout */ } } /* * Check alarm() timeouts. */ if (alarmtime && alarmfunc && (((ulong)TickCount() - alarmtime) > 0)) (*alarmfunc)(0); finaltics = TickCount (); if (timeo_tics > 0) { /* Want to do timeout? */ if (intim + timeo_tics < finaltics) { return (-1); /* Too long, give up */ } } /* go back and try to get more chars */ } if (avail > MYBUFLEN) avail = MYBUFLEN; /* * CAREFUL: the Mac FSRead() function gets how much to read, * AND PUTS HOW MUCH IT DID READ into avail */ err = FSRead (innum, &avail, mybufp); /* Into our buffer */ if (err != noErr) { screen(SCR_EM,0,(long) err,"Serial input error:"); return (-3); /* return input error */ } if (avail <= 0) { screen(SCR_EM, 0, (long) avail, "No serial input error, but didn't read any:"); return (-3); /* return input error */ } /* not at end of packet yet, so let other tasks have a chance */ if (in_background) miniparser (TRUE); /* allow other tasks to run */ my_count = (int) avail; /* debug(F101, "myfillbuf read", "", my_count); */ --my_count; return((int) (mybufp[my_item = 0])); } /****************************************************************************/ /* T T I N L -- Read a record (up to break character) from comm line. */ /* If no break character encountered within "max", return "max" characters, with disposition of any remaining characters undefined. Otherwise, return the characters that were read, including the break character, in "dest" and the number of characters read as the value of function, or 0 upon end of file, or -1 if an error occurred. Times out & returns error if not completed within "timo" seconds. */ /* Reads up to "max" characters from the communication line, terminating on the packet-end character (eol), or timing out and returning -1 if the eol character not encountered within "timo" seconds. The characters that were input are copied into "dest" with their parity bits stripped if parity was selected. Returns the number of characters read. Characters after the eol are available upon the next call to this function. The idea is to minimize the number of system calls per packet, and also to minimize timeouts. This function is the inner loop of the program and must be as efficient as possible. The current strategy is to use myread(). WARNING: this function calls parchk(), which is defined in another module. Normally, ckutio.c does not depend on code from any other module, but there is an exception in this case because all the other ck?tio.c modules also need to call parchk(), so it's better to have it defined in a common place. */ /****************************************************************************/ #define CTRLC '\03' #ifdef PARSENSE int ttinl(CHAR *dest, int max, int timo, CHAR eol, CHAR start) { int flag; #else int ttinl(CHAR *dest, int max, int timo, CHAR eol) { #endif register int i, m, n; /* local variables */ int x; long timeoticks; long intim; /* when we started */ debug(F101,"ttinl max","",max); debug(F101,"ttinl timo","",timo); #ifdef PARSENSE debug(F000,"ttinl start","",start); flag = 0; /* Start of packet flag */ #endif x = 0; /* Return code */ m = (ttprty) ? 0177 : 0377; /* Parity stripping mask. */ *dest = '\0'; /* Clear destination buffer */ if (timo <= 0) { /* untimed */ timo = 0; /* Safety */ intim = 0; /* tell mac_myread not to timeout */ timeoticks = 0; } else { timeoticks = timo * 60; intim = TickCount (); /* now */ } i = 0; while (i < max-1) { /* debug(F101,"ttinl i","",i); */ if ((n = mac_myread(timeoticks,intim)) < 0) { debug(F101,"ttinl myread failure, n","",n); x = -1; break; } /* debug(F101,"ttinl char","",n&m); */ #ifdef PARSENSE if ((flag == 0) && ((n & 0x7f) == start)) flag = 1; if (flag) dest[i++] = n & m; #else dest[i++] = n & m; #endif /* PARSENSE */ #ifdef PARSENSE if (flag == 0) { debug(F101,"ttinl skipping","",n); continue; } #endif /* PARSENSE */ /* Check for end of packet */ if ((n & 0x7f) == eol) { debug(F101,"ttinl got eol","",eol); dest[i] = '\0'; /* Yes, terminate the string, */ /* debug(F101,"ttinl i","",i); */ #ifdef PARSENSE /* Here's where we actually check and adjust the parity. */ /* The major flaw here is if parity is NONE (ttprty = 0) and the packets */ /* really do have no parity, then parchk() is called for every packet. */ /* In practice, this doesn't really harm efficiency noticably, but it would */ /* be better if ttinl() had a way of knowing to stop doing this once a */ /* particular file transfer had been started and checked. */ if (ttprty == 0) { if ((ttprty = parchk(dest,start,i)) > 0) { register int j; debug(F101,"ttinl senses parity","",ttprty); debug(F110,"ttinl packet before",dest,0); for (j = 0; j < i; j++) dest[j] &= 0x7f; /* Strip parity from packet */ debug(F110,"ttinl packet after ",dest,0); } else debug(F101,"parchk","",ttprty); } #endif debug(F111,"ttinl got", dest,i); return(i); } } /* end while (i < max-1) */ debug(F100,"ttinl timout","",0); /* Get here on timeout. */ debug(F111," with",dest,i); return(x); /* and return error code. */ } /* ttinl */ /****************************************************************************/ /* ttinc(timo) - read a character with timeout. Return -1 on timeout. */ /****************************************************************************/ int ttinc (int timo) { register int m, n = 0; long timeoticks; long intim; /* when we started */ if (timo <= 0) { /* untimed */ intim = 0; /* tell mac_myread not to timeout */ timeoticks = 0; } else { timeoticks = timo * 60; intim = TickCount (); /* now */ } m = (ttprty) ? 0177 : 0377; /* Parity stripping mask. */ /* comm line failure returns -1 thru myread, so no &= 0377 */ n = mac_myread(timeoticks,intim); /* Wait for a character... */ /* debug(F101,"ttinc n","",n); */ return(n < 0 ? n : n & m); } /* ttinc */ /****************************************************************************/ /* PWP: input as many chars as we can read from the serial line right now */ /****************************************************************************/ int ttinm(register char *buf, register int max) { long avil, num; int err, i; if (!innum) return 0; /* if port closed, do nothing */ /* * DANGER WILL ROBINSON: this KNOWS about how mac_myread works * (in an incestuous way), so BE CAREFUL!!! */ if (my_count > 0) { /* do we have chars buffered up? */ for (i = 0; (my_count > 0) && (i < max); i++) *buf++ = mac_myread(0L, 0L); return (i); /* return contents of previous read buffer */ } SerGetBuf (innum, &avil); /* Get available count */ if (avil > 0) { /* Have something? */ num = (avil > max) ? max : avil; /* Set max */ err = FSRead (innum, &num, buf); /* Into our buffer */ if (err != noErr) printerr ("Serial input error: ", err); return (num); /* return how many */ } else { return (0); } } /****************************************************************************/ /****************************************************************************/ int ttchk () { long avcnt; /* pascal long */ if (!innum) return 0; /* if port closed, none available */ SerGetBuf (innum, &avcnt); /* get available */ return (avcnt + my_count); /* return avail plus our own */ } /* ttchk */ /****************************************************************************/ /* T T R E S -- Reset the serial line after doing a protocol things */ /****************************************************************************/ ttres () { fInX = FALSE; return(sershake(flow)); } unsigned long starttime; Boolean timerRunning = FALSE; /****************************************************************************/ /* R T I M E R -- Reset elapsed time counter */ /****************************************************************************/ VOID rtimer () { GetDateTime (&starttime); timerRunning = TRUE; } /* rtimer */ /****************************************************************************/ /* G T I M E R -- Get current value of elapsed time counter in seconds */ /****************************************************************************/ int gtimer () { unsigned long secs; if (timerRunning) { GetDateTime (&secs); return (secs - starttime); timerRunning = FALSE; } else return (0); } /* gtimer */ /****************************************************************************/ /* Z T I M E -- Return date/time string */ /* Various bits stolen from Unix Kermit assume that ztime() returns */ /* a asctime() format string, vis: */ /* "Thu Feb 8 12:00:00 1990" */ /****************************************************************************/ VOID ztime (s) char **s; { /* ztime() MUST return a string in asctime() format. */ unsigned long secs; DateTimeRec dtrec; static char dtime[32]; /* really only 25 needed, but be safe */ static char *month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char *day_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; GetDateTime (&secs); Secs2Date (secs, &dtrec); sprintf(dtime, "%3s %3s %2d %2d:%02d:%02d %4d", day_names[dtrec.dayOfWeek - 1], month_names[dtrec.month - 1], dtrec.day, dtrec.hour, dtrec.minute, dtrec.second, dtrec.year); *s = dtime; #ifdef COMMENT #include time_t time(time_t *timer); char *ctime(const time_t *timer); time_t time_now; time_now = time( (time_t) 0 ); *s = ctime( &time_now ); #endif /* COMMENT */ } /* ztime */ /* Console IO routines. The console is implemented as a text edit structure. * These routines are supported: * * conoc(c) -- output one character to TE record at insertion point * conol(s) -- output null terminated string to TE record " " * conoll(s) -- same but with CR on the end * conxo(n,s) -- n character to TE record " " * */ #define NILTE ((TEHandle ) NILPTR) #define LF 012 #define CR 015 /****************************************************************************/ /* C O N X O -- Output string of length len to console text edit record */ /****************************************************************************/ conxo (len, s) int len; register char *s; { register char *t; /* change NLs to CRs for the Mac */ for (t = s; *t && (t - s < len); t++) if (*t == LF) *t = CR; /* debug (F101, "conxo here: ", s, len); */ if (rcmdw->teh == NULL) return(0); TEDeactivate(rcmdw->teh); (void) trimcon(rcmdw, len); TEInsert (s, (long) len, rcmdw->teh); /* insert the string */ TESetSelect(TE_MAX, TE_MAX, rcmdw->teh); TEActivate(rcmdw->teh); rcdwscroll (rcmdw); /* possibly scroll it */ return (0); } /* conxo */ /****************************************************************************/ /* C O N O C -- Output a character to the console text edit record */ /****************************************************************************/ conoc (char c) { (void) conxo (1, &c); } /* conoc */ /****************************************************************************/ /****************************************************************************/ conopen () { return (1); } /* conopen */ /****************************************************************************/ /* C O N O L -- Write a line to the console text edit record */ /****************************************************************************/ conol (s) register char *s; { return (conxo (strlen (s), s)); } /* conol */ /****************************************************************************/ /* C O N O L L -- Output a string followed by CRLF */ /****************************************************************************/ conoll (s) char *s; { (void) conol (s); /* first the string */ (void) conoc (CR); /* now the return */ return (0); } /* conoll */ /***************************************** * C-Kermit Compatibility routines * *****************************************/ /* * dummy routines for MAC */ #define CINC(v,l)((v+1 == l)? 0 : v+1) #define CBL 25 /* console buffer length */ int cbin = 0; /* circular buffer pointers */ int cbout = 0; unsigned char cbuf[CBL]; /* console buffer */ concb (char esc) { #pragma unused (esc) } conres () { } conchk () { if (sstate == 'a') /* if cmd-. in miniparser */ return 1; else return 0; } tthang () { toggle_dtr(70); return(1); } VOID setint () { } /* * ttgmdm * Get modem control signals. * The mac only has one input handshake (CTS) and one output * line (DTR). DTR may not be present on older MACS with * DB-9 (instead of DIN-8) connectors. */ ttgmdm() { int err, r; SerStaRec stat; err = SerStatus(outnum, &stat); if (err != noErr) { printfalert("ttgmdm: Error getting serial status: %d", err); return -1; /* not available */ } r = 0; if (!stat.ctsHold) r |= BM_CTS; /* * Always say dtr is on. We may be lying if we've enabled * dtr input flow control. */ r |= BM_DTR; return r; } /* * Things from ckutio.c */ int ckxech = 1; /* 0 if system normally echoes console characters, else 1 */ int ttcarr = CAR_AUT; /* Carrier handling mode. */ #include "ckuver.h" /* Version herald */ char *ckxsys = HERALD; /* T T S C A R R -- Set ttcarr variable, controlling carrier handling. * * 0 = Off: Always ignore carrier. E.g. you can connect without carrier. * 1 = On: Heed carrier, except during dialing. Carrier loss gives disconnect. * 2 = Auto: For "modem direct": The same as "Off". * For real modem types: Heed carrier during connect, but ignore * it anytime else. Compatible with pre-5A C-Kermit versions. * * As you can see, this setting does not affect dialing, which always ignores * carrier (unless there is some special exception for some modem type). It * does affect ttopen() if it is set before ttopen() is used. This setting * takes effect on the next call to ttopen()/ttpkt()/ttvt(). And they are * (or should be) always called before any communications is tried, which * means that, practically speaking, the effect is immediate. * * Of course, nothing of this applies to remote mode (xlocal = 0). * * Someone has yet to uncover how to manipulate the carrier in the BSD * environment (or any non-termio using environment). Until that time, this * will simply be a no-op for BSD. * * Note that in previous versions, the carrier was most often left unchanged * in ttpkt()/ttvt() unless they were called with DIALING or CONNECT. This * has changed. Now it is controlled by ttcarr in conjunction with these * modes. */ int ttscarr (carrier) int carrier; { ttcarr = carrier; debug(F101, "ttscarr","",ttcarr); return(ttcarr); } VOID bgchk () { /* Check background status */ #ifdef notdef if (bgset < 0) pflag = !backgrd; else pflag = (bgset == 0 ? 1 : 0); #endif } /* T T X I N -- Get n characters from tty input buffer */ /* Returns number of characters actually gotten, or -1 on failure */ /* Intended for use only when it is known that n characters are actually */ /* Available in the input buffer. */ int ttxin (n,buf) int n; CHAR *buf; { int i, c; for (i = 0; i < n; i++) { c = ttinc(0); if (c < 0) return -1; *buf++ = ttinc(0); /* ignore errors */ } return n; } /* C O N I N C -- Get a character from the console */ int coninc (timo) int timo; { int c; newparser(1, 0, (long) timo); /* wait till timeout, no menus */ if (cbin == cbout) /* if timeout */ return -1; c = cbuf[cbout]; cbout = CINC(cbout, CBL); /* advance pointer */ return c; } /* * puts for mac console */ int mac_puts (const char *string) { const char *cp; if (cmdinterminal) cursor_erase (ttermw); for (cp = string; *cp; cp++) ucharout(*cp); if (cmdinterminal) { if (ttermw->scroll_amount) flushscroll(ctermw); /* sync the screen */ if (ctermw->out_maxcol) /* KLUDGE */ flushbuf(ctermw); cursor_draw(ctermw); } return 1; /* $$$ what should this be? */ } /* * printf for mac console */ int mac_printf (const char *format, ...) { int i, rc; char string[512]; /* Hope that this is big enough!! */ char *cp; va_list ap; va_start(ap, format); rc = vsprintf(string, format, ap); va_end(ap); if ((i = strlen(string)) >= sizeof(string)) printerr("Overran allocation for string in printf(), len:", i); if (cmdinterminal) cursor_erase (ttermw); for (cp = string; *cp; cp++) ucharout(*cp); if (cmdinterminal) { if (ctermw->scroll_amount) flushscroll(ctermw); /* sync the screen */ if (ctermw->out_maxcol) /* KLUDGE */ flushbuf(ctermw); cursor_draw(ctermw); } return (rc); } int mac_perror (const char *s) { mac_printf("MacError: %s\n",s); } /* * putchar for mac console */ int mac_putchar (int c) { if (cmdinterminal) cursor_erase (ttermw); ucharout(c); if (cmdinterminal) { if (ctermw->scroll_amount) flushscroll(ctermw); /* sync the screen */ if (ctermw->out_maxcol) /* KLUDGE */ flushbuf(ctermw); cursor_draw(ctermw); } return 1; /* $$$ what should this be? */ } /* * outchar * output a character to a termw window. */ void outchar (struct termw *termw, char c) { unsigned char buf[2]; buf[0] = c; buf[1] = 0; printem(termw, buf, 1); } /* * Send a single character to the console, unix format. * The MPW compiler thinks that '\r' is 0x0a and that * '\n' is 0x0d. In this routine, we translate things * to the appropriate action. * * This will be broken if we call this routine with chars * that are already correct, e.g.: CR. */ static void ucharout (char c) { switch (c) { case '\n': /* unix newline -> CR, LF */ cmdout(LF); cmdout(CR); return; case '\r': /* \r -> CR */ cmdout(CR); return; default: cmdout(c); return; } } /* * cmdout * write a character to the command window * (copied from conxo) */ VOID cmdout (char c) { unsigned char buf[2]; buf[0] = c; buf[1] = 0; /* * Output to terminal window if no command window. */ if (cmdinterminal) { printem(ttermw, buf, 1); return; } printem(ctermw, buf, 1); } /* * getchar for mac console */ int mac_getchar () { int c; while (cbin == cbout) { /* while buffer is empty */ if (sstate = newparser(1, 1, 0L)) { if (sstate == 'p') /* if wakeup flag */ sstate = '\0'; else if (sstate == 'n') { /* if null command state */ sstate = '\0'; return (-3); } else return(-3); /* NULL command */ } } c = cbuf[cbout]; cbout = CINC(cbout, CBL); /* advance pointer */ return c; } #ifdef THINK_C /* Just a dummy so that we can use so ckuus*.c */ char * getenv(const char *s) { return (NULL); /* the Mac doesn't have an "environment" */ } #endif /* THINK_C */ /* * writecbc * Store a character into the console input buffer. */ writecbc (c) char c; { int t; cbuf[cbin] = c; t = CINC(cbin, CBL); /* advance pointer */ if (t == cbout) /* if buffer was full */ return; /* drop character */ cbin = t; } /* * writecb * Store a pascal string into the console input buffer. */ writecb (string) char *string; { int l, i; l = *string++; /* length */ for (i = 0; i < l; i++) writecbc(*string++); } /* C O N B I N -- Put console in binary mode */ /* Returns 0 if ok, -1 if not */ int conbin (char esc) { #pragma unused (esc) return 0; } /* * portnameToDriver * * Return false if error */ Boolean portnameToDriver (unsigned char *port_name, unsigned char *input_driver, unsigned char *output_driver ) { CRMRecPtr crmp; CRMRec dummycrmrec; CRMSerialPtr serp; char hs1, hs2, hs3; Boolean found = false; /* if CTB ... */ if (NGetTrapAddress(0x8b, OSTrap) != NGetTrapAddress(0x9f,OSTrap)) { crmp = &dummycrmrec; dummycrmrec.crmDeviceType = crmSerialDevice; dummycrmrec.crmDeviceID = 0; while (crmp = (CRMRecPtr)CRMSearch((QElemPtr)crmp)) { if (serp = (CRMSerialPtr)crmp->crmAttributes) { hs1 = HGetState((Handle)serp->outputDriverName); hs3 = HGetState((Handle)serp->inputDriverName); hs2 = HGetState((Handle)serp->name); HLock((Handle)serp->outputDriverName); HLock((Handle)serp->inputDriverName); HLock((Handle)serp->name); /* if portname match */ if (EqualString(port_name, *serp->name, false, false)) { if (input_driver) BlockMove(*(char **)serp->inputDriverName, input_driver, 256 ); if (output_driver) BlockMove(*(char **)serp->outputDriverName, output_driver, 256 ); found = true; } HSetState((Handle)serp->outputDriverName, hs1); HSetState((Handle)serp->inputDriverName, hs3); HSetState((Handle)serp->name, hs2); if (found) return true; } } } else { if (EqualString(port_name, "\pPrinter", false, false)) { if (input_driver) BlockMove("\p.Bin", input_driver, 5); if (output_driver) BlockMove("\p.Bout", output_driver, 6); return true; } else { /* default to Phone */ if (input_driver) BlockMove("\p.Ain", input_driver, 5); if (output_driver) BlockMove("\p.Aout", output_driver, 6); return true; } } if (input_driver) *input_driver = '\0'; if (output_driver) *output_driver = '\0'; return false; } /* * Sets the default serial port name. */ void default_serial_port (char *default_name) { CRMRecPtr crmp; CRMRec dummycrmrec; CRMSerialPtr serp; char hs1, hs2, hs3; Boolean pass2 = false; Boolean found = false; /* * If the CTB is not present, default to the phone port. */ if (NGetTrapAddress(0x8b, OSTrap) == NGetTrapAddress(0x9f,OSTrap)) { strcpy(default_name, "Phone"); return; } /* * With the CTB, default to the Modem Port, unless it does not exist in * which case we use the first serial port we can find. */ retry: crmp = &dummycrmrec; dummycrmrec.crmDeviceType = crmSerialDevice; dummycrmrec.crmDeviceID = 0; while (crmp = (CRMRecPtr)CRMSearch((QElemPtr)crmp)) { if (serp = (CRMSerialPtr)crmp->crmAttributes) { hs1 = HGetState((Handle)serp->outputDriverName); hs3 = HGetState((Handle)serp->inputDriverName); hs2 = HGetState((Handle)serp->name); HLock((Handle)serp->outputDriverName); HLock((Handle)serp->inputDriverName); HLock((Handle)serp->name); if (pass2 || EqualString("\pModem Port", *serp->name, false, false) ) { BlockMove(*(char **)serp->name, default_name, 256); p2cstr(default_name); found = true; } HSetState((Handle)serp->outputDriverName, hs1); HSetState((Handle)serp->inputDriverName, hs3); HSetState((Handle)serp->name, hs2); if (found) return; } } if (pass2) macfatal("Could not find any serial ports to use", 0); pass2 = true; goto retry; } /* * Return driver ref if driver is open */ short isOpenDriver (unsigned char *driverName) { short dref = 0; DCtlHandle dceHndl; if (dceHndl = findDriver(driverName)) if ((*dceHndl)->dCtlFlags & 0x20) /* if open (bit 5) */ dref = (*dceHndl)->dCtlRefNum; return dref; } /* * findDriver * Return driver dctlhandle if it exists. */ /* enum { UnitNtryCnt = 0x1D2 }; */ /* [GLOBAL VAR] count of entries in unit table [word] */ #define LMGetUTableBase() (* (Ptr *) 0x011C) DCtlHandle findDriver (unsigned char *driverName) { short count; DCtlEntry **entry, ***utable; unsigned char *namePtr; count = *(short *)UnitNtryCnt; /* number of entries in unit table */ utable = (DCtlEntry ***)LMGetUTableBase(); /* the unit table */ while (count-- > 0) { entry = *utable++; if (entry) { /* see if ram based (test bit 6) */ if ((*entry)->dCtlFlags & 0x40) { /* in ram, so we have a handle */ namePtr = (CHAR *)(*(Handle)((*entry)->dCtlDriver)) + 18; } else { /* in rom, so we have a pointer */ namePtr = (CHAR *)((*entry)->dCtlDriver) + 18; } /* compare name, case insensitive, diacritical sensitive */ if (RelString(driverName, namePtr, false, true) == 0) { return entry; } } } return 0; } /* * HaveSerialPortArbitration * Return true if the serial port arbitrator is present. */ #define gestaltArbitorAttr 'arb ' #define gestaltSerialArbitrationExists 0 #pragma parameter __D0 my_Gestalt(__D0,__A1) pascal OSErr my_Gestalt(OSType selector,long *response) = {0xA1AD,0x2288}; #define _Unimplemented 0xA89F #define _GestaltDispatch 0xA0AD Boolean HaveSerialPortArbitration() { int io; long attribs; /* * My copy of Think has bad glue for Gestalt, so do the * checks here. */ if (NGetTrapAddress((short)_GestaltDispatch, OSTrap) == NGetTrapAddress((short)_Unimplemented, ToolTrap)) return false; io = my_Gestalt(gestaltArbitorAttr, &attribs); return ((io == noErr) && (attribs & (1<