t suppor it.CPMUTL C UMODEM C  ^C Abort and returnto operating system ^X Skip to top of next page nd skip to next file :The PHLP Command Th PHEL cmman i specia versio o PIN designe t prin Hel (HP files PHEL know abou t structur o Hel Files a i use thi informatio t pin the i preferre forma I i i dealin wit a indxe Hel File PHEL print t inde o th firs pag an h bod startin o th secon page PHELР /* copyright (c) by Aaron Wohl, 1981,1982 written by Aaron Wohl 12-24-81 (wohl@cmuc) This file may be used for non-profit use provided this this notice remains at the front of the file. This program reads and writes cpm format floppy disks. cpm is a trade mark of Digital Reasearch. It runs under version 7 unix. unix is a trademark of bell labs. See the function help() for documentation. please mail any bug fixes to wohl@cmuc the source for this file is on the unix host vlsi@cmuc pathname /usr/avw/cpmutl/cpmutlVERSION-NUMBER.c vlsi is on the arpanet running TCP-IP a copy is also kept on mit-mc cpm;ar43:cpmutl VERSION-NUMBERc and on [cmuc]ps:cpmutl.c.VERSION-NUMBER (for people that can't figure out how to use ITS) mail will be sent info-cpm announcing new versions */ #define version 7 #define when "10-28-82" /* change log: ver when who why --- -------- ------------- -------------------- 2 12-27-81 wohl@cmuc allow a trailing * to cross the . in a ufn 3 12-28-81 wohl@cmuc remove refrences to cmu local functions honor the record count when reading 4 2- 9-82 wohl@cmuc fix a '=> [cpm]' string to be '[cpm] =>' put with one filename arg uses it for both 5 5- 8-82 wohl@cmuc initilize extent filler bytes to zero 6 5-17-82 wohl@cmuc fix printout of free space (was 2k low) add support for user numbers add a \n to the interleving off message 7 10-28-82 mz@gp for file get, close each file Things to (think about) doing: a) have the create file system function ask for confirmation b) multiple put should skip files with names that are too long or truncate the name c) skip files which are directories (multiple put) */ #include #include #include #include #define TRUE 1 #define FALSE 0 #define MAX_USER 15 /* maximum user number */ #define CP_NUMDIR 64 /* number of directory slots */ #define DIR_EXT (CP_NUMDIR+1) /* fake index to allocate directory space */ #define CP_DIRSEC 16 /* number of sectors of directory */ #define CP_NAMESIZE (8+3+1+1) /* file name, name+ext+dot+null */ #define CP_BASE (26*2) /* sector of start of file system */ #define CP_SIZE (26*77) /* sectors on a disk */ #define CP_CLUSTER 8 /* sectors in a allocation cluster */ #define CP_CLCHAR (CP_CLUSTER*128) /* number of bytes in a cluster */ #define CP_ALOC ((CP_SIZE-CP_BASE)/CP_CLUSTER) /* clusters per disk */ #define SPACE 040 /* ascii space character */ #define np(val) ((val) & 0177) /* no parity please */ #define FT_SZ 3 /* number of bytes in a file type */ #define FN_SZ 8 /* number of bytes in a file name */ #define SEC 128 /* bytes in a sector */ #define DM 16 /* number of disk map entries */ #define DELF 0xe5 /* entry type for a deleted file */ /* what a directory entry looks like */ struct cp_dir_ext { unsigned char cp_et; /* entry type */ unsigned char cp_fn[FN_SZ]; /* file name */ unsigned char cp_ft[FT_SZ]; /* file type */ unsigned char cp_ex; /* extent number */ unsigned char cp_fl[2]; /* filler */ unsigned char cp_rc; /* record count */ unsigned char cp_dm[DM]; /* disk allocation map */ }; struct cp_dir_ext cp_dir[CP_NUMDIR]; char cp_name[CP_NUMDIR][CP_NAMESIZE]; /* name as file.typ */ int ext_next[CP_NUMDIR]; /* slot of next higher extent */ int ext_prev[CP_NUMDIR]; /* slot of next lower extent */ int cp_btb[CP_ALOC]; /* allocation bit table */ int dsk_inuse; /* number of clusters in use */ int num_fil; /* number of files */ int num_ext; /* number of extents */ int dir_err; /* number of directory errors */ /* includes number of i/o errors in dir */ int io_err; /* number of i/o errors */ int user_num = 0; /* current user number */ int any_user = TRUE; /* true if files from any user can be seen */ #define flag(c) (flg[(c) - 'a']) char *man = { "clgp" }; char *flname; int fldes; long lseek(); int gcmd(),lcmd(),icmd(),ccmd(),pcmd(),ncmd(); /* command functions */ int (*comfun)(); char flg[26]; char mode = 'u'; int file; /* main program */ main(argc, argv) char *argv[]; { char *cp; fprintf(stdout,"cpmutl version(%d), date:%s\n",version,when); init_data(); /* initialize data */ cp = argv[1]; for(cp = argv[1]; *cp; cp++) switch(*cp) { case 'a': flag('a')++; continue; case 't': mode = 't'; continue; case 'b': mode = 'b'; continue; case 'o': flag('o')++; /* allow writes to a damaged directory */ continue; case 'i': flag('i')++; continue; case 'n': flag('w')++; /* will be writing */ setcom(ncmd); continue; case 'p': flag('w')++; /* will be writing */ setcom(pcmd); continue; case 'c': flag('w')++; /* will be writing */ setcom(ccmd); continue; case 'g': setcom(gcmd); continue; case 'l': setcom(lcmd); continue; case 'f': flname = argv[2]; argv++; argc--; continue; case 'h': help(); case 'u': user_num=0; /* read in the number */ while(isdigit(*(cp+1))) user_num = user_num * 10 + (*++cp - '0'); if(user_num > MAX_USER) quit(1,"\ncpmutl:user number out of range\n"); any_user = FALSE; continue; default: quit(1,"\ncpmutl: bad option '%c'. use h for help\n", *cp); } if(comfun == 0) { quit(1,"\ncpmutl: one of [%s] must be specified. use h for help\n",man); } (*comfun)(argc-2,&argv[2]); fprintf(stdout,"\n"); exit(0); } /* command parsing subroutines */ need_mode() { if(mode == 'u') quit(1,"\ncpmutl: mode t(ext) or b(inary) must be specified\n"); } setcom(fun) int (*fun)(); { if(comfun != 0) quit(1,"\ncpmutl: only one of [%s] allowed\n", man); comfun = fun; } help() { fprintf(stdout,"commands:\n"); fprintf(stdout," l afn list files matching afn\n"); fprintf(stdout," g afn [unix_prefix] get files matching afn\n"); fprintf(stdout," p ufn unix_file_name put a file to floppy\n"); fprintf(stdout," p '*' unix_files put a bunch of files\n"); fprintf(stdout," n afn nuke - delete matching files\n"); fprintf(stdout," c create a new file system\n"); fprintf(stdout,"\noptions:\n"); fprintf(stdout," t - text mode b - binary mode\n"); fprintf(stdout," i - don't do interleaving u - user number\n"); fprintf(stdout," a - absolute disk address, start.length e.g. 0.2002\n"); fprintf(stdout," f - use the named unix file instead of /dev/floppy\n"); fprintf(stdout," o - override, allows writes to a damaged directories\n"); fprintf(stdout,"\nterms:\n"); fprintf(stdout," afn - ambiguous cpm file name, e.g. *.asm or foo*\n"); fprintf(stdout," *** don't forget to quote afns to the shell, '*.asm'\n"); fprintf(stdout," ufn - unambiguous file name\n"); fprintf(stdout," interleaving - tracks 2-76 are normaly interleaved\n"); fprintf(stdout," with a skew of 6 sectors\n"); exit(1); } /* create a new file system */ ccmd() { int i; fprintf(stdout,"initializing file system "); fflush(stdout); lread(CP_BASE,CP_DIRSEC,cp_dir); /* read in the directory */ /* so we can zap disk to undo this */ for(i=0; i 2) toomany(); if(flag('a')) { /* want absolute disk addresses? */ if(argc < 2) toofew(); abs_read(cpm_name,argv[1]); return; } need_mode(); /* need have given a file mode */ if (argc == 1) unix_name = ""; /* no unix prefix */ else unix_name = argv[1]; /* use this unix prefix */ parse_ext(&wild,cpm_name); getdir(); /* read the directory */ cur = -1; /* start looking here */ while((cur = lookup(&wild,cur)) != -1) /* find the next matching file */ getfile(cur,unix_name); } /* pcmd - put files on the floppy */ pcmd(argc,argv) char *argv[]; int argc; { int i; char *uname; if(argc<1) toofew(); if(flag('a')) { /* want absolute disk addresses? */ if(argc<2) toofew(); if(argc > 2) toomany(); abs_write(argv[0],argv[1]); return; } need_mode(); getdir(); if (argc == 1) /* only one arg? */ writefile(argv[0],argv[0]); else for(i=1; i sector %d, length %d ",unix_name,start,size); fflush(stdout); while(size--) { if((status = fread(buf,1,SEC,ifile)) != SEC) /* read a sector from unix */ quit(1,"\ncpmutl:read error on unix file %s\n",unix_name); lwrite(start,1,buf); start++; /* advance to the next sector */ } reporterrs(); fclose(ifile); } /* ndcm - nuke some files */ ncmd(argc,argv) char *argv[]; int argc; { struct cp_dir_ext del_ext; if(argc < 1) toofew(); if(argc > 2) toomany(); getdir(); /* get the directory */ parse_ext(&del_ext,argv[0]); /* parse the file spec to delete */ nuke_ext(&del_ext); /* delete matching extents */ putdir(); } /* delete matching entries */ nuke_ext(nuk_me) struct cp_dir_ext *nuk_me; { int i; for(i=0; icp_fn[i] = src->cp_fn[i]; for(i=0; icp_ft[i] = src->cp_ft[i]; } /* lcmd - list the floppy directory */ lcmd(argc,argv) char *argv[]; int argc; { int i,cur,curext; int totrec=0,totext=0,totk=0; struct cp_dir_ext wild; char *cpm_name; if(argc > 1) toomany(); if(argc == 0) cpm_name = "*.*"; /* default file name */ else cpm_name = argv[0]; getdir(); /* read in the directory */ fprintf(stdout, "disk status: %d files, %d extents, %dK allocated, %dK free\n\n", num_fil,num_ext,dsk_inuse-2,CP_ALOC-dsk_inuse); fprintf(stdout," recs K ex usr name\n"); parse_ext(&wild,cpm_name); /* parse the cpm file name */ cur = -1; while((cur = curext = lookup(&wild,cur)) != -1) { /* find a file */ int recsiz=0,ksiz=0,numext=0; while(curext != -1) { /* do all of its extents */ numext++; /* found one more extent */ recsiz += cp_dir[curext].cp_rc; /* this many records */ ksiz += ((cp_dir[curext].cp_rc+CP_CLUSTER-1)/CP_CLUSTER); /* k size */ curext = ext_next[curext]; /* move on */ } fprintf(stdout, " %4d %3dK %2d [%d]%s\n",recsiz,ksiz,numext, cp_dir[cur].cp_et,cp_name[cur]); totrec += recsiz; totk += ksiz; totext += numext; } fprintf(stdout," ---- --- --\n"); fprintf(stdout," %4d %3d %2d\n",totrec,totk,totext); } /* read subroutines */ /* read a file from the floppy */ getfile(index,unix_prefix) int index; char *unix_prefix; { int clstr,cur_io_err,extsiz,currec; FILE *ofile; char *unix_name; char buf[100]; /* default to the prefix */ if (*unix_prefix == 0) /* is there a unix file name */ unix_name = cp_name[index]; /* no, just use the cpm name */ else { char *cp; for(cp = unix_prefix; *cp; *cp++ ); /* find the end */ cp--; /* point to the last char */ if (*cp != '/') /* is this a prefix */ unix_name = unix_prefix; /* no, its a name */ else strcpy(buf,unix_prefix), /* put on the prefix */ strcat(buf,cp_name[index]), /* then the name */ unix_name = buf; /* that's it */ } if ((ofile = fopen(unix_name,"w")) == NULL) /* try to open the unix file */ quit(1,"\ncpmutl:can't create unix file %s\n",unix_name); fprintf(stdout,"[%d]%s => %s ",cp_dir[index].cp_et, cp_name[index],unix_name); fflush(stdout); markerrs(); for( ; index != -1; index=ext_next[index]) { /* all extents */ extsiz = cp_dir[index].cp_rc; /* read all the records */ for(currec=0; currec < extsiz; currec++) { /* read all the records */ if(read_sector(cp_dir[index].cp_dm[currec/CP_CLUSTER], currec % CP_CLUSTER,ofile)) break; /* stop at eof */ } } reporterrs(); fclose(ofile); } /* more read subroutines */ /* read a bunch of sectors into a file */ abs_read(wherefrom,unix_name) { int start,size,status; char buf[SEC]; FILE *ofile; if(sscanf(wherefrom,"%d.%d",&start,&size) != 2) quit(1,"\ncpmutl:can't parse start.size address\n"); if ((ofile = fopen(unix_name,"w")) == NULL) /* try to open the unix file */ quit(1,"\ncpmutl:can't create unix file %s\n",unix_name); markerrs(); fprintf(stdout, "reading from sector %d, length %d => %s ",start,size,unix_name); fflush(stdout); while(size--) { lread(start,1,buf); /* read a sector from the floppy */ status=fwrite(buf,1,SEC,ofile); /* write it to the file */ if(status != SEC) quit(1,"\ncpmutl:file write error for unix file %s\n",unix_name); start++; /* advance to the next sector */ } reporterrs(); fclose(ofile); } /* read in a sector */ int read_sector(dsk_adr,sec,ofile) int dsk_adr,sec; FILE *ofile; { int i; char buf[SEC],cur; /* sector buffer */ lread((dsk_adr*CP_CLUSTER)+CP_BASE+sec,1,buf); /* read it in */ if(sec == 0) /* start of a cluster? */ putc('.',stdout), /* yes */ fflush(stdout); if (mode == 't') { for(i=0; icp_et != user_num) return FALSE; /* fail if not the correct user */ for(i=0; icp_fn[i]) != np(ext2->cp_fn[i])) && /* fail if not equal */ (np(ext1->cp_fn[i]) != '?')) /* and ext1 not wild */ return FALSE; for(i=0; icp_ft[i]) != np(ext2->cp_ft[i])) && /* fail if not equal */ (np(ext1->cp_ft[i]) != '?')) /* and ext1 not wild */ return FALSE; return TRUE; /* success */ } /* parse the passed extent */ parse_ext(rslt,name) struct cp_dir_ext *rslt; char *name; { int i; char cur; ini_ext(rslt); /* initialize the extent */ for(i=0; icp_fn[i] = '?'; /* fill out with ? then */ else rslt->cp_fn[i] = cur, name++; } if(*name == '*') { /* need to skip a star? */ *name++; /* get out of the * */ if(*name == 0) /* the end of the string? */ name = "*"; /* yes, start crosses the . then */ } if(*name == '.') *name++; /* maybe skip the separator */ for(i=0; icp_ft[i] = '?'; /* fill out with ? then */ else rslt->cp_ft[i] = cur, name++; } if(*name == '*') *name++; /* get out of the * */ if(*name != 0) quit(1,"\ncpmutl:cpm file name to long\n"); } ini_ext(rslt) struct cp_dir_ext *rslt; { int i; rslt->cp_et = user_num; /* mark this extent in use */ for(i=0; icp_fn[i] = SPACE; for(i=0; icp_ft[i] = SPACE; rslt->cp_rc = 0; /* no records yet */ rslt->cp_ex = 0; /* relative extent number */ for(i=0; icp_dm[i] = 0; /* no disk space assigned yet */ for(i=0; i<2; i++) rslt->cp_fl[i] = 0; /* initilize filler words */ } /* random small subroutines */ /* record io errors for a transfer */ int cur_io_err; markerrs() { cur_io_err = io_err; } /* print [ok] or number of io errors */ reporterrs() { if(cur_io_err != io_err) fprintf(stderr,"\ncpmutl:%d io errors\n",io_err-cur_io_err); else fprintf(stdout," [ok]\n"); } /* find the next file to match cpm_name */ lookup(wild,index) int index; struct cp_dir_ext *wild; { index++; /* skip over the current extent */ for( ; index= CP_ALOC) { /* does this cluster exist? */ dir_err++; fprintf(stderr,"\ncpmutl:illegal cluster %d in disk map for ",where); prname(who); fprintf(stderr,"\n"); } else if (cp_btb[where] != -1) { /* has someone else already claimed it? */ dir_err++; fprintf(stderr,"\ncpmutl:Cluster %d multiply linked:\n",where); fprintf(stderr," first file: "); prname(who); fprintf(stderr," second file: "); prname(cp_btb[where]); } else { cp_btb[where] = who; /* remember who has it for debugging */ dsk_inuse++; /* one more cluster in use */ } } /* print the name of a file */ prname(index) int index; { int i,temp; if(index == DIR_EXT) fprintf(stderr,"directory\n"); else fprintf(stderr,"name:[%d]%s, extent:%d, recs:%d, slot:%d\n", cp_dir[index].cp_et,cp_name[index],cp_dir[index].cp_ex, cp_dir[index].cp_rc,index); } /* check that the record count for index is consistent with */ /* the list of clusters allocated to it in the extent map */ check_rc(index) int index; { int bit_aloc,j; bit_aloc=(cp_dir[index].cp_rc+CP_CLUSTER-1)/CP_CLUSTER; if(bit_aloc > DM) { /* is it in range? */ dir_err++; /* no */ fprintf(stderr,"\ncpmutl:record count out of bounds\n for extent:"); prname(index); return; /* give up on this one */ } for(j=0; j= CP_NUMDIR) { /* was a lower extent found ? */ dir_err++; /* no, that is an error */ fprintf(stderr,"\ncpmutl:can't find lower extent: "); prname(index); } } } /* set cp_name from cp_fn and cp_ft */ setname(index) { int i,j,temp; j=0; /* index into the file name */ for(i=0; i [%d]%s ",unix_name, cp_dir[cur_ext].cp_et,cp_name[cur_ext]); fflush(stdout); while(TRUE) { i = fread((char *)&ch,1,1,ifile); /* get the next input byte */ if(i == 0) break; /* stop this at eof */ if(i != 1) quit(1,"\ncpmutl:read error on unix file %s\n",unix_name); if(mode == 't') { if((ch = np(ch)) == '\n') cpm_put('M'-64), /* new line is cr */ cpm_put('J'-64); /* then lf */ else cpm_put(ch); } else cpm_put(ch); } if(mode == 't') cpm_put('Z'-64); /* the finishing touch for text */ if((cur_pos % CP_CLCHAR) != 0) /* data left in the floppy buffer */ flbuf(); /* yes, flush the buffer */ fclose(ifile); /* close the input file */ putdir(); /* write the directory back */ reporterrs(); } /* write routines */ cpm_put(curchar) char curchar; { extern int cur_ext; extern int cur_pos; extern char buff[]; int new_ext,i,buf_ptr; if(cur_pos >= (CP_CLCHAR*DM)) { /* this extent already full? */ new_ext = aloc_ext(); /* make the new extent */ cur_pos = 0; /* reset position pointer */ copy_ext(&cp_dir[cur_ext],&cp_dir[new_ext]); /* copy over the name */ cp_dir[new_ext].cp_ex = cp_dir[cur_ext].cp_ex + 1; ext_next[cur_ext] = new_ext; /* forward link */ ext_prev[new_ext] = cur_ext; /* back link */ cur_ext = new_ext; setname(cur_ext); /* set up cp_name for it */ } if((cur_pos % SEC) == 0) /* going into a new record ? */ cp_dir[cur_ext].cp_rc++; /* yes */ buf_ptr = cur_pos % CP_CLCHAR; /* where in the buffer */ if(buf_ptr == 0) /* need a new cluster */ cp_dir[cur_ext].cp_dm[cur_pos / CP_CLCHAR] = aloc_cluster(cur_ext); buff[buf_ptr] = curchar; /* store the char */ if(buf_ptr == (CP_CLCHAR-1)) /* last char of the buffer? */ flbuf(); /* yes, flush the buffer */ cur_pos++; /* advance to the next character */ } flbuf() { extern int cur_ext; extern int cur_pos; extern char buff[]; lwrite((cp_dir[cur_ext].cp_dm[cur_pos / CP_CLCHAR])*CP_CLUSTER+CP_BASE, CP_CLUSTER,buff); /* write out the buffer */ putc('.',stdout); fflush(stdout); } start_new_file(name) char *name; { extern int cur_ext; extern int cur_pos; int cur,i; struct cp_dir_ext temp_ext; parse_ext(&temp_ext,name); /* parse the file name */ nuke_ext(&temp_ext); /* nuke matching current extents */ cur_pos = 0; cur_ext = aloc_ext(); /* make up an extent for the file */ copy_ext(&temp_ext,&cp_dir[cur_ext]); /* give it a name */ num_fil++; /* this extent is really a file */ setname(cur_ext); /* set up cp_name */ dirok(); /* make sure directory is ok */ } /* allocate a new extent */ int aloc_ext() { int i,newext; for(newext=0; newext= CP_NUMDIR) /* was a free extent found? */ quit(1,"\ncpmutl:allocate extent failure, floppy directory full\n"); num_ext++; /* one more extent now in use */ ini_ext(&cp_dir[newext]); /* clear it */ return(newext); /* return the extent to the user */ } /* allocate a new cluster */ int aloc_cluster(extent) int extent; { int i; for(i=0; i= CP_BASE) && /* is this in the directory? */ (startad < (CP_BASE + CP_DIRSEC))) dir_err++; /* it counts as a directory error */ } obuff += SEC; startad++; } } /* write some sectors to the floppy doing interleaving */ lwrite(startad,count,obuff) int startad, count; char *obuff; { long trans(); extern fldes; fl_init(); while(count--) { lseek(fldes, trans(startad)*SEC, 0); if (write(fldes,obuff,SEC) != SEC) { io_err++; fprintf(stderr,"\ncpmutl: write sector error %d\n",startad); if ((startad >= CP_BASE) && /* is this in the directory? */ (startad < (CP_BASE + CP_DIRSEC))) dir_err++; /* it counts as a directory error */ } obuff += SEC; startad++; } } /* quit - a cmu local function */ quit(status,fmt,args) int status; char *fmt; { _doprnt(fmt,&args,stderr); exit(status); } /* * UMODEM -- Implements the "CP/M User's Group XMODEM" protocol, * the TERM II File Transfer Protocol (FTP) Number 1, * and the TERM II File Transfer Protocol Number 4 for * packetized file up/downloading. * * Note: UNIX System-Dependent values are indicated by the string [SD] * in a comment field on the same line as the values. * * * -- Lauren Weinstein, 6/81 * -- (Version 2.0) Modified for JHU/UNIX by Richard Conn, 8/1/81 * -- Version 2.1 Mods by Richard Conn, 8/2/81 * . File Size Included on Send Option * -- Version 2.2 Mods by Richard Conn, 8/2/81 * . Log File Generation and Option Incorporated * -- Version 2.3 Mods by Richard Conn, 8/3/81 * . TERM II FTP 1 Supported * . Error Log Reports Enhanced * . CAN Function Added to FTP 3 * . 'd' Option Added to Delete umodem.log File before starting * -- Version 2.4 Mods by Richard Conn, 8/4/81 * . 16K-extent sector number check error corrected * . Count of number of received sectors added * -- Version 2.5 Mods by Richard Conn, 8/5/81 * . ARPA Net Flag added * . ARPA Net parameter ('a') added to command line * . ARPA Net BIS, BIE, BOS, BOE added * . ARPA Net FFH escape added * -- Version 2.6 Mods by Bennett Marks, 8/21/81 (Bucky @ CCA-UNIX) * . mods for UNIX V7 (Note: for JHU compilation define * the variable JHU during 'cc' * . added 'mungmode' flag to protect from inadvertant * overwrite on file receive * . changed timeout handling prior to issuing checksum * -- Version 2.7 Mods by Richard Conn, 8/25/81 (rconn @ BRL) * . correct minor "ifndef" error in which ifndef had no arg * . restructured "ifdef" references so that other versions * of UNIX than Version 7 and JHU can be easily incorporated; * previous ifdef references were for JHU/not JHU; * to compile under Version 7 or JHU UNIX, the following * command lines are recommended: * "cc umodem.c -o umodem -DVER7" for Version 7 * "cc -7 umodem.c -o umodem -DJHU" for JHU * . added 'y' file status display option; this option gives * the user an estimate of the size of the target file to * send from the UNIX system in terms of CP/M records (128 * bytes) and Kbytes (1024 byte units) * . added '7' option which modifies the transmission protocols * for 7 significant bits rather than 8; modifies both FTP 1 * and FTP 3 * -- Version 2.8 Mods by Richard Conn, 8/28/81 * . corrected system-dependent reference to TIOCSSCR (for * disabling page mode) and created the PAGEMODE flag which * is to be set to TRUE to enable this * . added -4 option which engages TERM II, FTP 4 (new release) * -- Version 2.9 Mods by Richard Conn, 9/1/81 * . internal documentation on ARPA Net protocols expanded * . possible operator precedence problem with BITMASK corrected * by redundant parentheses * -- Version 3.0 Mods by Lauren Weinstein, 9/14/81 * . fixed bug in PAGEMODE defines (removed PAGEMODE define * line; now should be compiled with "-DPAGEMODE" if * Page Mode is desired) * . included forward declaration of ttyname() to avoid problems * with newer V7 C compilers * -- Version 3.1 Mods by Lauren Weinstein, 4/17/82 * . avoids sending extraneous last sector when file EOF * occurs on an exact sector boundary * -- Version 3.2 Mods by Michael M Rubenstein, 5/26/83 * . fixed bug in readbyte. assumed that int's are ordered * from low significance to high * . added LOGDEFAULT define to allow default logging to be * off. compile with -DLOGDEFAULT=0 to set default to no * no logging. * */ #include #include #include /* JHU UNIX tty parameter file */ #ifdef JHU #include #endif /* Version 7 UNIX tty parameter file */ #ifdef VER7 #include #endif /* log default define */ #ifndef LOGDEFAULT #define LOGDEFAULT 1 #endif #include #define VERSION 32 /* Version Number */ #define TRUE 1 #define FALSE 0 /* Compile with "-DPAGEMODE" if Page Mode (TIOCSSCR) is supported on your * UNIX system. If it is supported, make sure that TIOCSSCR is the * correct name for Page Mode engagement. */ /* ASCII Constants */ #define SOH 001 #define STX 002 #define ETX 003 #define EOT 004 #define ENQ 005 #define ACK 006 #define LF 012 /* Unix LF/NL */ #define CR 015 #define NAK 025 #define SYN 026 #define CAN 030 #define ESC 033 #define CTRLZ 032 /* CP/M EOF for text (usually!) */ /* UMODEM Constants */ #define TIMEOUT -1 #define ERRORMAX 10 /* maximum errors tolerated */ #define RETRYMAX 10 /* maximum retries to be made */ #define BBUFSIZ 128 /* buffer size -- do not change! */ /* [SD] Mode for Created Files */ #define CREATMODE 0644 /* mode for created files */ /* ARPA Net Constants */ /* The following constants are used to communicate with the ARPA * Net SERVER TELNET and TIP programs. These constants are defined * as follows: * IAC <-- Is A Command; indicates that * a command follows * WILL/WONT <-- Command issued to SERVER TELNET * (Host); WILL issues command * and WONT issues negative of * the command * DO/DONT <-- Command issued to TIP; DO issues * command and DONT issues * negative of the command * TRBIN <-- Transmit Binary Command * Examples: * IAC WILL TRBIN <-- Host is configured to transmit Binary * IAC WONT TRBIN <-- Host is configured NOT to transmit binary * IAC DO TRBIN <-- TIP is configured to transmit Binary * IAC DONT TRBIN <-- TIP is configured NOT to transmit binary */ #define IAC 0377 /* Is A Command */ #define DO 0375 /* Command to TIP */ #define DONT 0376 /* Negative of Command to TIP */ #define WILL 0373 /* Command to SERVER TELNET (Host) */ #define WONT 0374 /* Negative of Command to SERVER TELNET */ #define TRBIN 0 /* Transmit Binary Command */ /* JHU UNIX structures */ #ifdef JHU struct sttybuf ttys, ttysnew, ttystemp; /* for stty terminal mode calls */ #endif /* Version 7 UNIX structures */ #ifdef VER7 struct sgttyb ttys, ttysnew, ttystemp; /* for stty terminal mode calls */ #endif struct stat statbuf; /* for terminal message on/off control */ FILE *LOGFP, *fopen(); char buff[BBUFSIZ]; #ifdef JHU int wason; #endif #ifdef VER7 int pagelen; char *ttyname(); /* forward declaration for C */ #endif char *tty; char XMITTYPE; int ARPA, RECVFLAG, SENDFLAG, FTP1, PMSG, DELFLAG, LOGFLAG, MUNGMODE; int STATDISP, BIT7, BITMASK; int delay; alarmfunc(); main(argc, argv) int argc; char **argv; { char *logfile; int index; char flag; logfile = "umodem.log"; /* Name of LOG File */ printf("\nUMODEM Version %d.%d", VERSION/10, VERSION%10); printf(" -- UNIX-Based Remote File Transfer Facility\n"); if (argc < 3 || *argv[1] != '-') { printf("\nUsage: \n\tumodem "); printf("-[rb!rt!sb!st][p][l][1][a][m][d][y][7][4]"); printf(" filename\n"); printf("\n"); printf("\n\trb <-- Receive Binary"); printf("\n\trt <-- Receive Text"); printf("\n\tsb <-- Send Binary"); printf("\n\tst <-- Send Text"); printf("\n\tp <-- Turn ON Parameter Display"); #if LOGDEFAULT printf("\n\tl <-- (ell) Turn OFF LOG File Entries"); #else printf("\n\tl <-- (ell) Turn ON LOG File Entries"); #endif printf("\n\t1 <-- (one) Employ TERM II FTP 1"); printf("\n\ta <-- Turn ON ARPA Net Flag"); printf("\n\tm <-- Allow file overwiting on receive"); printf("\n\td <-- Delete umodem.log File before starting"); printf("\n\ty <-- Display file status (size) information only"); printf("\n\t7 <-- Enable 7-bit transfer mask"); printf("\n\t4 <-- Enable TERM II FTP 4"); printf("\n"); exit(-1); } index = 1; /* set index for loop */ delay = 3; /* assume FTP 3 delay */ PMSG = FALSE; /* turn off flags */ FTP1 = FALSE; /* assume FTP 3 (CP/M UG XMODEM2) */ RECVFLAG = FALSE; /* not receive */ SENDFLAG = FALSE; /* not send either */ XMITTYPE = 't'; /* assume text */ DELFLAG = FALSE; /* do NOT delete log file before starting */ LOGFLAG = LOGDEFAULT; ARPA = FALSE; /* assume not on ARPA Net */ MUNGMODE = FALSE; /* protect files from overwriting */ STATDISP = FALSE; /* assume not a status display */ BIT7 = FALSE; /* assume 8-bit communication */ while ((flag = argv[1][index++]) != '\0') switch (flag) { case 'a' : ARPA = TRUE; /* set ARPA Net */ break; case 'p' : PMSG = TRUE; /* print all messages */ break; case '1' : FTP1 = TRUE; /* select FTP 1 */ delay = 5; /* FTP 1 delay constant */ printf("\nUMODEM: TERM II FTP 1 Selected\n"); break; case 'd' : DELFLAG = TRUE; /* delete log file first */ break; case 'l' : LOGFLAG = ~LOGDEFAULT; /* turn off log report */ break; case 'r' : RECVFLAG = TRUE; /* receive file */ XMITTYPE = gettype(argv[1][index++]); /* get t/b */ break; case 's' : SENDFLAG = TRUE; /* send file */ XMITTYPE = gettype(argv[1][index++]); break; case 'm' : MUNGMODE = TRUE; /* allow overwriting of files */ break; case 'y' : STATDISP = TRUE; /* display file status */ break; case '7' : BIT7 = TRUE; /* transfer only 7 bits */ break; case '4' : FTP1 = TRUE; /* select FTP 1 (varient) */ BIT7 = TRUE; /* transfer only 7 bits */ delay = 5; /* FTP 1 delay constant */ printf("\nUMODEM: TERM II FTP 4 Selected\n"); break; default : error("Invalid Flag", FALSE); } if (BIT7 && (XMITTYPE == 'b')) { printf("\nUMODEM: Fatal Error -- Both 7-Bit Transfer and "); printf("Binary Transfer Selected"); exit(-1); /* error exit to UNIX */ } if (BIT7) /* set MASK value */ BITMASK = 0177; /* 7 significant bits */ else BITMASK = 0377; /* 8 significant bits */ if (PMSG) { printf("\nSupported File Transfer Protocols:"); printf("\n\tTERM II FTP 1"); printf("\n\tCP/M UG XMODEM2 (TERM II FTP 3)"); printf("\n\tTERM II FTP 4"); printf("\n\n"); } if (LOGFLAG) { if (!DELFLAG) LOGFP = fopen(logfile, "a"); /* append to LOG file */ else LOGFP = fopen(logfile, "w"); /* new LOG file */ fprintf(LOGFP,"\n\n++++++++\n"); fprintf(LOGFP,"\nUMODEM Version %d.%d\n", VERSION/10, VERSION%10); printf("\nUMODEM: LOG File '%s' is Open\n", logfile); } if (STATDISP) yfile(argv[2]); /* status of a file */ if (RECVFLAG && SENDFLAG) error("Both Send and Receive Functions Specified", FALSE); if (!RECVFLAG && !SENDFLAG) error("Neither Send nor Receive Functions Specified", FALSE); if (RECVFLAG) { if(open(argv[2], 0) != -1) /* possible abort if file exists */ { printf("\nUMODEM: Warning -- Target File Exists\n"); if( MUNGMODE == FALSE ) error("Fatal - Can't overwrite file\n",FALSE); printf("UMODEM: Overwriting Target File\n"); } rfile(argv[2]); /* receive file */ } else sfile(argv[2]); /* send file */ } gettype(ichar) char ichar; { if (ichar == 't') return(ichar); if (ichar == 'b') return(ichar); error("Invalid Send/Receive Parameter - not t or b", FALSE); return; } /* set tty modes for UMODEM transfers */ setmodes() { /* Device characteristics for JHU UNIX */ #ifdef JHU if (gtty(0, &ttys) < 0) /* get current tty params */ error("Can't get TTY Parameters", TRUE); tty = ttyname(0); /* identify current tty */ /* duplicate current modes in ttysnew structure */ ttysnew.ispeed = ttys.ispeed; /* copy input speed */ ttysnew.ospeed = ttys.ospeed; /* copy output speed */ ttysnew.xflags = ttys.xflags; /* copy JHU/UNIX extended flags */ ttysnew.mode = ttys.mode; /* copy standard terminal flags */ ttysnew.mode |= RAW; /* set for RAW Mode */ /* This ORs in the RAW mode value, thereby setting RAW mode and leaving the other mode settings unchanged */ ttysnew.mode &= ~ECHO; /* set for no echoing */ /* This ANDs in the complement of the ECHO setting (for NO echo), thereby leaving all current parameters unchanged and turning OFF ECHO only */ ttysnew.mode &= ~XTABS; /* set for no tab expansion */ ttysnew.mode &= ~LCASE; /* set for no upper-to-lower case xlate */ ttysnew.mode |= ANYP; /* set for ANY Parity */ ttysnew.mode &= ~NL3; /* turn off ALL delays - new line */ ttysnew.mode &= ~TAB3; /* turn off tab delays */ ttysnew.mode &= ~CR3; /* turn off CR delays */ ttysnew.mode &= ~FF1; /* turn off FF delays */ ttysnew.mode &= ~BS1; /* turn off BS delays */ /* the following are JHU/UNIX xflags settings; they are [SD] */ ttysnew.xflags &= ~PAGE; /* turn off paging */ ttysnew.xflags &= ~STALL; /* turn off ^S/^Q recognition */ ttysnew.xflags &= ~TAPE; /* turn off ^S/^Q input control */ ttysnew.xflags &= ~FOLD; /* turn off CR/LF folding at col 72 */ ttysnew.xflags |= NB8; /* turn on 8-bit input/output */ if (stty(0, &ttysnew) < 0) /* set new params */ error("Can't set new TTY Parameters", TRUE); if (stat(tty, &statbuf) < 0) /* get tty status */ error("Can't get your TTY Status", TRUE); if (statbuf.st_mode&011) /* messages are on [SD] */ { wason = TRUE; if (chmod(tty, 020600) < 0) /* turn off tty messages [SD] */ error("Can't change TTY Mode", TRUE); } else wason = FALSE; /* messages are already off */ #endif /* Device characteristics for Version 7 UNIX */ #ifdef VER7 if (ioctl(0,TIOCGETP,&ttys)<0) /* get tty params [V7] */ error("Can't get TTY Parameters", TRUE); tty = ttyname(0); /* identify current tty */ /* transfer current modes to new structure */ ttysnew.sg_ispeed = ttys.sg_ispeed; /* copy input speed */ ttysnew.sg_ospeed = ttys.sg_ospeed; /* copy output speed */ ttysnew.sg_erase = ttys.sg_erase; /* copy erase flags */ ttysnew.sg_flags = ttys.sg_flags; /* copy flags */ ttysnew.sg_kill = ttys.sg_kill; /* copy std terminal flags */ ttysnew.sg_flags |= RAW; /* set for RAW Mode */ /* This ORs in the RAW mode value, thereby setting RAW mode and leaving the other mode settings unchanged */ ttysnew.sg_flags &= ~ECHO; /* set for no echoing */ /* This ANDs in the complement of the ECHO setting (for NO echo), thereby leaving all current parameters unchanged and turning OFF ECHO only */ ttysnew.sg_flags &= ~XTABS; /* set for no tab expansion */ ttysnew.sg_flags &= ~LCASE; /* set for no upper-to-lower case xlate */ ttysnew.sg_flags |= ANYP; /* set for ANY Parity */ ttysnew.sg_flags &= ~NL3; /* turn off ALL delays - new line */ ttysnew.sg_flags &= ~TAB2; /* turn off tab delays */ ttysnew.sg_flags &= ~CR3; /* turn off CR delays */ ttysnew.sg_flags &= ~FF1; /* turn off FF delays */ ttysnew.sg_flags &= ~BS1; /* turn off BS delays */ ttysnew.sg_flags &= ~TANDEM; /* turn off flow control */ #ifdef PAGEMODE /* make sure page mode is off */ ioctl(0,TIOCSSCR,&pagelen); /* [SD] */ #endif /* set new paramters */ if (ioctl(0,TIOCSETP,&ttysnew) < 0) error("Can't set new TTY Parameters", TRUE); if (stat(tty, &statbuf) < 0) /* get tty status */ error("Can't get your TTY Status", TRUE); #endif if (PMSG) { printf("\nUMODEM: TTY Device Parameters Altered"); ttyparams(); /* print tty params */ } if (ARPA) /* set 8-bit on ARPA Net */ setarpa(); return; } /* set ARPA Net for 8-bit transfers */ setarpa() { sendbyte(IAC); /* Is A Command */ sendbyte(WILL); /* Command to SERVER TELNET (Host) */ sendbyte(TRBIN); /* Command is: Transmit Binary */ sendbyte(IAC); /* Is A Command */ sendbyte(DO); /* Command to TIP */ sendbyte(TRBIN); /* Command is: Transmit Binary */ sleep(3); /* wait for TIP to configure */ return; } /* restore normal tty modes */ restoremodes(errcall) int errcall; { if (ARPA) /* if ARPA Net, reconfigure */ resetarpa(); /* Device characteristic restoration for JHU UNIX */ #ifdef JHU if (wason) /* if messages were on originally */ if (chmod(tty, 020611) < 0) /* [SD] */ error("Can't change TTY Mode", FALSE); if (stty(0, &ttys) < 0) /* restore original tty modes */ { if (!errcall) error("RESET - Can't restore normal TTY Params", FALSE); else { printf("UMODEM: "); printf("RESET - Can't restore normal TTY Params\n"); } } #endif /* Device characteristic restoration for Version 7 UNIX */ #ifdef VER7 if (ioctl(0,TIOCSETP,&ttys) < 0) { if (!errcall) error("RESET - Can't restore normal TTY Params", FALSE); else { printf("UMODEM: "); printf("RESET - Can't restore normal TTY Params\n"); } } #endif if (PMSG) { printf("\nUMODEM: TTY Device Parameters Restored"); ttyparams(); /* print tty params */ } return; } /* reset the ARPA Net */ resetarpa() { sendbyte(IAC); /* Is A Command */ sendbyte(WONT); /* Negative Command to SERVER TELNET (Host) */ sendbyte(TRBIN); /* Command is: Don't Transmit Binary */ sendbyte(IAC); /* Is A Command */ sendbyte(DONT); /* Negative Command to TIP */ sendbyte(TRBIN); /* Command is: Don't Transmit Binary */ return; } /* print error message and exit; if mode == TRUE, restore normal tty modes */ error(msg, mode) char *msg; int mode; { if (mode) restoremodes(TRUE); /* put back normal tty modes */ printf("UMODEM: %s\n", msg); if (LOGFLAG) { fprintf(LOGFP, "UMODEM Fatal Error: %s\n", msg); fclose(LOGFP); } exit(-1); } /** print status (size) of a file **/ yfile(name) char *name; { printf("UMODEM File Status Display for %s\n", name); if (LOGFLAG) fprintf(LOGFP,"UMODEM File Status Display for %s\n", name); if (open(name,0) < 0) { printf("File %s does not exist\n", name); if (LOGFLAG) fprintf(LOGFP,"File %s does not exist\n", name); exit(-1); /* error exit to UNIX */ } prfilestat(name); /* print status */ printf("\n"); if (LOGFLAG) { fprintf(LOGFP,"\n"); fclose(LOGFP); } exit(0); /* exit to UNIX -- no error */ } /** receive a file **/ rfile(name) char *name; { char mode; int fd, j, firstchar, sectnum, sectcurr, tmode; int sectcomp, errors, errorflag, recfin; register int bufctr, checksum; register int c; int errorchar, fatalerror, startstx, inchecksum, endetx, endenq; long recvsectcnt; mode = XMITTYPE; /* set t/b mode */ if ((fd = creat(name, CREATMODE)) < 0) error("Can't create file for receive", FALSE); setmodes(); /* setup tty modes for xfer */ printf("\r\nUMODEM: File Name: %s", name); if (LOGFLAG) { fprintf(LOGFP, "\n----\nUMODEM Receive Function\n"); fprintf(LOGFP, "File Name: %s\n", name); if (FTP1) if (!BIT7) fprintf(LOGFP, "TERM II File Transfer Protocol 1 Selected\n"); else fprintf(LOGFP, "TERM II File Transfer Protocol 4 Selected\n"); else fprintf(LOGFP, "TERM II File Transfer Protocol 3 (CP/M UG) Selected\n"); if (BIT7) fprintf(LOGFP, "7-Bit Transmission Enabled\n"); else fprintf(LOGFP, "8-Bit Transmission Enabled\n"); } printf("\r\nUMODEM: "); if (BIT7) printf("7-Bit"); else printf("8-Bit"); printf(" Transmission Enabled"); printf("\r\nUMODEM: Ready to RECEIVE File\r\n"); recfin = FALSE; sectnum = errors = 0; fatalerror = FALSE; /* NO fatal errors */ recvsectcnt = 0; /* number of received sectors */ if (mode == 't') tmode = TRUE; else tmode = FALSE; if (FTP1) { while (readbyte(4) != SYN); sendbyte(ACK); /* FTP 1 Sync */ } else sendbyte(NAK); /* FTP 3 Sync */ do { errorflag = FALSE; do { firstchar = readbyte(6); } while ((firstchar != SOH) && (firstchar != EOT) && (firstchar != TIMEOUT)); if (firstchar == TIMEOUT) { if (LOGFLAG) fprintf(LOGFP, "Timeout on Sector %d\n", sectnum); errorflag = TRUE; } if (firstchar == SOH) { if (FTP1) readbyte(5); /* discard leading zero */ sectcurr = readbyte(delay); sectcomp = readbyte(delay); if (FTP1) startstx = readbyte(delay); /* get leading STX */ if ((sectcurr + sectcomp) == BITMASK) { if (sectcurr == ((sectnum+1)&BITMASK)) { checksum = 0; for (j = bufctr = 0; j < BBUFSIZ; j++) { buff[bufctr] = c = readbyte(delay); checksum = ((checksum+c)&BITMASK); if (!tmode) /* binary mode */ { bufctr++; continue; } if (c == CR) continue; /* skip CR's */ if (c == CTRLZ) /* skip CP/M EOF char */ { recfin = TRUE; /* flag EOF */ continue; } if (!recfin) bufctr++; } if (FTP1) endetx = readbyte(delay); /* get ending ETX */ inchecksum = readbyte(delay); /* get checksum */ if (FTP1) endenq = readbyte(delay); /* get ENQ */ if (checksum == inchecksum) /* good checksum */ { errors = 0; recvsectcnt++; sectnum = sectcurr; /* update sector counter */ if (write(fd, buff, bufctr) < 0) error("File Write Error", TRUE); else { if (FTP1) sendbyte(ESC); /* FTP 1 requires */ sendbyte(ACK); } } else { if (LOGFLAG) fprintf(LOGFP, "Checksum Error on Sector %d\n", sectnum); errorflag = TRUE; } } else { if (sectcurr == sectnum) { while(readbyte(3) != TIMEOUT); if (FTP1) sendbyte(ESC); /* FTP 1 requires */ sendbyte(ACK); } else { if (LOGFLAG) { fprintf(LOGFP, "Phase Error - Received Sector is "); fprintf(LOGFP, "%d while Expected Sector is %d\n", sectcurr, ((sectnum+1)&BITMASK)); } errorflag = TRUE; fatalerror = TRUE; if (FTP1) sendbyte(ESC); /* FTP 1 requires */ sendbyte(CAN); } } } else { if (LOGFLAG) fprintf(LOGFP, "Header Sector Number Error on Sector %d\n", sectnum); errorflag = TRUE; } } if (FTP1 && !errorflag) { if (startstx != STX) { errorflag = TRUE; /* FTP 1 STX missing */ errorchar = STX; } if (endetx != ETX) { errorflag = TRUE; /* FTP 1 ETX missing */ errorchar = ETX; } if (endenq != ENQ) { errorflag = TRUE; /* FTP 1 ENQ missing */ errorchar = ENQ; } if (errorflag && LOGFLAG) { fprintf(LOGFP, "Invalid Packet-Control Character: "); switch (errorchar) { case STX : fprintf(LOGFP, "STX"); break; case ETX : fprintf(LOGFP, "ETX"); break; case ENQ : fprintf(LOGFP, "ENQ"); break; default : fprintf(LOGFP, "Error"); break; } fprintf(LOGFP, "\n"); } } if (errorflag == TRUE) { errors++; while (readbyte(3) != TIMEOUT); sendbyte(NAK); } } while ((firstchar != EOT) && (errors != ERRORMAX) && !fatalerror); if ((firstchar == EOT) && (errors < ERRORMAX)) { if (!FTP1) sendbyte(ACK); close(fd); restoremodes(FALSE); /* restore normal tty modes */ if (FTP1) while (readbyte(3) != TIMEOUT); /* flush EOT's */ sleep(3); /* give other side time to return to terminal mode */ if (LOGFLAG) { fprintf(LOGFP, "\nReceive Complete\n"); fprintf(LOGFP,"Number of Received CP/M Records is %ld\n", recvsectcnt); fclose(LOGFP); } printf("\n"); exit(0); } else { if (LOGFLAG && FTP1 && fatalerror) fprintf(LOGFP, "Synchronization Error"); error("TIMEOUT -- Too Many Errors", TRUE); } } /** send a file **/ sfile(name) char *name; { char mode; int fd, charval, attempts; int nlflag, sendfin, tmode; register int bufctr, checksum, sectnum; char c; int sendresp; /* response char to sent block */ mode = XMITTYPE; /* set t/b mode */ if ((fd = open(name, 0)) < 0) { if (LOGFLAG) fprintf(LOGFP, "Can't Open File\n"); error("Can't open file for send", FALSE); } setmodes(); /* setup tty modes for xfer */ printf("\r\nUMODEM: File Name: %s", name); if (LOGFLAG) { fprintf(LOGFP, "\n----\nUMODEM Send Function\n"); fprintf(LOGFP, "File Name: %s\n", name); } prfilestat(name); /* print file size statistics */ if (LOGFLAG) { if (FTP1) if (!BIT7) fprintf(LOGFP, "TERM II File Transfer Protocol 1 Selected\n"); else fprintf(LOGFP, "TERM II File Transfer Protocol 4 Selected\n"); else fprintf(LOGFP, "TERM II File Transfer Protocol 3 (CP/M UG) Selected\n"); if (BIT7) fprintf(LOGFP, "7-Bit Transmission Enabled\n"); else fprintf(LOGFP, "8-Bit Transmission Enabled\n"); } printf("\r\nUMODEM: "); if (BIT7) printf("7-Bit"); else printf("8-Bit"); printf(" Transmission Enabled"); printf("\r\nUMODEM: Ready to SEND File\r\n"); if (mode == 't') tmode = TRUE; else tmode = FALSE; sendfin = nlflag = FALSE; attempts = 0; if (FTP1) { sendbyte(SYN); /* FTP 1 Synchronize with Receiver */ while (readbyte(5) != ACK) { if(++attempts > RETRYMAX*6) error("Remote System Not Responding", TRUE); sendbyte(SYN); } } else { while (readbyte(30) != NAK) /* FTP 3 Synchronize with Receiver */ if (++attempts > RETRYMAX) error("Remote System Not Responding", TRUE); } sectnum = 1; /* first sector number */ attempts = 0; do { for (bufctr=0; bufctr < BBUFSIZ;) { if (nlflag) { buff[bufctr++] = LF; /* leftover newline */ nlflag = FALSE; } if ((charval = read(fd, &c, 1)) < 0) error("File Read Error", TRUE); if (charval == 0) /* EOF for read */ { sendfin = TRUE; /* this is the last sector */ if (!bufctr) /* if EOF on sector boundary */ break; /* avoid sending extra sector */ if (tmode) buff[bufctr++] = CTRLZ; /* Control-Z for CP/M EOF */ else bufctr++; continue; } if (tmode && c == LF) /* text mode & Unix newline? */ { if (c == LF) /* Unix newline? */ { buff[bufctr++] = CR; /* insert carriage return */ if (bufctr < BBUFSIZ) buff[bufctr++] = LF; /* insert Unix newline */ else nlflag = TRUE; /* insert newline on next sector */ } continue; } buff[bufctr++] = c; /* copy the char without change */ } attempts = 0; if (!bufctr) /* if EOF on sector boundary */ break; /* avoid sending empty sector */ do { sendbyte(SOH); /* send start of packet header */ if (FTP1) sendbyte(0); /* FTP 1 Type 0 Packet */ sendbyte(sectnum); /* send current sector number */ sendbyte(-sectnum-1); /* and its complement */ if (FTP1) sendbyte(STX); /* send STX */ checksum = 0; /* init checksum */ for (bufctr=0; bufctr < BBUFSIZ; bufctr++) { sendbyte(buff[bufctr]); /* send the byte */ if (ARPA && (buff[bufctr]==0xff)) /* ARPA Net FFH esc */ sendbyte(buff[bufctr]); /* send 2 FFH's for one */ checksum = ((checksum+buff[bufctr])&BITMASK); } /* while (readbyte(3) != TIMEOUT); flush chars from line */ if (FTP1) sendbyte(ETX); /* send ETX */ sendbyte(checksum); /* send the checksum */ if (FTP1) sendbyte(ENQ); /* send ENQ */ attempts++; if (FTP1) { sendresp = NAK; /* prepare for NAK */ if (readbyte(10) == ESC) sendresp = readbyte(10); } else sendresp = readbyte(10); /* get response */ if ((sendresp != ACK) && LOGFLAG) { fprintf(LOGFP, "Non-ACK Received on Sector %d\n", sectnum); if (sendresp == TIMEOUT) fprintf(LOGFP, "This Non-ACK was a TIMEOUT\n"); } } while((sendresp != ACK) && (attempts != RETRYMAX)); sectnum++; /* increment to next sector number */ } while (!sendfin && (attempts != RETRYMAX)); if (attempts == RETRYMAX) error("Remote System Not Responding", TRUE); attempts = 0; if (FTP1) while (attempts++ < 10) sendbyte(EOT); else { sendbyte(EOT); /* send 1st EOT */ while ((readbyte(15) != ACK) && (attempts++ < RETRYMAX)) sendbyte(EOT); if (attempts >= RETRYMAX) error("Remote System Not Responding on Completion", TRUE); } close(fd); restoremodes(FALSE); sleep(5); /* give other side time to return to terminal mode */ if (LOGFLAG) { fprintf(LOGFP, "\nSend Complete\n"); fclose(LOGFP); } printf("\n"); exit(0); } /* print file size status information */ prfilestat(name) char *name; { struct stat filestatbuf; /* file status info */ stat(name, &filestatbuf); /* get file status bytes */ printf("\r\nUMODEM: Estimated File Size %ldK, %ld Records, %ld Bytes", (filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1, filestatbuf.st_size); if (LOGFLAG) fprintf(LOGFP,"Estimated File Size %ldK, %ld Records, %ld Bytes\n", (filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1, filestatbuf.st_size); return; } /* get a byte from data stream -- timeout if "seconds" elapses */ readbyte(seconds) unsigned seconds; { char c; signal(SIGALRM,alarmfunc); /* catch alarms */ alarm(seconds); /* set the alarm clock */ if (read(0, &c, 1) < 0) /* get a char; error means we timed out */ { return(TIMEOUT); } alarm(0); /* turn off the alarm */ return((c&BITMASK)); /* return the char */ } /* send a byte to data stream */ sendbyte(data) char data; { char dataout; dataout = (data&BITMASK); /* mask for 7 or 8 bits */ write(1, &dataout, 1); /* write the byte */ return; } /* function for alarm clock timeouts */ alarmfunc() { return; /* this is basically a dummy function to force error */ /* status return on the "read" call in "readbyte" */ } /* print data on TTY setting */ ttyparams() { /* Obtain TTY parameters for JHU UNIX */ #ifdef JHU gtty(0, &ttystemp); /* get current tty params */ #endif /* Obtain TTY parameters for Version 7 UNIX */ #ifdef VER7 ioctl(0,TIOCGETP,&ttystemp); #endif tty = ttyname(0); /* get name of tty */ stat(tty, &statbuf); /* get more tty params */ printf("\r\n\nTTY Device Parameter Display"); printf("\r\n\tTTY Device Name is %s\r\n\n", tty); printf("\tAny Parity Allowed "); pryn(ANYP); printf("\tEven Parity Allowed"); pryn(EVENP); printf("\tOdd Parity Allowed "); pryn(ODDP); printf("\tEcho Enabled "); pryn(ECHO); printf("\tLower Case Map "); pryn(LCASE); printf("\tTabs Expanded "); pryn(XTABS); printf("\tCR Mode Enabled "); pryn(CRMOD); printf("\tRAW Mode Enabled "); pryn(RAW); /* Print extended terminal characteristics for JHU UNIX */ #ifdef JHU printf("\tBinary Mode Enabled"); pryn1(NB8); printf("\tCR/LF in Col 72 "); pryn1(FOLD); printf("\tRecognize ^S/^Q "); pryn1(STALL); printf("\tSend ^S/^Q "); pryn1(TAPE); printf("\tTerminal can BS "); pryn1(SCOPE); printf("\r\n"); /* New line to separate topics */ printf("\tTerminal Paging is "); pryn1(PAGE); if (ttystemp.xflags&PAGE) printf("\t Lines/Page is %d\r\n", ttystemp.xflags&PAGE); printf("\r\n"); /* New line to separate topics */ printf("\tTTY Input Rate : "); prbaud(ttystemp.ispeed); /* print baud rate */ printf("\tTTY Output Rate : "); prbaud(ttystemp.ospeed); /* print output baud rate */ printf("\r\n"); /* New line to separate topics */ printf("\tMessages Enabled "); if (statbuf.st_mode&011) printf(": Yes\r\n"); else printf(": No\r\n"); #endif /* Print extended characteristics for Version 7 UNIX */ #ifdef VER7 printf("\tTTY Input Rate : "); prbaud(ttystemp.sg_ispeed); printf("\tTTY Output Rate : "); prbaud(ttystemp.sg_ospeed); /* print output baud rate */ #endif } pryn(iflag) int iflag; { /* JHU UNIX flag test */ #ifdef JHU if (ttystemp.mode&iflag) #endif /* Version 7 UNIX flag test */ #ifdef VER7 if (ttystemp.sg_flags&iflag) #endif printf(": Yes\r\n"); else printf(": No\r\n"); } /* Extended flag test for JHU UNIX only */ #ifdef JHU pryn1(iflag) int iflag; { if (ttystemp.xflags&iflag) printf(": Yes\r\n"); else printf(": No\r\n"); } #endif prbaud(speed) char speed; { switch (speed) { /* JHU UNIX speed flag cases */ #ifdef JHU case B0050 : printf("50"); break; case B0075 : printf("75"); break; case B0110 : printf("110"); break; case B0134 : printf("134.5"); break; case B0150 : printf("150"); break; case B0200 : printf("200"); break; case B0300 : printf("300"); break; case B0600 : printf("600"); break; case B1200 : printf("1200"); break; case B1800 : printf("1800"); break; case B2400 : printf("2400"); break; case B4800 : printf("4800"); break; case B9600 : printf("9600"); break; case EXT_A : printf("External A"); break; case EXT_B : printf("External B"); break; #endif /* Version 7 UNIX speed flag cases */ #ifdef VER7 case B50 : printf("50"); break; case B75 : printf("75"); break; case B110 : printf("110"); break; case B134 : printf("134.5"); break; case B150 : printf("150"); break; case B200 : printf("200"); break; case B300 : printf("300"); break; case B600 : printf("600"); break; case B1200 : printf("1200"); break; case B1800 : printf("1800"); break; case B2400 : printf("2400"); break; case B4800 : printf("4800"); break; case B9600 : printf("9600"); break; case EXTA : printf("External A"); break; case EXTB : printf("External B"); break; #endif default : printf("Error"); break; } printf(" Baud\r\n"); }