1 /* ::[[ @(#) flip.c 1.18 89/07/04 16:07:16 ]]:: */
2 #ifndef LINT
3 static char sccsid[]="::[[ @(#) flip.c 1.18 89/07/04 16:07:16 ]]::";
4 #endif
5 
6 /*
7 Copyright 1989 Rahul Dhesi, All rights reserved.
8 
9 Checksum: 1217582374 (check or update with "brik")
10 */
11 
12 /*
13 Does newline conversions between **IX and MS-DOS conventions.  Uses a state
14 machine, so repeated conversions on the same file will do no harm.
15 Assumes the US ASCII character set in some places (search for 'ASCII').
16 */
17 
18 /* change contents of flip.h as needed */
19 #include "flip.h"
20 
21 enum choices   { MSTOIX, IXTOMS, NEITHER };           /* conversion choices */
22 enum state     { NONE, SAWCR, SAWLF, SAWCRLF, SAWCTRLZ };
23 
24 void usage PARMS ((void));
25 void give_help PARMS ((void));
26 void flip_exit PARMS ((int));
27 int getopt PARMS ((int argc, char **argv, char *options));
28 void doarg PARMS ((char *, enum choices));
29 int dofile PARMS ((char *, enum choices));
30 char *nextfile PARMS ((int, char *, int));
31 int ixtoms PARMS ((FILE *, FILE *));
32 int mstoix PARMS ((FILE *, FILE *));
33 void error PARMS ((char *, char *));
34 void setup_sigs PARMS ((void));
35 void cleanup PARMS ((int));
36 int mkstemp PARMS ((char *));
37 
38 #ifdef STDINCLUDE
39 # include <stdlib.h>
40 # include <string.h>
41 #else
42 void *malloc PARMS ((int));
43 void exit PARMS ((int));
44 char *strcpy PARMS ((char *, char *));
45 #endif
46 
47 #ifdef IX
48 #include <sys/types.h>
49 #include <sys/stat.h>		/* stat and chmod */
50 #include <unistd.h>
51 #endif
52 
53 #ifdef NDEBUG
54 # define assert(c)
55 #else
56 # ifdef STDINCLUDE
57 #  include <assert.h>
58 # else
59 #  define assert(c)  if(!(c)) \
60                   fprintf(stderr,"assert error %s:%d\n",__FILE__,__LINE__)
61 # endif /* STDINCLUDE */
62 #endif /* NDEBUG */
63 
64 #ifdef USE_TABLE
65 char *bintab;
66 #endif
67 
68 #ifdef USE_SIG
69 int got_sig;               /* will indicate if signal received */
70 #endif
71 
72 char *myname = NULL;
73 
74 int exitstat = 0;          /* exit status */
75 int verbose = 0;           /* set by -v option */
76 int touch = 0;             /* set by -t option */
77 int strip = 0;             /* set by -s option */
78 int bintoo = 0;            /* set by -b option */
79 int ztrunc = 0;            /* set by -z option */
80 int use_stdio = 0;	   /* set by "-" filename */
81 
main(argc,argv)82 main (argc, argv)
83 int argc;
84 char **argv;
85 
86 {
87    int option;
88    extern int optind;
89    int i;
90    enum choices which = NEITHER;
91 #ifdef USE_TABLE
92 #define TABSIZ    256
93    char table[TABSIZ];
94 #endif
95 
96 #ifdef PICKNAME
97    register char *p; /* temp pointer for finding our name */
98    register char *arg0 = *argv;
99 #endif /* PICKNAME */
100 
101    SPEC_INIT            /* optional initialization */
102 
103 #ifdef USE_SIG
104    setup_sigs();
105 #endif
106 
107 #ifdef PICKNAME
108 # define STRCMP(a,op,b)    (strcmp(a,b) op 0)
109    p = arg0 + strlen(arg0);
110 
111    if (p != arg0) {     /* if program name is defined */
112       while (p != arg0 && *p != '/' && *p != '\\')
113          p--;
114       assert ((p - arg0) <= strlen (arg0));
115       if (p != arg0)
116          p++;
117 
118       /* p now points to trailing name component, or nothing */
119       myname = p;
120       while (*p != '\0' && *p != '.') {   /* ASCII convert to lowercase */
121          if (*p >= 'A' && *p <= 'Z') {
122             *p = (*p - 'A' + 'a');
123          }
124          p++;
125       }
126       if (p != myname && *p == '.')
127          *p = '\0';     /* remove trailing .exe or .com under MS-DOS etc. */
128 
129       if (STRCMP(myname,==,"toix"))
130          which = MSTOIX;
131       else if (STRCMP(myname,==,"toms"))
132          which = IXTOMS;
133    } else
134    myname = "flip";
135 #else
136    myname = "flip";
137 #endif /* PICKNAME */
138 
139 argv[0] = myname;                /* for use by getopt */
140 
141 #ifdef USE_TABLE
142 /* table to find out which characters are binary */
143    for (i = 0;  i < TABSIZ;  i++) {
144       if ( (i < 7) || (i > 13 && i < 26) || (i > 126)) /*ASCII binary chars*/
145          table[i] = 1;
146       else
147          table[i] = 0;
148    }
149    bintab = table;
150 #endif /* USE_TABLE */
151 
152    if (argc < 2) {
153       usage();
154       flip_exit (1);
155    }
156 
157    while ((option = getopt (argc, argv, "umhvtsbz")) != EOF) {
158       switch (option) {
159        case 'u': which = MSTOIX;  break;
160        case 'm': which = IXTOMS; break;
161        case 'h': give_help(); flip_exit (0);
162        case 'v': verbose = 1;  break;
163        case 't': touch = 1;  break;
164        case 's': strip = 1;  break;
165        case 'b': bintoo = 1;  break;
166        case 'z': ztrunc = 1;  break;
167        default:  usage(); flip_exit (1);
168       }
169    }
170 
171    switch (which) {
172     case MSTOIX:
173       /* printf ("converting to **ix format\n"); */
174       break;
175     case IXTOMS:
176       /* printf ("converting to msdos format\n"); */
177       break;
178     default:
179       fprintf (stderr, "%s: error: -u or -m is required\n", myname);
180       flip_exit (1);
181       break;
182    }
183 
184    if (argc <= optind) {
185       fprintf (stderr, "%s: error: filenames are needed\n", myname);
186       flip_exit (1);
187    }
188 
189    for (i = optind;  i < argc;  i++)
190       doarg (argv[i], which);
191 
192    return (exitstat);
193 }
194 
195 
196 /*
197 Does conversion for one argument, calling dofile with wildcards
198 expanded.  Updates exitstat in case of error.
199 */
200 
doarg(arg,which)201 void doarg (arg, which)
202 char *arg;
203 enum choices which;
204 {
205 #ifdef WILDCARD
206    char *this_file;
207 
208    nextfile (0, arg, 0);
209    while ((this_file = nextfile(1, (char *) NULL, 0)) != NULL) {
210       exitstat |= dofile (this_file, which);
211    }
212 #else
213    exitstat |= dofile (arg, which);
214 #endif /* WILDCARD */
215 }
216 
217 #ifdef USE_SIG
218 # include <signal.h>
219 #endif
220 
221 FILE *outfile;       /* make it visible to both dofile and cleanup */
222 
223 /*
224 Here we have filename and an option.  We call a different routine
225 for each type of conversion.  This way more types of conversions
226 can be easily added in the future.
227 */
228 
dofile(fname,which)229 int dofile (fname, which)
230 char *fname;
231 enum choices which;
232 {
233    FILE *infile;
234    char tfname[PATHSIZE];
235    SGFTIME timestamp;  /* save file timestamp here, restore later */
236    int errstat = 0;
237    char *p;             /* temp file ptr */
238 #ifdef IX
239    struct stat ifilestat;
240 #endif
241 
242 #ifdef USE_SIG
243    if (got_sig)
244       flip_exit (INT_EXIT);
245 #endif
246 
247    if(STRCMP(fname,==,"-"))
248       use_stdio = 1;
249    if(!use_stdio) {
250       /* if writable, open for reading */
251       if ((infile = fopen (fname, R_PL_B)) != NULL) {
252          fclose (infile);
253          infile = fopen (fname, RB);
254 #ifdef IX
255 	 if (stat (fname, &ifilestat)) {
256 	   /* can't get the file's permissions */
257 	   char *buf = malloc(strlen(fname)+strlen(myname)+3);
258 	   if (buf) {
259 	     sprintf(buf, "%s: %s", myname, fname);
260 	     perror(buf);
261 	   }
262 	   ifilestat.st_mode = 0644;
263 	 }
264 #endif
265       }
266    } else {
267       infile = stdin;
268    }
269 
270    if (infile == NULL) {
271       error (fname, ": can't open");
272       return (1);
273    }
274    if(use_stdio) {
275      outfile = stdout;
276    } else {
277      /*
278        to make temp file in same dir as original, we make p point to
279        the filename component of fname, put '\0' there, and strcat the
280        temp name to it */
281      strcpy (tfname, fname);
282      p = tfname + strlen(tfname);
283 
284      while (p != tfname && *p != '/' && *p != '\\')
285        p--;
286      if (p != tfname)
287        p++;
288      *p = '\0';
289 
290 #define  TEMPLATE    "XXXXXX"
291 #ifdef IX
292      strcat (tfname, TEMPLATE);
293 
294      {
295        int fd = mkstemp(tfname);
296        if (fd == -1 || (outfile = fdopen(fd, WB)) == NULL) {
297 	 fclose (infile);
298 	 error (fname, ": skipped, could not open temporary file");
299 	 return (1);
300        }
301      }
302 #else
303      {
304        char template[7];
305        strcpy (template, TEMPLATE);
306        strcat (tfname, mktemp (template));
307      }
308 
309      outfile = fopen (tfname, WB);
310      if (outfile == NULL) {
311        fclose (infile);
312        error (fname, ": skipped, could not open temporary file");
313        return (1);
314      }
315 #endif
316    }
317 
318    if (!touch)
319       GETFT (infile, fname, timestamp);      /* save current timestamp */
320 
321    assert (which == IXTOMS || which == MSTOIX);
322 
323 #ifdef BIGBUF
324    setvbuf (infile,  (char *) NULL, _IOFBF, BIGBUF);
325    setvbuf (outfile, (char *) NULL, _IOFBF, BIGBUF);
326 #endif /* BIGBUF */
327 
328    switch (which) {
329     case IXTOMS:
330       errstat = ixtoms (infile, outfile);
331       break;
332     case MSTOIX:
333       errstat = mstoix (infile, outfile);
334       break;
335    }
336 
337    fclose (infile);
338 
339    switch (errstat) {
340     case ERRBINF:
341       fclose (outfile);
342       DELFILE (tfname);
343       fprintf (stderr, "%s: binary file, not converted\n", fname);
344       return (1);
345       /* break; */  /* unreachable code */
346 #ifdef USE_SIG
347     case ERRSIG:
348       fclose (outfile);
349       DELFILE (tfname);
350       flip_exit (INT_EXIT);
351       break;
352 #endif
353     default:
354       ;
355    }
356 
357    assert (errstat == 0);
358 
359    if (!ferror(outfile) && fflush(outfile) != EOF && fclose(outfile) != EOF) {
360       int moved;
361       DELFILE (fname);
362       if (!use_stdio) {
363          moved = MVFILE (tfname, fname);
364          if (moved == 0) {
365             FILE *fptr;
366             if (!touch && (fptr = fopen (fname, RB)) != NULL) {
367                SETFT (fptr, fname, timestamp);
368                fclose (fptr);
369             }
370 #ifdef IX
371 	    if (chmod(fname, ifilestat.st_mode)) {
372 	      /* can't set the file's permissions */
373 	      char *buf = malloc(strlen(fname)+strlen(myname)+3);
374 	      if (buf) {
375 		sprintf(buf, "%s: %s", myname, fname);
376 		perror(myname);
377 	      }
378 	    }
379 #endif
380             if (verbose) {
381                printf ("%s\n", fname);
382                fflush (stdout);
383             }
384             return (0);
385          } else {
386             error (fname, ": not converted, could not rename temp file");
387             DELFILE (tfname);
388             return (1);
389          }
390       }
391    } else {
392       fclose (outfile); /* outfile was not closed, so close it here */
393       return (1);
394    }
395 }
396 
397 /* convert from ms-dos to **ix format */
mstoix(infile,outfile)398 int mstoix (infile, outfile)
399 FILE *infile;           /* input file   */
400 FILE *outfile;          /* output file  */
401 {
402    int c;
403    enum state state = NONE;
404 
405    /* lone LF => unchanged, lone CR => unchanged,
406       CR LF => LF, ^Z at end means EOF; ^Z elsewhere => unchanged */
407 
408    while (1) {       /* break out on EOF only */
409       while ((c = getc (infile)) != EOF) {
410 #ifdef USE_SIG
411          if (got_sig)
412             return (ERRSIG);
413 #endif
414          if (!bintoo && BINCHAR(c))
415             return (ERRBINF);
416          if (strip)
417             STRIP(c);
418          switch (c) {
419           case LF:
420             CHECK_BREAK
421             putc (c, outfile); if (state == SAWCR) state = NONE; break;
422           case CR:
423             state = SAWCR; break;
424           case CTRLZ:
425             if (state == SAWCR) putc (CR, outfile);
426             state = SAWCTRLZ; goto saweof;
427           default:
428             if (state == SAWCR) { state = NONE; putc (CR, outfile); }
429             putc (c, outfile);
430             break;
431          }
432       }
433  saweof:
434       /* exit outer loop only on EOF or ^Z as last char */
435       if (
436           ztrunc && state == SAWCTRLZ
437           || (c = getc (infile)) == EOF
438          )
439          break;
440       else
441          ungetc (c, infile);
442       if (state == SAWCTRLZ)
443          putc (CTRLZ, outfile);
444    }
445    return (0);
446 }
447 
448 /* convert from **ix to ms-dos format */
ixtoms(infile,outfile)449 int ixtoms (infile, outfile)
450 FILE *infile;           /* input file   */
451 FILE *outfile;          /* output file  */
452 {
453    int c;
454    enum state state = NONE;
455 
456    /* LF => CR LF, but leave CR LF alone */
457    while ((c = getc (infile)) != EOF) {
458 #ifdef USE_SIG
459       if (got_sig)
460          return (ERRSIG);
461 #endif
462       if (!bintoo && BINCHAR(c))
463          return (ERRBINF);
464       if (strip)
465          STRIP(c);
466       switch (c) {
467        case LF:
468          CHECK_BREAK
469          if (state == SAWCR)
470             state = NONE;
471          else
472             putc (CR, outfile);
473          putc (LF, outfile);
474          break;
475        case CR:
476          state = SAWCR; putc (c, outfile); break;
477        case CTRLZ:
478          if (ztrunc)
479             return (0);
480          /* FALL THROUGH */
481        default:
482          state = NONE; putc (c, outfile); break;
483       }
484    }
485    return (0);
486 }
487 
488 /* set up signal handler for selected signals */
489 #ifdef USE_SIG
setup_sigs()490 void setup_sigs ()
491 {
492 # ifdef SIGPIPE
493    if (signal (SIGPIPE, SIG_IGN) != SIG_IGN)
494       signal (SIGPIPE, cleanup);
495 # endif
496 # ifdef SIGHUP
497    if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
498       signal (SIGHUP, cleanup);
499 # endif
500 # ifdef SIGQUIT
501    if (signal (SIGQUIT, SIG_IGN) != SIG_IGN)
502       signal (SIGQUIT, cleanup);
503 # endif
504 # ifdef SIGINT
505    if (signal (SIGINT, SIG_IGN) != SIG_IGN)
506       signal (SIGINT, cleanup);
507 # endif
508 # ifdef SIGTERM
509    if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
510       signal (SIGTERM, cleanup);
511 # endif
512 }
513 
514 /* set flag on signal */
cleanup(sig)515 void cleanup(sig)
516 int sig;
517 {
518    signal (sig, SIG_IGN);     /* needed for flaky System V Release 2 */
519    got_sig = 1;
520    signal (sig, cleanup);     /* ditto */
521 }
522 #endif /* USE_SIG */
523 
524 #define  ERRSIZE     200
525 
526 /* prints error message via perror */
error(msg1,msg2)527 void error (msg1, msg2)
528 char *msg1, *msg2;
529 {
530    char buf[ERRSIZE];
531    strcpy (buf, myname);
532    strcat (buf, ": ");
533    strcat (buf, msg1);
534    strcat (buf, msg2);
535    perror (buf);
536    fflush (stderr);
537 }
538 
539 /* gives brief usage message */
usage()540 void usage()
541 {
542    fprintf (stderr,
543 "usage:  %s [-u | -m] [other options] file ...  (or \"%s -h\" for more help)\n",
544  myname, myname);
545 }
546 
547 /* gives help screen */
548 
give_help()549 void give_help()
550 {
551 printf ("\
552 File interchange program flip version " VERSION ".  Copyright 1989 Rahul Dhesi,\n\
553 All rights reserved.  Both noncommercial and commercial copying, use, and\n\
554 creation of derivative works are permitted in accordance with the\n\
555 requirements of the GNU license.  This program does newline conversions.\n");
556 
557 printf ("\n\
558    Usage:     flip -umhvtsbz file ...\n\
559 \n\
560 One of -u, -m, or -h is required;  others are optional.  See user manual.\n");
561 
562 printf ("\n\
563    -u     convert to **IX format (CR LF => LF, lone CR or LF unchanged,\n\
564           trailing control Z removed, embedded control Z unchanged)\n\
565    -m     convert to MS-DOS format (lone LF => CR LF, lone CR unchanged)\n\
566    -h     give this help message\n\
567    -v     be verbose, print filenames as they are processed\n\
568    -t     touch files (don't preserve timestamps)\n\
569    -s     strip high bit\n\
570    -b     convert binary files too (else binary files are left unchanged)\n\
571    -z     truncate file at first control Z encountered\n\
572 ");
573 #ifdef PICKNAME
574 printf ("\n\
575 May be invoked as \"toix\" (same as \"flip -u\") or \"toms\" (same as \"flip -m\").\n\
576 ");
577 #endif
578 return;
579 }
580 
581 /* normal exits go through here -- atexit would be nice but not portable */
flip_exit(stat)582 void flip_exit(stat)
583 int stat;
584 {
585    SPEC_EXIT
586    exit (stat);
587 }
588 
589 /*
590 special code for **IX -- not in use because can't properly handle
591 SIGINT while /bin/mv is executing
592 */
593 
594 #ifdef NIX
595 # ifndef MVFILE
MVFILE(src,dest)596 int MVFILE (src, dest)
597 char *dest;
598 char *src;
599 {
600    char cmd[2 * PATHSIZE];
601    sprintf (cmd, "/bin/mv %s %s", src, dest);
602    return (system(cmd));
603 }
604 # endif /* MVFILE */
605 #endif /* NIX */
606