]> cygwin.com Git - cygwin-apps/cygutils.git/blob - src/conv/conv.c
Convert many programs to GPLv3+.
[cygwin-apps/cygutils.git] / src / conv / conv.c
1 /**
2 * conv.c '\n' convertor
3 * based on hd2c 0.5.12 by Peter Hanecak (made 17.1.2001)
4 * Copyright 1997,.. by Peter Hanecak <hanecak@megaloman.com>.
5 * All rights reserved.
6 * based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997)
7 * Copyright 1997,.. by Peter Hanecak <hanecak@megaloman.com>.
8 * All rights reserved.
9 * Copyright 2001,2002,2005,2009 by Charles Wilson
10 * All rights reserved.
11 *
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 *
25 * See the COPYING file for full license information.
26 */
27
28 #if HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include "common.h"
33
34 static const char versionID[] = PACKAGE_VERSION;
35 static const char revID[] =
36 "$Id$";
37 static const char copyrightID[] =
38 "Copyright (c) 2009\nCharles S. Wilson. All rights reserved.\nLicensed under GPLv3+\n";
39
40 #define UNIX2DOS_NAME_S "u2d"
41 #define UNIX2DOS_NAME_L "unix2dos"
42 #define DOS2UNIX_NAME_S "d2u"
43 #define DOS2UNIX_NAME_L "dos2unix"
44
45 #define PARAM_CT_AUTO "auto"
46 #define PARAM_CT_UNIX2DOS_S "u2d"
47 #define PARAM_CT_UNIX2DOS_L "unix2dos"
48 #define PARAM_CT_DOS2UNIX_S "d2u"
49 #define PARAM_CT_DOS2UNIX_L "dos2unix"
50
51 #define PARAM_CT_AUTO_SHORT 'A'
52 #define PARAM_CT_UNIX2DOS_SHORT 'D'
53 #define PARAM_CT_DOS2UNIX_SHORT 'U'
54
55 #define CT_AUTO 0
56 #define CT_UNIX2DOS 1
57 #define CT_DOS2UNIX 2
58
59 #define SM_SAFE 0
60 #define SM_FORCE 1
61
62 typedef struct Opt_ {
63 char * progname;
64 int ConvType;
65 int SafeMode;
66 } Opt;
67
68 static void printTopDescription(FILE * f, char * name);
69 static void printBottomDescription(FILE * f, char * name);
70 static const char * getVersion(void);
71 static void usage(poptContext optCon, FILE * f, char * name);
72 static void help(poptContext optCon, FILE * f, char * name);
73 static void version(poptContext optCon, FILE * f, char * name);
74 static void license(poptContext optCon, FILE * f, char * name);
75 static int basename(char* p, const char* s);
76 static int convert(const char *fn, Opt opt);
77 static int exitOnZero(const char *fn, Opt opts,
78 FILE* in, FILE* out, char* tempFn);
79
80 int main(int argc, const char ** argv) {
81 poptContext optCon;
82 const char ** rest;
83 Opt opts = {
84 NULL,
85 CT_AUTO,
86 SM_SAFE
87 };
88 int safeFlag = 0;
89 int forceFlag = 0;
90 int u2dFlag = 0;
91 int d2uFlag = 0;
92 int autoFlag = 0;
93 int convFlags = 0;
94 int rc;
95 int ec = 0;
96 int xargc = 0;
97 int progtype;
98
99 struct poptOption generalOptionsTable[] = {
100 { PARAM_CT_AUTO, PARAM_CT_AUTO_SHORT, \
101 POPT_ARG_NONE, &autoFlag, CT_AUTO, \
102 "Output format will be the opposite of the autodetected source format", NULL },
103 { PARAM_CT_UNIX2DOS_S, PARAM_CT_UNIX2DOS_SHORT, \
104 POPT_ARG_NONE, &u2dFlag, CT_UNIX2DOS, \
105 "Output will be in DOS format", NULL },
106 { PARAM_CT_UNIX2DOS_L, '\0', \
107 POPT_ARG_NONE, &u2dFlag, CT_UNIX2DOS, \
108 "Output will be in DOS format", NULL },
109 { PARAM_CT_DOS2UNIX_S, PARAM_CT_DOS2UNIX_SHORT, \
110 POPT_ARG_NONE, &d2uFlag, CT_DOS2UNIX, \
111 "Output will be in UNIX format", NULL },
112 { PARAM_CT_DOS2UNIX_L, '\0', \
113 POPT_ARG_NONE, &d2uFlag, CT_DOS2UNIX, \
114 "Output will be in UNIX format", NULL },
115 { "force", '\0', POPT_ARG_NONE, &forceFlag, 'f',
116 "Ignore binary file detection", NULL },
117 { "safe", '\0', POPT_ARG_NONE, &safeFlag, 's',
118 "Do not modify binary files", NULL },
119 { NULL, '\0', 0, NULL, 0, NULL, NULL }
120 };
121
122 struct poptOption helpOptionsTable[] = {
123 { "help", '?', POPT_ARG_NONE, NULL, '?', \
124 "Show this help message", NULL},
125 { "usage", '\0', POPT_ARG_NONE, NULL, 'u', \
126 "Display brief usage message", NULL},
127 { "version", '\0', POPT_ARG_NONE, NULL, 'v', \
128 "Display version information", NULL},
129 { "license", '\0', POPT_ARG_NONE, NULL, 'l', \
130 "Display licensing information", NULL},
131 { NULL, '\0', 0, NULL, 0, NULL, NULL }
132 };
133
134 struct poptOption opt[] = {
135 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, generalOptionsTable, 0, \
136 "Main options (not all may apply)", NULL },
137 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, helpOptionsTable, 0, \
138 "Help options", NULL },
139 { NULL, '\0', 0, NULL, 0, NULL, NULL }
140 };
141
142 if( (opts.progname = strdup(argv[0])) == NULL ) {
143 fprintf(stderr, "%s: memory allocation error\n", argv[0]);
144 exit(1);
145 }
146 basename(opts.progname, argv[0]);
147 // set defaults based on program name
148 if( strcasecmp(opts.progname, UNIX2DOS_NAME_S) == 0 ) {
149 opts.ConvType = CT_UNIX2DOS;
150 progtype = CT_UNIX2DOS;
151 }
152 else if ( strcasecmp(opts.progname, UNIX2DOS_NAME_L) == 0 ) {
153 opts.ConvType = CT_UNIX2DOS;
154 progtype = CT_UNIX2DOS;
155 }
156 else if ( strcasecmp(opts.progname, DOS2UNIX_NAME_S) == 0 ) {
157 opts.ConvType = CT_DOS2UNIX;
158 progtype = CT_DOS2UNIX;
159 }
160 else if ( strcasecmp(opts.progname, DOS2UNIX_NAME_L) == 0 ) {
161 opts.ConvType = CT_DOS2UNIX;
162 progtype = CT_DOS2UNIX;
163 }
164 else
165 progtype = CT_AUTO;
166
167 optCon = poptGetContext(NULL, argc, argv, opt, 0);
168 poptSetOtherOptionHelp(optCon, "[OPTION...] [input file list...]");
169
170 while ((rc = poptGetNextOpt(optCon)) > 0) {
171 switch (rc) {
172 case '?': help(optCon, stderr, opts.progname);
173 goto exit;
174 case 'u': usage(optCon, stderr, opts.progname);
175 goto exit;
176 case 'v': version(optCon, stderr, opts.progname);
177 goto exit;
178 case 'l': license(optCon, stderr, opts.progname);
179 goto exit;
180 }
181 }
182 if (rc < -1 ) {
183 fprintf(stderr, "%s: bad argument %s: %s\n",
184 opts.progname, poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
185 poptStrerror(rc));
186 ec = 2;
187 goto exit;
188 }
189 rest = poptGetArgs(optCon);
190
191 convFlags = (u2dFlag << 2) | (d2uFlag << 1) | autoFlag;
192 if ((convFlags == 7) || (convFlags == 6) || (convFlags == 5) || (convFlags == 3))
193 {
194 fprintf(stderr,
195 "%s: Only one congruent set of options allowed:\n"
196 "(--%s, --%s, -%c), (--%s, --%s, -%c), (--%s, -%c)\n",
197 opts.progname,
198 PARAM_CT_UNIX2DOS_S, PARAM_CT_UNIX2DOS_L, PARAM_CT_UNIX2DOS_SHORT,
199 PARAM_CT_DOS2UNIX_S, PARAM_CT_DOS2UNIX_L, PARAM_CT_DOS2UNIX_SHORT,
200 PARAM_CT_AUTO, PARAM_CT_AUTO_SHORT);
201 ec = 3;
202 goto exit;
203 }
204 if (d2uFlag) opts.ConvType = CT_DOS2UNIX;
205 if (u2dFlag) opts.ConvType = CT_UNIX2DOS;
206 if (autoFlag) opts.ConvType = CT_AUTO;
207
208 // check that there is no conflict between program's name and options
209 if (progtype == CT_UNIX2DOS) {
210 if (opts.ConvType != CT_UNIX2DOS) {
211 fprintf(stderr,
212 "%s: cannot accept any conversion type argument other\n" \
213 " than --%s (--%s, -%c) when the program is called with this name\n", \
214 opts.progname, PARAM_CT_UNIX2DOS_S, PARAM_CT_UNIX2DOS_L, \
215 PARAM_CT_UNIX2DOS_SHORT);
216 ec = 3;
217 goto exit;
218 }
219 }
220 if (progtype == CT_DOS2UNIX) {
221 if (opts.ConvType != CT_DOS2UNIX) {
222 fprintf(stderr,
223 "%s: cannot accept any conversion type argument other\n" \
224 " than --%s (--%s, -%c) when this program is called with this name\n", \
225 opts.progname, PARAM_CT_DOS2UNIX_S, PARAM_CT_DOS2UNIX_L, \
226 PARAM_CT_DOS2UNIX_SHORT);
227 ec = 3;
228 goto exit;
229 }
230 }
231 if ((safeFlag == 1) && (forceFlag == 1))
232 {
233 fprintf(stderr,
234 "%s: Warning, both --force and --safe specified. Using --safe.\n",
235 opts.progname);
236 opts.SafeMode = SM_SAFE;
237 }
238 else
239 {
240 if (safeFlag == 1) opts.SafeMode = SM_SAFE;
241 if (forceFlag == 1) opts.SafeMode = SM_FORCE;
242 }
243
244 if (rest)
245 while (*rest) {
246 if ((ec = convert(*rest++, opts)) < 0)
247 break;
248 }
249 else
250 ec = convert(NULL, opts);
251
252 exit:
253 poptFreeContext(optCon);
254 free(opts.progname);
255 return ec;
256 }
257 /* int basename(char* p, const char* s)
258 *
259 * strip leading path names and a final ".exe" if they
260 * exist. Place the result in buffer p. Return the
261 * length of p, or -1 if error.
262 */
263 static int basename(char* p, const char* s)
264 {
265 char* start;
266 char* end;
267 char* s1;
268 s1 = strdup(s);
269 // first, replace all \ with /
270 while (start = strchr(s1, '\\'))
271 *start = '/';
272 // then, locate the final /
273 start = strrchr(s1, '/');
274 if (!start)
275 start = s1;
276 else
277 start++; // if s ends with /, then this points to '\0'
278 end = &s1[strlen(s1)]; // this points to '\0'
279
280 // the following assumes single byte char's
281 if (( ((int) (end - start)) > 4 ) && // long enough to have .exe extension
282 // second part not evaluated (short circuit) if string fragment too short
283 (strcasecmp(end-4,".exe") == 0)) // end -4 > start, so we're okaya
284 {
285 end -= 4;
286 *end = '\0';
287 }
288 strncpy(p, start, ((int) (end - start)) + 1);
289 free(s1);
290 return strlen(p);
291 }
292
293 static const char * getVersion()
294 {
295 return versionID;
296 }
297
298 static void printTopDescription(FILE * f, char * name)
299 {
300 if( ( strcasecmp(name, UNIX2DOS_NAME_S) == 0 ) ||
301 ( strcasecmp(name, UNIX2DOS_NAME_L) == 0 ) ) {
302 fprintf(f, "%s is part of cygutils version %s\n", name, getVersion());
303 fprintf(f, " converts the line endings of text files from\n");
304 fprintf(f, " UNIX style (0x0a) to DOS style (0x0d 0x0a)\n\n");
305 }
306 else if ( ( strcasecmp(name, DOS2UNIX_NAME_S) == 0 ) ||
307 ( strcasecmp(name, DOS2UNIX_NAME_L) == 0 ) ) {
308 fprintf(f, "%s is part of cygutils version %s\n", name, getVersion());
309 fprintf(f, " converts the line endings of text files from\n");
310 fprintf(f, " DOS style (0x0d 0x0a) to UNIX style (0x0a)\n\n");
311 }
312 else {
313 fprintf(f, "%s is part of cygutils version %s\n", name, getVersion());
314 fprintf(f, " converts the line endings of text files to/from\n");
315 fprintf(f, " DOS style (0x0d 0x0a) and UNIX style (0x0a)\n");
316 fprintf(f, " When no conversion options are specified, the input format\n");
317 fprintf(f, " will be automatically detected and converted to the opposite\n");
318 fprintf(f, " format on output\n\n");
319 }
320 }
321
322 static void printBottomDescription(FILE * f, char * name)
323 {
324 fprintf(f, "\n");
325 fprintf(f, "Other arguments\n");
326 fprintf(f, " [input file list...] for each file listed, convert in place.\n");
327 fprintf(f, " If none specified, then use stdin/stdout\n");
328 }
329 static printLicense(FILE * f, char * name)
330 {
331 fprintf(f, "This program is free software; you can redistribute it and/or\n");
332 fprintf(f, "modify it under the terms of the GNU General Public License\n");
333 fprintf(f, "as published by the Free Software Foundation; either version 2\n");
334 fprintf(f, "of the License, or (at your option) any later version.\n");
335 fprintf(f, "\n");
336 fprintf(f, "This program is distributed in the hope that it will be useful,\n");
337 fprintf(f, "but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
338 fprintf(f, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
339 fprintf(f, "GNU General Public License for more details.\n");
340 fprintf(f, "\n");
341 fprintf(f, "You should have received a copy of the GNU General Public License\n");
342 fprintf(f, "along with this program; if not, write to the Free Software\n");
343 fprintf(f, "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n");
344 fprintf(f, "\n");
345 fprintf(f, "See the COPYING file for license information.\n");
346 }
347
348 static void usage(poptContext optCon, FILE * f, char * name)
349 {
350 poptPrintUsage(optCon, f, 0);
351 }
352
353 static void help(poptContext optCon, FILE * f, char * name)
354 {
355 printTopDescription(f, name);
356 poptPrintHelp(optCon, f, 0);
357 printBottomDescription(f, name);
358 }
359
360 static void version(poptContext optCon, FILE * f, char * name)
361 {
362 printTopDescription(f, name);
363 }
364
365 static void license(poptContext optCon, FILE * f, char * name)
366 {
367 printTopDescription(f, name);
368 printLicense(f, name);
369 }
370
371 // 0 : continue
372 // 1 : error, skip to next file
373 // <0 : error, exit immediately
374 static int exitOnZero(const char *fn, Opt opts,
375 FILE* in, FILE* out, char* tempFn)
376 {
377 char buf[PATH_MAX * 2];
378
379 // We may be dealing with a binary file
380 // if not using stdin, and not --safe mode, warn and bail
381 if ((fn != NULL) && (opts.SafeMode == SM_SAFE)) {
382 snprintf(buf, PATH_MAX*2 - 1,
383 "\n%s: skipping binary file %s...", opts.progname, fn);
384 buf[PATH_MAX*2 - 1] = '\0';
385 fputs(buf, stderr);
386
387 if (fclose(in) < 0) {
388 perror(opts.progname);
389 return -2;
390 }
391 if (fclose(out) < 0) {
392 perror(opts.progname);
393 return -2;
394 }
395 if (remove(tempFn) < 0) {
396 perror(opts.progname);
397 return -2;
398 }
399 return 1;
400 }
401 return 0;
402 }
403
404 #if defined(_WIN32) && !defined(__CYGWIN__)
405 int mkstemp(char *path);
406 char * mkdtemp (char *path);
407 #endif
408
409 // if fn is NULL then input is stdin and output is stdout
410 static int convert(const char *fn, Opt opts) {
411 static const char * TEMPLATE = "conv_XXXXXXXX";
412 int c;
413 int fileChanged = 0;
414 char tempFn[20];
415 int tempFd;
416 FILE *in = stdin, *out = stdout;
417 char buf[PATH_MAX * 2];
418
419 strncpy (tempFn, TEMPLATE, 20);
420 if (fn != NULL) {
421 int inFileDesc = 0;
422 fprintf(stderr, "%s: ", fn);
423 if ((in = fopen(fn, "rb")) == NULL) {
424 snprintf(buf, PATH_MAX*2 - 1, "\n%s processing %s (could not open input file)",
425 opts.progname, fn);
426 perror(buf);
427 return -1;
428 }
429 tempFd = mkstemp(tempFn);
430 if (tempFd < 0)
431 {
432 snprintf(buf, PATH_MAX*2 - 1, "\n%s processing %s (could not open temp file %s)",
433 opts.progname, fn, tempFn);
434 perror(buf);
435 return -2;
436 }
437 if ((out = fdopen(tempFd, "wb")) == NULL) {
438 snprintf(buf, PATH_MAX*2 - 1, "\n%s processing %s (could not open temp file stream)",
439 opts.progname, fn);
440 perror(buf);
441 return -2;
442 }
443 }
444 else {
445 setmode(0, O_BINARY);
446 setmode(1, O_BINARY);
447 }
448
449 while ((c = fgetc(in)) != EOF) {
450
451 if (c == '\0') {
452 int a = exitOnZero(fn, opts, in, out, tempFn);
453 if (a < 0) return a;
454 if (a > 0) goto convert_ret;
455 // otherwise, write out the '\0' and continue
456 fputc('\0', out);
457 continue;
458 }
459
460 if (c == '\r') {
461 if (opts.ConvType == CT_AUTO) {
462 opts.ConvType = CT_DOS2UNIX;
463 }
464 if (opts.ConvType == CT_DOS2UNIX) {
465 // check next char
466 int c2;
467 // eat all extra '\r'
468 while ((c2 = fgetc(in)) == '\r') {
469 fileChanged = 1;
470 }
471 if (c2 == EOF) {
472 // file ended on a '\r'. Finish the line and quit loop.
473 fputc('\n', out);
474 fileChanged = 1;
475 c = c2;
476 break;
477 } else if (c2 == '\0') {
478 int a = exitOnZero(fn, opts, in, out, tempFn);
479 if (a < 0) return a;
480 if (a > 0) goto convert_ret;
481 // otherwise, finish the line, promote c2, and fall off bottom of loop
482 fputc('\n', out);
483 fileChanged = 1;
484 c = c2;
485 } else if (c2 == '\n') {
486 // '\r' followed by '\n'. Promote c2, and fall off
487 fileChanged = 1;
488 c = c2;
489 } else {
490 // '\r' followed by something else. Write out '\n', promote c2, and fall off
491 fputc('\n', out);
492 fileChanged = 1;
493 c = c2;
494 }
495 fputc(c, out);
496 continue;
497 } // CT_DOS2UNIX
498
499 if (opts.ConvType == CT_UNIX2DOS) {
500 // check next char
501 int c2;
502 // eat all extra '\r'
503 while ((c2 = fgetc(in)) == '\r') {
504 fileChanged = 1;
505 }
506 if (c2 == EOF) {
507 // file ended on a '\r'. Finish the line and quit loop.
508 fputc('\r', out);
509 fputc('\n', out);
510 fileChanged = 1;
511 c = c2;
512 break;
513 } else if (c2 == '\0') {
514 int a = exitOnZero(fn, opts, in, out, tempFn);
515 if (a < 0) return a;
516 if (a > 0) goto convert_ret;
517 // otherwise, finish the line, promote c2, and fall off bottom of loop
518 fputc('\r', out);
519 fputc('\n', out);
520 fileChanged = 1;
521 c = c2;
522 } else if (c2 == '\n') {
523 // '\r' followed by '\n'. Write out '\r', promote c2, and fall off
524 fputc('\r', out);
525 c = c2;
526 } else {
527 // '\r' followed by something else. Write out '\r\n', promote c2, and fall off
528 fputc('\r', out);
529 fputc('\n', out);
530 fileChanged = 1;
531 c = c2;
532 }
533 fputc(c, out);
534 continue;
535 } // CT_UNIX2DOS
536 } // '\r'
537
538 if (c == '\n') {
539 // guaranteed that this is NOT preceeded by a '\r'
540 if (opts.ConvType == CT_AUTO)
541 opts.ConvType = CT_UNIX2DOS;
542 if (opts.ConvType == CT_UNIX2DOS) {
543 fileChanged = 1;
544 fputc('\r', out);
545 }
546 fputc('\n', out);
547 continue;
548 } // '\n'
549
550 // c not '\r', '\n', or '\0'
551 fputc(c, out);
552 }
553
554
555 if (fn != NULL) {
556 if (fclose(in) < 0) {
557 perror(opts.progname);
558 return -2;
559 }
560 if (fclose(out) < 0) {
561 perror(opts.progname);
562 return -2;
563 }
564 if (fileChanged != 0)
565 {
566 if ((in = fopen(tempFn, "rb")) == NULL) {
567 perror(opts.progname);
568 return -1;
569 }
570 if ((out = fopen(fn, "wb")) == NULL) {
571 perror(fn);
572 return -2;
573 }
574
575 while ((c = fgetc(in)) != EOF)
576 fputc(c, out);
577
578 if (fclose(in) < 0) {
579 perror(opts.progname);
580 return -2;
581 }
582 if (fclose(out) < 0) {
583 perror(opts.progname);
584 return -2;
585 }
586 }
587
588 if (remove(tempFn) < 0) {
589 perror(opts.progname);
590 return -2;
591 }
592 }
593
594 convert_ret:
595 if (fn != NULL)
596 fprintf(stderr, "done.\n");
597 return 0;
598 }
599
600 #if defined(_WIN32) && !defined(__CYGWIN__)
601 #include "mkstemp.c"
602 #endif
603
This page took 0.063561 seconds and 5 git commands to generate.