Coverage Report

Created: 2026-05-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/oficonv/libsrc/citrus_iconv_std.c
Line
Count
Source
1
/*-
2
 * Copyright (c)2003 Citrus Project,
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
 * SUCH DAMAGE.
25
 */
26
27
#include "dcmtk/config/osconfig.h"
28
#include "citrus_iconv_std.h"
29
30
#ifdef HAVE_SYS_QUEUE_H
31
#include <sys/queue.h>
32
#else
33
#include "dcmtk/oficonv/queue.h"
34
#endif
35
36
37
#include <errno.h>
38
#include <limits.h>
39
#include <stdbool.h>
40
#include <stdio.h>
41
#include <stdlib.h>
42
#include <string.h>
43
44
#include "citrus_bcs.h"
45
#include "citrus_types.h"
46
#include "citrus_module.h"
47
#include "citrus_region.h"
48
#include "citrus_mmap.h"
49
#include "citrus_hash.h"
50
#include "citrus_iconv.h"
51
#include "citrus_stdenc.h"
52
#include "citrus_mapper.h"
53
#include "citrus_csmapper.h"
54
#include "citrus_memstream.h"
55
#include "citrus_esdb.h"
56
57
/* ---------------------------------------------------------------------- */
58
59
_CITRUS_ICONV_DECLS(iconv_std);
60
_CITRUS_ICONV_DEF_OPS(iconv_std);
61
62
63
/* ---------------------------------------------------------------------- */
64
65
int
66
_citrus_iconv_std_iconv_getops(struct _citrus_iconv_ops *ops)
67
0
{
68
69
0
    memcpy(ops, &_citrus_iconv_std_iconv_ops,
70
0
        sizeof(_citrus_iconv_std_iconv_ops));
71
72
0
    return (0);
73
0
}
74
75
/* ---------------------------------------------------------------------- */
76
77
/*
78
 * convenience routines for stdenc.
79
 */
80
static __inline void
81
save_encoding_state(struct _citrus_iconv_std_encoding *se)
82
0
{
83
84
0
    if (se->se_ps)
85
0
        memcpy(se->se_pssaved, se->se_ps,
86
0
            _citrus_stdenc_get_state_size(se->se_handle));
87
0
}
88
89
static __inline void
90
restore_encoding_state(struct _citrus_iconv_std_encoding *se)
91
0
{
92
93
0
    if (se->se_ps)
94
0
        memcpy(se->se_ps, se->se_pssaved,
95
0
            _citrus_stdenc_get_state_size(se->se_handle));
96
0
}
97
98
static __inline void
99
init_encoding_state(struct _citrus_iconv_std_encoding *se)
100
0
{
101
102
0
    if (se->se_ps)
103
0
        _citrus_stdenc_init_state(se->se_handle, se->se_ps);
104
0
}
105
106
static __inline int
107
mbtocsx(struct _citrus_iconv_std_encoding *se,
108
    _citrus_csid_t *csid, _citrus_index_t *idx, char **s, size_t n, size_t *nresult,
109
    struct iconv_hooks *hooks)
110
0
{
111
112
0
    return (_citrus_stdenc_mbtocs(se->se_handle, csid, idx, s, n, se->se_ps,
113
0
                  nresult, hooks));
114
0
}
115
116
static __inline int
117
cstombx(struct _citrus_iconv_std_encoding *se,
118
    char *s, size_t n, _citrus_csid_t csid, _citrus_index_t idx, size_t *nresult,
119
    struct iconv_hooks *hooks)
120
0
{
121
122
0
    return (_citrus_stdenc_cstomb(se->se_handle, s, n, csid, idx, se->se_ps,
123
0
                  nresult, hooks));
124
0
}
125
126
static __inline int
127
wctombx(struct _citrus_iconv_std_encoding *se,
128
    char *s, size_t n, _citrus_wc_t wc, size_t *nresult,
129
    struct iconv_hooks *hooks)
130
0
{
131
132
0
    return (_citrus_stdenc_wctomb(se->se_handle, s, n, wc, se->se_ps, nresult,
133
0
                 hooks));
134
0
}
135
136
static __inline int
137
put_state_resetx(struct _citrus_iconv_std_encoding *se, char *s, size_t n,
138
    size_t *nresult)
139
0
{
140
141
0
    return (_citrus_stdenc_put_state_reset(se->se_handle, s, n, se->se_ps, nresult));
142
0
}
143
144
static __inline int
145
get_state_desc_gen(struct _citrus_iconv_std_encoding *se, int *rstate)
146
0
{
147
0
    struct _citrus_stdenc_state_desc ssd;
148
0
    int ret;
149
150
0
    ret = _citrus_stdenc_get_state_desc(se->se_handle, se->se_ps,
151
0
        _CITRUS_STDENC_SDID_GENERIC, &ssd);
152
0
    if (!ret)
153
0
        *rstate = ssd.u.generic.state;
154
155
0
    return (ret);
156
0
}
157
158
/*
159
 * init encoding context
160
 */
161
static int
162
init_encoding(struct _citrus_iconv_std_encoding *se, struct _citrus_stdenc *cs,
163
    void *ps1, void *ps2)
164
0
{
165
0
    int ret = -1;
166
167
0
    se->se_handle = cs;
168
0
    se->se_ps = ps1;
169
0
    se->se_pssaved = ps2;
170
171
0
    if (se->se_ps)
172
0
        ret = _citrus_stdenc_init_state(cs, se->se_ps);
173
0
    if (!ret && se->se_pssaved)
174
0
        ret = _citrus_stdenc_init_state(cs, se->se_pssaved);
175
176
0
    return (ret);
177
0
}
178
179
static int
180
open_csmapper(struct _citrus_csmapper **rcm, const char *src, const char *dst,
181
    unsigned long *rnorm)
182
0
{
183
0
    struct _citrus_csmapper *cm;
184
0
    int ret;
185
186
0
    ret = _citrus_csmapper_open(&cm, src, dst, 0, rnorm);
187
0
    if (ret)
188
0
        return (ret);
189
0
    if (_citrus_csmapper_get_src_max(cm) != 1 || _citrus_csmapper_get_dst_max(cm) != 1 ||
190
0
        _citrus_csmapper_get_state_size(cm) != 0) {
191
0
        _citrus_csmapper_close(cm);
192
0
        return (EINVAL);
193
0
    }
194
195
0
    *rcm = cm;
196
197
0
    return (0);
198
0
}
199
200
static void
201
close_dsts(struct _citrus_iconv_std_dst_list *dl)
202
0
{
203
0
    struct _citrus_iconv_std_dst *sd;
204
205
0
    while ((sd = TAILQ_FIRST(dl)) != NULL) {
206
0
        TAILQ_REMOVE(dl, sd, sd_entry);
207
0
        _citrus_csmapper_close(sd->sd_mapper);
208
0
        free(sd);
209
0
    }
210
0
}
211
212
static int
213
open_dsts(struct _citrus_iconv_std_dst_list *dl,
214
    const struct _citrus_esdb_charset *ec, const struct _citrus_esdb *dbdst)
215
0
{
216
0
    struct _citrus_iconv_std_dst *sd, *sdtmp;
217
0
    unsigned long norm;
218
0
    int i, ret;
219
220
0
    sd = malloc(sizeof(*sd));
221
0
    if (sd == NULL)
222
0
        return (errno);
223
224
0
    for (i = 0; i < dbdst->db_num_charsets; i++) {
225
0
        ret = open_csmapper(&sd->sd_mapper, ec->ec_csname,
226
0
            dbdst->db_charsets[i].ec_csname, &norm);
227
0
        if (ret == 0) {
228
0
            sd->sd_csid = dbdst->db_charsets[i].ec_csid;
229
0
            sd->sd_norm = norm;
230
            /* insert this mapper by sorted order. */
231
0
            TAILQ_FOREACH(sdtmp, dl, sd_entry) {
232
0
                if (sdtmp->sd_norm > norm) {
233
0
                    TAILQ_INSERT_BEFORE(sdtmp, sd,
234
0
                        sd_entry);
235
0
                    sd = NULL;
236
0
                    break;
237
0
                }
238
0
            }
239
0
            if (sd)
240
0
                TAILQ_INSERT_TAIL(dl, sd, sd_entry);
241
0
            sd = malloc(sizeof(*sd));
242
0
            if (sd == NULL) {
243
0
                ret = errno;
244
0
                close_dsts(dl);
245
0
                return (ret);
246
0
            }
247
0
        } else if (ret != ENOENT) {
248
0
            close_dsts(dl);
249
0
            free(sd);
250
0
            return (ret);
251
0
        }
252
0
    }
253
0
    free(sd);
254
0
    return (0);
255
0
}
256
257
static void
258
close_srcs(struct _citrus_iconv_std_src_list *sl)
259
0
{
260
0
    struct _citrus_iconv_std_src *ss;
261
262
0
    while ((ss = TAILQ_FIRST(sl)) != NULL) {
263
0
        TAILQ_REMOVE(sl, ss, ss_entry);
264
0
        close_dsts(&ss->ss_dsts);
265
0
        free(ss);
266
0
    }
267
0
}
268
269
static int
270
open_srcs(struct _citrus_iconv_std_src_list *sl,
271
    const struct _citrus_esdb *dbsrc, const struct _citrus_esdb *dbdst)
272
0
{
273
0
    struct _citrus_iconv_std_src *ss;
274
0
    int count = 0, i, ret;
275
276
0
    ss = malloc(sizeof(*ss));
277
0
    if (ss == NULL)
278
0
        return (errno);
279
280
0
    TAILQ_INIT(&ss->ss_dsts);
281
282
0
    for (i = 0; i < dbsrc->db_num_charsets; i++) {
283
0
        ret = open_dsts(&ss->ss_dsts, &dbsrc->db_charsets[i], dbdst);
284
0
        if (ret)
285
0
            goto err;
286
0
        if (!TAILQ_EMPTY(&ss->ss_dsts)) {
287
0
            ss->ss_csid = dbsrc->db_charsets[i].ec_csid;
288
0
            TAILQ_INSERT_TAIL(sl, ss, ss_entry);
289
0
            ss = malloc(sizeof(*ss));
290
0
            if (ss == NULL) {
291
0
                ret = errno;
292
0
                goto err;
293
0
            }
294
0
            count++;
295
0
            TAILQ_INIT(&ss->ss_dsts);
296
0
        }
297
0
    }
298
0
    free(ss);
299
300
0
    return (count ? 0 : ENOENT);
301
302
0
err:
303
0
    free(ss);
304
0
    close_srcs(sl);
305
0
    return (ret);
306
0
}
307
308
/* do convert a character */
309
0
#define E_NO_CORRESPONDING_CHAR ENOENT /* XXX */
310
static int
311
/*ARGSUSED*/
312
do_conv(const struct _citrus_iconv_std_shared *is,
313
    _citrus_csid_t *csid, _citrus_index_t *idx)
314
0
{
315
0
    struct _citrus_iconv_std_dst *sd;
316
0
    struct _citrus_iconv_std_src *ss;
317
0
    _citrus_index_t tmpidx;
318
0
    int ret;
319
320
0
    TAILQ_FOREACH(ss, &is->is_srcs, ss_entry) {
321
0
        if (ss->ss_csid == *csid) {
322
0
            TAILQ_FOREACH(sd, &ss->ss_dsts, sd_entry) {
323
0
                ret = _citrus_csmapper_convert(sd->sd_mapper,
324
0
                    &tmpidx, *idx, NULL);
325
0
                switch (ret) {
326
0
                case _CITRUS_MAPPER_CONVERT_SUCCESS:
327
0
                    *csid = sd->sd_csid;
328
0
                    *idx = tmpidx;
329
0
                    return (0);
330
0
                case _CITRUS_MAPPER_CONVERT_NONIDENTICAL:
331
0
                    break;
332
0
                case _CITRUS_MAPPER_CONVERT_SRC_MORE:
333
                    /*FALLTHROUGH*/
334
0
                case _CITRUS_MAPPER_CONVERT_DST_MORE:
335
                    /*FALLTHROUGH*/
336
0
                case _CITRUS_MAPPER_CONVERT_ILSEQ:
337
0
                    return (EILSEQ);
338
0
                case _CITRUS_MAPPER_CONVERT_FATAL:
339
0
                    return (EINVAL);
340
0
                }
341
0
            }
342
0
            break;
343
0
        }
344
0
    }
345
346
0
    return (E_NO_CORRESPONDING_CHAR);
347
0
}
348
/* ---------------------------------------------------------------------- */
349
350
static int
351
/*ARGSUSED*/
352
_citrus_iconv_std_iconv_init_shared(struct _citrus_iconv_shared *ci,
353
    const char * src, const char * dst)
354
0
{
355
0
    struct _citrus_esdb esdbdst, esdbsrc;
356
0
    struct _citrus_iconv_std_shared *is;
357
0
    int ret;
358
359
0
    is = malloc(sizeof(*is));
360
0
    if (is == NULL) {
361
0
        ret = errno;
362
0
        goto err0;
363
0
    }
364
0
    ret = _citrus_esdb_open(&esdbsrc, src);
365
0
    if (ret)
366
0
        goto err1;
367
0
    ret = _citrus_esdb_open(&esdbdst, dst);
368
0
    if (ret)
369
0
        goto err2;
370
0
    ret = _citrus_stdenc_open(&is->is_src_encoding, esdbsrc.db_encname,
371
0
        esdbsrc.db_variable, esdbsrc.db_len_variable);
372
0
    if (ret)
373
0
        goto err3;
374
0
    ret = _citrus_stdenc_open(&is->is_dst_encoding, esdbdst.db_encname,
375
0
        esdbdst.db_variable, esdbdst.db_len_variable);
376
0
    if (ret)
377
0
        goto err4;
378
0
    is->is_use_invalid = esdbdst.db_use_invalid;
379
0
    is->is_invalid = esdbdst.db_invalid;
380
381
0
    TAILQ_INIT(&is->is_srcs);
382
0
    ret = open_srcs(&is->is_srcs, &esdbsrc, &esdbdst);
383
0
    if (ret)
384
0
        goto err5;
385
386
0
    _citrus_esdb_close(&esdbsrc);
387
0
    _citrus_esdb_close(&esdbdst);
388
0
    ci->ci_closure = is;
389
390
0
    return (0);
391
392
0
err5:
393
0
    _citrus_stdenc_close(is->is_dst_encoding);
394
0
err4:
395
0
    _citrus_stdenc_close(is->is_src_encoding);
396
0
err3:
397
0
    _citrus_esdb_close(&esdbdst);
398
0
err2:
399
0
    _citrus_esdb_close(&esdbsrc);
400
0
err1:
401
0
    free(is);
402
0
err0:
403
0
    return (ret);
404
0
}
405
406
static void
407
_citrus_iconv_std_iconv_uninit_shared(struct _citrus_iconv_shared *ci)
408
0
{
409
0
    struct _citrus_iconv_std_shared *is = ci->ci_closure;
410
411
0
    if (is == NULL)
412
0
        return;
413
414
0
    _citrus_stdenc_close(is->is_src_encoding);
415
0
    _citrus_stdenc_close(is->is_dst_encoding);
416
0
    close_srcs(&is->is_srcs);
417
0
    free(is);
418
0
}
419
420
static int
421
_citrus_iconv_std_iconv_init_context(struct _citrus_iconv *cv)
422
0
{
423
0
    const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure;
424
0
    struct _citrus_iconv_std_context *sc;
425
0
    char *ptr;
426
0
    size_t sz, szpsdst, szpssrc;
427
428
0
    szpssrc = _citrus_stdenc_get_state_size(is->is_src_encoding);
429
0
    szpsdst = _citrus_stdenc_get_state_size(is->is_dst_encoding);
430
431
0
    sz = (szpssrc + szpsdst)*2 + sizeof(struct _citrus_iconv_std_context);
432
0
    sc = malloc(sz);
433
0
    if (sc == NULL)
434
0
        return (errno);
435
436
0
    ptr = (char *)&sc[1];
437
0
    if (szpssrc > 0)
438
0
        init_encoding(&sc->sc_src_encoding, is->is_src_encoding,
439
0
            ptr, ptr+szpssrc);
440
0
    else
441
0
        init_encoding(&sc->sc_src_encoding, is->is_src_encoding,
442
0
            NULL, NULL);
443
0
    ptr += szpssrc*2;
444
0
    if (szpsdst > 0)
445
0
        init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding,
446
0
            ptr, ptr+szpsdst);
447
0
    else
448
0
        init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding,
449
0
            NULL, NULL);
450
451
0
    cv->cv_closure = (void *)sc;
452
453
0
    return (0);
454
0
}
455
456
static void
457
_citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv *cv)
458
0
{
459
460
0
    free(cv->cv_closure);
461
0
}
462
463
static int
464
_citrus_iconv_std_iconv_convert(struct _citrus_iconv * cv,
465
    char * * in, size_t * inbytes,
466
    char * * out, size_t * outbytes,
467
    uint32_t flags, size_t * invalids)
468
0
{
469
0
    const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure;
470
0
    struct _citrus_iconv_std_context *sc = cv->cv_closure;
471
0
    _citrus_csid_t csid;
472
0
    _citrus_index_t idx;
473
0
    char *tmpin;
474
0
    size_t inval, in_mb_cur_min, szrin, szrout;
475
0
    int ret, state = 0;
476
477
0
    inval = 0;
478
0
    if (in == NULL || *in == NULL) {
479
        /* special cases */
480
0
        if (out != NULL && *out != NULL) {
481
            /* init output state and store the shift sequence */
482
0
            save_encoding_state(&sc->sc_src_encoding);
483
0
            save_encoding_state(&sc->sc_dst_encoding);
484
0
            szrout = 0;
485
486
0
            ret = put_state_resetx(&sc->sc_dst_encoding,
487
0
                *out, *outbytes, &szrout);
488
0
            if (ret)
489
0
                goto err;
490
491
0
            if (szrout == (size_t)-2) {
492
                /* too small to store the character */
493
0
                ret = EINVAL;
494
0
                goto err;
495
0
            }
496
0
            *out += szrout;
497
0
            *outbytes -= szrout;
498
0
        } else
499
            /* otherwise, discard the shift sequence */
500
0
            init_encoding_state(&sc->sc_dst_encoding);
501
0
        init_encoding_state(&sc->sc_src_encoding);
502
0
        *invalids = 0;
503
0
        return (0);
504
0
    }
505
506
0
    in_mb_cur_min = _citrus_stdenc_get_mb_cur_min(is->is_src_encoding);
507
508
    /* normal case */
509
0
    for (;;) {
510
0
        if (*inbytes == 0) {
511
0
            ret = get_state_desc_gen(&sc->sc_src_encoding, &state);
512
0
            if (state == _CITRUS_STDENC_SDGEN_INITIAL ||
513
0
                state == _CITRUS_STDENC_SDGEN_STABLE)
514
0
                break;
515
0
        }
516
517
        /* save the encoding states for the error recovery */
518
0
        save_encoding_state(&sc->sc_src_encoding);
519
0
        save_encoding_state(&sc->sc_dst_encoding);
520
521
        /* mb -> csid/index */
522
0
        tmpin = *in;
523
0
        szrin = szrout = 0;
524
0
        ret = mbtocsx(&sc->sc_src_encoding, &csid, &idx, &tmpin,
525
0
            *inbytes, &szrin, cv->ci_hooks);
526
527
0
        if (ret != 0 && (ret != EILSEQ ||
528
0
            !cv->ci_discard_ilseq)) {
529
0
                goto err;
530
0
        } else if (ret == EILSEQ) {
531
                /*
532
                 * If //IGNORE was specified, we'll just keep crunching
533
                 * through invalid characters.
534
                 */
535
0
                *in += in_mb_cur_min;
536
0
                *inbytes -= in_mb_cur_min;
537
0
                restore_encoding_state(&sc->sc_src_encoding);
538
0
                restore_encoding_state(&sc->sc_dst_encoding);
539
0
                continue;
540
0
        }
541
542
0
        if (szrin == (size_t)-2) {
543
            /* incompleted character */
544
0
            ret = get_state_desc_gen(&sc->sc_src_encoding, &state);
545
0
            if (ret) {
546
0
                ret = EINVAL;
547
0
                goto err;
548
0
            }
549
0
            switch (state) {
550
0
            case _CITRUS_STDENC_SDGEN_INITIAL:
551
0
            case _CITRUS_STDENC_SDGEN_STABLE:
552
                /* fetch shift sequences only. */
553
0
                goto next;
554
0
            }
555
0
            ret = EINVAL;
556
0
            goto err;
557
0
        }
558
        /* convert the character */
559
0
        ret = do_conv(is, &csid, &idx);
560
0
        if (ret) {
561
0
            if (ret == E_NO_CORRESPONDING_CHAR) {
562
                /*
563
                 * GNU iconv returns EILSEQ when no
564
                 * corresponding character in the output.
565
                 * Some software depends on this behavior
566
                 * though this is against POSIX specification.
567
                 */
568
0
                if (cv->ci_ilseq_invalid != 0) {
569
0
                        ret = EILSEQ;
570
0
                        goto err;
571
0
                }
572
0
                inval++;
573
0
                szrout = 0;
574
0
                if ((((flags & _CITRUS_ICONV_F_HIDE_INVALID) == 0) &&
575
0
                    !cv->ci_discard_ilseq) &&
576
0
                    is->is_use_invalid) {
577
0
                    ret = wctombx(&sc->sc_dst_encoding,
578
0
                        *out, *outbytes, is->is_invalid,
579
0
                        &szrout, cv->ci_hooks);
580
0
                    if (ret)
581
0
                        goto err;
582
0
                }
583
0
                goto next;
584
0
            } else
585
0
                goto err;
586
0
        }
587
        /* csid/index -> mb */
588
0
        ret = cstombx(&sc->sc_dst_encoding,
589
0
            *out, *outbytes, csid, idx, &szrout,
590
0
            cv->ci_hooks);
591
0
        if (ret)
592
0
            goto err;
593
0
next:
594
0
        *inbytes -= tmpin-*in; /* szrin is insufficient on \0. */
595
0
        *in = tmpin;
596
0
        *outbytes -= szrout;
597
0
        *out += szrout;
598
0
    }
599
0
    *invalids = inval;
600
601
0
    return (0);
602
603
0
err:
604
0
    restore_encoding_state(&sc->sc_src_encoding);
605
0
    restore_encoding_state(&sc->sc_dst_encoding);
606
0
    *invalids = inval;
607
608
0
    return (ret);
609
0
}