1/*****************************************************************************
2*
3*   Copyright (C) 1999-2014, International Business Machines
4*   Corporation and others.  All Rights Reserved.
5*
6******************************************************************************/
7
8/*
9 * uconv(1): an iconv(1)-like converter using ICU.
10 *
11 * Original code by Jonas Utterstr&#x00F6;m <jonas.utterstrom@vittran.norrnod.se>
12 * contributed in 1999.
13 *
14 * Conversion to the C conversion API and many improvements by
15 * Yves Arrouye <yves@realnames.com>, current maintainer.
16 *
17 * Markus Scherer maintainer from 2003.
18 * See source code repository history for changes.
19 */
20
21#include <unicode/utypes.h>
22#include <unicode/putil.h>
23#include <unicode/ucnv.h>
24#include <unicode/uenum.h>
25#include <unicode/unistr.h>
26#include <unicode/translit.h>
27#include <unicode/uset.h>
28#include <unicode/uclean.h>
29#include <unicode/utf16.h>
30
31#include <stdio.h>
32#include <errno.h>
33#include <string.h>
34#include <stdlib.h>
35
36#include "cmemory.h"
37#include "cstring.h"
38#include "ustrfmt.h"
39
40#include "unicode/uwmsg.h"
41
42U_NAMESPACE_USE
43
44#if U_PLATFORM_USES_ONLY_WIN32_API && !defined(__STRICT_ANSI__)
45#include <io.h>
46#include <fcntl.h>
47#if U_PLATFORM_USES_ONLY_WIN32_API
48#define USE_FILENO_BINARY_MODE 1
49/* Windows likes to rename Unix-like functions */
50#ifndef fileno
51#define fileno _fileno
52#endif
53#ifndef setmode
54#define setmode _setmode
55#endif
56#ifndef O_BINARY
57#define O_BINARY _O_BINARY
58#endif
59#endif
60#endif
61
62#ifdef UCONVMSG_LINK
63/* below from the README */
64#include "unicode/utypes.h"
65#include "unicode/udata.h"
66U_CFUNC char uconvmsg_dat[];
67#endif
68
69#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
70
71#define DEFAULT_BUFSZ   4096
72#define UCONVMSG "uconvmsg"
73
74static UResourceBundle *gBundle = 0;    /* Bundle containing messages. */
75
76/*
77 * Initialize the message bundle so that message strings can be fetched
78 * by u_wmsg().
79 *
80 */
81
82static void initMsg(const char *pname) {
83    static int ps = 0;
84
85    if (!ps) {
86        char dataPath[2048];        /* XXX Sloppy: should be PATH_MAX. */
87        UErrorCode err = U_ZERO_ERROR;
88
89        ps = 1;
90
91        /* Set up our static data - if any */
92#if defined(UCONVMSG_LINK) && U_PLATFORM != U_PF_OS390 /* On z/OS, this is failing. */
93        udata_setAppData(UCONVMSG, (const void*) uconvmsg_dat, &err);
94        if (U_FAILURE(err)) {
95          fprintf(stderr, "%s: warning, problem installing our static resource bundle data uconvmsg: %s - trying anyways.\n",
96                  pname, u_errorName(err));
97          err = U_ZERO_ERROR; /* It may still fail */
98        }
99#endif
100
101        /* Get messages. */
102        gBundle = u_wmsg_setPath(UCONVMSG, &err);
103        if (U_FAILURE(err)) {
104            fprintf(stderr,
105                    "%s: warning: couldn't open bundle %s: %s\n",
106                    pname, UCONVMSG, u_errorName(err));
107#ifdef UCONVMSG_LINK
108            fprintf(stderr,
109                    "%s: setAppData was called, internal data %s failed to load\n",
110                        pname, UCONVMSG);
111#endif
112
113            err = U_ZERO_ERROR;
114            /* that was try #1, try again with a path */
115            uprv_strcpy(dataPath, u_getDataDirectory());
116            uprv_strcat(dataPath, U_FILE_SEP_STRING);
117            uprv_strcat(dataPath, UCONVMSG);
118
119            gBundle = u_wmsg_setPath(dataPath, &err);
120            if (U_FAILURE(err)) {
121                fprintf(stderr,
122                    "%s: warning: still couldn't open bundle %s: %s\n",
123                    pname, dataPath, u_errorName(err));
124                fprintf(stderr, "%s: warning: messages will not be displayed\n", pname);
125            }
126        }
127    }
128}
129
130/* Mapping of callback names to the callbacks passed to the converter
131   API. */
132
133static struct callback_ent {
134    const char *name;
135    UConverterFromUCallback fromu;
136    const void *fromuctxt;
137    UConverterToUCallback tou;
138    const void *touctxt;
139} transcode_callbacks[] = {
140    { "substitute",
141      UCNV_FROM_U_CALLBACK_SUBSTITUTE, 0,
142      UCNV_TO_U_CALLBACK_SUBSTITUTE, 0 },
143    { "skip",
144      UCNV_FROM_U_CALLBACK_SKIP, 0,
145      UCNV_TO_U_CALLBACK_SKIP, 0 },
146    { "stop",
147      UCNV_FROM_U_CALLBACK_STOP, 0,
148      UCNV_TO_U_CALLBACK_STOP, 0 },
149    { "escape",
150      UCNV_FROM_U_CALLBACK_ESCAPE, 0,
151      UCNV_TO_U_CALLBACK_ESCAPE, 0},
152    { "escape-icu",
153      UCNV_FROM_U_CALLBACK_ESCAPE, UCNV_ESCAPE_ICU,
154      UCNV_TO_U_CALLBACK_ESCAPE, UCNV_ESCAPE_ICU },
155    { "escape-java",
156      UCNV_FROM_U_CALLBACK_ESCAPE, UCNV_ESCAPE_JAVA,
157      UCNV_TO_U_CALLBACK_ESCAPE, UCNV_ESCAPE_JAVA },
158    { "escape-c",
159      UCNV_FROM_U_CALLBACK_ESCAPE, UCNV_ESCAPE_C,
160      UCNV_TO_U_CALLBACK_ESCAPE, UCNV_ESCAPE_C },
161    { "escape-xml",
162      UCNV_FROM_U_CALLBACK_ESCAPE, UCNV_ESCAPE_XML_HEX,
163      UCNV_TO_U_CALLBACK_ESCAPE, UCNV_ESCAPE_XML_HEX },
164    { "escape-xml-hex",
165      UCNV_FROM_U_CALLBACK_ESCAPE, UCNV_ESCAPE_XML_HEX,
166      UCNV_TO_U_CALLBACK_ESCAPE, UCNV_ESCAPE_XML_HEX },
167    { "escape-xml-dec",
168      UCNV_FROM_U_CALLBACK_ESCAPE, UCNV_ESCAPE_XML_DEC,
169      UCNV_TO_U_CALLBACK_ESCAPE, UCNV_ESCAPE_XML_DEC },
170    { "escape-unicode", UCNV_FROM_U_CALLBACK_ESCAPE, UCNV_ESCAPE_UNICODE,
171      UCNV_TO_U_CALLBACK_ESCAPE, UCNV_ESCAPE_UNICODE }
172};
173
174/* Return a pointer to a callback record given its name. */
175
176static const struct callback_ent *findCallback(const char *name) {
177    int i, count =
178        sizeof(transcode_callbacks) / sizeof(*transcode_callbacks);
179
180    /* We'll do a linear search, there aren't many of them and bsearch()
181       may not be that portable. */
182
183    for (i = 0; i < count; ++i) {
184        if (!uprv_stricmp(name, transcode_callbacks[i].name)) {
185            return &transcode_callbacks[i];
186        }
187    }
188
189    return 0;
190}
191
192/* Print converter information. If lookfor is set, only that converter will
193   be printed, otherwise all converters will be printed. If canon is non
194   zero, tags and aliases for each converter are printed too, in the format
195   expected for convrters.txt(5). */
196
197static int printConverters(const char *pname, const char *lookfor,
198    UBool canon)
199{
200    UErrorCode err = U_ZERO_ERROR;
201    int32_t num;
202    uint16_t num_stds;
203    const char **stds;
204
205    /* If there is a specified name, just handle that now. */
206
207    if (lookfor) {
208        if (!canon) {
209            printf("%s\n", lookfor);
210            return 0;
211        } else {
212        /*  Because we are printing a canonical name, we need the
213            true converter name. We've done that already except for
214            the default name (because we want to print the exact
215            name one would get when calling ucnv_getDefaultName()
216            in non-canon mode). But since we do not know at this
217            point if we have the default name or something else, we
218            need to normalize again to the canonical converter
219            name. */
220
221            const char *truename = ucnv_getAlias(lookfor, 0, &err);
222            if (U_SUCCESS(err)) {
223                lookfor = truename;
224            } else {
225                err = U_ZERO_ERROR;
226            }
227        }
228    }
229
230    /* Print converter names. We come here for one of two reasons: we
231       are printing all the names (lookfor was null), or we have a
232       single converter to print but in canon mode, hence we need to
233       get to it in order to print everything. */
234
235    num = ucnv_countAvailable();
236    if (num <= 0) {
237        initMsg(pname);
238        u_wmsg(stderr, "cantGetNames");
239        return -1;
240    }
241    if (lookfor) {
242        num = 1;                /* We know where we want to be. */
243    }
244
245    num_stds = ucnv_countStandards();
246    stds = (const char **) uprv_malloc(num_stds * sizeof(*stds));
247    if (!stds) {
248        u_wmsg(stderr, "cantGetTag", u_wmsg_errorName(U_MEMORY_ALLOCATION_ERROR));
249        return -1;
250    } else {
251        uint16_t s;
252
253        if (canon) {
254            printf("{ ");
255        }
256        for (s = 0; s < num_stds; ++s) {
257            stds[s] = ucnv_getStandard(s, &err);
258            if (canon) {
259                printf("%s ", stds[s]);
260            }
261            if (U_FAILURE(err)) {
262                u_wmsg(stderr, "cantGetTag", u_wmsg_errorName(err));
263                goto error_cleanup;
264            }
265        }
266        if (canon) {
267            puts("}");
268        }
269    }
270
271    for (int32_t i = 0; i < num; i++) {
272        const char *name;
273        uint16_t num_aliases;
274
275        /* Set the name either to what we are looking for, or
276        to the current converter name. */
277
278        if (lookfor) {
279            name = lookfor;
280        } else {
281            name = ucnv_getAvailableName(i);
282        }
283
284        /* Get all the aliases associated to the name. */
285
286        err = U_ZERO_ERROR;
287        num_aliases = ucnv_countAliases(name, &err);
288        if (U_FAILURE(err)) {
289            printf("%s", name);
290
291            UnicodeString str(name, "");
292            putchar('\t');
293            u_wmsg(stderr, "cantGetAliases", str.getTerminatedBuffer(),
294                u_wmsg_errorName(err));
295            goto error_cleanup;
296        } else {
297            uint16_t a, s, t;
298
299            /* Write all the aliases and their tags. */
300
301            for (a = 0; a < num_aliases; ++a) {
302                const char *alias = ucnv_getAlias(name, a, &err);
303
304                if (U_FAILURE(err)) {
305                    UnicodeString str(name, "");
306                    putchar('\t');
307                    u_wmsg(stderr, "cantGetAliases", str.getTerminatedBuffer(),
308                        u_wmsg_errorName(err));
309                    goto error_cleanup;
310                }
311
312                /* Print the current alias so that it looks right. */
313                printf("%s%s%s", (canon ? (a == 0? "" : "\t" ) : "") ,
314                                 alias,
315                                 (canon ? "" : " "));
316
317                /* Look (slowly, linear searching) for a tag. */
318
319                if (canon) {
320                    /* -1 to skip the last standard */
321                    for (s = t = 0; s < num_stds-1; ++s) {
322                        UEnumeration *nameEnum = ucnv_openStandardNames(name, stds[s], &err);
323                        if (U_SUCCESS(err)) {
324                            /* List the standard tags */
325                            const char *standardName;
326                            UBool isFirst = TRUE;
327                            UErrorCode enumError = U_ZERO_ERROR;
328                            while ((standardName = uenum_next(nameEnum, NULL, &enumError))) {
329                                /* See if this alias is supported by this standard. */
330                                if (!strcmp(standardName, alias)) {
331                                    if (!t) {
332                                        printf(" {");
333                                        t = 1;
334                                    }
335                                    /* Print a * after the default standard name */
336                                    printf(" %s%s", stds[s], (isFirst ? "*" : ""));
337                                }
338                                isFirst = FALSE;
339                            }
340                        }
341                    }
342                    if (t) {
343                        printf(" }");
344                    }
345                }
346                /* Terminate this entry. */
347                if (canon) {
348                    puts("");
349                }
350
351                /* Move on. */
352            }
353            /* Terminate this entry. */
354            if (!canon) {
355                puts("");
356            }
357        }
358    }
359
360    /* Free temporary data. */
361
362    uprv_free(stds);
363
364    /* Success. */
365
366    return 0;
367error_cleanup:
368    uprv_free(stds);
369    return -1;
370}
371
372/* Print all available transliterators. If canon is non zero, print
373   one transliterator per line. */
374
375static int printTransliterators(UBool canon)
376{
377#if UCONFIG_NO_TRANSLITERATION
378    printf("no transliterators available because of UCONFIG_NO_TRANSLITERATION, see uconfig.h\n");
379    return 1;
380#else
381    UErrorCode status = U_ZERO_ERROR;
382    UEnumeration *ids = utrans_openIDs(&status);
383    int32_t i, numtrans = uenum_count(ids, &status);
384
385    char sepchar = canon ? '\n' : ' ';
386
387    for (i = 0; U_SUCCESS(status)&& (i < numtrans); ++i) {
388    	int32_t len;
389    	const char *nextTrans = uenum_next(ids, &len, &status);
390
391        printf("%s", nextTrans);
392        if (i < numtrans - 1) {
393            putchar(sepchar);
394        }
395    }
396
397    uenum_close(ids);
398
399    /* Add a terminating newline if needed. */
400
401    if (sepchar != '\n') {
402        putchar('\n');
403    }
404
405    /* Success. */
406
407    return 0;
408#endif
409}
410
411enum {
412    uSP = 0x20,         // space
413    uCR = 0xd,          // carriage return
414    uLF = 0xa,          // line feed
415    uNL = 0x85,         // newline
416    uLS = 0x2028,       // line separator
417    uPS = 0x2029,       // paragraph separator
418    uSig = 0xfeff       // signature/BOM character
419};
420
421static inline int32_t
422getChunkLimit(const UnicodeString &prev, const UnicodeString &s) {
423    // find one of
424    // CR, LF, CRLF, NL, LS, PS
425    // for paragraph ends (see UAX #13/Unicode 4)
426    // and include it in the chunk
427    // all of these characters are on the BMP
428    // do not include FF or VT in case they are part of a paragraph
429    // (important for bidi contexts)
430    static const UChar paraEnds[] = {
431        0xd, 0xa, 0x85, 0x2028, 0x2029
432    };
433    enum {
434        iCR, iLF, iNL, iLS, iPS, iCount
435    };
436
437    // first, see if there is a CRLF split between prev and s
438    if (prev.endsWith(paraEnds + iCR, 1)) {
439        if (s.startsWith(paraEnds + iLF, 1)) {
440            return 1; // split CRLF, include the LF
441        } else if (!s.isEmpty()) {
442            return 0; // complete the last chunk
443        } else {
444            return -1; // wait for actual further contents to arrive
445        }
446    }
447
448    const UChar *u = s.getBuffer(), *limit = u + s.length();
449    UChar c;
450
451    while (u < limit) {
452        c = *u++;
453        if (
454            ((c < uSP) && (c == uCR || c == uLF)) ||
455            (c == uNL) ||
456            ((c & uLS) == uLS)
457        ) {
458            if (c == uCR) {
459                // check for CRLF
460                if (u == limit) {
461                    return -1; // LF may be in the next chunk
462                } else if (*u == uLF) {
463                    ++u; // include the LF in this chunk
464                }
465            }
466            return (int32_t)(u - s.getBuffer());
467        }
468    }
469
470    return -1; // continue collecting the chunk
471}
472
473enum {
474    CNV_NO_FEFF,    // cannot convert the U+FEFF Unicode signature character (BOM)
475    CNV_WITH_FEFF,  // can convert the U+FEFF signature character
476    CNV_ADDS_FEFF   // automatically adds/detects the U+FEFF signature character
477};
478
479static inline UChar
480nibbleToHex(uint8_t n) {
481    n &= 0xf;
482    return
483        n <= 9 ?
484            (UChar)(0x30 + n) :
485            (UChar)((0x61 - 10) + n);
486}
487
488// check the converter's Unicode signature properties;
489// the fromUnicode side of the converter must be in its initial state
490// and will be reset again if it was used
491static int32_t
492cnvSigType(UConverter *cnv) {
493    UErrorCode err;
494    int32_t result;
495
496    // test if the output charset can convert U+FEFF
497    USet *set = uset_open(1, 0);
498    err = U_ZERO_ERROR;
499    ucnv_getUnicodeSet(cnv, set, UCNV_ROUNDTRIP_SET, &err);
500    if (U_SUCCESS(err) && uset_contains(set, uSig)) {
501        result = CNV_WITH_FEFF;
502    } else {
503        result = CNV_NO_FEFF; // an error occurred or U+FEFF cannot be converted
504    }
505    uset_close(set);
506
507    if (result == CNV_WITH_FEFF) {
508        // test if the output charset emits a signature anyway
509        const UChar a[1] = { 0x61 }; // "a"
510        const UChar *in;
511
512        char buffer[20];
513        char *out;
514
515        in = a;
516        out = buffer;
517        err = U_ZERO_ERROR;
518        ucnv_fromUnicode(cnv,
519            &out, buffer + sizeof(buffer),
520            &in, a + 1,
521            NULL, TRUE, &err);
522        ucnv_resetFromUnicode(cnv);
523
524        if (NULL != ucnv_detectUnicodeSignature(buffer, (int32_t)(out - buffer), NULL, &err) &&
525            U_SUCCESS(err)
526        ) {
527            result = CNV_ADDS_FEFF;
528        }
529    }
530
531    return result;
532}
533
534class ConvertFile {
535public:
536    ConvertFile() :
537        buf(NULL), outbuf(NULL), fromoffsets(NULL),
538        bufsz(0), signature(0) {}
539
540    void
541    setBufferSize(size_t bufferSize) {
542        bufsz = bufferSize;
543
544        buf = new char[2 * bufsz];
545        outbuf = buf + bufsz;
546
547        // +1 for an added U+FEFF in the intermediate Unicode buffer
548        fromoffsets = new int32_t[bufsz + 1];
549    }
550
551    ~ConvertFile() {
552        delete [] buf;
553        delete [] fromoffsets;
554    }
555
556    UBool convertFile(const char *pname,
557                      const char *fromcpage,
558                      UConverterToUCallback toucallback,
559                      const void *touctxt,
560                      const char *tocpage,
561                      UConverterFromUCallback fromucallback,
562                      const void *fromuctxt,
563                      UBool fallback,
564                      const char *translit,
565                      const char *infilestr,
566                      FILE * outfile, int verbose);
567private:
568    friend int main(int argc, char **argv);
569
570    char *buf, *outbuf;
571    int32_t *fromoffsets;
572
573    size_t bufsz;
574    int8_t signature; // add (1) or remove (-1) a U+FEFF Unicode signature character
575};
576
577// Convert a file from one encoding to another
578UBool
579ConvertFile::convertFile(const char *pname,
580                         const char *fromcpage,
581                         UConverterToUCallback toucallback,
582                         const void *touctxt,
583                         const char *tocpage,
584                         UConverterFromUCallback fromucallback,
585                         const void *fromuctxt,
586                         UBool fallback,
587                         const char *translit,
588                         const char *infilestr,
589                         FILE * outfile, int verbose)
590{
591    FILE *infile;
592    UBool ret = TRUE;
593    UConverter *convfrom = 0;
594    UConverter *convto = 0;
595    UErrorCode err = U_ZERO_ERROR;
596    UBool flush;
597    UBool closeFile = FALSE;
598    const char *cbufp, *prevbufp;
599    char *bufp;
600
601    uint32_t infoffset = 0, outfoffset = 0;   /* Where we are in the file, for error reporting. */
602
603    const UChar *unibuf, *unibufbp;
604    UChar *unibufp;
605
606    size_t rd, wr;
607
608#if !UCONFIG_NO_TRANSLITERATION
609    Transliterator *t = 0;      // Transliterator acting on Unicode data.
610    UnicodeString chunk;        // One chunk of the text being collected for transformation.
611#endif
612    UnicodeString u;            // String to do the transliteration.
613    int32_t ulen;
614
615    // use conversion offsets for error messages
616    // unless a transliterator is used -
617    // a text transformation will reorder characters in unpredictable ways
618    UBool useOffsets = TRUE;
619
620    // Open the correct input file or connect to stdin for reading input
621
622    if (infilestr != 0 && strcmp(infilestr, "-")) {
623        infile = fopen(infilestr, "rb");
624        if (infile == 0) {
625            UnicodeString str1(infilestr, "");
626            str1.append((UChar32) 0);
627            UnicodeString str2(strerror(errno), "");
628            str2.append((UChar32) 0);
629            initMsg(pname);
630            u_wmsg(stderr, "cantOpenInputF", str1.getBuffer(), str2.getBuffer());
631            return FALSE;
632        }
633        closeFile = TRUE;
634    } else {
635        infilestr = "-";
636        infile = stdin;
637#ifdef USE_FILENO_BINARY_MODE
638        if (setmode(fileno(stdin), O_BINARY) == -1) {
639            initMsg(pname);
640            u_wmsg(stderr, "cantSetInBinMode");
641            return FALSE;
642        }
643#endif
644    }
645
646    if (verbose) {
647        fprintf(stderr, "%s:\n", infilestr);
648    }
649
650#if !UCONFIG_NO_TRANSLITERATION
651    // Create transliterator as needed.
652
653    if (translit != NULL && *translit) {
654        UParseError parse;
655        UnicodeString str(translit), pestr;
656
657        /* Create from rules or by ID as needed. */
658
659        parse.line = -1;
660
661        if (uprv_strchr(translit, ':') || uprv_strchr(translit, '>') || uprv_strchr(translit, '<') || uprv_strchr(translit, '>')) {
662            t = Transliterator::createFromRules(UNICODE_STRING_SIMPLE("Uconv"), str, UTRANS_FORWARD, parse, err);
663        } else {
664            t = Transliterator::createInstance(UnicodeString(translit, -1, US_INV), UTRANS_FORWARD, err);
665        }
666
667        if (U_FAILURE(err)) {
668            str.append((UChar32) 0);
669            initMsg(pname);
670
671            if (parse.line >= 0) {
672                UChar linebuf[20], offsetbuf[20];
673                uprv_itou(linebuf, 20, parse.line, 10, 0);
674                uprv_itou(offsetbuf, 20, parse.offset, 10, 0);
675                u_wmsg(stderr, "cantCreateTranslitParseErr", str.getTerminatedBuffer(),
676                    u_wmsg_errorName(err), linebuf, offsetbuf);
677            } else {
678                u_wmsg(stderr, "cantCreateTranslit", str.getTerminatedBuffer(),
679                    u_wmsg_errorName(err));
680            }
681
682            if (t) {
683                delete t;
684                t = 0;
685            }
686            goto error_exit;
687        }
688
689        useOffsets = FALSE;
690    }
691#endif
692
693    // Create codepage converter. If the codepage or its aliases weren't
694    // available, it returns NULL and a failure code. We also set the
695    // callbacks, and return errors in the same way.
696
697    convfrom = ucnv_open(fromcpage, &err);
698    if (U_FAILURE(err)) {
699        UnicodeString str(fromcpage, "");
700        initMsg(pname);
701        u_wmsg(stderr, "cantOpenFromCodeset", str.getTerminatedBuffer(),
702            u_wmsg_errorName(err));
703        goto error_exit;
704    }
705    ucnv_setToUCallBack(convfrom, toucallback, touctxt, 0, 0, &err);
706    if (U_FAILURE(err)) {
707        initMsg(pname);
708        u_wmsg(stderr, "cantSetCallback", u_wmsg_errorName(err));
709        goto error_exit;
710    }
711
712    convto = ucnv_open(tocpage, &err);
713    if (U_FAILURE(err)) {
714        UnicodeString str(tocpage, "");
715        initMsg(pname);
716        u_wmsg(stderr, "cantOpenToCodeset", str.getTerminatedBuffer(),
717            u_wmsg_errorName(err));
718        goto error_exit;
719    }
720    ucnv_setFromUCallBack(convto, fromucallback, fromuctxt, 0, 0, &err);
721    if (U_FAILURE(err)) {
722        initMsg(pname);
723        u_wmsg(stderr, "cantSetCallback", u_wmsg_errorName(err));
724        goto error_exit;
725    }
726    ucnv_setFallback(convto, fallback);
727
728    UBool willexit, fromSawEndOfBytes, toSawEndOfUnicode;
729    int8_t sig;
730
731    // OK, we can convert now.
732    sig = signature;
733    rd = 0;
734
735    do {
736        willexit = FALSE;
737
738        // input file offset at the beginning of the next buffer
739        infoffset += rd;
740
741        rd = fread(buf, 1, bufsz, infile);
742        if (ferror(infile) != 0) {
743            UnicodeString str(strerror(errno));
744            initMsg(pname);
745            u_wmsg(stderr, "cantRead", str.getTerminatedBuffer());
746            goto error_exit;
747        }
748
749        // Convert the read buffer into the new encoding via Unicode.
750        // After the call 'unibufp' will be placed behind the last
751        // character that was converted in the 'unibuf'.
752        // Also the 'cbufp' is positioned behind the last converted
753        // character.
754        // At the last conversion in the file, flush should be set to
755        // true so that we get all characters converted.
756        //
757        // The converter must be flushed at the end of conversion so
758        // that characters on hold also will be written.
759
760        cbufp = buf;
761        flush = (UBool)(rd != bufsz);
762
763        // convert until the input is consumed
764        do {
765            // remember the start of the current byte-to-Unicode conversion
766            prevbufp = cbufp;
767
768            unibuf = unibufp = u.getBuffer((int32_t)bufsz);
769
770            // Use bufsz instead of u.getCapacity() for the targetLimit
771            // so that we don't overflow fromoffsets[].
772            ucnv_toUnicode(convfrom, &unibufp, unibuf + bufsz, &cbufp,
773                buf + rd, useOffsets ? fromoffsets : NULL, flush, &err);
774
775            ulen = (int32_t)(unibufp - unibuf);
776            u.releaseBuffer(U_SUCCESS(err) ? ulen : 0);
777
778            // fromSawEndOfBytes indicates that ucnv_toUnicode() is done
779            // converting all of the input bytes.
780            // It works like this because ucnv_toUnicode() returns only under the
781            // following conditions:
782            // - an error occurred during conversion (an error code is set)
783            // - the target buffer is filled (the error code indicates an overflow)
784            // - the source is consumed
785            // That is, if the error code does not indicate a failure,
786            // not even an overflow, then the source must be consumed entirely.
787            fromSawEndOfBytes = (UBool)U_SUCCESS(err);
788
789            if (err == U_BUFFER_OVERFLOW_ERROR) {
790                err = U_ZERO_ERROR;
791            } else if (U_FAILURE(err)) {
792                char pos[32], errorBytes[32];
793                int8_t i, length, errorLength;
794
795                UErrorCode localError = U_ZERO_ERROR;
796                errorLength = (int8_t)sizeof(errorBytes);
797                ucnv_getInvalidChars(convfrom, errorBytes, &errorLength, &localError);
798                if (U_FAILURE(localError) || errorLength == 0) {
799                    errorLength = 1;
800                }
801
802                // print the input file offset of the start of the error bytes:
803                // input file offset of the current byte buffer +
804                // length of the just consumed bytes -
805                // length of the error bytes
806                length =
807                    (int8_t)sprintf(pos, "%d",
808                        (int)(infoffset + (cbufp - buf) - errorLength));
809
810                // output the bytes that caused the error
811                UnicodeString str;
812                for (i = 0; i < errorLength; ++i) {
813                    if (i > 0) {
814                        str.append((UChar)uSP);
815                    }
816                    str.append(nibbleToHex((uint8_t)errorBytes[i] >> 4));
817                    str.append(nibbleToHex((uint8_t)errorBytes[i]));
818                }
819
820                initMsg(pname);
821                u_wmsg(stderr, "problemCvtToU",
822                        UnicodeString(pos, length, "").getTerminatedBuffer(),
823                        str.getTerminatedBuffer(),
824                        u_wmsg_errorName(err));
825
826                willexit = TRUE;
827                err = U_ZERO_ERROR; /* reset the error for the rest of the conversion. */
828            }
829
830            // Replaced a check for whether the input was consumed by
831            // looping until it is; message key "premEndInput" now obsolete.
832
833            if (ulen == 0) {
834                continue;
835            }
836
837            // remove a U+FEFF Unicode signature character if requested
838            if (sig < 0) {
839                if (u.charAt(0) == uSig) {
840                    u.remove(0, 1);
841
842                    // account for the removed UChar and offset
843                    --ulen;
844
845                    if (useOffsets) {
846                        // remove an offset from fromoffsets[] as well
847                        // to keep the array parallel with the UChars
848                        memmove(fromoffsets, fromoffsets + 1, ulen * 4);
849                    }
850
851                }
852                sig = 0;
853            }
854
855#if !UCONFIG_NO_TRANSLITERATION
856            // Transliterate/transform if needed.
857
858            // For transformation, we use chunking code -
859            // collect Unicode input until, for example, an end-of-line,
860            // then transform and output-convert that and continue collecting.
861            // This makes the transformation result independent of the buffer size
862            // while avoiding the slower keyboard mode.
863            // The end-of-chunk characters are completely included in the
864            // transformed string in case they are to be transformed themselves.
865            if (t != NULL) {
866                UnicodeString out;
867                int32_t chunkLimit;
868
869                do {
870                    chunkLimit = getChunkLimit(chunk, u);
871                    if (chunkLimit < 0 && flush && fromSawEndOfBytes) {
872                        // use all of the rest at the end of the text
873                        chunkLimit = u.length();
874                    }
875                    if (chunkLimit >= 0) {
876                        // complete the chunk and transform it
877                        chunk.append(u, 0, chunkLimit);
878                        u.remove(0, chunkLimit);
879                        t->transliterate(chunk);
880
881                        // append the transformation result to the result and empty the chunk
882                        out.append(chunk);
883                        chunk.remove();
884                    } else {
885                        // continue collecting the chunk
886                        chunk.append(u);
887                        break;
888                    }
889                } while (!u.isEmpty());
890
891                u = out;
892                ulen = u.length();
893            }
894#endif
895
896            // add a U+FEFF Unicode signature character if requested
897            // and possible/necessary
898            if (sig > 0) {
899                if (u.charAt(0) != uSig && cnvSigType(convto) == CNV_WITH_FEFF) {
900                    u.insert(0, (UChar)uSig);
901
902                    if (useOffsets) {
903                        // insert a pseudo-offset into fromoffsets[] as well
904                        // to keep the array parallel with the UChars
905                        memmove(fromoffsets + 1, fromoffsets, ulen * 4);
906                        fromoffsets[0] = -1;
907                    }
908
909                    // account for the additional UChar and offset
910                    ++ulen;
911                }
912                sig = 0;
913            }
914
915            // Convert the Unicode buffer into the destination codepage
916            // Again 'bufp' will be placed behind the last converted character
917            // And 'unibufp' will be placed behind the last converted unicode character
918            // At the last conversion flush should be set to true to ensure that
919            // all characters left get converted
920
921            unibuf = unibufbp = u.getBuffer();
922
923            do {
924                bufp = outbuf;
925
926                // Use fromSawEndOfBytes in addition to the flush flag -
927                // it indicates whether the intermediate Unicode string
928                // contains the very last UChars for the very last input bytes.
929                ucnv_fromUnicode(convto, &bufp, outbuf + bufsz,
930                                 &unibufbp,
931                                 unibuf + ulen,
932                                 NULL, (UBool)(flush && fromSawEndOfBytes), &err);
933
934                // toSawEndOfUnicode indicates that ucnv_fromUnicode() is done
935                // converting all of the intermediate UChars.
936                // See comment for fromSawEndOfBytes.
937                toSawEndOfUnicode = (UBool)U_SUCCESS(err);
938
939                if (err == U_BUFFER_OVERFLOW_ERROR) {
940                    err = U_ZERO_ERROR;
941                } else if (U_FAILURE(err)) {
942                    UChar errorUChars[4];
943                    const char *errtag;
944                    char pos[32];
945                    UChar32 c;
946                    int8_t i, length, errorLength;
947
948                    UErrorCode localError = U_ZERO_ERROR;
949                    errorLength = (int8_t)LENGTHOF(errorUChars);
950                    ucnv_getInvalidUChars(convto, errorUChars, &errorLength, &localError);
951                    if (U_FAILURE(localError) || errorLength == 0) {
952                        // need at least 1 so that we don't access beyond the length of fromoffsets[]
953                        errorLength = 1;
954                    }
955
956                    int32_t ferroffset;
957
958                    if (useOffsets) {
959                        // Unicode buffer offset of the start of the error UChars
960                        ferroffset = (int32_t)((unibufbp - unibuf) - errorLength);
961                        if (ferroffset < 0) {
962                            // approximation - the character started in the previous Unicode buffer
963                            ferroffset = 0;
964                        }
965
966                        // get the corresponding byte offset out of fromoffsets[]
967                        // go back if the offset is not known for some of the UChars
968                        int32_t fromoffset;
969                        do {
970                            fromoffset = fromoffsets[ferroffset];
971                        } while (fromoffset < 0 && --ferroffset >= 0);
972
973                        // total input file offset =
974                        // input file offset of the current byte buffer +
975                        // byte buffer offset of where the current Unicode buffer is converted from +
976                        // fromoffsets[Unicode offset]
977                        ferroffset = infoffset + (prevbufp - buf) + fromoffset;
978                        errtag = "problemCvtFromU";
979                    } else {
980                        // Do not use fromoffsets if (t != NULL) because the Unicode text may
981                        // be different from what the offsets refer to.
982
983                        // output file offset
984                        ferroffset = (int32_t)(outfoffset + (bufp - outbuf));
985                        errtag = "problemCvtFromUOut";
986                    }
987
988                    length = (int8_t)sprintf(pos, "%u", (int)ferroffset);
989
990                    // output the code points that caused the error
991                    UnicodeString str;
992                    for (i = 0; i < errorLength;) {
993                        if (i > 0) {
994                            str.append((UChar)uSP);
995                        }
996                        U16_NEXT(errorUChars, i, errorLength, c);
997                        if (c >= 0x100000) {
998                            str.append(nibbleToHex((uint8_t)(c >> 20)));
999                        }
1000                        if (c >= 0x10000) {
1001                            str.append(nibbleToHex((uint8_t)(c >> 16)));
1002                        }
1003                        str.append(nibbleToHex((uint8_t)(c >> 12)));
1004                        str.append(nibbleToHex((uint8_t)(c >> 8)));
1005                        str.append(nibbleToHex((uint8_t)(c >> 4)));
1006                        str.append(nibbleToHex((uint8_t)c));
1007                    }
1008
1009                    initMsg(pname);
1010                    u_wmsg(stderr, errtag,
1011                            UnicodeString(pos, length, "").getTerminatedBuffer(),
1012                            str.getTerminatedBuffer(),
1013                           u_wmsg_errorName(err));
1014                    u_wmsg(stderr, "errorUnicode", str.getTerminatedBuffer());
1015
1016                    willexit = TRUE;
1017                    err = U_ZERO_ERROR; /* reset the error for the rest of the conversion. */
1018                }
1019
1020                // Replaced a check for whether the intermediate Unicode characters were all consumed by
1021                // looping until they are; message key "premEnd" now obsolete.
1022
1023                // Finally, write the converted buffer to the output file
1024                size_t outlen = (size_t) (bufp - outbuf);
1025                outfoffset += (int32_t)(wr = fwrite(outbuf, 1, outlen, outfile));
1026                if (wr != outlen) {
1027                    UnicodeString str(strerror(errno));
1028                    initMsg(pname);
1029                    u_wmsg(stderr, "cantWrite", str.getTerminatedBuffer());
1030                    willexit = TRUE;
1031                }
1032
1033                if (willexit) {
1034                    goto error_exit;
1035                }
1036            } while (!toSawEndOfUnicode);
1037        } while (!fromSawEndOfBytes);
1038    } while (!flush);           // Stop when we have flushed the
1039                                // converters (this means that it's
1040                                // the end of output)
1041
1042    goto normal_exit;
1043
1044error_exit:
1045    ret = FALSE;
1046
1047normal_exit:
1048    // Cleanup.
1049
1050    ucnv_close(convfrom);
1051    ucnv_close(convto);
1052
1053#if !UCONFIG_NO_TRANSLITERATION
1054    delete t;
1055#endif
1056
1057    if (closeFile) {
1058        fclose(infile);
1059    }
1060
1061    return ret;
1062}
1063
1064static void usage(const char *pname, int ecode) {
1065    const UChar *msg;
1066    int32_t msgLen;
1067    UErrorCode err = U_ZERO_ERROR;
1068    FILE *fp = ecode ? stderr : stdout;
1069    int res;
1070
1071    initMsg(pname);
1072    msg =
1073        ures_getStringByKey(gBundle, ecode ? "lcUsageWord" : "ucUsageWord",
1074                            &msgLen, &err);
1075    UnicodeString upname(pname, (int32_t)(uprv_strlen(pname) + 1));
1076    UnicodeString mname(msg, msgLen + 1);
1077
1078    res = u_wmsg(fp, "usage", mname.getBuffer(), upname.getBuffer());
1079    if (!ecode) {
1080        if (!res) {
1081            fputc('\n', fp);
1082        }
1083        if (!u_wmsg(fp, "help")) {
1084            /* Now dump callbacks and finish. */
1085
1086            int i, count =
1087                sizeof(transcode_callbacks) / sizeof(*transcode_callbacks);
1088            for (i = 0; i < count; ++i) {
1089                fprintf(fp, " %s", transcode_callbacks[i].name);
1090            }
1091            fputc('\n', fp);
1092        }
1093    }
1094
1095    exit(ecode);
1096}
1097
1098extern int
1099main(int argc, char **argv)
1100{
1101    FILE *outfile;
1102    int ret = 0;
1103
1104    size_t bufsz = DEFAULT_BUFSZ;
1105
1106    const char *fromcpage = 0;
1107    const char *tocpage = 0;
1108    const char *translit = 0;
1109    const char *outfilestr = 0;
1110    UBool fallback = FALSE;
1111
1112    UConverterFromUCallback fromucallback = UCNV_FROM_U_CALLBACK_STOP;
1113    const void *fromuctxt = 0;
1114    UConverterToUCallback toucallback = UCNV_TO_U_CALLBACK_STOP;
1115    const void *touctxt = 0;
1116
1117    char **iter, **remainArgv, **remainArgvLimit;
1118    char **end = argv + argc;
1119
1120    const char *pname;
1121
1122    UBool printConvs = FALSE, printCanon = FALSE, printTranslits = FALSE;
1123    const char *printName = 0;
1124
1125    UBool verbose = FALSE;
1126    UErrorCode status = U_ZERO_ERROR;
1127
1128    ConvertFile cf;
1129
1130    /* Initialize ICU */
1131    u_init(&status);
1132    if (U_FAILURE(status)) {
1133        fprintf(stderr, "%s: can not initialize ICU.  status = %s\n",
1134            argv[0], u_errorName(status));
1135        exit(1);
1136    }
1137
1138    // Get and prettify pname.
1139    pname = uprv_strrchr(*argv, U_FILE_SEP_CHAR);
1140#if U_PLATFORM_USES_ONLY_WIN32_API
1141    if (!pname) {
1142        pname = uprv_strrchr(*argv, '/');
1143    }
1144#endif
1145    if (!pname) {
1146        pname = *argv;
1147    } else {
1148        ++pname;
1149    }
1150
1151    // First, get the arguments from command-line
1152    // to know the codepages to convert between
1153
1154    remainArgv = remainArgvLimit = argv + 1;
1155    for (iter = argv + 1; iter != end; iter++) {
1156        // Check for from charset
1157        if (strcmp("-f", *iter) == 0 || !strcmp("--from-code", *iter)) {
1158            iter++;
1159            if (iter != end)
1160                fromcpage = *iter;
1161            else
1162                usage(pname, 1);
1163        } else if (strcmp("-t", *iter) == 0 || !strcmp("--to-code", *iter)) {
1164            iter++;
1165            if (iter != end)
1166                tocpage = *iter;
1167            else
1168                usage(pname, 1);
1169        } else if (strcmp("-x", *iter) == 0) {
1170            iter++;
1171            if (iter != end)
1172                translit = *iter;
1173            else
1174                usage(pname, 1);
1175        } else if (!strcmp("--fallback", *iter)) {
1176            fallback = TRUE;
1177        } else if (!strcmp("--no-fallback", *iter)) {
1178            fallback = FALSE;
1179        } else if (strcmp("-b", *iter) == 0 || !strcmp("--block-size", *iter)) {
1180            iter++;
1181            if (iter != end) {
1182                bufsz = atoi(*iter);
1183                if ((int) bufsz <= 0) {
1184                    initMsg(pname);
1185                    UnicodeString str(*iter);
1186                    initMsg(pname);
1187                    u_wmsg(stderr, "badBlockSize", str.getTerminatedBuffer());
1188                    return 3;
1189                }
1190            } else {
1191                usage(pname, 1);
1192            }
1193        } else if (strcmp("-l", *iter) == 0 || !strcmp("--list", *iter)) {
1194            if (printTranslits) {
1195                usage(pname, 1);
1196            }
1197            printConvs = TRUE;
1198        } else if (strcmp("--default-code", *iter) == 0) {
1199            if (printTranslits) {
1200                usage(pname, 1);
1201            }
1202            printName = ucnv_getDefaultName();
1203        } else if (strcmp("--list-code", *iter) == 0) {
1204            if (printTranslits) {
1205                usage(pname, 1);
1206            }
1207
1208            iter++;
1209            if (iter != end) {
1210                UErrorCode e = U_ZERO_ERROR;
1211                printName = ucnv_getAlias(*iter, 0, &e);
1212                if (U_FAILURE(e) || !printName) {
1213                    UnicodeString str(*iter);
1214                    initMsg(pname);
1215                    u_wmsg(stderr, "noSuchCodeset", str.getTerminatedBuffer());
1216                    return 2;
1217                }
1218            } else
1219                usage(pname, 1);
1220        } else if (strcmp("--canon", *iter) == 0) {
1221            printCanon = TRUE;
1222        } else if (strcmp("-L", *iter) == 0
1223            || !strcmp("--list-transliterators", *iter)) {
1224            if (printConvs) {
1225                usage(pname, 1);
1226            }
1227            printTranslits = TRUE;
1228        } else if (strcmp("-h", *iter) == 0 || !strcmp("-?", *iter)
1229            || !strcmp("--help", *iter)) {
1230            usage(pname, 0);
1231        } else if (!strcmp("-c", *iter)) {
1232            fromucallback = UCNV_FROM_U_CALLBACK_SKIP;
1233        } else if (!strcmp("--to-callback", *iter)) {
1234            iter++;
1235            if (iter != end) {
1236                const struct callback_ent *cbe = findCallback(*iter);
1237                if (cbe) {
1238                    fromucallback = cbe->fromu;
1239                    fromuctxt = cbe->fromuctxt;
1240                } else {
1241                    UnicodeString str(*iter);
1242                    initMsg(pname);
1243                    u_wmsg(stderr, "unknownCallback", str.getTerminatedBuffer());
1244                    return 4;
1245                }
1246            } else {
1247                usage(pname, 1);
1248            }
1249        } else if (!strcmp("--from-callback", *iter)) {
1250            iter++;
1251            if (iter != end) {
1252                const struct callback_ent *cbe = findCallback(*iter);
1253                if (cbe) {
1254                    toucallback = cbe->tou;
1255                    touctxt = cbe->touctxt;
1256                } else {
1257                    UnicodeString str(*iter);
1258                    initMsg(pname);
1259                    u_wmsg(stderr, "unknownCallback", str.getTerminatedBuffer());
1260                    return 4;
1261                }
1262            } else {
1263                usage(pname, 1);
1264            }
1265        } else if (!strcmp("-i", *iter)) {
1266            toucallback = UCNV_TO_U_CALLBACK_SKIP;
1267        } else if (!strcmp("--callback", *iter)) {
1268            iter++;
1269            if (iter != end) {
1270                const struct callback_ent *cbe = findCallback(*iter);
1271                if (cbe) {
1272                    fromucallback = cbe->fromu;
1273                    fromuctxt = cbe->fromuctxt;
1274                    toucallback = cbe->tou;
1275                    touctxt = cbe->touctxt;
1276                } else {
1277                    UnicodeString str(*iter);
1278                    initMsg(pname);
1279                    u_wmsg(stderr, "unknownCallback", str.getTerminatedBuffer());
1280                    return 4;
1281                }
1282            } else {
1283                usage(pname, 1);
1284            }
1285        } else if (!strcmp("-s", *iter) || !strcmp("--silent", *iter)) {
1286            verbose = FALSE;
1287        } else if (!strcmp("-v", *iter) || !strcmp("--verbose", *iter)) {
1288            verbose = TRUE;
1289        } else if (!strcmp("-V", *iter) || !strcmp("--version", *iter)) {
1290            printf("%s v2.1  ICU " U_ICU_VERSION "\n", pname);
1291            return 0;
1292        } else if (!strcmp("-o", *iter) || !strcmp("--output", *iter)) {
1293            ++iter;
1294            if (iter != end && !outfilestr) {
1295                outfilestr = *iter;
1296            } else {
1297                usage(pname, 1);
1298            }
1299        } else if (0 == strcmp("--add-signature", *iter)) {
1300            cf.signature = 1;
1301        } else if (0 == strcmp("--remove-signature", *iter)) {
1302            cf.signature = -1;
1303        } else if (**iter == '-' && (*iter)[1]) {
1304            usage(pname, 1);
1305        } else {
1306            // move a non-option up in argv[]
1307            *remainArgvLimit++ = *iter;
1308        }
1309    }
1310
1311    if (printConvs || printName) {
1312        return printConverters(pname, printName, printCanon) ? 2 : 0;
1313    } else if (printTranslits) {
1314        return printTransliterators(printCanon) ? 3 : 0;
1315    }
1316
1317    if (!fromcpage || !uprv_strcmp(fromcpage, "-")) {
1318        fromcpage = ucnv_getDefaultName();
1319    }
1320    if (!tocpage || !uprv_strcmp(tocpage, "-")) {
1321        tocpage = ucnv_getDefaultName();
1322    }
1323
1324    // Open the correct output file or connect to stdout for reading input
1325    if (outfilestr != 0 && strcmp(outfilestr, "-")) {
1326        outfile = fopen(outfilestr, "wb");
1327        if (outfile == 0) {
1328            UnicodeString str1(outfilestr, "");
1329            UnicodeString str2(strerror(errno), "");
1330            initMsg(pname);
1331            u_wmsg(stderr, "cantCreateOutputF",
1332                str1.getBuffer(), str2.getBuffer());
1333            return 1;
1334        }
1335    } else {
1336        outfilestr = "-";
1337        outfile = stdout;
1338#ifdef USE_FILENO_BINARY_MODE
1339        if (setmode(fileno(outfile), O_BINARY) == -1) {
1340            u_wmsg(stderr, "cantSetOutBinMode");
1341            exit(-1);
1342        }
1343#endif
1344    }
1345
1346    /* Loop again on the arguments to find all the input files, and
1347    convert them. */
1348
1349    cf.setBufferSize(bufsz);
1350
1351    if(remainArgv < remainArgvLimit) {
1352        for (iter = remainArgv; iter != remainArgvLimit; iter++) {
1353            if (!cf.convertFile(
1354                    pname, fromcpage, toucallback, touctxt, tocpage,
1355                    fromucallback, fromuctxt, fallback, translit, *iter,
1356                    outfile, verbose)
1357            ) {
1358                goto error_exit;
1359            }
1360        }
1361    } else {
1362        if (!cf.convertFile(
1363                pname, fromcpage, toucallback, touctxt, tocpage,
1364                fromucallback, fromuctxt, fallback, translit, 0,
1365                outfile, verbose)
1366        ) {
1367            goto error_exit;
1368        }
1369    }
1370
1371    goto normal_exit;
1372error_exit:
1373#if !UCONFIG_NO_LEGACY_CONVERSION
1374    ret = 1;
1375#else
1376    fprintf(stderr, "uconv error: UCONFIG_NO_LEGACY_CONVERSION is on. See uconfig.h\n");
1377#endif
1378normal_exit:
1379
1380    if (outfile != stdout) {
1381        fclose(outfile);
1382    }
1383
1384    u_cleanup();
1385
1386    return ret;
1387}
1388
1389
1390/*
1391 * Hey, Emacs, please set the following:
1392 *
1393 * Local Variables:
1394 * indent-tabs-mode: nil
1395 * End:
1396 *
1397 */
1398