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