diff options
Diffstat (limited to 'gc/cord/cordprnt.c')
-rw-r--r-- | gc/cord/cordprnt.c | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/gc/cord/cordprnt.c b/gc/cord/cordprnt.c new file mode 100644 index 0000000..9c8cc87 --- /dev/null +++ b/gc/cord/cordprnt.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* An sprintf implementation that understands cords. This is probably */ +/* not terribly portable. It assumes an ANSI stdarg.h. It further */ +/* assumes that I can make copies of va_list variables, and read */ +/* arguments repeatedly by applyting va_arg to the copies. This */ +/* could be avoided at some performance cost. */ +/* We also assume that unsigned and signed integers of various kinds */ +/* have the same sizes, and can be cast back and forth. */ +/* We assume that void * and char * have the same size. */ +/* All this cruft is needed because we want to rely on the underlying */ +/* sprintf implementation whenever possible. */ +/* Boehm, September 21, 1995 6:00 pm PDT */ + +#include "cord.h" +#include "ec.h" +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "gc.h" + +#define CONV_SPEC_LEN 50 /* Maximum length of a single */ + /* conversion specification. */ +#define CONV_RESULT_LEN 50 /* Maximum length of any */ + /* conversion with default */ + /* width and prec. */ + + +static int ec_len(CORD_ec x) +{ + return(CORD_len(x[0].ec_cord) + (x[0].ec_bufptr - x[0].ec_buf)); +} + +/* Possible nonumeric precision values. */ +# define NONE -1 +# define VARIABLE -2 +/* Copy the conversion specification from CORD_pos into the buffer buf */ +/* Return negative on error. */ +/* Source initially points one past the leading %. */ +/* It is left pointing at the conversion type. */ +/* Assign field width and precision to *width and *prec. */ +/* If width or prec is *, VARIABLE is assigned. */ +/* Set *left to 1 if left adjustment flag is present. */ +/* Set *long_arg to 1 if long flag ('l' or 'L') is present, or to */ +/* -1 if 'h' is present. */ +static int extract_conv_spec(CORD_pos source, char *buf, + int * width, int *prec, int *left, int * long_arg) +{ + register int result = 0; + register int current_number = 0; + register int saw_period = 0; + register int saw_number; + register int chars_so_far = 0; + register char current; + + *width = NONE; + buf[chars_so_far++] = '%'; + while(CORD_pos_valid(source)) { + if (chars_so_far >= CONV_SPEC_LEN) return(-1); + current = CORD_pos_fetch(source); + buf[chars_so_far++] = current; + switch(current) { + case '*': + saw_number = 1; + current_number = VARIABLE; + break; + case '0': + if (!saw_number) { + /* Zero fill flag; ignore */ + break; + } /* otherwise fall through: */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + saw_number = 1; + current_number *= 10; + current_number += current - '0'; + break; + case '.': + saw_period = 1; + if(saw_number) { + *width = current_number; + saw_number = 0; + } + current_number = 0; + break; + case 'l': + case 'L': + *long_arg = 1; + current_number = 0; + break; + case 'h': + *long_arg = -1; + current_number = 0; + break; + case ' ': + case '+': + case '#': + current_number = 0; + break; + case '-': + *left = 1; + current_number = 0; + break; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + case 'c': + case 'C': + case 's': + case 'S': + case 'p': + case 'n': + case 'r': + goto done; + default: + return(-1); + } + CORD_next(source); + } + return(-1); + done: + if (saw_number) { + if (saw_period) { + *prec = current_number; + } else { + *prec = NONE; + *width = current_number; + } + } else { + *prec = NONE; + } + buf[chars_so_far] = '\0'; + return(result); +} + +int CORD_vsprintf(CORD * out, CORD format, va_list args) +{ + CORD_ec result; + register int count; + register char current; + CORD_pos pos; + char conv_spec[CONV_SPEC_LEN + 1]; + + CORD_ec_init(result); + for (CORD_set_pos(pos, format, 0); CORD_pos_valid(pos); CORD_next(pos)) { + current = CORD_pos_fetch(pos); + if (current == '%') { + CORD_next(pos); + if (!CORD_pos_valid(pos)) return(-1); + current = CORD_pos_fetch(pos); + if (current == '%') { + CORD_ec_append(result, current); + } else { + int width, prec; + int left_adj = 0; + int long_arg = 0; + CORD arg; + size_t len; + + if (extract_conv_spec(pos, conv_spec, + &width, &prec, + &left_adj, &long_arg) < 0) { + return(-1); + } + current = CORD_pos_fetch(pos); + switch(current) { + case 'n': + /* Assign length to next arg */ + if (long_arg == 0) { + int * pos_ptr; + pos_ptr = va_arg(args, int *); + *pos_ptr = ec_len(result); + } else if (long_arg > 0) { + long * pos_ptr; + pos_ptr = va_arg(args, long *); + *pos_ptr = ec_len(result); + } else { + short * pos_ptr; + pos_ptr = va_arg(args, short *); + *pos_ptr = ec_len(result); + } + goto done; + case 'r': + /* Append cord and any padding */ + if (width == VARIABLE) width = va_arg(args, int); + if (prec == VARIABLE) prec = va_arg(args, int); + arg = va_arg(args, CORD); + len = CORD_len(arg); + if (prec != NONE && len > prec) { + if (prec < 0) return(-1); + arg = CORD_substr(arg, 0, prec); + len = prec; + } + if (width != NONE && len < width) { + char * blanks = GC_MALLOC_ATOMIC(width-len+1); + + memset(blanks, ' ', width-len); + blanks[width-len] = '\0'; + if (left_adj) { + arg = CORD_cat(arg, blanks); + } else { + arg = CORD_cat(blanks, arg); + } + } + CORD_ec_append_cord(result, arg); + goto done; + case 'c': + if (width == NONE && prec == NONE) { + register char c; + + c = va_arg(args, char); + CORD_ec_append(result, c); + goto done; + } + break; + case 's': + if (width == NONE && prec == NONE) { + char * str = va_arg(args, char *); + register char c; + + while (c = *str++) { + CORD_ec_append(result, c); + } + goto done; + } + break; + default: + break; + } + /* Use standard sprintf to perform conversion */ + { + register char * buf; + va_list vsprintf_args = args; + /* The above does not appear to be sanctioned */ + /* by the ANSI C standard. */ + int max_size = 0; + int res; + + if (width == VARIABLE) width = va_arg(args, int); + if (prec == VARIABLE) prec = va_arg(args, int); + if (width != NONE) max_size = width; + if (prec != NONE && prec > max_size) max_size = prec; + max_size += CONV_RESULT_LEN; + if (max_size >= CORD_BUFSZ) { + buf = GC_MALLOC_ATOMIC(max_size + 1); + } else { + if (CORD_BUFSZ - (result[0].ec_bufptr-result[0].ec_buf) + < max_size) { + CORD_ec_flush_buf(result); + } + buf = result[0].ec_bufptr; + } + switch(current) { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'c': + if (long_arg <= 0) { + (void) va_arg(args, int); + } else if (long_arg > 0) { + (void) va_arg(args, long); + } + break; + case 's': + case 'p': + (void) va_arg(args, char *); + break; + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + (void) va_arg(args, double); + break; + default: + return(-1); + } + res = vsprintf(buf, conv_spec, vsprintf_args); + len = (size_t)res; + if ((char *)(GC_word)res == buf) { + /* old style vsprintf */ + len = strlen(buf); + } else if (res < 0) { + return(-1); + } + if (buf != result[0].ec_bufptr) { + register char c; + + while (c = *buf++) { + CORD_ec_append(result, c); + } + } else { + result[0].ec_bufptr = buf + len; + } + } + done:; + } + } else { + CORD_ec_append(result, current); + } + } + count = ec_len(result); + *out = CORD_balance(CORD_ec_to_cord(result)); + return(count); +} + +int CORD_sprintf(CORD * out, CORD format, ...) +{ + va_list args; + int result; + + va_start(args, format); + result = CORD_vsprintf(out, format, args); + va_end(args); + return(result); +} + +int CORD_fprintf(FILE * f, CORD format, ...) +{ + va_list args; + int result; + CORD out; + + va_start(args, format); + result = CORD_vsprintf(&out, format, args); + va_end(args); + if (result > 0) CORD_put(out, f); + return(result); +} + +int CORD_vfprintf(FILE * f, CORD format, va_list args) +{ + int result; + CORD out; + + result = CORD_vsprintf(&out, format, args); + if (result > 0) CORD_put(out, f); + return(result); +} + +int CORD_printf(CORD format, ...) +{ + va_list args; + int result; + CORD out; + + va_start(args, format); + result = CORD_vsprintf(&out, format, args); + va_end(args); + if (result > 0) CORD_put(out, stdout); + return(result); +} + +int CORD_vprintf(CORD format, va_list args) +{ + int result; + CORD out; + + result = CORD_vsprintf(&out, format, args); + if (result > 0) CORD_put(out, stdout); + return(result); +} |