/* * sprintf.c v1.05+ for LPMud 3.0.52 (+ plus mappings) * * 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). * * 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. * "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 "stdio.h" #include "interpret.h" #include "mapping.h" #include "object.h" #include "sent.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 extern char *xalloc(), *string_copy(); extern void free_svalue (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; * 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_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 10000 #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 ADD_CHAR(x) {\ buff[bpos++] = x;\ if (bpos > BUFF_SIZE) ERROR(ERR_BUFF_OVERFLOW); \ curpos++;\ } #define GET_NEXT_ARG {\ if (++arg >= argc) ERROR(ERR_TO_FEW_ARGS); \ carg = (argv + arg);\ } #define SAVE_CHAR(pointer) {\ savechars *new;\ new = (savechars *)xalloc(sizeof(savechars));\ 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 */ jmp_buf error_jmp; /* for error longjmp()s */ int call_master_ob; /* should the master object be called for the * name of an object? */ /* * Probably should make this a #define... */ void stradd(char **dst, int *size, char *add) { int i; if ((i = (strlen(*dst) + strlen(add))) >= *size) { *size += i + 1; *dst = (char *) realloc(*dst, *size); } strcat(*dst, add); } /* end of stradd() */ void numadd(char **dst, int *size, int num) { int i, j, nve; if (num < 0) { num *= -1; nve = 1; } else nve = 0; for (i = 10, j = 0; num >= i; i *= 10, j++) ; i = strlen(*dst) + nve; if ((i + j) >= *size) { *size += i + j + 1; *dst = (char *) realloc(*dst, *size); } if (nve) (*dst)[i - 1] = '-'; (*dst)[i + j + 1] = '\0'; for ( ; j >= 0; j--, num /= 10) (*dst)[i + j] = (num % 10) + '0'; } /* end of num_add() */ /* * 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 *) realloc(*dst, *size); } 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(struct svalue *obj, char **str, int size, int indent, int trailing) { int i; if (indent >= 0) add_indent(str, &size, indent); else indent = -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); break; case T_NUMBER: numadd(str, &size, obj->u.number); break; case T_FLOAT: { char buffer[1024]; sprintf(buffer,"%8e",obj->u.real); stradd(str,&size,buffer); } 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); svalue_to_string(&(obj->u.vec->item[i]), str, size, indent + 2, 0); stradd(str, &size, " })"); } break; case T_OBJECT: { struct svalue *temp; stradd(str, &size, obj->u.ob->name); if (call_master_ob) { push_object(obj->u.ob); temp = apply_master_ob(M_OBJECT_NAME, 1); if (temp && (temp->type == T_STRING)) { stradd(str, &size, " (\""); stradd(str, &size, temp->u.string); stradd(str, &size, "\")"); } } break; } case T_MAPPING: if (!(obj->u.map->size)) { stradd(str, &size, "([ ])"); } else { struct apair *pair = 0; int j; stradd(str, &size, "([ /* sizeof() == "); numadd(str, &size, obj->u.map->size); stradd(str, &size, " */\n"); for (j = obj->u.map->size - 1; obj->u.map->pairs[j] == 0 && j >= 0; j--); for (i = 0; i <= j; i++) { for (pair = obj->u.map->pairs[i]; (i != j) ? pair : pair->next; pair = pair->next) { svalue_to_string(&(pair->arg), str, size, indent + 2, 0); stradd(str, &size, " : "); svalue_to_string(&(pair->val), str, size, -indent - 2, 1); } } if (pair) { svalue_to_string(&(pair->arg), str, size, indent + 2, 0); stradd(str, &size, " : "); svalue_to_string(&(pair->val), str, size, -indent - 2, 0); } stradd(str, &size, " ])"); } break; default: stradd(str, &size, "!ERROR: GARBAGE SVALUE!"); } /* end of switch (obj->type) */ if (trailing) stradd(str, &size, ",\n"); } /* end of svalue_to_string() */ /* * 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(char *str, char *pad, int fs, format_info finfo, short trailing) { int i, len; len = strlen(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); j = (fs - len) / 2 + (fs - len) % 2; for (i = 0; i < j; i++) { if (pad[i % len] == '\\') { i++; j++; } ADD_CHAR(pad[i % l]); } for (i = 0; i < len; i++) ADD_CHAR(str[i]); j = (fs - len) / 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 -= len; 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(cst **column, short int trailing) { register unsigned int done; unsigned int save; for (done = 0; (done < (*column)->pres) && ((*column)->d.col)[done] && (((*column)->d.col)[done] != '\n'); done++) ; if (((*column)->d.col)[done] && (((*column)->d.col)[done] != '\n')) { save = done; for ( ; done && (((*column)->d.col)[done] != ' '); done--) ; /* * handle larger than column size words... */ if (!done) done = save; } save = ((*column)->d.col)[done]; ((*column)->d.col)[done] = '\0'; add_justified(((*column)->d.col), (*column)->pad, (*column)->size, (*column)->info, (trailing || ((*column)->next))); ((*column)->d.col)[done] = save; ((*column)->d.col) += done; /* incremented below ... */ /* * if this or the next character is a NULL then take this column out * of the list. */ if (!(*((*column)->d.col)) || !(*(++((*column)->d.col)))) { cst *temp; int ret; if (*(((*column)->d.col) - 1) == '\n') ret = 2; else ret = 1; temp = (*column)->next; free((char *)(*column)); (*column) = 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(cst **table, short int trailing) { char save; register unsigned int done, i; #define TAB (*table) for (i = 0; i < TAB->nocols && (TAB->d.tab[i]); i++) { for (done = 0; ((TAB->d.tab[i])[done]) && ((TAB->d.tab[i])[done] != '\n'); done++) ; save = (TAB->d.tab[i])[done]; (TAB->d.tab[i])[done] = '\0'; add_justified((TAB->d.tab[i]), TAB->pad, TAB->size, TAB->info, (trailing || (i < TAB->nocols-1) || (TAB->next))); (TAB->d.tab[i])[done] = save; (TAB->d.tab[i]) += done; /* incremented next line ... */ if (!(*(TAB->d.tab[i])) || !(*(++(TAB->d.tab[i])))) (TAB->d.tab[i]) = 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(int call_master, char *format_str, int argc, struct svalue *argv) { format_info finfo; savechars *saves; /* chars to restore */ cst *csts; /* list of columns/tables to be done */ struct svalue *carg; /* current arg */ unsigned int nelemno; /* next offset into array */ unsigned int fpos; /* position in format_str */ unsigned int arg; /* current arg number */ unsigned int fs; /* field size */ int pres; /* presision */ unsigned int i; char *pad; /* fs pad string */ call_master_ob = call_master; if ((i = setjmp(error_jmp))) { /* error handling */ char *err; 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; default: #ifdef RETURN_ERROR_MESSAGES sprintf(buff, "ERROR: (s)printf(): !feature - undefined error 0x%X !\n", i); fprintf(stderr, "%s:%s: %s", current_object->name, get_srccode_position_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\n", err); fprintf(stderr, "%s:%s: %s", current_object->name, get_srccode_position_if_any(), buff); return buff; #else error("ERROR: (s)printf(): %s\n", err); #endif /* RETURN_ERROR_MESSAGES */ } arg = -1; bpos = 0; curpos = 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; continue; } ADD_CHAR('\n'); curpos = 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'); 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 '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->u.vec->size == 0) { fpos--; /* About 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 *)xalloc(sizeof(struct svalue)); clean->type = T_STRING; clean->string_type = STRING_MALLOC; clean->u.string = (char *)xalloc(512); clean->u.string[0] = '\0'; svalue_to_string(carg, &(clean->u.string), 500, 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; 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 *)xalloc(sizeof(cst)); (*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) */ unsigned int n, len, max; #define TABLE carg->u.string (*temp) = (cst *)xalloc(sizeof(cst)); (*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 **)xalloc(pres*sizeof(char *)); (*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) { 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[4]; 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_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 (carg->type != T_NUMBER) { /* sigh... */ #ifdef RETURN_ERROR_MESSAGES sprintf(buff, "ERROR: (s)printf(): incorrect argument type to %%%c.\n", cheat[i-1]); fprintf(stderr, "%s:%s: %s", current_object->name, get_srccode_position_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'; 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); free((char *)clean); clean = 0; } 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() */