mirror of
https://github.com/autc04/Retro68.git
synced 2024-09-07 13:54:33 +00:00
397 lines
11 KiB
C
397 lines
11 KiB
C
|
/*
|
||
|
* 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 = 0;
|
||
|
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 = (char)va_arg(args, int);
|
||
|
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;
|
||
|
int max_size = 0;
|
||
|
int res;
|
||
|
# ifdef __va_copy
|
||
|
__va_copy(vsprintf_args, args);
|
||
|
# else
|
||
|
# if defined(__GNUC__) && !defined(__DJGPP__) /* and probably in other cases */
|
||
|
va_copy(vsprintf_args, args);
|
||
|
# else
|
||
|
vsprintf_args = args;
|
||
|
# endif
|
||
|
# endif
|
||
|
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);
|
||
|
}
|