#include #include #include #include #include #include #include #include "sexp.h" static int put_atom(const char *str) { int i; for (i = 0; str[i]; ++i) { assert(isalnum(str[i]) || str[i] == '-' || str[i] == '_'); putchar(str[i]); } return i; } static int put_string(const char *str) { int i; putchar('"'); for (i = 0; str[i]; ++i) { switch (str[i]) { case '"': putchar('\\'); putchar('"'); break; case '\\': putchar('\\'); putchar('\\'); break; default: if (isprint(str[i])) { putchar(str[i]); } else { printf("\\u%4x", str[i]); } break; } } putchar('"'); return i + 2; } int ha_sexp_printf(const char *s, ...) { va_list ap; va_start(ap, s); int l = 0; char c; while ((c = *s)) { ++s; switch (c) { case 'a': l += put_atom(va_arg(ap, const char *)); break; case 'd': l += printf("%d", va_arg(ap, int)); break; case 'f': l += printf("%f", va_arg(ap, double)); break; case 's': l += put_string(va_arg(ap, const char *)); break; case '(': putchar('('); ++l; continue; case ')': putchar(')'); ++l; break; case ' ': case '\n': case '\t': putchar(c); ++l; continue; default: assert(0); } if (*s && *s != ')') { putchar(' '); ++l; } } va_end(ap); return l; } static char * scan_atom(char *buf, size_t sz) { size_t bufsz = 1024; if (sz == 0) { assert(buf == NULL); buf = malloc(bufsz); if (!buf) { errno = ENOMEM; goto error; } } char c; while (isspace(c = getchar()) && c != -1); buf[0] = c; for (size_t i = 1; sz == 0 || i < sz; ++i) { if (sz == 0 && i >= bufsz) { char *newbuf = realloc(buf, bufsz * 2); if (!newbuf) { errno = ENOMEM; goto error; } bufsz *= 2; buf = newbuf; } c = getchar(); if (c == -1) { buf[i] = '\0'; return buf; } if (isspace(c) || c == ')') { ungetc(c, stdin); buf[i] = '\0'; return buf; } if (!isalnum(c) && c != '-' && c != '_') { errno = EINVAL; goto error; } buf[i] = c; } errno = ENOBUFS; error: if (sz == 0) { free(buf); } return NULL; } static char * scan_str(char *buf, size_t sz) { char c; while (isspace(c = getchar()) && c != -1); if (c != '"') { return NULL; } size_t bufsz = 1024; if (sz == 0) { assert(buf == NULL); buf = malloc(bufsz); if (!buf) { errno = ENOMEM; goto error; } } for (size_t i = 0; sz == 0 || i < sz; ++i) { if ((c = getchar()) == -1) { goto inval; } if (sz == 0 && i >= bufsz) { char *newbuf = realloc(buf, bufsz * 2); if (!newbuf) { errno = ENOMEM; goto error; } bufsz *= 2; buf = newbuf; } if (c == '"') { /* Success */ buf[i] = '\0'; return buf; } unsigned int n; switch (c) { case '\\': c = getchar(); if (c == -1) { goto inval; } switch (c) { case '"': case '\\': buf[i] = c; break; case 'u': if (scanf("%4x", &n) != 1) { goto inval; } buf[i] = (char)n; break; default: goto inval; } break; default: buf[i] = c; break; } } errno = ENOBUFS; goto error; inval: errno = EINVAL; error: if (sz == 0) { free(buf); } return NULL; } int ha_sexp_scanf(const char *s, ...) { va_list ap; va_start(ap, s); int n = 0, r; char c; char *str, **strptr; size_t sz; while ((c = *s)) { ++s; r = 0; switch (c) { case 'a': str = va_arg(ap, char *); sz = va_arg(ap, size_t); r = scan_atom(str, sz) != NULL; break; case 's': str = va_arg(ap, char *); sz = va_arg(ap, size_t); r = scan_str(str, sz) != NULL; break; case 'A': strptr = va_arg(ap, char **); *strptr = scan_atom(NULL, 0); r = *strptr != NULL; break; case 'S': strptr = va_arg(ap, char **); *strptr = scan_str(NULL, 0); r = *strptr != NULL; break; case 'd': r = scanf("%d", va_arg(ap, int *)); break; case 'f': r = scanf("%lf", va_arg(ap, double *)); break; case '(': case ')': if (getchar() != c) { va_end(ap); return n; } r = 1; break; case ' ': case '\n': case '\t': /* no-op */ continue; } if (r == 0) { /* error */ break; } n += r; } va_end(ap); return n; }