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>.
6 * based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997)
7 * Copyright 1997,.. by Peter Hanecak <hanecak@megaloman.com>.
9 * Copyright 2001,2002,2005,2009 by Charles Wilson
10 * All rights reserved.
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.
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.
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/>.
25 * See the COPYING file for full license information.
34 static const char versionID
[] = PACKAGE_VERSION
;
35 static const char revID
[] =
37 static const char copyrightID
[] =
38 "Copyright (c) 2009\nCharles S. Wilson. All rights reserved.\nLicensed under GPLv3+\n";
40 #define UNIX2DOS_NAME_S "u2d"
41 #define UNIX2DOS_NAME_L "unix2dos"
42 #define DOS2UNIX_NAME_S "d2u"
43 #define DOS2UNIX_NAME_L "dos2unix"
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"
51 #define PARAM_CT_AUTO_SHORT 'A'
52 #define PARAM_CT_UNIX2DOS_SHORT 'D'
53 #define PARAM_CT_DOS2UNIX_SHORT 'U'
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
);
80 int main(int argc
, const char ** argv
) {
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
}
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
}
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
}
142 if( (opts
.progname
= strdup(argv
[0])) == NULL
) {
143 fprintf(stderr
, "%s: memory allocation error\n", argv
[0]);
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
;
152 else if ( strcasecmp(opts
.progname
, UNIX2DOS_NAME_L
) == 0 ) {
153 opts
.ConvType
= CT_UNIX2DOS
;
154 progtype
= CT_UNIX2DOS
;
156 else if ( strcasecmp(opts
.progname
, DOS2UNIX_NAME_S
) == 0 ) {
157 opts
.ConvType
= CT_DOS2UNIX
;
158 progtype
= CT_DOS2UNIX
;
160 else if ( strcasecmp(opts
.progname
, DOS2UNIX_NAME_L
) == 0 ) {
161 opts
.ConvType
= CT_DOS2UNIX
;
162 progtype
= CT_DOS2UNIX
;
167 optCon
= poptGetContext(NULL
, argc
, argv
, opt
, 0);
168 poptSetOtherOptionHelp(optCon
, "[OPTION...] [input file list...]");
170 while ((rc
= poptGetNextOpt(optCon
)) > 0) {
172 case '?': help(optCon
, stderr
, opts
.progname
);
174 case 'u': usage(optCon
, stderr
, opts
.progname
);
176 case 'v': version(optCon
, stderr
, opts
.progname
);
178 case 'l': license(optCon
, stderr
, opts
.progname
);
183 fprintf(stderr
, "%s: bad argument %s: %s\n",
184 opts
.progname
, poptBadOption(optCon
, POPT_BADOPTION_NOALIAS
),
189 rest
= poptGetArgs(optCon
);
191 convFlags
= (u2dFlag
<< 2) | (d2uFlag
<< 1) | autoFlag
;
192 if ((convFlags
== 7) || (convFlags
== 6) || (convFlags
== 5) || (convFlags
== 3))
195 "%s: Only one congruent set of options allowed:\n"
196 "(--%s, --%s, -%c), (--%s, --%s, -%c), (--%s, -%c)\n",
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
);
204 if (d2uFlag
) opts
.ConvType
= CT_DOS2UNIX
;
205 if (u2dFlag
) opts
.ConvType
= CT_UNIX2DOS
;
206 if (autoFlag
) opts
.ConvType
= CT_AUTO
;
208 // check that there is no conflict between program's name and options
209 if (progtype
== CT_UNIX2DOS
) {
210 if (opts
.ConvType
!= CT_UNIX2DOS
) {
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
);
220 if (progtype
== CT_DOS2UNIX
) {
221 if (opts
.ConvType
!= CT_DOS2UNIX
) {
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
);
231 if ((safeFlag
== 1) && (forceFlag
== 1))
234 "%s: Warning, both --force and --safe specified. Using --safe.\n",
236 opts
.SafeMode
= SM_SAFE
;
240 if (safeFlag
== 1) opts
.SafeMode
= SM_SAFE
;
241 if (forceFlag
== 1) opts
.SafeMode
= SM_FORCE
;
246 if ((ec
= convert(*rest
++, opts
)) < 0)
250 ec
= convert(NULL
, opts
);
253 poptFreeContext(optCon
);
257 /* int basename(char* p, const char* s)
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.
263 static int basename(char* p
, const char* s
)
269 // first, replace all \ with /
270 while (start
= strchr(s1
, '\\'))
272 // then, locate the final /
273 start
= strrchr(s1
, '/');
277 start
++; // if s ends with /, then this points to '\0'
278 end
= &s1
[strlen(s1
)]; // this points to '\0'
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
288 strncpy(p
, start
, ((int) (end
- start
)) + 1);
293 static const char * getVersion()
298 static void printTopDescription(FILE * f
, char * name
)
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");
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");
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");
322 static void printBottomDescription(FILE * f
, char * name
)
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");
329 static printLicense(FILE * f
, char * name
)
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");
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");
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");
345 fprintf(f
, "See the COPYING file for license information.\n");
348 static void usage(poptContext optCon
, FILE * f
, char * name
)
350 poptPrintUsage(optCon
, f
, 0);
353 static void help(poptContext optCon
, FILE * f
, char * name
)
355 printTopDescription(f
, name
);
356 poptPrintHelp(optCon
, f
, 0);
357 printBottomDescription(f
, name
);
360 static void version(poptContext optCon
, FILE * f
, char * name
)
362 printTopDescription(f
, name
);
365 static void license(poptContext optCon
, FILE * f
, char * name
)
367 printTopDescription(f
, name
);
368 printLicense(f
, name
);
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
)
377 char buf
[PATH_MAX
* 2];
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';
387 if (fclose(in
) < 0) {
388 perror(opts
.progname
);
391 if (fclose(out
) < 0) {
392 perror(opts
.progname
);
395 if (remove(tempFn
) < 0) {
396 perror(opts
.progname
);
404 #if defined(_WIN32) && !defined(__CYGWIN__)
405 int mkstemp(char *path
);
406 char * mkdtemp (char *path
);
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";
416 FILE *in
= stdin
, *out
= stdout
;
417 char buf
[PATH_MAX
* 2];
419 strncpy (tempFn
, TEMPLATE
, 20);
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)",
429 tempFd
= mkstemp(tempFn
);
432 snprintf(buf
, PATH_MAX
*2 - 1, "\n%s processing %s (could not open temp file %s)",
433 opts
.progname
, fn
, tempFn
);
437 if ((out
= fdopen(tempFd
, "wb")) == NULL
) {
438 snprintf(buf
, PATH_MAX
*2 - 1, "\n%s processing %s (could not open temp file stream)",
445 setmode(0, O_BINARY
);
446 setmode(1, O_BINARY
);
449 while ((c
= fgetc(in
)) != EOF
) {
452 int a
= exitOnZero(fn
, opts
, in
, out
, tempFn
);
454 if (a
> 0) goto convert_ret
;
455 // otherwise, write out the '\0' and continue
461 if (opts
.ConvType
== CT_AUTO
) {
462 opts
.ConvType
= CT_DOS2UNIX
;
464 if (opts
.ConvType
== CT_DOS2UNIX
) {
467 // eat all extra '\r'
468 while ((c2
= fgetc(in
)) == '\r') {
472 // file ended on a '\r'. Finish the line and quit loop.
477 } else if (c2
== '\0') {
478 int a
= exitOnZero(fn
, opts
, in
, out
, tempFn
);
480 if (a
> 0) goto convert_ret
;
481 // otherwise, finish the line, promote c2, and fall off bottom of loop
485 } else if (c2
== '\n') {
486 // '\r' followed by '\n'. Promote c2, and fall off
490 // '\r' followed by something else. Write out '\n', promote c2, and fall off
499 if (opts
.ConvType
== CT_UNIX2DOS
) {
502 // eat all extra '\r'
503 while ((c2
= fgetc(in
)) == '\r') {
507 // file ended on a '\r'. Finish the line and quit loop.
513 } else if (c2
== '\0') {
514 int a
= exitOnZero(fn
, opts
, in
, out
, tempFn
);
516 if (a
> 0) goto convert_ret
;
517 // otherwise, finish the line, promote c2, and fall off bottom of loop
522 } else if (c2
== '\n') {
523 // '\r' followed by '\n'. Write out '\r', promote c2, and fall off
527 // '\r' followed by something else. Write out '\r\n', promote c2, and fall off
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
) {
550 // c not '\r', '\n', or '\0'
556 if (fclose(in
) < 0) {
557 perror(opts
.progname
);
560 if (fclose(out
) < 0) {
561 perror(opts
.progname
);
564 if (fileChanged
!= 0)
566 if ((in
= fopen(tempFn
, "rb")) == NULL
) {
567 perror(opts
.progname
);
570 if ((out
= fopen(fn
, "wb")) == NULL
) {
575 while ((c
= fgetc(in
)) != EOF
)
578 if (fclose(in
) < 0) {
579 perror(opts
.progname
);
582 if (fclose(out
) < 0) {
583 perror(opts
.progname
);
588 if (remove(tempFn
) < 0) {
589 perror(opts
.progname
);
596 fprintf(stderr
, "done.\n");
600 #if defined(_WIN32) && !defined(__CYGWIN__)