[comp.sys.handhelds] Casio BOSS <-> UNIX Interface

mdapoz@hybrid.uucp (Mark Dapoz) (07/05/90)

There seems to be a growing interest in how to communicate to a Casio BOSS
from machines other than DOS based thingies.  Here's my solution of how to
get a UNIX machine to transfer information to/from a BOSS.  It's not quite
as complete as I'd like it to be, but it's good enough to get most people
started.  The program is known to work on Sys V UNIX, it'll probably work
on BSD but I don't have immediate access to it to test it.
		-mark

---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- 

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by mdapoz@hybrid on Thu Jul  5 01:11:42 EDT 1990
# Contents:  lib/ include/ bin/ MANIFEST Makefile lib/next_field.c
#	lib/extract_tele.c lib/boss_open.c lib/boss_read.c lib/extract_memo.c
#	lib/Makefile lib/extract_bus.c lib/extract_sch.c lib/extract_cal.c
#	lib/boss_close.c lib/boss_write.c lib/add_tele.c lib/add_end_tx.c
#	lib/packet_chksm.c lib/text_packet.c lib/add_memo.c lib/date_packet.c
#	lib/add_sched.c lib/time_packet.c lib/alarm_packet.c lib/add_cal.c
#	lib/add_bcard.c include/boss.h
 
echo x - MANIFEST
sed 's/^@//' > "MANIFEST" <<'@//E*O*F MANIFEST//'
total 14
drwxr-xr-x  7 mdapoz  staff       240 Jul  5 01:08 ./
drwxrwxrwx  9 root    root       2656 Jul  5 01:09 ../
-rw-r--r--  1 mdapoz  staff       771 Jul  5 00:37 Makefile 
drwxr-xr-x  2 mdapoz  staff        64 Jul  5 00:54 bin/
drwxr-xr-x  2 mdapoz  staff       192 Jul  5 00:54 cfg/
drwxr-xr-x  2 mdapoz  staff        96 Jul  5 00:53 doc/
drwxr-xr-x  2 mdapoz  staff        48 Jul  5 00:31 include/
drwxr-xr-x  2 mdapoz  staff       736 Jul  5 00:54 lib/

@./bin:
total 2
drwxr-xr-x  2 mdapoz  staff        64 Jul  5 00:54 ./
drwxr-xr-x  7 mdapoz  staff       240 Jul  5 01:08 ../

@./cfg:
total 88
drwxr-xr-x  2 mdapoz  staff       192 Jul  5 00:54 ./
drwxr-xr-x  7 mdapoz  staff       240 Jul  5 01:08 ../
-rw-r--r--  1 mdapoz  staff       732 Jun 26 00:59 Makefile 
-rw-r--r--  1 mdapoz  staff     11120 Jun 26 00:39 boss.c 
-rw-r--r--  1 mdapoz  staff     23243 Jun 26 00:41 boss_cfg.y 
-rw-r--r--  1 mdapoz  staff      6899 Jun 26 00:42 boss_lex.l 
-rw-r--r--  1 mdapoz  staff       859 Jul  5 00:44 y.tab.h 

@./doc:
total 85
drwxr-xr-x  2 mdapoz  staff        96 Jul  5 00:53 ./
drwxr-xr-x  7 mdapoz  staff       240 Jul  5 01:08 ../
-rw-r--r--  1 mdapoz  staff     19525 Jun 25 20:15 bosslib.doc 
-rw-r--r--  1 mdapoz  staff      8567 Jul  5 00:29 grammar.spec 
-rw-r--r--  1 mdapoz  staff     13610 Jun 25 20:15 protocol.spec 

@./include:
total 13
drwxr-xr-x  2 mdapoz  staff        48 Jul  5 00:31 ./
drwxr-xr-x  7 mdapoz  staff       240 Jul  5 01:08 ../
-rw-r--r--  1 mdapoz  staff      5227 Jun 26 00:43 boss.h 

@./lib:
total 103
drwxr-xr-x  2 mdapoz  staff       736 Jul  5 00:54 ./
drwxr-xr-x  7 mdapoz  staff       240 Jul  5 01:08 ../
-rw-r--r--  1 mdapoz  staff      1505 Mar 18 18:14 Makefile 
-rw-r--r--  1 mdapoz  staff      3370 Jun 26 00:44 add_bcard.c 
-rw-r--r--  1 mdapoz  staff      2349 Jun 26 00:44 add_cal.c 
-rw-r--r--  1 mdapoz  staff       848 Jun 26 00:44 add_end_tx.c 
-rw-r--r--  1 mdapoz  staff      1953 Jun 26 00:44 add_memo.c 
-rw-r--r--  1 mdapoz  staff      2299 Jun 26 00:45 add_sched.c 
-rw-r--r--  1 mdapoz  staff      2845 Jun 26 00:45 add_tele.c 
-rw-r--r--  1 mdapoz  staff       798 Jun 26 00:45 alarm_packet.c 
-rw-r--r--  1 mdapoz  staff       166 Jun 26 00:45 boss_close.c 
-rw-r--r--  1 mdapoz  staff      1722 Jun 26 00:45 boss_open.c 
-rw-r--r--  1 mdapoz  staff      5561 Jun 26 00:45 boss_read.c 
-rw-r--r--  1 mdapoz  staff      3345 Jun 26 00:46 boss_write.c 
-rw-r--r--  1 mdapoz  staff       794 Jun 26 00:46 date_packet.c 
-rw-r--r--  1 mdapoz  staff      4175 Jun 26 00:46 extract_bus.c 
-rw-r--r--  1 mdapoz  staff      2365 Jun 26 00:46 extract_cal.c 
-rw-r--r--  1 mdapoz  staff      1906 Jun 26 00:46 extract_memo.c 
-rw-r--r--  1 mdapoz  staff      3458 Jun 26 00:46 extract_sch.c 
-rw-r--r--  1 mdapoz  staff      3424 Jun 26 00:46 extract_tele.c 
-rw-r--r--  1 mdapoz  staff       805 Jun 26 00:47 next_field.c 
-rw-r--r--  1 mdapoz  staff       464 Jun 26 00:47 packet_chksm.c 
-rw-r--r--  1 mdapoz  staff      1957 Jun 26 00:49 text_packet.c 
-rw-r--r--  1 mdapoz  staff       794 Jun 26 00:48 time_packet.c 
@//E*O*F MANIFEST//
chmod u=rw,g=r,o=r MANIFEST
 
echo x - Makefile
sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//'
# workaround for System V make bug
SHELL	= /bin/sh
BOSSLIB	= libboss
TOPDIR	= .
BINDIR	= $(TOPDIR)/bin
INCDIR	= $(TOPDIR)/include
LIBDIR	= $(TOPDIR)/lib
CFGDIR	= $(TOPDIR)/cfg
DOCDIR	= $(TOPDIR)/doc
CC	= cc
COPTS 	= -O 
#CC	= gcc
#COPTS 	= -g -shlib
LIBS 	= $(LIBDIR)/$(BOSSLIB).a

all:	$(BINDIR)/boss

$(BINDIR)/boss:  $(LIBS) /dev/null
	cd $(CFGDIR); $(MAKE) CC="$(CC)" COPTS="$(COPTS)"

$(LIBS): /dev/null
	cd $(LIBDIR); $(MAKE) CC="$(CC)" COPTS="$(COPTS)"

clean:
	-rm $(CFGDIR)/*.o $(LIBDIR)/*.o > /dev/null 2>&1

spotless: clean
	-rm $(LIBS) $(BINDIR)/* > /dev/null 2>&1
tar:
	tar -cvf $(TOPDIR)/boss.tar $(DOCDIR) $(LIBDIR)/*.c \
	$(LIBDIR)/Makefile $(TOPDIR)/Makefile $(INCDIR)  \
	$(CFGDIR)/boss_cfg.y $(CFGDIR)/boss_lex.l $(CFGDIR)/boss.c \
	$(CFGDIR)/Makefile
@//E*O*F Makefile//
chmod u=rw,g=r,o=r Makefile
 
echo mkdir - lib
mkdir lib
chmod u=rwx,g=rx,o=rx lib
 
echo x - lib/next_field.c
sed 's/^@//' > "lib/next_field.c" <<'@//E*O*F lib/next_field.c//'
/*
   next_field() - scan "string" for tokens separated by "delim"
		  similar to strtok() except null tokens are returned
			Mark Dapoz
*/

#include <stdio.h>

char *next_field(string, delim)
char *string;
char delim;
{
    static char *position;	/* position of last scan */
    static int  done;		/* end of string detected? */
    char *start, *anchor;

    if (string == NULL) {	/* NULL indicates same string as last call */
	if (done)
	    return(NULL);	/* end of string reached */
	start=anchor=position;
    } else {
	done=0;			/* first invocation */
	start=anchor=string;
    }
    while (*start != delim && *start) /* look for delimiter or end of string */
        start++;
    if (!*start) /* end of string? */
	done=1;
    *start='\0'; /* tokenize */
    position=start+1;
    return(anchor);
}
@//E*O*F lib/next_field.c//
chmod u=rw,g=r,o=r lib/next_field.c
 
echo x - lib/extract_tele.c
sed 's/^@//' > "lib/extract_tele.c" <<'@//E*O*F lib/extract_tele.c//'
/*
   extract_tele() - extract telephone entries from a stream of linked list 
		    packets
				Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct telephone *extract_tele(packet_ptr)
struct boss_packet *packet_ptr;
{
    int	 tele_record=0;	/* marker to indicate current record is for tele */
    char rec_data[384];	/* buffer to hold one record of data */
    char *tok_ptr;	/* used in breaking up record into fields */
    int  tok_len;
    char *mem_ptr;
    int	 i;
    struct telephone *work_tele_ptr;
    struct telephone *base_tele_ptr;

    extern char *next_field();

    base_tele_ptr=NULL;
    *rec_data='\0';
    do {
	switch(packet_ptr->header.control) {
	    case SEL_DEST: /* packet for selecting destination */
		tele_record=packet_ptr->data[0] == DEST_TEL ? 1 : 0;
		break;
	    case REC_END: /* end of record packet */
		if (tele_record) { /* ignore if telephone not destination */
		    if (base_tele_ptr == NULL) { /* first record? */
		    	base_tele_ptr=work_tele_ptr=(struct telephone *)
			    malloc(sizeof(struct telephone));
		    } else { /* add to linked list of records */
		    	work_tele_ptr->next=(struct telephone *)
			    malloc(sizeof(struct telephone));
		    	work_tele_ptr=work_tele_ptr->next;
		    }
		    work_tele_ptr->next=NULL; /* init data in struct */
		    work_tele_ptr->name=
		    work_tele_ptr->tel_number=
		    work_tele_ptr->addr=
		    work_tele_ptr->free_1=
		    work_tele_ptr->free_2=
		    work_tele_ptr->free_3=
		    work_tele_ptr->free_4=
		    work_tele_ptr->free_5=
		    work_tele_ptr->free_6=NULL;
		    work_tele_ptr->marked =	/* marked record? */
			packet_ptr->header.target ? 1 : 0;
		    tok_ptr=next_field(rec_data, '\n'); /* split into fields */
		    for (i=0; tok_ptr!=NULL; tok_ptr=next_field(NULL,'\n'),i++){
			if (tok_len=strlen(tok_ptr)) /* allocate room for data*/
			    mem_ptr=(char *)malloc(tok_len+1);
			else
			    continue;
			switch(i) { /* put in appropriate spot in struct */
			    case 0:
				work_tele_ptr->name=mem_ptr;
				strcpy(work_tele_ptr->name, tok_ptr);
				break;
			    case 1:
				work_tele_ptr->tel_number=mem_ptr;
				strcpy(work_tele_ptr->tel_number, tok_ptr);
				break;
			    case 2:
				work_tele_ptr->addr=mem_ptr;
				strcpy(work_tele_ptr->addr, tok_ptr);
				break;
			    case 3:
				work_tele_ptr->free_1=mem_ptr;
				strcpy(work_tele_ptr->free_1, tok_ptr);
				break;
			    case 4:
				work_tele_ptr->free_2=mem_ptr;
				strcpy(work_tele_ptr->free_2, tok_ptr);
				break;
			    case 5:
				work_tele_ptr->free_3=mem_ptr;
				strcpy(work_tele_ptr->free_3, tok_ptr);
				break;
			    case 6:
				work_tele_ptr->free_4=mem_ptr;
				strcpy(work_tele_ptr->free_4, tok_ptr);
				break;
			    case 7:
				work_tele_ptr->free_5=mem_ptr;
				strcpy(work_tele_ptr->free_5, tok_ptr);
				break;
			    case 8:
				work_tele_ptr->free_6=mem_ptr;
				strcpy(work_tele_ptr->free_6, tok_ptr);
				break;
			}
		    }
		}
		*rec_data='\0'; /* clear data */
		break;
	    case TX_END: /* end of transmission packet */
		break;
	    case 0: /* not control packet => data packet */
		if ((packet_ptr->header.target & 0xf0) != TARG_TEXT ||
		    !tele_record) /* make sure it's a text packet */
			break;
		strcat(rec_data, packet_ptr->data); /* build record data */
	}
	packet_ptr=packet_ptr->next; /* follow chain */
    } while (packet_ptr->next != NULL);
    return(base_tele_ptr);
}
@//E*O*F lib/extract_tele.c//
chmod u=rw,g=r,o=r lib/extract_tele.c
 
echo x - lib/boss_open.c
sed 's/^@//' > "lib/boss_open.c" <<'@//E*O*F lib/boss_open.c//'
/*
   boss_open() - open the device the BOSS is connected to and set the
		 communications parameters
			Mark Dapoz
*/

#include <stdio.h>
#include <termio.h>
#include <fcntl.h>

#define	SYSERR	-1		/* system error return value */

boss_open(device)
char *device;
{
    struct termio term;		/* tty info structure */
    int  boss_fd;		/* device file descriptor */
    int	 i;

    /* open with O_NDELAY first to avoid blocking on the device */
    if((i=open(device, O_RDONLY | O_NDELAY)) != SYSERR) {
    	ioctl(i, TCGETA, &term);
	term.c_cflag |= CLOCAL | CREAD; /* local connection for reading */
	term.c_cflag &= ~(CBAUD);	/* clear the line */
    	ioctl(i, TCSETA, &term);
    	ioctl(i, TCXONC, 1);		/* start line in case of flow control */
	/* here is the real open, which is blocking */
    	if ((boss_fd = open(device, O_RDWR)) != SYSERR) {
    		close(i); /* fcntl(i, F_SETFD, 0x01); */
	} else {
		perror("Unable to open device");
		return(-1);
    	}
    } else {
	perror("Unable to open device");
	return(-1);
    }

    /*
     * set up terminal parameters: 9600 baud, 8 data bits, 1 stop bit
     */

    if(ioctl(boss_fd, TCGETA, &term) == SYSERR)	{
	perror("Unable to get tty parameters");
	return(-1);
    }
    term.c_lflag = ISIG;		/* enable signals */
    term.c_iflag = BRKINT;		/* make break cause interrupt */
    term.c_oflag &= ~OPOST;		/* raw character output */
    term.c_cflag = CREAD | CLOCAL | HUPCL | CS8; /* enable receiver */
    term.c_cc[VMIN] = 1;
    term.c_cc[VTIME] = 0;
    term.c_cflag &= ~CBAUD ;
    term.c_cflag |= B9600;		/* set baud rate */
    if (ioctl(boss_fd, TCSETAW, &term) == SYSERR) {
	perror("Unable to set tty parameters");
	return(-1);
    }
    return(boss_fd);
}
@//E*O*F lib/boss_open.c//
chmod u=rw,g=r,o=r lib/boss_open.c
 
echo x - lib/boss_read.c
sed 's/^@//' > "lib/boss_read.c" <<'@//E*O*F lib/boss_read.c//'
/*
   boss_read() - accept packets from device specified by file descriptor
		 "boss_fd" and put them on a link list of packets
				Mark Dapoz
*/

#include <stdio.h>
#include <signal.h>
#include "boss.h"

#define	DEBUG_STREAM		/* debug incomming byte stream */
#undef	DEBUG_STREAM
#define	DEBUG_STATES		/* debug state machine */
#undef	DEBUG_STATES

#ifdef	DEBUG_STATES
#define	dprints(x)	printf(x)
#else
#define	dprints(x)
#endif

struct boss_packet *boss_read(boss_fd)
int boss_fd;
{
    char cur_byte, prev_byte=0;	/* Data from BOSS */
    char start_ack = CRLF_ACK;	/* Value of ack for initial handshake */
    char record_ack = PROTO_ACK;/* Value of ack for record handshakes */
    char record_nack = PROTO_ABORT;/* Value for aborting transfer */
    int  state=CONNECT;		/* Current state withn packet */
    int  connected=0;		/* Already in connect state? */
    int  nibble=0;		/* Portion of byte being received */
    int  first_crlf=0;		/* Dump first occurrence of cr lf */
    int  hdr_count;		/* Amount of packet header received */
    int  data_count;		/* Amount of packet data received */
    int	 rec_count=0;		/* Number of records read so far */
    int	 read_rc;		/* read(2) return value */
    int	 timeout;		/* time to wait for next char */
    byte verify_checksum;	/* Checksum of packet (provided) */
    byte packet_checksum;	/* Checksum of packet (calculated) */
    struct boss_packet *next_packet;
    struct boss_packet *packet_ptr;
    struct boss_packet *base_packet;

    int read_timeout();		/* interrupt handler */

    packet_ptr=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    packet_ptr->next=NULL;
    base_packet=packet_ptr;
    signal(SIGALRM, read_timeout);
    timeout=START_TIMEOUT;
    alarm(timeout); /* limit wait for transfer to start */
    while((read_rc=read(boss_fd, &cur_byte, 1)) == 1) {
	alarm(0); /* turn off any pending alarm */
#ifdef DEBUG_STREAM
	printf("%02xh\n", cur_byte);
#endif
	switch(state) {
	    case CONNECT:
		if ((cur_byte == LF) && (prev_byte == CR)) {
		    if (!first_crlf) {
			first_crlf++;
			break;
		    }
		    timeout=CHAR_TIMEOUT; /* got initial chars */
		    write(boss_fd, &start_ack, 1);  /* handshake with BOSS */
		    dprints("Switching to COLON state\n");
		    state=COLON;
		}
		break;
	    case COLON:
		if (cur_byte != ':') {
		    if (connected) {
			if (cur_byte != PROTO_ABORT) { /* aborted? */
			    fprintf(stderr, "Ugh, expecting ':'\n");
			    fprintf(stderr, "Got '%02xh' instead\n", cur_byte);
			}
			return(NULL);
		    } else {
			dprints("Switching to CONNECT state\n");
			state=CONNECT;
		    }
		} else {
		    connected=1; /* indicate handshake completed */
		    dprints("Switching to SIZE state\n");
		    state=SIZE;
		}
		break;
	    case SIZE:
		if (!nibble)
		    nibble++;
		else {
		    packet_checksum=packet_ptr->size=
		    	h2d(prev_byte)*16 + h2d(cur_byte);
		    dprints("Switching to HEADER state\n");
		    hdr_count=0;	/* init header count */
		    state=HEADER;
		    nibble=0;		/* reset nibble counter */
		}
		break;
	    case HEADER:
		if (!nibble)
		    nibble++;
		else {
		    switch(hdr_count) {
			case 0:
		    		packet_checksum+=(packet_ptr->header.target=
				    h2d(prev_byte)*16 + h2d(cur_byte));
				break;
			case 1:
		    		packet_checksum+=(packet_ptr->header.position=
				    h2d(prev_byte)*16 + h2d(cur_byte));
				break;
			case 2:
		    		packet_checksum+=(packet_ptr->header.control=
				    h2d(prev_byte)*16 + h2d(cur_byte));
				break;
		    }
		    if (++hdr_count == 3) {
			dprints("Switching to DATA state\n");
			if (packet_ptr->size)
			    state=DATA;	/* read data only if size > 0 */
			else
			    state=CHECKSUM;
			data_count=0;
		    } 
		    nibble=0;
		}
		break;
	    case DATA:
		if (!nibble)
		    nibble++;
		else {
		    packet_checksum += (packet_ptr->data[data_count]=
		    	h2d(prev_byte)*16 + h2d(cur_byte));
		    if (++data_count == packet_ptr->size) {
			packet_ptr->data[packet_ptr->size]='\0'; /* for strcpy*/
			dprints("Switching to CHECKSUM state\n");
			state=CHECKSUM;
		    } 
		    nibble=0;
		}
		break;
	    case CHECKSUM:
		if (!nibble)
		    nibble++;
		else {
		    packet_ptr->checksum=verify_checksum=
			h2d(prev_byte)*16 + h2d(cur_byte);
		    if ((((packet_checksum^0xff)+1)&0xff) != verify_checksum) {
			fprintf(stderr, "Checksum error: %02xh != %02xh\n",
			    verify_checksum, (((packet_checksum^0xff)+1)&0xff));
			write(boss_fd, &record_nack, 1);  /* abort */
			return(NULL);
		    }
			/* Check if handshake is required */
		    if (packet_ptr->size == 0 && 
		    	packet_ptr->header.control == REC_END) {
			    write(boss_fd, &record_ack, 1);  /* handshake */
			    fprintf(stderr, "Record=%d\r", rec_count++);
			}
			/* Check if last packet */
		    if (packet_ptr->size == 0 && 
		    	packet_ptr->header.control == TX_END) {
			    return(base_packet); /* end of transmission */
			}
    		    next_packet = (struct boss_packet *)
		        malloc(sizeof(struct boss_packet));
		    next_packet->next = NULL;
		    packet_ptr->next = next_packet;
		    packet_ptr=next_packet;
		    nibble=0;
		    dprints("Switching to COLON state\n");
		    state=COLON;
		}
		break;
	    default:
		fprintf(stderr, "panic: invalid state: %02xh\n", state);
		return(NULL);
	}
	prev_byte=cur_byte;
	alarm(timeout); /* limit wait for next char */
    }
    if (read_rc < 0)
    	return(NULL);
    else
    	return(base_packet);
}

read_timeout() /* read timeout interrupt handler */
{
    fprintf(stderr, "Timeout reading\n");
    return(-1);
}
@//E*O*F lib/boss_read.c//
chmod u=rw,g=r,o=r lib/boss_read.c
 
echo x - lib/extract_memo.c
sed 's/^@//' > "lib/extract_memo.c" <<'@//E*O*F lib/extract_memo.c//'
/*
   extract_memo() - extract memo entries from a stream of linked list packets
			Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct memo *extract_memo(packet_ptr)
struct boss_packet *packet_ptr;
{
    int	 memo_record=0;	/* marker to indicate current record is for memo */
    char rec_data[384];	/* buffer to hold one record of data */
    char *mem_ptr;
    struct memo *work_memo_ptr;
    struct memo *base_memo_ptr;

    base_memo_ptr=NULL;
    *rec_data='\0';
    do {
	switch(packet_ptr->header.control) {
	    case SEL_DEST: /* packet for selecting destination */
		memo_record=packet_ptr->data[0] == DEST_MEMO ? 1 : 0;
		break;
	    case REC_END: /* end of record packet */
		if (memo_record) { /* ignore if memo not destination */
		    if (base_memo_ptr == NULL) { /* first record? */
		    	base_memo_ptr=work_memo_ptr=(struct memo *)
			    malloc(sizeof(struct memo));
		    } else { /* add to linked list of records */
		    	work_memo_ptr->next=(struct memo *)
			    malloc(sizeof(struct memo));
		    	work_memo_ptr=work_memo_ptr->next;
		    }
		    work_memo_ptr->next=NULL; /* init data in struct */
		    work_memo_ptr->text=NULL;
		    work_memo_ptr->marked =	/* marked record? */
			packet_ptr->header.target ? 1 : 0;
		    if (strlen(rec_data)) { /* allocate room for data*/
			work_memo_ptr->text=(char *)malloc(strlen(rec_data)+1);
			strcpy(work_memo_ptr->text, rec_data);
		    }
		}
		*rec_data='\0'; /* clear data */
		break;
	    case TX_END: /* end of transmission packet */
		break;
	    case 0: /* not control packet => data packet */
		if ((packet_ptr->header.target & 0xf0) != TARG_TEXT ||
		    !memo_record) /* make sure it's a text packet */
			break;
		strcat(rec_data, packet_ptr->data); /* build record data */
	}
	packet_ptr=packet_ptr->next; /* follow chain */
    } while (packet_ptr->next != NULL);
    return(base_memo_ptr);
}
@//E*O*F lib/extract_memo.c//
chmod u=rw,g=r,o=r lib/extract_memo.c
 
echo x - lib/Makefile
sed 's/^@//' > "lib/Makefile" <<'@//E*O*F lib/Makefile//'
# libboss makefile

# workaround for System V make bug
SHELL	= /bin/sh
TOPDIR	= ..
BINDIR	= $(TOPDIR)/bin
LIBDIR	= $(TOPDIR)/lib
INCDIR	= $(TOPDIR)/include
CFGDIR	= $(TOPDIR)/cfg
CFLAGS	= $(COPTS) -I$(INCDIR)
LINTFLAGS=-hau -I$(INCDIR)
LIB	= $(LIBDIR)/libboss.a

# RANLIB is ranlib on non-USG systems, echo on USG systems
#RANLIB	=ranlib
RANLIB	=:
SRCS	= boss_open.c boss_close.c boss_read.c extract_tele.c extract_memo.c \
	  extract_bus.c extract_sch.c extract_cal.c next_field.c \
	  boss_write.c add_tele.c packet_chksm.c add_end_tx.c text_packet.c \
	  add_memo.c add_bcard.c add_sched.c date_packet.c time_packet.c \
	  alarm_packet.c add_cal.c

all:	$(LIB)

$(LIB): $(SRCS)
	$(CC) $(CFLAGS) -c $?
	ar rv $@ *.o
	$(RANLIB) $@

lint:
	lint $(LINTFLAGS) $(SRCS)

clean:
	rm -f *.o

# header dependencies for libboss.a members
add_bcard.o:		$(INCDIR)/boss.h
add_cal.o:		$(INCDIR)/boss.h
add_end_tx.o:		$(INCDIR)/boss.h
add_memo.o:		$(INCDIR)/boss.h
add_sched.o:		$(INCDIR)/boss.h
add_tele.o:		$(INCDIR)/boss.h
alarm_packet.o:		$(INCDIR)/boss.h
boss_close.o:		$(INCDIR)/boss.h
boss_open.o:		$(INCDIR)/boss.h
boss_read.o:		$(INCDIR)/boss.h
boss_write.o:		$(INCDIR)/boss.h
date_packet.o:		$(INCDIR)/boss.h
extract_bus.o:		$(INCDIR)/boss.h
extract_cal.o:		$(INCDIR)/boss.h
extract_memo.o:		$(INCDIR)/boss.h
extract_sch.o:		$(INCDIR)/boss.h
extract_tele.o:		$(INCDIR)/boss.h
next_field.o:		$(INCDIR)/boss.h
packet_chksm.o:		$(INCDIR)/boss.h
text_packet.o:		$(INCDIR)/boss.h
time_packet.o:		$(INCDIR)/boss.h
@//E*O*F lib/Makefile//
chmod u=rw,g=r,o=r lib/Makefile
 
echo x - lib/extract_bus.c
sed 's/^@//' > "lib/extract_bus.c" <<'@//E*O*F lib/extract_bus.c//'
/*
   extract_bcard() - extract business card entries from a stream of linked
		     list of packets
				Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct bus_card *extract_bcard(packet_ptr)
struct boss_packet *packet_ptr;
{
    int	 bcard_record=0;/* marker to indicate current record is for bus card */
    char rec_data[384];	/* buffer to hold one record of data */
    char *tok_ptr;	/* used in breaking up record into fields */
    int  tok_len;
    char *mem_ptr;
    int	 i;
    struct bus_card *work_bcard_ptr;
    struct bus_card *base_bcard_ptr;

    extern char *next_field();

    base_bcard_ptr=NULL;
    *rec_data='\0';
    do {
	switch(packet_ptr->header.control) {
	    case SEL_DEST: /* packet for selecting destination */
		bcard_record=packet_ptr->data[0] == DEST_BUS ? 1 : 0;
		break;
	    case REC_END: /* end of record packet */
		if (bcard_record) { /* ignore if bus card not destination */
		    if (base_bcard_ptr == NULL) { /* first record? */
		    	base_bcard_ptr=work_bcard_ptr=(struct bus_card *)
			    malloc(sizeof(struct bus_card));
		    } else { /* add to linked list of records */
		    	work_bcard_ptr->next=(struct bus_card *)
			    malloc(sizeof(struct bus_card));
		    	work_bcard_ptr=work_bcard_ptr->next;
		    }
		    work_bcard_ptr->next=NULL; /* init data in struct */
		    work_bcard_ptr->employer=
		    work_bcard_ptr->name=
		    work_bcard_ptr->tel_number=
		    work_bcard_ptr->position=
		    work_bcard_ptr->dept=
		    work_bcard_ptr->po_box=
		    work_bcard_ptr->addr=
		    work_bcard_ptr->telex=
		    work_bcard_ptr->fax_number=
		    work_bcard_ptr->free_1=
		    work_bcard_ptr->free_2=
		    work_bcard_ptr->free_3=
		    work_bcard_ptr->free_4=
		    work_bcard_ptr->free_5=NULL;
		    work_bcard_ptr->marked =	/* marked record? */
			packet_ptr->header.target ? 1 : 0;
		    tok_ptr=next_field(rec_data, '\n'); /* split into fields */
		    for (i=0; tok_ptr!=NULL; tok_ptr=next_field(NULL,'\n'),i++){
			if (tok_len=strlen(tok_ptr)) /* allocate room for data*/
			    mem_ptr=(char *)malloc(tok_len+1);
			else
			    continue;
			switch(i) { /* put in appropriate spot in struct */
			    case 0:
				work_bcard_ptr->employer=mem_ptr;
				strcpy(work_bcard_ptr->employer, tok_ptr);
				break;
			    case 1:
				work_bcard_ptr->name=mem_ptr;
				strcpy(work_bcard_ptr->name, tok_ptr);
				break;
			    case 2:
				work_bcard_ptr->tel_number=mem_ptr;
				strcpy(work_bcard_ptr->tel_number, tok_ptr);
				break;
			    case 3:
				work_bcard_ptr->position=mem_ptr;
				strcpy(work_bcard_ptr->position, tok_ptr);
				break;
			    case 4:
				work_bcard_ptr->dept=mem_ptr;
				strcpy(work_bcard_ptr->dept, tok_ptr);
				break;
			    case 5:
				work_bcard_ptr->po_box=mem_ptr;
				strcpy(work_bcard_ptr->po_box, tok_ptr);
				break;
			    case 6:
				work_bcard_ptr->addr=mem_ptr;
				strcpy(work_bcard_ptr->addr, tok_ptr);
				break;
			    case 7:
				work_bcard_ptr->telex=mem_ptr;
				strcpy(work_bcard_ptr->telex, tok_ptr);
				break;
			    case 8:
				work_bcard_ptr->fax_number=mem_ptr;
				strcpy(work_bcard_ptr->fax_number, tok_ptr);
				break;
			    case 9:
				work_bcard_ptr->free_1=mem_ptr;
				strcpy(work_bcard_ptr->free_1, tok_ptr);
				break;
			    case 10:
				work_bcard_ptr->free_2=mem_ptr;
				strcpy(work_bcard_ptr->free_2, tok_ptr);
				break;
			    case 11:
				work_bcard_ptr->free_3=mem_ptr;
				strcpy(work_bcard_ptr->free_3, tok_ptr);
				break;
			    case 12:
				work_bcard_ptr->free_4=mem_ptr;
				strcpy(work_bcard_ptr->free_4, tok_ptr);
				break;
			    case 13:
				work_bcard_ptr->free_5=mem_ptr;
				strcpy(work_bcard_ptr->free_5, tok_ptr);
				break;
			}
		    }
		}
		*rec_data='\0'; /* clear data */
		break;
	    case TX_END: /* end of transmission packet */
		break;
	    case 0: /* not control packet => data packet */
		if ((packet_ptr->header.target & 0xf0) != TARG_TEXT ||
		    !bcard_record) /* make sure it's a text packet */
			break;
		strcat(rec_data, packet_ptr->data); /* build record data */
	}
	packet_ptr=packet_ptr->next; /* follow chain */
    } while (packet_ptr->next != NULL);
    return(base_bcard_ptr);
}
@//E*O*F lib/extract_bus.c//
chmod u=rw,g=r,o=r lib/extract_bus.c
 
echo x - lib/extract_sch.c
sed 's/^@//' > "lib/extract_sch.c" <<'@//E*O*F lib/extract_sch.c//'
/*
   extract_sched() - extract schedule entries from a stream of linked list 
		     packets
				Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct schedule *extract_sched(packet_ptr)
struct boss_packet *packet_ptr;
{
    int	 sched_record=0;/* marker to indicate current record is for schedule */
    char rec_data[384];	/* buffer to hold one record of data */
    char sched_date[22];/* buffer for date of schedule */
    char sched_time[12];/* buffer for time of schedule */
    char sched_alarm[6];/* buffer for alarm time of schedule */
    struct schedule *work_sched_ptr;
    struct schedule *base_sched_ptr;

    base_sched_ptr=NULL;
    *rec_data='\0';
    do {
	switch(packet_ptr->header.control) {
	    case SEL_DEST: /* packet for selecting destination */
		sched_record=packet_ptr->data[0] == DEST_SCHED ? 1 : 0;
		break;
	    case REC_END: /* end of record packet */
		if (sched_record) { /* ignore if schedule not destination */
		    if (base_sched_ptr == NULL) { /* first record? */
		    	base_sched_ptr=work_sched_ptr=(struct schedule *)
			    malloc(sizeof(struct schedule));
		    } else { /* add to linked list of records */
		    	work_sched_ptr->next=(struct schedule *)
			    malloc(sizeof(struct schedule));
		    	work_sched_ptr=work_sched_ptr->next;
		    }
		    work_sched_ptr->next=NULL; /* init data in struct */
		    work_sched_ptr->date=
		    work_sched_ptr->time=
		    work_sched_ptr->alarm=
		    work_sched_ptr->text=NULL;
		    work_sched_ptr->marked =	/* marked record? */
			packet_ptr->header.target ? 1 : 0;
		    if (strlen(sched_date)) { /* allocate room for date */
			work_sched_ptr->date=(char *)
			    malloc(strlen(sched_date)+1);
			strcpy(work_sched_ptr->date, sched_date);
		    }
		    if (strlen(sched_time)) { /* allocate room for time */
			work_sched_ptr->time=(char *)
			    malloc(strlen(sched_time)+1);
			strcpy(work_sched_ptr->time, sched_time);
		    }
		    if (strlen(sched_alarm)) { /* allocate room for alarm */
			work_sched_ptr->alarm=(char *)
			    malloc(strlen(sched_alarm)+1);
			strcpy(work_sched_ptr->alarm, sched_alarm);
		    }
		    if (strlen(sched_alarm)) { /* allocate room for alarm */
			work_sched_ptr->alarm=(char *)
			    malloc(strlen(sched_alarm)+1);
			strcpy(work_sched_ptr->alarm, sched_alarm);
		    }
		    if (strlen(rec_data)) { /* allocate room for description */
			work_sched_ptr->text=(char *)
			    malloc(strlen(rec_data)+1);
			strcpy(work_sched_ptr->text, rec_data);
		    }
		}
		*rec_data='\0';		/* clear data */
		*sched_date='\0';
		*sched_time='\0';
		*sched_alarm='\0';
		break;
	    case TX_END: /* end of transmission packet */
		break;
	    case 0: /* not control packet => data packet */
		if ((packet_ptr->header.target & 0xf0) == TARG_TEXT &&
		    sched_record) /* make sure it's a text packet */
			strcat(rec_data, packet_ptr->data);/*build record data*/
				/* time setting packet? */
		if (packet_ptr->header.target == TARG_TIME && sched_record)
		    strcpy(sched_time, packet_ptr->data);
				/* date setting packet? */
		if (packet_ptr->header.target == TARG_DATE && sched_record)
		    strcpy(sched_date, packet_ptr->data);
				/* alarm setting packet? */
		if (packet_ptr->header.target == TARG_ALARM && sched_record)
		    strcpy(sched_alarm, packet_ptr->data);
		break;
	}
	packet_ptr=packet_ptr->next; /* follow chain */
    } while (packet_ptr->next != NULL);
    return(base_sched_ptr);
}
@//E*O*F lib/extract_sch.c//
chmod u=rw,g=r,o=r lib/extract_sch.c
 
echo x - lib/extract_cal.c
sed 's/^@//' > "lib/extract_cal.c" <<'@//E*O*F lib/extract_cal.c//'
/*
   extract_cal() - extract marked calendar entries from a stream of linked
  		   list packets
			Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct calendar *extract_cal(packet_ptr)
struct boss_packet *packet_ptr;
{
    int	 cal_record=0;/* marker to indicate current record is for calendar */
    char cal_date[11];	/* buffer for date of calendar */
    int  marked_days[4];/* buffer for bitfield of marked days */
    struct calendar *work_cal_ptr;
    struct calendar *base_cal_ptr;

    base_cal_ptr=NULL;
    do {
	switch(packet_ptr->header.control) {
	    case SEL_DEST: /* packet for selecting destination */
		cal_record=packet_ptr->data[0] == DEST_CAL ? 1 : 0;
		break;
	    case REC_END: /* end of record packet */
		if (cal_record) { /* ignore if calendar not destination */
		    if (base_cal_ptr == NULL) { /* first record? */
		    	base_cal_ptr=work_cal_ptr=(struct calendar *)
			    malloc(sizeof(struct calendar));
		    } else { /* add to linked list of records */
		    	work_cal_ptr->next=(struct calendar *)
			    malloc(sizeof(struct calendar));
		    	work_cal_ptr=work_cal_ptr->next;
		    }
		    work_cal_ptr->next=NULL; /* init data in struct */
		    work_cal_ptr->month=NULL;
		    if (strlen(cal_date)) { /* allocate room for date */
			work_cal_ptr->month=(char *)
			    malloc(strlen(cal_date)+1);
			strcpy(work_cal_ptr->month, cal_date);
		    }
		    work_cal_ptr->days[0]=marked_days[0]; /* copy bitfields */
		    work_cal_ptr->days[1]=marked_days[1];
		    work_cal_ptr->days[2]=marked_days[2];
		    work_cal_ptr->days[3]=marked_days[3];
		}
		*cal_date='\0';	/* clear data */
		marked_days[0]=marked_days[1]=marked_days[2]=marked_days[3]=0;
		break;
	    case TX_END: /* end of transmission packet */
		break;
	    case 0: /* not control packet => data packet */
				/* date setting packet? */
		if (packet_ptr->header.target == TARG_DATE && cal_record)
		    strcpy(cal_date, packet_ptr->data);
				/* marked days packet? */
		if (packet_ptr->header.target == TARG_MARK && cal_record) {
		    marked_days[0]=packet_ptr->data[0];
		    marked_days[1]=packet_ptr->data[1];
		    marked_days[2]=packet_ptr->data[2];
		    marked_days[3]=packet_ptr->data[3];
		}
		break;
	}
	packet_ptr=packet_ptr->next; /* follow chain */
    } while (packet_ptr->next != NULL);
    return(base_cal_ptr);
}
@//E*O*F lib/extract_cal.c//
chmod u=rw,g=r,o=r lib/extract_cal.c
 
echo x - lib/boss_close.c
sed 's/^@//' > "lib/boss_close.c" <<'@//E*O*F lib/boss_close.c//'
/*
   boss_close() - close the device the BOSS is connected to
			Mark Dapoz
*/

#include <stdio.h>

boss_close(boss_fd)
int boss_fd;
{
    return(close(boss_fd));
}
@//E*O*F lib/boss_close.c//
chmod u=rw,g=r,o=r lib/boss_close.c
 
echo x - lib/boss_write.c
sed 's/^@//' > "lib/boss_write.c" <<'@//E*O*F lib/boss_write.c//'
/*
   boss_write() - write packets from the linked list "boss_packets" to device 
		  specified by file descriptor "boss_fd"
				Mark Dapoz
*/

#include <stdio.h>
#include <signal.h>
#include "boss.h"

#define	DEBUG_STREAM		/* debug incomming byte stream */
#undef	DEBUG_STREAM

int boss_write(boss_fd, boss_packets)
int boss_fd;
struct boss_packet *boss_packets;
{
    int  write_timeout();	/* interrupt handler */
    char boss_ack;		/* type of ack returned by BOSS */
    char ascii_buffer[(1+3+256+1)*2]; /* ascii buffer for packet tx */
    int  buffer_pos;		/*location of next char in ascii_buffer */
    int  i;

    signal(SIGALRM, write_timeout);
    alarm(0);
#ifdef DEBUG_STREAM
    printf("writting to BOSS\n");
#endif
    for (i=0; i < 15; i++) {
	sprintf(ascii_buffer, "%c%c", CR, LF); /* initial handshake */
	write(boss_fd, ascii_buffer, 2);
	boss_ack=0;
	signal(SIGALRM, write_timeout); /* just in case no comm */
	alarm(2);
	read(boss_fd, &boss_ack, 1); /* see what its got to say */
	alarm(0); /* turn off any pending alarms */
	if (boss_ack == CRLF_ACK) {
	    boss_ack=0;
	    signal(SIGALRM, write_timeout);	/* flush line */
	    alarm(1);
	    while(read(boss_fd, &boss_ack, 1) > 0) { /* read what we can */
		signal(SIGALRM, write_timeout);
		alarm(1);
	    }
	    alarm(0); /* turn off any pending alarms */
	    break;
	}
    }
    if (i == 10) /* successful? */
	return(1);
    while(boss_packets != NULL) { /* run through the list */
	buffer_pos=sprintf(ascii_buffer, ":%02X%02X%02X%02X",
	    boss_packets->size,
	    boss_packets->header.target,
	    boss_packets->header.position,
	    boss_packets->header.control);
	for (i=0; i < boss_packets->size; i++)
	    buffer_pos+=sprintf(ascii_buffer+buffer_pos, "%02X",
		boss_packets->data[i]);
	sprintf(ascii_buffer+buffer_pos, "%02X", boss_packets->checksum);
#ifdef DEBUG_STREAM
	printf("%s\n", ascii_buffer);
#endif
		/* dump it one at a time, BOSS can't keep up with write() */
	for (i=0; i < strlen(ascii_buffer); i++)
	    write(boss_fd, ascii_buffer+i, 1);
		/* wait for acks */
	if (boss_packets->header.control == REC_END ||
	    boss_packets->header.control == SEL_DEST) { /* get response */
#ifdef DEBUG_STREAM
	    if (boss_packets->header.control == REC_END)
		printf("--- End of Record ---\n\n");
	    else
		printf("--- Dest Select Record ---\n\n");
#endif
	    boss_ack=0;
	    signal(SIGALRM, write_timeout);
	    alarm(30);	/* just in case no more comm */
	    read(boss_fd, &boss_ack, 1);
	    alarm(0); /* turn off any pending alarm */
	    switch (boss_ack) { /* check response from BOSS */
		case 0:
			fprintf(stderr, "No ack received from BOSS\n");
	    		write(boss_fd, "!", 1); /* send abort signal */
			return(1);
		case PROTO_ABORT:
			fprintf(stderr, "Transfer aborted by BOSS\n");
			return(1);
		case PROTO_LOST:
			fprintf(stderr, "Sync error\n");
	    		write(boss_fd, "!", 1); /* send abort signal */
			return(1);
		case PROTO_ACK:
			/*
			fprintf(stderr, "Record successfully sent\n");
			*/
			break;
		default:
			fprintf(stderr, "Transmit error, got %c(%02xh)\n",
			    boss_ack, boss_ack);
	    }
	}
	boss_packets=boss_packets->next; /* move to next packet */
    }
    return(0);
}

write_timeout() /* write timeout interrupt handler */
{
#ifdef DEBUG_TIME
    fprintf(stdout, "Timeout reading\n");
#endif
    return(1);
}
@//E*O*F lib/boss_write.c//
chmod u=rw,g=r,o=r lib/boss_write.c
 
echo x - lib/add_tele.c
sed 's/^@//' > "lib/add_tele.c" <<'@//E*O*F lib/add_tele.c//'
/*
   add_tele() - add telephone entries to a stream of linked list packets
			Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct boss_packet *last;	/* pointer to tail of BOSS packets */
int text_pos;			/* current position of text record */

struct boss_packet *add_tele(tele_entry, packet_ptr)
struct telephone *tele_entry;
struct boss_packet *packet_ptr;
{
    struct boss_packet *head;	/* first packet on BOSS packet stream */
    struct boss_packet *build_packet;

    build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    if ((head=last=packet_ptr) != NULL) {
	for (; last->next !=NULL; last=last->next); /* locate end */
	last->next=build_packet;
    } else
	head=last=build_packet; /* init list */
		/* build select destination packet */
    build_packet->size=2;
    build_packet->header.target=0;
    build_packet->header.position=0;
    build_packet->header.control=SEL_DEST;
    build_packet->data[0]=DEST_TEL; /* we want a telephone entry */
    build_packet->data[1]=0;
    build_packet->next=NULL;
    packet_checksum(build_packet);
    last=build_packet; /* adv pointer */
		/* run through list of telephone entries to add only adding
		   those entries with real data */
    while (tele_entry != NULL) {
	text_pos=0; /* start at beginning */
	if (tele_entry->name != NULL)
	    if (strlen(tele_entry->name))
		add_text_packet(tele_entry->name);
	if (tele_entry->tel_number != NULL)
	    if (strlen(tele_entry->tel_number))
		add_text_packet(tele_entry->tel_number);
	if (tele_entry->addr != NULL)
	    if (strlen(tele_entry->addr))
		add_text_packet(tele_entry->addr);
	if (tele_entry->free_1 != NULL)
	    if (strlen(tele_entry->free_1))
		add_text_packet(tele_entry->free_1);
	if (tele_entry->free_2 != NULL)
	    if (strlen(tele_entry->free_2))
		add_text_packet(tele_entry->free_2);
	if (tele_entry->free_3 != NULL)
	    if (strlen(tele_entry->free_3))
		add_text_packet(tele_entry->free_3);
	if (tele_entry->free_4 != NULL)
	    if (strlen(tele_entry->free_4))
		add_text_packet(tele_entry->free_4);
	if (tele_entry->free_5 != NULL)
	    if (strlen(tele_entry->free_5))
		add_text_packet(tele_entry->free_5);
	if (tele_entry->free_6 != NULL)
	    if (strlen(tele_entry->free_6))
		add_text_packet(tele_entry->free_6);
		/* build end of record packet */
	build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
	build_packet->size=0;
	if (tele_entry->marked) /* marked record? */
	    build_packet->header.target=0x80;
	else
	    build_packet->header.target=0;
	build_packet->header.position=0;
	build_packet->header.control=REC_END; /* end of record */
	packet_checksum(build_packet);
	last->next=build_packet; /* add to linked list of packets */
	last=build_packet;
	last->next=NULL;
	tele_entry=tele_entry->next; /* next record */
    }
    return(head);
}
@//E*O*F lib/add_tele.c//
chmod u=rw,g=r,o=r lib/add_tele.c
 
echo x - lib/add_end_tx.c
sed 's/^@//' > "lib/add_end_tx.c" <<'@//E*O*F lib/add_end_tx.c//'
/*
   add_end_tx() - add end of transmission record to a stream of linked list 
		  packets
				Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct boss_packet *add_end_tx(packet_ptr)
struct boss_packet *packet_ptr;
{
    struct boss_packet *head, *last;
    struct boss_packet *build_packet;

    build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    if ((head=last=packet_ptr) != NULL) {
	for (; last->next !=NULL; last=last->next);
	last->next=build_packet;
    } else
	head=last=build_packet;
		/* build end of transmission packet */
    build_packet->size=0;
    build_packet->header.target=0;
    build_packet->header.position=0;
    build_packet->header.control=TX_END;
    build_packet->next=NULL;
    packet_checksum(build_packet);
		/* add to linked list of packets */
    return(head);
}
@//E*O*F lib/add_end_tx.c//
chmod u=rw,g=r,o=r lib/add_end_tx.c
 
echo x - lib/packet_chksm.c
sed 's/^@//' > "lib/packet_chksm.c" <<'@//E*O*F lib/packet_chksm.c//'
/*
   packet_checksum - calculate the checksum value for a BOSS packet
			Mark Dapoz
*/

#include "boss.h"

packet_checksum(packet_ptr)
struct boss_packet *packet_ptr;
{
    int checksum;
    int i;

    i=checksum=packet_ptr->size;
    checksum+=packet_ptr->header.target;
    checksum+=packet_ptr->header.position;
    checksum+=packet_ptr->header.control;
    for (; i > 0; checksum+=packet_ptr->data[--i]);
    packet_ptr->checksum=((checksum^0xff)+1)&0xff;
}
@//E*O*F lib/packet_chksm.c//
chmod u=rw,g=r,o=r lib/packet_chksm.c
 
echo x - lib/text_packet.c
sed 's/^@//' > "lib/text_packet.c" <<'@//E*O*F lib/text_packet.c//'
/*
   add_text_packet() - add a text selection packet to a stream of linked list 
		       of packets
				Mark Dapoz
*/

#include <stdio.h>
#include "boss.h"

	/* add a text only packet to the stream pointed to by last */
add_text_packet(text)
char *text;
{
    extern struct boss_packet *last;	/* pointer to tail of BOSS packets */
    extern int text_pos;		/* current position of text record */

    struct boss_packet *build_packet, *alt_packet;
    int double_packet=0;

    if (strlen(text)+text_pos > MAX_BOSS_REC) /* check if too big */
	return(1);
    build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    if (strlen(text) < 256) {
	build_packet->size=strlen(text);
	strncpy(build_packet->data, text, build_packet->size);
    } else { /* too big to fit in one packet */
	build_packet->size=255;
	strncpy(build_packet->data, text, 255);
	build_packet->data[255]='\0';
	alt_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
	strncpy(alt_packet->data, text+255, strlen(text)-255);
	alt_packet->size=strlen(text)-255;
	alt_packet->header.target=TARG_TEXT;
	alt_packet->header.position=0;
	alt_packet->header.control=0;
	double_packet=1;
    }
    build_packet->header.target=TARG_TEXT;
    build_packet->header.position=0;
    build_packet->header.control=0;
    if (text_pos > 255) {
	build_packet->header.position=text_pos&0xff;
	build_packet->header.target+=text_pos/256;
    } else
	build_packet->header.position=text_pos;
    packet_checksum(build_packet);
    last->next=build_packet; /* add to linked list of packets */
    if (double_packet) {
	if (text_pos+255 > 255) {
	    alt_packet->header.position=(text_pos+255)&0xff;
	    alt_packet->header.target+=(text_pos+255)/256;
	} else
	    alt_packet->header.position=text_pos+255;
	packet_checksum(alt_packet);
	build_packet->next=alt_packet;
	last=alt_packet;
    } else
	last=build_packet;
    last->next=NULL;
    text_pos+=strlen(text);
    return(0);
}
@//E*O*F lib/text_packet.c//
chmod u=rw,g=r,o=r lib/text_packet.c
 
echo x - lib/add_memo.c
sed 's/^@//' > "lib/add_memo.c" <<'@//E*O*F lib/add_memo.c//'
/*
   add_memo() - add memo entries to a stream of linked list packets
			Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct boss_packet *last;	/* pointer to tail of BOSS packets */
int text_pos;			/* current position of text record */

struct boss_packet *add_memo(memo_entry, packet_ptr)
struct memo *memo_entry;
struct boss_packet *packet_ptr;
{
    struct boss_packet *head;	/* first packet on BOSS packet stream */
    struct boss_packet *build_packet;

    build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    if ((head=last=packet_ptr) != NULL) {
	for (; last->next !=NULL; last=last->next); /* locate end */
	last->next=build_packet;
    } else
	head=last=build_packet; /* init list */
		/* build select destination packet */
    build_packet->size=2;
    build_packet->header.target=0;
    build_packet->header.position=0;
    build_packet->header.control=SEL_DEST;
    build_packet->data[0]=DEST_MEMO; /* we want a memo entry */
    build_packet->data[1]=0;
    build_packet->next=NULL;
    packet_checksum(build_packet);
    last=build_packet; /* adv pointer */
		/* run through list of telephone entries to add only adding
		   those entries with real data */
    while (memo_entry != NULL) {
	text_pos=0; /* start at beginning */
	if (memo_entry->text != NULL)
	    if (strlen(memo_entry->text))
		add_text_packet(memo_entry->text);
		/* build end of record packet */
	build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
	build_packet->size=0;
	if (memo_entry->marked) /* marked record? */
	    build_packet->header.target=0x80;
	else
	    build_packet->header.target=0;
	build_packet->header.position=0;
	build_packet->header.control=REC_END; /* end of record */
	packet_checksum(build_packet);
	last->next=build_packet; /* add to linked list of packets */
	last=build_packet;
	last->next=NULL;
	memo_entry=memo_entry->next; /* next record */
    }
    return(head);
}
@//E*O*F lib/add_memo.c//
chmod u=rw,g=r,o=r lib/add_memo.c
 
echo x - lib/date_packet.c
sed 's/^@//' > "lib/date_packet.c" <<'@//E*O*F lib/date_packet.c//'
/*
   add_date_packet() - add a date selection packet to a stream of linked list 
		       of packets
				Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

add_date_packet(date)
char *date;
{
    extern struct boss_packet *last;	/* pointer to tail of BOSS packets */
    struct boss_packet *build_packet;

		/* build date select packet */
    build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    strncpy(build_packet->data, date, build_packet->size=strlen(date));
    build_packet->header.target=TARG_DATE;
    build_packet->header.position=0;
    build_packet->header.control=0;
    build_packet->next=NULL;
    packet_checksum(build_packet);
    last->next=build_packet; /* add to linked list of packets */
    last=build_packet;
    return(0);
}
@//E*O*F lib/date_packet.c//
chmod u=rw,g=r,o=r lib/date_packet.c
 
echo x - lib/add_sched.c
sed 's/^@//' > "lib/add_sched.c" <<'@//E*O*F lib/add_sched.c//'
/*
   add_sched() - add schedule entries to a stream of linked list packets
			Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct boss_packet *last;	/* pointer to tail of BOSS packets */
int text_pos;			/* current position of text record */

struct boss_packet *add_sched(sched_entry, packet_ptr)
struct schedule *sched_entry;
struct boss_packet *packet_ptr;
{
    struct boss_packet *head;	/* first packet on BOSS packet stream */
    struct boss_packet *build_packet;

    build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    if ((head=last=packet_ptr) != NULL) {
	for (; last->next !=NULL; last=last->next); /* locate end */
	last->next=build_packet;
    } else
	head=last=build_packet; /* init list */
		/* build select destination packet */
    build_packet->size=2;
    build_packet->header.target=0;
    build_packet->header.position=0;
    build_packet->header.control=SEL_DEST;
    build_packet->data[0]=DEST_SCHED; /* we want a schedule entry */
    build_packet->data[1]=0;
    build_packet->next=NULL;
    packet_checksum(build_packet);
    last=build_packet; /* adv pointer */
		/* run through list of telephone entries to add only adding
		   those entries with real data */
    while (sched_entry != NULL) {
	text_pos=0; /* start at beginning */
	if (sched_entry->date != NULL)
	    if (strlen(sched_entry->date))
		add_date_packet(sched_entry->date);
	if (sched_entry->time != NULL)
	    if (strlen(sched_entry->time))
		add_time_packet(sched_entry->time);
	if (sched_entry->alarm != NULL)
	    if (strlen(sched_entry->alarm))
		add_alarm_packet(sched_entry->alarm);
	if (sched_entry->text != NULL)
	    if (strlen(sched_entry->text))
		add_text_packet(sched_entry->text);
		/* build end of record packet */
	build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
	build_packet->size=0;
	if (sched_entry->marked) /* marked record? */
	    build_packet->header.target=0x80;
	else
	    build_packet->header.target=0;
	build_packet->header.position=0;
	build_packet->header.control=REC_END; /* end of record */
	packet_checksum(build_packet);
	last->next=build_packet; /* add to linked list of packets */
	last=build_packet;
	last->next=NULL;
	sched_entry=sched_entry->next; /* next record */
    }
    return(head);
}
@//E*O*F lib/add_sched.c//
chmod u=rw,g=r,o=r lib/add_sched.c
 
echo x - lib/time_packet.c
sed 's/^@//' > "lib/time_packet.c" <<'@//E*O*F lib/time_packet.c//'
/*
   add_time_packet() - add a time selection packet to a stream of linked list 
		       of packets
				Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

add_time_packet(time)
char *time;
{
    extern struct boss_packet *last;	/* pointer to tail of BOSS packets */
    struct boss_packet *build_packet;

		/* build time select packet */
    build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    strncpy(build_packet->data, time, build_packet->size=strlen(time));
    build_packet->header.target=TARG_TIME;
    build_packet->header.position=0;
    build_packet->header.control=0;
    build_packet->next=NULL;
    packet_checksum(build_packet);
    last->next=build_packet; /* add to linked list of packets */
    last=build_packet;
    return(0);
}
@//E*O*F lib/time_packet.c//
chmod u=rw,g=r,o=r lib/time_packet.c
 
echo x - lib/alarm_packet.c
sed 's/^@//' > "lib/alarm_packet.c" <<'@//E*O*F lib/alarm_packet.c//'
/*
   add_alarm_packet() - add an alarm selection packet to a stream of linked 
			list of packets
				Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

add_alarm_packet(alarm)
char *alarm;
{
    extern struct boss_packet *last;	/* pointer to tail of BOSS packets */
    struct boss_packet *build_packet;

		/* build alarm select packet */
    build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    strncpy(build_packet->data, alarm, build_packet->size=strlen(alarm));
    build_packet->header.target=TARG_ALARM;
    build_packet->header.position=0;
    build_packet->header.control=0;
    build_packet->next=NULL;
    packet_checksum(build_packet);
    last->next=build_packet; /* add to linked list of packets */
    last=build_packet;
    return(0);
}
@//E*O*F lib/alarm_packet.c//
chmod u=rw,g=r,o=r lib/alarm_packet.c
 
echo x - lib/add_cal.c
sed 's/^@//' > "lib/add_cal.c" <<'@//E*O*F lib/add_cal.c//'
/*
   add_cal() - add calendar entries to a stream of linked list packets
			Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct boss_packet *last;	/* pointer to tail of BOSS packets */
int text_pos;			/* current position of text record */

struct boss_packet *add_cal(cal_entry, packet_ptr)
struct calendar *cal_entry;
struct boss_packet *packet_ptr;
{
    struct boss_packet *head;	/* first packet on BOSS packet stream */
    struct boss_packet *build_packet;

    build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    if ((head=last=packet_ptr) != NULL) {
	for (; last->next !=NULL; last=last->next); /* locate end */
	last->next=build_packet;
    } else
	head=last=build_packet; /* init list */
		/* build select destination packet */
    build_packet->size=2;
    build_packet->header.target=0;
    build_packet->header.position=0;
    build_packet->header.control=SEL_DEST;
    build_packet->data[0]=DEST_CAL; /* we want a calendar entry */
    build_packet->data[1]=0;
    build_packet->next=NULL;
    packet_checksum(build_packet);
    last=build_packet; /* adv pointer */
		/* run through list of telephone entries to add only adding
		   those entries with real data */
    while (cal_entry != NULL) {
	text_pos=0; /* start at beginning */
	if (cal_entry->month != NULL)
	    if (strlen(cal_entry->month))
		add_date_packet(cal_entry->month);
		/* build mark days packet */
	build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
	build_packet->size=4; /* always 4 bytes of data */
	build_packet->header.target=TARG_MARK; /* mark days in calendar */
	build_packet->header.position=0;
	build_packet->header.control=0;
	memcpy(build_packet->data, cal_entry->days, 4); /* copy data */
	packet_checksum(build_packet);
	last->next=build_packet; /* add to linked list of packets */
	last=build_packet;
	last->next=NULL;
		/* build end of record packet */
	build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
	build_packet->size=0;
	build_packet->header.target=0;
	build_packet->header.position=0;
	build_packet->header.control=REC_END; /* end of record */
	packet_checksum(build_packet);
	last->next=build_packet; /* add to linked list of packets */
	last=build_packet;
	last->next=NULL;
	cal_entry=cal_entry->next; /* next record */
    }
    return(head);
}
@//E*O*F lib/add_cal.c//
chmod u=rw,g=r,o=r lib/add_cal.c
 
echo x - lib/add_bcard.c
sed 's/^@//' > "lib/add_bcard.c" <<'@//E*O*F lib/add_bcard.c//'
/*
   add_bcard() - add business card entries to a stream of linked list packets
			Mark Dapoz
*/

#include <stdio.h>
#include <string.h>
#include "boss.h"

struct boss_packet *last;	/* pointer to tail of BOSS packets */
int text_pos;			/* current position of text record */

struct boss_packet *add_bcard(bus_entry, packet_ptr)
struct bus_card *bus_entry;
struct boss_packet *packet_ptr;
{
    struct boss_packet *head;	/* first packet on BOSS packet stream */
    struct boss_packet *build_packet;

    build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
    if ((head=last=packet_ptr) != NULL) {
	for (; last->next !=NULL; last=last->next); /* locate end */
	last->next=build_packet;
    } else
	head=last=build_packet; /* init list */
		/* build select destination packet */
    build_packet->size=2;
    build_packet->header.target=0;
    build_packet->header.position=0;
    build_packet->header.control=SEL_DEST;
    build_packet->data[0]=DEST_BUS; /* we want a business card entry */
    build_packet->data[1]=0;
    build_packet->next=NULL;
    packet_checksum(build_packet);
    last=build_packet; /* adv pointer */
		/* run through list of business card entries to add only adding
		   those entries with real data */
    while (bus_entry != NULL) {
	text_pos=0; /* start at beginning */
	if (bus_entry->employer != NULL)
	    if (strlen(bus_entry->employer))
		add_text_packet(bus_entry->employer);
	if (bus_entry->name != NULL)
	    if (strlen(bus_entry->name))
		add_text_packet(bus_entry->name);
	if (bus_entry->tel_number != NULL)
	    if (strlen(bus_entry->tel_number))
		add_text_packet(bus_entry->tel_number);
	if (bus_entry->position != NULL)
	    if (strlen(bus_entry->position))
		add_text_packet(bus_entry->position);
	if (bus_entry->dept != NULL)
	    if (strlen(bus_entry->dept))
		add_text_packet(bus_entry->dept);
	if (bus_entry->po_box != NULL)
	    if (strlen(bus_entry->po_box))
		add_text_packet(bus_entry->po_box);
	if (bus_entry->addr != NULL)
	    if (strlen(bus_entry->addr))
		add_text_packet(bus_entry->addr);
	if (bus_entry->telex != NULL)
	    if (strlen(bus_entry->telex))
		add_text_packet(bus_entry->telex);
	if (bus_entry->fax_number != NULL)
	    if (strlen(bus_entry->fax_number))
		add_text_packet(bus_entry->fax_number);
	if (bus_entry->free_1 != NULL)
	    if (strlen(bus_entry->free_1))
		add_text_packet(bus_entry->free_1);
	if (bus_entry->free_2 != NULL)
	    if (strlen(bus_entry->free_2))
		add_text_packet(bus_entry->free_2);
	if (bus_entry->free_3 != NULL)
	    if (strlen(bus_entry->free_3))
		add_text_packet(bus_entry->free_3);
	if (bus_entry->free_4 != NULL)
	    if (strlen(bus_entry->free_4))
		add_text_packet(bus_entry->free_4);
	if (bus_entry->free_5 != NULL)
	    if (strlen(bus_entry->free_5))
		add_text_packet(bus_entry->free_5);
		/* build end of record packet */
	build_packet=(struct boss_packet *)malloc(sizeof(struct boss_packet));
	build_packet->size=0;
	if (bus_entry->marked) /* marked record? */
	    build_packet->header.target=0x80;
	else
	    build_packet->header.target=0;
	build_packet->header.position=0;
	build_packet->header.control=REC_END; /* end of record */
	packet_checksum(build_packet);
	last->next=build_packet; /* add to linked list of packets */
	last=build_packet;
	last->next=NULL;
	bus_entry=bus_entry->next; /* next record */
    }
    return(head);
}
@//E*O*F lib/add_bcard.c//
chmod u=rw,g=r,o=r lib/add_bcard.c
 
echo mkdir - include
mkdir include
chmod u=rwx,g=rx,o=rx include
 
echo x - include/boss.h
sed 's/^@//' > "include/boss.h" <<'@//E*O*F include/boss.h//'
/*
  boss.h - global definitions for BOSS packet structure and state machine
	   declarations.
	   Mark Dapoz   1990/02/11
	     mdapoz@hybrid.UUCP  or  
	     mdapoz%hybrid@cs.toronto.edu
*/

#include <ctype.h>

#define	START_TIMEOUT	120	/* wait for 2 minutes for transfer to start */
#define	CHAR_TIMEOUT	5	/* wait for 5 seconds for next char */

#ifndef byte
#define	byte	unsigned char	/* 8 bits ? */
#endif

/*
Packet format:
+------+--------+-----------+---------------------------------------+-------+
|      |  data  |   Header  |             Data Bytes                | check |
| ":"  |  byte  +---+---+---+---+---+---+-----------+---+---+---+---| sum   |
|      |  count |   |   |   |   |   |   | ......... |   |   |   |   |       |
+------+--------+---+---+---+---+---+---+-----------+---+---+---+---+-------+
*/

struct boss_packet {	/* information passed in one BOSS packet */
	byte size;			/* number of data bytes in packet */
	struct packet_header{
		byte target;		/* target location for packets */
		byte position;		/* position in record */
		byte control; } header;	/* control packet information */
	byte data[256];			/* packet data bytes */
	byte checksum;			/* 2's compliment checksum */
	struct boss_packet *next;	/* pointer to next packet */
	};

				/* Packet header control byte values */
#define	REC_END		0x01	/* end of record indicator */
#define	SEL_DEST	0x02	/* BOSS destination selection */
#define	TX_END		0xff	/* end of transmission indicator */

				/* SEL_DEST destination values */
#define	DEST_CAL	0x80	/* calendar function */
#define	DEST_TEL	0x90	/* telephone directory */
#define	DEST_MEMO	0xa0	/* memo pad */
#define	DEST_SCHED	0xb0	/* scheduler */
#define	DEST_BUS	0xc0	/* business card directory */

				/* Packet header target byte values */
#define	TARG_TEXT	0x80	/* text data */
#define	TARG_ALARM	0xc0	/* set alarm in scheduler */
#define	TARG_MARK	0xd0	/* mark days in scheduler */
#define	TARG_TIME	0xe0	/* set time in scheduler */
#define	TARG_DATE	0xf0	/* set date in scheduler */

#define	CR		0x0d	/* ascii chars used */
#define	LF		0x0a

				/* states used by state machine */
#define	CONNECT		1	/* waiting to connect to BOSS */
#define	COLON		2	/* scanning for ":" */
#define	SIZE		3	/* reading BOSS packet size */
#define	HEADER		4	/* reading BOSS packet header */
#define	DATA		5	/* reading BOSS packet data */
#define	CHECKSUM	6	/* reading BOSS packet checksum */

#define	MAX_BOSS_REC	384	/* maximum amount of info in an entry */
#define	CRLF_ACK	0x11	/* positive ack for initial connection */
#define	PROTO_ACK	'#'	/* positive ack for data transfer protocol */
#define	PROTO_ABORT	'!'	/* abort signal for data transfer protocol */
#define	PROTO_LOST	'?'	/* confused signal for data transfer protocol */

				/* hexadecimal to decimal conversion */
static int hex2dec[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
			 0, 0, 0, 0, 0, 0, 0,
			 10, 11, 12, 13, 14, 15 };

#define	h2d(x)	(isxdigit(x) ? hex2dec[(toupper(x))-'0'] : 0)

			/* structures used by library routines */
struct bus_card {		/* business card entry structure */
	char *employer;
	char *name;
	char *tel_number;
	char *position;
	char *dept;
	char *po_box;
	char *addr;
	char *telex;
	char *fax_number;
	char *free_1;
	char *free_2;
	char *free_3;
	char *free_4;
	char *free_5;
	byte marked;		/* is this record to be marked */
	struct bus_card *next;
	};

struct telephone {		/*  telephone directory entry structure */
	char *name;
	char *tel_number;
	char *addr;
	char *free_1;
	char *free_2;
	char *free_3;
	char *free_4;
	char *free_5;
	char *free_6;
	byte marked;		/* is this record to be marked */
	struct telephone *next;
	};

struct memo {			/*  memo pad entry structure */
	char *text;
	byte marked;		/* is this record to be marked */
	struct memo *next;
	};

struct schedule {		/* schedule keeper entry structure */
	char *date;
	char *time;
	char *alarm;
	char *text;
	byte marked;		/* is this record to be marked */
	struct schedule *next;
	};

struct calendar {		/* calendar entry structure */
	char *month;
	byte days[4];		/* bitfield indicating marked days */
	struct calendar *next;
	};

				/* BOSS interface routines */
int boss_open();		/* open a device for BOSS i/o */
int boss_close();		/* close device opened for BOSS i/o */
int boss_write();		/* write BOSS packets */
struct boss_packet *boss_read();/* read BOSS packets */
struct telephone *extract_tele();/* extract telephone entries from packets */
struct bus_card *extract_bcard();/* extract bus card entries from packets */
struct memo *extract_memo();	/* extract memo entries from packets */
struct schedule *extract_sched();/* extract schedule entries from packets */
struct calendar *extract_cal();/* extract marked calendar entries from packets*/
struct boss_packet *add_tele();	/* add telephone entries to packet stream */
struct boss_packet *add_bcard();/* add business card entried to packet stream */
struct boss_packet *add_memo();	/* add memo entries to packet stream */
struct boss_packet *add_sched(); /* add schedule entries to packet stream */
struct boss_packet *add_cal(); /* add calendar entries to packet stream */
				/* general purpose functions in libboss.a */
char *next_field(); /* similar to strtok() function */
@//E*O*F include/boss.h//
chmod u=rw,g=r,o=r include/boss.h
 
echo mkdir - bin
mkdir bin
chmod u=rwx,g=rx,o=rx bin
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     65    458   3086 MANIFEST
     33     95    771 Makefile
     32    117    805 next_field.c
    113    328   3424 extract_tele.c
     60    252   1722 boss_open.c
    193    608   5561 boss_read.c
     58    227   1906 extract_memo.c
     57    149   1505 Makefile
    138    366   4175 extract_bus.c
     97    353   3458 extract_sch.c
     69    238   2365 extract_cal.c
     12     22    166 boss_close.c
    114    394   3345 boss_write.c
     83    258   2845 add_tele.c
     32     79    848 add_end_tx.c
     20     37    464 packet_chksm.c
     62    169   1957 text_packet.c
     59    202   1953 add_memo.c
     28     74    794 date_packet.c
     68    223   2299 add_sched.c
     28     74    794 time_packet.c
     28     74    798 alarm_packet.c
     67    240   2349 add_cal.c
     98    296   3370 add_bcard.c
    152    796   5227 boss.h
   1766   6129  55987 total
!!!
wc  MANIFEST Makefile lib/next_field.c lib/extract_tele.c lib/boss_open.c lib/boss_read.c lib/extract_memo.c lib/Makefile lib/extract_bus.c lib/extract_sch.c lib/extract_cal.c lib/boss_close.c lib/boss_write.c lib/add_tele.c lib/add_end_tx.c lib/packet_chksm.c lib/text_packet.c lib/add_memo.c lib/date_packet.c lib/add_sched.c lib/time_packet.c lib/alarm_packet.c lib/add_cal.c lib/add_bcard.c include/boss.h | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0
-- 
Managing a software development team 	|   Mark Dapoz  
is a lot like being on the psychiatric	|   mdapoz%hybrid@cs.toronto.edu
ward.  -Mitch Kapor, San Jose Mercury	|   mdapoz@torvm3.iinus1.ibm.com

mdapoz@hybrid.uucp (Mark Dapoz) (07/05/90)

---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- 

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by mdapoz@hybrid on Thu Jul  5 01:05:50 EDT 1990
# Contents:  doc/ cfg/ doc/protocol.spec doc/bosslib.doc doc/grammar.spec
#	cfg/boss.c cfg/boss_cfg.y cfg/Makefile cfg/y.tab.h cfg/boss_lex.l
 
echo mkdir - doc
mkdir doc
chmod u=rwx,g=rx,o=rx doc
 
echo x - doc/protocol.spec
sed 's/^@//' > "doc/protocol.spec" <<'@//E*O*F doc/protocol.spec//'

		Casio SF-9000 Serial Communication Protocol
		-------------------------------------------


Author: Mark Dapoz 
Email: mdapoz@hybrid.UUCP  or  mdapoz%hybrid@cs.toronto.edu  or
       ...uunet!mnetor!hybrid!mdapoz

Document version: 1.0
Date: 1990-06-25


Introduction
------------

This document describes the communication protocol used by the Casio SF-9000
(aka BOSS) to communicate with a host computer connected to the RS232 adapter.
The information presented in this document has been derived from work performed
by the author and is in no way based on any Casio documents or code.  As such,
this document may contain errors and is presented on an "as is" basis.

Packet Format
-------------

The basic unit of communication in the protocol is a packet.  A packet is
defined to be a series of bytes in the following format:

+------+--------+-----------+---------------------------------------+-------+
|      |  data  |   Header  |             Data Bytes                | check |
| ":"  |  byte  +---+---+---+---+---+---+-----------+---+---+---+---| sum   |
|      |  count |   |   |   |   |   |   | ......... |   |   |   |   |       |
+------+--------+---+---+---+---+---+---+-----------+---+---+---+---+-------+

The initial byte must be a colon (ie. ":", ascii character 0x3a).  This is
immediatly followed by a single byte called a "data byte count" which 
represents a numerical value of the number of "data bytes" contained in the
packet.  The value of "data byte count" is limited to 255 and as such each
packet may only contain 255 bytes worth of data.  Immediatly following the
"data byte count" is a sequence of three (3) bytes which form a packet
header.  This packet header will be discussed further in the following section.
Following the packet header are the packet "data bytes", the number of which
must be equal to the value of "data byte count".  The actual data represented 
by the "data bytes" is dependant on the header.  Following the "data bytes" 
is a checksum byte which is the sum of all the bytes following the colon (but
not including it) and ending at the last data byte.  The final result is 
stored in two's compliment format.

Serial Line Data Stream
----------------------

The communication data stream must be able to handle 7 bit communication.  To
accomplish this, it is necessary to encode the 8 bit data into a format which
may reliably be transmitted in a 7 bit format.  The encoding used by the BOSS
is to transmit every byte as its ascii hexadecimal representation which 
requires two bytes.  As an example, if the byte 0xf5 is to be transmitted,
what will instead be transmitted are the bytes 0x46 and 0x35 (ie ascii F 5).
This encoding is performed even if an 8 bit communication path is being used.
In the above packet description, the only byte which is not encoded in this
manner is the initial colon.

Header Format
-------------

The header for each packet is composed of three bytes and carries all the
information required to interpret the data represented by the data bytes.
The three header bytes may be labelled as follows: target, position & control
respectively.  

o Control Byte Breakdown

If the control byte contains a non zero value, then this packet is to be 
interpreted as a control packet.  The known values for the control byte are 
as follows:

xxh 00h 01h - End of record indicator
	      This indicates the completion of transmission of one record.
	      If the record just transmitted was a marked record, xx will be
	      80, otherwise it will be 00.  No data is passed with this
	      packet.

00h 00h ffh - End of transmission indicator
	      This is the last packet the BOSS will send and indicates a
	      closure in communication.  No data is passed with this packet

00h 00h 02h - Select target destination
	      This packet is used to select which area in the BOSS the
	      following packets will be stored.  The data bytes will indicate
	      the target location for packets which follow.  Two data bytes 
	      will always be transmitted with this packet and can be 
	      interpreted using the following table:

		Data Bytes	Target Location
		----------	---------------
		  80h 00h	Calendar
		  90h 00h	Telephone Directory
		  a0h 00h	Memo Pad
		  b0h 00h	Schedule
		  c0h 00h	Business Cards

o Target Byte Breakdown

If the control byte is 0x00, then the target byte is used to determine the
disposition of the packet.  The known interpretations of the target byte
are as follows:

8xh xxh 00h - Store data at preselected target location.
	      The low order nibble of the first byte along with the second
	      byte (12 bits in total) indicate the position the data in this
	      packet is placed within the record.  The actual target location
	      of the record must have been previously selected using a "select
	      target destination" control packet.

c0h 00h 00h - Alarm marker
	      The data field contains an ascii string of the form "hh:ss" which
	      represents the hours and minutes (in 24 hour format) at which
	      time the alarm is to be sounded.  The date of the alarm must be
	      previously set using a "date marker" packet.

d0h 00h 00h - Marked days in calendar
	      The data field will always contain 4 bytes which provide 32 bits
	      worth of information.  These bits each represent one day in the
	      month.  If a bit is turned on, the associated day is highlighed
	      in the calendar.  The least significant bit (ie. last bit in 
	      the fourth byte) represents the first day of the month.  The
	      month which this packet refers to will have been previously set
	      using a "date marker" packet.

e0h 00h 00h - Time marker
	      The data field contains an ascii string of the form "hh:ss" or
	      "hh:ss~hh:ss" for a time range, which represents the hours and 
	      minutes (in 24 hour format).  This packet sets the time range
	      for items such as schedules.

f0h 00h 00h - Date marker
	      The data field contains an ascii string of the form "yyyy-mm-dd"
	      or "yyyy-mm-dd~yyyy-mm-dd" for a date range, which represents 
	      the year, month and day for the following packets.  This packet
	      sets the date range for items such as schedules.

Handshake Protocol
------------------

The are two handshakes which must be performed during the protocol.  The first
one involves an initial handshake to establish communication between the
two ends.  The sending end will continue to send the sequence cr lf (ie. 
carriage return (ascii 0x0d), line feed (ascii 0x0a)) until the receiving
end acknowledges this with a 0x11.  At this point the sending end begins to
transmit the first packet.  Packets are continually transmitted until an
"end of record" packet has been transmitted.  At this point the receiving
end will acknowledge correct receipt of all packets (as verified by the 
checksum) by sending a "#" (ascii 0x23).  Once the sending end receives 
this acknowledgement, it will continue to send the next sequence of packets.

Packet Sequences
----------------

Each data area within the BOSS (eg. telephone directory) has a certain sequence
of packets associated with it.  Below is a description of the packet sequences
transmitted by the SF-9000.  Each of these sequences is terminated by an
"end of record" packet.

o Telephone Directory

The initial packet sent out is a "select destination" packet which specifies
the telephone directory.  Following that is a series of "store data" packets
which contain the data for the fields of the record.  The data can be treated
as a byte stream of fields separated by linefeed (ascii 0x0a) characters.
A typical packet sequence may be as follows:

Note: \n denotes a linefeed character (ascii 0x0a)

		Packet Data				Interpretation
		-----------				--------------
Packet 001:  Size=02h  Header= 00h 00h 02h	Select destination of telephone
Data=90h 00h 					directory

Packet 002:  Size=0bh  Header= 80h 00h 00h	First packet contains first
Data=John Smith\n				field of record

Packet 003:  Size=0fh  Header= 80h 0bh 00h	Second field of record
Data=(123) 123-4567\n

Packet 004:  Size=01h  Header= 80h 1ah 00h	Third field of record
Data=\n

Packet 005:  Size=0eh  Header= 80h 1bh 00h	Fourth field of record
Data=bus: (123) 123-4567

Packet 006:  Size=00h  Header= 00h 00h 01h	End of record indicator

Packet 007:  Size=00h  Header= 00h 00h ffh	End of transmission indicator

o Memo Pad

The initial packet sent out is a "select destination" packet which specifies
the memo pad.  Following that is a series of "store data" packets which 
contain the data for the memo.  The data can be treated as a byte stream of 
data.

A typical packet sequence may be as follows:

Note: \r denotes a carriage return character (ascii 0x0d)

		Packet Data				Interpretation
		-----------				--------------
Packet 001:  Size=02h  Header= 00h 00h 02h	Select destination of memo
Data=a0h 00h 

Packet 002:  Size=0eh  Header= 80h 00h 00h	First packet of data
Data=Things to buy\r

Packet 003:  Size=01h  Header= 80h 0eh 00h	Second packet of data
Data=\r

Packet 004:  Size=0dh  Header= 80h 0fh 00h	Third packet of data
Data=New Batteries

Packet 005:  Size=00h  Header= 00h 00h 01h	End of record indicator

Packet 006:  Size=00h  Header= 00h 00h ffh	End of transmission indicator

o Business Card

The initial packet sent out is a "select destination" packet which specifies
the business card.  Following that is a series of "store data" packets
which contain the data for the fields of the record.  The data can be treated
as a byte stream of fields separated by linefeed (ascii 0x0a) characters.

A typical packet sequence may be as follows:

Note: \n denotes a linefeed character (ascii 0x0a)
      \r denotes a carriage return character (ascii 0x0d)

		Packet Data				Interpretation
		-----------				--------------
Packet 001:  Size=02h  Header= 00h 00h 02h	Select destination of bus card
Data=c0h 00h 

Packet 002:  Size=12h  Header= 80h 00h 00h	First field of record
Data=J.Q. Public Corp.\n

Packet 003:  Size=0bh  Header= 80h 12h 00h	Second field of record
Data=John Smith\n

Packet 004:  Size=0fh  Header= 80h 1dh 00h	Third field of record
Data=(123) 123-4567\n

Packet 005:  Size=13h  Header= 80h 2ch 00h	Fourth field of record
Data=Systems Programmer\n

Packet 006:  Size=04h  Header= 80h 3fh 00h	Fifth field of record
Data=123\n

Packet 007:  Size=01h  Header= 80h 43h 00h	Sixth field of record
Data=\n

Packet 008:  Size=0eh  Header= 80h 44h 00h	Seventh field of record
Data=123 River St.\r

Packet 009:  Size=16h  Header= 80h 52h 00h	Seventh field continued
Data=Toronto, Ont. A1B 2C3\n

Packet 010:  Size=01h  Header= 80h 68h 00h	Eighth field of record
Data=\n

Packet 011:  Size=01h  Header= 80h 69h 00h	Nineth field of record
Data=\n

Packet 012:  Size=13h  Header= 80h 6ah 00h	Tenth field of record
Data=jsmith@jqpublic.com

Packet 013:  Size=00h  Header= 00h 00h 01h	End of record indicator

Packet 014:  Size=00h  Header= 00h 00h ffh	End of transmission indicator

o Schedule

The initial packet sent out is a "select destination" packet which specifies
the schedule.  Following that is a "date marker" packet which sets the date
of this record.  This may be followed by an optional "set time" packet which
sets a specific time for this record.  This packet is then followed by an
optional "alarm marker" packet which sets the alarm for the record.  Following
this is a series of "store data" packets which contain the text description of
the event.  The "store data" packets can be treated as a byte stream.

A typical packet sequence may be as follows:

Note: \r denotes a carriage return character (ascii 0x0d)

		Packet Data				Interpretation
		-----------				--------------
Packet 001:  Size=02h  Header= 00h 00h 02h	Select destination of scheduler
Data=b0h 00h 

Packet 002:  Size=15h  Header= f0h 00h 00h	Set target dates for this record
Data=1990-01-22~1990-01-26

Packet 003:  Size=12h  Header= 80h 00h 00h	First part of descrption
Data=Usenix Conference\r

Packet 004:  Size=0eh  Header= 80h 12h 00h	Second part of description
Data=Washington, DC

Packet 005:  Size=00h  Header= 00h 00h 01h	End of record indicator

Packet 006:  Size=0ah  Header= f0h 00h 00h	Select date for next record
Data=1990-02-02

Packet 007:  Size=0bh  Header= e0h 00h 00h	Set scheduled time for record
Data=10:30~12:00

Packet 008:  Size=05h  Header= c0h 00h 00h	Set scheduled alarm for record
Data=10:15

Packet 008:  Size=14h  Header= 80h 00h 00h	First part of text for record
Data=Look at kernel dump\r

Packet 009:  Size=0eh  Header= 80h 14h 00h	Second part of text for record
Data=NFS server #2\r

Packet 010:  Size=08h  Header= 80h 22h 00h	Third part of text for record
Data=Module 6

Packet 011:  Size=00h  Header= 00h 00h 01h	End of record indicator

o Calendar

The initial packet sent out is a "select destination" packet which specifies
the calendar.  Following that is a "date marker" packet which sets the date
of this record (always specifying the first days of a month).  This is 
followed by a "marked days" packet which will contain a bitfield indicating 
which days in the calendar are marked.

A typical packet sequence may be as follows:

		Packet Data				Interpretation
		-----------				--------------
Packet 001:  Size=02h  Header= 00h 00h 02h	Select destination of calendar
Data=80h 00h 

Packet 002:  Size=0ah  Header= f0h 00h 00h	Set target date for this record
Data=1990-03-01

Packet 003:  Size=04h  Header= d0h 00h 00h	Bitfield indicating the 1st and
Data=00h 02h 00h 01h 				18th are marked days

Packet 004:  Size=00h  Header= 00h 00h 01h	End of record indicator


@//E*O*F doc/protocol.spec//
chmod u=rw,g=r,o=r doc/protocol.spec
 
echo x - doc/bosslib.doc
sed 's/^@//' > "doc/bosslib.doc" <<'@//E*O*F doc/bosslib.doc//'

			Casio BOSS C Library Interface
			------------------------------

Author: Mark Dapoz 
Email: mdapoz@hybrid.UUCP  or  mdapoz%hybrid@cs.toronto.edu  or
       ...uunet!mnetor!hybrid!mdapoz

Document version: 1.0
Date: 1990-06-25


Introduction
------------

This document describes the C library routines provided to interface 
applications to the Casio BOSS.  The basic unit of communication between
these routines is a linked list of structures.  The specific structure
used is dependent on the routine and is specified in the following section.
All of these routine allocate memory for the linked lists dynamically.  Since 
the actual amount of memory which is allocated is generally small, none of
these routines check for memory allocation failure and assume that a call
to malloc() will always succeed.  It is the applications responsibilty to
de-allocate the memory used by the linked lists if it is no longer required.

Basic Functions
---------------

int boss_open(device)
char *device;

Opens the device specified by "device" and sets the communications parameters
to 9600 baud, 8 data bits and 1 stop bit.  Upon successful completion, the 
function returns a non-negative integer representing the file descriptor of
the opened device.  Otherwise a value of -1 is returned.


int boss_close(boss_fd)	
int boss_fd;

Closes the device with the file descriptor of "boss_fd" which is usually 
provided by the boss_open() routine.  Upon successful completion, the function 
returns a value of zero.  Otherwise a value of -1 is returned.
			

struct boss_packet *boss_read(boss_fd)
int boss_fd;

Accepts packets from the device specified by file descriptor "boss_fd" and 
places them on a link list of structures of the form "boss_packet".  Upon
successful receipt of all packets, the function returns a non-null pointer
to the first element in the linked list.  Otherwise a null pointer is
returned.  Each member of the linked list is a structure of the following
form:

struct boss_packet {		/* information passed in one BOSS packet */
	byte size;			/* number of data bytes in packet */
	struct packet_header{
		byte target;		/* target location for packets */
		byte position;		/* position in record */
		byte control; } header;	/* control packet information */
	byte data[256];			/* packet data bytes */
	byte checksum;			/* 2's compliment checksum */
	struct boss_packet *next;	/* pointer to next packet */
	};

The linked list returned by boss_read() forms the input for all the data
extraction routines.  The linked list is terminated by a null pointer in
the "next" member.


int boss_write(boss_fd, boss_packets)
int boss_fd;
struct boss_packet *boss_packets;

Accepts a linked list of packet structures pointed to by boss_packets and 
writes them to the device specified by file descriptor boss_fd.
Upon successful transmission of all packets, the function returns a value
of zero, otherwise a non-zero value is returned.  Each member of the linked 
list of packets is a structure of the following form (usually built using
the add_* routines):

struct boss_packet {		/* information passed in one BOSS packet */
	byte size;			/* number of data bytes in packet */
	struct packet_header{
		byte target;		/* target location for packets */
		byte position;		/* position in record */
		byte control; } header;	/* control packet information */
	byte data[256];			/* packet data bytes */
	byte checksum;			/* 2's compliment checksum */
	struct boss_packet *next;	/* pointer to next packet */
	};


Packet Manipulation Routines (data extraction)
----------------------------------------------

The following routines are provided to extract data from the linked list
of packets returned by the boss_read() routine.  Each of the routines accepts 
the linked list returned by boss_read() as input.  The data returned by each
function is a linked list of structures containing the data appropriate
to that routine.  In each of the structures, you will find a member named
"next" which is a pointer to the next structure in the list.  The final
structure in the list will contain a null pointer in this member.


struct bus_card *extract_bcard(packet_ptr)
struct boss_packet *packet_ptr;

Extract the business card entries from a stream of linked list of BOSS 
packets.  Upon completion, the function returns a non-null pointer to the 
first element of a linked list of structures.  If no business card entries 
are present in the input stream, a null pointer is returned.  Each member of 
the linked list is a structure of the following form:

struct bus_card {		/* business card entry structure */
	char *employer;
	char *name;
	char *tel_number;
	char *position;
	char *dept;
	char *po_box;
	char *addr;
	char *telex;
	char *fax_number;
	char *free_1;
	char *free_2;
	char *free_3;
	char *free_4;
	char *free_5;
	byte marked;		/* is this record to be marked */
	struct bus_card *next;
	};

Each member in the structure corresponds to the fields of a business card entry
as documented in the BOSS owner's manual.  The only exception to this is the
"marked" member.  This member contains a boolean value indicating whether or not
this record is marked.  If a field does not contain a value, then the pointer
for that member will be the null pointer.


struct telephone *extract_tele(packet_ptr)
struct boss_packet *packet_ptr;

Extract the telephone book entries from a stream of linked list of BOSS 
packets.  Upon completion, the function returns a non-null pointer to the 
first element of a linked list of structures.  If no telephone entries are 
present in the input stream, a null pointer is returned.  Each member of the 
linked list is a structure of the following form:

struct telephone {		/*  telephone directory entry structure */
	char *name;
	char *tel_number;
	char *addr;
	char *free_1;
	char *free_2;
	char *free_3;
	char *free_4;
	char *free_5;
	char *free_6;
	byte marked;		/* is this record to be marked */
	struct telephone *next;
	};

Each member in the structure corresponds to the fields of a telephone entry
as documented in the BOSS owner's manual.  The only exception to this is the
"marked" member.  This member contains a boolean value indicating whether or not
this record is marked.  If a field does not contain a value, then the pointer
for that member will be the null pointer.


struct memo *extract_memo(packet_ptr)
struct boss_packet *packet_ptr;

Extract the memo entries from a stream of linked list of BOSS packets.  Upon 
completion, the function returns a non-null pointer to the first element of 
a linked list of structures.  If no memo entries are present in the input
stream, a null pointer is returned.  Each member of the linked list is a 
structure of the following form:

struct memo {			/*  memo pad entry structure */
	char *text;
	byte marked;		/* is this record to be marked */
	struct memo *next;
	};

The member "text" is a pointer to the data for the memo entry.  The "marked" 
member contains a boolean value indicating whether or not this record is marked.


struct schedule *extract_sched(packet_ptr)
struct boss_packet *packet_ptr;

Extract the schedule entries from a stream of linked list of BOSS packets.  
Upon completion, the function returns a non-null pointer to the first element 
of a linked list of structures.  If no schedule entries are present in the input
stream, a null pointer is returned.  Each member of the linked list is a 
structure of the following form:

struct schedule {		/* schedule keeper entry structure */
	char *date;
	char *time;
	char *alarm;
	char *text;
	byte marked;		/* is this record to be marked */
	struct schedule *next;
	};

Each member in the structure corresponds to the fields of a schedule entry
as documented in the BOSS owner's manual.  The only exception to this is the
"marked" member.  This member contains a boolean value indicating whether or not
this record is marked.  If a field does not contain a value, then the pointer
for that member will be the null pointer.  Although the members in this 
structure correspond to the fields documented in the BOSS owner's manual, the
actual data contained in the fields is not documented in the owner's manual.
The following table should be used to interpret the values of the above members.

	Member Name		Valid Data Formats
	-----------		------------------
	   date			yyyy-mm-dd		(single date)
				yyyy-mm-dd~yyyy-mm-dd	(date range)
	   time			hh:mm			(single time, 24hr fmt)
				hh:mm~hh:mm		(time range, 24hr fmt)
	   alarm		hh:mm

where: "yyyy" represents a year specification
       "mm" represents a month specification
       "dd" represents a day specification
       "hh" represents an hour specification (in 24 hour format)
       "mm" represents a minute specification


struct calendar *extract_cal(packet_ptr)
struct boss_packet *packet_ptr;

Extract the calendar entries from a stream of linked list of BOSS packets.  
Upon completion, the function returns a non-null pointer to the first element 
of a linked list of structures.  If no calendar entries are present in the input
stream, a null pointer is returned.  Each member of the linked list is a 
structure of the following form:

struct calendar {		/* calendar entry structure */
	char *month;
	byte days[4];		/* bitfield indicating marked days */
	struct calendar *next;
	};

The month specification is of the form "yyyy-mm-01" where "yyyy" represents the
year and "mm" the month.  The "days" member will contain 4 bytes which provide 
32 bits worth of information.  These bits each represent one day in the month.
If a bit is turned on, the associated day is highlighed in the calendar.  The 
least significant bit (ie. last bit in the fourth byte) represents the first 
day of the month.


Packet Manipulation Routines (data addition)
--------------------------------------------

The following routines are provided to add data to the linked list of packets 
required by the boss_write() routine.  Each of the routines accepts as input 
two parameters, the first of which is a linked list of data structures
describing the data for the records to add (In each of the structures, you 
will find a member named "next" which is a pointer to the next structure in 
the list.  The final structure in the list will contain a null pointer in this 
member).  The second parameter is a pointer to a linked list of packets to 
which the records are to be added.  If the record is not to be added to an 
existing linked list of packets (ie. start a new linked list), then the 
pointer passed must be the NULL pointer.  The data returned by each of 
functions is a linked list of packets.


struct boss_packet *add_bcard(bus_entry, packet_ptr)
struct bus_card *bus_entry;
struct boss_packet *packet_ptr;

Add business card entries from a linked list of bus_card structures to the 
linked list of packets pointed to by packet_ptr.  Upon completion, the function
returns the pointer to the first element of the linked list of packets (if
packet_ptr is non-null, then that value is returned with the new packets
added to the end of it).

Each member of the bus_card linked list is a structure of the following form:

struct bus_card {		/* business card entry structure */
	char *employer;
	char *name;
	char *tel_number;
	char *position;
	char *dept;
	char *po_box;
	char *addr;
	char *telex;
	char *fax_number;
	char *free_1;
	char *free_2;
	char *free_3;
	char *free_4;
	char *free_5;
	byte marked;		/* is this record to be marked */
	struct bus_card *next;
	};

Each member in the structure corresponds to the fields of a business card entry
as documented in the BOSS owner's manual.  The only exception to this is the
"marked" member.  This member contains a boolean value indicating whether or not
this record is marked.  If a field does not contain a value, then the pointer
for that member will be the null pointer.


struct boss_packet *add_cal(cal_entry, packet_ptr)
struct calendar *cal_entry;
struct boss_packet *packet_ptr;

Add calendar entries from a linked list of calendar structures to the linked 
list of packets pointed to by packet_ptr.  Upon completion, the function
returns the pointer to the first element of the linked list of packets (if
packet_ptr is non-null, then that value is returned with the new packets
added to the end of it).

Each member of the calendar linked list is a structure of the following form:

struct calendar {		/* calendar entry structure */
	char *month;
	byte days[4];		/* bitfield indicating marked days */
	struct calendar *next;
	};

The month specification must be of the form "yyyy-mm-01" where "yyyy" represents
the year and "mm" the month.  The "days" member must contain 4 bytes which 
provide 32 bits worth of information.  These bits each represent one day in 
the month.  If a bit is turned on, the associated day is highlighed in the 
calendar.  The least significant bit (ie. last bit in the fourth byte) 
represents the first day of the month.


struct boss_packet *add_end_tx(packet_ptr)
struct boss_packet *packet_ptr;

Add an end of transmission packet to the linked list of packets pointed to by 
packet_ptr.  Upon completion, the function returns the pointer to the first 
element of the linked list of packets (if packet_ptr is non-null, then that 
value is returned with the end of transmission packet added to the end of it).


struct boss_packet *add_memo(memo_entry, packet_ptr)
struct memo *memo_entry;
struct boss_packet *packet_ptr;

Add memo entries from a linked list of memo structures to the linked list of 
packets pointed to by packet_ptr.  Upon completion, the function returns the 
pointer to the first element of the linked list of packets (if packet_ptr is 
non-null, then that value is returned with the new packets added to the end 
of it).

Each member of the memo linked list is a structure of the following form:

struct memo {			/*  memo pad entry structure */
	char *text;
	byte marked;		/* is this record to be marked */
	struct memo *next;
	};

The member "text" is a pointer to the data for the memo entry.  The "marked" 
member contains a boolean value indicating whether or not this record is marked.


struct boss_packet *add_sched(sched_entry, packet_ptr)
struct schedule *sched_entry;
struct boss_packet *packet_ptr;

Add schedule entries from a linked list of schedule structures to the linked 
list of packets pointed to by packet_ptr.  Upon completion, the function 
returns the pointer to the first element of the linked list of packets (if 
packet_ptr is non-null, then that value is returned with the new packets added 
to the end of it).

Each member of the schedule linked list is a structure of the following form:

struct schedule {		/* schedule keeper entry structure */
	char *date;
	char *time;
	char *alarm;
	char *text;
	byte marked;		/* is this record to be marked */
	struct schedule *next;
	};

Each member in the structure corresponds to the fields of a schedule entry
as documented in the BOSS owner's manual.  The only exception to this is the
"marked" member.  This member contains a boolean value indicating whether or not
this record is marked.  If a field does not contain a value, then the pointer
for that member should be the null pointer.  Although the members in this 
structure correspond to the fields documented in the BOSS owner's manual, the
actual data contained in the fields is not documented in the owner's manual.
The following table should be used to assign the values to the above members.

	Member Name		Valid Data Formats
	-----------		------------------
	   date			yyyy-mm-dd		(single date)
				yyyy-mm-dd~yyyy-mm-dd	(date range)
	   time			hh:mm			(single time, 24hr fmt)
				hh:mm~hh:mm		(time range, 24hr fmt)
	   alarm		hh:mm

where: "yyyy" represents a year specification
       "mm" represents a month specification
       "dd" represents a day specification
       "hh" represents an hour specification (in 24 hour format)
       "mm" represents a minute specification


struct boss_packet *add_tele(tele_entry, packet_ptr)
struct telephone *tele_entry;
struct boss_packet *packet_ptr;

Add telephone book entries from a linked list of telephone structures to the 
linked list of packets pointed to by packet_ptr.  Upon completion, the function 
returns the pointer to the first element of the linked list of packets (if 
packet_ptr is non-null, then that value is returned with the new packets added 
to the end of it).

Each member of the telephone linked list is a structure of the following form:

struct telephone {		/*  telephone directory entry structure */
	char *name;
	char *tel_number;
	char *addr;
	char *free_1;
	char *free_2;
	char *free_3;
	char *free_4;
	char *free_5;
	char *free_6;
	byte marked;		/* is this record to be marked */
	struct telephone *next;
	};

Each member in the structure corresponds to the fields of a telephone entry
as documented in the BOSS owner's manual.  The only exception to this is the
"marked" member.  This member contains a boolean value indicating whether or not
this record is marked.  If a field does not contain a value, then the pointer
for that member must be the null pointer.


Usage Notes:
-----------

The above routines which convert the high level data structures to low level
BOSS packets do NOT impose any restrictions on what data the fields contain.
If your application passes invalid data to the conversion routines (eg. bad
date format), then that invalid data will be passed along to the BOSS and
will most probably result in a transmission error.  In addition, certain
BOSS functions such as the business card directory require both the employer 
and the name field to contain data in order for a record to be valid.  Again,
these conversion routines do NOT enforce these rules, that is an application
level responsibility.

The BOSS has a maximum record size of 384 bytes total!  It is an application
responsibility to ensure that this size is not exceeded, the library routines
will NOT enforce this rule!

ALL fields which contain data must be terminated with a line feed (ie. 0x0a)
character except for the last field which contains data.  The last field is
not terminated by any special character.

The high level data structures members are organised in a very specific order, 
namely the order in which the BOSS presents them to the user.  If you wish
to leave a member blank, you must either assign that member a null pointer or
provide it with a pointer to a string containing the line feed (ie. 0x0a) 
character.  The null pointer may be assigned only if ALL the members following
the current member are also assigned the null pointer (ie. no more data fields
follow).  Otherwise, the line feed character must be used.

Example:

We wish to pass a telephone book entry to the boss specifying the following
fields: name, telephone number & free field 1.  The telephone data structure
would then have to contain the following values:

	Member			Value
	-----------		------------------
	name			John Smith\n
	tel_number		(416) 123-4567\n
	addr			\n
	free_1			Good friend
	free_2			NULL
	free_3			NULL
	free_4			NULL
	free_5			NULL
	free_6			NULL

\n denotes the line feed character (ie. 0x0a) and NULL denotes the null pointer

For a complete example of how to use these libraries, a sample application
which uses a contex free grammer to express the data stored in the BOSS can
be found in the cfg directory.  The file boss_cfg.y provides a good example
of how to use the data addition routines while boss.c provides an example of 
how to use the data extraction routines.

@//E*O*F doc/bosslib.doc//
chmod u=rw,g=r,o=r doc/bosslib.doc
 
echo x - doc/grammar.spec
sed 's/^@//' > "doc/grammar.spec" <<'@//E*O*F doc/grammar.spec//'

		Casio BOSS Interface Grammar Specification
		------------------------------------------

Author: Mark Dapoz 
Email: mdapoz@hybrid.UUCP  or  mdapoz%hybrid@cs.toronto.edu  or
       ...uunet!mnetor!hybrid!mdapoz

Document version: 1.0
Date: 1990-07-04


Introduction
------------

This document describes a general purpose context free grammar which has
been written to allow the Casio BOSS line of personal schedulers to be
easily programmed from a computer.  The grammar has been implemented using
the yacc and lex programming tools found on most UNIX systems.  Although
this is not a very extensive grammar, it does provide the ability to program
all the functions provided by the BOSS.  In the future, elements such as
loops and date arithmetic may be added to enhance its functionality.


Basic Definitions
-----------------

The basic element in the grammar is the %select structure which has the
following format:

			%select { TARGET }
				.
				.
			%endselect

where TARGET is one of the following strings:

			business card
			calendar
			memo
			schedule
			telephone

Each of the five target strings corresponds to its equivalent storage area in 
the BOSS.

Once a target location has been selected, each of the individual entries for
the location are separated by a %begin line which is always located at the
start of the entry.  Therefore, the basic %select structure can now be 
rewritten as follows:

			%select { TARGET }
			%begin
				.
				.
			%begin
				.
				.
			%begin
				.
				.
			%endselect

Following the %begin line is placed the target specific data which describes
the information to be assigned to the entry.  The valid assignments for each
target location vary depending on the location, therefore each will be 
described below one at a time.  However, first we must define some general 
purpose terms which are used.

Terminal Sybols
---------------

A STRING will be defined as any sequence of printable characters which are
enclosed by double quotes (").  Non-printable characters can be represented
by using their octal representation (specified as three digits) preceeded by 
a backslash (\).  Thus the following string can be used to represent the 
control-G (BEL) character: "\007"

Likewise, the double quote character can be represented within a string by
preceeding it with a backslash.  Thus the string "\"" will represent a single
double quote character.  The BOSS uses a full 8 bit character set to represent
stored information.  As a result there are many characters which cannot be
edited using many of the 7 bit editors found on UNIX systems.  To cope with
these editors, any non-printable character will be represented using its
octal representation.

A BOOLEAN will be defined to be either the word true or false.

A DATE will be defined to be a string of characters in the following form:
			yyyy-mm-dd
where 
	yyyy is a year specification which falls between the range of 1901 to
	     2099 (inclusive)
	mm   is a month specification which falls between the range of 01 to
	     12 (inclusive)
	dd   is a day specification which falls between the range of 01 to 31 
	     (inclusive, but dependant on month)

A DATE_RANGE will be defined as two valid DATE specifications separated by a
tilda (~) character.  Thus, the following is a valid date range specification:

			1990-01-22~1990-01-26

A MONTH is equivalent to a DATE specification except the day must be specified
as 01.

A TIME will be defined to be a string of characters of the following form:
			hh:mm
where 
	hh is an hour specification which falls between the range of 00 to 23
	   (inclusive)
	mm is a minute specification which falls between the range of 00 to 59
	   (inclusive)

A TIME_RANGE will be defined as two valid TIME specifications separated by a
tilda (~) character.  Thus, the following is a valid time range specification:

			08:30~13:00

A DAYS will be defined to be a sequence of numbers ranging from 1 to 31
(inclusive).


Valid Assignments
-----------------

In the following section, all text appearing to the left of an equal sign (=)
will be treated with case independence.  Thus, Name="John Smith" is equivalent
to name="John Smith" or NAME="John Smith".

o Business Cards

The business card %select structure contains the following definitions:

		%select {business card}
		%begin
		Employer   = STRING			(required)
		Name       = STRING			(required)
		Tel Number = STRING
		Position   = STRING
		Department = STRING
		P.O. Box   = STRING
		Address    = STRING
		Telex      = STRING
		Fax Number = STRING
		Free 1     = STRING
		Free 2     = STRING
		Free 3     = STRING
		Free 4     = STRING
		Free 5     = STRING
		marked     = BOOLEAN
		%endselect

Each of the above assignments corresponds to the fields of a business card
entry as defined by the Casio BOSS.  Required fields for a business card
entry are noted above.  The "marked" assignment is the only field which does 
not appear in the Casio BOSS record and it's used to indicate that this
entry is to be mark in the BOSS.  If any of the above assignments occur
more than once in a record, the multiple assignments will be concatenated
together with a carriage return placed between them.  For example, the following
assignments will cause two lines to be displayed on the BOSS, but both lines
will be part of the Address field:

		Address = "123 First Street"
		Address = "Don Mills, Ontario"


o Calendar

The calendar section is used to define which days for a particular month
are to appear highlighed.  The calendar %select structure contains the 
following definitions:

		%select {calendar}
		%begin
		Month      = MONTH			(required)
		Marked Days= DAYS			(required)
		%endselect


o Memo

The memo %select structure contains the following definitions:

		%select {memo}
		%begin
		Text       = STRING			(required)
		marked     = BOOLEAN
		%endselect

See the business card definition for more details.


o Schedule

The schedule %select structure contains the following definitions if a
specific date is specified:

		%select {schedule}
		%begin
		Date       = DATE			(required)
		Time       = TIME_RANGE
		Alarm Time = TIME
		Text       = STRING
		marked     = BOOLEAN
		%endselect

or the following definition if a date range is specified:

		%select {schedule}
		%begin
		Date       = DATE_RANGE			(required)
		Text       = STRING
		marked     = BOOLEAN
		%endselect


o Telephone

The telephone %select structure contains the following definitions:

		%select {telephone}
		%begin
		Name       = STRING			(required)
		Tel Number = STRING
		Address    = STRING
		Free 1     = STRING
		Free 2     = STRING
		Free 3     = STRING
		Free 4     = STRING
		Free 5     = STRING
		Free 6     = STRING
		marked     = BOOLEAN
		%endselect


Example
-------

Below is an example of how the above grammar can be used to specify data for
a BOSS.

%select {business card}
 %begin
  Employer   = "Kernel Hackers Inc."
  Name       = "John Q. Public"
  Tel Number = "(416) 555-1212 (bus)"
  Tel Number = "(416) 555-1212 (res)"
  Address    = "3500 Don Mills Road"
  Address    = "Willowdale, Ontario"
  Address    = "M2H 1K9"
  Fax Number = "fax: (416) 555-1212"
%endselect

%select {calendar}
 %begin
  Month      = 1990-03-01
  Marked Days= 2 18 

 %begin
  Month      = 1990-06-01
  Marked Days= 9 10 16 17 
%endselect

%select {memo}
 %begin
  Text       = "Automotive Maintenance Record"
  Text       = ""
  Text       = "20196 April 19, 1988"
  Text       = "      Oil Change, QState 10W30"
  Text       = "21647 Replace plugs"
  Text       = "      Clean carburator & engine"
  Text       = "23615 Adjusted carb choke/float"
  Text       = "27394 Aug 21, 1988"
  Text       = "      Oil Change, Castrol 20W50"
  Text       = "27725 Change front tires"
  Text       = "      Goodyear P225 60VR15"
  Text       = "33052 May 20, 1989"
  Text       = "      New air filter"
  Text       = "33111 May 21, 1989"
  Text       = "      Change oil, 20W50"
  Text       = "      Flush rad & refill"
%endselect

%select {schedule}
 %begin
  Date       = 1990-01-22~1990-01-26
  Text       = "usenix Conference"
  Text       = "Washington, DC"

 %begin
  Date       = 1990-04-13
  Text       = "Good Friday"

 %begin
  Date       = 1990-05-29
  Time       = 09:30~11:00
  Text       = "Department Meeting"
  Text       = "Room 7215"
%endselect

%select {telephone}
 %begin
  Name       = "John Q. Public"
  Tel Number = "(416) 555-1212"
  Address    = "100 Yonge St."
  Address    = "Toronto, Ont."
  Address    = "M9R 3W1"
  Free 1     = "fax: (416) 555-1212"
%endselect
@//E*O*F doc/grammar.spec//
chmod u=rw,g=r,o=r doc/grammar.spec
 
echo mkdir - cfg
mkdir cfg
chmod u=rwx,g=rx,o=rx cfg
 
echo x - cfg/boss.c
sed 's/^@//' > "cfg/boss.c" <<'@//E*O*F cfg/boss.c//'
/*
  boss.c - read/write data from a Casio BOSS SF-9000
	   Mark Dapoz   1990/06/25
	     mdapoz@hybrid.UUCP  or  
	     mdapoz%hybrid@cs.toronto.edu
*/

#include <stdio.h>
#include "boss.h"

#define	VERSION "Version 1.0  1990/06/25"

#ifndef DEVICE
#define	DEVICE	"/dev/tty000"	/* Device BOSS is connected to */
#endif

#define	UNKNOWN	0		/* modes this program can operate in */
#define	SEND	1
#define	RECEIVE	2
#define	TEST	3

    static char long_entry[] = "Very Long Entry\r\r123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*123456789*\rLast";

main(argc, argv)
int argc;
char **argv;
{
    struct boss_packet *boss_data;	/* linked list of raw packets */
    struct telephone *tele_ptr;		/* linked list of telephone entries */
    struct bus_card *bcard_ptr;		/* linked list of bus card entries */
    struct memo *memo_ptr;		/* linked list of memo entries */
    struct schedule *sched_ptr;		/* linked list of schedule entries */
    struct calendar *cal_ptr;		/* linked list of calendar entries */
    int boss_fd;	/* file descriptor for reading and writting from BOSS */
    int mode=UNKNOWN;			/* mode program is operating in */
    int opterr=0;			/* errors in command line options */
    int debug=0;			/* debug mode control */
    int c;
    FILE *infile;
    extern int optind;			/* getopt() params */
    extern char *optarg;

    struct boss_packet *build_debug_packet_stream();
    struct boss_packet *interpret_stream();

    while ((c=getopt(argc, argv, "ds:r")) != EOF)
	switch(c) {
	    case 'd':			/* debug mode control */
			debug=1;
			break;
	    case 's':			/* send a file to the BOSS */
			if (mode == UNKNOWN) {
			    mode=SEND;
			    if ((infile=fopen(optarg, "r")) < 0) {
				perror("error opening input file");
				exit(1);
			    }
			} else
			    opterr++;
			break;
	    case 'r':			/* receive a file from the BOSS */
			if (mode == UNKNOWN)
			    mode=RECEIVE;
			else
			    opterr++;
			break;
	    case '?':
			opterr++;
			break;
	}
    if (opterr) {
	fprintf(stderr, "usage: %s [-s foo] or [-r]\n", argv[0]);
	exit(1);
    }
    fprintf(stderr, "Casio BOSS communications, %s\n", VERSION);
    fprintf(stderr, "Opening BOSS on device %s\n", DEVICE);
    if ((boss_fd=boss_open(DEVICE)) < 0) /* Open connection to BOSS */
	exit(1);
    switch(mode) {
	case UNKNOWN:
	case RECEIVE:
		fprintf(stderr, "Reading from BOSS\n");
				/* read data, exit on error */
		if ((boss_data=boss_read(boss_fd)) == NULL) {
		    fprintf(stderr, "Closing BOSS (aborted)\n");
		    close(boss_fd);
		    exit(1);
		}
		if (debug) /* dump the packet stream structure? */
		    print_packet_stream(boss_data); 
		if ((tele_ptr=extract_tele(boss_data)) != NULL)
		    print_tele(tele_ptr);
		if ((bcard_ptr=extract_bcard(boss_data)) != NULL)
		    print_bcard(bcard_ptr);
		if ((memo_ptr=extract_memo(boss_data)) != NULL)
		    print_memo(memo_ptr);
		if ((sched_ptr=extract_sched(boss_data)) != NULL)
		    print_sched(sched_ptr);
		if ((cal_ptr=extract_cal(boss_data)) != NULL)
		    print_cal(cal_ptr);
		break;
	case SEND:
		/*
		boss_data=build_debug_packet_stream();
		*/
		if ((boss_data=interpret_stream(infile)) != NULL) {
    		    if (debug) /* dump the packet stream structure? */
			print_packet_stream(boss_data);
		    fprintf(stderr, "Put BOSS in receive mode ");
		    fprintf(stderr, "and press return when ready\n");
		    getchar();
		    fprintf(stderr, "Writting to BOSS\n");
			/* write data, exit on error */
		    if (boss_write(boss_fd, boss_data)) { 
		        fprintf(stderr, "Closing BOSS (aborted)\n");
		        close(boss_fd);
		        exit(1);
		    }
		    break;
		}
    }
    fprintf(stderr, "Closing BOSS\n");
    boss_close(boss_fd);
}

print_field(prefix, data, quote)
char *prefix;
unsigned char *data;
char *quote;
{
    if (data == NULL)	/* check for things to print first */
	return;
    if (*data == '\n' || *data == 0)
	return;
    for (printf("%s%s", prefix, quote); *data; data++)
	if (*data == '\r')
	    printf("%s\n%s%s", quote, prefix, quote);
	else
	    if (*data < ' ' || *data > '~') /* printable? */
	    	printf("\\%03o", *data);
	    else
	    	printf("%c", *data);
    printf("%s\n", quote);
}

print_tele(tele_ptr)
struct telephone *tele_ptr;
{
    printf("\n%%select {telephone}\n\n");
    do {
	printf("%%begin\n");
	print_field("Name       = ", tele_ptr->name, "\"");
	print_field("Tel Number = ", tele_ptr->tel_number, "\"");
	print_field("Address    = ", tele_ptr->addr, "\"");
	print_field("Free 1     = ", tele_ptr->free_1, "\"");
	print_field("Free 2     = ", tele_ptr->free_2, "\"");
	print_field("Free 3     = ", tele_ptr->free_3, "\"");
	print_field("Free 4     = ", tele_ptr->free_4, "\"");
	print_field("Free 5     = ", tele_ptr->free_5, "\"");
	print_field("Free 6     = ", tele_ptr->free_6, "\"");
	if (tele_ptr->marked)
	    printf("marked     = true\n");
	printf("\n");
	tele_ptr=tele_ptr->next;
    } while (tele_ptr != NULL);
    printf("%%endselect\n");
}

print_bcard(bcard_ptr)
struct bus_card *bcard_ptr;
{
    printf("\n%%select {business card}\n\n");
    do {
	printf("%%begin\n");
	print_field("Employer   = ", bcard_ptr->employer, "\"");
	print_field("Name       = ", bcard_ptr->name, "\"");
	print_field("Tel Number = ", bcard_ptr->tel_number, "\"");
	print_field("Position   = ", bcard_ptr->position, "\"");
	print_field("Department = ", bcard_ptr->dept, "\"");
	print_field("P.O. Box   = ", bcard_ptr->po_box, "\"");
	print_field("Address    = ", bcard_ptr->addr, "\"");
	print_field("Telex      = ", bcard_ptr->telex, "\"");
	print_field("Fax Number = ", bcard_ptr->fax_number, "\"");
	print_field("Free 1     = ", bcard_ptr->free_1, "\"");
	print_field("Free 2     = ", bcard_ptr->free_2, "\"");
	print_field("Free 3     = ", bcard_ptr->free_3, "\"");
	print_field("Free 4     = ", bcard_ptr->free_4, "\"");
	print_field("Free 5     = ", bcard_ptr->free_5, "\"");
	if (bcard_ptr->marked)
	    printf("marked     = true\n");
	printf("\n");
	bcard_ptr=bcard_ptr->next;
    } while (bcard_ptr != NULL);
    printf("%%endselect\n");
}

print_memo(memo_ptr)
struct memo *memo_ptr;
{
    printf("\n%%select {memo}\n\n");
    do {
	printf("%%begin\n");
	print_field("Text       = ", memo_ptr->text, "\"");
	if (memo_ptr->marked)
	    printf("marked     = true\n");
	printf("\n");
	memo_ptr=memo_ptr->next;
    } while (memo_ptr != NULL);
    printf("%%endselect\n");
}

print_sched(sched_ptr)
struct schedule *sched_ptr;
{
    printf("\n%%select {schedule}\n\n");
    do {
	printf("%%begin\n");
	print_field("Date       = ", sched_ptr->date, "");
	print_field("Time       = ", sched_ptr->time, "");
	print_field("Alarm Time = ", sched_ptr->alarm, "");
	print_field("Text       = ", sched_ptr->text, "\"");
	if (sched_ptr->marked)
	    printf("marked     = true\n");
	printf("\n");
	sched_ptr=sched_ptr->next;
    } while (sched_ptr != NULL);
    printf("%%endselect\n");
}

print_cal(cal_ptr)
struct calendar *cal_ptr;
{
    int i;

    printf("\n%%select {calendar}\n\n");
    do {
	printf("%%begin\n");
	print_field("Month      = ", cal_ptr->month, "");
	printf("Marked Days= ");
	for (i=0; i<32; i++)
	    if ((cal_ptr->days[3-i/8] >> (i-(i/8)*8)) & 0x01)
		printf("%d ", i+1);
	printf("\n\n");
	cal_ptr=cal_ptr->next;
    } while (cal_ptr != NULL);
    printf("%%endselect\n");
}

print_packet_stream(chain_ptr)
struct boss_packet *chain_ptr;
{
    int packno = 1;
    int i;

    do {
	printf("\nPacket %03d:  Size=%02xh  Header= %02xh %02xh %02xh\n",
	    packno, chain_ptr->size, chain_ptr->header.target,
	    chain_ptr->header.position, chain_ptr->header.control);
	if (chain_ptr->size) { /* print any data received */
	    printf("Data=");
			    		/* text data? */
	    if (((chain_ptr->header.target)&0xf0) == TARG_TEXT ||
	    	(chain_ptr->header.target) == TARG_TIME ||
	    	(chain_ptr->header.target) == TARG_ALARM ||
	    	(chain_ptr->header.target) == TARG_DATE)
		printf("%s\n", chain_ptr->data);
	    else {
		for (i=0; i < chain_ptr->size; i++)
		    printf("%02xh ", chain_ptr->data[i]);
		printf("\n");
	    }
	}
	packno++;
	chain_ptr=chain_ptr->next;
    } while (chain_ptr != NULL);
}

struct boss_packet *build_debug_packet_stream()
{
    struct boss_packet *boss_data;	/* linked list of raw packets */
    struct telephone *tele_ptr;		/* linked list of telephone entries */
    struct bus_card *bcard_ptr;		/* linked list of bus card entries */
    struct memo *memo_ptr;		/* linked list of memo entries */
    struct schedule *sched_ptr;		/* linked list of schedule entries */
    struct calendar *cal_ptr;		/* linked list of calendar entries */

    tele_ptr=(struct telephone *)malloc(sizeof(struct telephone));
    tele_ptr->name = "J. Q. Public\n";
    tele_ptr->tel_number = "(416) 555-1212\n";
    tele_ptr->addr = "100 Gerrard St. E.\rToronto, Ontario\n";
    tele_ptr->free_1 = "bus: (416) 555-1212 x666";
    tele_ptr->free_2 = NULL;
    tele_ptr->free_3 = NULL;
    tele_ptr->free_4 = NULL;
    tele_ptr->free_5 = NULL;
    tele_ptr->free_6 = NULL;
    tele_ptr->marked = 1;
    tele_ptr->next= NULL;
    boss_data=add_tele(tele_ptr, NULL);

    memo_ptr=(struct memo *)malloc(sizeof(struct memo));
    memo_ptr->text = long_entry;
    memo_ptr->marked = 1;
    memo_ptr->next= NULL;
    add_memo(memo_ptr, boss_data);

    bcard_ptr=(struct bus_card *)malloc(sizeof(struct bus_card));
    bcard_ptr->employer = "ABC Company\n";
    bcard_ptr->name = "John Q Smith\n";
    bcard_ptr->tel_number = "(416) 555-1212\n";
    bcard_ptr->position = "Systems Programmer\n";
    bcard_ptr->dept = "62/325\n";
    bcard_ptr->po_box = "Box 125\n";
    bcard_ptr->addr = "100 Gerrard St. E.\rToronto, Ontario\n";
    bcard_ptr->telex = "123456\n";
    bcard_ptr->fax_number = "(000) 123-4567";
    bcard_ptr->free_1 = NULL;
    bcard_ptr->free_2 = NULL;
    bcard_ptr->free_3 = NULL;
    bcard_ptr->free_4 = NULL;
    bcard_ptr->free_5 = NULL;
    bcard_ptr->marked = 1;
    bcard_ptr->next= NULL;
    add_bcard(bcard_ptr, boss_data);

    sched_ptr=(struct schedule *)malloc(sizeof(struct schedule));
    sched_ptr->date = "1990-01-22~1990-01-24";
    sched_ptr->time = NULL;
    sched_ptr->alarm = NULL;
    sched_ptr->text = "Test Conference\rToronto, Ontario";
    sched_ptr->marked = 1;
    sched_ptr->next= NULL;
    add_sched(sched_ptr, boss_data);

    sched_ptr->date = "1990-02-21";
    sched_ptr->time = "10:30~11:30";
    sched_ptr->alarm = "10:15";
    sched_ptr->text = "Test Meeting\rToronto, Ontario";
    sched_ptr->marked = 0;
    sched_ptr->next= NULL;
    add_sched(sched_ptr, boss_data);

    cal_ptr=(struct calendar *)malloc(sizeof(struct calendar));
    cal_ptr->month = "1990-01-01";
    cal_ptr->days[0] = 0x00;
    cal_ptr->days[1] = 0x02; /* 18'th */
    cal_ptr->days[2] = 0x00;
    cal_ptr->days[3] = 0xff; /* 1'st - 8'th marked */
    add_cal(cal_ptr, boss_data);

    add_end_tx(boss_data);
    return(boss_data);
}
@//E*O*F cfg/boss.c//
chmod u=rw,g=r,o=r cfg/boss.c
 
echo x - cfg/boss_cfg.y
sed 's/^@//' > "cfg/boss_cfg.y" <<'@//E*O*F cfg/boss_cfg.y//'
%{

/*
   boss_cfg.y - yacc grammar and actions for input data for Casio BOSS
		   Mark Dapoz   1990/06/25
		     mdapoz@hybrid.UUCP  or  
		     mdapoz%hybrid@cs.toronto.edu
*/

#include <stdio.h>
#include "boss.h"

#ifdef DEBUG_YACC	/* print debug info? */
#ifndef dprintf
#define	dprintf(x)	printf(x)
#endif
#ifndef dprintf1
#define	dprintf1(x,y)	printf(x,y)
#endif
#ifndef dprintf2
#define	dprintf2(x,y,z)	printf(x,y,z)
#endif
#else
#ifndef dprintf
#define	dprintf(x)
#endif
#ifndef dprintf1
#define	dprintf1(x,y)
#endif
#ifndef dprintf2
#define	dprintf2(x,y,z)
#endif
#endif

extern FILE *yyin; /* lex stdin file */

char *strput(); /* put a string somewhere in memory */

int  linenum=0; /* current line number of input file */
int  inrec=0; /* marker to indicate processing a record */
int  rectype=0; /* type of record being built */
int  recline=0; /* line number in file current record starts at */
int  abort=0; /* severe error encountered? */
char msgbuf[256]; /* message buffer */

struct boss_packet *boss_stream=NULL; /* BOSS packet stream */
struct telephone *tele_llist=NULL; /* linked list of telephone entries */
struct memo *memo_llist=NULL;	   /* linked list of memo entries */
struct bus_card *bcard_llist=NULL; /* linked list of business card entries */
struct schedule *sched_llist=NULL; /* linked list of schedule entries */
struct calendar *cal_llist=NULL;   /* linked list of calendar entries */
			/* temporary buffers for strings */
char t_name[MAX_BOSS_REC+1]; 
char t_empl[MAX_BOSS_REC+1]; 
char t_tel_num[MAX_BOSS_REC+1]; 
char t_pos[MAX_BOSS_REC+1]; 
char t_dept[MAX_BOSS_REC+1]; 
char t_po_box[MAX_BOSS_REC+1]; 
char t_addr[MAX_BOSS_REC+1]; 
char t_telex[MAX_BOSS_REC+1]; 
char t_fax[MAX_BOSS_REC+1]; 
char t_free[6][MAX_BOSS_REC+1]; 
char t_text[MAX_BOSS_REC+1]; 
char t_date[4+3+3+1+4+3+3+1]; /* date range spec yyyy-mm-dd~yyyy-mm-dd */
char t_time[2+1+2+1+2+1+2+1]; /* time range spec hh:mm~hh:mm */
char t_alarm[2+1+2+1]; /* alarm spec hh:mm */
char t_month[4+1+2+1+2+1]; /* month spec yyyy-mm-01 */
long t_mdays; /* marked days */
int  marked;	/* marked record? */
%}

%union
{
    int  val;		/* general interger return value */
    long lval;		/* general long return value */
    char string[MAX_BOSS_REC+1]; /* string data return value */
}
%token <string> STRING
%token <string> DATE_SPEC TIME_SPEC
%token <val> NUMBER
%token BEGIN_REC ENDSEL SELECT 
%token <val> TRUE FALSE
%token <val> UNKNOWN
%token <val> MEMO TELE SCHED BCARD CALENDAR
%token <val> MARKED NAME TEL_NUMBER ADDRESS FREE EMPLOYER POSITION DEPT 
%token <val> TELEX FAX PO_BOX TEXT DATE TIME ALARM_TIME MONTH MARKED_DAYS
%type <val> target record_field boolean
%type <lval> numbers

%%
lines:	/* empty */
	| lines line '\n'
	| lines error '\n'
	{ 
	    yyerrok;
	}
	;

line:	/* empty */
	{ 
	    linenum++;
	}

	/* the following production rules are for valid assignment operations */

	| MARKED '=' boolean	/* marked record assignment */
	{
	    linenum++;
	    if (valid_assign_context($1))
	    	if ($3 == TRUE) {
		    dprintf("Marked record\n");
		    marked=1;
	    	}
	}
	| record_field '=' STRING /* general string assignment operations */
	{
	    linenum++;
	    if (valid_assign_context($1)) {
	    	switch($1) {
		    case NAME:
			dprintf1("Assigned Name = %s\n", $3);
			stradd(t_name, $3);
			break;
		    case TEL_NUMBER:
	    		dprintf1("Assigned Tel Number = %s\n", $3);
			stradd(t_tel_num, $3);
			break;
		    case ADDRESS:
	    		dprintf1("Assigned Address = %s\n", $3);
			stradd(t_addr, $3);
			break;
		    case EMPLOYER:
	    		dprintf1("Assigned Employer = %s\n", $3);
			stradd(t_empl, $3);
			break;
		    case POSITION:
	    		dprintf1("Assigned Position = %s\n", $3);
			stradd(t_pos, $3);
			break;
		    case DEPT:
	    		dprintf1("Assigned Department = %s\n", $3);
			stradd(t_dept, $3);
			break;
		    case PO_BOX:
	    		dprintf1("Assigned P.O. Box = %s\n", $3);
			stradd(t_po_box, $3);
			break;
		    case TELEX:
	    		dprintf1("Assigned Telex Number= %s\n", $3);
			stradd(t_telex, $3);
			break;
		    case FAX:
	    		dprintf1("Assigned Fax Number = %s\n", $3);
			stradd(t_fax, $3);
			break;
		    case TEXT:
	    		dprintf1("Assigned Text = %s\n", $3);
			stradd(t_text, $3);
			break;
	    	}
	    }
	}
	| FREE NUMBER '=' STRING /* free field assignment structure */
	{
	    linenum++;
	    if (valid_assign_context($1)) {
	    	if ($2 < 1 || $2 > 6) {
		    error("free field out of range", linenum);
	    	} else {
		    dprintf2("Assigned Free %d = %s\n", $2, $4);
		    stradd(t_free[$2-1], $4);
		}
	    }
	}
	| MONTH '=' DATE_SPEC /* assignment of target month */
	{
	    linenum++;
	    if (valid_assign_context($1)) {
		if (verify_month($3)) {
	    	    dprintf1("Assigned Month = %s\n", $3);
		    strcpy(t_month, $3);
		}
	    }
	}
	| DATE '=' DATE_SPEC /* a single date specification */
	{
	    linenum++;
	    if (valid_assign_context($1)) {
		if (verify_date($3)) {
	    	    dprintf1("Single Date = %s\n", $3);
		    strcpy(t_date, $3);
		}
	    }
	}
	| DATE '=' DATE_SPEC '~' DATE_SPEC /* a date range specification */
	{
	    linenum++;
	    if (valid_assign_context($1)) {
		if (verify_date($3) && verify_date($5)) {
	    	    dprintf2("Range Date = %s to %s\n", $3, $5);
		    sprintf(t_date, "%s~%s", $3, $5);
		}
	    }
	}
	| TIME '=' TIME_SPEC /* a single time specification */
	{
	    linenum++;
	    if (valid_assign_context($1)) {
		if (verify_time($3)) {
	    	    dprintf1("Single Time = %s\n", $3);
		    strcpy(t_time, $3);
		}
	    }
	}
	| TIME '=' TIME_SPEC '~' TIME_SPEC /* a time range specification */
	{
	    linenum++;
	    if (valid_assign_context($1)) {
		if (verify_time($3) && verify_time($5)) {
	    	    dprintf2("Range Time = %s to %s\n", $3, $5);
		    sprintf(t_time, "%s~%s", $3, $5);
		}
	    }
	}
	| ALARM_TIME '=' TIME_SPEC /* a single alarm time specification */
	{
	    linenum++;
	    if (valid_assign_context($1)) {
		if (verify_time($3)) {
	    	    dprintf1("Single Alarm Time = %s\n", $3);
		    strcpy(t_alarm, $3);
		}
	    }
	}
	| MARKED_DAYS '=' numbers /* assignment of marked days in a calendar */
	{
	    linenum++;
	    if (valid_assign_context($1)) {
		t_mdays=t_mdays | $3;
#ifdef DEBUG_YACC
	    	dprintf("Assigned Marked Days =");
	    	{
	    	int i;
	    	for (i=0; i < 31; i++)
		    if ($3 & (1<<i))
		    	dprintf1("%d ", i+1);
	    	dprintf("\n");
	    	}
#endif
	    }
	}

	/* the following production rules are for valid control operations */

	| SELECT '{' target '}' /* select the destination area of the BOSS */
	{
	    linenum++;
	    if (inrec)
		build_boss_rec(); /* build the appropriate boss record */
	    rectype=$3; /* mark record being built */
	    inrec=0;
	    switch($3) {
		case MEMO:
			dprintf("Selecting memo\n");
			break;
		case TELE:
			dprintf("Selecting telephone\n");
			break;
		case SCHED:
			dprintf("Selecting schedule\n");
			break;
		case BCARD:
			dprintf("Selecting business card\n");
			break;
		case CALENDAR:
			dprintf("Selecting calendar\n");
			break;
	    }
	}
	| BEGIN_REC /* indicates the start of a record */
	{
	    linenum++;
	    dprintf("Beginning record\n");
	    if (inrec)
		build_boss_rec(); /* build the appropriate boss record */
	    else
		inrec=1;
	    recline=linenum;
	    clear_temp(); /* clear temporary variables */
	}
	| ENDSEL /* indicates the end of a target area */
	{
	    linenum++;
	    dprintf("Ending\n");
	    if (inrec)
		build_boss_rec(); /* build the appropriate boss record */
	    inrec=rectype=0;
	}
	;

	/* valid target locations for a %select operation */

target:		  MEMO		/* memo pad */
		| TELE		/* telephone directory */
		| SCHED		/* schedule keeper */
		| BCARD		/* business cards */
		| CALENDAR	/* personal calendar */
		;

	/* valid values for a lhs of a string assignment */

record_field:	  NAME
		| TEL_NUMBER
		| ADDRESS
		| EMPLOYER
		| POSITION
		| DEPT
		| PO_BOX
		| TELEX
		| FAX
		| TEXT
		;

	/* general boolean operator */

boolean:	  TRUE
		| FALSE
		;

	/* accept a list of numbers between 1 and 31 and return a bitfield
	   containing 32 bits indicating which numbers occurred */

numbers:	  numbers NUMBER
		{
		    if ( $2 < 1 || $2 > 31) { /* check for valid bounds */
		        sprintf(msgbuf, "Invalid day (%d) specified in Marked Days, ignoring", $2);
			warning(msgbuf, linenum+1);
		    } else
			$$ = $1 | 1<<($2-1); /* set bitfield on */
		}
		| NUMBER
		{
		    if ( $1 < 1 || $1 > 31) { /* check for valid bounds */
		        sprintf(msgbuf, "Invalid day (%d) specified in Marked Days, ignoring", $1);
			warning(msgbuf, linenum+1);
		    } else
			$$ = 1<<($1-1); /* set bitfield on */
		}
		;
%%

struct boss_packet *interpret_stream(file)
FILE *file;
{
    yyin=file; /* assign input file */
    yyparse();
    if (!abort) {
	dprintf("Reliable BOSS stream provided\n");
	return(boss_stream);
    } else {
	dprintf("Error encountered in producing BOSS stream\n");
	return(NULL);
    }
}

/* general purpose error routine for yacc parser */
yyerror(s)
char *s;
{
    error(s, ++linenum);
}

/* general purpose clean up routine when end of input is detected */
yywrap()
{
    if(abort) {
	fprintf(stderr, "Aborting due to %d severe error(s) encountered\n",
	    abort);
    } else {
	if (inrec)
	    build_boss_rec(); /* build the appropriate boss record */
	if (tele_llist != NULL)
	    boss_stream=add_tele(tele_llist, boss_stream);
	if (memo_llist != NULL)
	    boss_stream=add_memo(memo_llist, boss_stream);
	if (bcard_llist != NULL)
	    boss_stream=add_bcard(bcard_llist, boss_stream);
	if (sched_llist != NULL)
	    boss_stream=add_sched(sched_llist, boss_stream);
	if (cal_llist != NULL)
	    boss_stream=add_cal(cal_llist, boss_stream);
	add_end_tx(boss_stream); /* close off data stream */
    }
    return(1);
}

/* clear temporary variables used in building record */
clear_temp()
{
    t_name[0]='\0'; 	
    t_empl[0]='\0'; 
    t_tel_num[0]='\0'; 
    t_pos[0]='\0'; 
    t_dept[0]='\0'; 
    t_po_box[0]='\0'; 
    t_addr[0]='\0'; 
    t_telex[0]='\0'; 
    t_fax[0]='\0'; 
    t_free[0][0]='\0'; 
    t_free[1][0]='\0'; 
    t_free[2][0]='\0'; 
    t_free[3][0]='\0'; 
    t_free[4][0]='\0'; 
    t_free[5][0]='\0'; 
    t_text[0]='\0'; 
    t_date[0]='\0';
    t_time[0]='\0';
    t_alarm[0]='\0';
    t_month[0]='\0';
    t_mdays=marked=0;
}

/* build the appropriate boss record */
build_boss_rec()
{
    switch(rectype) {
	case MEMO:		/* memo pad */
		dprintf("Processing BOSS record: memo\n");
		add_memo_rec();
		break;
	case TELE:		/* telephone directory */
		dprintf("Processing BOSS record: telephone\n");
		add_tele_rec();
		break;
	case SCHED:		/* schedule keeper */
		dprintf("Processing BOSS record: schedule\n");
		add_sched_rec();
		break;
	case BCARD:		/* business cards */
		dprintf("Processing BOSS record: business card\n");
		add_bcard_rec();
		break;
	case CALENDAR:		/* personal calendar */
		dprintf("Processing BOSS record: calendar\n");
		add_cal_rec();
		break;
	default:
		error("illegal sequence, %select must preceed %record",linenum);
		break;
    }
}

/* add a calendar record to the linked list of calendar entries */
add_cal_rec()
{
    struct calendar *cal_rec;
    char *fill_field();

    if (!strlen(t_month)) { /* date field is mandatory */
	sprintf(msgbuf, "Date not specified for calendar record starting at line %d",
		recline);
	error(msgbuf, linenum);
	return;
    }
    if (!(cal_rec=(struct calendar *)malloc(sizeof(struct calendar)))) {
	error("malloc failed", linenum);
	return;
    }
    cal_rec->month=strput(t_month); /* copy date */
			/* add marked days */
    cal_rec->days[0]=(t_mdays>>(8*3))&0xff;
    cal_rec->days[1]=(t_mdays>>(8*2))&0xff;
    cal_rec->days[2]=(t_mdays>>(8*1))&0xff;
    cal_rec->days[3]=(t_mdays>>(8*0))&0xff;
    			/* clear out last \n field separator */
    *(cal_rec->month+strlen(cal_rec->month)-1)='\0'; 
    cal_rec->next=cal_llist; /* insert at head of linked list */
    cal_llist=cal_rec;
}

/* add a schedule record to the linked list of schedule entries */
add_sched_rec()
{
    struct schedule *sched_rec;
    char *last=NULL; /* last field to be filled in */
    char *fill_field();

    if (!strlen(t_date)) { /* date field is mandatory */
	sprintf(msgbuf, "Date not specified for schedule record starting at line %d",
		recline);
	error(msgbuf, linenum);
	return;
    }
    if (!(sched_rec=(struct schedule *)malloc(sizeof(struct schedule)))) {
	error("malloc failed", linenum);
	return;
    }
    sched_rec->date=strput(t_date); /* copy date */
			/* description provided? */
    sched_rec->text=fill_field(t_text, &last);
			/* alarm provided? */
    sched_rec->alarm=fill_field(t_alarm, &last);
			/* time provided? */
    sched_rec->time=fill_field(t_time, &last);
    if (last == NULL) /* catch no optional field case */
	last=sched_rec->date;
    *(last+strlen(last)-1)='\0'; /* clear out last \n field separator */
    sched_rec->marked=marked;
    sched_rec->next=sched_llist; /* insert at head of linked list */
    sched_llist=sched_rec;
}

/* add a telephone record to the linked list of telephone entries */
add_tele_rec()
{
    struct telephone *tele_rec;
    char *last=NULL; /* last field to be filled in */
    char *fill_field();

    if (!strlen(t_name)) { /* name field is mandatory */
	sprintf(msgbuf, "Name not specified for telephone record starting at line %d",
		recline);
	error(msgbuf, linenum);
	return;
    }
    if (!(tele_rec=(struct telephone *)malloc(sizeof(struct telephone)))) {
	error("malloc failed", linenum);
	return;
    }
    tele_rec->name=strput(t_name); /* copy name */
			/* free text fields provided? */
    tele_rec->free_6=fill_field(t_free[5], &last);
    tele_rec->free_5=fill_field(t_free[4], &last);
    tele_rec->free_4=fill_field(t_free[3], &last);
    tele_rec->free_3=fill_field(t_free[2], &last);
    tele_rec->free_2=fill_field(t_free[1], &last);
    tele_rec->free_1=fill_field(t_free[0], &last);
			/* address provided? */
    tele_rec->addr=fill_field(t_addr, &last);
			/* telephone number provided? */
    tele_rec->tel_number=fill_field(t_tel_num, &last);
    if (last == NULL) /* catch no optional field case */
	last=tele_rec->name;
    *(last+strlen(last)-1)='\0'; /* clear out last \n field separator */
    tele_rec->marked=marked;
    tele_rec->next=tele_llist; /* insert at head of linked list */
    tele_llist=tele_rec;
}

/* add a memo record to the linked list of memo entries */
add_memo_rec()
{
    struct memo *memo_rec;
    char *last=NULL; /* last field to be filled in */
    char *fill_field();

    if (!strlen(t_text)) /* any text supplied? */
	return;
    if (!(memo_rec=(struct memo *)malloc(sizeof(struct memo)))) {
	error("malloc failed", linenum);
	return;
    }
    last=memo_rec->text=strput(t_text); /* copy name */
    *(last+strlen(last)-1)='\0'; /* clear out last \n field separator */
    memo_rec->marked=marked;
    memo_rec->next=memo_llist; /* insert at head of linked list */
    memo_llist=memo_rec;
}

/* add a business card record to the linked list of business card entries */
add_bcard_rec()
{
    struct bus_card *bcard_rec;
    char *last=NULL; /* last field to be filled in */
    char *fill_field();

    if (!strlen(t_name)) { /* name field is mandatory */
	sprintf(msgbuf, "Name not specified for business card record starting at line %d",
		recline);
	error(msgbuf, linenum);
	return;
    }
    if (!strlen(t_empl)) { /* employer field is mandatory */
	sprintf(msgbuf, "Employer not specified for business card record starting at line %d",
		recline);
	error(msgbuf, linenum);
	return;
    }
    if (!(bcard_rec=(struct bus_card *)malloc(sizeof(struct bus_card)))) {
	error("malloc failed", linenum);
	return;
    }
    bcard_rec->name=strput(t_name); /* copy name */
    bcard_rec->employer=strput(t_empl); /* copy employer */
			/* free text fields provided? */
    bcard_rec->free_5=fill_field(t_free[4], &last);
    bcard_rec->free_4=fill_field(t_free[3], &last);
    bcard_rec->free_3=fill_field(t_free[2], &last);
    bcard_rec->free_2=fill_field(t_free[1], &last);
    bcard_rec->free_1=fill_field(t_free[0], &last);
			/* fax number provided? */
    bcard_rec->fax_number=fill_field(t_fax, &last);
			/* telex number provided? */
    bcard_rec->telex=fill_field(t_telex, &last);
			/* address provided? */
    bcard_rec->addr=fill_field(t_addr, &last);
			/* P.O. Box provided? */
    bcard_rec->po_box=fill_field(t_po_box, &last);
			/* department provided? */
    bcard_rec->dept=fill_field(t_dept, &last);
			/* position provided? */
    bcard_rec->position=fill_field(t_pos, &last);
			/* telephone number provided? */
    bcard_rec->tel_number=fill_field(t_tel_num, &last);
    if (last == NULL) /* catch no optional field case */
	last=bcard_rec->name;
    *(last+strlen(last)-1)='\0'; /* clear out last \n field separator */
    bcard_rec->marked=marked;
    bcard_rec->next=bcard_llist; /* insert at head of linked list */
    bcard_llist=bcard_rec;
}

/* print a severe error message and set global flags */
error(s, l)
char *s;
int l;
{
    fprintf(stderr, "line %d: %s\n", l, s);
    abort++;
}

/* print a warning message */
warning(s, l)
char *s;
int l;
{
    fprintf(stderr, "Warning line %d: %s\n", l, s);
}

/* add a string to the temp buffers with length checking */
stradd(to, from)
char *to, *from;
{
    if (strlen(to) + strlen(from) + 1 > MAX_BOSS_REC) {
	error("data exceeds maximum BOSS record size", linenum);
    } else {
	if (strlen(to))
	    strcat(to, "\r"); /* advance to next line */
	strcat(to, from);
    }
}

/* check if the time specified is within the valid BOSS time spec */
verify_time(time)
char *time;
{
    char temp_time[2+1+2+1]; /* time spec hh:mm */
    int estat=1;

    strcpy(temp_time, time); /* make a working copy */
    temp_time[2]='\0'; /* break up string into components */
		/* check for correct hour range */
    if (atoi(temp_time) > 23) {
	error("Time specified outside of 00-23 range for hours", linenum);
	estat=0; /* indicate error */
    }
		/* check for correct minute range */
    if (atoi(temp_time+3) > 59) {
	error("Time specified outside of 00-59 range for minutes", linenum);
	estat=0; /* indicate error */
    }
    return(estat);
}

/* check if the date specified is within the valid BOSS dates for a month */
verify_month(month)
char *month;
{
    char temp_month[4+1+2+1+2+1]; /* month spec yyyy-mm-01 */
    int estat=1;

    strcpy(temp_month, month); /* make a working copy */
    temp_month[4]=temp_month[7]='\0'; /* break up string into components */
		/* check for correct year range */
    if (atoi(temp_month) < 1901 || atoi(temp_month) > 2099) {
	error("Year specified outside of 1901-2099 range", linenum);
	estat=0; /* indicate error */
    }
		/* check for correct month range */
    if (atoi(temp_month+5) < 1 || atoi(temp_month+5) > 12) {
	error("Month specified outside of 01-12 range", linenum);
	estat=0; /* indicate error */
    }
		/* check for correct day specification */
    if (atoi(temp_month+8) != 1) {
	error("Month specification must occur on first day of month", linenum);
	estat=0; /* indicate error */
    }
    return(estat);
}

/* check if the date specified is within the valid BOSS dates */
verify_date(date)
char *date;
{
    char temp_date[4+1+2+1+2+1]; /* date spec yyyy-mm-dd */
    int estat=1;
    int day;

    strcpy(temp_date, date); /* make a working copy */
    temp_date[4]=temp_date[7]='\0'; /* break up string into components */
		/* check for correct year range */
    if (atoi(temp_date) < 1901 || atoi(temp_date) > 2099) {
	error("Year specified outside of 1901-2099 range", linenum);
	estat=0; /* indicate error */
    }
		/* check for correct month range */
    if (atoi(temp_date+5) < 1 || atoi(temp_date+5) > 12) {
	error("Month specified outside of 01-12 range", linenum);
	estat=0; /* indicate error */
    }
		/* check for correct day specification */
    if ((day=atoi(temp_date+8)) < 1 || atoi(temp_date+8) > 31) {
	error("Day specified outside of 01-31 range", linenum);
	estat=0; /* indicate error */
    } else {
	switch(atoi(temp_date+5)) { /* check individual dates for months */
	    case 2: /* feb */
		if (day > 29) {
		    error("day specified beyond end of month, only 28 days", linenum);
		    estat=0;
		}
		if (day == 29)
		    warning("leap year specified but no verification performed", linenum);
		break;
	    case 4: /* apr */
	    case 6: /* june */
	    case 9: /* sept */
	    case 11: /* nov */
		if (day > 30) {
		    error("day specified beyond end of month, only 30 days", linenum);
		    estat=0;
		}
		break;
	    case 1: /* jan */
	    case 3: /* mar */
	    case 5: /* may */
	    case 7: /* july */
	    case 8: /* aug */
	    case 10: /* oct */
	    case 12: /* dec */
	    default:
		break; /* already checked for beyond 31 days */
	}
    }
    return(estat);
}

/* check if we're in a proper state for the assignment statement */
valid_assign_context(assign_type)
int assign_type;
{
    if (inrec) {
	switch(rectype) {
	    case MEMO:
		switch(assign_type) {
	    	    case TEXT: /* valid type of assignments */
		    case MARKED:
			break;
		    default:
			error("Assignment not valid for memo records",linenum);
			return(0);
		}
		break;
	    case TELE:
		switch(assign_type) {
		    case NAME: /* valid type of assignments */
	    	    case TEL_NUMBER:
	    	    case ADDRESS:
	    	    case FREE:
		    case MARKED:
			break;
		    default:
			error("Assignment not valid for telephone records",linenum);
			return(0);
		}
		break;
	    case SCHED:
		switch(assign_type) {
	    	    case DATE: /* valid type of assignments */
	    	    case TIME:
	    	    case ALARM_TIME:
	    	    case TEXT:
		    case MARKED:
			break;
		    default:
			error("Assignment not valid for schedule records",linenum);
			return(0);
		}
		break;
	    case BCARD:
		switch(assign_type) {
	    	    case EMPLOYER: /* valid type of assignments */
		    case NAME:
	    	    case TEL_NUMBER:
	    	    case POSITION:
	    	    case DEPT:
	    	    case PO_BOX:
	    	    case ADDRESS:
	    	    case TELEX:
	    	    case FAX:
	    	    case FREE:
		    case MARKED:
			break;
		    default:
			error("Assignment not valid for business card records",linenum);
			return(0);
		}
		break;
	    case CALENDAR:
		switch(assign_type) {
	    	    case MONTH: /* valid type of assignments */
	    	    case MARKED_DAYS:
		    case MARKED:
			break;
		    default:
			error("Assignment not valid for calendar records",linenum);
			return(0);
		}
		break;
	}
	return(1);
    } else {
	error("assignment found outside a %begin structure", linenum);
	return(0);
    }
}

/* allocate a field for the BOSS interface library */
char *fill_field(from, last)
char *from, **last;
{
    char *to;

    if (strlen(from)) { /* is there data to copy? */
	to=strput(from);
	if (*last == NULL) /* optional field set yet? */
	    *last=to;
    } else {
	if (*last != NULL) /* should we add a skip field marker? */
	    to=strput(""); /* skip field */
	else
	    to=NULL;
    }
    return(to);
}

/* put a string in memory somewhere */
char *strput(from)
char *from;
{
    char *to;

    to=(char *)malloc(strlen(from)+2); /* allocate room */
    strcpy(to, from); /* copy string */
    strcat(to, "\n"); /* field separator */
    return(to);
}
@//E*O*F cfg/boss_cfg.y//
chmod u=rw,g=r,o=r cfg/boss_cfg.y
 
echo x - cfg/Makefile
sed 's/^@//' > "cfg/Makefile" <<'@//E*O*F cfg/Makefile//'
# workaround for System V make bug
SHELL	= /bin/sh
#BOSSDEV	= /dev/tty1
BOSSDEV	= /dev/tty000
BOSSLIB	= libboss
TOPDIR	= ..
BINDIR	= $(TOPDIR)/bin
INCDIR	= $(TOPDIR)/include
LIBDIR	= $(TOPDIR)/lib
CFGDIR	= $(TOPDIR)/cfg
CFLAGS 	= $(COPTS) -I$(INCDIR) -DDEVICE=\"$(BOSSDEV)\"
LIBS 	= $(LIBDIR)/$(BOSSLIB).a
YFLAGS 	= -D

all:	$(BINDIR)/boss

$(BINDIR)/boss:	$(LIBS) boss.o boss_cfg.o boss_lex.o
	$(CC) -I$(INCDIR) -o $(BINDIR)/boss boss.o boss_cfg.o \
	boss_lex.o $(LIBS)

boss.o:	$(INCDIR)/boss.h

boss_lex.o: boss_lex.l y.tab.h

boss_cfg.o: boss_cfg.y

y.tab.h: boss_cfg.y $(INCDIR)/boss.h

$(LIBS):
	cd $(LIBDIR); $(MAKE) CC="$(CC)" COPTS="$(COPTS)"

clean:
	rm -f y.tab.h lex.yy.c *.o

lint:	boss.c $(INCDIR)/boss.h
	lint boss.c
@//E*O*F cfg/Makefile//
chmod u=rw,g=r,o=r cfg/Makefile
 
echo x - cfg/y.tab.h
sed 's/^@//' > "cfg/y.tab.h" <<'@//E*O*F cfg/y.tab.h//'

typedef union 
{
    int  val;		/* general interger return value */
    long lval;		/* general long return value */
    char string[MAX_BOSS_REC+1]; /* string data return value */
} YYSTYPE;
extern YYSTYPE yylval;
# define STRING 257
# define DATE_SPEC 258
# define TIME_SPEC 259
# define NUMBER 260
# define BEGIN_REC 261
# define ENDSEL 262
# define SELECT 263
# define TRUE 264
# define FALSE 265
# define UNKNOWN 266
# define MEMO 267
# define TELE 268
# define SCHED 269
# define BCARD 270
# define CALENDAR 271
# define MARKED 272
# define NAME 273
# define TEL_NUMBER 274
# define ADDRESS 275
# define FREE 276
# define EMPLOYER 277
# define POSITION 278
# define DEPT 279
# define TELEX 280
# define FAX 281
# define PO_BOX 282
# define TEXT 283
# define DATE 284
# define TIME 285
# define ALARM_TIME 286
# define MONTH 287
# define MARKED_DAYS 288
@//E*O*F cfg/y.tab.h//
chmod u=rw,g=r,o=r cfg/y.tab.h
 
echo x - cfg/boss_lex.l
sed 's/^@//' > "cfg/boss_lex.l" <<'@//E*O*F cfg/boss_lex.l//'
%{

/*
   boss_lex.l - lexical analysis for yacc grammar for input data to Casio BOSS
		   Mark Dapoz   1990/06/25
		     mdapoz@hybrid.UUCP  or  
		     mdapoz%hybrid@cs.toronto.edu
*/

#ifdef DEBUG_LEX
#define	dprintf(x)	printf(x)
#define	dprintf1(x,y)	printf(x,y)
#else
#define	dprintf(x)
#define	dprintf1(x,y)
#endif

#include <string.h>
#include "boss.h"	/* boss data structure definitions */
#include "y.tab.h"	/* yacc token values */
%}
%a 2500
%o 4500
%%
[ \t]			;

\"[^"]*			{ /* string definition */
			if (yytext[yyleng-1] == '\\' &&
			    yytext[yyleng-2] != '\\' ) /* already escaped? */
			        yymore();
			else {
			    input(); /* discard last " */
			    esc_string(yytext+1); /* interpret escapes */
			    dprintf1("\ttokenized string %s\n", yytext+1);
			    strcpy(yylval.string, yytext+1);
			    return(STRING);
			}
			}

[Tt][Rr][Uu][Ee]	{ /* true */
			dprintf("\ttokenized true\n");
			yylval.val = TRUE;
			return(TRUE);
			}

[Ff][Aa][Ll][Ss][Ee]	{ /* false */
			dprintf("\ttokenized false\n");
			yylval.val = FALSE;
			return(FALSE);
			}

%[Ss][Ee][Ll][Ee][Cc][Tt] { /* %select */
			dprintf("\ttokenized %%select\n");
			return(SELECT);
			}

%[Bb][Ee][Gg][Ii][Nn]	{ /* %begin */
			dprintf("\ttokenized %%begin\n");
			return(BEGIN_REC);
			}

%[Ee][Nn][Dd][Ss][Ee][Ll][Ee][Cc][Tt] { /* %endselect */
			dprintf("\ttokenized %%endselect\n");
			return(ENDSEL);
			}
%{
		/* The following are objects for %select */
%}

[Mm][Ee][Mm][Oo]	{ /* memo */
			dprintf("\ttokenized target memo\n");
			yylval.val = MEMO;
			return(MEMO);
			}

[Tt][Ee][Ll][Ee][Pp][Hh][Oo][Nn][Ee] { /* telephone */
			dprintf("\ttokenized target telephone\n");
			yylval.val = TELE;
			return(TELE);
			}

[Ss][Cc][Hh][Ee][Dd][Uu][Ll][Ee] { /* schedule */
			dprintf("\ttokenized target schedule\n");
			yylval.val = SCHED;
			return(SCHED);
			}

[Bb][Uu][Ss][Ii][Nn][Ee][Ss][Ss][ /t]*[Cc][Aa][Rr][Dd] { /* business card */
			dprintf("\ttokenized target schedule\n");
			yylval.val = BCARD;
			return(BCARD);
			}

[Cc][Aa][Ll][Ee][Nn][Dd][Aa][Rr] { /* calendar */
			dprintf("\ttokenized target calendar\n");
			yylval.val = CALENDAR;
			return(CALENDAR);
			}

%{
 /* The following are valid date and time specifications */
%}

[0-9][0-9][0-9][0-9]\-[0-9][0-9]\-[0-9][0-9] { /* date spec yyyy-mm-dd */
			dprintf("\ttokenized date\n");
			strcpy(yylval.string, yytext);
			return(DATE_SPEC);
			}

[0-9][0-9]:[0-9][0-9]	{ /* time spec hh:mm */
			dprintf("\ttokenized time\n");
			strcpy(yylval.string, yytext);
			return(TIME_SPEC);
			}

%{
 /* The following are valid lhs values for assignments */
%}

[Mm][Aa][Rr][Kk][Ee][Dd] { /* marked */
			dprintf("\ttokenized marked\n");
			yylval.val = MARKED;
			return(MARKED);
			}

[Nn][Aa][Mm][Ee]	{ /* name */
			dprintf("\ttokenized name\n");
			yylval.val = NAME;
			return(NAME);
			}

[Tt][Ee][Ll][ /t]*[Nn][Uu][Mm][Bb][Ee][Rr] { /* telephone number */
			dprintf("\ttokenized telephone number\n");
			yylval.val = TEL_NUMBER;
			return(TEL_NUMBER);
			}

[Aa][Dd][Dd][Rr][Ee][Ss][Ss] { /* address */
			dprintf("\ttokenized address\n");
			yylval.val = ADDRESS;
			return(ADDRESS);
			}

[Ff][Rr][Ee][Ee]	{ /* free */
			dprintf("\ttokenized free\n");
			yylval.val = FREE;
			return(FREE);
			}

[Pp][Oo][Ss][Ii][Tt][Ii][Oo][Nn] { /* position */
			dprintf("\ttokenized position\n");
			yylval.val = POSITION;
			return(POSITION);
			}

[Dd][Ee][Pp][Aa][Rr][Tt][Mm][Ee][Nn][Tt] { /* department */
			dprintf("\ttokenized department\n");
			yylval.val = DEPT;
			return(DEPT);
			}

[Pp]\.[Oo]\.[ /t]*[Bb][Oo][Xx] { /* P.O. Box */
			dprintf("\ttokenized P.O. Box\n");
			yylval.val = PO_BOX;
			return(PO_BOX);
			}

[Tt][Ee][Ll][Ee][Xx]	{ /* telex */
			dprintf("\ttokenized telex\n");
			yylval.val = TELEX;
			return(TELEX);
			}

[Ff][Aa][Xx][ /t]*[Nn][Uu][Mm][Bb][Ee][Rr] { /* fax number */
			dprintf("\ttokenized fax number\n");
			yylval.val = FAX;
			return(FAX);
			}

[Tt][Ee][Xx][Tt]	{ /* text */
			dprintf("\ttokenized text\n");
			yylval.val = TEXT;
			return(TEXT);
			}

[Ee][Mm][Pp][Ll][Oo][Yy][Ee][Rr] { /* employer */
			dprintf("\ttokenized employer\n");
			yylval.val = EMPLOYER;
			return(EMPLOYER);
			}

[Dd][Aa][Tt][Ee]	{ /* date */
			dprintf("\ttokenized date\n");
			yylval.val = DATE;
			return(DATE);
			}

[Tt][Ii][Mm][Ee]	{ /* time */
			dprintf("\ttokenized time\n");
			yylval.val = TIME;
			return(TIME);
			}

[Aa][Ll][Aa][Rr][Mm][ /t]*[Tt][Ii][Mm][Ee]	{ /* alarm time */
			dprintf("\ttokenized alarm time\n");
			yylval.val = ALARM_TIME;
			return(ALARM_TIME);
			}

[Mm][Oo][Nn][Tt][Hh]	{ /* month */
			dprintf("\ttokenized month\n");
			yylval.val = MONTH;
			return(MONTH);
			}

[Mm][Aa][Rr][Kk][Ee][Dd][ /t]*[Dd][Aa][Yy][Ss] { /* marked days */
			dprintf("\ttokenized marked days\n");
			yylval.val = MARKED_DAYS;
			return(MARKED_DAYS);
			}

%{
 /* The following are misc tokens recognised */
%}

[~=\n\r{}]		{ /* special chars allowed */
			return(yytext[0]);
			}

[+-]?[0-9]+		{ /* signed or unsigned numeric value */
			switch(*yytext) {
			    case '-':
			    	yylval.val = (-1)*atoi(yytext+1);
				break;
			    case '+':
				yylval.val = atoi(yytext+1);
				break;
			    default:
				yylval.val = atoi(yytext);
				break;
			}
			dprintf1("\ttokenized number %d\n", yylval.val);
			return(NUMBER);
			}

[^ ~=\n\r{}\t\\"]+	{ /* misc tokens, AMBIGUOUS, MUST BE LAST RULE! */
			dprintf1("\tunknown token %s\n", yytext);
			printf("\tunknown token %s\n", yytext);
			return(UNKNOWN);
			}
%%
esc_string(string) /* interpret escape sequences inside strings */
char *string;
{
    char *buffer, *loc;
    int len, inbackslash=0;

    if (strchr(string, '\\')) { /* any escape sequences to handle? */
	buffer = (char *)malloc(strlen(string)); /* allocate room to build it */
	strcpy(buffer, next_field(string, '\\')); /* first token */
	if (strlen(buffer) == 0) {
	    strcpy(buffer, "\\");
	    inbackslash=1;
	}
	while ((loc=next_field(NULL, '\\'))) {
	    if (inbackslash) { /* accept data as clear text? */
		inbackslash=0;
		strcat(buffer, loc);
		continue;
	    }
	    if (isoctal(loc)) { /* octal digit? */
		len=strlen(buffer);
		*(buffer+len)=otod(loc);
		*(buffer+len+1)='\0';
	    } else
		if (strlen(loc) == 0) { /* backslash wanted? */
		    strcat(buffer, "\\");
		    inbackslash=1; /* accept following data as clear text */
		} else
		    strcat(buffer, loc);
	}
	strcpy(string, buffer);
	free(buffer);
    }
}

isoctal(string) /* do we have three octal digits? */
char *string;
{
    int i;

    if (strlen(string) < 3)
	return(0);
    for(i=0; i < 3; string++, i++)
	if (*string < '0' || *string > '7')
	    return(0);
    return(1);
}

otod(digits) /* convert three octal digits to decimal (int) value */
char *digits;
{
    int value=0, base;

    for (base=2; base >= 0; digits++, base--)
	value+=(*digits-'0') * (1 << (base*3));
    return(value);
}
@//E*O*F cfg/boss_lex.l//
chmod u=rw,g=r,o=r cfg/boss_lex.l
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
    350   2122  13610 protocol.spec
    499   3082  19525 bosslib.doc
    310   1283   8567 grammar.spec
    353   1141  11120 boss.c
    868   2901  23243 boss_cfg.y
     36     88    732 Makefile
     40    160    859 y.tab.h
    308    761   6899 boss_lex.l
   2764  11538  84555 total
!!!
wc  doc/protocol.spec doc/bosslib.doc doc/grammar.spec cfg/boss.c cfg/boss_cfg.y cfg/Makefile cfg/y.tab.h cfg/boss_lex.l | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0
-- 
Managing a software development team 	|   Mark Dapoz  
is a lot like being on the psychiatric	|   mdapoz%hybrid@cs.toronto.edu
ward.  -Mitch Kapor, San Jose Mercury	|   mdapoz@torvm3.iinus1.ibm.com