summaryrefslogtreecommitdiff
path: root/libc/stdio/scanf.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/stdio/scanf.c')
-rw-r--r--libc/stdio/scanf.c83
1 files changed, 60 insertions, 23 deletions
diff --git a/libc/stdio/scanf.c b/libc/stdio/scanf.c
index c8b487ed1..fc70b9244 100644
--- a/libc/stdio/scanf.c
+++ b/libc/stdio/scanf.c
@@ -31,14 +31,24 @@
* implementation doesn't for the "100ergs" case mentioned above.
*/
-#include <stdlib.h>
-#include <unistd.h>
+#define _GNU_SOURCE
+#include <features.h>
+#if defined(__UCLIBC__) && !defined(__USE_ISOC99)
#define __USE_ISOC99
+#endif
+
+#define _STDIO_UTILITY
#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
+#ifdef __STDIO_THREADSAFE
+#include <pthread.h>
+#endif /* __STDIO_THREADSAFE */
+
#ifdef L_scanf
#ifdef __STDC__
int scanf(const char *fmt, ...)
@@ -59,29 +69,22 @@ va_dcl
#endif
#ifdef L_sscanf
-#ifdef __STDC__
-int sscanf(const char *sp, const char *fmt, ...)
+#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS)
+#warning skipping sscanf since no buffering and no custom streams!
#else
-int sscanf(sp, fmt, va_alist)
-__const char *sp;
-__const char *fmt;
-va_dcl
-#endif
-{
- FILE string[1] = {
- {0, (unsigned char *) ((unsigned) -1), 0, 0, (char *) ((unsigned) -1),
- 0, -1, _IOFBF}
- };
+int sscanf(const char *sp, const char *fmt, ...)
+{
va_list ptr;
int rv;
- string->bufpos = (unsigned char *) ((void *) sp);
va_start(ptr, fmt);
- rv = vfscanf(string, fmt, ptr);
+ rv = vsscanf(sp, fmt, ptr);
va_end(ptr);
return rv;
}
+
+#endif
#endif
#ifdef L_fscanf
@@ -114,16 +117,37 @@ va_list ap;
#endif
#ifdef L_vsscanf
+#ifdef __STDIO_BUFFERS
int vsscanf(__const char *sp, __const char *fmt, va_list ap)
{
- FILE string[1] = {
- {0, (unsigned char *) ((unsigned) -1), 0, 0, (char *) ((unsigned) -1),
- 0, -1, _IOFBF}
- };
+ FILE string[1];
+
+ string->filedes = -2; /* for debugging */
+ string->modeflags = (__FLAG_NARROW|__FLAG_READONLY);
+ string->bufstart = string->bufrpos = (unsigned char *) ((void *) sp);
+ string->bufgetc = (char *) ((unsigned) -1);
- string->bufpos = (unsigned char *) sp;
return vfscanf(string, fmt, ap);
}
+#else /* __STDIO_BUFFERS */
+#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
+int vsscanf(__const char *sp, __const char *fmt, va_list ap)
+{
+ FILE *f;
+ int rv;
+
+ if ((f = fmemopen((char *)sp, strlen(sp), "r")) == NULL) {
+ return -1;
+ }
+ rv = vfscanf(f, fmt, ap);
+ fclose(f);
+
+ return rv;
+}
+#else /* __STDIO_GLIBC_CUSTOM_STREAMS */
+#warning skipping vsscanf since no buffering and no custom streams!
+#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
+#endif /* __STDIO_BUFFERS */
#endif
#ifdef L_vfscanf
@@ -154,6 +178,7 @@ struct scan_cookie {
int width_flag;
int ungot_char;
int ungot_flag;
+ int app_ungot;
};
#ifdef __UCLIBC_HAS_LONG_LONG__
@@ -181,8 +206,11 @@ static void init_scan_cookie(struct scan_cookie *sc, FILE *fp)
sc->nread = 0;
sc->width_flag = 0;
sc->ungot_flag = 0;
+ sc->app_ungot = ((fp->modeflags & __MASK_UNGOT) ? fp->ungot[1] : 0);
}
+/* TODO -- what about literal '\0' chars in a file??? */
+
static int scan_getc_nw(struct scan_cookie *sc)
{
if (sc->ungot_flag == 0) {
@@ -233,6 +261,10 @@ static void kill_scan_cookie(struct scan_cookie *sc)
{
if (sc->ungot_flag) {
ungetc(sc->ungot_char,sc->fp);
+ /* Deal with distiction between user and scanf ungots. */
+ if (sc->nread == 0) { /* Only one char was read... app ungot? */
+ sc->fp->ungot[1] = sc->app_ungot; /* restore ungot state. */
+ }
}
}
@@ -267,6 +299,8 @@ va_list ap;
unsigned char buf[MAX_DIGITS+2];
unsigned char scanset[UCHAR_MAX + 1];
+ __STDIO_THREADLOCK(fp);
+
init_scan_cookie(&sc,fp);
fmt = (unsigned const char *) format;
@@ -547,7 +581,7 @@ va_list ap;
goto done;
}
/* Unrecognized specifier! */
- goto done;
+ goto RETURN_cnt;
} if (isspace(*fmt)) { /* Consume all whitespace. */
while (isspace(scan_getc_nw(&sc)))
{}
@@ -567,9 +601,12 @@ va_list ap;
kill_scan_cookie(&sc);
if ((sc.ungot_char <= 0) && (cnt == 0) && (*fmt)) {
- return (EOF);
+ cnt = EOF;
}
+ RETURN_cnt:
+ __STDIO_THREADUNLOCK(fp);
+
return (cnt);
}