diff options
Diffstat (limited to 'moon-abe/pbc-0.5.14/misc/extend_printf.c')
-rw-r--r-- | moon-abe/pbc-0.5.14/misc/extend_printf.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/moon-abe/pbc-0.5.14/misc/extend_printf.c b/moon-abe/pbc-0.5.14/misc/extend_printf.c new file mode 100644 index 00000000..5e6537e2 --- /dev/null +++ b/moon-abe/pbc-0.5.14/misc/extend_printf.c @@ -0,0 +1,188 @@ +/* + * Behaves as gmp_printf with new conversion specifier %B for element_t types + */ + +#include <stdio.h> +#include <stdint.h> // for intptr_t +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <gmp.h> +#include "pbc_utils.h" +#include "pbc_field.h" +#include "pbc_memory.h" + +struct sninfo_s { + char *s; + size_t size; + size_t left; + size_t result; +}; + +// TODO: remove repeated code for error handling +static int do_print(int (*strcb)(void *, char *s), + int (*fstrcb)(void *, char *s, void *), + int (*elcb)(void *, element_ptr e), + void *data, + const char *format, va_list ap) { + // A primitive front-end for printf()-family functions. Only handles types + // in specifiers, and assumes they all take void * arguments. + // + // I wish register_printf_specifier() were more widespread. + int count = 0, status; + char *copy, *c, *start, *next; + element_ptr e; + int found; + + copy = pbc_strdup(format); + start = next = copy; + + for(;;) { + for(;;) { + c = strchr(next, '%'); + if (!c) { + status = strcb(data, start); + if (status < 0) { + count = -1; + } else count += status; + goto done; + } + if (!*(c + 1)) goto done; + if (*(c + 1) != '%') break; + next = c + 2; + } + *c = 0; + status = strcb(data, start); + if (status < 0) { + count = -1; + goto done; + } else count += status; + *c = '%'; + start = c; + found = 0; + while(!found) { + c++; + switch (*c) { + case '\0': + goto done; + case 'B': + e = va_arg(ap, element_ptr); + status = elcb(data, e); + if (status < 0) { + count = -1; + goto done; + } else count += status; + found = 1; + break; + default: + if (strchr("diouxXeEfFgGaAcspnmZ", *c)) { + if (*c == 'Z') c++; + char ch = *(c+1); + *(c+1) = '\0'; + status = fstrcb(data, start, va_arg(ap, void *)); + if (status < 0) { + count = -1; + goto done; + } else count += status; + *(c+1) = ch; + found = 1; + } + break; + } + } + next = start = c + 1; + } + +done: + pbc_free(copy); + + return count; +} + +static int string_cb(void *file, char *s) { + if (fputs(s, file) == EOF) return -1; + return strlen(s); +} + +static int format_cb(void *file, char *fstring, void *ptr) { + return gmp_fprintf(file, fstring, ptr); +} + +static int element_cb(void *file, element_ptr e) { + return element_out_str(file, 0, e); +} + +int element_vfprintf(FILE *stream, const char *format, va_list ap) { + return do_print(string_cb, format_cb, element_cb, stream, format, ap); +} + +int element_fprintf(FILE *stream, const char *format, ...) { + int status; + va_list ap; + + va_start(ap, format); + status = element_vfprintf(stream, format, ap); + va_end(ap); + return status; +} + +int element_printf(const char *format, ...) { + int status; + va_list ap; + + va_start(ap, format); + status = element_vfprintf(stdout, format, ap); + va_end(ap); + return status; +} + +static void next(struct sninfo_s *p, int status) { + p->result += status; + p->left = p->result >= p->size ? 0 : p->size - p->result; +} + +static int string_cbv(void *data, char *s) { + struct sninfo_s *p = data; + int status = snprintf(p->s + p->result, p->left, "%s", s); + if (status < 0) return status; + next(data, status); + return status; +} + +static int format_cbv(void *data, char *fstring, void *ptr) { + struct sninfo_s *p = data; + int status = gmp_snprintf(p->s + p->result, p->left, fstring, ptr); + if (status < 0) return status; + next(data, status); + return status; +} + +static int element_cbv(void *data, element_ptr e) { + struct sninfo_s *p = data; + int status = element_snprint(p->s + p->result, p->left, e); + if (status < 0) return status; + next(data, status); + return status; +} + +int element_vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) { + struct sninfo_s info; + + info.s = buf; + info.left = info.size = size; + info.result = 0; + + do_print(string_cbv, format_cbv, element_cbv, &info, fmt, ap); + + return info.result; +} + +int element_snprintf(char *buf, size_t size, const char *fmt, ...) { + int status; + va_list ap; + + va_start(ap, fmt); + status = element_vsnprintf(buf, size, fmt, ap); + va_end(ap); + return status; +} |