1*63eb84d1Schristos /* Iterating through multibyte strings: macros for multi-byte encodings.
2*63eb84d1Schristos Copyright (C) 2001, 2005 Free Software Foundation, Inc.
3*63eb84d1Schristos
4*63eb84d1Schristos This program is free software; you can redistribute it and/or modify
5*63eb84d1Schristos it under the terms of the GNU General Public License as published by
6*63eb84d1Schristos the Free Software Foundation; either version 2, or (at your option)
7*63eb84d1Schristos any later version.
8*63eb84d1Schristos
9*63eb84d1Schristos This program is distributed in the hope that it will be useful,
10*63eb84d1Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
11*63eb84d1Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12*63eb84d1Schristos GNU General Public License for more details.
13*63eb84d1Schristos
14*63eb84d1Schristos You should have received a copy of the GNU General Public License
15*63eb84d1Schristos along with this program; if not, write to the Free Software Foundation,
16*63eb84d1Schristos Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17*63eb84d1Schristos
18*63eb84d1Schristos /* Written by Bruno Haible <bruno@clisp.org>. */
19*63eb84d1Schristos
20*63eb84d1Schristos /* The macros in this file implement forward iteration through a
21*63eb84d1Schristos multi-byte string, without knowing its length a-priori.
22*63eb84d1Schristos
23*63eb84d1Schristos With these macros, an iteration loop that looks like
24*63eb84d1Schristos
25*63eb84d1Schristos char *iter;
26*63eb84d1Schristos for (iter = buf; *iter != '\0'; iter++)
27*63eb84d1Schristos {
28*63eb84d1Schristos do_something (*iter);
29*63eb84d1Schristos }
30*63eb84d1Schristos
31*63eb84d1Schristos becomes
32*63eb84d1Schristos
33*63eb84d1Schristos mbui_iterator_t iter;
34*63eb84d1Schristos for (mbui_init (iter, buf); mbui_avail (iter); mbui_advance (iter))
35*63eb84d1Schristos {
36*63eb84d1Schristos do_something (mbui_cur_ptr (iter), mb_len (mbui_cur (iter)));
37*63eb84d1Schristos }
38*63eb84d1Schristos
39*63eb84d1Schristos The benefit of these macros over plain use of mbrtowc is:
40*63eb84d1Schristos - Handling of invalid multibyte sequences is possible without
41*63eb84d1Schristos making the code more complicated, while still preserving the
42*63eb84d1Schristos invalid multibyte sequences.
43*63eb84d1Schristos
44*63eb84d1Schristos Compared to mbiter.h, the macros here don't need to know the string's
45*63eb84d1Schristos length a-priori. The downside is that at each step, the look-ahead
46*63eb84d1Schristos that guards against overrunning the terminating '\0' is more expensive.
47*63eb84d1Schristos The mbui_* macros are therefore suitable when there is a high probability
48*63eb84d1Schristos that only the first few multibyte characters need to be inspected.
49*63eb84d1Schristos Whereas the mbi_* macros are better if usually the iteration runs
50*63eb84d1Schristos through the entire string.
51*63eb84d1Schristos
52*63eb84d1Schristos mbui_iterator_t
53*63eb84d1Schristos is a type usable for variable declarations.
54*63eb84d1Schristos
55*63eb84d1Schristos mbui_init (iter, startptr)
56*63eb84d1Schristos initializes the iterator, starting at startptr.
57*63eb84d1Schristos
58*63eb84d1Schristos mbui_avail (iter)
59*63eb84d1Schristos returns true if there are more multibyte chracters available before
60*63eb84d1Schristos the end of string is reached. In this case, mbui_cur (iter) is
61*63eb84d1Schristos initialized to the next multibyte chracter.
62*63eb84d1Schristos
63*63eb84d1Schristos mbui_advance (iter)
64*63eb84d1Schristos advances the iterator by one multibyte character.
65*63eb84d1Schristos
66*63eb84d1Schristos mbui_cur (iter)
67*63eb84d1Schristos returns the current multibyte character, of type mbchar_t. All the
68*63eb84d1Schristos macros defined in mbchar.h can be used on it.
69*63eb84d1Schristos
70*63eb84d1Schristos mbui_cur_ptr (iter)
71*63eb84d1Schristos return a pointer to the beginning of the current multibyte character.
72*63eb84d1Schristos
73*63eb84d1Schristos mbui_reloc (iter, ptrdiff)
74*63eb84d1Schristos relocates iterator when the string is moved by ptrdiff bytes.
75*63eb84d1Schristos
76*63eb84d1Schristos Here are the function prototypes of the macros.
77*63eb84d1Schristos
78*63eb84d1Schristos extern void mbui_init (mbui_iterator_t iter, const char *startptr);
79*63eb84d1Schristos extern bool mbui_avail (mbui_iterator_t iter);
80*63eb84d1Schristos extern void mbui_advance (mbui_iterator_t iter);
81*63eb84d1Schristos extern mbchar_t mbui_cur (mbui_iterator_t iter);
82*63eb84d1Schristos extern const char * mbui_cur_ptr (mbui_iterator_t iter);
83*63eb84d1Schristos extern void mbui_reloc (mbui_iterator_t iter, ptrdiff_t ptrdiff);
84*63eb84d1Schristos */
85*63eb84d1Schristos
86*63eb84d1Schristos #ifndef _MBUITER_H
87*63eb84d1Schristos #define _MBUITER_H 1
88*63eb84d1Schristos
89*63eb84d1Schristos #include <assert.h>
90*63eb84d1Schristos #include <stdbool.h>
91*63eb84d1Schristos #include <stdlib.h>
92*63eb84d1Schristos
93*63eb84d1Schristos /* Tru64 with Desktop Toolkit C has a bug: <stdio.h> must be included before
94*63eb84d1Schristos <wchar.h>.
95*63eb84d1Schristos BSD/OS 4.1 has a bug: <stdio.h> and <time.h> must be included before
96*63eb84d1Schristos <wchar.h>. */
97*63eb84d1Schristos #include <stdio.h>
98*63eb84d1Schristos #include <time.h>
99*63eb84d1Schristos #include <wchar.h>
100*63eb84d1Schristos
101*63eb84d1Schristos #include "mbchar.h"
102*63eb84d1Schristos #include "strnlen1.h"
103*63eb84d1Schristos
104*63eb84d1Schristos struct mbuiter_multi
105*63eb84d1Schristos {
106*63eb84d1Schristos bool in_shift; /* true if next byte may not be interpreted as ASCII */
107*63eb84d1Schristos mbstate_t state; /* if in_shift: current shift state */
108*63eb84d1Schristos bool next_done; /* true if mbui_avail has already filled the following */
109*63eb84d1Schristos struct mbchar cur; /* the current character:
110*63eb84d1Schristos const char *cur.ptr pointer to current character
111*63eb84d1Schristos The following are only valid after mbui_avail.
112*63eb84d1Schristos size_t cur.bytes number of bytes of current character
113*63eb84d1Schristos bool cur.wc_valid true if wc is a valid wide character
114*63eb84d1Schristos wchar_t cur.wc if wc_valid: the current character
115*63eb84d1Schristos */
116*63eb84d1Schristos };
117*63eb84d1Schristos
118*63eb84d1Schristos static inline void
mbuiter_multi_next(struct mbuiter_multi * iter)119*63eb84d1Schristos mbuiter_multi_next (struct mbuiter_multi *iter)
120*63eb84d1Schristos {
121*63eb84d1Schristos if (iter->next_done)
122*63eb84d1Schristos return;
123*63eb84d1Schristos if (iter->in_shift)
124*63eb84d1Schristos goto with_shift;
125*63eb84d1Schristos /* Handle most ASCII characters quickly, without calling mbrtowc(). */
126*63eb84d1Schristos if (is_basic (*iter->cur.ptr))
127*63eb84d1Schristos {
128*63eb84d1Schristos /* These characters are part of the basic character set. ISO C 99
129*63eb84d1Schristos guarantees that their wide character code is identical to their
130*63eb84d1Schristos char code. */
131*63eb84d1Schristos iter->cur.bytes = 1;
132*63eb84d1Schristos iter->cur.wc = *iter->cur.ptr;
133*63eb84d1Schristos iter->cur.wc_valid = true;
134*63eb84d1Schristos }
135*63eb84d1Schristos else
136*63eb84d1Schristos {
137*63eb84d1Schristos assert (mbsinit (&iter->state));
138*63eb84d1Schristos iter->in_shift = true;
139*63eb84d1Schristos with_shift:
140*63eb84d1Schristos iter->cur.bytes = mbrtowc (&iter->cur.wc, iter->cur.ptr,
141*63eb84d1Schristos strnlen1 (iter->cur.ptr, MB_CUR_MAX),
142*63eb84d1Schristos &iter->state);
143*63eb84d1Schristos if (iter->cur.bytes == (size_t) -1)
144*63eb84d1Schristos {
145*63eb84d1Schristos /* An invalid multibyte sequence was encountered. */
146*63eb84d1Schristos iter->cur.bytes = 1;
147*63eb84d1Schristos iter->cur.wc_valid = false;
148*63eb84d1Schristos /* Whether to set iter->in_shift = false and reset iter->state
149*63eb84d1Schristos or not is not very important; the string is bogus anyway. */
150*63eb84d1Schristos }
151*63eb84d1Schristos else if (iter->cur.bytes == (size_t) -2)
152*63eb84d1Schristos {
153*63eb84d1Schristos /* An incomplete multibyte character at the end. */
154*63eb84d1Schristos iter->cur.bytes = strlen (iter->cur.ptr);
155*63eb84d1Schristos iter->cur.wc_valid = false;
156*63eb84d1Schristos /* Whether to set iter->in_shift = false and reset iter->state
157*63eb84d1Schristos or not is not important; the string end is reached anyway. */
158*63eb84d1Schristos }
159*63eb84d1Schristos else
160*63eb84d1Schristos {
161*63eb84d1Schristos if (iter->cur.bytes == 0)
162*63eb84d1Schristos {
163*63eb84d1Schristos /* A null wide character was encountered. */
164*63eb84d1Schristos iter->cur.bytes = 1;
165*63eb84d1Schristos assert (*iter->cur.ptr == '\0');
166*63eb84d1Schristos assert (iter->cur.wc == 0);
167*63eb84d1Schristos }
168*63eb84d1Schristos iter->cur.wc_valid = true;
169*63eb84d1Schristos
170*63eb84d1Schristos /* When in the initial state, we can go back treating ASCII
171*63eb84d1Schristos characters more quickly. */
172*63eb84d1Schristos if (mbsinit (&iter->state))
173*63eb84d1Schristos iter->in_shift = false;
174*63eb84d1Schristos }
175*63eb84d1Schristos }
176*63eb84d1Schristos iter->next_done = true;
177*63eb84d1Schristos }
178*63eb84d1Schristos
179*63eb84d1Schristos static inline void
mbuiter_multi_reloc(struct mbuiter_multi * iter,ptrdiff_t ptrdiff)180*63eb84d1Schristos mbuiter_multi_reloc (struct mbuiter_multi *iter, ptrdiff_t ptrdiff)
181*63eb84d1Schristos {
182*63eb84d1Schristos iter->cur.ptr += ptrdiff;
183*63eb84d1Schristos }
184*63eb84d1Schristos
185*63eb84d1Schristos /* Iteration macros. */
186*63eb84d1Schristos typedef struct mbuiter_multi mbui_iterator_t;
187*63eb84d1Schristos #define mbui_init(iter, startptr) \
188*63eb84d1Schristos ((iter).cur.ptr = (startptr), \
189*63eb84d1Schristos (iter).in_shift = false, memset (&(iter).state, '\0', sizeof (mbstate_t)), \
190*63eb84d1Schristos (iter).next_done = false)
191*63eb84d1Schristos #define mbui_avail(iter) \
192*63eb84d1Schristos (mbuiter_multi_next (&(iter)), !mb_isnul ((iter).cur))
193*63eb84d1Schristos #define mbui_advance(iter) \
194*63eb84d1Schristos ((iter).cur.ptr += (iter).cur.bytes, (iter).next_done = false)
195*63eb84d1Schristos
196*63eb84d1Schristos /* Access to the current character. */
197*63eb84d1Schristos #define mbui_cur(iter) (iter).cur
198*63eb84d1Schristos #define mbui_cur_ptr(iter) (iter).cur.ptr
199*63eb84d1Schristos
200*63eb84d1Schristos /* Relocation. */
201*63eb84d1Schristos #define mbui_reloc(iter, ptrdiff) mbuiter_multi_reloc (&iter, ptrdiff)
202*63eb84d1Schristos
203*63eb84d1Schristos #endif /* _MBUITER_H */
204