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