/********************************************
print.c
libmawk changes (C) 2009-2010, Tibor 'Igor2' Palinkas;
based on mawk code coming with the below copyright:
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.
********************************************/
#include "mawk.h"
#include <stdio.h>
#include <stdarg.h>
#include "bi_vars.h"
#include "bi_funct.h"
#include "memory.h"
#include "field.h"
#include "scan.h"
#include "files.h"
#include "vio.h"
#include "cell.h"
static mawk_string_t *do_printf(mawk_state_t *, FILE_NODE *, char *, unsigned, mawk_cell_t *);
static void bad_conversion(mawk_state_t *, int, char *, char *);
static void write_error(mawk_state_t * MAWK);
/* 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 fprintf(FILE *, const char *, ...);
#endif
#ifdef NO_SPRINTF_IN_STDIO
int sprintf(char *, const char *, ...);
#endif
/* Once mawk_execute() starts the sprintf code is (belatedly) the only
code allowed to use string_buff */
void mawk_print_cell(mawk_state_t *MAWK, register mawk_cell_t *p, register FILE_NODE *fnode)
{
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:
mawk_vio_putc(MAWK, fnode->vf, string(p)->str[0]);
break;
default:
mawk_vio_write(MAWK, fnode->vf, string(p)->str, len);
}
break;
case C_NUM:
{
Int ival = mawk_d_to_I(p->d.dval);
const char *txt;
if (P_isnan(p->d.dval)) {
mawk_vio_write_str(MAWK, fnode->vf, "nan");
break;
}
txt = mawk_num_print_spec(ival);
if (txt != NULL)
mawk_vio_write_str(MAWK, fnode->vf, txt);
else if ((mawk_num_t) ival == p->d.dval) { /* integers print as "%[l]d" */
char buff[64];
int len;
len = sprintf(buff, INT_FMT, ival);
mawk_vio_write(MAWK, fnode->vf, buff, len);
}
else {
fnode->vf->imp->vprintf(MAWK, fnode->vf, string(MAWK_OFMT)->str, p->d.dval);
}
}
break;
default:
mawk_bozo(MAWK, "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
*/
mawk_cell_t *mawk_bi_print(mawk_state_t *MAWK, mawk_cell_t *sp)
{
/* sp is stack ptr passed in */
register mawk_cell_t *p;
register int k;
FILE_NODE *fnode;
k = sp->type;
if (k < 0) {
/* k holds redirection */
if ((--sp)->type < C_STRING)
mawk_cast1_to_str(MAWK, sp);
fnode = mawk_file_find(MAWK, string(sp), k, 1);
free_STRING(string(sp));
k = (--sp)->type;
/* k now has number of arguments */
}
else
fnode = MAWK->fnode_stdout;
if (k) {
p = sp - k; /* clear k variables off the stack */
sp = p - 1;
k--;
while (k > 0) {
if (fnode != NULL) {
mawk_print_cell(MAWK, p, fnode);
mawk_print_cell(MAWK, OFS, fnode);
}
mawk_cell_destroy(MAWK, p);
p++;
k--;
}
if (fnode != NULL)
mawk_print_cell(MAWK, p, fnode);
mawk_cell_destroy(MAWK, p);
}
else { /* print $0 */
sp--;
if (fnode != NULL)
mawk_print_cell(MAWK, &MAWK->field[0], fnode);
}
if (fnode != NULL) {
mawk_print_cell(MAWK, ORS, fnode);
if (mawk_vio_error(MAWK, fnode->vf))
write_error(MAWK);
}
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))
typedef int (*PRINTER) (PTR, const char *, ...);
/*-------------------------------------------------------*/
static void bad_conversion(mawk_state_t *MAWK, int cnt, char *who, char *format)
{
mawk_rt_error(MAWK, "improper conversion(number %d) in %s(\"%s\")", cnt, who, format);
}
int sprintf_wrapper(mawk_state_t *MAWK, char *ostr, const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = vsprintf(ostr, fmt, ap);
va_end(ap);
return ret;
}
/* the contents of format are preserved,
caller does mawk_cell_t cleanup
This routine does both printf and sprintf (if fp==0)
*/
static mawk_string_t *do_printf(mawk_state_t *MAWK, FILE_NODE *fnode, char *format, unsigned argcnt, mawk_cell_t *cp)
{
/* argcnt number of args on eval stack */
/* mawk_cell_t *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 (fnode == NULL) { /* doing sprintf */
target = MAWK->sprintf_buff;
printer = (PRINTER) sprintf_wrapper;
who = "sprintf";
}
else { /* doing printf */
target = (char *) fnode->vf; /* will never change */
printer = (PRINTER) fnode->vf->imp->vprintf;
who = "printf";
}
while (1) {
if (fnode) { /* printf */
while (*q != '%') {
if (*q == 0) {
if (mawk_vio_error(MAWK, fnode->vf))
write_error(MAWK);
/* return is ignored */
return (mawk_string_t *) 0;
}
else {
mawk_vio_putc(MAWK, fnode->vf, *q);
q++;
}
}
}
else { /* sprintf */
while (*q != '%')
if (*q == 0) {
if (target > MAWK->sprintf_limit) { /* mawk_damaged */
/* hope this works */
mawk_rt_overflow(MAWK, "sprintf buffer", MAWK->sprintf_limit - MAWK->sprintf_buff);
}
else { /* really done */
mawk_string_t *retval;
int len = target - MAWK->sprintf_buff;
retval = mawk_new_STRING0(MAWK, len);
memcpy(retval->str, MAWK->sprintf_buff, len);
return retval;
}
}
else
*target++ = *q++;
}
/* *q == '%' */
num_conversion++;
if (*++q == '%') { /* %% */
if (fnode)
mawk_vio_putc(MAWK, fnode->vf, *q);
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_NUM)
mawk_cast1_to_num(MAWK, cp);
ast[ast_cnt++] = d_to_i(cp++->d.dval);
argcnt--;
q++;
}
else
while (MAWK->scan_code[*(unsigned char *) q] == SC_DIGIT)
q++;
/* width is done */
if (*q == '.') { /* have precision */
q++;
if (*q == '*') {
if (cp->type != C_NUM)
mawk_cast1_to_num(MAWK, cp);
ast[ast_cnt++] = d_to_i(cp++->d.dval);
argcnt--;
q++;
}
else
while (MAWK->scan_code[*(unsigned char *) q] == SC_DIGIT)
q++;
}
if (argcnt <= 0)
mawk_rt_error(MAWK, "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(MAWK, num_conversion, who, format);
if (cp->type < C_STRING)
mawk_cast1_to_str(MAWK, cp);
pf_type = PF_S;
break;
case 'c':
if (l_flag + h_flag)
bad_conversion(MAWK, num_conversion, who, format);
switch (cp->type) {
case C_NOINIT:
Ival = 0;
break;
case C_STRNUM:
case C_NUM:
Ival = mawk_d_to_I(cp->d.dval);
break;
case C_STRING:
Ival = string(cp)->str[0];
break;
case C_MBSTRN:
mawk_check_strnum(MAWK, cp);
Ival = cp->type == C_STRING ? string(cp)->str[0] : mawk_d_to_I(cp->d.dval);
break;
default:
mawk_bozo(MAWK, "printf %c");
}
pf_type = PF_C;
break;
case 'd':
case 'o':
case 'x':
case 'X':
case 'i':
case 'u':
{
const char *txt;
if (cp->type != C_NUM)
mawk_cast1_to_num(MAWK, cp);
#ifdef MAWK_PRINTF_INFNAN
txt = mawk_num_print_spec(cp->d.dval);
if (txt == NULL) {
#endif
Ival = mawk_d_to_I(cp->d.dval);
pf_type = PF_D;
#ifdef MAWK_PRINTF_INFNAN
}
else {
cp->ptr = (PTR) mawk_new_STRING(MAWK, txt);
cp->type = C_STRING;
cp->d.dval = 42;
p = "%s";
pf_type = PF_S;
}
#endif
}
break;
#ifndef MAWK_NO_FLOAT
case 'e':
case 'g':
case 'f':
case 'E':
case 'G':
if (h_flag + l_flag)
bad_conversion(MAWK, num_conversion, who, format);
if (cp->type != C_NUM)
mawk_cast1_to_num(MAWK, cp);
if (P_isnan(cp->d.dval)) {
cp->ptr = (PTR) mawk_new_STRING(MAWK, "nan");
cp->type = C_STRING;
cp->d.dval = 42;
p = "%s";
pf_type = PF_S;
}
else
pf_type = PF_F;
break;
#endif
default:
bad_conversion(MAWK, 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) (MAWK, (PTR) target, p, (int) Ival);
break;
case AST(1, PF_C):
(*printer) (MAWK, (PTR) target, p, ast[0], (int) Ival);
break;
case AST(2, PF_C):
(*printer) (MAWK, (PTR) target, p, ast[0], ast[1], (int) Ival);
break;
case AST(0, PF_S):
(*printer) (MAWK, (PTR) target, p, string(cp)->str);
break;
case AST(1, PF_S):
(*printer) (MAWK, (PTR) target, p, ast[0], string(cp)->str);
break;
case AST(2, PF_S):
(*printer) (MAWK, (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) (MAWK, (PTR) target, FMT, Ival);
break;
case AST(1, PF_D):
(*printer) (MAWK, (PTR) target, FMT, ast[0], Ival);
break;
case AST(2, PF_D):
(*printer) (MAWK, (PTR) target, FMT, ast[0], ast[1], Ival);
break;
#undef FMT
case AST(0, PF_F):
(*printer) (MAWK, (PTR) target, p, cp->d.dval);
break;
case AST(1, PF_F):
(*printer) (MAWK, (PTR) target, p, ast[0], cp->d.dval);
break;
case AST(2, PF_F):
(*printer) (MAWK, (PTR) target, p, ast[0], ast[1], cp->d.dval);
break;
}
if (fnode == NULL)
while (*target)
target++;
*q = save;
argcnt--;
cp++;
}
}
mawk_cell_t *mawk_bi_printf(mawk_state_t *MAWK, register mawk_cell_t *sp)
{
register int k;
register mawk_cell_t *p;
FILE_NODE *fnode;
k = sp->type;
if (k < 0) {
/* k has redirection */
if ((--sp)->type < C_STRING)
mawk_cast1_to_str(MAWK, sp);
fnode = mawk_file_find(MAWK, string(sp), k, 1);
free_STRING(string(sp));
k = (--sp)->type;
/* k is now number of args including format */
}
else
fnode = MAWK->fnode_stdout;
sp -= k; /* sp points at the format string */
k--;
if (sp->type < C_STRING)
mawk_cast1_to_str(MAWK, sp);
do_printf(MAWK, fnode, string(sp)->str, k, sp + 1);
free_STRING(string(sp));
/* cleanup arguments on eval stack */
for (p = sp + 1; k; k--, p++)
mawk_cell_destroy(MAWK, p);
return --sp;
}
mawk_cell_t *mawk_bi_sprintf(mawk_state_t *MAWK, mawk_cell_t *sp)
{
mawk_cell_t *p;
int argcnt = sp->type;
mawk_string_t *sval;
sp -= argcnt; /* sp points at the format string */
argcnt--;
if (sp->type != C_STRING)
mawk_cast1_to_str(MAWK, sp);
sval = do_printf(MAWK, NULL, string(sp)->str, argcnt, sp + 1);
free_STRING(string(sp));
sp->ptr = (PTR) sval;
/* cleanup */
for (p = sp + 1; argcnt; argcnt--, p++)
mawk_cell_destroy(MAWK, p);
return sp;
}
static void write_error(mawk_state_t * MAWK)
{
mawk_errmsg(MAWK, errno, "write failure");
mawk_exit(MAWK, 2);
}