freebsd-dev/contrib/awk/profile.c
2001-11-02 21:06:08 +00:00

1382 lines
26 KiB
C

/*
* profile.c - gawk parse tree pretty-printer with counts
*/
/*
* Copyright (C) 1999-2001 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Programming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "awk.h"
/* where to place redirections for getline, print, printf */
enum redir_placement {
BEFORE = 0,
AFTER = 1
};
#undef tree_eval
static void tree_eval P((NODE *tree));
static void parenthesize P((NODETYPE parent_type, NODE *tree));
static void eval_condition P((NODE *tree));
static void pp_op_assign P((NODE *tree));
static void pp_func_call P((NODE *name, NODE *arg_list));
static void pp_match_op P((NODE *tree));
static void pp_lhs P((NODE *ptr));
static void pp_print_stmt P((const char *command, NODE *tree));
static void pp_delete P((NODE *tree));
static void pp_in_array P((NODE *array, NODE *subscript));
static void pp_getline P((NODE *tree));
static void pp_builtin P((NODE *tree));
static void pp_list P((NODE *tree));
static void pp_string P((char *str, size_t len, int delim));
static int is_scalar P((NODETYPE type));
static int prec_level P((NODETYPE type));
#ifdef PROFILING
static RETSIGTYPE dump_and_exit P((int signum));
static RETSIGTYPE just_dump P((int signum));
#endif
/* pretty printing related functions and variables */
static char **fparms; /* function parameter names */
static FILE *prof_fp; /* where to send the profile */
static long indent_level = 0;
static int in_BEGIN_or_END = FALSE;
static int in_expr = FALSE;
#define SPACEOVER 0
/* init_profiling --- do needed initializations, see also main.c */
void
init_profiling(int *flag, const char *def_file)
{
/* run time init avoids glibc innovations */
prof_fp = stderr;
#ifdef PROFILING
if (*flag == FALSE) {
*flag = TRUE;
set_prof_file(def_file);
}
#endif
}
/* set_prof_file --- set the output file for profiling */
void
set_prof_file(const char *file)
{
assert(file != NULL);
prof_fp = fopen(file, "w");
if (prof_fp == NULL) {
warning(_("could not open `%s' for writing: %s"),
file, strerror(errno));
warning(_("sending profile to standard error"));
prof_fp = stderr;
}
}
void
init_profiling_signals()
{
#ifdef PROFILING
#ifdef SIGHUP
signal(SIGHUP, dump_and_exit);
#endif
#ifdef SIGUSR1
signal(SIGUSR1, just_dump);
#endif
#endif
}
/* indent --- print out enough tabs */
static void
indent(long count)
{
int i;
if (count == 0)
putc('\t', prof_fp);
else
fprintf(prof_fp, "%6ld ", count);
assert(indent_level >= 0);
for (i = 0; i < indent_level; i++)
putc('\t', prof_fp);
}
/* indent_in --- increase the level, with error checking */
static void
indent_in()
{
assert(indent_level >= 0);
indent_level++;
}
/* indent_out --- decrease the level, with error checking */
static void
indent_out()
{
indent_level--;
assert(indent_level >= 0);
}
/*
* pprint:
* Tree is a bunch of rules to run. Returns zero if it hit an exit()
* statement
*/
static void
pprint(register NODE *volatile tree)
{
register NODE *volatile t = NULL; /* temporary */
int volatile traverse = TRUE; /* True => loop thru tree (Node_rule_list) */
/* avoid false source indications */
source = NULL;
sourceline = 0;
if (tree == NULL)
return;
sourceline = tree->source_line;
source = tree->source_file;
switch (tree->type) {
case Node_rule_node:
traverse = FALSE; /* False => one for-loop iteration only */
/* FALL THROUGH */
case Node_rule_list:
for (t = tree; t != NULL; t = t->rnode) {
if (traverse)
tree = t->lnode;
sourceline = tree->source_line;
source = tree->source_file;
if (! in_BEGIN_or_END)
indent(tree->exec_count);
if (tree->lnode) {
eval_condition(tree->lnode);
if (tree->rnode)
fprintf(prof_fp, "\t");
}
if (tree->rnode) {
if (! in_BEGIN_or_END) {
fprintf(prof_fp, "{");
if (tree->lnode != NULL
&& tree->lnode->exec_count)
fprintf(prof_fp, " # %ld",
tree->lnode->exec_count);
fprintf(prof_fp, "\n");
}
indent_in();
pprint(tree->rnode);
indent_out();
if (! in_BEGIN_or_END) {
indent(SPACEOVER);
fprintf(prof_fp, "}\n");
}
}
if (! traverse) /* case Node_rule_node */
break; /* don't loop */
if (t->rnode && ! in_BEGIN_or_END)
fprintf(prof_fp, "\n");
}
break;
case Node_statement_list:
for (t = tree; t != NULL; t = t->rnode) {
pprint(t->lnode);
}
break;
case Node_K_if:
indent(tree->exec_count);
fprintf(prof_fp, "if (");
in_expr++;
eval_condition(tree->lnode);
in_expr--;
fprintf(prof_fp, ") {");
#ifdef PROFILING
if (tree->rnode->exec_count)
fprintf(prof_fp, " # %ld", tree->rnode->exec_count);
#endif
fprintf(prof_fp, "\n");
indent_in();
pprint(tree->rnode->lnode);
indent_out();
if (tree->rnode->rnode != NULL) {
if (tree->exec_count - tree->rnode->exec_count > 0)
indent(tree->exec_count - tree->rnode->exec_count);
else
indent(0);
fprintf(prof_fp, "} else {\n");
indent_in();
pprint(tree->rnode->rnode);
indent_out();
}
indent(SPACEOVER);
fprintf(prof_fp, "}\n");
break;
case Node_K_while:
indent(tree->exec_count);
fprintf(prof_fp, "while (");
in_expr++;
eval_condition(tree->lnode);
in_expr--;
fprintf(prof_fp, ") {\n");
indent_in();
pprint(tree->rnode);
indent_out();
indent(SPACEOVER);
fprintf(prof_fp, "}\n");
break;
case Node_K_do:
indent(tree->exec_count);
fprintf(prof_fp, "do {\n");
indent_in();
pprint(tree->rnode);
indent_out();
indent(SPACEOVER);
fprintf(prof_fp, "} while (");
in_expr++;
eval_condition(tree->lnode);
in_expr--;
fprintf(prof_fp, ")\n");
break;
case Node_K_for:
indent(tree->exec_count);
fprintf(prof_fp, "for (");
in_expr++;
pprint(tree->forloop->init);
fprintf(prof_fp, "; ");
eval_condition(tree->forloop->cond);
fprintf(prof_fp, "; ");
pprint(tree->forloop->incr);
fprintf(prof_fp, ") {\n");
in_expr--;
indent_in();
pprint(tree->lnode);
indent_out();
indent(SPACEOVER);
fprintf(prof_fp, "}\n");
break;
case Node_K_arrayfor:
#define hakvar forloop->init
#define arrvar forloop->incr
indent(tree->exec_count);
fprintf(prof_fp, "for (");
in_expr++;
pp_lhs(tree->hakvar);
in_expr--;
fprintf(prof_fp, " in ");
t = tree->arrvar;
if (t->type == Node_param_list)
fprintf(prof_fp, "%s", fparms[t->param_cnt]);
else
fprintf(prof_fp, "%s", t->vname);
fprintf(prof_fp, ") {\n");
indent_in();
pprint(tree->lnode);
indent_out();
indent(SPACEOVER);
fprintf(prof_fp, "}\n");
break;
case Node_K_break:
indent(tree->exec_count);
fprintf(prof_fp, "break\n");
break;
case Node_K_continue:
indent(tree->exec_count);
fprintf(prof_fp, "continue\n");
break;
case Node_K_print:
pp_print_stmt("print", tree);
break;
case Node_K_printf:
pp_print_stmt("printf", tree);
break;
case Node_K_delete:
pp_delete(tree);
break;
case Node_K_next:
indent(tree->exec_count);
fprintf(prof_fp, "next\n");
break;
case Node_K_nextfile:
indent(tree->exec_count);
fprintf(prof_fp, "nextfile\n");
break;
case Node_K_exit:
indent(tree->exec_count);
fprintf(prof_fp, "exit");
if (tree->lnode != NULL) {
fprintf(prof_fp, " ");
tree_eval(tree->lnode);
}
fprintf(prof_fp, "\n");
break;
case Node_K_return:
indent(tree->exec_count);
fprintf(prof_fp, "return");
if (tree->lnode != NULL) {
fprintf(prof_fp, " ");
tree_eval(tree->lnode);
}
fprintf(prof_fp, "\n");
break;
default:
/*
* Appears to be an expression statement.
* Throw away the value.
*/
if (in_expr)
tree_eval(tree);
else {
indent(tree->exec_count);
tree_eval(tree);
fprintf(prof_fp, "\n");
}
break;
}
}
/* tree_eval --- evaluate a subtree */
static void
tree_eval(register NODE *tree)
{
if (tree == NULL)
return;
switch (tree->type) {
case Node_param_list:
fprintf(prof_fp, "%s", fparms[tree->param_cnt]);
return;
case Node_var:
if (tree->vname != NULL)
fprintf(prof_fp, "%s", tree->vname);
else
fatal(_("internal error: Node_var with null vname"));
return;
case Node_val:
if ((tree->flags & (NUM|NUMBER)) != 0)
fprintf(prof_fp, "%g", tree->numbr);
else {
if ((tree->flags & INTLSTR) != 0)
fprintf(prof_fp, "_");
pp_string(tree->stptr, tree->stlen, '"');
}
return;
case Node_and:
eval_condition(tree->lnode);
fprintf(prof_fp, " && ");
eval_condition(tree->rnode);
return;
case Node_or:
eval_condition(tree->lnode);
fprintf(prof_fp, " || ");
eval_condition(tree->rnode);
return;
case Node_not:
parenthesize(tree->type, tree->lnode);
return;
/* Builtins */
case Node_builtin:
pp_builtin(tree);
return;
case Node_in_array:
in_expr++;
pp_in_array(tree->lnode, tree->rnode);
in_expr--;
return;
case Node_func_call:
pp_func_call(tree->rnode, tree->lnode);
return;
case Node_K_getline:
pp_getline(tree);
return;
/* unary operations */
case Node_NR:
fprintf(prof_fp, "NR");
return;
case Node_FNR:
fprintf(prof_fp, "FNR");
return;
case Node_NF:
fprintf(prof_fp, "NF");
return;
case Node_FIELDWIDTHS:
fprintf(prof_fp, "FIELDWIDTHS");
return;
case Node_FS:
fprintf(prof_fp, "FS");
return;
case Node_RS:
fprintf(prof_fp, "RS");
return;
case Node_IGNORECASE:
fprintf(prof_fp, "IGNORECASE");
return;
case Node_OFS:
fprintf(prof_fp, "OFS");
return;
case Node_ORS:
fprintf(prof_fp, "ORS");
return;
case Node_OFMT:
fprintf(prof_fp, "OFMT");
return;
case Node_CONVFMT:
fprintf(prof_fp, "CONVFMT");
return;
case Node_BINMODE:
fprintf(prof_fp, "BINMODE");
return;
case Node_field_spec:
case Node_subscript:
pp_lhs(tree);
return;
case Node_var_array:
if (tree->vname != NULL)
fprintf(prof_fp, "%s", tree->vname);
else
fatal(_("internal error: Node_var_array with null vname"));
return;
case Node_unary_minus:
fprintf(prof_fp, " -");
tree_eval(tree->subnode);
return;
case Node_cond_exp:
eval_condition(tree->lnode);
fprintf(prof_fp, " ? ");
tree_eval(tree->rnode->lnode);
fprintf(prof_fp, " : ");
tree_eval(tree->rnode->rnode);
return;
case Node_match:
case Node_nomatch:
case Node_regex:
pp_match_op(tree);
return;
case Node_func:
fatal(_("function `%s' called with space between name and `(',\n%s"),
tree->lnode->param,
_("or used in other expression context"));
/* assignments */
case Node_assign:
tree_eval(tree->lnode);
fprintf(prof_fp, " = ");
tree_eval(tree->rnode);
return;
case Node_concat:
fprintf(prof_fp, "(");
tree_eval(tree->lnode);
fprintf(prof_fp, " ");
tree_eval(tree->rnode);
fprintf(prof_fp, ")");
return;
/* other assignment types are easier because they are numeric */
case Node_preincrement:
case Node_predecrement:
case Node_postincrement:
case Node_postdecrement:
case Node_assign_exp:
case Node_assign_times:
case Node_assign_quotient:
case Node_assign_mod:
case Node_assign_plus:
case Node_assign_minus:
pp_op_assign(tree);
return;
default:
break; /* handled below */
}
/* handle binary ops */
in_expr++;
parenthesize(tree->type, tree->lnode);
switch (tree->type) {
case Node_geq:
fprintf(prof_fp, " >= ");
break;
case Node_leq:
fprintf(prof_fp, " <= ");
break;
case Node_greater:
fprintf(prof_fp, " > ");
break;
case Node_less:
fprintf(prof_fp, " < ");
break;
case Node_notequal:
fprintf(prof_fp, " != ");
break;
case Node_equal:
fprintf(prof_fp, " == ");
break;
case Node_exp:
fprintf(prof_fp, " ^ ");
break;
case Node_times:
fprintf(prof_fp, " * ");
break;
case Node_quotient:
fprintf(prof_fp, " / ");
break;
case Node_mod:
fprintf(prof_fp, " %% ");
break;
case Node_plus:
fprintf(prof_fp, " + ");
break;
case Node_minus:
fprintf(prof_fp, " - ");
break;
case Node_var_array:
fatal(_("attempt to use array `%s' in a scalar context"),
tree->vname);
return;
default:
fatal(_("illegal type (%s) in tree_eval"), nodetype2str(tree->type));
}
parenthesize(tree->type, tree->rnode);
in_expr--;
return;
}
/* eval_condition --- is TREE true or false */
static void
eval_condition(register NODE *tree)
{
if (tree == NULL) /* Null trees are the easiest kinds */
return;
if (tree->type == Node_line_range) {
/* /.../, /.../ */
eval_condition(tree->condpair->lnode);
fprintf(prof_fp,", ");
eval_condition(tree->condpair->rnode);
return;
}
/*
* Could just be J.random expression. in which case, null and 0 are
* false, anything else is true
*/
tree_eval(tree);
return;
}
/* pp_op_assign --- do +=, -=, etc. */
static void
pp_op_assign(register NODE *tree)
{
char *op = NULL;
enum Order {
NA = 0,
PRE = 1,
POST = 2
} order = NA;
switch(tree->type) {
case Node_preincrement:
op = "++";
order = PRE;
break;
case Node_predecrement:
op = "--";
order = PRE;
break;
case Node_postincrement:
op = "++";
order = POST;
break;
case Node_postdecrement:
op = "--";
order = POST;
break;
default:
break; /* handled below */
}
if (order == PRE) {
fprintf(prof_fp, "%s", op);
pp_lhs(tree->lnode);
return;
} else if (order == POST) {
pp_lhs(tree->lnode);
fprintf(prof_fp, "%s", op);
return;
}
/* a binary op */
pp_lhs(tree->lnode);
switch(tree->type) {
case Node_assign_exp:
fprintf(prof_fp, " ^= ");
break;
case Node_assign_times:
fprintf(prof_fp, " *= ");
break;
case Node_assign_quotient:
fprintf(prof_fp, " /= ");
break;
case Node_assign_mod:
fprintf(prof_fp, " %%= ");
break;
case Node_assign_plus:
fprintf(prof_fp, " += ");
break;
case Node_assign_minus:
fprintf(prof_fp, " -= ");
break;
default:
cant_happen();
}
tree_eval(tree->rnode);
}
/* pp_lhs --- print the lhs */
static void
pp_lhs(register NODE *ptr)
{
register NODE *n;
switch (ptr->type) {
case Node_var_array:
fatal(_("attempt to use array `%s' in a scalar context"),
ptr->vname);
case Node_var:
fprintf(prof_fp, "%s", ptr->vname);
break;
case Node_FIELDWIDTHS:
fprintf(prof_fp, "FIELDWIDTHS");
break;
case Node_RS:
fprintf(prof_fp, "RS");
break;
case Node_FS:
fprintf(prof_fp, "FS");
break;
case Node_FNR:
fprintf(prof_fp, "FNR");
break;
case Node_NR:
fprintf(prof_fp, "NR");
break;
case Node_NF:
fprintf(prof_fp, "NF");
break;
case Node_IGNORECASE:
fprintf(prof_fp, "IGNORECASE");
break;
case Node_BINMODE:
fprintf(prof_fp, "BINMODE");
break;
case Node_LINT:
fprintf(prof_fp, "LINT");
break;
case Node_OFMT:
fprintf(prof_fp, "OFMT");
break;
case Node_CONVFMT:
fprintf(prof_fp, "CONVFMT");
break;
case Node_ORS:
fprintf(prof_fp, "ORS");
break;
case Node_OFS:
fprintf(prof_fp, "OFS");
break;
case Node_param_list:
fprintf(prof_fp, "%s", fparms[ptr->param_cnt]);
break;
case Node_field_spec:
fprintf(prof_fp, "$");
if (is_scalar(ptr->lnode->type))
tree_eval(ptr->lnode);
else {
fprintf(prof_fp, "(");
tree_eval(ptr->lnode);
fprintf(prof_fp, ")");
}
break;
case Node_subscript:
n = ptr->lnode;
if (n->type == Node_func) {
fatal(_("attempt to use function `%s' as array"),
n->lnode->param);
} else if (n->type == Node_param_list) {
fprintf(prof_fp, "%s[", fparms[n->param_cnt]);
} else
fprintf(prof_fp, "%s[", n->vname);
if (ptr->rnode->type == Node_expression_list)
pp_list(ptr->rnode);
else
tree_eval(ptr->rnode);
fprintf(prof_fp, "]");
break;
case Node_func:
fatal(_("`%s' is a function, assignment is not allowed"),
ptr->lnode->param);
case Node_builtin:
fatal(_("assignment is not allowed to result of builtin function"));
default:
cant_happen();
}
}
/* match_op --- do ~ and !~ */
static void
pp_match_op(register NODE *tree)
{
register NODE *re;
char *op;
char *restr;
size_t relen;
NODE *text = NULL;
if (tree->type == Node_regex)
re = tree->re_exp;
else {
re = tree->rnode->re_exp;
text = tree->lnode;
}
if ((re->re_flags & CONST) != 0) {
restr = re->stptr;
relen = re->stlen;
} else {
restr = re->stptr;
relen = re->stlen;
}
if (tree->type == Node_regex) {
pp_string(restr, relen, '/');
return;
}
if (tree->type == Node_nomatch)
op = "!~";
else if (tree->type == Node_match)
op = "~";
else
op = "";
tree_eval(text);
fprintf(prof_fp, " %s ", op);
fprintf(prof_fp, "/%.*s/", (int) relen, restr);
}
/* pp_redir --- print a redirection */
static void
pp_redir(register NODE *tree, enum redir_placement dir)
{
char *op = "[BOGUS]"; /* should never be seen */
if (tree == NULL)
return;
switch (tree->type) {
case Node_redirect_output:
op = ">";
break;
case Node_redirect_append:
op = ">>";
break;
case Node_redirect_pipe:
op = "|";
break;
case Node_redirect_pipein:
op = "|";
break;
case Node_redirect_input:
op = "<";
break;
case Node_redirect_twoway:
op = "|&";
break;
default:
cant_happen();
}
if (dir == BEFORE) {
if (! is_scalar(tree->subnode->type)) {
fprintf(prof_fp, "(");
tree_eval(tree->subnode);
fprintf(prof_fp, ")");
} else
tree_eval(tree->subnode);
fprintf(prof_fp, " %s ", op);
} else {
fprintf(prof_fp, " %s ", op);
if (! is_scalar(tree->subnode->type)) {
fprintf(prof_fp, "(");
tree_eval(tree->subnode);
fprintf(prof_fp, ")");
} else
tree_eval(tree->subnode);
}
}
/* pp_list --- dump a list of arguments, without parens */
static void
pp_list(register NODE *tree)
{
for (; tree != NULL; tree = tree->rnode) {
if (tree->type != Node_expression_list) {
fprintf(stderr, "pp_list: got %s\n",
nodetype2str(tree->type));
fflush(stderr);
}
assert(tree->type == Node_expression_list);
tree_eval(tree->lnode);
if (tree->rnode != NULL)
fprintf(prof_fp, ", ");
}
}
/* pp_print_stmt --- print a "print" or "printf" statement */
static void
pp_print_stmt(const char *command, register NODE *tree)
{
NODE *redir = tree->rnode;
indent(tree->exec_count);
fprintf(prof_fp, "%s", command);
if (redir != NULL) { /* parenthesize if have a redirection */
fprintf(prof_fp, "(");
pp_list(tree->lnode);
fprintf(prof_fp, ")");
pp_redir(redir, AFTER);
} else {
fprintf(prof_fp, " ");
pp_list(tree->lnode);
}
fprintf(prof_fp, "\n");
}
/* pp_delete --- print a "delete" statement */
static void
pp_delete(register NODE *tree)
{
NODE *array, *subscript;
array = tree->lnode;
subscript = tree->rnode;
indent(array->exec_count);
if (array->type == Node_param_list)
fprintf(prof_fp, "delete %s", fparms[array->param_cnt]);
else
fprintf(prof_fp, "delete %s", array->vname);
if (subscript != NULL) {
fprintf(prof_fp, "[");
pp_list(subscript);
fprintf(prof_fp, "]");
}
fprintf(prof_fp, "\n");
}
/* pp_in_array --- pretty print "foo in array" test */
static void
pp_in_array(NODE *array, NODE *subscript)
{
if (subscript->type == Node_expression_list) {
fprintf(prof_fp, "(");
pp_list(subscript);
fprintf(prof_fp, ")");
} else
pprint(subscript);
if (array->type == Node_param_list)
fprintf(prof_fp, " in %s", fparms[array->param_cnt]);
else
fprintf(prof_fp, " in %s", array->vname);
}
/* pp_getline --- print a getline statement */
static void
pp_getline(register NODE *tree)
{
NODE *redir = tree->rnode;
int before, after;
/*
* command | getline
* or
* command |& getline
* or
* getline < file
*/
if (redir != NULL) {
before = (redir->type == Node_redirect_pipein
|| redir->type == Node_redirect_twoway);
after = ! before;
} else
before = after = FALSE;
if (before)
pp_redir(redir, BEFORE);
fprintf(prof_fp, "getline");
if (tree->lnode != NULL) { /* optional var */
fprintf(prof_fp, " ");
pp_lhs(tree->lnode);
}
if (after)
pp_redir(redir, AFTER);
}
/* pp_builtin --- print a builtin function */
static void
pp_builtin(register NODE *tree)
{
fprintf(prof_fp, "%s(", getfname(tree->proc));
pp_list(tree->subnode);
fprintf(prof_fp, ")");
}
/* pp_func_call --- print a function call */
static void
pp_func_call(NODE *name, NODE *arglist)
{
fprintf(prof_fp, "%s(", name->stptr);
pp_list(arglist);
fprintf(prof_fp, ")");
}
/* dump_prog --- dump the program */
/*
* XXX: I am not sure it is right to have the strings in the dump
* be translated, but I'll leave it alone for now.
*/
void
dump_prog(NODE *begin, NODE *prog, NODE *end)
{
time_t now;
(void) time(& now);
/* \n on purpose, with \n in ctime() output */
fprintf(prof_fp, _("\t# gawk profile, created %s\n"), ctime(& now));
if (begin != NULL) {
fprintf(prof_fp, _("\t# BEGIN block(s)\n\n"));
fprintf(prof_fp, "\tBEGIN {\n");
in_BEGIN_or_END = TRUE;
pprint(begin);
in_BEGIN_or_END = FALSE;
fprintf(prof_fp, "\t}\n");
if (prog != NULL || end != NULL)
fprintf(prof_fp, "\n");
}
if (prog != NULL) {
fprintf(prof_fp, _("\t# Rule(s)\n\n"));
pprint(prog);
if (end != NULL)
fprintf(prof_fp, "\n");
}
if (end != NULL) {
fprintf(prof_fp, _("\t# END block(s)\n\n"));
fprintf(prof_fp, "\tEND {\n");
in_BEGIN_or_END = TRUE;
pprint(end);
in_BEGIN_or_END = FALSE;
fprintf(prof_fp, "\t}\n");
}
}
/* pp_func --- pretty print a function */
void
pp_func(char *name, size_t namelen, NODE *f)
{
int j;
char **pnames;
static int first = TRUE;
if (first) {
first = FALSE;
fprintf(prof_fp, _("\n\t# Functions, listed alphabetically\n"));
}
fprintf(prof_fp, "\n");
indent(f->exec_count);
fprintf(prof_fp, "function %.*s(", (int) namelen, name);
pnames = f->parmlist;
fparms = pnames;
for (j = 0; j < f->lnode->param_cnt; j++) {
fprintf(prof_fp, "%s", pnames[j]);
if (j < f->lnode->param_cnt - 1)
fprintf(prof_fp, ", ");
}
fprintf(prof_fp, ")\n\t{\n");
indent_in();
pprint(f->rnode); /* body */
indent_out();
fprintf(prof_fp, "\t}\n");
}
/* pp_string --- pretty print a string or regex constant */
static void
pp_string(char *str, size_t len, int delim)
{
pp_string_fp(prof_fp, str, len, delim, FALSE);
}
/* pp_string_fp --- printy print a string to the fp */
/*
* This routine concentrates string pretty printing in one place,
* so that it can be called from multiple places within gawk.
*/
void
pp_string_fp(FILE *fp, char *in_str, size_t len, int delim, int breaklines)
{
static char escapes[] = "\b\f\n\r\t\v\\";
static char printables[] = "bfnrtv\\";
char *cp;
int i;
int count;
#define BREAKPOINT 70 /* arbitrary */
unsigned char *str = (unsigned char *) in_str;
fprintf(fp, "%c", delim);
for (count = 0; len > 0; len--, str++) {
if (++count >= BREAKPOINT && breaklines) {
fprintf(fp, "%c\n%c", delim, delim);
count = 0;
}
if (*str == delim) {
fprintf(fp, "\\%c", delim);
count++;
} else if (*str == BELL) {
fprintf(fp, "\\a");
count++;
} else if ((cp = strchr(escapes, *str)) != NULL) {
i = cp - escapes;
putc('\\', fp);
count++;
putc(printables[i], fp);
if (breaklines && *str == '\n' && delim == '"') {
fprintf(fp, "\"\n\"");
count = 0;
}
/* NB: Deliberate use of lower-case versions. */
} else if (isascii(*str) && isprint(*str)) {
putc(*str, fp);
} else {
char buf[10];
sprintf(buf, "\\%03o", *str & 0xff);
count += strlen(buf) - 1;
fprintf(fp, "%s", buf);
}
}
fprintf(fp, "%c", delim);
}
/* is_scalar --- true or false if we'll get a scalar value */
static int
is_scalar(NODETYPE type)
{
switch (type) {
case Node_var:
case Node_var_array:
case Node_val:
case Node_BINMODE:
case Node_CONVFMT:
case Node_FIELDWIDTHS:
case Node_FNR:
case Node_FS:
case Node_IGNORECASE:
case Node_LINT:
case Node_NF:
case Node_NR:
case Node_OFMT:
case Node_OFS:
case Node_ORS:
case Node_RS:
case Node_subscript:
return TRUE;
default:
return FALSE;
}
}
/* prec_level --- return the precedence of an operator, for paren tests */
static int
prec_level(NODETYPE type)
{
switch (type) {
case Node_var:
case Node_var_array:
case Node_param_list:
case Node_subscript:
case Node_func_call:
case Node_val:
case Node_builtin:
case Node_BINMODE:
case Node_CONVFMT:
case Node_FIELDWIDTHS:
case Node_FNR:
case Node_FS:
case Node_IGNORECASE:
case Node_LINT:
case Node_NF:
case Node_NR:
case Node_OFMT:
case Node_OFS:
case Node_ORS:
case Node_RS:
return 15;
case Node_field_spec:
return 14;
case Node_exp:
return 13;
case Node_preincrement:
case Node_predecrement:
case Node_postincrement:
case Node_postdecrement:
return 12;
case Node_unary_minus:
case Node_not:
return 11;
case Node_times:
case Node_quotient:
case Node_mod:
return 10;
case Node_plus:
case Node_minus:
return 9;
case Node_concat:
return 8;
case Node_equal:
case Node_notequal:
case Node_greater:
case Node_leq:
case Node_geq:
case Node_match:
case Node_nomatch:
return 7;
case Node_K_getline:
return 6;
case Node_less:
return 5;
case Node_in_array:
return 5;
case Node_and:
return 4;
case Node_or:
return 3;
case Node_cond_exp:
return 2;
case Node_assign:
case Node_assign_times:
case Node_assign_quotient:
case Node_assign_mod:
case Node_assign_plus:
case Node_assign_minus:
case Node_assign_exp:
return 1;
default:
fatal(_("unexpected type %s in prec_level"), nodetype2str(type));
return 0; /* keep the compiler happy */
}
}
/* parenthesize --- print a subtree in parentheses if need be */
static void
parenthesize(NODETYPE parent_type, NODE *tree)
{
NODETYPE child_type;
if (tree == NULL)
return;
child_type = tree->type;
in_expr++;
/* first the special cases, then the general ones */
if (parent_type == Node_not && child_type == Node_in_array) {
fprintf(prof_fp, "! (");
pp_in_array(tree->lnode, tree->rnode);
fprintf(prof_fp, ")");
/* other special cases here, as needed */
} else if (prec_level(child_type) < prec_level(parent_type)) {
fprintf(prof_fp, "(");
tree_eval(tree);
fprintf(prof_fp, ")");
} else
tree_eval(tree);
in_expr--;
}
#ifdef PROFILING
/* just_dump --- dump the profile and function stack and keep going */
static RETSIGTYPE
just_dump(int signum)
{
extern NODE *begin_block, *expression_value, *end_block;
dump_prog(begin_block, expression_value, end_block);
dump_funcs();
dump_fcall_stack(prof_fp);
fflush(prof_fp);
signal(signum, just_dump); /* for OLD Unix systems ... */
}
/* dump_and_exit --- dump the profile, the function stack, and exit */
static RETSIGTYPE
dump_and_exit(int signum)
{
just_dump(signum);
exit(1);
}
#endif