#ifdef ournix #include "ournix.h" #endif char sccsID[] = "@(#) rev.c V1.2 Copyright Julian H. Stacey, Munich, 16th June 1988\n" ; /* | Function: Reverses characters within lines. | Works on multiple files, write result back to file, | Works as a pipe if no arguments. | Later: Add a temp directory flag. Add mktemp() | Return Status: 0 if no errors, else number of files that failed. | Status: Seems to work, not rigorously tested though. | Bugs: Says line is too long if last line does not end with \n |Incompatabilities: Output of BSD utility of named files is to stdout, | this utility isnt. */ #include #if ( MSDOS || __OpenBSD__ ) #include #endif #ifdef i386 /* { */ #include #endif /* } */ #include #include #ifdef ns32000 /* { */ #define BSD #endif /* } */ #ifdef BSD /* { */ #include #endif /* } */ #define BUFSIZE 4096 #define END_CH '\n' char **ARGV ; int error_count = 0 ; int src_fd ; int new_fd ; char buf_in[BUFSIZE + 1] ; char buf_out[BUFSIZE + 1] ; extern int errno ; #ifdef scs /* { */ extern char *mktemp() ; #define TMP_NAME "reXXXXXX" #else /* }{ */ #include #ifdef MSDOS /* { */ #define TMP_NAME "reXXXXXX" #else /* }{ */ #define TMP_NAME "rev.XXXXXX" #endif /* } */ #endif /* } */ #include /* for MAXPATHLEN */ #ifdef sco /* this sco may come from imake */ #define MAXPATHLEN PATHSIZE #endif char tmp_name[MAXPATHLEN] ; /* temporary file name */ void perror() ; main(argc, argv) int argc ; char **argv ; { ARGV = argv ; #ifdef VSL /* { */ #include "../../include/vsl.h" #endif /* } */ #ifdef DEBUG fprintf(stderr,"%s",sccsID) ; #endif while ((argc > 1) && (**++argv == '-')) { switch(*++*argv) { case 'c': /* reverse characters within line */ break ; case 'l' : fprintf(stderr, "%s: Reversing lines within file not yet implemented, exiting.\n", *ARGV); break ; case '?': printf("Syntax: %s [-P] [-c] [-l] [files]\n", *ARGV); exit(0) ; case 'P': printf("%s",sccsID); exit(0) ; default : fprintf(stderr, "%s: bad flag %c.\n",*ARGV,**argv) ; exit(1) ; } argc-- ; argv++ ; } if (argc == 1) exit(do_file((char *)0)) ; else while (--argc > 0) if (do_file(*argv++)) error_count++ ; exit(error_count) ; } /* Strategy: Reads input in large blocks, Writes output one line at a time. */ do_file(src_name) char *src_name ; { char *base_p, *top_p ; char *tmp_p ; int byte_count ; unsigned remnant = 0 ; /* number of bytes left over from last block read */ if (src_name == (char *)0) { /* no args ; pipe operation, standard input to output */ src_fd = 0 ; /* stdin */ new_fd = 1 ; /* stdout */ } else { /* 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 */ 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)) { fprintf(stderr, "%s: Program error, mktemp %s failed, exiting.\n", *ARGV,tmp_name); exit(++error_count); } (void)strcat(tmp_name,".tmp"); if ((src_fd = open(src_name,O_RDONLY)) == -1) { fprintf(stderr,"%s: Can't open %s.\n",*ARGV,src_name) ; return(1) ; } if ((new_fd = open(tmp_name,O_RDWR | O_CREAT /* | O_TRUNC */ , S_IREAD | S_IWRITE )) == -1) /* S_IREAD is read permission for owner */ { fprintf(stderr, "%s: Cant open temporary file %s, exiting.\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. */ } } /* strategy: increment base_p til a \n, reverse chars up to next \n, repeat */ while ( (byte_count = read(src_fd,buf_in + remnant, (int) (BUFSIZE - remnant) )) > 0) /* read blocks*/ { byte_count += remnant ; buf_in[byte_count] = '\0' ; for (base_p = top_p = buf_in ; (top_p < buf_in + byte_count) ; ) { if (*top_p == END_CH ) { if (rev_line(base_p,top_p -1)) return(1) ; base_p = ++top_p ; } else top_p++ ; } if (base_p == buf_in) { buf_in[byte_count] = '\0' ; fprintf(stderr,"%s: File %s, Line too long:\n %s\n", *ARGV, src_name, buf_in ) ; (void)close(src_fd) ; (void)close(new_fd) ; (void)unlink(tmp_name) ; return(1); /* dont exit, because next file might be more reasonable. */ } /* now collect remnant of input buffer excluding last \n, including last character in buffer, & move it down */ if ( base_p < ( buf_in + byte_count)) { (void)strcpy(buf_in,base_p); remnant = strlen(buf_in) ; } else remnant = 0 ; } /* Now copy temporary file back to source file */ if (src_name != (char *)0) { (void)close(src_fd) ; (void)close(new_fd) ; #ifndef BSD /* not necessary for BSD */ (void)unlink(src_name) ; #endif 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) */ { fprintf(stderr,"%s: moving %s to %s failed.\n", *ARGV, tmp_name, src_name ) ; perror(*ARGV); exit(++error_count); /* rename failure is serious, possible program error or other unexpected contingency, so dont just return, exit */ } } return(0) ; } /* Output chars starting from top_p, decrementing down to base_p inclusive return 0 == ok , 1 == error*/ rev_line(base_p,top_p) char *base_p, *top_p; { unsigned len ; char *current_p ; for (current_p = buf_out ; (top_p >= base_p); *current_p++ = *top_p--); *current_p++ = END_CH ; *current_p = '\0' ; len = strlen(buf_out) ; if (write(new_fd,buf_out,(int)len) < len) { fprintf(stderr,"%s: Failed to write %s.\n",*ARGV,tmp_name); (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) ; }