[comp.sources.mac] Serial Printing desk accessory source

earleh@dartvax.UUCP (Earle R. Horton) (10/05/87)

[Serial Printing desk accessory source]

This is the source for a desk accessory I wrote to get letter-quality
printing from my Mac without buying a LaserWriter.  I wrote it to be
as general as possible.  I wanted to have:

     User-selectable strings for initializing the printer, etc.
     Variable left margins and page length.
     Support of either sheet feed or continuous feed.
     A bunch of communications port options.
     Compatible with 64k ROMs (God knows why).
     Expands tabs, or not, as required.
     Prints text only files with carriage return at end of each line.
     Most important: work on as many different printer types as possible.

I have posted the binhex'd DA to comp.binaries.mac.  These are the sources.
The DA worked with my Tandy DMP-110 and with my Silver-Reed Penman Deluxe
with the I/F40 Serial Interface.  I tried a bunch of baud rates, and 
both CTS and ^S^Q flow control.  I regret that I didn't support ^S^Q
flow control for 64k ROMs, but you never can tell whether the RAM serial
driver is going to be present...

It seemed to work with all the printer settings I threw at it, so I posted
the binhex, and here are the sources.  The comments are kinda skimpy, but
I believe I have used enough meaningful variable names to compensate.
Even if you see no need for the desk accessory since your boss just got
a LaserWriter or whatever, you might be able to glean some programming
tips from this code.  How to send junk out the serial port, how to push
radio buttons, how to manipulate STR# resources in memory, how to 
highlite the "OK" button when using ModalDialog() instead of Alert().

You might even see some huge bugs, and then be able to flame me in
next week's comp.sys.mac (or whatever they want to call it next week).
If you want, you can flame me for not writing a Chooser-compatible
printer driver instead of this.  (Make my day.)

The system I used was LSC and RMaker.  Code may differ for other systems.
-- 
*********************************************************************
*Earle R. Horton, H.B. 8000, Dartmouth College, Hanover, NH 03755   *
*********************************************************************

---
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	SerialPrint.c
#	SerialPrint.rsrc
sed 's/^X//' << 'SHAR_EOF' > SerialPrint.c
X/*
X * SerialPrint.c.  A generic serial printer driver in desk accessory
X * form for the Macintosh.
X * Earle R. Horton.  August 17, 1987
X * LightspeedC source.
X * To use, build a project with this file and MacTraps in it.
X * Set the project type to Desk Accessory.  Build the desk accessory.
X * Run RMaker to put the owned resources in the suitcase file.
X * Do not attempt to "Run" from within LightspeedC.  The use of owned
X * resources presents problems here.
X */
X#include <DeviceMgr.h>
X#include <WindowMgr.h>	/* includes QuickDraw.h, MacTypes.h */
X#include <EventMgr.h>
X#include <MenuMgr.h>
X#include <FileMgr.h>
X#include <DialogMgr.h>
X#include <SerialDvr.h>	
X#include <StdFilePkg.h>
X#include <ControlMgr.h>
X
X# define	nil			0L
X
X/*  global variables and macros to use them */
Xtypedef struct{
X	int	dummy[9];
X} pconfig,*Pcfg,**Pfg;
XPfg	settings = nil;
X/* Be a real Mac cowboy, access data by the handle. */
X#define pport 		((*settings)->dummy[0])
X#define pbaud 		((*settings)->dummy[1])
X#define sheetfeed	((*settings)->dummy[2])
X#define tabstops 	((*settings)->dummy[3])
X#define width 		((*settings)->dummy[4])
X#define margin 		((*settings)->dummy[5])
X#define pagelength 	((*settings)->dummy[6])
X#define XonXoff		((*settings)->dummy[7])
X#define SDClose		((*settings)->dummy[8])
X/* Yeeeeee-Hah !! I won't even lock it! */
Xtypedef struct bauds{
X	int	rate;
X	char label[10];
X}BAUDS;
XBAUDS	mybauds[] = {			/* Baud rate constant table. */
X	{ baud300,	"\p300"},	/* Should use a string list. */
X	{ baud600,	"\p600"},	/* But these are numbers, so I */
X	{ baud1200,	"\p1200"},	/* Think it's OK. */
X	{ baud1800,	"\p1800"},
X	{ baud2400,	"\p2400"},
X	{ baud3600,	"\p3600"},
X	{ baud4800,	"\p4800"},
X	{ baud7200,	"\p7200"},
X	{ baud9600,	"\p9600"},
X	{ baud19200,	"\p19200"}
X};
Xtypedef struct {			/* Structure description of STR# */
X	int	numstrings;		/* resource. */
X	unsigned char	thestrings[];
X}stringlist,**StrList;
XStrList	mystrings = nil;
XPoint where = {80,80};
XSFTypeList	mytypelist = {'TEXT'};
XParamBlockRec 	iopb;
XParamBlockRec 	filepb;
Xchar _iobuf[522];
Xint ResID;
Xstatic int phone   =   	FALSE;
Xstatic int printer = 	FALSE;
Xstatic MenuHandle	MHandl;
Xstatic CHANGE1	   = 	FALSE;
Xstatic CHANGE2     = 	FALSE;
Xstatic	int		rom,machine;
X
X/* Useful constants */
X#define OWNED_BASE -16384
X#define BAUDLABEL 8		/* Item numbers of configuration box. */
X#define BAUDBUTTON 9
X#define DONEITEM 1
X#define STOPITEM 2
X#define MODEM	5
X#define PRINTER 6
X
X/* Menu item numbers */
X#define ABOUTITEM	1
X#define SETITEM 	2
X#define PAGEITEM 	3
X#define FLOWITEM	4
X#define PRINTITEM 	5
X#define SAVEITEM	6
X#define QUITITEM 	7
X/* Printer setup editText Item numbers */
X
X#define	TABSITEM	5
X#define	WIDTHITEM	6
X#define	MARGINITEM	7
X#define	LINESITEM	8
X
X#define EOLITEM		11
X#define	INITITEM	12
X#define	TOPITEM		13
X#define	EOPITEM		14
X#define	EOFITEM		15
X#define SHEETITEM	19
X#define CONTINUOUSITEM	20
X#define NUMSTRINGS	5
X#define RESPAD		24
X
X#define	SERRESET	8    /* csCodes for PBControl */
X#define	SERSHAKE	10
X
X#define FLOWBOX		(ResID + 4)  /* Should have done all like this */
X					/* forgot... */
X#define XONCR	((char)17)
X#define XOFFCR	((char)19)
X
X
Xmain(p, d, n)
XcntrlParam *p;		/*  ==> parameter block  */
XDCtlPtr d;		/*  ==> device control entry  */
Xint n;			/*  entry point selector  */
X
X{
X
X	/*  check to make sure our data area was allocated  */
X		
X	if (d->dCtlStorage == 0)
X	{
X		if (n == 0)	/*  open  */
X			CloseDriver(d->dCtlRefNum);
X	}
X	else switch (n)		/*  dispatch  */
X	{
X	    case 0:		/*  open  */
X		ResID = (~d->dCtlRefNum)<<5 | OWNED_BASE; 
X		d->dCtlMenu = ResID;
X		MHandl = GetMenu(ResID);
X		if(MHandl == nil){
X			CloseDriver(d->dCtlRefNum);
X			return(0);
X		}			
X		InsertMenu(MHandl,0);
X		DrawMenuBar();
X		if ((settings = (Pfg)(GetResource('Stng',ResID))) == nil ||
X		    (mystrings = (StrList)(GetResource('STR#',ResID))) == nil){
X			CloseDriver(d->dCtlRefNum);
X			return(0);
X		}
X		LoadResource(settings);
X		HNoPurge(settings);
X		LoadResource(mystrings);
X		HNoPurge(mystrings);
X		HLock(mystrings);
X		Environs(&rom,&machine);
X		if(rom < 117){			/* If < 512KE, no XOn/XOff */
X			SDClose = FALSE;
X			XonXoff = FALSE;
X		}
X		break;
X
X	    case 2:		/*  control  */
X		switch (p->csCode){
X		    case accMenu:
X			switch (p->csParam[1]){
X			    case ABOUTITEM:
X				DoAbout();
X				break;
X			    case SETITEM:
X				if(!prsetup())
X				    CloseDriver(d->dCtlRefNum);
X				break;
X			    case PAGEITEM:
X				if(!pagesetup())
X				    CloseDriver(d->dCtlRefNum);
X				break;
X			    case FLOWITEM:
X				moresettings();
X				break;
X			    case PRINTITEM:
X				ffprint();
X				break;
X			    case SAVEITEM:
X				if(CHANGE1){
X				    ChangedResource(settings);
X				}
X				if(CHANGE2){
X				    ChangedResource(mystrings);
X				}
X				if(CHANGE1 || CHANGE2)
X				    UpdateResFile(HomeResFile(settings));
X				break;
X			    case QUITITEM:
X				CloseDriver(d->dCtlRefNum);
X				break;
X			}
X			break; 
X		}
X		break;
X	    case 4:		/*  close  */
X		    quit();
X		    break;
X	}
X	
X	/*  done  */
X	
X	return(0);
X}
Xquit()
X{
X	if(MHandl != nil){
X		DeleteMenu(ResID);
X		ReleaseResource(MHandl);
X		DrawMenuBar();
X		MHandl = nil;
X	}
X	if(SDClose){
X		if(printer)RAMSDClose(sPortA);
X		if(phone)RAMSDClose(sPortB);
X	}
X	if (settings != nil) {	/* We don't want to leave our junk laying */
X		HPurge(settings);		/* around... */
X		ReleaseResource(settings);
X		settings = nil;
X	}
X	if (mystrings != nil) {
X		ReleaseResource(mystrings);
X		mystrings = nil;
X	}
X}
Xpushradiobutton(thedialog,itemhit,first,last)	/* push a radio Button */
XDialogPtr thedialog;				/* set itemhit, unset  */
Xint itemhit,first,last;				/* all others in range */
X{
X	int itemtype,i;
X	Handle itemhandle;			/* Does check boxes, too. */
X	Rect itemrect;
X	ControlHandle itemcntlhand;
X	if(first ==0) return;
X	for(i=first-1;last-i++;){
X		GetDItem(thedialog,i,&itemtype,&itemhandle,&itemrect);
X		itemcntlhand = (ControlHandle)itemhandle;
X		if(i == itemhit) SetCtlValue(itemcntlhand,1);
X		else SetCtlValue(itemcntlhand,0);
X	}
X}
Xpagesetup()		/* Derive left margin, tab stops, page length */
X{			/* and number columns.			      */
XDialogPtr pagedialog;
XWindowPtr tempport;
Xint 	itemhit,i,numtype,donetype;
XHandle	numitem,doneitem;
XRect	numbox,donebox;
XStr255	numtext;
Xlong	temp;
X	if((pagedialog = GetNewDialog(ResID+1, 0L,(WindowPtr) -1)) == nil)
X		return FALSE;
X	GetDItem(pagedialog,DONEITEM,&donetype,&doneitem,&donebox);
X	for(i = TABSITEM - 1;LINESITEM - i++;){
X	    GetDItem(pagedialog,i,&numtype,&numitem,&numbox);
X	    NumToString((long)((*settings)->dummy[i-2]),numtext);
X	    SetIText(numitem,numtext);
X	}
X	GetPort(&tempport);
X	SetPort(pagedialog);
X	PenSize(3,3);
X	InsetRect(&donebox,-4,-4);
X	FrameRoundRect(&donebox,16,16);
X	itemhit = 0;
X	while(itemhit != DONEITEM){
X	      ModalDialog(0L,&itemhit);
X	      switch(itemhit){
X		  default:
X		      break;
X	      }
X	}
X	for(i = TABSITEM - 1;LINESITEM - i++;){
X	    GetDItem(pagedialog,i,&numtype,&numitem,&numbox);
X	    GetIText(numitem,numtext);
X	    StringToNum(numtext,&temp);
X	    (*settings)->dummy[i-2] = (int)temp;
X	}
X	DisposDialog(pagedialog);
X	SetPort(tempport);                  		
X	CHANGE1 = TRUE;
X	return(TRUE);
X}
X#define XONXOFF	2
X#define CTS	3
X#define SDCLOSE	4
Xmoresettings()		/* If 64k ROM or XL, show dialog, do nothing. */
X{
XDialogPtr flowdialog;
XWindowPtr tempport;
Xint	itemhit,donetype;
XHandle	doneitem;
XRect	donebox;
X	if((flowdialog = GetNewDialog(FLOWBOX,0L,(WindowPtr) -1)) == nil)
X		return FALSE;
X	GetDItem(flowdialog,DONEITEM,&donetype,&doneitem,&donebox);
X	GetPort(&tempport);
X	SetPort(flowdialog);
X	PenSize(3,3);
X	InsetRect(&donebox,-4,-4);
X	FrameRoundRect(&donebox,16,16);
X	if (SDClose)
X	    pushradiobutton(flowdialog, SDCLOSE,SDCLOSE,SDCLOSE);
X	else 
X	    pushradiobutton(flowdialog,0,SDCLOSE,SDCLOSE);
X	if (XonXoff) 
X	    pushradiobutton(flowdialog,XONXOFF,XONXOFF,CTS);
X	else 
X	    pushradiobutton(flowdialog,CTS,XONXOFF,CTS);
X	itemhit = 0;
X	while(itemhit != DONEITEM){
X		ModalDialog(0L,&itemhit);
X		if( rom >= 117){  /* If ROM version < 117, forget this. */
X		    switch(itemhit){
X			case XONXOFF:
X			    XonXoff = TRUE;
X			    pushradiobutton(flowdialog, itemhit,XONXOFF,CTS);
X			    break;
X			case CTS:
X			    XonXoff = FALSE;
X			    pushradiobutton(flowdialog, itemhit,XONXOFF,CTS);
X			    break;
X			case SDCLOSE:
X			    SDClose = !SDClose;
X			    if (SDClose)
X				pushradiobutton(flowdialog,
X				    SDCLOSE,SDCLOSE,SDCLOSE);
X			    else 
X				pushradiobutton(flowdialog,0,SDCLOSE,SDCLOSE);
X			    break;
X		    }
X		}
X	}
X	CHANGE1 = TRUE;
X	DisposDialog(flowdialog);
X	SetPort(tempport);
X	return TRUE;
X}
X
X /* This one enquires for and sets the printer variables.
X * Radio buttons (cute) are used.
X */
Xprsetup()
X{
XDialogPtr printdialog;
XWindowPtr tempport;
Xint 	itemhit,i,baudtype,edittype,donetype;
XHandle	bauditem,doneitem,edititem;
XRect	baudbox,donebox,editbox;
XStr255	thestring;
Xunsigned char	*strptr;
Xlong	length;
X	if (pbaud<0 || pbaud>9) pbaud = 0;
X	if((printdialog = GetNewDialog(ResID, 0L,(WindowPtr) -1)) == nil)
X		return FALSE;
X	GetDItem(printdialog,BAUDLABEL,&baudtype,&bauditem,&baudbox);
X	GetDItem(printdialog,DONEITEM,&donetype,&doneitem,&donebox);
X	SetIText(bauditem,mybauds[pbaud].label);
X/* This gets the editText items in the dialog box to contain
X * the strings from one of our string lists.
X */
X	strptr = &((*mystrings)->thestrings[0]);
X	for(i = EOLITEM-1;EOFITEM - i++;){
X	    GetDItem(printdialog,i,&edittype,&edititem,&editbox);
X	    SetIText(edititem,strptr);
X	    strptr += (*strptr) + 1;
X	}
X	GetPort(&tempport);
X	SetPort(printdialog);
X	PenSize(3,3);
X	InsetRect(&donebox,-4,-4);
X	FrameRoundRect(&donebox,16,16);
X	pushradiobutton(printdialog, pport+5,5,6);
X	pushradiobutton(printdialog,SHEETITEM + sheetfeed,SHEETITEM,
X	    CONTINUOUSITEM);
X	itemhit = 0;
X	while(itemhit != DONEITEM){
X	      ModalDialog(0L,&itemhit);
X	      switch(itemhit){
X			case MODEM:		/* port change. */
X			case PRINTER:
X				pport = itemhit - MODEM;
X				pushradiobutton(printdialog,itemhit,
X				   MODEM,PRINTER);
X				break;
X			case BAUDBUTTON:	/* next baud rate change */
X			    /* Ten radio buttons would be just too much. */
X				if(++pbaud == 10) pbaud = 0;
X				SetIText(bauditem,mybauds[pbaud].label);
X				break;
X			case SHEETITEM:	      /* feed options */
X			case CONTINUOUSITEM:
X				sheetfeed = itemhit - SHEETITEM;
X				pushradiobutton(printdialog,
X				    itemhit,SHEETITEM,CONTINUOUSITEM);
X				break;
X		}
X	}
X	/* The user has set the baud rate and the port, and also possibly
X	 * edited the printer control strings.  Since we used ModalDialog()
X	 * with no filterproc we don't know whether any of the strings have
X	 * been changed.  Therefore we just rebuild the whole string list.
X	 * First, determine the length.
X	 */
X	length = (long) (sizeof(int)+RESPAD);
X	/* Try doing this with RESPAD = 0! */
X	for(i = EOLITEM-1;EOFITEM - i++;){
X	    GetDItem(printdialog,i,&edittype,&edititem,&editbox);
X	    GetIText(edititem,thestring);
X	    length += (long) thestring[0];
X	}
X	/* Size might have changed, so we unlock the handle and attempt to
X	 * resize it.  Should work, unless no RAM left. */
X	HUnlock(mystrings);
X	SetHandleSize(mystrings,length);
X	if (GetHandleSize(mystrings)!=length){	/* Abort on error. */
X	    DisposDialog(printdialog);
X	    SetPort(tempport);                  		
X	    return(FALSE);
X	}
X	HNoPurge(mystrings);
X	HLock(mystrings);
X	/* Rebuild the STR# from the item list. */
X	strptr = &((*mystrings)->thestrings[0]);
X	for(i = EOLITEM-1;EOFITEM - i++;){
X	    GetDItem(printdialog,i,&edittype,&edititem,&editbox);
X	    GetIText(edititem,strptr);
X	    strptr += (*strptr) + 1;
X	}
X	DisposDialog(printdialog);
X	SetPort(tempport);                  		
X	CHANGE2 = CHANGE1 = TRUE;
X	return(TRUE);
X}
Xwaitnextpage()		/* For sheet feeders. */
X{
XDialogPtr sheetdialog;
XWindowPtr tempport;
Xint	itemhit,donetype;
XHandle	doneitem;
XRect	donebox;
X	if((sheetdialog = GetNewDialog(ResID+3, 0L,(WindowPtr) -1)) == nil)
X		return FALSE;
X	GetDItem(sheetdialog,DONEITEM,&donetype,&doneitem,&donebox);
X	GetPort(&tempport);
X	SetPort(sheetdialog);
X	PenSize(3,3);
X	InsetRect(&donebox,-4,-4);
X	FrameRoundRect(&donebox,16,16);
X	ModalDialog(0L,&itemhit);
X	DisposDialog(sheetdialog);
X	SetPort(tempport);
X	if(itemhit == STOPITEM) return FALSE;
X	return TRUE;
X}
X
Xchar outbuf[200];	/* Or whatever size you think appropriate. */
Xchar inbuf[200];
Xffprint()
X{
Xregister int    nline,nchars,i,tab;
Xregister 	ParmBlkPtr pb;
Xchar 		c;
Xint		serconfig,drivernum,length,filefinished;
XEventRecord	myevent;
Xlong 		count1;
Xunsigned char	*eolstr,*initstr,*topstr,*eopstr,*eofstr;
X
X	pb = &iopb;
X	/* Get strings from string list.  (Pascal strings) */
X	eolstr = eofstr = &((*mystrings)->thestrings[0]);
X	initstr = (eofstr += (*eofstr) + 1);
X	topstr = (eofstr += (*eofstr) + 1);
X	eopstr = (eofstr += (*eofstr) + 1);
X	eofstr += (*eofstr) + 1;
X
X	tab = tabstops - 1;
X	switch (pport){		/* get the correct port */
X		case 0:		/* modem port */
X			if(!phone) {
X				prinit("\p.AOut");
X				phone = TRUE;
X				if(rom >= 117)RAMSDOpen(sPortA);
X			}
X			drivernum = AoutRefNum;
X			break;
X		case 1:		/* printer port */
X			if(!printer) {
X				prinit("\p.BOut");
X				printer = TRUE;
X				if(rom >= 117)RAMSDOpen(sPortB);
X			}
X			drivernum = BoutRefNum;
X			break;
X		}
X	/* set up the io parameter block for writing to the serial driver. */
X	/* a control call resets the baud rate */
X	pb->ioParam.ioRefNum = drivernum;
X	pb->ioParam.ioCompletion = nil;
X	((cntrlParam *)pb)->csCode = SERRESET;
X	serconfig = data8 + noParity + stop20;
X	serconfig += mybauds[pbaud].rate;
X	((cntrlParam*)pb)->csParam[0] = serconfig;
X	PBControl(pb,FALSE);
X#define shake 	((SerShk*)&((cntrlParam*)pb)->csParam[0])
X	if(rom >= 117){		/* If ROM version < 117, forget this. */
X	    shake->errs = FALSE;
X	    shake->evts = FALSE;
X	    shake->fDTR = FALSE;
X	    shake->fInX = FALSE;
X	    if(XonXoff){
X		shake->fXOn = TRUE;
X		shake->fCTS = FALSE;
X		shake->xOn = XONCR;
X		shake->xOff = XOFFCR;
X	    }
X	    else {
X		shake->fXOn = FALSE;
X		shake->fCTS = TRUE;
X	    }
X	    ((cntrlParam *)pb)->csCode = SERSHAKE;
X	    PBControl(pb,FALSE);
X	}
X#undef shake
X	pb->ioParam.ioPosMode = 0;
X	pb->ioParam.ioPosOffset = 0;
X	
X	for(nchars=0;margin-nchars;nchars++){ /* pad the sucker on the  */
X		outbuf[nchars] = ' ';         /* left with margin spaces */
X	}
X
X	while(ffopen()){
X	    printstring(initstr);
X	    filefinished = FALSE;
X	    nline = 0;
X	    while (!filefinished){
X		if(nline == 0){			/* top of a page */
X			printstring(topstr);
X			nline++;
X		}
X		else if (nline == pagelength + 2){		/* bottom */
X			printstring(eopstr);
X			nline = 0;
X			if(sheetfeed == 0) {
X			    if (waitnextpage() == FALSE) {
X				PBClose(&filepb,FALSE);
X				return;
X			    }
X			}
X		}
X		else {						/* body */
X		    if(!getline(inbuf,width,&length)) 
X			    filefinished = TRUE;
X		    nchars = 0;
X		    i = 0;
X		    while( i <= width && nchars <= length){
X			switch(c = inbuf[nchars++]){
X			    case 9: /* a tab */
X				if (tab){
X				    do{
X					outbuf[margin + i++] = ' ';
X					}while((i % tab)!=0);
X				}
X				else
X				    outbuf[margin + i++]= c;
X				break;
X			    default:
X				outbuf[margin + i++]= c;
X				break;
X			}
X		    }
X		    count1 = (long) (i + margin - 1);
X		    nline++;       /* count lines */
X		/* check after every line for user abort */
X		/* Admitted, less effective at high baud rates. */
X		if (GetNextEvent(everyEvent, &myevent))  {
X		    switch (myevent.what) {
X			case keyDown:
X			    c = LoWord(myevent.message & charCodeMask);
X			    if (myevent.modifiers & cmdKey) {
X				if( c == '.'){
X				     PBClose(&filepb,FALSE);
X				    return;
X				}	/* quit now */
X			    }
X			    break;
X			default:
X			    break;
X		    }
X		}
X		    /* write out the expanded line */
X		    /* to the proper device driver. */
X		if(!filefinished){
X		    pb->ioParam.ioBuffer = outbuf;
X		    pb->ioParam.ioReqCount = count1;
X		    PBWrite(pb,FALSE);
X		    printstring(eolstr);
X		}
X		}
X	    }
X	PBClose(&filepb,FALSE);
X	printstring(eofstr);
X	}
X}
Xchar stringbuf[256];
X/*
X * Used for interpreting the printer control codes. Not very efficient,
X * since they are interpreted every time sent.
X */
Xprintstring(string)
Xunsigned char	string[];		
X{
X	register unsigned int	i;
X	long	count;
X	count = 0L;
X	i = string[0];
X	for(i=0;string[0]-i++;){
X	    switch (string[i]){
X		case '^':
X		    stringbuf[count] = 31 & string[++i];
X		    break;
X		default:
X		    stringbuf[count] = string[i];
X		    break;
X	    }
X	    count++;
X	}
X	iopb.ioParam.ioBuffer = stringbuf;
X	iopb.ioParam.ioReqCount = count;
X	PBWrite(&iopb,FALSE);
X}
Xprinit(name)				    /* Open a driver by name. */
X		/* ROM serial driver?  RAM serial driver?  Who cares? */
X		/* Seriously, if ROM is 128k or newer, we do the RAM  */
X		/* serial driver open later. */
Xchar *name;
X{
X	iopb.ioParam.ioNamePtr = (StringPtr)name;
X	iopb.ioParam.ioCompletion = nil;
X	iopb.ioParam.ioPermssn = 0;
X	PBOpen(&iopb,FALSE);
X}
Xffopen ()
X{
Xstatic	SFReply frommac;
Xregister ParmBlkPtr pb;
X
Xpb = &filepb;
X	
X	SFGetFile (where, 0L, 0L, 1, mytypelist, 0L, &frommac);
X	if (frommac.good) {
X		pb->ioParam.ioNamePtr = frommac.fName;
X		pb->ioParam.ioCompletion = nil;
X		pb->ioParam.ioVersNum = 0;
X		pb->ioParam.ioMisc = _iobuf;
X		pb->ioParam.ioVRefNum = frommac.vRefNum;
X		pb->ioParam.ioPermssn = fsRdPerm;
X		pb->ioParam.ioPosMode = 3456;
X		PBOpen(pb,FALSE);
X		if(pb->ioParam.ioResult){
X			return (FALSE);
X		}
X		else return (TRUE);
X	}
X	else return (FALSE);
X}
X/*
X * Read a line from a file, and store the bytes in the supplied buffer. The
X * "nbuf" is the length of the buffer. 	Return FALSE if no bytes read.
X * filepb.ioParam.ioPosMode determines that we stop at the newline
X * character, which is <cr>.  Should possibly stop on error, but I 
X * think error implies no bytes read (?).
X */
Xgetline(buf,nbuf,nbytes)
Xregister char   buf[];
Xint nbuf;
Xint *nbytes;
X{
Xregister int    i;
Xregister ParmBlkPtr pb;
X
Xpb = &filepb;
X
X	pb->ioParam.ioPosMode = 3456;
X	/* This means reads stop at '\r', see IM for details. */
X	pb->ioParam.ioBuffer = buf;
X	pb->ioParam.ioReqCount = (long)nbuf;
X	PBRead(pb,FALSE);
X	if ((i = (int)pb->ioParam.ioActCount) == 0){
X	     *nbytes = 0;
X	     return (FALSE);
X	}
X	if(buf[i-1] == '\r') i--;	/* strip CR */
X	*nbytes = i;	
X	return (TRUE);
X}
X/*
X * DoAbout:
X *
X *	Dialog box handler for extended about box.  Put up a dialog
X *	box with help information.  Cycle through a string list
X *	(STR# ID HLIST) to change the contents of a statText item.
X *	Help information is updated by changing the contents of the
X *	STR#, making things easier to localize.
X */
X#define HDLOG ResID+2
X#define HLIST ResID+1
X#define HITEM 5
X#define MORE 1
X#define BACK 2
X#define ENOUGH 3
X#define	int16	short
XDoAbout()
X{
Xint16 		res,i,helptype,nlimit;
XDialogPtr	helpdialog;
XWindowPtr	tempport;
XStr255		helpstring;
XHandle		helpitem,moreitem;
XRect		helpbox,morebox;
Xint16		**stringlist;
Xint16		moretype;
X	/* Prepare to use dialog box. */
X	helpdialog = GetNewDialog(HDLOG,(long)0,(WindowPtr) -1);
X	GetDItem(helpdialog,HITEM,&helptype,&helpitem,&helpbox);
X	GetDItem(helpdialog,MORE,&moretype,&moreitem,&morebox);
X	GetPort(&tempport);
X	SetPort(helpdialog);
X	PenSize(3,3);
X	InsetRect(&morebox,-4,-4);
X	FrameRoundRect(&morebox,16,16);
X	/* How many strings? */
X	stringlist = (int16 **)GetResource('STR#',HLIST);
X	if(stringlist == nil) return FALSE;
X	LoadResource(stringlist);
X	nlimit = (int16) **stringlist;
X	i = 0;
X	res = 1;
X	while(res != ENOUGH){		/* Done when quit button selected. */
X		if (res == BACK) i--;			    /* Cycle back. */
X		else i++;				/* Cycle forwards. */
X		if (i<1) i=nlimit;	   /* Choose < 1, use last string. */
X		GetIndString(helpstring,HLIST,i);	/* Get the string. */
X		if(helpstring[0] == '\0'){   /* Out of range, last string. */
X			i=1;		       /* Cycle back to first one. */
X			GetIndString(helpstring,HLIST,i);	/* Get it. */
X		}
X		SetIText(helpitem,helpstring);			/* Use it. */
X		ModalDialog(0L,&res);	    /* ModalDialog handles events. */
X	}
X	DisposDialog(helpdialog);			/* Done, clean up. */
X	SetPort(tempport);
X	ReleaseResource(stringlist);
X	return TRUE;
X}
X
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > SerialPrint.rsrc
X*Resource definition file for Device Dependent Printing Desk Accessory
X
X!LSC:SerialPrint:Serial
XDFILDMOV
X
XType DITL
XPrinter Dialog Template,-16000
X20
X
X*   1
XBtnItem Enabled
X224 143 240 213
XDone
X
X*   2
XStatText Enabled
X149 7 165 120
XBottom of page
X
X*   3
XStatText Disabled
X6 91 23 219
XPrinter Port Setup
X
X*   4
XStatText Disabled
X30 6 46 51
XPort:
X
X*   5
XRadioItem Enabled
X30 57 46 157
XModem Port
X
X*   6
XRadioItem Enabled
X30 165 46 265
XPrinter Port
X
X*   7
XStatText Disabled
X57 6 73 81
XBaud Rate:
X
X*   8
XStatText Enabled
X57 98 73 143
Xruntime
X
X*   9
XBtnItem Enabled
X58 153 74 218
XmoreI
X
X*   10
XStatText Disabled
X83 5 99 122
XLine Terminator
X
X*   11
XEditText Enabled
X83 128 99 320
Xruntime
X
X*   12
XEditText Enabled
X105 128 121 320
Xruntime
X
X*   13
XEditText Enabled
X127 128 143 320
Xruntime
X
X*   14
XEditText Enabled
X149 128 165 320
Xruntime
X
X*   15
XEditText Enabled
X171 128 187 320
Xruntime
X
X*   16
XStatText Disabled
X105 6 121 123
XInitialize
X
X*   17
XStatText Disabled
X127 7 143 124
XTop of page
X
X*   18
XStatText Enabled
X171 8 187 121
XEnd of file
X
X*    19
XRadioItem Enabled 
X197 8 213 121
XSheet feed
X
X*   20
XRadioItem Enabled
X197 165 213 265
XContinuous
X
XType DLOG
XPrinter Config Box,-16000
XSerial Port Configuration
X40 80 296 424
XVisible NoGoAway
X1 ;; procID
X0 ;; refCon
X-16000
X
XType DLOG
XPage Setup Box,-15999
XPage Setup
X48 51 165 449
XVisible NoGoAway
X1 ;; procID
X0 ;; refCon
X-15999
X
XType DLOG
XNext Page Box,-15997
XNext Page
X48 51 100 300
XVisible NoGoAway
X1 ;; procID
X0 ;; refCon
X-15997
X
XType DITL
XNext Page Template,-15997
X3
X
X*   1
XBtnItem Enabled
X30 60 46 140
XDone
X
X*   2
XBtnItem Enabled
X30 160 46 240
XStop
X
X*   3
XStatText
X8 8 24 292
XSerialPrint: Insert Next Sheet
X
X
XType DITL
XPage Setup Template,-15999
X10
X
X*   1
XBtnItem
X96 165 112 235
XDone
X
X*   2
XStatText
X64 201 82 316
XLines per page
X
X*   3
XStatText
X7 166 24 243
XPage Setup
X
X*   4
XStatText
X34 5 50 50
XTabs
X
X*   5
XEditText
X34 135 52 167
Xruntime
X
X*   6
XEditText
X32 328 49 369
Xruntime
X
X*   7
XEditText
X64 136 80 177
Xruntime
X
X*   8
XEditText
X66 331 82 376
Xruntime
X
X*   9
XStatText
X64 5 80 122
XLeft Margin
X
X*   10
XStatText
X34 202 50 277
XWidth
X
XType Stng = GNRL
XPrinter Settings,-16000
X.H	;; Printer port, 1200 baud,no sheet feed, tabstops 8, width = 90,
X0001 0002 0000 0008 005A 0008 0037 0000 0000	;;left margin 8, 55 lines/page
X
XType MENU		;; Set procID of this menu to 0 with ResEdit
XSerialPrint menu,-16000	;; for operation without la bombe
XSerialPrint
XAbout SerialPrintI
XPrinter SettingsI
XPage SetupI
XFlow ControlI
XPrintI
XSave Settings
XQuit
X
XType STR#		;; String list for EOL, EOP, BOP strings
XPrinter control strings,-16000
X5
X^M			;; End of line
X^[^W			;; Initialize printer
X^M^M^M^M^M		;; beginning of page
X^L			;; end of page
X^L			;; end of document
X
XType STR#		;; About this DA help strings
XHelp Strings,-15999
X12
XSerialPrint, Edit of August 17, 1987\0DEarle R. Horton\0DPortions Copyright THINK Technologies\0DCompiled with the LightspeedC compiler and RMaker.
XSerialPrint is an adaptable printing program meant to be used in draft mode with printers for which no Macintosh printer driver is available.
XIt prints text-only files which have a carriage return at the end of every line.\0DThis revision does not wrap long lines at word boundaries.
XThe Printer SettingsI menu allows you to change the port, baud rate, and five printer control strings.  Your printer manual should have the appropriate settings to use.
XNon-printing characters are set using control character notation.  For example, carriage return is "^M", line feed is "^J".\0DSome printers require ^M at the end of a line, some require ^M^J.
XTo set the top margin, put the appropriate number of your printer's linefeed character combinations in the box marked "Top of page".
XIf your printer does not recognize the formfeed character (^L), just put some newlines in the box marked "Bottom of page".
XThe Page SetupI menu allows setting of tabstops, left margin, #columns in page, and page length.  Page length is the number of printed rows on the page, and does not include top and bottom margins.
XThe flow control menu has no effect with 64k ROM or Mac XL systems, where CTS flow control only is used.\0DFor newer systems, this menu allows selection of either XOn/XOff or CTS.
XThe flow control menu also allows you to specify whether the printer driver used is closed when you quit SerialPrint.\0D(Not done with 64k ROMs.)
XThe most difficult part of using SerialPrint may be obtaining the proper printer cable.  Obtain pinout diagrams of your Macintosh and printer; take them to a responsible electronics technician.
XSettings are not saved unless the menu item is chosen.  This edit of SerialPrint saves its settings in the system file.
X
XType DLOG
XExtended AboutI Box,-15998
XAbout SerialPrint
X60 86 242 412
XVisible NoGoAway
X1 ;; procID
X0 ;; refCon
X-15998 ;; itemsID
X
XType DITL
XExtended AboutI Box Template,-15998
X5
X
XBtnItem
X152 44 171 109
XMore
X
XBtnItem
X152 129 171 190
XLess
X
XBtnItem
X152 210 171 285
XEnough
X
XStatText Disabled
X18 111 25 229
XSerialPrint
X
XStatText
X46 8 142 310
XString list <OWNER> + 2.
X
X* This resource owned by: DRVR 12: local id 4
XType DLOG
Xmore settings,-15996
XNew Dialog
X59 134 209 336
XVisible GoAway
X1 ;; procID
X0 ;; refCon
X-15996
X
X* This resource owned by: DRVR 12: local id 4
XType DITL
Xflow control template,-15996
X4
X
XBtnItem
X109 67 129 127
Xdone
X
XRadioItem
X10 19 30 193
XXOn/XOff Flow control
X
XRadioItem
X40 19 60 193
XCTS Flow Control
X
XChkItem
X70 19 90 193
XClose Driver on Quit
X
SHAR_EOF
exit
---