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