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