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