/******************************************** print.c copyright 1991-1993. Michael D. Brennan This is a source file for mawk, an implementation of the AWK programming language. Mawk is distributed without warranty under the terms of the GNU General Public License, version 2, 1991. ********************************************/ /* $Log: print.c,v $ * Revision 1.7 1996/09/18 01:04:36 mike * Check ferror() after print and printf. * * Revision 1.6 1995/10/13 16:56:45 mike * Some assumptions that int==long were still in do_printf -- now removed. * * Revision 1.5 1995/06/18 19:17:50 mike * Create a type Int which on most machines is an int, but on machines * with 16bit ints, i.e., the PC is a long. This fixes implicit assumption * that int==long. * * Revision 1.4 1994/10/08 19:15:50 mike * remove SM_DOS * * Revision 1.3 1993/07/15 23:38:19 mike * SIZE_T and indent * * Revision 1.2 1993/07/07 00:07:50 mike * more work on 1.2 * * Revision 1.1.1.1 1993/07/03 18:58:18 mike * move source to cvs * * Revision 5.6 1993/02/13 21:57:30 mike * merge patch3 * * Revision 5.5 1993/01/01 21:30:48 mike * split new_STRING() into new_STRING and new_STRING0 * * Revision 5.4.1.2 1993/01/20 12:53:11 mike * d_to_l() * * Revision 5.4.1.1 1993/01/15 03:33:47 mike * patch3: safer double to int conversion * * Revision 5.4 1992/11/29 18:03:11 mike * when printing integers, convert doubles to * longs so output is the same on 16bit systems as 32bit systems * * Revision 5.3 1992/08/17 14:23:21 brennan * patch2: After parsing, only bi_sprintf() uses string_buff. * * Revision 5.2 1992/02/24 10:52:16 brennan * printf and sprintf() can now have more args than % conversions * removed HAVE_PRINTF_HD -- it was too obscure * * Revision 5.1 91/12/05 07:56:22 brennan * 1.1 pre-release * */ #include "mawk.h" #include "bi_vars.h" #include "bi_funct.h" #include "memory.h" #include "field.h" #include "scan.h" #include "files.h" static void PROTO(print_cell, (CELL *, FILE *)) ; static STRING *PROTO(do_printf, (FILE *, char *, unsigned, CELL *)) ; static void PROTO(bad_conversion, (int, char *, char *)) ; static void PROTO(write_error,(void)) ; /* prototyping fprintf() or sprintf() is a loser as ellipses will always cause problems with ansi compilers depending on what they've already seen, but we need them here and sometimes they are missing */ #ifdef NO_FPRINTF_IN_STDIO int PROTO(fprintf, (FILE *, const char *,...)) ; #endif #ifdef NO_SPRINTF_IN_STDIO int PROTO(sprintf, (char *, const char *,...)) ; #endif /* this can be moved and enlarged by -W sprintf=num */ char *sprintf_buff = string_buff ; char *sprintf_limit = string_buff + SPRINTF_SZ ; /* Once execute() starts the sprintf code is (belatedly) the only code allowed to use string_buff */ static void print_cell(p, fp) register CELL *p ; register FILE *fp ; { int len ; switch (p->type) { case C_NOINIT: break ; case C_MBSTRN: case C_STRING: case C_STRNUM: switch (len = string(p)->len) { case 0: break ; case 1: putc(string(p)->str[0], fp) ; break ; default: fwrite(string(p)->str, 1, len, fp) ; } break ; case C_DOUBLE: { Int ival = d_to_I(p->dval) ; /* integers print as "%[l]d" */ if ((double) ival == p->dval) fprintf(fp, INT_FMT, ival) ; else fprintf(fp, string(OFMT)->str, p->dval) ; } break ; default: bozo("bad cell passed to print_cell") ; } } /* on entry to bi_print or bi_printf the stack is: sp[0] = an integer k if ( k < 0 ) output is to a file with name in sp[-1] { so open file and sp -= 2 } sp[0] = k >= 0 is the number of print args sp[-k] holds the first argument */ CELL * bi_print(sp) CELL *sp ; /* stack ptr passed in */ { register CELL *p ; register int k ; FILE *fp ; k = sp->type ; if (k < 0) { /* k holds redirection */ if ((--sp)->type < C_STRING) cast1_to_s(sp) ; fp = (FILE *) file_find(string(sp), k) ; free_STRING(string(sp)) ; k = (--sp)->type ; /* k now has number of arguments */ } else fp = stdout ; if (k) { p = sp - k ; /* clear k variables off the stack */ sp = p - 1 ; k-- ; while (k > 0) { print_cell(p,fp) ; print_cell(OFS,fp) ; cell_destroy(p) ; p++ ; k-- ; } print_cell(p, fp) ; cell_destroy(p) ; } else { /* print $0 */ sp-- ; print_cell(&field[0], fp) ; } print_cell(ORS, fp) ; if (ferror(fp)) write_error() ; return sp ; } /*---------- types and defs for doing printf and sprintf----*/ #define PF_C 0 /* %c */ #define PF_S 1 /* %s */ #define PF_D 2 /* int conversion */ #define PF_F 3 /* float conversion */ /* for switch on number of '*' and type */ #define AST(num,type) ((PF_F+1)*(num)+(type)) /* some picky ANSI compilers go berserk without this */ #ifdef NO_PROTOS typedef int (*PRINTER) () ; #else typedef int (*PRINTER) (PTR, const char *,...) ; #endif /*-------------------------------------------------------*/ static void bad_conversion(cnt, who, format) int cnt ; char *who, *format ; { rt_error("improper conversion(number %d) in %s(\"%s\")", cnt, who, format) ; } /* the contents of format are preserved, caller does CELL cleanup This routine does both printf and sprintf (if fp==0) */ static STRING * do_printf(fp, format, argcnt, cp) FILE *fp ; char *format ; unsigned argcnt ; /* number of args on eval stack */ CELL *cp ; /* ptr to an array of arguments (on the eval stack) */ { char save ; char *p ; register char *q = format ; register char *target ; int l_flag, h_flag ; /* seen %ld or %hd */ int ast_cnt ; int ast[2] ; Int Ival ; int num_conversion = 0 ; /* for error messages */ char *who ; /*ditto*/ int pf_type ; /* conversion type */ PRINTER printer ; /* pts at fprintf() or sprintf() */ #ifdef SHORT_INTS char xbuff[256] ; /* splice in l qualifier here */ #endif if (fp == (FILE *) 0) /* doing sprintf */ { target = sprintf_buff ; printer = (PRINTER) sprintf ; who = "sprintf" ; } else /* doing printf */ { target = (char *) fp ; /* will never change */ printer = (PRINTER) fprintf ; who = "printf" ; } while (1) { if (fp) /* printf */ { while (*q != '%') { if (*q == 0) { if (ferror(fp)) write_error() ; /* return is ignored */ return (STRING *) 0 ; } else { putc(*q,fp) ; q++ ; } } } else /* sprintf */ { while (*q != '%') if (*q == 0) { if (target > sprintf_limit) /* damaged */ { /* hope this works */ rt_overflow("sprintf buffer", sprintf_limit - sprintf_buff) ; } else /* really done */ { STRING *retval ; int len = target - sprintf_buff ; retval = new_STRING0(len) ; memcpy(retval->str, sprintf_buff, len) ; return retval ; } } else *target++ = *q++ ; } /* *q == '%' */ num_conversion++ ; if (*++q == '%') /* %% */ { if (fp) putc(*q, fp) ; else *target++ = *q ; q++ ; continue ; } /* mark the '%' with p */ p = q - 1 ; /* eat the flags */ while (*q == '-' || *q == '+' || *q == ' ' || *q == '#' || *q == '0') q++ ; ast_cnt = 0 ; if (*q == '*') { if (cp->type != C_DOUBLE) cast1_to_d(cp) ; ast[ast_cnt++] = d_to_i(cp++->dval) ; argcnt-- ; q++ ; } else while (scan_code[*(unsigned char *) q] == SC_DIGIT) q++ ; /* width is done */ if (*q == '.') /* have precision */ { q++ ; if (*q == '*') { if (cp->type != C_DOUBLE) cast1_to_d(cp) ; ast[ast_cnt++] = d_to_i(cp++->dval) ; argcnt-- ; q++ ; } else while (scan_code[*(unsigned char *) q] == SC_DIGIT) q++ ; } if (argcnt <= 0) rt_error("not enough arguments passed to %s(\"%s\")", who, format) ; l_flag = h_flag = 0 ; if (*q == 'l') { q++ ; l_flag = 1 ; } else if (*q == 'h') { q++ ; h_flag = 1 ; } switch (*q++) { case 's': if (l_flag + h_flag) bad_conversion(num_conversion, who, format) ; if (cp->type < C_STRING) cast1_to_s(cp) ; pf_type = PF_S ; break ; case 'c': if (l_flag + h_flag) bad_conversion(num_conversion, who, format) ; switch (cp->type) { case C_NOINIT: Ival = 0 ; break ; case C_STRNUM: case C_DOUBLE: Ival = d_to_I(cp->dval) ; break ; case C_STRING: Ival = string(cp)->str[0] ; break ; case C_MBSTRN: check_strnum(cp) ; Ival = cp->type == C_STRING ? string(cp)->str[0] : d_to_I(cp->dval) ; break ; default: bozo("printf %c") ; } pf_type = PF_C ; break ; case 'd': case 'o': case 'x': case 'X': case 'i': case 'u': if (cp->type != C_DOUBLE) cast1_to_d(cp) ; Ival = d_to_I(cp->dval) ; pf_type = PF_D ; break ; case 'e': case 'g': case 'f': case 'E': case 'G': if (h_flag + l_flag) bad_conversion(num_conversion, who, format) ; if (cp->type != C_DOUBLE) cast1_to_d(cp) ; pf_type = PF_F ; break ; default: bad_conversion(num_conversion, who, format) ; } save = *q ; *q = 0 ; #ifdef SHORT_INTS if (pf_type == PF_D) { /* need to splice in long modifier */ strcpy(xbuff, p) ; if (l_flag) /* do nothing */ ; else { int k = q - p ; if (h_flag) { Ival = (short) Ival ; /* replace the 'h' with 'l' (really!) */ xbuff[k - 2] = 'l' ; if (xbuff[k - 1] != 'd' && xbuff[k - 1] != 'i') Ival &= 0xffff ; } else { /* the usual case */ xbuff[k] = xbuff[k - 1] ; xbuff[k - 1] = 'l' ; xbuff[k + 1] = 0 ; } } } #endif /* ready to call printf() */ switch (AST(ast_cnt, pf_type)) { case AST(0, PF_C): (*printer) ((PTR) target, p, (int) Ival) ; break ; case AST(1, PF_C): (*printer) ((PTR) target, p, ast[0], (int) Ival) ; break ; case AST(2, PF_C): (*printer) ((PTR) target, p, ast[0], ast[1], (int) Ival) ; break ; case AST(0, PF_S): (*printer) ((PTR) target, p, string(cp)->str) ; break ; case AST(1, PF_S): (*printer) ((PTR) target, p, ast[0], string(cp)->str) ; break ; case AST(2, PF_S): (*printer) ((PTR) target, p, ast[0], ast[1], string(cp)->str) ; break ; #ifdef SHORT_INTS #define FMT xbuff /* format in xbuff */ #else #define FMT p /* p -> format */ #endif case AST(0, PF_D): (*printer) ((PTR) target, FMT, Ival) ; break ; case AST(1, PF_D): (*printer) ((PTR) target, FMT, ast[0], Ival) ; break ; case AST(2, PF_D): (*printer) ((PTR) target, FMT, ast[0], ast[1], Ival) ; break ; #undef FMT case AST(0, PF_F): (*printer) ((PTR) target, p, cp->dval) ; break ; case AST(1, PF_F): (*printer) ((PTR) target, p, ast[0], cp->dval) ; break ; case AST(2, PF_F): (*printer) ((PTR) target, p, ast[0], ast[1], cp->dval) ; break ; } if (fp == (FILE *) 0) while (*target) target++ ; *q = save ; argcnt-- ; cp++ ; } } CELL * bi_printf(sp) register CELL *sp ; { register int k ; register CELL *p ; FILE *fp ; k = sp->type ; if (k < 0) { /* k has redirection */ if ((--sp)->type < C_STRING) cast1_to_s(sp) ; fp = (FILE *) file_find(string(sp), k) ; free_STRING(string(sp)) ; k = (--sp)->type ; /* k is now number of args including format */ } else fp = stdout ; sp -= k ; /* sp points at the format string */ k-- ; if (sp->type < C_STRING) cast1_to_s(sp) ; do_printf(fp, string(sp)->str, k, sp + 1); free_STRING(string(sp)) ; /* cleanup arguments on eval stack */ for (p = sp + 1; k; k--, p++) cell_destroy(p) ; return --sp ; } CELL * bi_sprintf(sp) CELL *sp ; { CELL *p ; int argcnt = sp->type ; STRING *sval ; sp -= argcnt ; /* sp points at the format string */ argcnt-- ; if (sp->type != C_STRING) cast1_to_s(sp) ; sval = do_printf((FILE *) 0, string(sp)->str, argcnt, sp + 1) ; free_STRING(string(sp)) ; sp->ptr = (PTR) sval ; /* cleanup */ for (p = sp + 1; argcnt; argcnt--, p++) cell_destroy(p) ; return sp ; } static void write_error() { errmsg(errno, "write failure") ; mawk_exit(2) ; }