Imported Upstream version 6.0
[platform/upstream/dos2unix.git] / unix2dos.c
1 /*
2  *  Name: unix2dos
3  *  Documentation:
4  *    Convert lf ('\x0a') characters in a file to cr lf ('\x0d' '\x0a')
5  *    combinations.
6  *
7  *  The dos2unix package is distributed under FreeBSD style license.
8  *  See also http://www.freebsd.org/copyright/freebsd-license.html
9  *  --------
10  * 
11  *  Copyright (C) 2009-2012 Erwin Waterlander
12  *  Copyright (C) 1994-1995 Benjamin Lin.
13  *  All rights reserved.
14  *
15  *  Redistribution and use in source and binary forms, with or without
16  *  modification, are permitted provided that the following conditions
17  *  are met:
18  *  1. Redistributions of source code must retain the above copyright
19  *     notice, this list of conditions and the following disclaimer.
20  *  2. Redistributions in binary form must reproduce the above copyright
21  *     notice in the documentation and/or other materials provided with
22  *     the distribution.
23  *
24  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
25  *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27  *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
28  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30  *  OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32  *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
33  *  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
34  *  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  *  == 1.0 == 1989.10.04 == John Birchfield (jb@koko.csustan.edu)
37  *  == 1.1 == 1994.12.20 == Benjamin Lin (blin@socs.uts.edu.au)
38  *     Cleaned up for Borland C/C++ 4.02
39  *  == 1.2 == 1995.03.09 == Benjamin Lin (blin@socs.uts.edu.au)
40  *     Fixed minor typo error
41  *  == 1.3 == 1995.03.16 == Benjamin Lin (blin@socs.uts.edu.au)
42  *     Modified to more conform to UNIX style.
43  *  == 2.0 == 1995.03.19 == Benjamin Lin (blin@socs.uts.edu.au)
44  *     Rewritten from scratch.
45  *  == 2.2 == 1995.03.30 == Benjamin Lin (blin@socs.uts.edu.au)
46  *     Conversion from SunOS charset implemented.
47  *
48  *  See ChangeLog.txt for complete version history.
49  *
50  */
51
52
53 /* #define DEBUG 1 */
54
55 #include "common.h"
56 #include "unix2dos.h"
57 #include "querycp.h"
58 #ifdef D2U_UNICODE
59 #ifndef MSDOS  /* Unix, Cygwin */
60 # include <langinfo.h>
61 #endif
62 #endif
63
64 void PrintLicense(void)
65 {
66   fprintf(stderr, "%s", _("\
67 Copyright (C) 2009-2012 Erwin Waterlander\n\
68 Copyright (C) 1994-1995 Benjamin Lin\n\
69 All rights reserved.\n\n"));
70   PrintBSDLicense();
71 }
72
73 #ifdef D2U_UNICODE
74 void AddDOSNewLineW(FILE* ipOutF, CFlag *ipFlag, wint_t CurChar, wint_t PrevChar)
75 {
76   if (ipFlag->NewLine) {  /* add additional CR-LF? */
77     /* Don't add line ending if it is a DOS line ending. Only in case of Unix line ending. */
78     if ((CurChar == 0x0a) && (PrevChar != 0x0d)) {
79       d2u_putwc(0x0d, ipOutF, ipFlag);
80       d2u_putwc(0x0a, ipOutF, ipFlag);
81     }
82   }
83 }
84 #endif
85
86 void AddDOSNewLine(FILE* ipOutF, CFlag *ipFlag, int CurChar, int PrevChar)
87 {
88   if (ipFlag->NewLine) {  /* add additional CR-LF? */
89     /* Don't add line ending if it is a DOS line ending. Only in case of Unix line ending. */
90     if ((CurChar == '\x0a') && (PrevChar != '\x0d')) {
91       fputc('\x0d', ipOutF);
92       fputc('\x0a', ipOutF);
93     }
94   }
95 }
96
97 /* converts stream ipInF to DOS format text and write to stream ipOutF
98  * RetVal: 0  if success
99  *         -1  otherwise
100  */
101 #ifdef D2U_UNICODE
102 int ConvertUnixToDosW(FILE* ipInF, FILE* ipOutF, CFlag *ipFlag, char *progname)
103 {
104     int RetVal = 0;
105     wint_t TempChar;
106     wint_t PreviousChar = 0;
107
108     ipFlag->status = 0;
109
110     /* LF    -> CR-LF */
111     /* CR-LF -> CR-LF, in case the input file is a DOS text file */
112     /* \x0a = Newline/Line Feed (LF) */
113     /* \x0d = Carriage Return (CR) */
114
115     switch (ipFlag->FromToMode)
116     {
117       case FROMTO_UNIX2DOS: /* unix2dos */
118         while ((TempChar = d2u_getwc(ipInF, ipFlag->bomtype)) != WEOF) {  /* get character */
119           if ((ipFlag->Force == 0) &&
120               (TempChar < 32) &&
121               (TempChar != 0x0a) &&  /* Not an LF */
122               (TempChar != 0x0d) &&  /* Not a CR */
123               (TempChar != 0x09) &&  /* Not a TAB */
124               (TempChar != 0x0c)) {  /* Not a form feed */
125             RetVal = -1;
126             ipFlag->status |= BINARY_FILE ;
127             break;
128           }
129           if (TempChar == 0x0a)
130           {
131             d2u_putwc(0x0d, ipOutF, ipFlag); /* got LF, put CR */
132           } else {
133              if (TempChar == 0x0d) /* got CR */
134              {
135                if ((TempChar = d2u_getwc(ipInF, ipFlag->bomtype)) == WEOF) /* get next char */
136                  TempChar = 0x0d;  /* Read error, or end of file. */
137                else
138                {
139                  d2u_putwc(0x0d, ipOutF, ipFlag); /* put CR */
140                  PreviousChar = 0x0d;
141                }
142              }
143           }
144           if (d2u_putwc(TempChar, ipOutF, ipFlag) == WEOF)
145           {
146               RetVal = -1;
147               if (!ipFlag->Quiet)
148               {
149                 if (!(ipFlag->status & UNICODE_CONVERSION_ERROR))
150                 {
151                   fprintf(stderr, "%s: ", progname);
152                   fprintf(stderr, "%s", _("can not write to output file\n"));
153                 }
154               }
155               break;
156           } else {
157             AddDOSNewLineW( ipOutF, ipFlag, TempChar, PreviousChar);
158           }
159           PreviousChar = TempChar;
160         }
161         break;
162       case FROMTO_UNIX2MAC: /* unix2mac */
163         while ((TempChar = d2u_getwc(ipInF, ipFlag->bomtype)) != WEOF) {
164           if ((ipFlag->Force == 0) &&
165               (TempChar < 32) &&
166               (TempChar != 0x0a) &&  /* Not an LF */
167               (TempChar != 0x0d) &&  /* Not a CR */
168               (TempChar != 0x09) &&  /* Not a TAB */
169               (TempChar != 0x0c)) {  /* Not a form feed */
170             RetVal = -1;
171             ipFlag->status |= BINARY_FILE ;
172             break;
173           }
174           if ((TempChar != 0x0a)) /* Not an LF */
175             {
176               if(d2u_putwc(TempChar, ipOutF, ipFlag) == WEOF){
177                 RetVal = -1;
178                 if (!ipFlag->Quiet)
179                 {
180                   if (!(ipFlag->status & UNICODE_CONVERSION_ERROR))
181                   {
182                     fprintf(stderr, "%s: ", progname);
183                     fprintf(stderr, "%s", _("can not write to output file\n"));
184                   }
185                 }
186                 break;
187               }
188               PreviousChar = TempChar;
189             }
190           else{
191             /* TempChar is an LF */
192             /* Don't touch this delimiter if it's a CR,LF pair. */
193             if ( PreviousChar == 0x0d ) {
194               if (d2u_putwc(0x0a, ipOutF, ipFlag) == WEOF)  /* CR,LF pair. Put LF */
195                 {
196                   RetVal = -1;
197                   if (!ipFlag->Quiet)
198                   {
199                     if (!(ipFlag->status & UNICODE_CONVERSION_ERROR))
200                     {
201                       fprintf(stderr, "%s: ", progname);
202                       fprintf(stderr, "%s", _("can not write to output file\n"));
203                     }
204                   }
205                   break;
206                 }
207               PreviousChar = TempChar;
208               continue;
209             }
210             PreviousChar = TempChar;
211             if (d2u_putwc(0x0d, ipOutF, ipFlag) == WEOF) /* Unix line end (LF). Put CR */
212               {
213                 RetVal = -1;
214                 if (!ipFlag->Quiet)
215                 {
216                   if (!(ipFlag->status & UNICODE_CONVERSION_ERROR))
217                   {
218                     fprintf(stderr, "%s: ", progname);
219                     fprintf(stderr, "%s", _("can not write to output file\n"));
220                   }
221                 }
222                 break;
223               }
224             if (ipFlag->NewLine) {  /* add additional CR? */
225               d2u_putwc(0x0d, ipOutF, ipFlag);
226             }
227           }
228         }
229         break;
230       default: /* unknown FromToMode */
231       ;
232 #if DEBUG
233       fprintf(stderr, "%s: ", progname);
234       fprintf(stderr, _("program error, invalid conversion mode %d\n"),ipFlag->FromToMode);
235       exit(1);
236 #endif
237     }
238     return RetVal;
239 }
240 #endif
241
242 /* converts stream ipInF to DOS format text and write to stream ipOutF
243  * RetVal: 0  if success
244  *         -1  otherwise
245  */
246 int ConvertUnixToDos(FILE* ipInF, FILE* ipOutF, CFlag *ipFlag, char *progname)
247 {
248     int RetVal = 0;
249     int TempChar;
250     int PreviousChar = 0;
251     int *ConvTable;
252
253     ipFlag->status = 0;
254
255     switch (ipFlag->ConvMode)
256     {
257       case CONVMODE_ASCII: /* ascii */
258         ConvTable = U2DAsciiTable;
259         break;
260       case CONVMODE_7BIT: /* 7bit */
261         ConvTable = U2D7BitTable;
262         break;
263       case CONVMODE_437: /* iso */
264         ConvTable = U2DIso437Table;
265         break;
266       case CONVMODE_850: /* iso */
267         ConvTable = U2DIso850Table;
268         break;
269       case CONVMODE_860: /* iso */
270         ConvTable = U2DIso860Table;
271         break;
272       case CONVMODE_863: /* iso */
273         ConvTable = U2DIso863Table;
274         break;
275       case CONVMODE_865: /* iso */
276         ConvTable = U2DIso865Table;
277         break;
278       case CONVMODE_1252: /* iso */
279         ConvTable = U2DIso1252Table;
280         break;
281       default: /* unknown convmode */
282         ipFlag->status |= WRONG_CODEPAGE ;
283         return(-1);
284     }
285     if ((ipFlag->ConvMode > 1) && (!ipFlag->Quiet)) /* not ascii or 7bit */
286     {
287        fprintf(stderr, "%s: ", progname);
288        fprintf(stderr, _("using code page %d.\n"), ipFlag->ConvMode);
289     }
290
291     /* LF    -> CR-LF */
292     /* CR-LF -> CR-LF, in case the input file is a DOS text file */
293     /* \x0a = Newline/Line Feed (LF) */
294     /* \x0d = Carriage Return (CR) */
295
296     switch (ipFlag->FromToMode)
297     {
298       case FROMTO_UNIX2DOS: /* unix2dos */
299         while ((TempChar = fgetc(ipInF)) != EOF) {  /* get character */
300           if ((ipFlag->Force == 0) &&
301               (TempChar < 32) &&
302               (TempChar != '\x0a') &&  /* Not an LF */
303               (TempChar != '\x0d') &&  /* Not a CR */
304               (TempChar != '\x09') &&  /* Not a TAB */
305               (TempChar != '\x0c')) {  /* Not a form feed */
306             RetVal = -1;
307             ipFlag->status |= BINARY_FILE ;
308             break;
309           }
310           if (TempChar == '\x0a')
311           {
312             fputc('\x0d', ipOutF); /* got LF, put CR */
313           } else {
314              if (TempChar == '\x0d') /* got CR */
315              {
316                if ((TempChar = fgetc(ipInF)) == EOF) /* get next char */
317                  TempChar = '\x0d';  /* Read error, or end of file. */
318                else
319                {
320                  fputc('\x0d', ipOutF); /* put CR */
321                  PreviousChar = '\x0d';
322                }
323              }
324           }
325           if (fputc(ConvTable[TempChar], ipOutF) == EOF)
326           {
327               RetVal = -1;
328               if (!ipFlag->Quiet)
329               {
330                 fprintf(stderr, "%s: ", progname);
331                 fprintf(stderr, "%s", _("can not write to output file\n"));
332               }
333               break;
334           } else {
335             AddDOSNewLine( ipOutF, ipFlag, TempChar, PreviousChar);
336           }
337           PreviousChar = TempChar;
338         }
339         break;
340       case FROMTO_UNIX2MAC: /* unix2mac */
341         while ((TempChar = fgetc(ipInF)) != EOF) {
342           if ((ipFlag->Force == 0) &&
343               (TempChar < 32) &&
344               (TempChar != '\x0a') &&  /* Not an LF */
345               (TempChar != '\x0d') &&  /* Not a CR */
346               (TempChar != '\x09') &&  /* Not a TAB */
347               (TempChar != '\x0c')) {  /* Not a form feed */
348             RetVal = -1;
349             ipFlag->status |= BINARY_FILE ;
350             break;
351           }
352           if ((TempChar != '\x0a')) /* Not an LF */
353             {
354               if(fputc(ConvTable[TempChar], ipOutF) == EOF){
355                 RetVal = -1;
356                 if (!ipFlag->Quiet)
357                 {
358                   fprintf(stderr, "%s: ", progname);
359                   fprintf(stderr, "%s", _("can not write to output file\n"));
360                 }
361                 break;
362               }
363               PreviousChar = TempChar;
364             }
365           else{
366             /* TempChar is an LF */
367             /* Don't touch this delimiter if it's a CR,LF pair. */
368             if ( PreviousChar == '\x0d' ) {
369               if (fputc('\x0a', ipOutF) == EOF)  /* CR,LF pair. Put LF */
370                 {
371                   RetVal = -1;
372                   if (!ipFlag->Quiet)
373                   {
374                     fprintf(stderr, "%s: ", progname);
375                     fprintf(stderr, "%s", _("can not write to output file\n"));
376                   }
377                   break;
378                 }
379               PreviousChar = TempChar;
380               continue;
381             }
382             PreviousChar = TempChar;
383             if (fputc('\x0d', ipOutF) == EOF) /* Unix line end (LF). Put CR */
384               {
385                 RetVal = -1;
386                 if (!ipFlag->Quiet)
387                 {
388                   fprintf(stderr, "%s: ", progname);
389                   fprintf(stderr, "%s", _("can not write to output file\n"));
390                 }
391                 break;
392               }
393             if (ipFlag->NewLine) {  /* add additional CR? */
394               fputc('\x0d', ipOutF);
395             }
396           }
397         }
398         break;
399       default: /* unknown FromToMode */
400       ;
401 #if DEBUG
402       fprintf(stderr, "%s: ", progname);
403       fprintf(stderr, _("program error, invalid conversion mode %d\n"),ipFlag->FromToMode);
404       exit(1);
405 #endif
406     }
407     return RetVal;
408 }
409
410 /* convert file ipInFN to DOS format text and write to file ipOutFN
411  * RetVal: 0 if success
412  *         -1 otherwise
413  */
414 int ConvertUnixToDosNewFile(char *ipInFN, char *ipOutFN, CFlag *ipFlag, char *progname)
415 {
416   int RetVal = 0;
417   FILE *InF = NULL;
418   FILE *TempF = NULL;
419   char *TempPath;
420   char *errstr;
421   struct stat StatBuf;
422   struct utimbuf UTimeBuf;
423 #ifndef NO_CHMOD
424   mode_t mask;
425 #endif
426 #ifdef NO_MKSTEMP
427   FILE* fd;
428 #else
429   int fd;
430 #endif
431   char *TargetFN = NULL;
432   int ResolveSymlinkResult = 0;
433
434   ipFlag->status = 0 ;
435
436   /* Test if output file is a symbolic link */
437   if (symbolic_link(ipOutFN) && !ipFlag->Follow)
438   {
439     ipFlag->status |= OUTPUTFILE_SYMLINK ;
440     /* Not a failure, skipping input file according spec. (keep symbolic link unchanged) */
441     return -1;
442   }
443
444   /* Test if input file is a regular file or symbolic link */
445   if (regfile(ipInFN, 1, ipFlag, progname))
446   {
447     ipFlag->status |= NO_REGFILE ;
448     /* Not a failure, skipping non-regular input file according spec. */
449     return -1;
450   }
451
452   /* Test if input file target is a regular file */
453   if (symbolic_link(ipInFN) && regfile_target(ipInFN, ipFlag,progname))
454   {
455     ipFlag->status |= INPUT_TARGET_NO_REGFILE ;
456     /* Not a failure, skipping non-regular input file according spec. */
457     return -1;
458   }
459
460   /* Test if output file target is a regular file */
461   if (symbolic_link(ipOutFN) && (ipFlag->Follow == SYMLINK_FOLLOW) && regfile_target(ipOutFN, ipFlag,progname))
462   {
463     ipFlag->status |= OUTPUT_TARGET_NO_REGFILE ;
464     /* Failure, input is regular, cannot produce output. */
465     if (!ipFlag->error) ipFlag->error = 1;
466     return -1;
467   }
468
469   /* retrieve ipInFN file date stamp */
470   if (stat(ipInFN, &StatBuf))
471   {
472     if (!ipFlag->Quiet)
473     {
474       ipFlag->error = errno;
475       errstr = strerror(errno);
476       fprintf(stderr, "%s: %s: %s\n", progname, ipInFN, errstr);
477     }
478     RetVal = -1;
479   }
480   
481 #ifdef NO_MKSTEMP
482   if((fd = MakeTempFileFrom(ipOutFN, &TempPath))==NULL) {
483 #else
484   if((fd = MakeTempFileFrom (ipOutFN, &TempPath)) < 0) {
485 #endif
486     if (!ipFlag->Quiet)
487     {
488       ipFlag->error = errno;
489       errstr = strerror(errno);
490       fprintf(stderr, "%s: ", progname);
491       fprintf(stderr, _("Failed to open temporary output file: %s\n"), errstr);
492     }
493     RetVal = -1;
494   }
495
496 #if DEBUG
497   fprintf(stderr, "%s: ", progname);
498   fprintf(stderr, _("using %s as temporary file\n"), TempPath);
499 #endif
500
501   /* can open in file? */
502   if (!RetVal)
503   {
504     InF=OpenInFile(ipInFN);
505     if (InF == NULL)
506     {
507       ipFlag->error = errno;
508       errstr = strerror(errno);
509       fprintf(stderr, "%s: %s: %s\n", progname, ipInFN, errstr);
510       RetVal = -1;
511     }
512   }
513
514   /* can open output file? */
515   if ((!RetVal) && (InF))
516   {
517 #ifdef NO_MKSTEMP
518     if ((TempF=fd) == NULL)
519     {
520 #else
521     if ((TempF=OpenOutFile(fd)) == NULL)
522     {
523       ipFlag->error = errno;
524       errstr = strerror(errno);
525       fprintf(stderr, "%s: %s\n", progname, errstr);
526 #endif
527       fclose (InF);
528       InF = NULL;
529       RetVal = -1;
530     }
531   }
532
533   InF = read_bom(InF, &ipFlag->bomtype);
534
535 #ifdef D2U_UNICODE
536 #ifndef MSDOS  /* Unix, Cygwin */
537   if ((ipFlag->bomtype == FILE_UTF16LE) || (ipFlag->bomtype == FILE_UTF16BE))
538   {
539     if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0)
540     {
541       /* Don't convert UTF-16 files when the locale encoding is not UTF-8
542        * to prevent loss of characters. */
543       ipFlag->status |= LOCALE_NOT_UTF8 ;
544       if (!ipFlag->error) ipFlag->error = 1;
545       RetVal = -1;
546     }
547   }
548 #endif
549 #if !defined(WIN32) && !defined(__CYGWIN__) /* Not Windows or Cygwin */
550   if ((ipFlag->bomtype == FILE_UTF16LE) || (ipFlag->bomtype == FILE_UTF16BE))
551   {
552     if (sizeof(wchar_t) < 4)
553     {
554       /* A decoded UTF-16 surrogate pair must fit in a wchar_t */
555       ipFlag->status |= WCHAR_T_TOO_SMALL ;
556       if (!ipFlag->error) ipFlag->error = 1;
557       RetVal = -1;
558     }
559   }
560 #endif
561 #endif
562
563   if ((ipFlag->add_bom) || (ipFlag->bomtype > 0))
564     fprintf(TempF, "%s", "\xEF\xBB\xBF");  /* UTF-8 BOM */
565
566   /* Turn off ISO and 7-bit conversion for Unicode text files */
567   if (ipFlag->bomtype > 0)
568     ipFlag->ConvMode = CONVMODE_ASCII;
569
570   /* conversion sucessful? */
571 #ifdef D2U_UNICODE
572   if ((ipFlag->bomtype == FILE_UTF16LE) || (ipFlag->bomtype == FILE_UTF16BE))
573   {
574     if ((!RetVal) && (ConvertUnixToDosW(InF, TempF, ipFlag, progname)))
575       RetVal = -1;
576     if (ipFlag->status & UNICODE_CONVERSION_ERROR)
577     {
578       if (!ipFlag->error) ipFlag->error = 1;
579       RetVal = -1;
580     }
581   } else {
582     if ((!RetVal) && (ConvertUnixToDos(InF, TempF, ipFlag, progname)))
583       RetVal = -1;
584   }
585 #else
586   if ((!RetVal) && (ConvertUnixToDos(InF, TempF, ipFlag, progname)))
587     RetVal = -1;
588 #endif
589
590    /* can close in file? */
591   if ((InF) && (fclose(InF) == EOF))
592     RetVal = -1;
593
594   /* can close output file? */
595   if ((TempF) && (fclose(TempF) == EOF))
596     RetVal = -1;
597
598 #ifdef NO_MKSTEMP
599   if(fd!=NULL)
600     fclose(fd);
601 #else
602   if(fd>=0)
603     close(fd);
604 #endif
605
606 #ifndef NO_CHMOD
607   if (!RetVal)
608   {
609     if (ipFlag->NewFile == 0) /* old file mode */
610     {
611        RetVal = chmod (TempPath, StatBuf.st_mode); /* set original permissions */
612     } 
613     else
614     {
615        mask = umask(0); /* get process's umask */
616        umask(mask); /* set umask back to original */
617        RetVal = chmod(TempPath, StatBuf.st_mode & ~mask); /* set original permissions, minus umask */
618     }
619     
620     if (RetVal)
621     {
622        if (!ipFlag->Quiet)
623        {
624          ipFlag->error = errno;
625          errstr = strerror(errno);
626          fprintf(stderr, "%s: ", progname);
627          fprintf(stderr, _("Failed to change the permissions of temporary output file %s: %s\n"), TempPath, errstr);
628        }
629     }
630   }
631 #endif
632
633 #ifndef NO_CHOWN
634   if (!RetVal && (ipFlag->NewFile == 0))  /* old file mode */
635   {
636      /* Change owner and group of the the tempory output file to the original file's uid and gid. */
637      /* Required when a different user (e.g. root) has write permission on the original file. */
638      /* Make sure that the original owner can still access the file. */
639      if (chown(TempPath, StatBuf.st_uid, StatBuf.st_gid))
640      {
641         if (!ipFlag->Quiet)
642         {
643           ipFlag->error = errno;
644           errstr = strerror(errno);
645           fprintf(stderr, "%s: ", progname);
646           fprintf(stderr, _("Failed to change the owner and group of temporary output file %s: %s\n"), TempPath, errstr);
647         }
648         RetVal = -1;
649      }
650   }
651 #endif
652
653   if ((!RetVal) && (ipFlag->KeepDate))
654   {
655     UTimeBuf.actime = StatBuf.st_atime;
656     UTimeBuf.modtime = StatBuf.st_mtime;
657     /* can change output file time to in file time? */
658     if (utime(TempPath, &UTimeBuf) == -1)
659     {
660       if (!ipFlag->Quiet)
661       {
662         ipFlag->error = errno;
663         errstr = strerror(errno);
664         fprintf(stderr, "%s: %s: %s\n", progname, TempPath, errstr);
665       }
666       RetVal = -1;
667     }
668   }
669
670   /* any error? cleanup the temp file */
671   if (RetVal && (TempPath != NULL))
672   {
673     if (unlink(TempPath) && (errno != ENOENT))
674     {
675       if (!ipFlag->Quiet)
676       {
677         ipFlag->error = errno;
678         errstr = strerror(errno);
679         fprintf(stderr, "%s: %s: %s\n", progname, TempPath, errstr);
680       }
681       RetVal = -1;
682     }
683   }
684
685   /* If output file is a symbolic link, optional resolve the link and modify  */
686   /* the target, instead of removing the link and creating a new regular file */
687   TargetFN = ipOutFN;
688   if (symbolic_link(ipOutFN) && !RetVal)
689   {
690     ResolveSymlinkResult = 0; /* indicates that TargetFN need not be freed */
691     if (ipFlag->Follow == SYMLINK_FOLLOW)
692     {
693       ResolveSymlinkResult = ResolveSymbolicLink(ipOutFN, &TargetFN, ipFlag, progname);
694       if (ResolveSymlinkResult < 0)
695       {
696         if (!ipFlag->Quiet)
697         {
698           fprintf(stderr, "%s: ", progname);
699           fprintf(stderr, _("problems resolving symbolic link '%s'\n"), ipOutFN);
700           fprintf(stderr, _("          output file remains in '%s'\n"), TempPath);
701         }
702         RetVal = -1;
703       }
704     }
705   }
706
707   /* can rename temporary file to output file? */
708   if (!RetVal)
709   {
710 #ifdef NEED_REMOVE
711     if (unlink(TargetFN) && (errno != ENOENT))
712     {
713       if (!ipFlag->Quiet)
714       {
715         ipFlag->error = errno;
716         errstr = strerror(errno);
717         fprintf(stderr, "%s: %s: %s\n", progname, TargetFN, errstr);
718       }
719       RetVal = -1;
720     }
721 #endif
722     if (rename(TempPath, TargetFN) == -1)
723     {
724       if (!ipFlag->Quiet)
725       {
726         ipFlag->error = errno;
727         errstr = strerror(errno);
728         fprintf(stderr, "%s: ", progname);
729         fprintf(stderr, _("problems renaming '%s' to '%s': %s\n"), TempPath, TargetFN, errstr);
730 #ifdef S_ISLNK
731         if (ResolveSymlinkResult > 0)
732           fprintf(stderr, _("          which is the target of symbolic link '%s'\n"), ipOutFN);
733 #endif
734         fprintf(stderr, _("          output file remains in '%s'\n"), TempPath);
735       }
736       RetVal = -1;
737     }
738
739     if (ResolveSymlinkResult > 0)
740       free(TargetFN);
741   }
742   free(TempPath);
743   return RetVal;
744 }
745
746 /* convert stdin to DOS format text and write to stdout
747  * RetVal: 0 if success
748  *         -1 otherwise
749  */
750 int ConvertUnixToDosStdio(CFlag *ipFlag, char *progname)
751 {
752     ipFlag->NewFile = 1;
753     ipFlag->Quiet = 1;
754     ipFlag->KeepDate = 0;
755     ipFlag->Force = 1;
756
757 #if defined(WIN32) && !defined(__CYGWIN__)
758
759     /* stdin and stdout are by default text streams. We need
760      * to set them to binary mode. Otherwise an LF will
761      * automatically be converted to CR-LF on DOS/Windows.
762      * Erwin */
763
764     /* 'setmode' was deprecated by MicroSoft
765      * since Visual C++ 2005. Use '_setmode' instead. */
766
767     _setmode(fileno(stdout), O_BINARY);
768     _setmode(fileno(stdin), O_BINARY);
769 #elif defined(MSDOS) || defined(__CYGWIN__) || defined(__OS2__)
770     setmode(fileno(stdout), O_BINARY);
771     setmode(fileno(stdin), O_BINARY);
772 #endif
773
774     read_bom(stdin, &ipFlag->bomtype);
775
776     if ((ipFlag->add_bom) || (ipFlag->bomtype > 0))
777        fprintf(stdout, "%s", "\xEF\xBB\xBF");  /* UTF-8 BOM */
778
779 #ifdef D2U_UNICODE
780     if ((ipFlag->bomtype == FILE_UTF16LE) || (ipFlag->bomtype == FILE_UTF16BE))
781     {
782        return (ConvertUnixToDosW(stdin, stdout, ipFlag, progname));
783     } else {
784        return (ConvertUnixToDos(stdin, stdout, ipFlag, progname));
785     }
786 #else
787     return (ConvertUnixToDos(stdin, stdout, ipFlag, progname));
788 #endif
789 }
790
791
792 int main (int argc, char *argv[])
793 {
794   /* variable declarations */
795   char progname[9];
796   int ArgIdx;
797   int CanSwitchFileMode;
798   int ShouldExit;
799   int RetVal = 0;
800   int process_options = 1;
801   CFlag *pFlag;
802   char *ptr;
803 #ifdef ENABLE_NLS
804   char localedir[1024];
805 #endif
806 # ifdef __MINGW64__
807   int _dowildcard = -1; /* enable wildcard expansion for Win64 */
808 # endif
809
810   progname[8] = '\0';
811   strcpy(progname,"unix2dos");
812
813 #ifdef ENABLE_NLS
814    ptr = getenv("DOS2UNIX_LOCALEDIR");
815    if (ptr == NULL)
816       strcpy(localedir,LOCALEDIR);
817    else
818    {
819       if (strlen(ptr) < sizeof(localedir))
820          strcpy(localedir,ptr);
821       else
822       {
823          fprintf(stderr,"%s: ",progname);
824          fprintf(stderr, "%s", _("error: Value of environment variable DOS2UNIX_LOCALEDIR is too long.\n"));
825          strcpy(localedir,LOCALEDIR);
826       }
827    }
828
829    setlocale (LC_ALL, "");
830    bindtextdomain (PACKAGE, localedir);
831    textdomain (PACKAGE);
832 #endif
833
834
835   /* variable initialisations */
836   ArgIdx = 0;
837   CanSwitchFileMode = 1;
838   ShouldExit = 0;
839   pFlag = (CFlag*)malloc(sizeof(CFlag));  
840   pFlag->NewFile = 0;
841   pFlag->Quiet = 0;
842   pFlag->KeepDate = 0;
843   pFlag->ConvMode = CONVMODE_ASCII;  /* default ascii */
844   pFlag->FromToMode = FROMTO_UNIX2DOS;  /* default unix2dos */
845   pFlag->NewLine = 0;
846   pFlag->Force = 0;
847   pFlag->Follow = SYMLINK_SKIP;
848   pFlag->status = 0;
849   pFlag->stdio_mode = 1;
850   pFlag->error = 0;
851 #ifdef D2U_UNICODE
852   pFlag->bomtype = FILE_MBS;
853 #endif
854   pFlag->add_bom = 0;
855
856   if ( ((ptr=strrchr(argv[0],'/')) == NULL) && ((ptr=strrchr(argv[0],'\\')) == NULL) )
857     ptr = argv[0];
858   else
859     ptr++;
860
861   if ((strcmpi("unix2mac", ptr) == 0) || (strcmpi("unix2mac.exe", ptr) == 0))
862   {
863     pFlag->FromToMode = FROMTO_UNIX2MAC;
864     strcpy(progname,"unix2mac");
865   }
866
867   while ((++ArgIdx < argc) && (!ShouldExit))
868   {
869     /* is it an option? */
870     if ((argv[ArgIdx][0] == '-') && process_options)
871     {
872       /* an option */
873       if (strcmp(argv[ArgIdx],"--") == 0)
874         process_options = 0;
875       else if ((strcmp(argv[ArgIdx],"-h") == 0) || (strcmp(argv[ArgIdx],"--help") == 0))
876       {
877         PrintUsage(progname);
878         return(pFlag->error);
879       }
880       else if ((strcmp(argv[ArgIdx],"-k") == 0) || (strcmp(argv[ArgIdx],"--keepdate") == 0))
881         pFlag->KeepDate = 1;
882       else if ((strcmp(argv[ArgIdx],"-f") == 0) || (strcmp(argv[ArgIdx],"--force") == 0))
883         pFlag->Force = 1;
884       else if ((strcmp(argv[ArgIdx],"-s") == 0) || (strcmp(argv[ArgIdx],"--safe") == 0))
885         pFlag->Force = 0;
886       else if ((strcmp(argv[ArgIdx],"-q") == 0) || (strcmp(argv[ArgIdx],"--quiet") == 0))
887         pFlag->Quiet = 1;
888       else if ((strcmp(argv[ArgIdx],"-l") == 0) || (strcmp(argv[ArgIdx],"--newline") == 0))
889         pFlag->NewLine = 1;
890       else if ((strcmp(argv[ArgIdx],"-m") == 0) || (strcmp(argv[ArgIdx],"--add-bom") == 0))
891         pFlag->add_bom = 1;
892       else if ((strcmp(argv[ArgIdx],"-S") == 0) || (strcmp(argv[ArgIdx],"--skip-symlink") == 0))
893         pFlag->Follow = SYMLINK_SKIP;
894       else if ((strcmp(argv[ArgIdx],"-F") == 0) || (strcmp(argv[ArgIdx],"--follow-symlink") == 0))
895         pFlag->Follow = SYMLINK_FOLLOW;
896       else if ((strcmp(argv[ArgIdx],"-R") == 0) || (strcmp(argv[ArgIdx],"--replace-symlink") == 0))
897         pFlag->Follow = SYMLINK_REPLACE;
898       else if ((strcmp(argv[ArgIdx],"-V") == 0) || (strcmp(argv[ArgIdx],"--version") == 0))
899       {
900         PrintVersion(progname);
901 #ifdef ENABLE_NLS
902         PrintLocaledir(localedir);
903 #endif
904         return(pFlag->error);
905       }
906       else if ((strcmp(argv[ArgIdx],"-L") == 0) || (strcmp(argv[ArgIdx],"--license") == 0))
907       {
908         PrintLicense();
909         return(pFlag->error);
910       }
911       else if (strcmp(argv[ArgIdx],"-ascii") == 0)  /* SunOS compatible options */
912         pFlag->ConvMode = CONVMODE_ASCII;
913       else if (strcmp(argv[ArgIdx],"-7") == 0)
914         pFlag->ConvMode = CONVMODE_7BIT;
915       else if (strcmp(argv[ArgIdx],"-iso") == 0)
916       {
917         pFlag->ConvMode = (int)query_con_codepage();
918         if (!pFlag->Quiet)
919         {
920            fprintf(stderr,"%s: ",progname);
921            fprintf(stderr,_("active code page: %d\n"), pFlag->ConvMode);
922         }
923         if (pFlag->ConvMode < 2)
924            pFlag->ConvMode = CONVMODE_437;
925       }
926       else if (strcmp(argv[ArgIdx],"-437") == 0)
927         pFlag->ConvMode = CONVMODE_437;
928       else if (strcmp(argv[ArgIdx],"-850") == 0)
929         pFlag->ConvMode = CONVMODE_850;
930       else if (strcmp(argv[ArgIdx],"-860") == 0)
931         pFlag->ConvMode = CONVMODE_860;
932       else if (strcmp(argv[ArgIdx],"-863") == 0)
933         pFlag->ConvMode = CONVMODE_863;
934       else if (strcmp(argv[ArgIdx],"-865") == 0)
935         pFlag->ConvMode = CONVMODE_865;
936       else if (strcmp(argv[ArgIdx],"-1252") == 0)
937         pFlag->ConvMode = CONVMODE_1252;
938       else if ((strcmp(argv[ArgIdx],"-c") == 0) || (strcmp(argv[ArgIdx],"--convmode") == 0))
939       {
940         if (++ArgIdx < argc)
941         {
942           if (strcmpi(argv[ArgIdx],"ascii") == 0)  /* Benjamin Lin's legacy options */
943             pFlag->ConvMode = CONVMODE_ASCII;
944           else if (strcmpi(argv[ArgIdx], "7bit") == 0)
945             pFlag->ConvMode = CONVMODE_7BIT;
946           else if (strcmpi(argv[ArgIdx], "iso") == 0)
947           {
948             pFlag->ConvMode = (int)query_con_codepage();
949             if (!pFlag->Quiet)
950             {
951                fprintf(stderr,"%s: ",progname);
952                fprintf(stderr,_("active code page: %d\n"), pFlag->ConvMode);
953             }
954             if (pFlag->ConvMode < 2)
955                pFlag->ConvMode = CONVMODE_437;
956           }
957           else if (strcmpi(argv[ArgIdx], "mac") == 0)
958             pFlag->FromToMode = FROMTO_UNIX2MAC;
959           else
960           {
961             fprintf(stderr,"%s: ",progname);
962             fprintf(stderr, _("invalid %s conversion mode specified\n"),argv[ArgIdx]);
963             pFlag->error = 1;
964             ShouldExit = 1;
965             pFlag->stdio_mode = 0;
966           }
967         }
968         else
969         {
970           ArgIdx--;
971           fprintf(stderr,"%s: ",progname);
972           fprintf(stderr,_("option '%s' requires an argument\n"),argv[ArgIdx]);
973           pFlag->error = 1;
974           ShouldExit = 1;
975           pFlag->stdio_mode = 0;
976         }
977       }
978
979       else if ((strcmp(argv[ArgIdx],"-o") == 0) || (strcmp(argv[ArgIdx],"--oldfile") == 0))
980       {
981         /* last convert not paired */
982         if (!CanSwitchFileMode)
983         {
984           fprintf(stderr,"%s: ",progname);
985           fprintf(stderr, _("target of file %s not specified in new file mode\n"), argv[ArgIdx-1]);
986           pFlag->error = 1;
987           ShouldExit = 1;
988           pFlag->stdio_mode = 0;
989         }
990         pFlag->NewFile = 0;
991       }
992
993       else if ((strcmp(argv[ArgIdx],"-n") == 0) || (strcmp(argv[ArgIdx],"--newfile") == 0))
994       {
995         /* last convert not paired */
996         if (!CanSwitchFileMode)
997         {
998           fprintf(stderr,"%s: ",progname);
999           fprintf(stderr, _("target of file %s not specified in new file mode\n"), argv[ArgIdx-1]);
1000           pFlag->error = 1;
1001           ShouldExit = 1;
1002           pFlag->stdio_mode = 0;
1003         }
1004         pFlag->NewFile = 1;
1005       }
1006       else { /* wrong option */
1007         PrintUsage(progname);
1008         ShouldExit = 1;
1009         pFlag->error = 1;
1010         pFlag->stdio_mode = 0;
1011       }
1012     }
1013     else
1014     {
1015       pFlag->stdio_mode = 0;
1016       /* not an option */
1017       if (pFlag->NewFile)
1018       {
1019         if (CanSwitchFileMode)
1020           CanSwitchFileMode = 0;
1021         else
1022         {
1023           RetVal = ConvertUnixToDosNewFile(argv[ArgIdx-1], argv[ArgIdx], pFlag, progname);
1024           if (pFlag->status & NO_REGFILE)
1025           {
1026             if (!pFlag->Quiet)
1027             {
1028               fprintf(stderr,"%s: ",progname);
1029               fprintf(stderr, _("Skipping %s, not a regular file.\n"), argv[ArgIdx-1]);
1030             }
1031           } else if (pFlag->status & OUTPUTFILE_SYMLINK)
1032           {
1033             if (!pFlag->Quiet)
1034             {
1035               fprintf(stderr,"%s: ",progname);
1036               fprintf(stderr, _("Skipping %s, output file %s is a symbolic link.\n"), argv[ArgIdx-1], argv[ArgIdx]);
1037             }
1038           } else if (pFlag->status & INPUT_TARGET_NO_REGFILE)
1039           {
1040             if (!pFlag->Quiet)
1041             {
1042               fprintf(stderr,"%s: ",progname);
1043               fprintf(stderr, _("Skipping symbolic link %s, target is not a regular file.\n"), argv[ArgIdx-1]);
1044             }
1045           } else if (pFlag->status & OUTPUT_TARGET_NO_REGFILE)
1046           {
1047             if (!pFlag->Quiet)
1048             {
1049               fprintf(stderr,"%s: ",progname);
1050               fprintf(stderr, _("Skipping %s, target of symbolic link %s is not a regular file.\n"), argv[ArgIdx-1], argv[ArgIdx]);
1051             }
1052           } else if (pFlag->status & BINARY_FILE)
1053           {
1054             if (!pFlag->Quiet)
1055             {
1056               fprintf(stderr,"%s: ",progname);
1057               fprintf(stderr, _("Skipping binary file %s\n"), argv[ArgIdx-1]);
1058             }
1059           } else if (pFlag->status & WRONG_CODEPAGE)
1060           {
1061             if (!pFlag->Quiet)
1062             {
1063               fprintf(stderr,"%s: ",progname);
1064               fprintf(stderr, _("code page %d is not supported.\n"), pFlag->ConvMode);
1065             }
1066           } else if (pFlag->status & LOCALE_NOT_UTF8)
1067           {
1068             if (!pFlag->Quiet)
1069             {
1070               fprintf(stderr,"%s: ",progname);
1071               fprintf(stderr, _("Skipping UTF-16 file %s, the current locale character encoding is not UTF-8.\n"), argv[ArgIdx-1]);
1072             }
1073           } else if (pFlag->status & WCHAR_T_TOO_SMALL)
1074           {
1075             if (!pFlag->Quiet)
1076             {
1077               fprintf(stderr,"%s: ",progname);
1078               fprintf(stderr, _("Skipping UTF-16 file %s, the size of wchar_t is %d bytes.\n"), argv[ArgIdx-1], (int)sizeof(wchar_t));
1079             }
1080           } else if (pFlag->status & UNICODE_CONVERSION_ERROR)
1081           {
1082             if (!pFlag->Quiet)
1083             {
1084               fprintf(stderr,"%s: ",progname);
1085               fprintf(stderr, _("Skipping UTF-16 file %s, an UTF-16 conversion error occurred.\n"), argv[ArgIdx-1]);
1086             }
1087           } else {
1088             if (!pFlag->Quiet)
1089             {
1090               fprintf(stderr,"%s: ",progname);
1091               if (pFlag->FromToMode == FROMTO_UNIX2MAC)
1092                 fprintf(stderr, _("converting file %s to file %s in Mac format ...\n"), argv[ArgIdx-1], argv[ArgIdx]);
1093               else
1094                 fprintf(stderr, _("converting file %s to file %s in DOS format ...\n"), argv[ArgIdx-1], argv[ArgIdx]);
1095             }
1096             if (RetVal)
1097             {
1098               if (!pFlag->Quiet)
1099               {
1100                 fprintf(stderr,"%s: ",progname);
1101                 fprintf(stderr, _("problems converting file %s to file %s\n"), argv[ArgIdx-1], argv[ArgIdx]);
1102               }
1103             }
1104           }
1105           CanSwitchFileMode = 1;
1106         }
1107       }
1108       else
1109       {
1110         RetVal = ConvertUnixToDosNewFile(argv[ArgIdx], argv[ArgIdx], pFlag, progname);
1111         if (pFlag->status & NO_REGFILE)
1112         {
1113           if (!pFlag->Quiet)
1114           {
1115             fprintf(stderr,"%s: ",progname);
1116             fprintf(stderr, _("Skipping %s, not a regular file.\n"), argv[ArgIdx]);
1117           }
1118         } else if (pFlag->status & OUTPUTFILE_SYMLINK)
1119         {
1120           if (!pFlag->Quiet)
1121           {
1122             fprintf(stderr,"%s: ",progname);
1123             fprintf(stderr, _("Skipping symbolic link %s.\n"), argv[ArgIdx]);
1124           }
1125         } else if (pFlag->status & INPUT_TARGET_NO_REGFILE)
1126         {
1127           if (!pFlag->Quiet)
1128           {
1129             fprintf(stderr,"%s: ",progname);
1130             fprintf(stderr, _("Skipping symbolic link %s, target is not a regular file.\n"), argv[ArgIdx]);
1131           }
1132         } else if (pFlag->status & BINARY_FILE)
1133         {
1134           if (!pFlag->Quiet)
1135           {
1136             fprintf(stderr,"%s: ",progname);
1137             fprintf(stderr, _("Skipping binary file %s\n"), argv[ArgIdx]);
1138           }
1139         } else if (pFlag->status & WRONG_CODEPAGE)
1140         {
1141           if (!pFlag->Quiet)
1142           {
1143             fprintf(stderr,"%s: ",progname);
1144             fprintf(stderr, _("code page %d is not supported.\n"), pFlag->ConvMode);
1145           }
1146         } else if (pFlag->status & LOCALE_NOT_UTF8)
1147         {
1148           if (!pFlag->Quiet)
1149           {
1150             fprintf(stderr,"%s: ",progname);
1151             fprintf(stderr, _("Skipping UTF-16 file %s, the current locale character encoding is not UTF-8.\n"), argv[ArgIdx]);
1152           }
1153         } else if (pFlag->status & WCHAR_T_TOO_SMALL)
1154         {
1155           if (!pFlag->Quiet)
1156           {
1157             fprintf(stderr,"%s: ",progname);
1158             fprintf(stderr, _("Skipping UTF-16 file %s, the size of wchar_t is %d bytes.\n"), argv[ArgIdx], (int)sizeof(wchar_t));
1159           }
1160         } else if (pFlag->status & UNICODE_CONVERSION_ERROR)
1161         {
1162           if (!pFlag->Quiet)
1163           {
1164             fprintf(stderr,"%s: ",progname);
1165             fprintf(stderr, _("Skipping UTF-16 file %s, an UTF-16 conversion error occurred.\n"), argv[ArgIdx]);
1166           }
1167         } else {
1168           if (!pFlag->Quiet)
1169           {
1170             fprintf(stderr,"%s: ",progname);
1171             if (pFlag->FromToMode == FROMTO_UNIX2MAC)
1172               fprintf(stderr, _("converting file %s to Mac format ...\n"), argv[ArgIdx]);
1173             else
1174               fprintf(stderr, _("converting file %s to DOS format ...\n"), argv[ArgIdx]);
1175           }
1176           if (RetVal)
1177           {
1178             if (!pFlag->Quiet)
1179             {
1180               fprintf(stderr,"%s: ",progname);
1181               fprintf(stderr, _("problems converting file %s\n"), argv[ArgIdx]);
1182             }
1183           }
1184         }
1185       }
1186     }
1187   }
1188
1189   /* no file argument, use stdin and stdout */
1190   if (pFlag->stdio_mode)
1191   {
1192     exit(ConvertUnixToDosStdio(pFlag, progname));
1193   }
1194
1195
1196   if (!CanSwitchFileMode)
1197   {
1198     fprintf(stderr,"%s: ",progname);
1199     fprintf(stderr, _("target of file %s not specified in new file mode\n"), argv[ArgIdx-1]);
1200     pFlag->error = 1;
1201   }
1202   return (pFlag->error);
1203 }
1204