/* * sprintf.c v1.05 for LPMud 3.0.52 * * An implementation of (s)printf() for LPC, with quite a few * extensions (note that as no floating point exists, some parameters * have slightly different meaning or restrictions to "standard" * (s)printf.) Implemented by Lynscar (Sean A Reith). * 2/28/93: float support for MudOS added by jacques/blackthorn * * This version supports the following as modifiers: * " " pad positive integers with a space. * "+" pad positive integers with a plus sign. * "-" left adjusted within field size. * NB: std (s)printf() defaults to right justification, which is * unnatural in the context of a mainly string based language * but has been retained for "compatability" ;) * "|" centered within field size. * "=" column mode if strings are greater than field size. this is only * meaningful with strings, all other types ignore * this. columns are auto-magically word wrapped. * "#" table mode, print a list of '\n' separated 'words' in a * table within the field size. only meaningful with strings. * n specifies the field size, a '*' specifies to use the corresponding * arg as the field size. if n is prepended with a zero, then is padded * zeros, else it is padded with spaces (or specified pad string). * "."n presision of n, simple strings truncate after this (if presision is * greater than field size, then field size = presision), tables use * presision to specify the number of columns (if presision not specified * then tables calculate a best fit), all other types ignore this. * ":"n n specifies the fs _and_ the presision, if n is prepended by a zero * then it is padded with zeros instead of spaces. * "@" the argument is an array. the corresponding format_info (minus the * "@") is applyed to each element of the array. * "'X'" The char(s) between the single-quotes are used to pad to field * size (defaults to space) (if both a zero (in front of field * size) and a pad string are specified, the one specified second * overrules). NOTE: to include "'" in the pad string, you must * use "\\'" (as the backslash has to be escaped past the * interpreter), similarly, to include "\" requires "\\\\". * The following are the possible type specifiers. * "%" in which case no arguments are interpreted, and a "%" is inserted, and * all modifiers are ignored. * "O" the argument is an LPC datatype. * "s" the argument is a string. * "d" the integer arg is printed in decimal. * "i" as d. * "f" floating point value. * "c" the integer arg is to be printed as a character. * "o" the integer arg is printed in octal. * "x" the integer arg is printed in hex. * "X" the integer arg is printed in hex (in capitals). */ #include <stdio.h> #include <string.h> #include <setjmp.h> #include <sys/types.h> #include "config.h" #include "lint.h" #include "opcodes.h" #include "stdio.h" #include "interpret.h" #include "mapping.h" #include "buffer.h" #include "object.h" #include "sent.h" #include "ignore.h" #include "exec.h" /* * If this #define is defined then error messages are returned, * otherwise error() is called (ie: A "wrongness in the fabric...") */ #define RETURN_ERROR_MESSAGES #if defined(F_SPRINTF) || defined(F_PRINTF) extern char *xalloc(), *string_copy(); extern INLINE void free_svalue PROT((struct svalue *)); extern struct object *current_object; typedef unsigned int format_info; /* * Format of format_info: * 00000000 0000xxxx : argument type: * 0000 : type not found yet; * 0001 : error type not found; * 0010 : percent sign, null argument; * 0011 : LPC datatype; * 0100 : string; * 1000 : integer; * 1001 : char; * 1010 : octal; * 1011 : hex; * 1100 : HEX; * 1101 : float; * 00000000 00xx0000 : justification: * 00 : right; * 01 : centre; * 10 : left; * 00000000 xx000000 : positive pad char: * 00 : none; * 01 : ' '; * 10 : '+'; * 0000000x 00000000 : array mode? * 000000x0 00000000 : column mode? * 00000x00 00000000 : table mode? */ #define INFO_T 0xF #define INFO_T_ERROR 0x1 #define INFO_T_NULL 0x2 #define INFO_T_LPC 0x3 #define INFO_T_STRING 0x4 #define INFO_T_INT 0x8 #define INFO_T_CHAR 0x9 #define INFO_T_OCT 0xA #define INFO_T_HEX 0xB #define INFO_T_C_HEX 0xC #define INFO_T_FLOAT 0xD #define INFO_J 0x30 #define INFO_J_CENTRE 0x10 #define INFO_J_LEFT 0x20 #define INFO_PP 0xC0 #define INFO_PP_SPACE 0x40 #define INFO_PP_PLUS 0x80 #define INFO_ARRAY 0x100 #define INFO_COLS 0x200 #define INFO_TABLE 0x400 #define BUFF_SIZE LARGEST_PRINTABLE_STRING #define ERROR(x) LONGJMP(error_jmp, x) #define ERR_BUFF_OVERFLOW 0x1 /* buffer overflowed */ #define ERR_TO_FEW_ARGS 0x2 /* more arguments spec'ed than passed */ #define ERR_INVALID_STAR 0x3 /* invalid arg to * */ #define ERR_PRES_EXPECTED 0x4 /* expected presision not found */ #define ERR_INVALID_FORMAT_STR 0x5 /* error in format string */ #define ERR_INCORRECT_ARG_S 0x6 /* invalid arg to %s */ #define ERR_CST_REQUIRES_FS 0x7 /* field size not given for c/t */ #define ERR_BAD_INT_TYPE 0x8 /* bad integer type... */ #define ERR_UNDEFINED_TYPE 0x9 /* undefined type found */ #define ERR_QUOTE_EXPECTED 0xA /* expected ' not found */ #define ERR_UNEXPECTED_EOS 0xB /* fs terminated unexpectedly */ #define ERR_NULL_PS 0xC /* pad string is null */ #define ERR_ARRAY_EXPECTED 0xD /* Yep! You guessed it. */ #define ADD_CHAR(x) {\ buff[bpos] = x;\ if (startignore) \ if (buff[bpos++] == '^') \ inignore = !inignore; \ else \ curpos += 2*!inignore; \ else \ if (buff[bpos++] == '%') \ startignore = 1; \ else \ curpos += !inignore; \ if (bpos>BUFF_SIZE) ERROR(ERR_BUFF_OVERFLOW); \ } #define GET_NEXT_ARG {\ if (++arg >= argc) ERROR(ERR_TO_FEW_ARGS); \ carg = (argv+arg);\ } #define SAVE_CHAR(pointer) {\ savechars *new;\ new = (savechars *)DXALLOC(sizeof(savechars), 107, "SAVE_CHAR");\ new->what = *(pointer);\ new->where = pointer;\ new->next = saves;\ saves = new;\ } /* * list of characters to restore before exiting. */ typedef struct SaveChars { char what; char *where; struct SaveChars *next; } savechars; typedef struct ColumnSlashTable { union CSTData { char *col; /* column data */ char **tab; /* table data */ } d; /* d == data */ unsigned short int nocols; /* number of columns in table *sigh* */ char *pad; unsigned int start; /* starting cursor position */ unsigned int size; /* column/table width */ int pres; /* presision */ format_info info; /* formatting data */ struct ColumnSlashTable *next; } cst; /* Columns Slash Tables */ static char buff[BUFF_SIZE]; /* buffer for returned string */ unsigned int bpos; /* position in buff */ unsigned int curpos; /* cursor position */ unsigned int inignore; /* are we not counting these characters */ unsigned int startignore; /* we found the first char... now for next */ jmp_buf error_jmp; /* LONGJMP() buffer for error catching */ /* * Probably should make this a #define... */ void stradd(dst, size, add) char **dst, *add; int *size; { int i; if ((i = (strlen(*dst) + strlen(add))) >= *size) { *size += i + 1; *dst = (char *)DREALLOC(*dst, *size, 108, "stradd"); } strcat(*dst, add); } /* end of stradd() */ void numadd(dst, size, num) char **dst; int *size, num; { int i, num_l, /* length of num as a string */ nve; /* true if num negative */ if (num < 0) { num *= -1; nve=1; } else nve=0; for (i=num/10, num_l=nve+1; i; i /= 10, num_l++) ; i = strlen(*dst); /* i = length of constructed string so far */ if ((i + num_l) >= *size) { *size += i + num_l + 2; *dst = (char *)DREALLOC(*dst, *size, 109, "stradd"); } (*dst)[i+num_l] = '\0'; if (nve) (*dst)[i] = '-'; else i--; for (num_l-=nve; num_l; num_l--, num /= 10) (*dst)[i+num_l] = (num%10) + '0'; } /* end of numadd() */ void floatadd(dst, size, flt) char **dst; int *size; double flt; { int i; int flt_l; /* length of float as a string */ char buf[80]; sprintf(buf,"%g",flt); flt_l = strlen(buf) + 1; i = strlen(*dst); /* i = length of constructed string so far */ if ((i + flt_l) >= *size) { *size += i + flt_l + 2; *dst = (char *)DREALLOC(*dst, *size, 109, "stradd"); } sprintf(*dst, "%s", buf); } /* end of floatadd() */ /* * This is a function purely because stradd() is, to keep same param * passing... */ void add_indent(dst, size, indent) char **dst; int *size, indent; { int i; i = strlen(*dst); if ((i + indent) >= *size) { *size += i + indent + 1; *dst = (char *)DREALLOC(*dst, *size, 110, "add_indent"); } for (;indent;indent--) (*dst)[i++] = ' '; (*dst)[i] = '\0'; } /* * Converts any LPC datatype into an arbitrary string format * and returns a pointer to this string. * Scary number of parameters for a recursive function. */ void svalue_to_string(obj, str, size, indent, trailing, indent2) struct svalue *obj; char **str; int size, indent, trailing, indent2; { int i; /* prevent an infinite recursion on self-referential structures */ if (indent > 200) { error("structure too deep to print.\n"); return; } if (!indent2) add_indent(str, &size, indent); switch (obj->type) { case T_INVALID: stradd(str,&size,"T_INVALID"); break; case T_LVALUE: stradd(str, &size, "lvalue: "); svalue_to_string(obj->u.lvalue, str, size, indent+2, trailing, 0); break; case T_NUMBER: numadd(str, &size, obj->u.number); break; case T_REAL: floatadd(str, &size, obj->u.real); break; case T_STRING: stradd(str, &size, "\""); stradd(str, &size, obj->u.string); stradd(str, &size, "\""); break; case T_POINTER: if (!(obj->u.vec->size)) { stradd(str, &size, "({ })"); } else { stradd(str, &size, "({ /* sizeof() == "); numadd(str, &size, obj->u.vec->size); stradd(str, &size, " */\n"); for (i=0; i<(obj->u.vec->size)-1; i++) svalue_to_string(&(obj->u.vec->item[i]), str, size, indent+2, 1, 0); svalue_to_string(&(obj->u.vec->item[i]), str, size, indent+2, 0, 0); stradd(str, &size, "\n"); add_indent(str, &size, indent); stradd(str, &size, "})"); } break; case T_BUFFER: stradd(str, &size, "<buffer>"); break; case T_FUNCTION: stradd(str, &size, "(: "); svalue_to_string(&(obj->u.fp->obj), str, size, indent+2, trailing, 0); stradd(str, &size, ", "); svalue_to_string(&(obj->u.fp->fun), str, size, indent+2, trailing, 0); stradd(str, &size, " :)"); break; case T_MAPPING: if (!(obj->u.map->count)) { stradd(str, &size, "([ ])"); } else { stradd(str, &size, "([ /* sizeof() == "); numadd(str, &size, obj->u.map->count); stradd(str, &size, " */\n"); for (i=0;i<(int)(obj->u.map->table_size);i++) { struct node *elm; for (elm = obj->u.map->table[i];elm;elm = elm->next) { svalue_to_string(&(elm->values[0]), str, size, indent+2, 0, 0); stradd(str, &size, " : "); svalue_to_string(&(elm->values[1]), str, size, indent+4, 1, 1); } } add_indent(str, &size, indent); stradd(str, &size, "])"); } break; case T_OBJECT: { struct svalue *temp; stradd(str, &size, obj->u.ob->name); push_object(obj->u.ob); temp = apply_master_ob("object_name", 1); if (temp && (temp->type == T_STRING)) { stradd(str, &size, " (\""); stradd(str, &size, temp->u.string); stradd(str, &size, "\")"); } /* * These flags aren't that useful... * if (obj->u.ob->flags & O_HEART_BEAT) stradd(str,&size," (hb)"); if (obj->u.ob->flags & O_IS_WIZARD) stradd(str,&size," (wiz)"); if (obj->u.ob->flags & O_ENABLE_COMMANDS) stradd(str,&size," (enabled)"); if (obj->u.ob->flags & O_CLONE) stradd(str,&size," (clone)"); if (obj->u.ob->flags & O_DESTRUCTED) stradd(str,&size," (destructed)"); if (obj->u.ob->flags & O_SWAPPED) stradd(str,&size," (swapped)"); if (obj->u.ob->flags & O_ONCE_INTERACTIVE) stradd(str,&size," (x-activ)"); if (obj->u.ob->flags & O_APPROVED) stradd(str,&size," (ok)"); if (obj->u.ob->flags & O_RESET_STATE) stradd(str,&size," (reset)"); if (obj->u.ob->flags & O_WILL_CLEAN_UP) stradd(str,&size," (clean up)"); */ break; } default: stradd(str, &size, "!ERROR: GARBAGE SVALUE!"); } /* end of switch (obj->type) */ if (trailing) stradd(str, &size, ",\n"); } /* end of svalue_to_string() */ /* The ignore strlen is so that the pading will work with our wonderful * ansi colour stuff. bing onwards. This was hacked in by Pinkfish * so you may hold me responsible if you wish */ int ignorestrlen(str) char *str; { int len=0, inignore=0, first=0; while (*str++) { if (first) { if (*str == IGNORE_C2) inignore = !inignore; len += !inignore; first = 0; } if (*str == IGNORE_C1) first = 1; else len += !inignore; } return len; } /* * Adds the string "str" to the buff after justifying it within "fs". * "trailing" is a flag which is set if trailing justification is to be done. * "str" is unmodified. trailing is, of course, ignored in the case * of right justification. */ void add_justified(str, pad, fs, finfo, trailing) char *str, *pad; int fs; format_info finfo; short int trailing; { int i, len, len2; len = strlen(str); len2 = ignorestrlen(str); switch(finfo & INFO_J) { case INFO_J_LEFT: for (i=0; i<len; i++) ADD_CHAR(str[i]); fs -= len; len = strlen(pad); if (trailing) for (i=0; fs>0; i++, fs--) { if (pad[i%len] == '\\') i++; ADD_CHAR(pad[i%len]); } break; case INFO_J_CENTRE: { int j, l; l = strlen(pad); if (!l) { /* Irk! */ l = 1; pad = " "; } j = (fs - len2)/2 + (fs - len2)%2; for (i=0; i<j; i++) { if (pad[i%l] == '\\') { i++; j++; } ADD_CHAR(pad[i%l]); } for (i=0; i<len; i++) ADD_CHAR(str[i]); j = (fs - len2)/2; if (trailing) for (i=0; i<j; i++) { if (pad[i%l] == '\\') { i++; j++; } ADD_CHAR(pad[i%l]); } break; } default: { /* std (s)printf defaults to right justification */ int l; fs -= len2; l = strlen(pad); for (i=0; i<fs; i++) { if (pad[i%l] == '\\') { i++; fs++; } ADD_CHAR(pad[i%l]); } for (i=0; i<len; i++) ADD_CHAR(str[i]); } } } /* end of add_justified() */ /* * Adds "column" to the buffer. * Returns 0 is column not finished. * Returns 1 if column completed. * Returns 2 if column completed has a \n at the end. */ int add_column(column, trailing) cst **column; short int trailing; { register unsigned int done, off=0, inadd_off=0, first_ig=0; unsigned int save; static char tmp_buf[2049]; /* hmm, is this always enough? */ #define COL (*column) #define COL_D (COL->d.col) for (done=0;((done-off)<COL->pres) && COL_D[done] && (COL_D[done]!='\n');done++) { if (first_ig) { if (COL_D[done] == IGNORE_C2) { inadd_off = !inadd_off; off += 2; } else done--; first_ig = 0; } else if (COL_D[done] == IGNORE_C1) first_ig = 1; else off += inadd_off; } if (COL_D[done] && (COL_D[done]!='\n')) { save = done; for (; done && (COL_D[done]!=' '); done--); /* * handle larger than column size words... */ if (!done) done = save; } strncpy(tmp_buf, COL_D, done); tmp_buf[done] = '\0'; /* this commented block and the one below would sometimes try to write to a constant string (ie, the one returned by f_range()), causing a crash. changed it to strncpy()...sigh -bobf/Blackthorn save = COL_D[done]; COL_D[done] = '\0'; */ add_justified(tmp_buf, COL->pad, COL->size, COL->info, (trailing || (COL->next))); /* COL_D[done] = save; */ COL_D += done; /* inc'ed below ... */ /* * if this or the next character is a NULL then take this column out * of the list. */ if (!(*COL_D) || !(*(++COL_D))) { cst *temp; int ret; if (*(COL_D-1) == '\n') ret = 2; else ret = 1; temp = COL->next; FREE((char *)COL); COL = temp; return ret; } return 0; } /* end of add_column() */ /* * Adds "table" to the buffer. * Returns 0 if table not completed. * Returns 1 if table completed. */ int add_table(table, trailing) cst **table; short int trailing; { char save; register unsigned int done, i; #define TAB (*table) #define TAB_D (TAB->d.tab[i]) for (i=0; i < TAB->nocols && TAB_D; i++) { for (done=0;(TAB_D[done])&&(TAB_D[done] != '\n');done++); save = TAB_D[done]; TAB_D[done] = '\0'; add_justified(TAB_D, TAB->pad, TAB->size, TAB->info, (trailing || (i < TAB->nocols-1) || (TAB->next))); TAB_D[done] = save; TAB_D += done; /* inc'ed next line ... */ if (!(*TAB_D) || !(*(++TAB_D))) TAB_D = 0; } if (trailing && i < TAB->nocols) for (; i < TAB->nocols; i++) for (done = 0; done < TAB->size; done++) ADD_CHAR(' '); if (!TAB->d.tab[0]) { cst *temp; temp = TAB->next; FREE((char *)TAB); TAB = temp; return 1; } return 0; } /* end of add_table() */ /* * THE (s)printf() function. * It returns a pointer to it's internal buffer (or a string in the text * segment) thus, the string must be copied if it has to survive after * this function is called again, or if it's going to be modified (esp. * if it risks being free()ed). */ char *string_print_formatted(format_str, argc, argv) char *format_str; int argc; struct svalue *argv; { format_info finfo; savechars *saves = 0; /* chars to restore */ cst *csts; /* list of columns/tables to be done */ struct svalue *carg; /* current arg */ VOLATILE unsigned int nelemno = 0; /* next offset into array */ unsigned int fpos; /* position in format_str */ VOLATILE unsigned int arg = 0; /* current arg number */ unsigned int fs; /* field size */ int pres; /* presision */ unsigned int i; char *pad; /* fs pad string */ #ifdef cray if (SETJMP(error_jmp)) { /* the cray setjmp is braindead */ char *err; i = -1; #else if ((i = SETJMP(error_jmp))) { /* error handling */ char *err; #endif /* * Must restore format_str before we exit. /Oros 930902 */ while (saves) { savechars *tmp; *(saves->where) = saves->what; tmp = saves; saves = saves->next; free((char *)tmp); } switch(i) { case ERR_BUFF_OVERFLOW: err = "BUFF_SIZE overflowed..."; break; case ERR_TO_FEW_ARGS: err = "More arguments specified than passed."; break; case ERR_INVALID_STAR: err = "Incorrect argument type to *."; break; case ERR_PRES_EXPECTED: err = "Expected presision not found."; break; case ERR_INVALID_FORMAT_STR: err = "Error in format string."; break; case ERR_INCORRECT_ARG_S: err = "Incorrect argument to type %s"; break; case ERR_CST_REQUIRES_FS: err = "Column/table mode requires a field size."; break; case ERR_BAD_INT_TYPE: err = "!feature - bad integer type!"; break; case ERR_UNDEFINED_TYPE: err = "!feature - undefined type!"; break; case ERR_QUOTE_EXPECTED: err = "Quote expected in format string."; break; case ERR_UNEXPECTED_EOS: err = "Unexpected end of format string."; break; case ERR_NULL_PS: err = "Null pad string specified."; break; case ERR_ARRAY_EXPECTED: err = "Array expected."; break; default: #ifdef RETURN_ERROR_MESSAGES debug_message("%s", "ERROR: (s)printf(): !feature - undefined error 0x%X !\n", i); if (current_object) { debug_message("program: %s, object: %s line %d\n", current_prog ? current_prog->name : "", current_object->name, get_line_number_if_any()); } sprintf(buff, "ERROR: (s)printf(): !feature - undefined error 0x%X !\n", i); fprintf(stderr, "%s:%d: %s", current_prog->name, get_line_number_if_any(), buff); return buff; #else error("ERROR: (s)printf(): !feature - undefined error 0x%X !\n", i); #endif /* RETURN_ERROR_MESSAGES */ } #ifdef RETURN_ERROR_MESSAGES sprintf(buff, "ERROR: (s)printf(): %s in arg %u\n", err, arg); fprintf(stderr, "%s:%d: %s", current_prog->name, get_line_number_if_any(), buff); debug_message("%s", "ERROR: (s)printf(): %s in arg %u\n", err, arg); if (current_object) { debug_message("program: %s, object: %s line %d\n", current_prog ? current_prog->name : "", current_object->name, get_line_number_if_any()); } return buff; #else error("ERROR: (s)printf(): %s in arg %d\n", err, arg); #endif /* RETURN_ERROR_MESSAGES */ } arg = -1; bpos = 0; curpos = 0; inignore = 0; startignore = 0; csts = 0; saves = 0; for (fpos=0; 1; fpos++) { if ((format_str[fpos] == '\n') || (!format_str[fpos])) { int column_stat = 0; if (!csts) { if (!format_str[fpos]) break; ADD_CHAR('\n'); curpos = 0; inignore = 0; startignore = 0; continue; } ADD_CHAR('\n'); curpos = 0; inignore = 0; startignore = 0; while (csts) { cst **temp; temp = &csts; while (*temp) { if ((*temp)->info & INFO_COLS) { if (*((*temp)->d.col-1) != '\n') while (*((*temp)->d.col) == ' ') (*temp)->d.col++; for (i=curpos; i<(*temp)->start; i++) ADD_CHAR(' '); column_stat = add_column(temp, 0); if (!column_stat) temp = &((*temp)->next); } else { for (i=curpos; i<(*temp)->start; i++) ADD_CHAR(' '); if (!add_table(temp, 0)) temp = &((*temp)->next); } } /* of while (*temp) */ if (csts || format_str[fpos] == '\n') ADD_CHAR('\n'); inignore = 0; startignore = 0; curpos = 0; } /* of while (csts) */ if (column_stat == 2) ADD_CHAR('\n'); if (!format_str[fpos]) break; continue; } if (format_str[fpos] == '%') { if (format_str[fpos+1] == '%') { ADD_CHAR('%'); fpos++; continue; } GET_NEXT_ARG; fs = 0; pres = 0; pad = " "; finfo = 0; for (fpos++; !(finfo & INFO_T); fpos++) { if (!format_str[fpos]) { finfo |= INFO_T_ERROR; break; } if (((format_str[fpos] >= '0') && (format_str[fpos] <= '9')) || (format_str[fpos] == '*')) { if (pres == -1) { /* then looking for pres */ if (format_str[fpos] == '*') { if (carg->type != T_NUMBER) ERROR(ERR_INVALID_STAR); pres = carg->u.number; GET_NEXT_ARG; continue; } pres = format_str[fpos] - '0'; for (fpos++; (format_str[fpos]>='0')&&(format_str[fpos]<='9'); fpos++) { pres = pres*10 + format_str[fpos] - '0'; } } else { /* then is fs (and maybe pres) */ if ((format_str[fpos] == '0') && (((format_str[fpos+1] >= '1') && (format_str[fpos+1] <= '9')) || (format_str[fpos+1] == '*'))) pad = "0"; else { if (format_str[fpos] == '*') { if (carg->type != T_NUMBER) ERROR(ERR_INVALID_STAR); fs = carg->u.number; if (pres == -2) pres = fs; /* colon */ GET_NEXT_ARG; continue; } fs = format_str[fpos] - '0'; } for (fpos++; (format_str[fpos]>='0')&&(format_str[fpos]<='9'); fpos++) { fs = fs*10 + format_str[fpos] - '0'; } if (pres == -2) { /* colon */ pres = fs; } } fpos--; /* bout to get incremented */ continue; } switch (format_str[fpos]) { case ' ': finfo |= INFO_PP_SPACE; break; case '+': finfo |= INFO_PP_PLUS; break; case '-': finfo |= INFO_J_LEFT; break; case '|': finfo |= INFO_J_CENTRE; break; case '@': finfo |= INFO_ARRAY; break; case '=': finfo |= INFO_COLS; break; case '#': finfo |= INFO_TABLE; break; case '.': pres = -1; break; case ':': pres = -2; break; case '%': finfo |= INFO_T_NULL; break; /* never reached */ case 'O': finfo |= INFO_T_LPC; break; case 's': finfo |= INFO_T_STRING; break; case 'd': finfo |= INFO_T_INT; break; case 'i': finfo |= INFO_T_INT; break; case 'f': finfo |= INFO_T_FLOAT; break; case 'c': finfo |= INFO_T_CHAR; break; case 'o': finfo |= INFO_T_OCT; break; case 'x': finfo |= INFO_T_HEX; break; case 'X': finfo |= INFO_T_C_HEX; break; case '\'': pad = &(format_str[++fpos]); while (1) { if (!format_str[fpos]) ERROR(ERR_UNEXPECTED_EOS); if (format_str[fpos] == '\\') { fpos += 2; continue; } if (format_str[fpos] == '\'') { if (format_str+fpos == pad) ERROR(ERR_NULL_PS); SAVE_CHAR(format_str+fpos); format_str[fpos] = '\0'; break; } fpos++; } break; default: finfo |= INFO_T_ERROR; } } /* end of for () */ if (pres < 0) ERROR(ERR_PRES_EXPECTED); /* * now handle the different arg types... */ if (finfo & INFO_ARRAY) { if (carg->type != T_POINTER) ERROR(ERR_ARRAY_EXPECTED); if (carg->u.vec->size == 0) { fpos--; /* 'bout to get incremented */ continue; } carg = (argv+arg)->u.vec->item; nelemno = 1; /* next element number */ } while (1) { struct svalue *clean = 0; if ((finfo & INFO_T) == INFO_T_LPC) { clean = (struct svalue *) DXALLOC(sizeof(struct svalue), 111, "string_print: 1"); clean->type = T_STRING; clean->subtype = STRING_MALLOC; clean->u.string = (char *)DXALLOC(500, 112, "string_print: 2"); clean->u.string[0] = '\0'; svalue_to_string(carg, &(clean->u.string), 500, 0, 0, 0); carg = clean; finfo ^= INFO_T_LPC; finfo |= INFO_T_STRING; } if ((finfo & INFO_T) == INFO_T_ERROR) { ERROR(ERR_INVALID_FORMAT_STR); } else if ((finfo & INFO_T) == INFO_T_NULL) { /* never reached... */ fprintf(stderr, "%s: (s)printf: INFO_T_NULL.... found.\n", current_object->name); ADD_CHAR('%'); } else if ((finfo & INFO_T) == INFO_T_STRING) { int slen; /* %s null handling added 930709 by Luke Mewburn <zak@rmit.oz.au> */ if (carg->type == T_NUMBER && carg->u.number == 0) { clean = (struct svalue *) DXALLOC(sizeof(struct svalue), 121, "string_print: z1"); clean->type = T_STRING; clean->subtype = STRING_MALLOC; clean->u.string = (char *)DXALLOC(sizeof(NULL_MSG), 122, "string_print: z2"); strcpy(clean->u.string, NULL_MSG); carg = clean; } if (carg->type != T_STRING) ERROR(ERR_INCORRECT_ARG_S); slen = strlen(carg->u.string); if ((finfo & INFO_COLS) || (finfo & INFO_TABLE)) { cst **temp; if (!fs) ERROR(ERR_CST_REQUIRES_FS); temp = &csts; while (*temp) temp = &((*temp)->next); if (finfo & INFO_COLS) { *temp = (cst *)DXALLOC(sizeof(cst), 113, "string_print: 3"); (*temp)->next = 0; (*temp)->d.col = carg->u.string; (*temp)->pad = pad; (*temp)->size = fs; (*temp)->pres = (pres) ? pres : fs; (*temp)->info = finfo; (*temp)->start = curpos; if ((add_column(temp, (((format_str[fpos] != '\n') && (format_str[fpos] != '\0')) || ((finfo & INFO_ARRAY) && (nelemno < (argv+arg)->u.vec->size)))) == 2) && !format_str[fpos]) { ADD_CHAR('\n'); } } else { /* (finfo & INFO_TABLE) */ #undef max unsigned int n, len, max; #define TABLE carg->u.string (*temp) = (cst *)DXALLOC(sizeof(cst), 114, "string_print: 4"); (*temp)->pad = pad; (*temp)->info = finfo; (*temp)->start = curpos; (*temp)->next = 0; max = len = 0; n = 1; for (i=0; TABLE[i]; i++) { if (TABLE[i] == '\n') { if (len > max) max = len; len = 0; if (TABLE[i+1]) n++; continue; } len++; } if (pres) { (*temp)->size = fs/pres; } else { if (len > max) max = len; /* the null terminated word */ pres = fs/(max+2); /* at least two separating spaces */ if (!pres) pres = 1; (*temp)->size = fs/pres; } len = n/pres; /* length of average column */ if (n < pres) pres = n; if (len*pres < n) len++; if (len > 1 && n%pres) pres -= (pres - n%pres)/len; (*temp)->d.tab = (char **) DXALLOC(pres*sizeof(char *), 115, "string_print: 5"); (*temp)->nocols = pres; /* heavy sigh */ (*temp)->d.tab[0] = TABLE; if (pres == 1) goto add_table_now; i = 1; /* the next column number */ n = 0; /* the current "word" number in this column */ for (fs = 0; TABLE[fs]; fs++) { /* throwing away fs... */ if (TABLE[fs] == '\n') { if (++n >= len) { SAVE_CHAR(((TABLE)+fs)); TABLE[fs] = '\0'; (*temp)->d.tab[i++] = TABLE+fs+1; if (i >= pres) goto add_table_now; n = 0; } } } add_table_now: add_table(temp, (((format_str[fpos] != '\n') && (format_str[fpos] != '\0')) || ((finfo & INFO_ARRAY) && (nelemno < (argv+arg)->u.vec->size)))); } } else { /* not column or table */ if (pres && pres<slen) { if (carg != clean) SAVE_CHAR(((carg->u.string)+pres)); carg->u.string[pres] = '\0'; slen = pres; } if (fs && fs>slen) { add_justified(carg->u.string, pad, fs, finfo, (((format_str[fpos] != '\n') && (format_str[fpos] != '\0')) || ((finfo & INFO_ARRAY) && (nelemno < (argv+arg)->u.vec->size))) || carg->u.string[slen-1] != '\n'); } else { for (i=0; i<slen; i++) ADD_CHAR(carg->u.string[i]); } } } else if (finfo & INFO_T_INT) { /* one of the integer types */ char cheat[8]; char temp[100]; *cheat = '%'; i = 1; switch (finfo & INFO_PP) { case INFO_PP_SPACE: cheat[i++] = ' '; break; case INFO_PP_PLUS: cheat[i++] = '+'; break; } switch (finfo & INFO_T) { case INFO_T_INT: cheat[i++] = 'd'; break; case INFO_T_FLOAT: cheat[i++] = 'f'; break; case INFO_T_CHAR: cheat[i++] = 'c'; break; case INFO_T_OCT: cheat[i++] = 'o'; break; case INFO_T_HEX: cheat[i++] = 'x'; break; case INFO_T_C_HEX: cheat[i++] = 'X'; break; default: ERROR(ERR_BAD_INT_TYPE); } if ((cheat[i - 1] == 'f' && carg->type != T_REAL) || (cheat[i - 1] != 'f' && carg->type != T_NUMBER)) { #ifdef RETURN_ERROR_MESSAGES sprintf(buff, "ERROR: (s)printf(): incorrect argument type to %%%c.\n", cheat[i-1]); fprintf(stderr, "%s:%d: %s", current_prog->name, get_line_number_if_any(), buff); return buff; #else error("ERROR: (s)printf(): incorrect argument type to %%%c.\n", cheat[i-1]); #endif /* RETURN_ERROR_MESSAGES */ } cheat[i] = '\0'; /* Floatingpoint output fixed by hasse@solace.hsh.se (Kniggit@VikingMud) */ if(carg->type == T_REAL) { if(pres) { sprintf(cheat, "%%.%df", pres); pres = 0; } sprintf(temp, cheat, carg->u.real); } else sprintf(temp, cheat, carg->u.number); { int tmpl = strlen(temp); if (pres && tmpl > pres) temp[pres] = '\0'; /* well.... */ if (tmpl < fs) add_justified(temp, pad, fs, finfo, (((format_str[fpos] != '\n') && (format_str[fpos] != '\0')) || ((finfo & INFO_ARRAY) && (nelemno < (argv+arg)->u.vec->size)))); else for (i=0; i<tmpl; i++) ADD_CHAR(temp[i]); } } else /* type not found */ ERROR(ERR_UNDEFINED_TYPE); if (clean) free_svalue(clean); if (!(finfo & INFO_ARRAY)) break; if (nelemno >= (argv+arg)->u.vec->size) break; carg = (argv+arg)->u.vec->item+nelemno++; } /* end of while (1) */ fpos--; /* bout to get incremented */ continue; } ADD_CHAR(format_str[fpos]); } /* end of for (fpos=0; 1; fpos++) */ ADD_CHAR('\0'); while (saves) { savechars *tmp; *(saves->where) = saves->what; tmp = saves; saves = saves->next; FREE((char *)tmp); } return buff; } /* end of string_print_formatted() */ #endif /* defined(F_SPRINTF) || defined(F_PRINTF) */