Imported Upstream version 6.0
[platform/upstream/dos2unix.git] / common.c
1 /*
2  *   Copyright (C) 2009-2012 Erwin Waterlander
3  *   All rights reserved.
4  * 
5  *   Redistribution and use in source and binary forms, with or without
6  *   modification, are permitted provided that the following conditions
7  *   are met:
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
12  *      the distribution.
13  * 
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.
25  */
26
27 #include "common.h"
28 #if defined(D2U_UNICODE)
29 #if defined(WIN32) || defined(__CYGWIN__)
30 #include <windows.h>
31 #endif
32 #endif
33
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
38 # endif
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
45 # endif
46 #endif
47
48
49 /******************************************************************
50  *
51  * int symbolic_link(char *path)
52  *
53  * test if *path points to a file that exists and is a symbolic link
54  *
55  * returns 1 on success, 0 when it fails.
56  *
57  ******************************************************************/
58 int symbolic_link(char *path)
59 {
60 #ifdef S_ISLNK
61    struct stat buf;
62
63    if (STAT(path, &buf) == 0)
64    {
65       if (S_ISLNK(buf.st_mode))
66          return(1);
67    }
68 #endif
69    return(0);
70 }
71
72 /******************************************************************
73  *
74  * int regfile(char *path, int allowSymlinks)
75  *
76  * test if *path points to a regular file (or is a symbolic link,
77  * if allowSymlinks != 0).
78  *
79  * returns 0 on success, -1 when it fails.
80  *
81  ******************************************************************/
82 int regfile(char *path, int allowSymlinks, CFlag *ipFlag, char *progname)
83 {
84    struct stat buf;
85    char *errstr;
86
87    if (STAT(path, &buf) == 0)
88    {
89 #if DEBUG
90       fprintf(stderr, "%s: %s MODE 0%o ", progname, path, buf.st_mode);
91 #ifdef S_ISSOCK
92       if (S_ISSOCK(buf.st_mode))
93          fprintf(stderr, " (socket)");
94 #endif
95 #ifdef S_ISLNK
96       if (S_ISLNK(buf.st_mode))
97          fprintf(stderr, " (symbolic link)");
98 #endif
99       if (S_ISREG(buf.st_mode))
100          fprintf(stderr, " (regular file)");
101       if (S_ISBLK(buf.st_mode))
102          fprintf(stderr, " (block device)");
103       if (S_ISDIR(buf.st_mode))
104          fprintf(stderr, " (directory)");
105       if (S_ISCHR(buf.st_mode))
106          fprintf(stderr, " (character device)");
107       if (S_ISFIFO(buf.st_mode))
108          fprintf(stderr, " (FIFO)");
109       fprintf(stderr, "\n");
110 #endif
111       if ((S_ISREG(buf.st_mode))
112 #ifdef S_ISLNK
113           || (S_ISLNK(buf.st_mode) && allowSymlinks)
114 #endif
115          )
116          return(0);
117       else
118          return(-1);
119    }
120    else
121    {
122      if (!ipFlag->Quiet)
123      {
124        ipFlag->error = errno;
125        errstr = strerror(errno);
126        fprintf(stderr, "%s: %s: %s\n", progname, path, errstr);
127      }
128      return(-1);
129    }
130 }
131
132 /******************************************************************
133  *
134  * int regfile_target(char *path)
135  *
136  * test if *path points to a regular file (follow symbolic link)
137  *
138  * returns 0 on success, -1 when it fails.
139  *
140  ******************************************************************/
141 int regfile_target(char *path, CFlag *ipFlag, char *progname)
142 {
143    struct stat buf;
144    char *errstr;
145
146    if (stat(path, &buf) == 0)
147    {
148       if (S_ISREG(buf.st_mode))
149          return(0);
150       else
151          return(-1);
152    }
153    else
154    {
155      if (!ipFlag->Quiet)
156      {
157        ipFlag->error = errno;
158        errstr = strerror(errno);
159        fprintf(stderr, "%s: %s: %s\n", progname, path, errstr);
160      }
161      return(-1);
162    }
163 }
164
165 void PrintBSDLicense(void)
166 {
167   fprintf(stderr, "%s", _("\
168 Redistribution and use in source and binary forms, with or without\n\
169 modification, are permitted provided that the following conditions\n\
170 are met:\n\
171 1. Redistributions of source code must retain the above copyright\n\
172    notice, this list of conditions and the following disclaimer.\n\
173 2. Redistributions in binary form must reproduce the above copyright\n\
174    notice in the documentation and/or other materials provided with\n\
175    the distribution.\n\n\
176 \
177 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY\n\
178 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\
179 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n\
180 PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE\n\
181 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n\
182 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\n\
183 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n\
184 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n\
185 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n\
186 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN\n\
187 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\
188 "));
189 }
190
191 void PrintUsage(char *progname)
192 {
193   fprintf(stderr, _("\
194 %s %s (%s)\n\
195 Usage: %s [options] [file ...] [-n infile outfile ...]\n\
196  -ascii                convert only line breaks (default)\n\
197  -iso                  conversion between DOS and ISO-8859-1 character set\n\
198    -1252               Use Windows code page 1252 (Western European)\n\
199    -437                Use DOS code page 437 (US) (default)\n\
200    -850                Use DOS code page 850 (Western European)\n\
201    -860                Use DOS code page 860 (Portuguese)\n\
202    -863                Use DOS code page 863 (French Canadian)\n\
203    -865                Use DOS code page 865 (Nordic)\n\
204  -7                    Convert 8 bit characters to 7 bit space\n\
205  -c, --convmode        conversion mode\n\
206    convmode            ascii, 7bit, iso, mac, default to ascii\n\
207  -f, --force           force conversion of binary files\n\
208  -h, --help            give this help\n\
209  -k, --keepdate        keep output file date\n\
210  -L, --license         display software license\n\
211  -l, --newline         add additional newline\n\
212  -m, --add-bom         add UTF-8 Byte Order Mark\n\
213  -n, --newfile         write to new file\n\
214    infile              original file in new file mode\n\
215    outfile             output file in new file mode\n\
216  -o, --oldfile         write to old file\n\
217    file ...            files to convert in old file mode\n\
218  -q, --quiet           quiet mode, suppress all warnings\n\
219                        always on in stdio mode\n\
220  -s, --safe            skip binary files (default)\n"),
221  progname, VER_REVISION, VER_DATE, progname);
222 #ifdef S_ISLNK
223   fprintf(stderr, _("\
224  -F, --follow-symlink  follow symbolic links and convert the targets\n\
225  -R, --replace-symlink replace symbolic links with converted files\n\
226                        (original target files remain unchanged)\n\
227  -S, --skip-symlink    keep symbolic links and targets unchanged (default)\n"));
228 #endif
229   fprintf(stderr, _("\
230  -V, --version         display version number\n"));
231 }
232
233
234 void PrintVersion(char *progname)
235 {
236   fprintf(stderr, "%s %s (%s)\n", progname, VER_REVISION, VER_DATE);
237 #if DEBUG
238   fprintf(stderr, "VER_AUTHOR: %s\n", VER_AUTHOR);
239 #endif
240 #if defined(__WATCOMC__) && defined(__I86__)
241   fprintf(stderr, "%s", _("DOS 16 bit version (WATCOMC).\n"));
242 #elif defined(__TURBOC__)
243   fprintf(stderr, "%s", _("DOS 16 bit version (TURBOC).\n"));
244 #elif defined(__WATCOMC__) && defined(__DOS__)
245   fprintf(stderr, "%s", _("DOS 32 bit version (WATCOMC).\n"));
246 #elif defined(DJGPP)
247   fprintf(stderr, "%s", _("DOS 32 bit version (DJGPP).\n"));
248 #elif defined(__MSYS__)
249   fprintf(stderr, "%s", _("MSYS version.\n"));
250 #elif defined(__CYGWIN__)
251   fprintf(stderr, "%s", _("Cygwin version.\n"));
252 #elif defined(__WIN64__)
253   fprintf(stderr, "%s", _("Windows 64 bit version (MinGW-w64).\n"));
254 #elif defined(__WATCOMC__) && defined(__NT__)
255   fprintf(stderr, "%s", _("Windows 32 bit version (WATCOMC).\n"));
256 #elif defined(__WIN32__)
257   fprintf(stderr, "%s", _("Windows 32 bit version (MinGW).\n"));
258 #elif defined (__OS2__) /* OS/2 Warp */
259   fprintf(stderr, "%s", _("OS/2 version.\n"));
260 #endif
261 #ifdef D2U_UNICODE
262   fprintf(stderr, "%s", _("With Unicode UTF-16 support.\n"));
263 #else
264   fprintf(stderr, "%s", _("Without Unicode UTF-16 support.\n"));
265 #endif
266 #ifdef ENABLE_NLS
267   fprintf(stderr, "%s", _("With native language support.\n"));
268 #else
269   fprintf(stderr, "%s", "Without native language support.\n");
270 #endif
271 }
272
273 #ifdef ENABLE_NLS
274 void PrintLocaledir(char *localedir)
275 {
276   fprintf(stderr, "LOCALEDIR: %s\n", localedir);
277 }
278 #endif
279
280 /* opens file of name ipFN in read only mode
281  * RetVal: NULL if failure
282  *         file stream otherwise
283  */
284 FILE* OpenInFile(char *ipFN)
285 {
286   return (fopen(ipFN, R_CNTRL));
287 }
288
289
290 /* opens file of name ipFN in write only mode
291  * RetVal: NULL if failure
292  *         file stream otherwise
293  */
294 FILE* OpenOutFile(int fd)
295 {
296   return (fdopen(fd, W_CNTRL));
297 }
298
299 #if defined(__TURBOC__) || defined(__MSYS__)
300 char *dirname(char *path)
301 {
302   char *ptr;
303
304   if (( path == NULL) || (((ptr=strrchr(path,'/')) == NULL) && ((ptr=strrchr(path,'\\')) == NULL)) )
305     return ".";
306   else
307   {
308     *ptr = '\0';
309     return(path);
310   }
311 }
312 #endif
313
314 #ifdef NO_MKSTEMP
315 FILE* MakeTempFileFrom(const char *OutFN, char **fname_ret)
316 #else
317 int MakeTempFileFrom(const char *OutFN, char **fname_ret)
318 #endif
319 {
320   char *cpy = strdup(OutFN);
321   char *dir = NULL;
322   size_t fname_len = 0;
323   char  *fname_str = NULL;
324 #ifdef NO_MKSTEMP
325   char *name;
326   FILE *fd = NULL;
327 #else
328   int fd = -1;
329 #endif
330   
331   *fname_ret = NULL;
332
333   if (!cpy)
334     goto make_failed;
335   
336   dir = dirname(cpy);
337   
338   fname_len = strlen(dir) + strlen("/d2utmpXXXXXX") + sizeof (char);
339   if (!(fname_str = malloc(fname_len)))
340     goto make_failed;
341   sprintf(fname_str, "%s%s", dir, "/d2utmpXXXXXX");
342   *fname_ret = fname_str;
343
344   free(cpy);
345
346 #ifdef NO_MKSTEMP
347   name = mktemp(fname_str);
348   *fname_ret = name;
349   if ((fd = fopen(fname_str, W_CNTRL)) == NULL)
350     goto make_failed;
351 #else
352   if ((fd = mkstemp(fname_str)) == -1)
353     goto make_failed;
354 #endif
355   
356   return (fd);
357   
358  make_failed:
359   free(*fname_ret);
360   *fname_ret = NULL;
361 #ifdef NO_MKSTEMP
362   return (NULL);
363 #else
364   return (-1);
365 #endif
366 }
367
368 /* Test if *lFN is the name of a symbolic link.  If not, set *rFN equal
369  * to lFN, and return 0.  If so, then use canonicalize_file_name or
370  * realpath to determine the pointed-to file; the resulting name is
371  * stored in newly allocated memory, *rFN is set to point to that value,
372  * and 1 is returned. On error, -1 is returned and errno is set as
373  * appropriate.
374  *
375  * Note that if symbolic links are not supported, then 0 is always returned
376  * and *rFN = lFN.
377  *
378  * RetVal: 0 if success, and *lFN is not a symlink
379  *         1 if success, and *lFN is a symlink
380  *         -1 otherwise
381  */
382 int ResolveSymbolicLink(char *lFN, char **rFN, CFlag *ipFlag, char *progname)
383 {
384   int RetVal = 0;
385 #ifdef S_ISLNK
386   struct stat StatBuf;
387   char *errstr;
388   char *targetFN = NULL;
389
390   if (STAT(lFN, &StatBuf))
391   {
392     if (!ipFlag->Quiet)
393     {
394       ipFlag->error = errno;
395       errstr = strerror(errno);
396       fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
397     }
398     RetVal = -1;
399   }
400   else if (S_ISLNK(StatBuf.st_mode))
401   {
402 #if USE_CANONICALIZE_FILE_NAME
403     targetFN = canonicalize_file_name(lFN);
404     if (!targetFN)
405     {
406       if (!ipFlag->Quiet)
407       {
408         errstr = strerror(errno);
409         fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
410         ipFlag->error = 1;
411       }
412       RetVal = -1;
413     }
414     else
415     {
416       *rFN = targetFN;
417       RetVal = 1;
418     }
419 #else
420     /* Sigh. Use realpath, but realize that it has a fatal
421      * flaw: PATH_MAX isn't necessarily the maximum path
422      * length -- so realpath() might fail. */
423     targetFN = (char *) malloc(PATH_MAX * sizeof(char));
424     if (!targetFN)
425     {
426       if (!ipFlag->Quiet)
427       {
428         errstr = strerror(errno);
429         fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
430         ipFlag->error = 1;
431       }
432       RetVal = -1;
433     }
434     else
435     {
436       /* is there any platform with S_ISLNK that does not have realpath? */
437       char *rVal = realpath(lFN, targetFN);
438       if (!rVal)
439       {
440         if (!ipFlag->Quiet)
441         {
442           errstr = strerror(errno);
443           fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
444           ipFlag->error = 1;
445         }
446         free(targetFN);
447         RetVal = -1;
448       }
449       else
450       {
451         *rFN = rVal;
452         RetVal = 1;
453       }
454     }
455 #endif /* !USE_CANONICALIZE_FILE_NAME */
456   }
457   else
458     *rFN = lFN;
459 #else  /* !S_ISLNK */
460   *rFN = lFN;
461 #endif /* !S_ISLNK */
462   return RetVal;
463 }
464
465 FILE *read_bom (FILE *f, int *bomtype)
466 {
467   int bom[3];
468   /* BOMs
469    * UTF16-LE  ff fe
470    * UTF16-BE  fe ff
471    * UTF-8     ef bb bf
472    */
473
474   *bomtype = FILE_MBS;
475
476    /* Check for BOM */
477    if  (f != NULL)
478    {
479       if ((bom[0] = fgetc(f)) == EOF)
480       {
481          ungetc(bom[0], f);
482          *bomtype = FILE_MBS;
483          return(f);
484       }
485       if ((bom[0] != 0xff) && (bom[0] != 0xfe) && (bom[0] != 0xef))
486       {
487          ungetc(bom[0], f);
488          *bomtype = FILE_MBS;
489          return(f);
490       }
491       if ((bom[1] = fgetc(f)) == EOF)
492       {
493          ungetc(bom[1], f);
494          ungetc(bom[0], f);
495          *bomtype = FILE_MBS;
496          return(f);
497       }
498       if ((bom[0] == 0xff) && (bom[1] == 0xfe)) /* UTF16-LE */
499       {
500          *bomtype = FILE_UTF16LE;
501          return(f);
502       }
503       if ((bom[0] == 0xfe) && (bom[1] == 0xff)) /* UTF16-BE */
504       {
505          *bomtype = FILE_UTF16BE;
506          return(f);
507       }
508       if ((bom[2] = fgetc(f)) == EOF)
509       {
510          ungetc(bom[2], f);
511          ungetc(bom[1], f);
512          ungetc(bom[0], f);
513          *bomtype = FILE_MBS;
514          return(f);
515       }
516       if ((bom[0] == 0xef) && (bom[1] == 0xbb) && (bom[2]== 0xbf)) /* UTF-8 */
517       {
518          *bomtype = FILE_UTF8;
519          return(f);
520       }
521       ungetc(bom[2], f);
522       ungetc(bom[1], f);
523       ungetc(bom[0], f);
524       *bomtype = FILE_MBS;
525       return(f);
526    }
527   return(f);
528 }
529
530
531 #ifdef D2U_UNICODE
532 wint_t d2u_getwc(FILE *f, int bomtype)
533 {
534    int c_trail, c_lead;
535    wint_t wc;
536
537    if (((c_lead=fgetc(f)) == EOF)  || ((c_trail=fgetc(f)) == EOF))
538       return(WEOF);
539
540    if (bomtype == FILE_UTF16LE)  /* UTF16 little endian */
541    {
542       c_trail <<=8;
543       wc = (wint_t)(c_trail + c_lead) ;
544    } else {                      /* UTF16 big endian */
545       c_lead <<=8;
546       wc = (wint_t)(c_trail + c_lead) ;
547    }
548    return(wc);
549 }
550
551 wint_t d2u_ungetwc(wint_t wc, FILE *f, int bomtype)
552 {
553    int c_trail, c_lead;
554
555    if (bomtype == FILE_UTF16LE)  /* UTF16 little endian */
556    {
557       c_trail = (int)(wc & 0xff00);
558       c_trail >>=8;
559       c_lead  = (int)(wc & 0xff);
560    } else {                      /* UTF16 big endian */
561       c_lead = (int)(wc & 0xff00);
562       c_lead >>=8;
563       c_trail  = (int)(wc & 0xff);
564    }
565
566    /* push back in reverse order */
567    if ((ungetc(c_trail,f) == EOF)  || (ungetc(c_lead,f) == EOF))
568       return(WEOF);
569    return(wc);
570 }
571
572 /* Put wide character */
573 wint_t d2u_putwc(wint_t wc, FILE *f, CFlag *ipFlag)
574 {
575    static char mbs[8];
576    static wchar_t lead, trail;
577    static wchar_t wstr[3];
578    size_t i,len;
579
580    if ((wc >= 0xd800) && (wc < 0xdc00))
581    {
582       /* fprintf(stderr, "UTF-16 lead %x\n",wc); */
583       lead = (wchar_t)wc; /* lead (high) surrogate */
584       return(wc);
585    }
586    if ((wc >= 0xdc00) && (wc < 0xe000))
587    {
588       /* fprintf(stderr, "UTF-16 trail %x\n",wc); */
589       trail = (wchar_t)wc; /* trail (low) surrogate */
590 #if defined(WIN32) || defined(__CYGWIN__)
591       /* On Windows (including Cygwin) wchar_t is 16 bit */
592       /* We cannot decode an UTF-16 surrogate pair, because it will
593          not fit in a 16 bit wchar_t. */
594       wstr[0] = lead;
595       wstr[1] = trail;
596       wstr[2] = L'\0';
597 #else      
598       /* On Unix wchar_t is 32 bit */
599       /* When we don't decode the UTF-16 surrogate pair, wcstombs() does not
600        * produce the same UTF-8 as WideCharToMultiByte().  The UTF-8 output
601        * produced by wcstombs() is bigger, because it just translates the wide
602        * characters in the range 0xD800..0xDBFF individually to UTF-8 sequences
603        * (although these code points are reserved for use only as surrogate
604        * pairs in UTF-16). Probably because on Unix the size of wide char
605        * (wchar_t) is 32 bit, wcstombs assumes the encoding is UTF-32, and
606        * ignores UTF-16 surrogates all together.  Some smart viewers can still
607        * display this UTF-8 correctly (like Total Commander lister), however
608        * the UTF-8 is not readable by Windows Notepad (on Windows 7).  When we
609        * decode the UTF-16 surrogate pairs ourselves the wcstombs() UTF-8
610        * output is identical to what WideCharToMultiByte() produces, and is
611        * readable by Notepad.
612        */ 
613       /* Decode UTF-16 surrogate pair */
614       wstr[0] = 0x10000;
615       wstr[0] += (lead & 0x03FF) << 10;
616       wstr[0] += (trail & 0x03FF);
617       wstr[1] = L'\0';
618 #endif
619    } else {
620       wstr[0] = (wchar_t)wc;
621       wstr[1] = L'\0';
622    }
623
624 #if defined(WIN32) || defined(__CYGWIN__)
625    /* On Windows we convert UTF-16 always to UTF-8 */
626    len = (size_t)(WideCharToMultiByte(CP_UTF8, 0, wstr, -1, mbs, sizeof(mbs), NULL, NULL) -1);
627 #else
628    /* On Unix we convert UTF-16 to the locale encoding */
629    len = wcstombs(mbs, wstr, sizeof(mbs));
630 #endif
631
632    if ( len == (size_t)(-1) )
633    {  /* Stop when there is a conversion error */
634       ipFlag->status |= UNICODE_CONVERSION_ERROR ;
635       return(WEOF);
636    } else {
637       for (i=0; i<len; i++)
638       {
639          if (fputc(mbs[i], f) == EOF)
640             return(WEOF);
641       }
642    }
643    return(wc);
644 }
645 #endif