#ifdef ournix #include "ournix.h" #endif char sccsID[] = "@(#) 78bit.c V1.4 Copyright Julian H. Stacey, Munich, 25th June 1990\n" ; /* manual exists */ /* assume output stream will arrive eventually on a printer using the epson control sequence method of setting parity (ie ^[>). */ #include #if ( MSDOS || SVR2 || __FreeBSD__ || __OpenBSD__ ) /* { */ #include #endif /* } */ #ifdef M_XENIX /* SCO System V/386 Release 3.2V2.0*/ #include #define L_SET SEEK_SET /* This lousy SCO product has no symbolic links - hence no lstat */ #endif #include #include #ifdef ns32000 /* { */ #define BSD #endif /* } */ #ifdef __386BSD__ /* { */ #ifndef BSD #define BSD #endif /* } */ #endif /* } */ #ifdef BSD /* { */ #include #endif /* } */ #ifdef MSDOS /* { */ #include #endif /* } */ typedef char FLAG ; #define BUFSIZE 100000 /* was 4096 till 930929, when resume has about 1500 pcl control chars per line */ char **ARGV ; int exit_count = 0 ; int src_fd ; int new_fd ; char buf_in[BUFSIZE + 1] ; extern int errno ; FLAG preserve_date ; FLAG f_7_to_8 = 0 ; FLAG verbose ; #define ESC 0x1b #ifdef scs /* { */ extern char *mktemp() ; #define TMP_NAME "78XXXXXX" #else /* }{ */ #include #ifdef MSDOS /* { */ #define TMP_NAME "78XXXXXX" #else /* }{ */ #define TMP_NAME "78bit.XXXXXX" #endif /* } */ #endif /* } */ #include /* for MAXPATHLEN */ char tmp_name[MAXPATHLEN] ; /* temporary file name */ main(argc, argv) int argc ; char **argv ; { ARGV = argv ; #ifdef VSL /* { */ #include "../../include/vsl.h" #endif /* } */ while (--argc && ((**++argv == '-') || (**argv == '+'))) switch (*++*argv) { case '8': case 'r': /* convert escape sequences to use appropriate parity bits and null bytes*/ f_7_to_8 = 1 ; break ; case '7': /* convert null bytes & parirty bits to escape sequences */ /* initial condition: f_7_to_8 = 0 ; */ break ; case 'd': /* preserve date */ preserve_date = 1 ; break ; case 'v': /* verbose */ verbose = 1 ; break ; case 'V': /* version number identification */ printf("%s",sccsID); break ; default: fprintf(stderr, "Aborting, Syntax error, Correct Usage:\n\t%s%s\n" ,*ARGV, " [-d] [-r] [-v] [-V] [files]") ; exit(1) ; break ; } if (argc == 0) exit(do_file((char *)0)) ; else { argc++ ; argv-- ; while (--argc > 0) if (do_file(*++argv)) exit_count++ ; } exit(exit_count) ; } /* Strategy: Reads input in large blocks, Writes output one char at a time. */ do_file(src_name) char *src_name ; { #define INNOCUOUS ' ' char last_ch = INNOCUOUS ; char parity = 0 ; struct stat stat_buf ; int len1, len2 ; #ifdef BSD /* { */ time_t timep[2]; #endif /* } */ #ifdef MSDOS /* { */ struct utimbuf set_time ; #endif /* } */ char *base_p, *top_p ; char *tmp_p ; #ifdef MSDOS /* { */ char mv_cmd[500] ; #endif /* } */ int byte_count ; FLAG esoteric = 0 ; char ch = INNOCUOUS ; long line_no = 1L ; if (src_name == (char *)0) { /* no args ; pipe operation, standard input to output */ src_fd = 0 ; /* stdin */ new_fd = 1 ; /* stdout */ } else { if (verbose) { printf("%s",src_name); (void)fflush(stdout) ; } #ifdef unix /* { */ #ifndef M_XENIX /* { */ if ( lstat(src_name, &stat_buf) != 0) { fprintf(stderr, "%s: lstat failed on %s.\n", *ARGV,src_name); return(1) ; } if ( (stat_buf.st_nlink != 1 ) || ( (stat_buf.st_mode & S_IFMT ) != S_IFREG ) ) esoteric = 1 ; #endif /* } */ #endif /* } */ if ( stat(src_name, &stat_buf) != 0) { fprintf(stderr,"%s: stat failed on %s.\n", *ARGV,src_name); return(1) ; } if (stat_buf.st_size == (off_t)0 ) return(0) ; /* now create a name in the same directory as the source, so we can guarantee no linking across devices will be attempted */ (void)strcpy(tmp_name,src_name) ; /* to end of string JJ could use strrchr instead */ for ( tmp_p = tmp_name ; (*tmp_p++ != '\0') ; ) ; for ( tmp_p -= 1 ; (*--tmp_p != '/') #ifdef MSDOS && ( *tmp_p != '\\') #endif && ( tmp_p >= tmp_name) ; ) ; (void)strcpy(tmp_p + 1,TMP_NAME); if (NULL == mktemp(tmp_name)) { perror(*ARGV); fprintf(stderr, "%s: Program error, mktemp %s failed, exiting.\n", *ARGV,tmp_name); exit(++exit_count); } (void)strcat(tmp_name,".tmp"); if ((src_fd = open(src_name,O_RDWR #ifdef MSDOS /* { */ | O_BINARY #endif /* } */ )) == -1) { fprintf(stderr,"%s: Can't open %s.\n",*ARGV,src_name) ; return(1) ; } /* create output temporary file */ /* JJLATER malloc would be faster, & avoid using disc io if file is small but malloc wont allow much for msdos */ if ((new_fd = open(tmp_name,O_RDWR | O_CREAT /* | O_TRUNC */ #ifdef MSDOS /* { */ | O_BINARY #endif /* } */ , S_IREAD | S_IWRITE )) == -1) /* S_IREAD is read permission for owner */ { perror(*ARGV); fprintf(stderr, "%s: Cant open temporary file %s. Continuing.\n" , *ARGV,tmp_name) ; (void) close(src_fd) ; return(1) ; /* dont exit, as we just be out of unix inodes (or msdos directory entries in root directory) on this device, next argument may be a different file system, so keep trying. */ } } while ( (byte_count = read(src_fd,buf_in, (int)BUFSIZE ) ) > 0) { for (tmp_p = buf_in, top_p = buf_in + byte_count ; byte_count-- ; tmp_p++ ) { if (ch == '\n') line_no++ ; ch = *tmp_p ; if (f_7_to_8) { /* set \0 to null char, then set parity bits where relevant */ if (ch & 0x80) { fprintf(stderr, "%s: %s <%c>, (0x%x), line %ld\n,aborting %s.\n", *ARGV, "Error, parity bit in character" ,ch & 0x7f,(int)(ch & 0xff), line_no, src_name) ; (void) close(src_fd) ; (void) close(new_fd) ; (void) unlink(tmp_name) ; return(1); } if (last_ch == '\\') { if (ch == '0') out_ch(((char) 0)|parity) ; else if (ch == '\\') out_ch('\\'|parity) ; else{ /* default for other/unknown */ fprintf(stderr, "%s: %s %c%c in line %ld,aborting %s.\n", *ARGV, "Error, unrecognised sequence", '\\',ch,line_no, src_name) ; (void) close(src_fd) ; (void) close(new_fd) ; (void) unlink(tmp_name) ; return(1); } last_ch = INNOCUOUS ; } else if (last_ch == ESC) { /* test for parity on */ if (ch == '>') parity = 0x80 ; /* test for backslash */ else if (ch == '#') parity = 0 ; else if (ch == ESC) out_ch(ESC|parity) ; /* default for other/unknown */ else { out_ch(ESC | parity) ; out_ch(ch | parity) ; } last_ch = INNOCUOUS ; } else { /* no pre-existing memorised special conditions */ last_ch = ch ; if ((ch !='\\') && (ch != ESC)) out_ch(ch | parity) ; } } else { /* !f_7_to_8 */ /* remove parity bits where relevant, and use set/reset parity force escape sequence; and convert each null char to \\ */ if (ch & 0x80) { /* tell printer to assert 8th bit for all following data */ if (!parity) { /* parity on comand */ out_ch(ESC) ; out_ch('>') ; parity = 0x80 ; } ch &= 0x7f ; } else { if (parity) { /* parity off command */ out_ch(ESC) ; out_ch('#') ; parity = 0 ; } } if (ch == 0) { out_ch('\\') ; out_ch('0') ; } else if (ch == '\\') { out_ch('\\') ; out_ch('\\') ; } else if (ch == ESC) { out_ch(ESC) ; out_ch(ESC) ; } else out_ch(ch) ; } } if (!f_7_to_8) { /* flush */ if (parity) { /* parity off command */ out_ch(ESC) ; out_ch('#') ; } } else { /* f_7_to_8 */ if ((ch == ESC) || (ch == '\\')) { fprintf(stderr, "%s: %s %s in line %ld,aborting %s.\n", *ARGV, "Error, unmatched terminal ", (ch == ESC) ? "backslash" : "escape", line_no, src_name) ; (void) close(src_fd) ; (void) close(new_fd) ; (void) unlink(tmp_name) ; return(1); } } } if (src_name != (char *)0) { /* Now return data to source */ /* differentiate between regular file as opposed to the more esoteric cases of hard or symbolic link, character or block special, socket, or directory. */ if (!esoteric) { /* regular file, move file name */ (void) close(src_fd) ; (void) close(new_fd) ; #ifndef BSD /* not necessary for BSD */ (void) unlink(src_name) ; #endif #ifndef SVR2 /* { */ if ( rename(tmp_name,src_name) ) /* Microsoft C Compiler Version 3 manual says arguments should be the opposite way round to BSD, but V4 compiler produces code that works OK with BSD parameter order - thus this seems to be a bug/incompatability detected with V3 manual, (& possibly compiler, though untested) */ { perror(*ARGV); fprintf(stderr,"%s: moving %s to %s failed.\n", *ARGV, tmp_name, src_name ) ; exit(++exit_count); /* rename failure is serious, possible program error or other unexpected contingency, so dont just return, but exit */ } #else /* } { */ sprintf(mv_cmd,"mv %s %s",tmp_name, src_name); system(mv_cmd); #endif /* } */ } #ifndef MSDOS /* { */ else { /* esoteric file, copy data back */ if ((lseek(src_fd, (off_t)0, L_SET) != 0) || (lseek(new_fd, (off_t)0, L_SET) != 0)) { perror(*ARGV) ; fprintf(stderr,"%s: Fatal error, aborting.\n", *ARGV); exit(1); } while ( (byte_count = read(new_fd,buf_in,BUFSIZE) ) > 0) { if (write(src_fd,buf_in,byte_count) != byte_count ) { /* JJLATER we should restore source here to original name */ perror(*ARGV) ; fprintf(stderr, "%s: Fatal error, aborting.\n", *ARGV); exit(1); } } (void) close(src_fd) ; (void) close(new_fd) ; (void) unlink(tmp_name) ; } #endif /* } */ if (preserve_date) { /* set time stamp */ #ifdef MSDOS /* { */ set_time.actime = stat_buf.st_atime ; set_time.modtime = stat_buf.st_mtime ; if (utime(src_name,&set_time)) #endif /* } */ #ifdef BSD /* { */ timep[0] = stat_buf.st_atime ; timep[1] = stat_buf.st_mtime ; if (utime(src_name,timep)) #endif /* } */ { fprintf(stderr, "%s: Failed to retain old time stamp on %s.\n" ,*ARGV, src_name ) ; return(1) ; } } if (verbose) { len1 = len2 = strlen(src_name) ; #define BS (char)8 while (len1--) putchar(BS) ; for ( len1 = len2 ; len1-- ; putchar(' ') ) ; for ( len1 = len2 ; len1-- ; putchar(BS) ) ; (void)fflush(stdout) ; } } return(0) ; } out_ch(ch) char ch ; { if (write(new_fd,&ch,1) < 1) { perror(*ARGV); fprintf(stderr,"%s: Failed to write %d.\n",*ARGV,(int)ch); (void) close(src_fd); (void) close(new_fd); (void) unlink(tmp_name); return(1) ; /* dont exit, as error could have been because we couldnt create a large temp. file, & next argument may require a smaller temp file */ } return(0); }