Sun Jul 28 23:46:14 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
[platform/upstream/linaro-glibc.git] / wcsmbs / mbrtowc.c
1 /* Copyright (C) 1996 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1996.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB.  If
17 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.  */
19
20 #include <errno.h>
21 #include <wchar.h>
22
23 #ifndef EILSEQ
24 #define EILSEQ EINVAL
25 #endif
26
27
28 static mbstate_t internal;
29
30 size_t
31 mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
32 {
33   size_t used = 0;
34
35   if (ps == NULL)
36     ps = &internal;
37
38   if (s == NULL)
39     {
40       /* See first paragraph of description in 7.16.6.3.2.  */
41       pwc = NULL;
42       s = "";
43       n = 1;
44     }
45
46   if (n > 0)
47     {
48       if (ps->count == 0)
49         {
50           unsigned char byte = (unsigned char) *s++;
51           ++used;
52
53           /* We must look for a possible first byte of a UTF8 sequence.  */
54           if (byte < 0x80)
55             {
56               /* One byte sequence.  */
57               if (pwc != NULL)
58                 *pwc = (wchar_t) byte;
59               return byte ? used : 0;
60             }
61
62           if ((byte & 0xc0) == 0x80 || (byte & 0xfe) == 0xfe)
63             {
64               /* Oh, oh.  An encoding error.  */
65               errno = EILSEQ;
66               return (size_t) -1;
67             }
68
69           if ((byte & 0xe0) == 0xc0)
70             {
71               /* We expect two bytes.  */
72               ps->count = 1;
73               ps->value = byte & 0x1f;
74             }
75           else if ((byte & 0xf0) == 0xe0)
76             {
77               /* We expect three bytes.  */
78               ps->count = 2;
79               ps->value = byte & 0x0f;
80             }
81           else if ((byte & 0xf8) == 0xf0)
82             {
83               /* We expect four bytes.  */
84               ps->count = 3;
85               ps->value = byte & 0x07;
86             }
87           else if ((byte & 0xfc) == 0xf8)
88             {
89               /* We expect five bytes.  */
90               ps->count = 4;
91               ps->value = byte & 0x03;
92             }
93           else
94             {
95               /* We expect six bytes.  */
96               ps->count = 5;
97               ps->value = byte & 0x01;
98             }
99         }
100
101       /* We know we have to handle a multibyte character and there are
102          some more bytes to read.  */
103       while (used < n)
104         {
105           /* The second to sixths byte must be of the form 10xxxxxx.  */
106           unsigned char byte = (unsigned char) *s++;
107           ++used;
108
109           if ((byte & 0xc0) != 0x80)
110             {
111               /* Oh, oh.  An encoding error.  */
112               errno = EILSEQ;
113               return (size_t) -1;
114             }
115
116           ps->value <<= 6;
117           ps->value |= byte & 0x3f;
118
119           if (--ps->count == 0)
120             {
121               /* The character is finished.  */
122               if (pwc != NULL)
123                 *pwc = (wchar_t) ps->value;
124               return ps->value ? used : 0;
125             }
126         }
127     }
128
129   return (size_t) -2;
130 }