Imported Upstream version 6.0.5
[platform/upstream/dos2unix.git] / common.c
1 /*
2  *   Copyright (C) 2009-2014 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
59 #if (defined(_WIN32) && !defined(__CYGWIN__))
60
61 int symbolic_link(const char *path)
62 {
63    DWORD attrs;
64
65    attrs = GetFileAttributes(path);
66
67    if (attrs == INVALID_FILE_ATTRIBUTES)
68       return(0);
69
70    return ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
71 }
72
73 #else
74 int symbolic_link(const char *path)
75 {
76 #ifdef S_ISLNK
77    struct stat buf;
78
79    if (STAT(path, &buf) == 0)
80    {
81       if (S_ISLNK(buf.st_mode))
82          return(1);
83    }
84 #endif
85    return(0);
86 }
87 #endif
88
89 /******************************************************************
90  *
91  * int regfile(char *path, int allowSymlinks)
92  *
93  * test if *path points to a regular file (or is a symbolic link,
94  * if allowSymlinks != 0).
95  *
96  * returns 0 on success, -1 when it fails.
97  *
98  ******************************************************************/
99 int regfile(char *path, int allowSymlinks, CFlag *ipFlag, char *progname)
100 {
101    struct stat buf;
102    char *errstr;
103
104    if (STAT(path, &buf) == 0)
105    {
106 #if DEBUG
107       fprintf(stderr, "%s: %s MODE 0%o ", progname, path, buf.st_mode);
108 #ifdef S_ISSOCK
109       if (S_ISSOCK(buf.st_mode))
110          fprintf(stderr, " (socket)");
111 #endif
112 #ifdef S_ISLNK
113       if (S_ISLNK(buf.st_mode))
114          fprintf(stderr, " (symbolic link)");
115 #endif
116       if (S_ISREG(buf.st_mode))
117          fprintf(stderr, " (regular file)");
118 #ifdef S_ISBLK
119       if (S_ISBLK(buf.st_mode))
120          fprintf(stderr, " (block device)");
121 #endif
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");
129 #endif
130       if ((S_ISREG(buf.st_mode))
131 #ifdef S_ISLNK
132           || (S_ISLNK(buf.st_mode) && allowSymlinks)
133 #endif
134          )
135          return(0);
136       else
137          return(-1);
138    }
139    else
140    {
141      if (!ipFlag->Quiet)
142      {
143        ipFlag->error = errno;
144        errstr = strerror(errno);
145        fprintf(stderr, "%s: %s: %s\n", progname, path, errstr);
146      }
147      return(-1);
148    }
149 }
150
151 /******************************************************************
152  *
153  * int regfile_target(char *path)
154  *
155  * test if *path points to a regular file (follow symbolic link)
156  *
157  * returns 0 on success, -1 when it fails.
158  *
159  ******************************************************************/
160 int regfile_target(char *path, CFlag *ipFlag, char *progname)
161 {
162    struct stat buf;
163    char *errstr;
164
165    if (stat(path, &buf) == 0)
166    {
167       if (S_ISREG(buf.st_mode))
168          return(0);
169       else
170          return(-1);
171    }
172    else
173    {
174      if (!ipFlag->Quiet)
175      {
176        ipFlag->error = errno;
177        errstr = strerror(errno);
178        fprintf(stderr, "%s: %s: %s\n", progname, path, errstr);
179      }
180      return(-1);
181    }
182 }
183
184 void PrintBSDLicense(void)
185 {
186   printf("%s", _("\
187 Redistribution and use in source and binary forms, with or without\n\
188 modification, are permitted provided that the following conditions\n\
189 are met:\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\
195 "));
196   printf("%s", _("\
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\
208 "));
209 }
210
211 void PrintUsage(char *progname)
212 {
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"));
239 #ifdef D2U_UNICODE
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"));
242 #endif
243 #ifdef S_ISLNK
244   printf(_(" -F, --follow-symlink  follow symbolic links and convert the targets\n"));
245 #endif
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"));
250 #endif
251   printf(_(" -V, --version         display version number\n"));
252 }
253
254 #define MINGW32_W64 1
255
256 void PrintVersion(char *progname)
257 {
258   printf("%s %s (%s)\n", progname, VER_REVISION, VER_DATE);
259 #if DEBUG
260   printf("VER_AUTHOR: %s\n", VER_AUTHOR);
261 #endif
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"));
290 #endif
291 #ifdef D2U_UNICODE
292   printf("%s", _("With Unicode UTF-16 support.\n"));
293 #else
294   printf("%s", _("Without Unicode UTF-16 support.\n"));
295 #endif
296 #ifdef ENABLE_NLS
297   printf("%s", _("With native language support.\n"));
298 #else
299   printf("%s", "Without native language support.\n");
300 #endif
301 }
302
303 #ifdef ENABLE_NLS
304 void PrintLocaledir(char *localedir)
305 {
306   printf("LOCALEDIR: %s\n", localedir);
307 }
308 #endif
309
310 /* opens file of name ipFN in read only mode
311  * RetVal: NULL if failure
312  *         file stream otherwise
313  */
314 FILE* OpenInFile(char *ipFN)
315 {
316   return (fopen(ipFN, R_CNTRL));
317 }
318
319
320 /* opens file of name ipFN in write only mode
321  * RetVal: NULL if failure
322  *         file stream otherwise
323  */
324 FILE* OpenOutFile(int fd)
325 {
326   return (fdopen(fd, W_CNTRL));
327 }
328
329 #if defined(__TURBOC__) || defined(__MSYS__) || defined(_MSC_VER)
330 char *dirname(char *path)
331 {
332   char *ptr;
333
334   if (( path == NULL) || (((ptr=strrchr(path,'/')) == NULL) && ((ptr=strrchr(path,'\\')) == NULL)) )
335     return ".";
336   else
337   {
338     *ptr = '\0';
339     return(path);
340   }
341 }
342 #endif
343
344 #ifdef NO_MKSTEMP
345 FILE* MakeTempFileFrom(const char *OutFN, char **fname_ret)
346 #else
347 int MakeTempFileFrom(const char *OutFN, char **fname_ret)
348 #endif
349 {
350   char *cpy = strdup(OutFN);
351   char *dir = NULL;
352   size_t fname_len = 0;
353   char  *fname_str = NULL;
354 #ifdef NO_MKSTEMP
355   char *name;
356   FILE *fd = NULL;
357 #else
358   int fd = -1;
359 #endif
360
361   *fname_ret = NULL;
362
363   if (!cpy)
364     goto make_failed;
365
366   dir = dirname(cpy);
367
368   fname_len = strlen(dir) + strlen("/d2utmpXXXXXX") + sizeof (char);
369   if (!(fname_str = malloc(fname_len)))
370     goto make_failed;
371   sprintf(fname_str, "%s%s", dir, "/d2utmpXXXXXX");
372   *fname_ret = fname_str;
373
374   free(cpy);
375
376 #ifdef NO_MKSTEMP
377   name = mktemp(fname_str);
378   *fname_ret = name;
379   if ((fd = fopen(fname_str, W_CNTRL)) == NULL)
380     goto make_failed;
381 #else
382   if ((fd = mkstemp(fname_str)) == -1)
383     goto make_failed;
384 #endif
385
386   return (fd);
387
388  make_failed:
389   free(*fname_ret);
390   *fname_ret = NULL;
391 #ifdef NO_MKSTEMP
392   return (NULL);
393 #else
394   return (-1);
395 #endif
396 }
397
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
403  * appropriate.
404  *
405  * Note that if symbolic links are not supported, then 0 is always returned
406  * and *rFN = lFN.
407  *
408  * RetVal: 0 if success, and *lFN is not a symlink
409  *         1 if success, and *lFN is a symlink
410  *         -1 otherwise
411  */
412 int ResolveSymbolicLink(char *lFN, char **rFN, CFlag *ipFlag, char *progname)
413 {
414   int RetVal = 0;
415 #ifdef S_ISLNK
416   struct stat StatBuf;
417   char *errstr;
418   char *targetFN = NULL;
419
420   if (STAT(lFN, &StatBuf))
421   {
422     if (!ipFlag->Quiet)
423     {
424       ipFlag->error = errno;
425       errstr = strerror(errno);
426       fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
427     }
428     RetVal = -1;
429   }
430   else if (S_ISLNK(StatBuf.st_mode))
431   {
432 #if USE_CANONICALIZE_FILE_NAME
433     targetFN = canonicalize_file_name(lFN);
434     if (!targetFN)
435     {
436       if (!ipFlag->Quiet)
437       {
438         errstr = strerror(errno);
439         fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
440         ipFlag->error = 1;
441       }
442       RetVal = -1;
443     }
444     else
445     {
446       *rFN = targetFN;
447       RetVal = 1;
448     }
449 #else
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));
454     if (!targetFN)
455     {
456       if (!ipFlag->Quiet)
457       {
458         errstr = strerror(errno);
459         fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
460         ipFlag->error = 1;
461       }
462       RetVal = -1;
463     }
464     else
465     {
466       /* is there any platform with S_ISLNK that does not have realpath? */
467       char *rVal = realpath(lFN, targetFN);
468       if (!rVal)
469       {
470         if (!ipFlag->Quiet)
471         {
472           errstr = strerror(errno);
473           fprintf(stderr, "%s: %s: %s\n", progname, lFN, errstr);
474           ipFlag->error = 1;
475         }
476         free(targetFN);
477         RetVal = -1;
478       }
479       else
480       {
481         *rFN = rVal;
482         RetVal = 1;
483       }
484     }
485 #endif /* !USE_CANONICALIZE_FILE_NAME */
486   }
487   else
488     *rFN = lFN;
489 #else  /* !S_ISLNK */
490   *rFN = lFN;
491 #endif /* !S_ISLNK */
492   return RetVal;
493 }
494
495 FILE *read_bom (FILE *f, int *bomtype)
496 {
497   int bom[3];
498   /* BOMs
499    * UTF16-LE  ff fe
500    * UTF16-BE  fe ff
501    * UTF-8     ef bb bf
502    */
503
504   *bomtype = FILE_MBS;
505
506    /* Check for BOM */
507    if  (f != NULL)
508    {
509       if ((bom[0] = fgetc(f)) == EOF)
510       {
511          ungetc(bom[0], f);
512          *bomtype = FILE_MBS;
513          return(f);
514       }
515       if ((bom[0] != 0xff) && (bom[0] != 0xfe) && (bom[0] != 0xef))
516       {
517          ungetc(bom[0], f);
518          *bomtype = FILE_MBS;
519          return(f);
520       }
521       if ((bom[1] = fgetc(f)) == EOF)
522       {
523          ungetc(bom[1], f);
524          ungetc(bom[0], f);
525          *bomtype = FILE_MBS;
526          return(f);
527       }
528       if ((bom[0] == 0xff) && (bom[1] == 0xfe)) /* UTF16-LE */
529       {
530          *bomtype = FILE_UTF16LE;
531          return(f);
532       }
533       if ((bom[0] == 0xfe) && (bom[1] == 0xff)) /* UTF16-BE */
534       {
535          *bomtype = FILE_UTF16BE;
536          return(f);
537       }
538       if ((bom[2] = fgetc(f)) == EOF)
539       {
540          ungetc(bom[2], f);
541          ungetc(bom[1], f);
542          ungetc(bom[0], f);
543          *bomtype = FILE_MBS;
544          return(f);
545       }
546       if ((bom[0] == 0xef) && (bom[1] == 0xbb) && (bom[2]== 0xbf)) /* UTF-8 */
547       {
548          *bomtype = FILE_UTF8;
549          return(f);
550       }
551       ungetc(bom[2], f);
552       ungetc(bom[1], f);
553       ungetc(bom[0], f);
554       *bomtype = FILE_MBS;
555       return(f);
556    }
557   return(f);
558 }
559
560
561 #ifdef D2U_UNICODE
562 wint_t d2u_getwc(FILE *f, int bomtype)
563 {
564    int c_trail, c_lead;
565    wint_t wc;
566
567    if (((c_lead=fgetc(f)) == EOF)  || ((c_trail=fgetc(f)) == EOF))
568       return(WEOF);
569
570    if (bomtype == FILE_UTF16LE)  /* UTF16 little endian */
571    {
572       c_trail <<=8;
573       wc = (wint_t)(c_trail + c_lead) ;
574    } else {                      /* UTF16 big endian */
575       c_lead <<=8;
576       wc = (wint_t)(c_trail + c_lead) ;
577    }
578    return(wc);
579 }
580
581 wint_t d2u_ungetwc(wint_t wc, FILE *f, int bomtype)
582 {
583    int c_trail, c_lead;
584
585    if (bomtype == FILE_UTF16LE)  /* UTF16 little endian */
586    {
587       c_trail = (int)(wc & 0xff00);
588       c_trail >>=8;
589       c_lead  = (int)(wc & 0xff);
590    } else {                      /* UTF16 big endian */
591       c_lead = (int)(wc & 0xff00);
592       c_lead >>=8;
593       c_trail  = (int)(wc & 0xff);
594    }
595
596    /* push back in reverse order */
597    if ((ungetc(c_trail,f) == EOF)  || (ungetc(c_lead,f) == EOF))
598       return(WEOF);
599    return(wc);
600 }
601
602 /* Put wide character */
603 wint_t d2u_putwc(wint_t wc, FILE *f, CFlag *ipFlag)
604 {
605    static char mbs[8];
606    static wchar_t lead, trail;
607    static wchar_t wstr[3];
608    size_t i,len;
609
610    if ((wc >= 0xd800) && (wc < 0xdc00))
611    {
612       /* fprintf(stderr, "UTF-16 lead %x\n",wc); */
613       lead = (wchar_t)wc; /* lead (high) surrogate */
614       return(wc);
615    }
616    if ((wc >= 0xdc00) && (wc < 0xe000))
617    {
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. */
624       wstr[0] = lead;
625       wstr[1] = trail;
626       wstr[2] = L'\0';
627 #else
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
634        * pairs in UTF-16).
635        *
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.
641        *
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.
647        */
648       /* Decode UTF-16 surrogate pair */
649       wstr[0] = 0x10000;
650       wstr[0] += (lead & 0x03FF) << 10;
651       wstr[0] += (trail & 0x03FF);
652       wstr[1] = L'\0';
653       /* fprintf(stderr, "UTF-32  %x\n",wstr[0]); */
654 #endif
655    } else {
656       wstr[0] = (wchar_t)wc;
657       wstr[1] = L'\0';
658    }
659
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);
663 #else
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); */
667 #endif
668
669    if ( len == (size_t)(-1) )
670    {  /* Stop when there is a conversion error */
671       ipFlag->status |= UNICODE_CONVERSION_ERROR ;
672       return(WEOF);
673    } else {
674       for (i=0; i<len; i++)
675       {
676          if (fputc(mbs[i], f) == EOF)
677             return(WEOF);
678       }
679    }
680    return(wc);
681 }
682 #endif