524 lines
12 KiB
C
524 lines
12 KiB
C
|
|
/********************************************
|
|
print.c
|
|
copyright 1992, 1991. 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.2 1993/07/02 23:57:48 jtc
|
|
/* Updated to mawk 1.1.4
|
|
/*
|
|
* 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 *)) ;
|
|
|
|
|
|
|
|
/* 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, SIZE_T(1), SIZE_T(len), fp) ;
|
|
}
|
|
break ;
|
|
|
|
case C_DOUBLE :
|
|
{
|
|
long ival = d_to_l(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) ;
|
|
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 */
|
|
#if HAVE_PROTOS
|
|
typedef int (*PRINTER)(PTR,char *,...) ;
|
|
#else
|
|
typedef int (*PRINTER)() ;
|
|
#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 ;
|
|
CELL *cp ; /* ptr to an array of arguments ( on the eval stack) */
|
|
unsigned argcnt ; /* number of args on 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] ;
|
|
long lval ; /* hold integer values */
|
|
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 ) 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_STRING((char*)0, len) ;
|
|
(void)memcpy(retval->str, sprintf_buff, SIZE_T(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 :
|
|
lval = 0 ;
|
|
break ;
|
|
|
|
case C_STRNUM :
|
|
case C_DOUBLE :
|
|
lval = (long) d_to_i(cp->dval) ;
|
|
break ;
|
|
|
|
case C_STRING :
|
|
lval = string(cp)->str[0] ;
|
|
break ;
|
|
|
|
case C_MBSTRN :
|
|
check_strnum(cp) ;
|
|
lval = cp->type == C_STRING ?
|
|
string(cp)->str[0] : (long) 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) ;
|
|
if ( cp->dval > MAX__LONG ) lval = MAX__LONG ;
|
|
else
|
|
if ( cp->dval > -MAX__LONG ) lval = (long) cp->dval ;
|
|
else lval = -MAX__LONG ;
|
|
|
|
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 )
|
|
{
|
|
lval = (short) lval ;
|
|
/* replace the 'h' with 'l' (really!) */
|
|
xbuff[k-2] = 'l' ;
|
|
if ( xbuff[k-1] != 'd' && xbuff[k-1] != 'i' ) lval &= 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) lval) ;
|
|
break ;
|
|
|
|
case AST(1, PF_C ) :
|
|
(*printer)((PTR) target, p, ast[0], (int) lval) ;
|
|
break ;
|
|
|
|
case AST(2, PF_C ) :
|
|
(*printer)((PTR) target, p, ast[0], ast[1], (int)lval) ;
|
|
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, lval) ;
|
|
break ;
|
|
|
|
case AST(1, PF_D) :
|
|
(*printer)((PTR) target, FMT, ast[0], lval) ;
|
|
break ;
|
|
|
|
case AST(2, PF_D) :
|
|
(*printer)((PTR) target, FMT, ast[0], ast[1], lval) ;
|
|
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 ;
|
|
}
|
|
|
|
|