diff --git a/qse/cmd/awk/awk.c b/qse/cmd/awk/awk.c
index a00a82aa..6a4ab057 100644
--- a/qse/cmd/awk/awk.c
+++ b/qse/cmd/awk/awk.c
@@ -421,6 +421,7 @@ struct opttab_t
{ QSE_T("ncmponstr"), QSE_AWK_NCMPONSTR, QSE_T("perform numeric comparsion on numeric strings") },
{ QSE_T("strictnaming"), QSE_AWK_STRICTNAMING, QSE_T("enable the strict naming rule") },
{ QSE_T("include"), QSE_AWK_INCLUDE, QSE_T("enable 'include'") },
+ { QSE_T("tolerant"), QSE_AWK_TOLERANT, QSE_T("make more I/O fault-tolerant") },
{ QSE_NULL, 0, QSE_NULL }
};
@@ -479,6 +480,7 @@ static int comparg (int argc, qse_char_t* argv[], struct arg_t* arg)
{ QSE_T(":ncmponstr"), QSE_T('\0') },
{ QSE_T(":strictnaming"), QSE_T('\0') },
{ QSE_T(":include"), QSE_T('\0') },
+ { QSE_T(":tolerant"), QSE_T('\0') },
{ QSE_T(":call"), QSE_T('c') },
{ QSE_T(":file"), QSE_T('f') },
diff --git a/qse/doc/page/awk.doc b/qse/doc/page/awk.doc
index da520847..2143ce45 100644
--- a/qse/doc/page/awk.doc
+++ b/qse/doc/page/awk.doc
@@ -4,16 +4,18 @@
- @ref awk_intro "INTRODUCTION"
- @ref awk_lang "AWK LANGUAGE"
- @ref awk_ext "AWK LANGUAGE EXTENSIONS"
- - @ref awk_ext_vardecl " VARIABLE DECLARATION"
- - @ref awk_ext_include "INCLUDE"
- - @ref awk_ext_rwpipe "TWO-WAY PIPE"
- - @ref awk_ext_return "RETURN"
- - @ref awk_ext_comment "COMMENT"
- - @ref awk_ext_fnc "EXTENDED FUNCTIONS"
- - @ref awk_ext_fs "EXTENDED FS"
- - @ref awk_ext_binnum "BINARY NUMBER"
- - @ref awk_ext_unicode "UNICODE ESCAPE SEQUENCE"
- - @ref awk_ext_ioenc "I/O ENCODING"
+ - @ref awk_ext_vardecl "VARIABLE DECLARATION"
+ - @ref awk_ext_include "INCLUDE"
+ - @ref awk_ext_print "EXTENDED PRINT/PRINTF"
+ - @ref awk_ext_exprgroup "GROUPED EXPRESSION"
+ - @ref awk_ext_rwpipe "TWO-WAY PIPE"
+ - @ref awk_ext_return "RETURN"
+ - @ref awk_ext_comment "COMMENT"
+ - @ref awk_ext_fnc "EXTENDED FUNCTIONS"
+ - @ref awk_ext_fs "EXTENDED FS"
+ - @ref awk_ext_binnum "BINARY NUMBER"
+ - @ref awk_ext_unicode "UNICODE ESCAPE SEQUENCE"
+ - @ref awk_ext_ioenc "I/O ENCODING"
@section awk_intro INTRODUCTION
@@ -289,6 +291,51 @@ blocks appear. To use \@include, you must turn on #QSE_AWK_INCLUDE.
BEGIN { func_in_abc (); }
@endcode
+@subsection awk_ext_print EXTENDED PRINT/PRINTF
+When #QSE_AWK_TOLERANT is on, print and printf are treated as if
+they are function calls. In this mode, they return a negative number
+on failure and a zero on success and any I/O failure doesn't abort
+a running program.
+
+@code
+BEGIN {
+ a = print "hello, world" > "/dev/null";
+ print a;
+ a = print ("hello, world") > "/dev/null";
+ print a;
+}
+@endcode
+
+Since print and printf are like function calls, you can use them
+in any context where a normal expression is allowed. For example,
+printf is used as a conditional expression in an 'if' statement
+in the sample code below.
+@code
+BEGIN {
+ if ((printf "hello, world\n" || "tcp://127.0.0.1:9999") <= -1)
+ print "FAILURE";
+ else
+ print "SUCCESS";
+}
+@endcode
+
+@subsection awk_ext_exprgroup GROUPED EXPRESSION
+When #QSE_AWK_TOLERANT is on, you can use a grouped expression without
+the 'in' operator. A grouped expression is a parentheses-enclosed list
+of expressions separated with a comma. Each expression in the group is
+evaluated in the appearing order. The evaluation result of the last
+expression in the group is returned as that of the group.
+
+@code
+BEGIN {
+ c = (1, 2, 9);
+ a=((1*c, 3*c), (3 - c), ((k = 6+(c+1, c+2)), (-7 * c)));
+ print c; # 9;
+ print a; # -63
+ print k; # 17
+}
+@endcode
+
@subsection awk_ext_rwpipe TWO-WAY PIPE
The two-way pipe indicated by @b || is supproted, in addition to the one-way
@@ -352,6 +399,77 @@ BEGIN {
}
@endcode
+Here is a more interesting example adopting Michael Sanders'
+AWK web server, modified for QSEAWK.
+
+@code
+#
+# Michael Sanders' AWK web server for QSEAWK.
+# Orginal code in http://awk.info/?tools/server
+#
+# qseawk --tolerant=on --rwpipe=on webserver.awk
+#
+BEGIN {
+ x = 1 # script exits if x < 1
+ port = 8080 # port number
+ host = "tcpd://0.0.0.0:" port # host string
+ url = "http://localhost:" port # server url
+ status = 200 # 200 == OK
+ reason = "OK" # server response
+ RS = ORS = "\r\n" # header line terminators
+ doc = Setup() # html document
+ len = length(doc) + length(ORS) # length of document
+ while (x) {
+ if ($1 == "GET") RunApp(substr($2, 2))
+ if (! x) break
+ print "HTTP/1.0", status, reason || host
+ print "Connection: Close" || host
+ print "Pragma: no-cache" || host
+ print "Content-length:", len || host
+ print ORS doc || host
+ close(host) # close client connection
+ host || getline # wait for new client request
+ }
+ # server terminated...
+ doc = Bye()
+ len = length(doc) + length(ORS)
+ print "HTTP/1.0", status, reason || host
+ print "Connection: Close" || host
+ print "Pragma: no-cache" || host
+ print "Content-length:", len || host
+ print ORS doc || host
+ close(host)
+}
+
+function Setup() {
+ tmp = "\
+
Simple gawk server\
+ \
+ xterm\
+
xcalc\
+
xload\
+
terminate script\
+ \
+ "
+ return tmp
+}
+
+function Bye() {
+ tmp = "\
+
Simple gawk server\
+ Script Terminated...\
+ "
+ return tmp
+}
+
+function RunApp(app) {
+ if (app == "xterm") {system("xterm&"); return}
+ if (app == "xcalc" ) {system("xcalc&"); return}
+ if (app == "xload" ) {system("xload&"); return}
+ if (app == "exit") {x = 0}
+}
+@endcode
+
@subsection awk_ext_return RETURN
The return statement is valid in pattern-action blocks as well as in functions. The execution of a calling block is aborted once the return statement is executed.
@@ -515,4 +633,5 @@ PEER: ?好!
Note that 你 has been converted to a question mark since the letter is
not supported by cp949.
+
*/
diff --git a/qse/include/qse/awk/awk.h b/qse/include/qse/awk/awk.h
index 37374ec6..712e7381 100644
--- a/qse/include/qse/awk/awk.h
+++ b/qse/include/qse/awk/awk.h
@@ -301,8 +301,6 @@ enum qse_awk_nde_type_t
QSE_AWK_NDE_NEXTFILE,
QSE_AWK_NDE_DELETE,
QSE_AWK_NDE_RESET,
- QSE_AWK_NDE_PRINT,
- QSE_AWK_NDE_PRINTF,
/* expression */
/* if you change the following values including their order,
@@ -336,7 +334,9 @@ enum qse_awk_nde_type_t
QSE_AWK_NDE_POS,
/* ---------------------------------- */
- QSE_AWK_NDE_GETLINE
+ QSE_AWK_NDE_GETLINE,
+ QSE_AWK_NDE_PRINT,
+ QSE_AWK_NDE_PRINTF
};
typedef enum qse_awk_nde_type_t qse_awk_nde_type_t;
@@ -751,16 +751,16 @@ enum qse_awk_option_t
* - @b ~ bitwise-not
* - @b // idiv (get quotient)
*/
- QSE_AWK_EXTRAOPS = (1 << 2),
+ QSE_AWK_EXTRAOPS = (1 << 2),
/** supports @b getline and @b print */
- QSE_AWK_RIO = (1 << 3),
+ QSE_AWK_RIO = (1 << 3),
/** enables the two-way pipe if #QSE_AWK_RIO is on */
- QSE_AWK_RWPIPE = (1 << 4),
+ QSE_AWK_RWPIPE = (1 << 4),
/** a new line can terminate a statement */
- QSE_AWK_NEWLINE = (1 << 5),
+ QSE_AWK_NEWLINE = (1 << 5),
/**
* removes empty fields when splitting a record if FS is a regular
@@ -787,30 +787,30 @@ enum qse_awk_option_t
* [], [h], [my], [n], [dle], []. Note that the first empty field is not
* removed as the field separator is not all spaces. (space + 'o').
*/
- QSE_AWK_STRIPRECSPC = (1 << 6),
+ QSE_AWK_STRIPRECSPC = (1 << 6),
/**
* strips off leading spaces when converting a string to a number.
*/
- QSE_AWK_STRIPSTRSPC = (1 << 7),
+ QSE_AWK_STRIPSTRSPC = (1 << 7),
/** enables @b nextofile */
- QSE_AWK_NEXTOFILE = (1 << 8),
+ QSE_AWK_NEXTOFILE = (1 << 8),
/** enables @b reset */
- QSE_AWK_RESET = (1 << 9),
+ QSE_AWK_RESET = (1 << 9),
/** CR + LF by default */
- QSE_AWK_CRLF = (1 << 10),
+ QSE_AWK_CRLF = (1 << 10),
/** allows the assignment of a map value to a variable */
- QSE_AWK_MAPTOVAR = (1 << 11),
+ QSE_AWK_MAPTOVAR = (1 << 11),
/** allows @b BEGIN, @b END, pattern-action blocks */
- QSE_AWK_PABLOCK = (1 << 12),
+ QSE_AWK_PABLOCK = (1 << 12),
/** allows {n,m} in a regular expression. */
- QSE_AWK_REXBOUND = (1 << 13),
+ QSE_AWK_REXBOUND = (1 << 13),
/**
* performs numeric comparison when a string convertable
@@ -835,6 +835,19 @@ enum qse_awk_option_t
*/
QSE_AWK_INCLUDE = (1 << 16),
+ /**
+ * makes AWK more fault-tolerant.
+ * - prevents termination due to print and printf failure.
+ * - achieves this by handling print and printf as if
+ * they are functions like getline.
+ * - allows an expression group in a normal context
+ * without the 'in' operator. the evaluation result
+ * of the last expression is returned as that of
+ * the expression group.
+ * - e.g.) a = (1, 3 * 3, 4, 5 + 1); # a is assigned 6.
+ */
+ QSE_AWK_TOLERANT = (1 << 17),
+
/**
* makes #qse_awk_t to behave compatibly with classical AWK
* implementations
diff --git a/qse/lib/awk/parse.c b/qse/lib/awk/parse.c
index d2ccba2d..933c256b 100644
--- a/qse/lib/awk/parse.c
+++ b/qse/lib/awk/parse.c
@@ -311,7 +311,7 @@ static global_t gtab[] =
{ QSE_T("OFILENAME"), 9, QSE_AWK_PABLOCK | QSE_AWK_NEXTOFILE },
/* output real-to-str conversion format for 'print' */
- { QSE_T("OFMT"), 4, QSE_AWK_RIO},
+ { QSE_T("OFMT"), 4, QSE_AWK_RIO },
/* output field separator for 'print' */
{ QSE_T("OFS"), 3, QSE_AWK_RIO },
@@ -2727,6 +2727,16 @@ static qse_awk_nde_t* parse_print (
qse_awk_nde_t* args_tail;
qse_awk_nde_t* tail_prev;
+ /* print and printf provide weird syntaxs.
+ *
+ * 1. print 10, 20;
+ * 2. print (10, 20);
+ * 3. print (10,20,30) in a;
+ * 4. print ((10,20,30) in a);
+ *
+ * Due the case 3, i can't consume LPAREN
+ * here and expect RPAREN later.
+ */
{
qse_awk_loc_t eloc = awk->tok.loc;
args = parse_expr_dc (awk, &eloc);
@@ -2831,11 +2841,11 @@ static qse_awk_nde_t* parse_print (
if (out == QSE_NULL)
{
out_type = MATCH(awk,TOK_GT)? QSE_AWK_OUT_FILE:
- MATCH(awk,TOK_RS)? QSE_AWK_OUT_APFILE:
+ MATCH(awk,TOK_RS)? QSE_AWK_OUT_APFILE:
MATCH(awk,TOK_BOR)? QSE_AWK_OUT_PIPE:
((awk->option & QSE_AWK_RWPIPE) &&
- MATCH(awk,TOK_LOR))? QSE_AWK_OUT_RWPIPE:
- QSE_AWK_OUT_CONSOLE;
+ MATCH(awk,TOK_LOR))? QSE_AWK_OUT_RWPIPE:
+ QSE_AWK_OUT_CONSOLE;
if (out_type != QSE_AWK_OUT_CONSOLE)
{
@@ -2977,15 +2987,21 @@ static qse_awk_nde_t* parse_statement_nb (
if (get_token(awk) <= -1) return QSE_NULL;
nde = parse_reset (awk, xloc);
}
- else if (MATCH(awk,TOK_PRINT))
+ else if (!(awk->option & QSE_AWK_TOLERANT))
{
- if (get_token(awk) <= -1) return QSE_NULL;
- nde = parse_print (awk, xloc, QSE_AWK_NDE_PRINT);
- }
- else if (MATCH(awk,TOK_PRINTF))
- {
- if (get_token(awk) <= -1) return QSE_NULL;
- nde = parse_print (awk, xloc, QSE_AWK_NDE_PRINTF);
+ /* in the non-tolerant mode, we treat print and printf
+ * as a separate statement */
+ if (MATCH(awk,TOK_PRINT))
+ {
+ if (get_token(awk) <= -1) return QSE_NULL;
+ nde = parse_print (awk, xloc, QSE_AWK_NDE_PRINT);
+ }
+ else if (MATCH(awk,TOK_PRINTF))
+ {
+ if (get_token(awk) <= -1) return QSE_NULL;
+ nde = parse_print (awk, xloc, QSE_AWK_NDE_PRINTF);
+ }
+ else nde = parse_expr_dc (awk, xloc);
}
else
{
@@ -3860,6 +3876,8 @@ static qse_awk_nde_t* parse_concat (
MATCH(awk,TOK_PLUSPLUS) ||
MATCH(awk,TOK_MINUSMINUS) ||
MATCH(awk,TOK_LNOT) ||
+ ((awk->option & QSE_AWK_TOLERANT) &&
+ (awk->tok.type == TOK_PRINT || awk->tok.type == TOK_PRINTF)) ||
awk->tok.type >= TOK_GETLINE)
{
/* TODO: is the check above sufficient? */
@@ -4514,9 +4532,9 @@ static qse_awk_nde_t* parse_primary_nogetline (
}
/* check if it is a chained node */
- if (nde->next != QSE_NULL)
+ if (nde->next)
{
- /* if so, it is a expression group */
+ /* if so, it is an expression group */
/* (expr1, expr2, expr2) */
qse_awk_nde_grp_t* tmp;
@@ -4525,7 +4543,8 @@ static qse_awk_nde_t* parse_primary_nogetline (
awk->parse.id.stmt != TOK_PRINTF) ||
awk->parse.depth.cur.expr != 1)
{
- if (!MATCH(awk,TOK_IN))
+ if (!(awk->option & QSE_AWK_TOLERANT) &&
+ !MATCH(awk,TOK_IN))
{
qse_awk_clrpt (awk, nde);
SETERR_TOK (awk, QSE_AWK_EKWIN);
@@ -4611,6 +4630,21 @@ static qse_awk_nde_t* parse_primary_nogetline (
return (qse_awk_nde_t*)nde;
}
+ else if (awk->option & QSE_AWK_TOLERANT)
+ {
+ /* in the tolerant mode, we treat print and printf
+ * as a function like getline */
+ if (MATCH(awk,TOK_PRINT))
+ {
+ if (get_token(awk) <= -1) return QSE_NULL;
+ return parse_print (awk, xloc, QSE_AWK_NDE_PRINT);
+ }
+ else if (MATCH(awk,TOK_PRINTF))
+ {
+ if (get_token(awk) <= -1) return QSE_NULL;
+ return parse_print (awk, xloc, QSE_AWK_NDE_PRINTF);
+ }
+ }
/* valid expression introducer is expected */
if (MATCH(awk,TOK_NEWLINE))
diff --git a/qse/lib/awk/run.c b/qse/lib/awk/run.c
index 94a26fed..84bd00b8 100644
--- a/qse/lib/awk/run.c
+++ b/qse/lib/awk/run.c
@@ -25,6 +25,8 @@
#include
#endif
+#define PRINT_IOERR -99
+
#define CMP_ERROR -99
#define DEF_BUF_CAPA 256
#define STACK_INCREMENT 512
@@ -242,6 +244,8 @@ static qse_awk_val_t* eval_lclidx (qse_awk_rtx_t* run, qse_awk_nde_t* nde);
static qse_awk_val_t* eval_argidx (qse_awk_rtx_t* run, qse_awk_nde_t* nde);
static qse_awk_val_t* eval_pos (qse_awk_rtx_t* run, qse_awk_nde_t* nde);
static qse_awk_val_t* eval_getline (qse_awk_rtx_t* run, qse_awk_nde_t* nde);
+static qse_awk_val_t* eval_print (qse_awk_rtx_t* run, qse_awk_nde_t* nde);
+static qse_awk_val_t* eval_printf (qse_awk_rtx_t* run, qse_awk_nde_t* nde);
static int __raw_push (qse_awk_rtx_t* run, void* val);
#define __raw_pop(run) \
@@ -1830,138 +1834,96 @@ static int run_block0 (qse_awk_rtx_t* rtx, qse_awk_nde_blk_t* nde)
static int run_statement (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
{
+ int xret;
+ qse_awk_val_t* tmp;
+
ON_STATEMENT (rtx, nde);
switch (nde->type)
{
case QSE_AWK_NDE_NULL:
- {
/* do nothing */
+ xret = 0;
break;
- }
case QSE_AWK_NDE_BLK:
- {
- if (run_block (rtx,
- (qse_awk_nde_blk_t*)nde) == -1) return -1;
+ xret = run_block (rtx, (qse_awk_nde_blk_t*)nde);
break;
- }
case QSE_AWK_NDE_IF:
- {
- if (run_if (rtx,
- (qse_awk_nde_if_t*)nde) == -1) return -1;
+ xret = run_if (rtx, (qse_awk_nde_if_t*)nde);
break;
- }
case QSE_AWK_NDE_WHILE:
case QSE_AWK_NDE_DOWHILE:
- {
- if (run_while (rtx,
- (qse_awk_nde_while_t*)nde) == -1) return -1;
+ xret = run_while (rtx, (qse_awk_nde_while_t*)nde);
break;
- }
case QSE_AWK_NDE_FOR:
- {
- if (run_for (rtx,
- (qse_awk_nde_for_t*)nde) == -1) return -1;
+ xret = run_for (rtx, (qse_awk_nde_for_t*)nde);
break;
- }
case QSE_AWK_NDE_FOREACH:
- {
- if (run_foreach (rtx,
- (qse_awk_nde_foreach_t*)nde) == -1) return -1;
+ xret = run_foreach (rtx, (qse_awk_nde_foreach_t*)nde);
break;
- }
case QSE_AWK_NDE_BREAK:
- {
- if (run_break (rtx,
- (qse_awk_nde_break_t*)nde) == -1) return -1;
+ xret = run_break (rtx, (qse_awk_nde_break_t*)nde);
break;
- }
case QSE_AWK_NDE_CONTINUE:
- {
- if (run_continue (rtx,
- (qse_awk_nde_continue_t*)nde) == -1) return -1;
+ xret = run_continue (rtx, (qse_awk_nde_continue_t*)nde);
break;
- }
case QSE_AWK_NDE_RETURN:
- {
- if (run_return (rtx,
- (qse_awk_nde_return_t*)nde) == -1) return -1;
+ xret = run_return (rtx, (qse_awk_nde_return_t*)nde);
break;
- }
case QSE_AWK_NDE_EXIT:
- {
- if (run_exit (rtx,
- (qse_awk_nde_exit_t*)nde) == -1) return -1;
+ xret = run_exit (rtx, (qse_awk_nde_exit_t*)nde);
break;
- }
case QSE_AWK_NDE_NEXT:
- {
- if (run_next (rtx,
- (qse_awk_nde_next_t*)nde) == -1) return -1;
+ xret = run_next (rtx, (qse_awk_nde_next_t*)nde);
break;
- }
case QSE_AWK_NDE_NEXTFILE:
- {
- if (run_nextfile (rtx,
- (qse_awk_nde_nextfile_t*)nde) == -1) return -1;
+ xret = run_nextfile (rtx, (qse_awk_nde_nextfile_t*)nde);
break;
- }
case QSE_AWK_NDE_DELETE:
- {
- if (run_delete (rtx,
- (qse_awk_nde_delete_t*)nde) == -1) return -1;
+ xret = run_delete (rtx, (qse_awk_nde_delete_t*)nde);
break;
- }
case QSE_AWK_NDE_RESET:
- {
- if (run_reset (rtx,
- (qse_awk_nde_reset_t*)nde) == -1) return -1;
+ xret = run_reset (rtx, (qse_awk_nde_reset_t*)nde);
break;
- }
-
case QSE_AWK_NDE_PRINT:
- {
- if (run_print (rtx,
- (qse_awk_nde_print_t*)nde) == -1) return -1;
+ if (rtx->awk->option & QSE_AWK_TOLERANT) goto __fallback__;
+ xret = run_print (rtx, (qse_awk_nde_print_t*)nde);
break;
- }
case QSE_AWK_NDE_PRINTF:
- {
- if (run_printf (rtx,
- (qse_awk_nde_print_t*)nde) == -1) return -1;
+ if (rtx->awk->option & QSE_AWK_TOLERANT) goto __fallback__;
+ xret = run_printf (rtx, (qse_awk_nde_print_t*)nde);
break;
- }
default:
- {
- qse_awk_val_t* v;
- v = eval_expression (rtx, nde);
- if (v == QSE_NULL) return -1;
-
- /* destroy the value if not referenced */
- qse_awk_rtx_refupval (rtx, v);
- qse_awk_rtx_refdownval (rtx, v);
-
+ __fallback__:
+ tmp = eval_expression (rtx, nde);
+ if (tmp == QSE_NULL) xret = -1;
+ else
+ {
+ /* destroy the value if not referenced */
+ qse_awk_rtx_refupval (rtx, tmp);
+ qse_awk_rtx_refdownval (rtx, tmp);
+ xret = 1;
+ }
break;
- }
}
- return 0;
+ return xret;
}
static int run_if (qse_awk_rtx_t* rtx, qse_awk_nde_if_t* nde)
@@ -2738,7 +2700,7 @@ static int run_print (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
qse_char_t* out = QSE_NULL;
const qse_char_t* dst;
qse_awk_val_t* v;
- int n;
+ int n, xret = 0;
QSE_ASSERT (
(nde->out_type == QSE_AWK_OUT_PIPE && nde->out != QSE_NULL) ||
@@ -2813,9 +2775,16 @@ static int run_print (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
QSE_STR_LEN(&rtx->inrec.line));
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
{
- if (out) QSE_AWK_FREE (rtx->awk, out);
- ADJERR_LOC (rtx, &nde->loc);
- return -1;
+ if (rtx->awk->option & QSE_AWK_TOLERANT)
+ {
+ xret = PRINT_IOERR;
+ }
+ else
+ {
+ if (out) QSE_AWK_FREE (rtx->awk, out);
+ ADJERR_LOC (rtx, &nde->loc);
+ return -1;
+ }
}
}
else
@@ -2842,9 +2811,16 @@ static int run_print (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
rtx->gbl.ofs.len);
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
{
- if (out) QSE_AWK_FREE (rtx->awk, out);
- ADJERR_LOC (rtx, &nde->loc);
- return -1;
+ if (rtx->awk->option & QSE_AWK_TOLERANT)
+ {
+ xret = PRINT_IOERR;
+ }
+ else
+ {
+ if (out) QSE_AWK_FREE (rtx->awk, out);
+ ADJERR_LOC (rtx, &nde->loc);
+ return -1;
+ }
}
}
@@ -2860,10 +2836,17 @@ static int run_print (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
rtx, nde->out_type, dst, v);
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
{
- if (out) QSE_AWK_FREE (rtx->awk, out);
- qse_awk_rtx_refdownval (rtx, v);
- ADJERR_LOC (rtx, &nde->loc);
- return -1;
+ if (rtx->awk->option & QSE_AWK_TOLERANT)
+ {
+ xret = PRINT_IOERR;
+ }
+ else
+ {
+ if (out) QSE_AWK_FREE (rtx->awk, out);
+ qse_awk_rtx_refdownval (rtx, v);
+ ADJERR_LOC (rtx, &nde->loc);
+ return -1;
+ }
}
qse_awk_rtx_refdownval (rtx, v);
@@ -2876,9 +2859,16 @@ static int run_print (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
rtx->gbl.ors.ptr, rtx->gbl.ors.len);
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
{
- if (out) QSE_AWK_FREE (rtx->awk, out);
- ADJERR_LOC (rtx, &nde->loc);
- return -1;
+ if (rtx->awk->option & QSE_AWK_TOLERANT)
+ {
+ xret = PRINT_IOERR;
+ }
+ else
+ {
+ if (out) QSE_AWK_FREE (rtx->awk, out);
+ ADJERR_LOC (rtx, &nde->loc);
+ return -1;
+ }
}
/* unlike printf, flushio() is not needed here as print
@@ -2887,7 +2877,7 @@ static int run_print (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
if (out) QSE_AWK_FREE (rtx->awk, out);
/*skip_write:*/
- return 0;
+ return xret;
}
static int run_printf (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
@@ -2896,7 +2886,7 @@ static int run_printf (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
const qse_char_t* dst;
qse_awk_val_t* v;
qse_awk_nde_t* head;
- int n;
+ int n, xret = 0;
QSE_ASSERT (
(nde->out_type == QSE_AWK_OUT_PIPE && nde->out != QSE_NULL) ||
@@ -2957,7 +2947,7 @@ static int run_printf (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
dst = (out == QSE_NULL)? QSE_T(""): out;
QSE_ASSERTX (nde->args != QSE_NULL,
- "a valid printf statement should have at least one argument. the parser must ensure this.");
+ "valid printf statement should have at least one argument. the parser must ensure this.");
if (nde->args->type == QSE_AWK_NDE_GRP)
{
@@ -2968,7 +2958,7 @@ static int run_printf (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
else head = nde->args;
QSE_ASSERTX (head != QSE_NULL,
- "a valid printf statement should have at least one argument. the parser must ensure this.");
+ "valid printf statement should have at least one argument. the parser must ensure this.");
v = eval_expression (rtx, head);
if (v == QSE_NULL)
@@ -2985,36 +2975,58 @@ static int run_printf (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
n = qse_awk_rtx_writeio_val (rtx, nde->out_type, dst, v);
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
{
- if (out != QSE_NULL) QSE_AWK_FREE (rtx->awk, out);
- qse_awk_rtx_refdownval (rtx, v);
- ADJERR_LOC (rtx, &nde->loc);
- return -1;
+ if (rtx->awk->option & QSE_AWK_TOLERANT)
+ {
+ xret = PRINT_IOERR;
+ }
+ else
+ {
+ if (out != QSE_NULL) QSE_AWK_FREE (rtx->awk, out);
+ qse_awk_rtx_refdownval (rtx, v);
+ ADJERR_LOC (rtx, &nde->loc);
+ return -1;
+ }
}
}
else
{
/* perform the formatted output */
- if (output_formatted (
+ n = output_formatted (
rtx, nde->out_type, dst,
((qse_awk_val_str_t*)v)->val.ptr,
((qse_awk_val_str_t*)v)->val.len,
- head->next) == -1)
+ head->next);
+ if (n <= -1)
{
- if (out != QSE_NULL) QSE_AWK_FREE (rtx->awk, out);
- qse_awk_rtx_refdownval (rtx, v);
- ADJERR_LOC (rtx, &nde->loc);
- return -1;
+ if (n == PRINT_IOERR) xret = n;
+ else
+ {
+ if (out != QSE_NULL) QSE_AWK_FREE (rtx->awk, out);
+ qse_awk_rtx_refdownval (rtx, v);
+ ADJERR_LOC (rtx, &nde->loc);
+ return -1;
+ }
}
}
qse_awk_rtx_refdownval (rtx, v);
/*skip_write:*/
- n = qse_awk_rtx_flushio (rtx, nde->out_type, dst);
+ if (qse_awk_rtx_flushio (rtx, nde->out_type, dst) <= -1)
+ {
+ if (rtx->awk->option & QSE_AWK_TOLERANT)
+ {
+ xret = PRINT_IOERR;
+ }
+ else
+ {
+ if (out != QSE_NULL) QSE_AWK_FREE (rtx->awk, out);
+ return -1;
+ }
+ }
if (out != QSE_NULL) QSE_AWK_FREE (rtx->awk, out);
-
- return n;
+ return xret;
}
static int output_formatted (
@@ -3030,7 +3042,17 @@ static int output_formatted (
if (ptr == QSE_NULL) return -1;
n = qse_awk_rtx_writeio_str (rtx, out_type, dst, ptr, len);
- if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/) return -1;
+ if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
+ {
+ if (rtx->awk->option & QSE_AWK_TOLERANT)
+ {
+ return PRINT_IOERR;
+ }
+ else
+ {
+ return -1;
+ }
+ }
return 0;
}
@@ -3145,7 +3167,9 @@ static qse_awk_val_t* eval_expression0 (qse_awk_rtx_t* run, qse_awk_nde_t* nde)
eval_lclidx,
eval_argidx,
eval_pos,
- eval_getline
+ eval_getline,
+ eval_print,
+ eval_printf
};
qse_awk_val_t* v;
@@ -3173,11 +3197,44 @@ static qse_awk_val_t* eval_expression0 (qse_awk_rtx_t* run, qse_awk_nde_t* nde)
static qse_awk_val_t* eval_group (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
{
+#if 0
/* eval_binop_in evaluates the QSE_AWK_NDE_GRP specially.
* so this function should never be reached. */
QSE_ASSERT (!"should never happen - NDE_GRP only for in");
SETERR_LOC (rtx, QSE_AWK_EINTERN, &nde->loc);
return QSE_NULL;
+#endif
+
+ /* a group can be evauluated in a normal context
+ * if a program is parsed with QSE_AWK_TOLERANT on.
+ * before the introduction of this option, the grouped
+ * expression was valid only coerced with the 'in'
+ * operator.
+ * */
+
+ /* when a group is evaluated in a normal context,
+ * we return the last expression as a value. */
+
+ qse_awk_val_t* val;
+ qse_awk_nde_t* np;
+
+ np = ((qse_awk_nde_grp_t*)nde)->body;
+
+ QSE_ASSERT (np != QSE_NULL);
+
+loop:
+ val = eval_expression (rtx, np);
+ if (val == QSE_NULL) return QSE_NULL;
+
+ np = np->next;
+ if (np)
+ {
+ qse_awk_rtx_refupval (rtx, val);
+ qse_awk_rtx_refdownval (rtx, val);
+ goto loop;
+ }
+
+ return val;
}
static qse_awk_val_t* eval_assignment (qse_awk_rtx_t* run, qse_awk_nde_t* nde)
@@ -6304,6 +6361,22 @@ skip_read:
return res;
}
+static qse_awk_val_t* eval_print (qse_awk_rtx_t* run, qse_awk_nde_t* nde)
+{
+ int n = run_print (run, (qse_awk_nde_print_t*)nde);
+ if (n == PRINT_IOERR) n = -1; /* let print return -1 */
+ else if (n <= -1) return QSE_NULL;
+ return qse_awk_rtx_makeintval (run, n);
+}
+
+static qse_awk_val_t* eval_printf (qse_awk_rtx_t* run, qse_awk_nde_t* nde)
+{
+ int n = run_printf (run, (qse_awk_nde_print_t*)nde);
+ if (n == PRINT_IOERR) n = -1; /* let print return -1 */
+ else if (n <= -1) return QSE_NULL;
+ return qse_awk_rtx_makeintval (run, n);
+}
+
static int __raw_push (qse_awk_rtx_t* run, void* val)
{
if (run->stack_top >= run->stack_limit)
diff --git a/qse/lib/awk/tree.c b/qse/lib/awk/tree.c
index ca131e9f..3bf905b7 100644
--- a/qse/lib/awk/tree.c
+++ b/qse/lib/awk/tree.c
@@ -149,6 +149,38 @@ static int print_tabs (qse_awk_t* awk, int depth)
return 0;
}
+static int print_printx (qse_awk_t* awk, qse_awk_nde_print_t* px)
+{
+ qse_cstr_t kw;
+
+ if (px->type == QSE_AWK_NDE_PRINT)
+ {
+ qse_awk_getkwname (awk, QSE_AWK_KWID_PRINT, &kw);
+ PUT_SRCSTRN (awk, kw.ptr, kw.len);
+ }
+ else
+ {
+ qse_awk_getkwname (awk, QSE_AWK_KWID_PRINTF, &kw);
+ PUT_SRCSTRN (awk, kw.ptr, kw.len);
+ }
+
+ if (px->args != QSE_NULL)
+ {
+ PUT_SRCSTR (awk, QSE_T(" "));
+ PRINT_EXPR_LIST (awk, px->args);
+ }
+
+ if (px->out != QSE_NULL)
+ {
+ PUT_SRCSTR (awk, QSE_T(" "));
+ PUT_SRCSTR (awk, print_outop_str[px->out_type]);
+ PUT_SRCSTR (awk, QSE_T(" "));
+ PRINT_EXPR (awk, px->out);
+ }
+
+ return 0;
+}
+
static int print_expr (qse_awk_t* awk, qse_awk_nde_t* nde)
{
qse_cstr_t kw;
@@ -650,8 +682,18 @@ static int print_expr (qse_awk_t* awk, qse_awk_nde_t* nde)
break;
}
+ case QSE_AWK_NDE_PRINT:
+ case QSE_AWK_NDE_PRINTF:
+ {
+ PUT_SRCSTR (awk, QSE_T("("));
+ if (print_printx (awk, (qse_awk_nde_print_t*)nde) <= -1) return -1;
+ PUT_SRCSTR (awk, QSE_T(")"));
+ break;
+ }
+
default:
{
+ qse_awk_seterrnum (awk, QSE_AWK_EINTERN, QSE_NULL);
return -1;
}
}
@@ -998,35 +1040,8 @@ static int print_stmt (qse_awk_t* awk, qse_awk_nde_t* p, int depth)
case QSE_AWK_NDE_PRINT:
case QSE_AWK_NDE_PRINTF:
{
- qse_awk_nde_print_t* px = (qse_awk_nde_print_t*)p;
-
PRINT_TABS (awk, depth);
-
- if (p->type == QSE_AWK_NDE_PRINT)
- {
- qse_awk_getkwname (awk, QSE_AWK_KWID_PRINT, &kw);
- PUT_SRCSTRN (awk, kw.ptr, kw.len);
- }
- else
- {
- qse_awk_getkwname (awk, QSE_AWK_KWID_PRINTF, &kw);
- PUT_SRCSTRN (awk, kw.ptr, kw.len);
- }
-
- if (px->args != QSE_NULL)
- {
- PUT_SRCSTR (awk, QSE_T(" "));
- PRINT_EXPR_LIST (awk, px->args);
- }
-
- if (px->out != QSE_NULL)
- {
- PUT_SRCSTR (awk, QSE_T(" "));
- PUT_SRCSTR (awk, print_outop_str[px->out_type]);
- PUT_SRCSTR (awk, QSE_T(" "));
- PRINT_EXPR (awk, px->out);
- }
-
+ if (print_printx (awk, (qse_awk_nde_print_t*)p) <= -1) return -1;
PUT_SRCSTR (awk, QSE_T(";"));
PUT_NL (awk);
break;
@@ -1038,6 +1053,7 @@ static int print_stmt (qse_awk_t* awk, qse_awk_nde_t* p, int depth)
PRINT_EXPR (awk, p);
PUT_SRCSTR (awk, QSE_T(";"));
PUT_NL (awk);
+ break;
}
}
diff --git a/qse/regress/awk/lang-047.awk b/qse/regress/awk/lang-047.awk
new file mode 100644
index 00000000..a5109f18
--- /dev/null
+++ b/qse/regress/awk/lang-047.awk
@@ -0,0 +1,29 @@
+BEGIN {
+ print print print print 10;
+ print (print 10 > "/tmp/should/not/be/creatable");
+ if ((print 10 > "/tmp/should/not/be/creatable") <= -1)
+ print "FAILURE";
+ else
+ print "SUCCESS";
+
+ print "------------------------------------";
+ a = 1 (print 10 > "/tmp/should/not/be/creatable");
+ print a;
+ print "------------------------------------";
+ b = ++a print 10;
+ printf "%d\n", b;
+ print "------------------------------------";
+ printf ("%d\n", b + (((print print print 30 + 50)) + 40));
+ print "------------------------------------";
+ printf ("%d\n", b + (((print print print 30 50)) + 40));
+
+ print "------------------------------------";
+ $(print 0 > "/dev/null") = "this is wonderful"; print $0;
+ print "------------------------------------";
+ $(getline dummy > "/dev/zero") = "that"; print $0;
+
+ print "------------------------------------";
+ x[0]=20; abc=(print ("hello", "world", (1, abc=20, abc=45))) in x; print abc
+}
+
+
diff --git a/qse/regress/awk/regress.out b/qse/regress/awk/regress.out
index b9142333..ce8e7e89 100644
--- a/qse/regress/awk/regress.out
+++ b/qse/regress/awk/regress.out
@@ -2361,6 +2361,66 @@ nan
2 DDDDDDDDDDDDDDDDDDDDDDDDD
1 DDDDDDDDDDDDDDDDDDDDDDDDD
--------------------------------------------------------------------------------
+[CMD] qseawk --newline=on --tolerant=on -d- -f lang-047.awk &1
+--------------------------------------------------------------------------------
+BEGIN {
+ print (print (print (print 10)));
+ print (print 10 > "/tmp/should/not/be/creatable");
+ if (((print 10 > "/tmp/should/not/be/creatable") <= -1))
+ print "FAILURE";
+ else
+ print "SUCCESS";
+ print "------------------------------------";
+ a = (1 (print 10 > "/tmp/should/not/be/creatable"));
+ print a;
+ print "------------------------------------";
+ b = (++(a) (print 10));
+ printf "%d\n",b;
+ print "------------------------------------";
+ printf ("%d\n",(b + ((print (print (print 80))) + 40)));
+ print "------------------------------------";
+ printf ("%d\n",(b + ((print (print (print (30 50)))) + 40)));
+ print "------------------------------------";
+ $(print 0 > "/dev/null") = "this is wonderful";
+ print $0;
+ print "------------------------------------";
+ $((getline dummy) > "/dev/zero") = "that";
+ print $0;
+ print "------------------------------------";
+ x[0] = 20;
+ abc = ((print ("hello","world",(1,abc = 20,abc = 45))) in x);
+ print abc;
+}
+
+10
+0
+0
+0
+-1
+FAILURE
+------------------------------------
+1-1
+------------------------------------
+10
+20
+------------------------------------
+80
+0
+0
+60
+------------------------------------
+3050
+0
+0
+60
+------------------------------------
+this is wonderful
+------------------------------------
+that is wonderful
+------------------------------------
+hello world 45
+1
+--------------------------------------------------------------------------------
[CMD] qseawk --newline=on -F: -f columnate.awk passwd.dat &1
--------------------------------------------------------------------------------
root x 0 0 root /root /bin/bash
diff --git a/qse/regress/awk/regress.out.xma b/qse/regress/awk/regress.out.xma
index 4bd40932..ea2fce1c 100644
--- a/qse/regress/awk/regress.out.xma
+++ b/qse/regress/awk/regress.out.xma
@@ -2361,6 +2361,66 @@ nan
2 DDDDDDDDDDDDDDDDDDDDDDDDD
1 DDDDDDDDDDDDDDDDDDDDDDDDD
--------------------------------------------------------------------------------
+[CMD] qseawk -m 500000 --newline=on --tolerant=on -d- -f lang-047.awk &1
+--------------------------------------------------------------------------------
+BEGIN {
+ print (print (print (print 10)));
+ print (print 10 > "/tmp/should/not/be/creatable");
+ if (((print 10 > "/tmp/should/not/be/creatable") <= -1))
+ print "FAILURE";
+ else
+ print "SUCCESS";
+ print "------------------------------------";
+ a = (1 (print 10 > "/tmp/should/not/be/creatable"));
+ print a;
+ print "------------------------------------";
+ b = (++(a) (print 10));
+ printf "%d\n",b;
+ print "------------------------------------";
+ printf ("%d\n",(b + ((print (print (print 80))) + 40)));
+ print "------------------------------------";
+ printf ("%d\n",(b + ((print (print (print (30 50)))) + 40)));
+ print "------------------------------------";
+ $(print 0 > "/dev/null") = "this is wonderful";
+ print $0;
+ print "------------------------------------";
+ $((getline dummy) > "/dev/zero") = "that";
+ print $0;
+ print "------------------------------------";
+ x[0] = 20;
+ abc = ((print ("hello","world",(1,abc = 20,abc = 45))) in x);
+ print abc;
+}
+
+10
+0
+0
+0
+-1
+FAILURE
+------------------------------------
+1-1
+------------------------------------
+10
+20
+------------------------------------
+80
+0
+0
+60
+------------------------------------
+3050
+0
+0
+60
+------------------------------------
+this is wonderful
+------------------------------------
+that is wonderful
+------------------------------------
+hello world 45
+1
+--------------------------------------------------------------------------------
[CMD] qseawk -m 500000 --newline=on -F: -f columnate.awk passwd.dat &1
--------------------------------------------------------------------------------
root x 0 0 root /root /bin/bash
diff --git a/qse/regress/awk/regress.sh.in b/qse/regress/awk/regress.sh.in
index 89b052a2..dd3eb118 100755
--- a/qse/regress/awk/regress.sh.in
+++ b/qse/regress/awk/regress.sh.in
@@ -173,6 +173,7 @@ PROGS="
lang-044.awk!lang-044.dat!!--newline=on -d-
lang-045.awk!!!--newline=on -d-
lang-046.awk!lang-046.dat2!!--newline=on -d- -vdatadir=@abs_srcdir@ -vdatafile=lang-046.dat1
+ lang-047.awk!!!--newline=on --tolerant=on -d-
columnate.awk!passwd.dat!!--newline=on -F:
levenshtein-utests.awk!!!--newline=on --include=on