065b85666e4554fb8c5eaa12d54447fd23b79bd1
[gnulib.git] / lib / mbfile.h
1 /* Multibyte character I/O: macros for multi-byte encodings.
2    Copyright (C) 2001, 2005, 2009-2012 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Written by Mitsuru Chinen <mchinen@yamato.ibm.com>
18    and Bruno Haible <bruno@clisp.org>.  */
19
20 /* The macros in this file implement multi-byte character input from a
21    stream.
22
23    mb_file_t
24      is the type for multibyte character input stream, usable for variable
25      declarations.
26
27    mbf_char_t
28      is the type for multibyte character or EOF, usable for variable
29      declarations.
30
31    mbf_init (mbf, stream)
32      initializes the MB_FILE for reading from stream.
33
34    mbf_getc (mbc, mbf)
35      reads the next multibyte character from mbf and stores it in mbc.
36
37    mb_iseof (mbc)
38      returns true if mbc represents the EOF value.
39
40    Here are the function prototypes of the macros.
41
42    extern void          mbf_init (mb_file_t mbf, FILE *stream);
43    extern void          mbf_getc (mbf_char_t mbc, mb_file_t mbf);
44    extern bool          mb_iseof (const mbf_char_t mbc);
45  */
46
47 #ifndef _MBFILE_H
48 #define _MBFILE_H 1
49
50 #include <assert.h>
51 #include <stdbool.h>
52 #include <stdio.h>
53 #include <string.h>
54
55 /* Tru64 with Desktop Toolkit C has a bug: <stdio.h> must be included before
56    <wchar.h>.
57    BSD/OS 4.1 has a bug: <stdio.h> and <time.h> must be included before
58    <wchar.h>.  */
59 #include <stdio.h>
60 #include <time.h>
61 #include <wchar.h>
62
63 #include "mbchar.h"
64
65 _GL_INLINE_HEADER_BEGIN
66 #ifndef MBFILE_INLINE
67 # define MBFILE_INLINE _GL_INLINE
68 #endif
69
70 struct mbfile_multi {
71   FILE *fp;
72   bool eof_seen;
73   bool have_pushback;
74   mbstate_t state;
75   unsigned int bufcount;
76   char buf[MBCHAR_BUF_SIZE];
77   struct mbchar pushback;
78 };
79
80 MBFILE_INLINE void
81 mbfile_multi_getc (struct mbchar *mbc, struct mbfile_multi *mbf)
82 {
83   size_t bytes;
84
85   /* If EOF has already been seen, don't use getc.  This matters if
86      mbf->fp is connected to an interactive tty.  */
87   if (mbf->eof_seen)
88     goto eof;
89
90   /* Return character pushed back, if there is one.  */
91   if (mbf->have_pushback)
92     {
93       mb_copy (mbc, &mbf->pushback);
94       mbf->have_pushback = false;
95       return;
96     }
97
98   /* Before using mbrtowc, we need at least one byte.  */
99   if (mbf->bufcount == 0)
100     {
101       int c = getc (mbf->fp);
102       if (c == EOF)
103         {
104           mbf->eof_seen = true;
105           goto eof;
106         }
107       mbf->buf[0] = (unsigned char) c;
108       mbf->bufcount++;
109     }
110
111   /* Handle most ASCII characters quickly, without calling mbrtowc().  */
112   if (mbf->bufcount == 1 && mbsinit (&mbf->state) && is_basic (mbf->buf[0]))
113     {
114       /* These characters are part of the basic character set.  ISO C 99
115          guarantees that their wide character code is identical to their
116          char code.  */
117       mbc->wc = mbc->buf[0] = mbf->buf[0];
118       mbc->wc_valid = true;
119       mbc->ptr = &mbc->buf[0];
120       mbc->bytes = 1;
121       mbf->bufcount = 0;
122       return;
123     }
124
125   /* Use mbrtowc on an increasing number of bytes.  Read only as many bytes
126      from mbf->fp as needed.  This is needed to give reasonable interactive
127      behaviour when mbf->fp is connected to an interactive tty.  */
128   for (;;)
129     {
130       /* We don't know whether the 'mbrtowc' function updates the state when
131          it returns -2, - this is the ISO C 99 and glibc-2.2 behaviour - or
132          not - amended ANSI C, glibc-2.1 and Solaris 2.7 behaviour.  We
133          don't have an autoconf test for this, yet.
134          The new behaviour would allow us to feed the bytes one by one into
135          mbrtowc.  But the old behaviour forces us to feed all bytes since
136          the end of the last character into mbrtowc.  Since we want to retry
137          with more bytes when mbrtowc returns -2, we must backup the state
138          before calling mbrtowc, because implementations with the new
139          behaviour will clobber it.  */
140       mbstate_t backup_state = mbf->state;
141
142       bytes = mbrtowc (&mbc->wc, &mbf->buf[0], mbf->bufcount, &mbf->state);
143
144       if (bytes == (size_t) -1)
145         {
146           /* An invalid multibyte sequence was encountered.  */
147           /* Return a single byte.  */
148           bytes = 1;
149           mbc->wc_valid = false;
150           break;
151         }
152       else if (bytes == (size_t) -2)
153         {
154           /* An incomplete multibyte character.  */
155           mbf->state = backup_state;
156           if (mbf->bufcount == MBCHAR_BUF_SIZE)
157             {
158               /* An overlong incomplete multibyte sequence was encountered.  */
159               /* Return a single byte.  */
160               bytes = 1;
161               mbc->wc_valid = false;
162               break;
163             }
164           else
165             {
166               /* Read one more byte and retry mbrtowc.  */
167               int c = getc (mbf->fp);
168               if (c == EOF)
169                 {
170                   /* An incomplete multibyte character at the end.  */
171                   mbf->eof_seen = true;
172                   bytes = mbf->bufcount;
173                   mbc->wc_valid = false;
174                   break;
175                 }
176               mbf->buf[mbf->bufcount] = (unsigned char) c;
177               mbf->bufcount++;
178             }
179         }
180       else
181         {
182           if (bytes == 0)
183             {
184               /* A null wide character was encountered.  */
185               bytes = 1;
186               assert (mbf->buf[0] == '\0');
187               assert (mbc->wc == 0);
188             }
189           mbc->wc_valid = true;
190           break;
191         }
192     }
193
194   /* Return the multibyte sequence mbf->buf[0..bytes-1].  */
195   mbc->ptr = &mbc->buf[0];
196   memcpy (&mbc->buf[0], &mbf->buf[0], bytes);
197   mbc->bytes = bytes;
198
199   mbf->bufcount -= bytes;
200   if (mbf->bufcount > 0)
201     {
202       /* It's not worth calling memmove() for so few bytes.  */
203       unsigned int count = mbf->bufcount;
204       char *p = &mbf->buf[0];
205
206       do
207         {
208           *p = *(p + bytes);
209           p++;
210         }
211       while (--count > 0);
212     }
213   return;
214
215 eof:
216   /* An mbchar_t with bytes == 0 is used to indicate EOF.  */
217   mbc->ptr = NULL;
218   mbc->bytes = 0;
219   mbc->wc_valid = false;
220   return;
221 }
222
223 MBFILE_INLINE void
224 mbfile_multi_ungetc (const struct mbchar *mbc, struct mbfile_multi *mbf)
225 {
226   mb_copy (&mbf->pushback, mbc);
227   mbf->have_pushback = true;
228 }
229
230 typedef struct mbfile_multi mb_file_t;
231
232 typedef mbchar_t mbf_char_t;
233
234 #define mbf_init(mbf, stream)                                           \
235   ((mbf).fp = (stream),                                                 \
236    (mbf).eof_seen = false,                                              \
237    (mbf).have_pushback = false,                                         \
238    memset (&(mbf).state, '\0', sizeof (mbstate_t)),                     \
239    (mbf).bufcount = 0)
240
241 #define mbf_getc(mbc, mbf) mbfile_multi_getc (&(mbc), &(mbf))
242
243 #define mbf_ungetc(mbc, mbf) mbfile_multi_ungetc (&(mbc), &(mbf))
244
245 #define mb_iseof(mbc) ((mbc).bytes == 0)
246
247 _GL_INLINE_HEADER_BEGIN
248
249 #endif /* _MBFILE_H */