diff --git a/polymer/eduke32/source/lunatic/control.lua b/polymer/eduke32/source/lunatic/control.lua index ddccbdff6..3f702bfb7 100644 --- a/polymer/eduke32/source/lunatic/control.lua +++ b/polymer/eduke32/source/lunatic/control.lua @@ -9,11 +9,13 @@ local math = require("math") local geom = require("geom") local con_lang = require("con_lang") +local byte = require("string").byte local setmetatable = setmetatable local assert = assert local error = error local print = print +local tostring = tostring local type = type local unpack = unpack @@ -455,12 +457,13 @@ local function strlen(cstr) assert(false) end +-- NOTE: dst==src is OK (effectively a no-op) local function strcpy(dst, src) local i=-1 repeat i = i+1 dst[i] = src[i] - until (src[i]~=0) + until (src[i]==0) end function _qstrlen(qnum) @@ -473,16 +476,42 @@ function _qstrcpy(qdst, qsrc) strcpy(cstr_dst, cstr_src) end +-- NOTE: qdst==qsrc is OK (duplicates the quote) function _qstrcat(qdst, qsrc) local cstr_dst = bcheck.quote_idx(qdst) local cstr_src = bcheck.quote_idx(qsrc) - local i, j = strlen(cstr_dst), 0 - while (i < MAXQUOTELEN-1) do - cstr_dst[i] = cstr_src[j] + if (cstr_src[0]==0) then + return + end + + if (cstr_dst[0]==0) then + return strcpy(cstr_dst, cstr_src) + end + + -- From here on: destination and source quote (potentially aliased) are + -- nonempty. + + local slen_dst = strlen(cstr_dst) + assert(slen_dst <= MAXQUOTELEN-1) + + if (slen_dst == MAXQUOTELEN-1) then + return + end + + local i = slen_dst + local j = 0 + + repeat + -- NOTE: don't copy the first char yet, so that the qdst==qsrc case + -- works correctly. i = i+1 j = j+1 - end + cstr_dst[i] = cstr_src[j] + until (i >= MAXQUOTELEN-1 or cstr_src[j]==0) + + -- Now copy the first char! + cstr_dst[slen_dst] = cstr_src[0] cstr_dst[i] = 0 end @@ -493,7 +522,7 @@ function _qsprintf(qdst, qsrc, ...) local src = bcheck.quote_idx(qsrc) local vals = {...} - local i, j, vi = 0, 0, 0 + local i, j, vi = 0, 0, 1 while (true) do local ch = src[j] @@ -503,13 +532,13 @@ function _qsprintf(qdst, qsrc, ...) break end - if (ch=='%') then + if (ch==byte'%') then local nch = src[j+1] - if (nch=='d' or (nch=='l' and src[j+2]=='d')) then + if (nch==byte'd' or (nch==byte'l' and src[j+2]==byte'd')) then -- number didfmt = true - if (vi == #vals) then + if (vi > #vals) then break end @@ -521,23 +550,24 @@ function _qsprintf(qdst, qsrc, ...) ffi.copy(buf+i, numstr, ncopied) i = i+ncopied - j = j+1+(nch=='d' and 1 or 2) - elseif (nch=='s') then + j = j+1+(nch==byte'd' and 1 or 2) + elseif (nch==byte's') then -- string didfmt = true - - if (vi == #vals) then + if (vi > #vals) then break end local k = -1 local tmpsrc = bcheck.quote_idx(vals[vi]) + vi = vi+1 + i = i-1 repeat + i = i+1 k = k+1 buf[i] = tmpsrc[k] - i = i+1 - until (i < MAXQUOTELEN-1 and tmpsrc[k]~=0) + until (i >= MAXQUOTELEN-1 or tmpsrc[k]==0) j = j+2 end diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index 33b84aea7..db1d48c64 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -747,11 +747,13 @@ function Cmd.definequote(qnum, quotestr) quotestr = stripws(quotestr) - if (ffi) then - if (#quotestr >= conl.MAXQUOTELEN) then - warnprintf("quote %d truncated to %d characters.", conl.MAXQUOTELEN-1) - end + if (#quotestr >= conl.MAXQUOTELEN) then + -- NOTE: Actually, C_DefineQuote takes care of this! That is, + -- standalone, the string isn't truncated. + warnprintf("quote %d truncated to %d characters.", qnum, conl.MAXQUOTELEN-1) + end + if (ffi) then ffiC.C_DefineQuote(qnum, quotestr) end @@ -2720,8 +2722,11 @@ else for _, fname in ipairs(filenames) do local ok, msg = pcall(do_include_file, "", fname) - if (not ok) then - print_on_failure(msg) + if (not ok or g_numerrors > 0) then + if (not ok) then + -- Unexpected error in the Lua code (i.e. a bug here). + print_on_failure(msg) + end return nil end end diff --git a/polymer/eduke32/source/lunatic/test/quote_ops.con b/polymer/eduke32/source/lunatic/test/quote_ops.con new file mode 100644 index 000000000..d9634359d --- /dev/null +++ b/polymer/eduke32/source/lunatic/test/quote_ops.con @@ -0,0 +1,66 @@ +// Don't try in C-CON! + +gamevar slen 0 0 +gamevar n -12345678 0 // a number having length 9 in the decimal representation + +// overlong string at definition (should warn) +definequote 400 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789=123456789- + +onevent EVENT_ENTERLEVEL + redefinequote 114 QWE + + redefinequote 115 012345678| + // qstrcat X Y, where X==Y: duplicates the quote + qstrcat 115 115 + userquote 115 // "012345678|012345678|" + + // simple qstrcpy test + qstrcpy 116 115 + userquote 116 // "012345678|012345678|" + + redefinequote 117 %s -- %d -- %s -- %d slen=%d + // test: + // - same destination quote as the format quote + // - multiple conversions + qstrlen slen 115 // -> 10 + qsprintf 117 /*<-*/ 117 /*args:*/ 116 9999 114 5555 slen + userquote 117 // "012345678|012345678| -- 9999 -- QWE -- 5555 slen=10" + + redefinequote 117 %s -- %d -- %s -- %d slen=%d + // test: + // - same destination quote as on of the source quotes under %s conversion + qsprintf 116 /*<-*/ 117 /*args:*/ 116 9999 116 5555 slen + userquote 116 // "012345678|012345678| -- 9999 -- 012345678|012345678| -- 5555 slen=10" + + // aliased qstrcpy test (a no-op) + qstrcpy 115 115 + userquote 115 // still "012345678|012345678|" + + qstrcat 115 115 // len 40 + qstrcat 115 115 // len 80 + qstrcat 115 115 + // result now: 12 x "012345678|" concantenated with "0123456" (= total length 127 = MAXQUOTELEN-1) + qstrcat 115 115 // this one is redundant, but must not overflow the quote buffer + userquote 115 + + redefinequote 117 X%s + qsprintf 117 /*<-*/ 117 /*args:*/ 115 + userquote 117 // result: "X" .. 12 x "012345678|" .. "012345" (= total length 127 = MAXQUOTELEN-1) + + // 32 %d (or %ld) conversion + redefinequote 117 %ld|%d|%ld|%d|%ld|%ld|%d|%ld|%d|%ld/%ld|%d|%ld|%d|%ld|%ld|%d|%ld|%d|%ld/%ld|%d|%ld|%d|%ld|%ld|%d|%ld|%d|%ld/%d/%d + // string shorter than MAXQUOTELEN-1: + qsprintf 116 /*<-*/ 117 /*args:*/ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // result: "0|1|2|3|4|5|6|7|8|9/0|1|2|3|4|5|6|7|8|9/0|1|2|3|4|5|6|7|8|9/0/1" + userquote 116 + + // string longer than MAXQUOTELEN-1: + qsprintf 116 /*<-*/ 117 /*args:*/ n n n n n n n n n n /**/ n n n n n n n n n n /**/ n n + userquote 116 + // result: 10 x "-12345678" .. "/-12345678|-12345678|-123456" (= total length 127 = MAXQUOTELEN-1) + + // overlong string at redefinition + redefinequote 117 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789=123456789- + userquote 117 // should end in "=1234567" (= total length 127 = MAXQUOTELEN-1) + userquote 400 // same thing +endevent