source: code/trunk/vendor/modernc.org/libc/scanf.go@ 822

Last change on this file since 822 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 11.4 KB
Line 
1// Copyright 2020 The Libc Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package libc // import "modernc.org/libc"
6
7import (
8 "strings"
9 "unsafe"
10)
11
12// The format string consists of a sequence of directives which describe how to
13// process the sequence of input characters. If processing of a directive
14// fails, no further input is read, and scanf() returns. A "failure" can
15// be either of the following: input failure, meaning that input characters
16// were unavailable, or matching failure, meaning that the input was
17// inappropriate.
18func scanf(r *strings.Reader, format, args uintptr) (nvalues int32) {
19 // var src []byte //TODO-
20 var ok bool
21out:
22 for {
23 c := *(*byte)(unsafe.Pointer(format))
24 // src = append(src, c) //TODO-
25 switch c {
26 case '%':
27 var n int
28 var match bool
29 format, n, match = scanfConversion(r, format, &args)
30 if !match {
31 break out
32 }
33
34 nvalues += int32(n)
35 ok = true
36 case 0:
37 break out
38 case ' ', '\t', '\n', '\r', '\v', '\f':
39 format = skipWhiteSpace(format)
40 ok = true
41 next:
42 for {
43 c, err := r.ReadByte()
44 if err != nil {
45 break out
46 }
47
48 switch c {
49 case ' ', '\t', '\n', '\r', '\v', '\f':
50 // nop
51 default:
52 r.UnreadByte()
53 break next
54 }
55 }
56 default:
57 c2, err := r.ReadByte()
58 if err != nil {
59 break out
60 }
61
62 if c2 != c {
63 r.UnreadByte()
64 break out
65 }
66
67 format++
68 ok = true
69 }
70 }
71 if ok {
72 return nvalues
73 }
74
75 return -1 // stdio.EOF but not defined for windows
76}
77
78func scanfConversion(r *strings.Reader, format uintptr, args *uintptr) (_ uintptr, nvalues int, match bool) {
79 format++ // '%'
80
81 // Each conversion specification in format begins with either the character '%'
82 // or the character sequence "%n$" (see below for the distinction) followed by:
83
84 mod := 0
85 width := -1
86flags:
87 for {
88 switch c := *(*byte)(unsafe.Pointer(format)); c {
89 case '*':
90 // An optional '*' assignment-suppression character: scanf() reads input as
91 // directed by the conversion specification, but discards the input. No
92 // corresponding pointer argument is re‐ quired, and this specification is not
93 // included in the count of successful assignments returned by scanf().
94 format++
95 panic(todo(""))
96 case '\'':
97 // For decimal conversions, an optional quote character ('). This specifies
98 // that the input number may include thousands' separators as defined by the
99 // LC_NUMERIC category of the current locale. (See setlocale(3).) The quote
100 // character may precede or follow the '*' assignment-suppression character.
101 format++
102 panic(todo(""))
103 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
104 // An optional decimal integer which specifies the maximum field width.
105 // Reading of characters stops either when this maximum is reached or when a
106 // nonmatching character is found, whichever happens first. Most conversions
107 // discard initial white space characters (the exceptions are noted below), and
108 // these discarded characters don't count toward the maximum field width.
109 // String input conversions store a terminating null byte ('\0') to mark the
110 // end of the input; the maximum field width does not include this terminator.
111 width = 0
112 num:
113 for {
114 var digit int
115 switch c := *(*byte)(unsafe.Pointer(format)); {
116 default:
117 break num
118 case c >= '0' && c <= '9':
119 format++
120 digit = int(c) - '0'
121 }
122 width0 := width
123 width = 10*width + digit
124 if width < width0 {
125 panic(todo(""))
126 }
127 }
128 case 'h', 'j', 'l', 'L', 'q', 't', 'z':
129 format, mod = parseLengthModifier(format)
130 default:
131 break flags
132 }
133 }
134
135 // A conversion specifier that specifies the type of input conversion to be
136 // performed.
137 switch c := *(*byte)(unsafe.Pointer(format)); c {
138 case '%':
139 // Matches a literal '%'. That is, %% in the format string matches a single
140 // input '%' character. No conversion is done (but initial white space
141 // characters are discarded), and assign‐ ment does not occur.
142 format++
143 panic(todo(""))
144 case 'd':
145 // Matches an optionally signed decimal integer; the next pointer must be a
146 // pointer to int.
147 format++
148 skipReaderWhiteSpace(r)
149 var digit, n uint64
150 allowSign := true
151 neg := false
152 dec:
153 for ; width != 0; width-- {
154 c, err := r.ReadByte()
155 if err != nil {
156 if match {
157 break dec
158 }
159
160 panic(todo("", err))
161 }
162
163 if allowSign {
164 switch c {
165 case '-':
166 allowSign = false
167 neg = true
168 continue
169 case '+':
170 allowSign = false
171 continue
172 }
173 }
174
175 switch {
176 case c >= '0' && c <= '9':
177 digit = uint64(c) - '0'
178 default:
179 r.UnreadByte()
180 break dec
181 }
182 match = true
183 n0 := n
184 n = n*10 + digit
185 if n < n0 {
186 panic(todo(""))
187 }
188 }
189 if !match {
190 break
191 }
192
193 arg := VaUintptr(args)
194 v := int64(n)
195 if neg {
196 v = -v
197 }
198 switch mod {
199 case modNone:
200 *(*int32)(unsafe.Pointer(arg)) = int32(v)
201 case modH:
202 *(*int16)(unsafe.Pointer(arg)) = int16(v)
203 case modHH:
204 *(*int8)(unsafe.Pointer(arg)) = int8(v)
205 case modL:
206 *(*long)(unsafe.Pointer(arg)) = long(n)
207 default:
208 panic(todo(""))
209 }
210 nvalues = 1
211 case 'D':
212 // Equivalent to ld; this exists only for backward compatibility. (Note:
213 // thus only in libc4. In libc5 and glibc the %D is silently ignored, causing
214 // old programs to fail mysteriously.)
215 format++
216 panic(todo(""))
217 case 'i':
218 // Matches an optionally signed integer; the next pointer must be a pointer to
219 // int. The integer is read in base 16 if it begins with 0x or 0X, in base 8
220 // if it begins with 0, and in base 10 otherwise. Only characters that
221 // correspond to the base are used.
222 format++
223 panic(todo(""))
224 case 'o':
225 // Matches an unsigned octal integer; the next pointer must be a pointer to
226 // unsigned int.
227 format++
228 panic(todo(""))
229 case 'u':
230 // Matches an unsigned decimal integer; the next pointer must be a pointer to
231 // unsigned int.
232 format++
233 panic(todo(""))
234 case 'x', 'X':
235 // Matches an unsigned hexadecimal integer; the next pointer must be a pointer
236 // to unsigned int.
237 format++
238 skipReaderWhiteSpace(r)
239 var digit, n uint64
240 allowPrefix := true
241 var b []byte
242 hex:
243 for ; width != 0; width-- {
244 c, err := r.ReadByte()
245 if err != nil {
246 if match {
247 break hex
248 }
249
250 panic(todo("", err))
251 }
252
253 if allowPrefix {
254 if len(b) == 1 && b[0] == '0' && (c == 'x' || c == 'X') {
255 allowPrefix = false
256 match = false
257 b = nil
258 continue
259 }
260
261 b = append(b, c)
262 }
263
264 switch {
265 case c >= '0' && c <= '9':
266 digit = uint64(c) - '0'
267 case c >= 'a' && c <= 'f':
268 digit = uint64(c) - 'a' + 10
269 case c >= 'A' && c <= 'F':
270 digit = uint64(c) - 'A' + 10
271 default:
272 r.UnreadByte()
273 break hex
274 }
275 match = true
276 n0 := n
277 n = n<<4 + digit
278 if n < n0 {
279 panic(todo(""))
280 }
281 }
282 if !match {
283 break
284 }
285
286 arg := VaUintptr(args)
287 switch mod {
288 case modNone:
289 *(*uint32)(unsafe.Pointer(arg)) = uint32(n)
290 case modH:
291 *(*uint16)(unsafe.Pointer(arg)) = uint16(n)
292 case modHH:
293 *(*byte)(unsafe.Pointer(arg)) = byte(n)
294 case modL:
295 *(*ulong)(unsafe.Pointer(arg)) = ulong(n)
296 default:
297 panic(todo(""))
298 }
299 nvalues = 1
300 case 'f', 'e', 'g', 'E', 'a':
301 // Matches an optionally signed floating-point number; the next pointer must be
302 // a pointer to float.
303 format++
304 panic(todo(""))
305 case 's':
306 // Matches a sequence of non-white-space characters; the next pointer must be
307 // a pointer to the initial element of a character array that is long enough to
308 // hold the input sequence and the terminating null byte ('\0'), which is added
309 // automatically. The input string stops at white space or at the maximum
310 // field width, whichever occurs first.
311 format++
312 panic(todo(""))
313 case 'c':
314 // Matches a sequence of characters whose length is specified by the maximum
315 // field width (default 1); the next pointer must be a pointer to char, and
316 // there must be enough room for all the characters (no terminating null byte
317 // is added). The usual skip of leading white space is suppressed. To skip
318 // white space first, use an explicit space in the format.
319 format++
320 panic(todo(""))
321 case '[':
322 // Matches a nonempty sequence of characters from the specified set of
323 // accepted characters; the next pointer must be a pointer to char, and there
324 // must be enough room for all the char‐ acters in the string, plus a
325 // terminating null byte. The usual skip of leading white space is suppressed.
326 // The string is to be made up of characters in (or not in) a particular set;
327 // the set is defined by the characters between the open bracket [ character
328 // and a close bracket ] character. The set excludes those characters if the
329 // first character after the open bracket is a circumflex (^). To include a
330 // close bracket in the set, make it the first character after the open bracket
331 // or the circumflex; any other position will end the set. The hyphen
332 // character - is also special; when placed between two other characters, it
333 // adds all intervening characters to the set. To include a hyphen, make it
334 // the last character before the final close bracket. For instance, [^]0-9-]
335 // means the set "everything except close bracket, zero through nine, and
336 // hyphen". The string ends with the appearance of a character not in the
337 // (or, with a circumflex, in) set or when the field width runs out.
338 format++
339 panic(todo(""))
340 case 'p':
341 // Matches a pointer value (as printed by %p in printf(3); the next pointer
342 // must be a pointer to a pointer to void.
343 format++
344 skipReaderWhiteSpace(r)
345 c, err := r.ReadByte()
346 if err != nil {
347 panic(todo(""))
348 }
349
350 if c != '0' {
351 r.UnreadByte()
352 panic(todo(""))
353 }
354
355 if c, err = r.ReadByte(); err != nil {
356 panic(todo(""))
357 }
358
359 if c != 'x' && c != 'X' {
360 r.UnreadByte()
361 panic(todo(""))
362 }
363
364 var digit, n uint64
365 ptr:
366 for ; width != 0; width-- {
367 c, err := r.ReadByte()
368 if err != nil {
369 if match {
370 break ptr
371 }
372
373 panic(todo(""))
374 }
375
376 switch {
377 case c >= '0' && c <= '9':
378 digit = uint64(c) - '0'
379 case c >= 'a' && c <= 'f':
380 digit = uint64(c) - 'a' + 10
381 case c >= 'A' && c <= 'F':
382 digit = uint64(c) - 'A' + 10
383 default:
384 r.UnreadByte()
385 break ptr
386 }
387 match = true
388 n0 := n
389 n = n<<4 + digit
390 if n < n0 {
391 panic(todo(""))
392 }
393 }
394 if !match {
395 break
396 }
397
398 arg := VaUintptr(args)
399 *(*uintptr)(unsafe.Pointer(arg)) = uintptr(n)
400 nvalues = 1
401 case 'n':
402 // Nothing is expected; instead, the number of characters consumed thus far
403 // from the input is stored through the next pointer, which must be a pointer
404 // to int. This is not a conversion and does not increase the count returned
405 // by the function. The assignment can be suppressed with the *
406 // assignment-suppression character, but the effect on the return value is
407 // undefined. Therefore %*n conversions should not be used.
408 format++
409 panic(todo(""))
410 default:
411 panic(todo("%#U", c))
412 }
413
414 return format, nvalues, match
415}
416
417func skipReaderWhiteSpace(r *strings.Reader) error {
418 for {
419 c, err := r.ReadByte()
420 if err != nil {
421 return err
422 }
423
424 switch c {
425 case ' ', '\t', '\n', '\r', '\v', '\f':
426 // ok
427 default:
428 r.UnreadByte()
429 return nil
430 }
431 }
432}
433
434func skipWhiteSpace(s uintptr) uintptr {
435 for {
436 switch c := *(*byte)(unsafe.Pointer(s)); c {
437 case ' ', '\t', '\n', '\r', '\v', '\f':
438 s++
439 default:
440 return s
441 }
442 }
443}
Note: See TracBrowser for help on using the repository browser.