diff --git a/jim-aio.c b/jim-aio.c index 473b4e7c..77888dab 100644 --- a/jim-aio.c +++ b/jim-aio.c @@ -1127,6 +1127,15 @@ static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv) return JIM_OK; } +/* Like strstr() but optimised in the case the the needle is of length 1 */ +static const char *jim_strstr(const char *haystack, int haylen, const char *needle, int needlen) +{ + if (needlen == 1) { + return (const char *)memchr(haystack, needle[0], haylen); + } + return strstr(haystack, needle); +} + static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); @@ -1134,8 +1143,44 @@ static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv) int len; int nb; unsigned flags = AIO_ONEREAD; - char *nl = NULL; + const char *nl = NULL; int offset = 0; + long keepnl = 0; + const char *nlstr = "\n"; + int nlstrlen = 1; + + while (argc >= 2) { + enum {OPT_EOL, OPT_KEEP}; + static const char * const options[] = { + "-eol", + "-keep", + NULL + }; + int opt; + + /* Expect an option here */ + if (*Jim_String(argv[0]) != '-') { + return JIM_USAGE; + } + + if (Jim_GetEnum(interp, argv[0], options, &opt, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { + return JIM_ERR; + } + + switch (opt) { + case OPT_EOL: + nlstr = Jim_GetString(argv[1], &nlstrlen); + break; + + case OPT_KEEP: + if (Jim_GetLong(interp, argv[1], &keepnl) != JIM_OK) { + return JIM_ERR; + } + break; + } + argc -= 2; + argv += 2; + } errno = 0; @@ -1148,12 +1193,12 @@ static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv) while (!aio_eof(af)) { if (af->readbuf) { const char *pt = Jim_GetString(af->readbuf, &len); - nl = memchr(pt + offset, '\n', len - offset); + nl = jim_strstr(pt + offset, len - offset, nlstr, nlstrlen); if (nl) { /* got a line */ - objPtr = Jim_NewStringObj(interp, pt, nl - pt); - /* And consume it plus the newline */ - aio_consume(af->readbuf, nl - pt + 1); + objPtr = Jim_NewStringObj(interp, pt, nl - pt + (keepnl ? nlstrlen : 0)); + /* And consume it plus the eol */ + aio_consume(af->readbuf, nl - pt + nlstrlen); break; } offset = len; @@ -2169,10 +2214,10 @@ static const jim_subcmd_type aio_command_table[] = { /* Description: Internal command to return the taint of the channel. */ }, { "gets", - "?var?", + "?-eol ? ?-keep 0|1? ?var?", aio_cmd_gets, 0, - 1, + -1, /* Description: Read one line and return it or store it in the var */ }, { "puts", diff --git a/jim_tcl.txt b/jim_tcl.txt index adf68a1f..cbcf5634 100644 --- a/jim_tcl.txt +++ b/jim_tcl.txt @@ -67,6 +67,7 @@ Changes since 0.83 #. New `info usage` to return the usage for a proc or native command #. New `info aliases` to list all aliases #. `expr` supports new +'=*'+ and +'=~'+ matching operators (see <<_expressions,EXPRESSIONS>>) +#. `aio gets` supports +*-eol*+ and +*-keep*+ Changes between 0.82 and 0.83 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -5163,9 +5164,9 @@ aio to flush the remaining data. As long as the eventloop runs (`vwait`, `update`), the write data will be automatically flushed. -+$handle *gets* '?varName?'+:: - Read one line from the cannel and return it or store it in the - var A terminating newline character is discarded. If +'varName'+ ++$handle *gets* ?*-eol* 'str'? ?*-keep 0|1*? '?varName?'+:: + Read one line from the channel and return it or store it in the + variable. A terminating newline character is discarded. If +'varName'+ is specified, then the line is placed in the variable by that name and the return value is a count of the number of characters read (not including the newline). If the end of the file is reached @@ -5179,6 +5180,14 @@ aio the file is not a newline character, then `gets` behaves as if there were an additional newline character at the end of the file. + :: + If +*-eol*+ is given, the end of line is considered to be the + given string instead of the newline character. If +*-keep 1*+ + is given, the end-of-line string is not discarded and instead + is returned as part of the result (and if the return value is the + character count, it includes the end-of-line string). + The default is +*-keep 0*+. + +$handle *getfd*+:: Returns the underlying file descriptor. On Unix platforms this is a small integer. diff --git a/tests/aio.test b/tests/aio.test index 3d229648..aa44d942 100644 --- a/tests/aio.test +++ b/tests/aio.test @@ -11,9 +11,9 @@ $f puts test-data $f close # create a test file file with several lines set f [open copy.in wb] -$f puts line1 -$f puts line2 -$f puts line3 +$f puts line1xy +$f puts line2xy +$f puts line3xy $f close set f [open testdata.in rb] @@ -94,10 +94,82 @@ test aio-3.2 {copy bad length} -body { set badvar a -test aio-4.1 {gets invalid var} -body { +test aio-4.1 {gets usage} -body { + $f gets abc def +} -returnCodes error -match glob -result {wrong # args: should be "* gets ?-eol ? ?-keep 0|1? ?var?"} + +test aio-4.2 {gets invalid option} -body { + $f gets -xyz 3 def +} -returnCodes error -result {bad option "-xyz": must be -eol, or -keep} + +test aio-4.3 {gets invalid var} -body { $f gets badvar(abc) } -returnCodes error -result {can't set "badvar(abc)": variable isn't array} +test aio-4.4 {gets -eol} -body { + set ff [open copy.in] + set rc [$ff gets -eol y\n buf] + $ff close + list $rc $buf +} -result {6 line1x} + +test aio-4.5 {gets -eol} -body { + set ff [open copy.in] + set rc [$ff gets -eol 2xy\n buf] + $ff close + list $rc $buf +} -result {12 {line1xy +line}} + +test aio-4.6 {gets -keep} -body { + set ff [open copy.in] + set rc [$ff gets -keep 1 buf] + $ff close + list $rc $buf +} -result {8 {line1xy +}} + +test aio-4.7 {gets -eol -keep} -body { + set ff [open copy.in] + set rc [$ff gets -keep 1 -eol 2xy\n buf] + $ff close + list $rc $buf +} -result {16 {line1xy +line2xy +}} + +test aio-4.8 {gets -eol no var} -body { + set ff [open copy.in] + set buf [$ff gets -eol xy -keep 1] + $ff close + set buf +} -result line1xy + +test aio-4.9 {gets -keep 0 count} -body { + set ff [open copy.in] + set count [$ff gets -keep 0 buf] + $ff close + list $count $buf +} -result {7 line1xy} + +test aio-4.10 {gets -keep 1 count} -body { + set ff [open copy.in] + set count [$ff gets -keep 1 buf] + $ff close + list $count $buf +} -result {8 {line1xy +}} + +test aio-4.11 {gets -eol multiple calls} -body { + set ff [open copy.in] + set lines {} + lappend lines [$ff gets -eol xy\n] + lappend lines [$ff gets -eol xy\n] + lappend lines [$ff gets -eol xy\n] + $ff close + set lines +} -result {line1 line2 line3} + test aio-5.1 {puts usage} -body { stdout puts -badopt abc } -returnCodes error -result {wrong # args: should be "stdout puts ?-nonewline? str"} @@ -188,19 +260,19 @@ test copyto-1.1 {basic copyto} { set result [list [$ff gets] [$ff gets] [$ff gets]] $ff close set result -} {line1 line2 line3} +} {line1xy line2xy line3xy} test copyto-1.2 {copyto with limit} { set in [open copy.in] set out [open copy.out w] - $in copyto $out 8 + $in copyto $out 10 $in close $out close set ff [open copy.out] set result [list [$ff gets] [$ff gets] [$ff gets]] $ff close set result -} {line1 li {}} +} {line1xy li {}} test copyto-1.3 {copyto after gets} { set in [open copy.in] @@ -213,7 +285,7 @@ test copyto-1.3 {copyto after gets} { set result [list [$ff gets] [$ff gets] [$ff gets]] $ff close set result -} {line2 line3 {}} +} {line2xy line3xy {}} test copyto-1.4 {copyto after read} { set in [open copy.in] @@ -226,7 +298,7 @@ test copyto-1.4 {copyto after read} { set result [list [$ff gets] [$ff gets] [$ff gets]] $ff close set result -} {e1 line2 line3} +} {e1xy line2xy line3xy} test copyto-1.5 {copyto after gets, seek} { set in [open copy.in] @@ -240,7 +312,7 @@ test copyto-1.5 {copyto after gets, seek} { set result [list [$ff gets] [$ff gets] [$ff gets]] $ff close set result -} {ne1 line2 line3} +} {ne1xy line2xy line3xy} test copyto-1.6 {copyto from pipe} { set in [open "|cat copy.in"] @@ -252,7 +324,7 @@ test copyto-1.6 {copyto from pipe} { set result [list [$ff gets] [$ff gets] [$ff gets]] $ff close set result -} {line1 line2 line3} +} {line1xy line2xy line3xy} test copyto-1.6 {copyto to pipe} { set out [open "|cat >copy.out" w] @@ -264,7 +336,7 @@ test copyto-1.6 {copyto to pipe} { set result [list [$ff gets] [$ff gets] [$ff gets]] $ff close set result -} {line1 line2 line3} +} {line1xy line2xy line3xy} # Creates a child process and returns {pid writehandle} # The child expects to read $numlines lines of input and exits with a return