391 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			391 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;
 | |
|     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, 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 = 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);
 | |
| }
 |