/* * 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 "std.h" #include "lpc_incl.h" #include "efuns_incl.h" #include "simul_efun.h" #include "ignore.h" #include "lex.h" #include "stralloc.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) 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 ERROR(x) LONGJMP(error_jmp, x) #define SPRINTF_ERROR(x) { if (clean) { free_svalue(clean, "sprintf error");\ FREE(clean); clean = 0; } ERROR(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 ERR_RECOVERY_ONLY 0xE /* err msg already done...just * recover */ #define ADD_CHAR(x) {\ outbuf_addchar(&obuff, x);\ if (startignore) \ if (x == '^') \ inignore = !inignore; \ else \ curpos += 2*!inignore; \ else \ if (x == '%') \ startignore = 1; \ else \ curpos += !inignore; \ if (obuff.real_size == USHRT_MAX) ERROR(ERR_BUFF_OVERFLOW); \ } #define M_ADD_CHAR(x) {\ outbuf_addchar(&obuff, x);\ if (startignore) \ if (x == '^') \ inignore = !inignore; \ else \ curpos += 2*!inignore; \ else \ if (x == '%') \ startignore = 1; \ else \ curpos += !inignore; \ if (obuff.real_size == USHRT_MAX) { SPRINTF_ERROR(ERR_BUFF_OVERFLOW); } \ } #define T_ADD_CHAR(x) {\ outbuf_addchar(&obuff, x);\ if (startignore) \ if (x == '^') \ inignore = !inignore; \ else \ curpos += 2*!inignore; \ else \ if (x == '%') \ startignore = 1; \ else \ curpos += !inignore; \ if (obuff.real_size == USHRT_MAX) obuff.real_size--; \ } #define GET_NEXT_ARG {\ if (++arg >= argc) ERROR(ERR_TO_FEW_ARGS); \ carg = (argv+arg);\ } #define SAVE_CHAR(pointer) {\ savechars *new;\ new = ALLOCATE(savechars, TAG_TEMPORARY, "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[LARGEST_PRINTABLE_STRING];/* buffer for returned errors */ static outbuffer_t obuff; static unsigned int curpos; /* cursor position */ static unsigned int inignore; /* are we not counting these characters */ static unsigned int startignore;/* we found the first char... now for next */ static jmp_buf error_jmp; /* LONGJMP() buffer for error catching */ static void numadd PROT((outbuffer_t *, int num)); static void add_indent PROT((outbuffer_t *, int indent)); static int ignorestrlen PROT((char *str)); static void add_justified PROT((char *str, char *pad, int fs, format_info finfo, short int trailing)); static int add_column PROT((cst ** column, short int trailing)); static int add_table PROT((cst ** table, short int trailing)); static void numadd P2(outbuffer_t *, outbuf, int, num) { int i, num_l, /* length of num as a string */ nve; /* true if num negative */ int space; int chop; char *p; if (num < 0) { /* Beek: yes, it's possible for num < 0, and num * -1 < 0. */ /* Beek: This shouldn't be a hardcoded const (assumes int is 4 bytes)*/ num = (num * -1) & 0x7fffffff; nve = 1; } else nve = 0; for (i = num / 10, num_l = nve + 1; i; i /= 10, num_l++); if ((space = outbuf_extend(outbuf, num_l))) { chop = num_l - space; while (chop--) num /= 10; /* lose that last digits that got chopped */ p = outbuf->buffer + outbuf->real_size; outbuf->real_size += space; p[space] = 0; if (nve) { *p++ = '-'; space--; } while (space--) { p[space] = (num % 10) + '0'; num /= 10; } } } /* end of numadd() */ static void add_indent P2(outbuffer_t *, outbuf, int, indent) { int l; if ((l = outbuf_extend(outbuf, indent))) { memset(outbuf->buffer + outbuf->real_size, ' ', l); *(outbuf->buffer + outbuf->real_size + l) = 0; outbuf->real_size += l; } } /* * 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 P5(svalue_t *, obj, outbuffer_t *, outbuf, int, indent, int, trailing, int, indent2) { int i; /* prevent an infinite recursion on self-referential structures */ if (indent > 200) { FREE_MSTR(outbuf->buffer); error("structure too deep to print.\n"); return; } if (!indent2) add_indent(outbuf, indent); switch (obj->type) { case T_INVALID: outbuf_add(outbuf, "T_INVALID"); break; case T_LVALUE: outbuf_add(outbuf, "lvalue: "); svalue_to_string(obj->u.lvalue, outbuf, indent + 2, trailing, 0); break; case T_NUMBER: numadd(outbuf, obj->u.number); break; case T_REAL: outbuf_addv(outbuf, "%g", obj->u.real); break; case T_STRING: outbuf_add(outbuf, "\""); outbuf_add(outbuf, obj->u.string); outbuf_add(outbuf, "\""); break; case T_CLASS: { int n = obj->u.arr->size; outbuf_add(outbuf, "CLASS( "); numadd(outbuf, n); outbuf_add(outbuf, n == 1 ? " element\n" : " elements\n"); for (i = 0; i < (obj->u.arr->size) - 1; i++) svalue_to_string(&(obj->u.arr->item[i]), outbuf, indent + 2, 1, 0); svalue_to_string(&(obj->u.arr->item[i]), outbuf, indent + 2, 0, 0); outbuf_add(outbuf, "\n"); add_indent(outbuf, indent); outbuf_add(outbuf, " )"); break; } case T_ARRAY: if (!(obj->u.arr->size)) { outbuf_add(outbuf, "({ })"); } else { outbuf_add(outbuf, "({ /* sizeof() == "); numadd(outbuf, obj->u.arr->size); outbuf_add(outbuf, " */\n"); for (i = 0; i < (obj->u.arr->size) - 1; i++) svalue_to_string(&(obj->u.arr->item[i]), outbuf, indent + 2, 1, 0); svalue_to_string(&(obj->u.arr->item[i]), outbuf, indent + 2, 0, 0); outbuf_add(outbuf, "\n"); add_indent(outbuf, indent); outbuf_add(outbuf, "})"); } break; case T_BUFFER: outbuf_add(outbuf, "<buffer>"); break; case T_FUNCTION: { svalue_t tmp; tmp.type = T_ARRAY; outbuf_add(outbuf, "(: "); switch (obj->u.fp->hdr.type) { case FP_LOCAL | FP_NOT_BINDABLE: outbuf_add(outbuf, obj->u.fp->hdr.owner->prog->functions[obj->u.fp->f.local.index].name); break; case FP_SIMUL: outbuf_add(outbuf, simuls[obj->u.fp->f.simul.index]->name); break; case FP_FUNCTIONAL: case FP_FUNCTIONAL | FP_NOT_BINDABLE: { char buf[10]; int n = obj->u.fp->f.functional.num_arg; outbuf_add(outbuf, "<code>("); for (i=1; i < n; i++) { sprintf(buf, "$%i, ", i); outbuf_add(outbuf, buf); } if (n) { sprintf(buf, "$%i", n); outbuf_add(outbuf, buf); } outbuf_add(outbuf, ")"); break; } case FP_EFUN: { int i; i = obj->u.fp->f.efun.index; outbuf_add(outbuf, instrs[i].name); break; } } if (obj->u.fp->hdr.args) { for (i=0; i<obj->u.fp->hdr.args->size; i++) { outbuf_add(outbuf, ", "); svalue_to_string(&(obj->u.fp->hdr.args->item[i]), outbuf, indent, 0, 0); } } } outbuf_add(outbuf, " :)"); break; case T_MAPPING: if (!(obj->u.map->count)) { outbuf_add(outbuf, "([ ])"); } else { outbuf_add(outbuf, "([ /* sizeof() == "); numadd(outbuf, obj->u.map->count); outbuf_add(outbuf, " */\n"); for (i = 0; i <= (int) (obj->u.map->table_size); i++) { mapping_node_t *elm; for (elm = obj->u.map->table[i]; elm; elm = elm->next) { svalue_to_string(&(elm->values[0]), outbuf, indent + 2, 0, 0); outbuf_add(outbuf, " : "); svalue_to_string(&(elm->values[1]), outbuf, indent + 4, 1, 1); } } add_indent(outbuf, indent); outbuf_add(outbuf, "])"); } break; case T_OBJECT: { svalue_t *temp; if (obj->u.ob->flags & O_DESTRUCTED) { numadd(outbuf, 0); break; } outbuf_add(outbuf, obj->u.ob->name); push_object(obj->u.ob); temp = safe_apply_master_ob(APPLY_OBJECT_NAME, 1); if (temp && temp != (svalue_t *) -1 && (temp->type == T_STRING)) { outbuf_add(outbuf, " (\""); outbuf_add(outbuf, temp->u.string); outbuf_add(outbuf, "\")"); } break; } default: outbuf_add(outbuf, "!ERROR: GARBAGE SVALUE!"); } /* end of switch (obj->type) */ if (trailing) outbuf_add(outbuf, ",\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 * * Beek: Can I press charges? */ static int ignorestrlen P1(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. */ /* In the following actually use T_ADD_CHAR (truncating if length is too * long :) ) It's better to be consistent to free clean maybe, but that * requires passing it to add_justified. It's even better to rewrite it * - volunteers? :) * * Sym */ static void add_justified P5(char *, str, char *, 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++) T_ADD_CHAR(str[i]); fs -= len; len = strlen(pad); if (trailing) for (i = 0; fs > 0; i++, fs--) { if (pad[i % len] == '\\') i++; T_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++; } T_ADD_CHAR(pad[i % l]); } for (i = 0; i < len; i++) T_ADD_CHAR(str[i]); j = (fs - len2) / 2; if (trailing) for (i = 0; i < j; i++) { if (pad[i % l] == '\\') { i++; j++; } T_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++; } T_ADD_CHAR(pad[i % l]); } for (i = 0; i < len; i++) T_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. */ static int add_column P2(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? */ int ret; #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; ret = 1; if (*COL_D == '\n') { COL_D++; ret = 2; } /* * if the next character is a NULL then take this column out of * the list. */ if (!(*COL_D)) { cst *temp; 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. */ static int add_table P2(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; if (TAB->d.tab) FREE((char *)(TAB->d.tab)); 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 P3(char *, format_str, int, argc, svalue_t *, argv) { format_info finfo; savechars *saves = 0; /* chars to restore */ cst *csts; /* list of columns/tables to be done */ svalue_t *carg; /* current arg */ VOLATILE unsigned int nelemno = 0; /* next offset into array */ unsigned int fpos; /* position in format_str */ VOLATILE SIGNED int arg = 0; /* current arg number */ unsigned int fs; /* field size */ int pres; /* presision */ unsigned int i; char *pad; /* fs pad string */ char *retvalue; /* free anything that is sitting around here */ if (obuff.buffer) FREE_MSTR(obuff.buffer); outbuf_zero(&obuff); #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; case ERR_RECOVERY_ONLY: return string_copy(buff, "sprintf error"); default: #ifdef RETURN_ERROR_MESSAGES sprintf(buff, "ERROR: (s)printf(): !feature - undefined error 0x%X !\n", i); debug_message("Program:%s File: %s: %s", current_prog->name, get_line_number_if_any(), buff); debug_message("%s", buff); if (current_object) { debug_message("program: %s, object: %s, file: %s\n", current_prog ? current_prog->name : "", current_object->name, get_line_number_if_any()); } return string_copy(buff, "sprintf error"); #else error("ERROR: (s)printf(): !feature - undefined error 0x%X !\n", i); #endif /* RETURN_ERROR_MESSAGES */ } /* end of switch */ #ifdef RETURN_ERROR_MESSAGES sprintf(buff, "ERROR: (s)printf(): %s (arg %u)\n", err, arg); debug_message("Program %s File: %s: %s", current_prog->name, get_line_number_if_any(), buff); debug_message("%s", buff); if (current_object) { debug_message("program: %s, object: %s, file: %s\n", current_prog ? current_prog->name : "", current_object->name, get_line_number_if_any()); } return string_copy(buff, "sprintf error"); #else error("ERROR: (s)printf(): %s (arg: %d)\n", err, arg); #endif /* RETURN_ERROR_MESSAGES */ } arg = -1; 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_ARRAY) ERROR(ERR_ARRAY_EXPECTED); if (carg->u.arr->size == 0) { fpos--; /* 'bout to get incremented */ continue; } carg = (argv + arg)->u.arr->item; nelemno = 1; /* next element number */ } while (1) { svalue_t *clean = 0; if ((finfo & INFO_T) == INFO_T_LPC) { outbuffer_t outbuf; outbuf_zero(&outbuf); svalue_to_string(carg, &outbuf, 0, 0, 0); outbuf_fix(&outbuf); /* This is gross - Beek */ clean = ALLOCATE(svalue_t, TAG_TEMPORARY, "string_print: 1"); clean->type = T_STRING; clean->subtype = STRING_MALLOC; clean->u.string = outbuf.buffer; 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); M_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 = ALLOCATE(svalue_t, TAG_TEMPORARY, "string_print: z1"); clean->type = T_STRING; clean->subtype = STRING_MALLOC; clean->u.string = string_copy(NULL_MSG, "sprintf NULL"); carg = clean; } if (carg->type != T_STRING) { SPRINTF_ERROR(ERR_INCORRECT_ARG_S); } slen = SVALUE_STRLEN(carg); if ((finfo & INFO_COLS) || (finfo & INFO_TABLE)) { cst **temp; if (!fs) { SPRINTF_ERROR(ERR_CST_REQUIRES_FS); } temp = &csts; while (*temp) temp = &((*temp)->next); if (finfo & INFO_COLS) { *temp = ALLOCATE(cst, TAG_TEMPORARY, "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.arr->size)))) == 2) && !format_str[fpos]) { M_ADD_CHAR('\n'); } } else {/* (finfo & INFO_TABLE) */ unsigned int n, len, max_len; #define TABLE carg->u.string (*temp) = ALLOCATE(cst, TAG_TEMPORARY, "string_print: 4"); (*temp)->d.tab = 0; (*temp)->pad = pad; (*temp)->info = finfo; (*temp)->start = curpos; (*temp)->next = 0; max_len = len = 0; n = 1; for (i = 0; TABLE[i]; i++) { if (TABLE[i] == '\n') { if (len > max_len) max_len = len; len = 0; if (TABLE[i + 1]) n++; continue; } len++; } if (pres) { (*temp)->size = fs / pres; } else { if (len > max_len) max_len = len; /* the null terminated word */ pres = fs / (max_len + 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 = CALLOCATE(pres, char *, TAG_TEMPORARY, "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.arr->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.arr->size))) || carg->u.string[slen - 1] != '\n'); } else { for (i = 0; i < slen; i++) M_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. (arg: %u)\n", cheat[i - 1], arg); fprintf(stderr, "Program %s File: %s: %s", current_prog->name, get_line_number_if_any(), buff); debug_message("%s", buff); if (current_object) { debug_message("program: %s, object: %s, file: %s\n", current_prog ? current_prog->name : "", current_object->name, get_line_number_if_any()); } ERROR(ERR_RECOVERY_ONLY); #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[tmpl = 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.arr->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, "string_print_formatted"); FREE(clean); clean = 0; } if (!(finfo & INFO_ARRAY)) break; if (nelemno >= (argv + arg)->u.arr->size) break; carg = (argv + arg)->u.arr->item + nelemno++; } /* end of while (1) */ fpos--; /* bout to get incremented */ continue; } ADD_CHAR(format_str[fpos]); } /* end of for (fpos=0; 1; fpos++) */ while (saves) { savechars *tmp; *(saves->where) = saves->what; tmp = saves; saves = saves->next; FREE((char *) tmp); } outbuf_fix(&obuff); retvalue = obuff.buffer; obuff.buffer = 0; return retvalue; } /* end of string_print_formatted() */ #endif /* defined(F_SPRINTF) || defined(F_PRINTF) */