2 * Copyright (C) 2009-2014 Erwin Waterlander
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice in the documentation and/or other materials provided with
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #if defined(D2U_UNICODE)
29 #if defined(_WIN32) || defined(__CYGWIN__)
34 #if defined(__GLIBC__)
35 /* on glibc, canonicalize_file_name() broken prior to 2.4 (06-Mar-2006) */
36 # if __GNUC_PREREQ (2,4)
37 # define USE_CANONICALIZE_FILE_NAME 1
39 #elif defined(__CYGWIN__)
40 /* on cygwin, canonicalize_file_name() available since api 0/213 */
41 /* (1.7.0beta61, 25-Sep-09) */
42 # include <cygwin/version.h>
43 # if (CYGWIN_VERSION_DLL_COMBINED >= 213) && (CYGWIN_VERSION_DLL_MAJOR >= 1007)
44 # define USE_CANONICALIZE_FILE_NAME 1
49 /******************************************************************
51 * int symbolic_link(char *path)
53 * test if *path points to a file that exists and is a symbolic link
55 * returns 1 on success, 0 when it fails.
57 ******************************************************************/
59 #if (defined(_WIN32) && !defined(__CYGWIN__))
61 int symbolic_link(const char *path)
65 attrs = GetFileAttributes(path);
67 if (attrs == INVALID_FILE_ATTRIBUTES)
70 return ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
74 int symbolic_link(const char *path)
79 if (STAT(path, &buf) == 0)
81 if (S_ISLNK(buf.st_mode))
89 /******************************************************************
91 * int regfile(char *path, int allowSymlinks)
93 * test if *path points to a regular file (or is a symbolic link,
94 * if allowSymlinks != 0).
96 * returns 0 on success, -1 when it fails.
98 ******************************************************************/
99 int regfile(char *path, int allowSymlinks, CFlag *ipFlag, char *progname)
104 if (STAT(path, &buf) == 0)
107 fprintf(stderr, "%s: %s MODE 0%o ", progname, path, buf.st_mode);
109 if (S_ISSOCK(buf.st_mode))
110 fprintf(stderr, " (socket)");
113 if (S_ISLNK(buf.st_mode))
114 fprintf(stderr, " (symbolic link)");
116 if (S_ISREG(buf.st_mode))
117 fprintf(stderr, " (regular file)");
119 if (S_ISBLK(buf.st_mode))
120 fprintf(stderr, " (block device)");
122 if (S_ISDIR(buf.st_mode))
123 fprintf(stderr, " (directory)");
124 if (S_ISCHR(buf.st_mode))
125 fprintf(stderr, " (character device)");
126 if (S_ISFIFO(buf.st_mode))
127 fprintf(stderr, " (FIFO)");
128 fprintf(stderr, "\n");
130 if ((S_ISREG(buf.st_mode))
132 || (S_ISLNK(buf.st_mode) && allowSymlinks)
143 ipFlag->error = errno;
144 errstr = strerror(errno);
145 fprintf(stderr, "%s: %s: %s\n", progname, path, errstr);
151 /******************************************************************
153 * int regfile_target(char *path)
155 * test if *path points to a regular file (follow symbolic link)
157 * returns 0 on success, -1 when it fails.
159 ******************************************************************/
160 int regfile_target(char *path, CFlag *ipFlag, char *progname)
165 if (stat(path, &buf) == 0)
167 if (S_ISREG(buf.st_mode))
176 ipFlag->error = errno;
177 errstr = strerror(errno);
178 fprintf(stderr, "%s: %s: %s\n", progname, path, errstr);
184 void PrintBSDLicense(void)
187 Redistribution and use in source and binary forms, with or without\n\
188 modification, are permitted provided that the following conditions\n\
190 1. Redistributions of source code must retain the above copyright\n\
191 notice, this list of conditions and the following disclaimer.\n\
192 2. Redistributions in binary form must reproduce the above copyright\n\
193 notice in the documentation and/or other materials provided with\n\
194 the distribution.\n\n\
197 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY\n\
198 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\
199 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n\
200 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE\n\
201 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n\
202 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\n\
203 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n\
204 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n\
205 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n\
206 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN\n\
207 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\
211 void PrintUsage(char *progname)
213 printf(_("Usage: %s [options] [file ...] [-n infile outfile ...]\n"), progname);
214 printf(_(" -ascii convert only line breaks (default)\n"));
215 printf(_(" -iso conversion between DOS and ISO-8859-1 character set\n"));
216 printf(_(" -1252 use Windows code page 1252 (Western European)\n"));
217 printf(_(" -437 use DOS code page 437 (US) (default)\n"));
218 printf(_(" -850 use DOS code page 850 (Western European)\n"));
219 printf(_(" -860 use DOS code page 860 (Portuguese)\n"));
220 printf(_(" -863 use DOS code page 863 (French Canadian)\n"));
221 printf(_(" -865 use DOS code page 865 (Nordic)\n"));
222 printf(_(" -7 convert 8 bit characters to 7 bit space\n"));
223 printf(_(" -c, --convmode conversion mode\n\
224 convmode ascii, 7bit, iso, mac, default to ascii\n"));
225 printf(_(" -f, --force force conversion of binary files\n"));
226 printf(_(" -h, --help display this help text\n"));
227 printf(_(" -k, --keepdate keep output file date\n"));
228 printf(_(" -L, --license display software license\n"));
229 printf(_(" -l, --newline add additional newline\n"));
230 printf(_(" -m, --add-bom add UTF-8 Byte Order Mark\n"));
231 printf(_(" -n, --newfile write to new file\n\
232 infile original file in new-file mode\n\
233 outfile output file in new-file mode\n"));
234 printf(_(" -o, --oldfile write to old file (default)\n\
235 file ... files to convert in old-file mode\n"));
236 printf(_(" -q, --quiet quiet mode, suppress all warnings\n\
237 (always on in stdio mode)\n"));
238 printf(_(" -s, --safe skip binary files (default)\n"));
240 printf(_(" -ul, --assume-utf16le assume that the input format is UTF-16LE\n"));
241 printf(_(" -ub, --assume-utf16be assume that the input format is UTF-16BE\n"));
244 printf(_(" -F, --follow-symlink follow symbolic links and convert the targets\n"));
246 #if defined(S_ISLNK) || (defined(_WIN32) && !defined(__CYGWIN__))
247 printf(_(" -R, --replace-symlink replace symbolic links with converted files\n\
248 (original target files remain unchanged)\n"));
249 printf(_(" -S, --skip-symlink keep symbolic links and targets unchanged (default)\n"));
251 printf(_(" -V, --version display version number\n"));
254 #define MINGW32_W64 1
256 void PrintVersion(char *progname)
258 printf("%s %s (%s)\n", progname, VER_REVISION, VER_DATE);
260 printf("VER_AUTHOR: %s\n", VER_AUTHOR);
262 #if defined(__WATCOMC__) && defined(__I86__)
263 printf("%s", _("DOS 16 bit version (WATCOMC).\n"));
264 #elif defined(__TURBOC__) && defined(__MSDOS__)
265 printf("%s", _("DOS 16 bit version (TURBOC).\n"));
266 #elif defined(__WATCOMC__) && defined(__DOS__)
267 printf("%s", _("DOS 32 bit version (WATCOMC).\n"));
268 #elif defined(__DJGPP__)
269 printf("%s", _("DOS 32 bit version (DJGPP).\n"));
270 #elif defined(__MSYS__)
271 printf("%s", _("MSYS version.\n"));
272 #elif defined(__CYGWIN__)
273 printf("%s", _("Cygwin version.\n"));
274 #elif defined(__WIN64__) && defined(__MINGW64__)
275 printf("%s", _("Windows 64 bit version (MinGW-w64).\n"));
276 #elif defined(__WATCOMC__) && defined(__NT__)
277 printf("%s", _("Windows 32 bit version (WATCOMC).\n"));
278 #elif defined(_WIN32) && defined(__MINGW32__) && (D2U_COMPILER == MINGW32_W64)
279 printf("%s", _("Windows 32 bit version (MinGW-w64).\n"));
280 #elif defined(_WIN32) && defined(__MINGW32__)
281 printf("%s", _("Windows 32 bit version (MinGW).\n"));
282 #elif defined(_WIN64) && defined(_MSC_VER)
283 printf(_("Windows 64 bit version (MSVC %d).\n"),_MSC_VER);
284 #elif defined(_WIN32) && defined(_MSC_VER)
285 printf(_("Windows 32 bit version (MSVC %d).\n"),_MSC_VER);
286 #elif defined (__OS2__) && defined(__WATCOMC__) /* OS/2 Warp */
287 printf("%s", _("OS/2 version (WATCOMC).\n"));
288 #elif defined (__OS2__) && defined(__EMX__) /* OS/2 Warp */
289 printf("%s", _("OS/2 version (EMX).\n"));
292 printf("%s", _("With Unicode UTF-16 support.\n"));
294 printf("%s", _("Without Unicode UTF-16 support.\n"));
297 printf("%s", _("With native language support.\n"));
299 printf("%s", "Without native language support.\n");
304 void PrintLocaledir(char *localedir)
306 printf("LOCALEDIR: %s\n", localedir);
310 /* opens file of name ipFN in read only mode
311 * RetVal: NULL if failure
312 * file stream otherwise
314 FILE* OpenInFile(char *ipFN)
316 return (fopen(ipFN, R_CNTRL));
320 /* opens file of name ipFN in write only mode
321 * RetVal: NULL if failure
322 * file stream otherwise
324 FILE* OpenOutFile(int fd)
326 return (fdopen(fd, W_CNTRL));
329 #if defined(__TURBOC__) || defined(__MSYS__) || defined(_MSC_VER)
330 char *dirname(char *path)
334 if (( path == NULL) || (((ptr=strrchr(path,'/')) == NULL) && ((ptr=strrchr(path,'\\')) == NULL)) )
345 FILE* MakeTempFileFrom(const char *OutFN, char **fname_ret)
347 int MakeTempFileFrom(const char *OutFN, char **fname_ret)
350 char *cpy = strdup(OutFN);
352 size_t fname_len = 0;
353 char *fname_str = NULL;
368 fname_len = strlen(dir) + strlen("/d2utmpXXXXXX") + sizeof (char);
369 if (!(fname_str = malloc(fname_len)))
371 sprintf(fname_str, "%s%s", dir, "/d2utmpXXXXXX");
372 *fname_ret = fname_str;
377 name = mktemp(fname_str);
379 if ((fd = fopen(fname_str, W_CNTRL)) == NULL)
382 if ((fd = mkstemp(fname_str)) == -1)
398 /* Test if *lFN is the name of a symbolic link. If not, set *rFN equal
399 * to lFN, and return 0. If so, then use canonicalize_file_name or
400 * realpath to determine the pointed-to file; the resulting name is
401 * stored in newly allocated memory, *rFN is set to point to that value,
402 * and 1 is returned. On error, -1 is returned and errno is set as
405 * Note that if symbolic links are not supported, then 0 is always returned
408 * RetVal: 0 if success, and *lFN is not a symlink
409 * 1 if success, and *lFN is a symlink
412 int ResolveSymbolicLink(char *lFN, char **rFN, CFlag *ipFlag, char *progname)
418 char *targetFN = NULL;
420 if (STAT(lFN, &StatBuf))
424 ipFlag->error = errno;
425 errstr = strerror(errno);
426 fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
430 else if (S_ISLNK(StatBuf.st_mode))
432 #if USE_CANONICALIZE_FILE_NAME
433 targetFN = canonicalize_file_name(lFN);
438 errstr = strerror(errno);
439 fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
450 /* Sigh. Use realpath, but realize that it has a fatal
451 * flaw: PATH_MAX isn't necessarily the maximum path
452 * length -- so realpath() might fail. */
453 targetFN = (char *) malloc(PATH_MAX * sizeof(char));
458 errstr = strerror(errno);
459 fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
466 /* is there any platform with S_ISLNK that does not have realpath? */
467 char *rVal = realpath(lFN, targetFN);
472 errstr = strerror(errno);
473 fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
485 #endif /* !USE_CANONICALIZE_FILE_NAME */
491 #endif /* !S_ISLNK */
495 FILE *read_bom (FILE *f, int *bomtype)
509 if ((bom[0] = fgetc(f)) == EOF)
515 if ((bom[0] != 0xff) && (bom[0] != 0xfe) && (bom[0] != 0xef))
521 if ((bom[1] = fgetc(f)) == EOF)
528 if ((bom[0] == 0xff) && (bom[1] == 0xfe)) /* UTF16-LE */
530 *bomtype = FILE_UTF16LE;
533 if ((bom[0] == 0xfe) && (bom[1] == 0xff)) /* UTF16-BE */
535 *bomtype = FILE_UTF16BE;
538 if ((bom[2] = fgetc(f)) == EOF)
546 if ((bom[0] == 0xef) && (bom[1] == 0xbb) && (bom[2]== 0xbf)) /* UTF-8 */
548 *bomtype = FILE_UTF8;
562 wint_t d2u_getwc(FILE *f, int bomtype)
567 if (((c_lead=fgetc(f)) == EOF) || ((c_trail=fgetc(f)) == EOF))
570 if (bomtype == FILE_UTF16LE) /* UTF16 little endian */
573 wc = (wint_t)(c_trail + c_lead) ;
574 } else { /* UTF16 big endian */
576 wc = (wint_t)(c_trail + c_lead) ;
581 wint_t d2u_ungetwc(wint_t wc, FILE *f, int bomtype)
585 if (bomtype == FILE_UTF16LE) /* UTF16 little endian */
587 c_trail = (int)(wc & 0xff00);
589 c_lead = (int)(wc & 0xff);
590 } else { /* UTF16 big endian */
591 c_lead = (int)(wc & 0xff00);
593 c_trail = (int)(wc & 0xff);
596 /* push back in reverse order */
597 if ((ungetc(c_trail,f) == EOF) || (ungetc(c_lead,f) == EOF))
602 /* Put wide character */
603 wint_t d2u_putwc(wint_t wc, FILE *f, CFlag *ipFlag)
606 static wchar_t lead, trail;
607 static wchar_t wstr[3];
610 if ((wc >= 0xd800) && (wc < 0xdc00))
612 /* fprintf(stderr, "UTF-16 lead %x\n",wc); */
613 lead = (wchar_t)wc; /* lead (high) surrogate */
616 if ((wc >= 0xdc00) && (wc < 0xe000))
618 /* fprintf(stderr, "UTF-16 trail %x\n",wc); */
619 trail = (wchar_t)wc; /* trail (low) surrogate */
620 #if defined(_WIN32) || defined(__CYGWIN__)
621 /* On Windows (including Cygwin) wchar_t is 16 bit */
622 /* We cannot decode an UTF-16 surrogate pair, because it will
623 not fit in a 16 bit wchar_t. */
628 /* On Unix wchar_t is 32 bit */
629 /* When we don't decode the UTF-16 surrogate pair, wcstombs() does not
630 * produce the same UTF-8 as WideCharToMultiByte(). The UTF-8 output
631 * produced by wcstombs() is bigger, because it just translates the wide
632 * characters in the range 0xD800..0xDBFF individually to UTF-8 sequences
633 * (although these code points are reserved for use only as surrogate
636 * Some smart viewers can still display this UTF-8 correctly (like Total
637 * Commander lister), however the UTF-8 is not readable by Windows
638 * Notepad (on Windows 7). When we decode the UTF-16 surrogate pairs
639 * ourselves the wcstombs() UTF-8 output is identical to what
640 * WideCharToMultiByte() produces, and is readable by Notepad.
642 * Surrogate halves in UTF-8 are invalid. See also
643 * http://en.wikipedia.org/wiki/UTF-8#Invalid_code_points
644 * http://tools.ietf.org/html/rfc3629#page-5
645 * It is a bug in (some implemenatations of) wcstombs().
646 * On Cygwin 1.7 wcstombs() produces correct UTF-8 from UTF-16 surrogate pairs.
648 /* Decode UTF-16 surrogate pair */
650 wstr[0] += (lead & 0x03FF) << 10;
651 wstr[0] += (trail & 0x03FF);
653 /* fprintf(stderr, "UTF-32 %x\n",wstr[0]); */
656 wstr[0] = (wchar_t)wc;
660 #if defined(_WIN32) || defined(__CYGWIN__)
661 /* On Windows we convert UTF-16 always to UTF-8 */
662 len = (size_t)(WideCharToMultiByte(CP_UTF8, 0, wstr, -1, mbs, sizeof(mbs), NULL, NULL) -1);
664 /* On Unix we convert UTF-16 to the locale encoding */
665 len = wcstombs(mbs, wstr, sizeof(mbs));
666 /* fprintf(stderr, "len %d\n",len); */
669 if ( len == (size_t)(-1) )
670 { /* Stop when there is a conversion error */
671 ipFlag->status |= UNICODE_CONVERSION_ERROR ;
674 for (i=0; i<len; i++)
676 if (fputc(mbs[i], f) == EOF)