commit 3a4930680e0b3b7e67dc7caff51cd9b2064e80a3 from: Aleksey Ryndin date: Thu Aug 01 12:13:30 2024 UTC Ref: tests/ commit - 13f23f154c199d25117407bd5be120c16498a74a commit + 3a4930680e0b3b7e67dc7caff51cd9b2064e80a3 blob - ef6133e32665cd288f65dc489a0eb2ef4b2e6305 blob + a82c515de940a30810efca70c11eba80bb99cdf2 --- gtransl.retro +++ gtransl.retro @@ -2,9 +2,10 @@ ~~~ '\r\n s:format 'CRLF s:const -:user-input '10_🔁_Text: CRLF s:append s:put #0 unix:exit ; -:not-found '51_Not_found CRLF s:append s:put #0 unix:exit ; -:bad-request '59_Bad_request CRLF s:append s:put #0 unix:exit ; +:vgi:user-input '10_🔁_Text: CRLF s:append s:put #0 unix:exit ; +:vgi:not-found '51_Not_found CRLF s:append s:put #0 unix:exit ; +:vgi:bad-request '59_Bad_request CRLF s:append s:put #0 unix:exit ; +:vgi:tmp-failure '40_Unexpected_error CRLF s:append s:put #0 unix:exit ; ~~~ Разбираем запрашиваемый URL из стандратного потока ввода: @@ -15,17 +16,17 @@ * последним извлекаем query строку с текстом, который требуется перевести (константа QUERY). ~~~ :cut-required-path (s-s) - dup '/gtransl/ s:index/string dup n:negative? [ not-found ] if + dup '/gtransl/ s:index/string dup n:negative? [ vgi:not-found ] if over s:length swap - '/gtransl/ s:length - #1 - s:right ; :drop-fist-char (s-s) dup s:length #1 - s:right ; :extract-path-part (ss-s) - swap dup $/ s:index/char n:negative? [ not-found ] if + swap dup $/ s:index/char n:negative? [ vgi:not-found ] if $/ s:split/char rot s:const drop-fist-char ; -:user-input? dup s:length n:zero? [ user-input ] if ; -:required-query (s-s) dup #0 s:fetch $? -eq? [ not-found ] if ; +:user-input? dup s:length n:zero? [ vgi:user-input ] if ; +:required-query (s-s) dup #0 s:fetch $? -eq? [ vgi:not-found ] if ; :extract-query (s-) required-query drop-fist-char 'QUERY s:const ; s:get cut-required-path 'SL extract-path-part 'TL extract-path-part @@ -37,7 +38,7 @@ s:get что бы исключить возможность исполнения произвольной команды. (Command Injection) ~~~ -:check-lang (s-) [ $a $z n:between? [ not-found ] -if ] s:for-each ; +:check-lang (s-) [ $a $z n:between? [ vgi:not-found ] -if ] s:for-each ; SL check-lang TL check-lang @@ -52,7 +53,7 @@ TL check-lang dup $. eq? [ drop ] if; dup $- eq? [ drop ] if; $~ eq? [ ] if; - bad-request + vgi:bad-request ] s:for-each ; QUERY check-query @@ -61,25 +62,27 @@ QUERY check-query Результат выполнения команды curl будем хранить в буфере из 16-ти килоячеек (+ ячейка для ASCII:NULL) ~~~ -:BUFFER_SIZE #16384 ; -'Buffer d:create BUFFER_SIZE #1 + allot +:html:BUFFER_SIZE #16384 ; +'html:Buffer d:create html:BUFFER_SIZE #1 + allot ~~~ -Для выполнения HTTPS-запроса используем curl. +Для выполнения HTTPS-запроса используем curl или (для тестов) cat из существующего файла с HTML-ответом. Чтение результата выполнения команды curl по переданному описателю пайпа происходит до чтения 0 символа (не байта). Считаем, что в этом случае пайп закрыт другой стороной. ~~~ -:read-curl-output (h-) BUFFER_SIZE [ dup file:read/c 0; buffer:add ] times ; -:do-curl - Buffer buffer:set - 'CURLRESP s:empty [ unix:getenv ] sip - dup s:length n:zero? [ - drop - QUERY TL SL 'curl_-s_--url-query_sl=%s_--url-query_tl=%s_"https://translate.google.com/m?q=%s" s:format - file:R unix:popen read-curl-output unix:pclose - ] if; - [ buffer:add ] s:for-each +:html:read-buffer (h-h) + html:Buffer buffer:set html:BUFFER_SIZE [ dup file:read/c 0; buffer:add ] times ; +:html:pipe-command-curl (-s) + QUERY TL SL 'curl_-m_5_-s_--url-query_sl=%s_--url-query_tl=%s_"https://translate.google.com/m?q=%s" s:format +; +:html:pipe-command (-s) + 'GTRANSLRESPFILE s:empty [ unix:getenv ] sip dup s:length n:zero? + [ drop html:pipe-command-curl ] [ 'cat_%s s:format ] choose +; +:html:do-request + html:pipe-command file:R unix:popen html:read-buffer unix:pclose +; ~~~ Рабоче-крестьянский парсинг HTML: @@ -91,9 +94,15 @@ QUERY check-query Затем в результате заменяем наиболее часто-используемые escape-последовательности HTML на символы. ~~~ -:extract-result-container (s-s) - '"result-container" s:split/string drop - $> s:split/char drop +:html:get-result-container (-s) + html:do-request + + html:Buffer '"result-container" s:index/string dup n:negative? [ vgi:tmp-failure ] if + html:Buffer s:length swap - html:Buffer swap s:right + + dup $> s:index/char dup n:negative? [ vgi:tmp-failure ] if + swap dup s:length rot - s:right + dup $< s:index/char #1 - #1 swap s:substr '& '& s:replace-all '& '& s:replace-all '& '& s:replace-all @@ -106,7 +115,7 @@ QUERY check-query Отдаём результат в формате "text/gemini" в стандартный поток вывода ~~~ -do-curl buffer:start extract-result-container +html:get-result-container '20_text/gemini CRLF s:append s:put '#_🔁_GTransl CRLF s:append s:put CRLF s:put blob - 297fd45b2a2a2a466021ce0e1228e39d3bc6f18d (mode 755) blob + /dev/null --- tests.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh - -echo "Not found tests..." -echo "gemini://any-key.press/vgi" | ./gtransl.retro | head -n 1 | \ - grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" -echo "gemini://any-key.press/vgi/gtransl" | ./gtransl.retro | \ - grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" -echo "gemini://any-key.press/vgi/gtransl/" | ./gtransl.retro | \ - head -n 1 | grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" -echo "gemini://any-key.press/vgi/gtransX/auto/ru/?hello" | ./gtransl.retro | head -n 1 | \ - grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" -echo "gemini://any-key.press/vgi/gtransl/?\" ; ls/ru/?hello" | ./gtransl.retro | head -n 1 | \ - grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" -echo "gemini://any-key.press/vgi/gtransl/auto/?\" ; ls/?hello" | ./gtransl.retro | head -n 1 | \ - grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" - -echo "Bad request tests..." -echo "gemini://any-key.press/vgi/gtransl/auto/ru/?\" ; ls" | ./gtransl.retro | head -n 1 | \ - grep "^59 Bad request" > /dev/null && echo "passed" || echo "FAILED" - -echo "Escaping tests..." -echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ - CURLRESP="
amp=& lt=< gt=> quot=" apos='
" ./gtransl.retro | \ - head -n 8 | tail -n 1 | \ - grep "^amp=& lt=< gt=> quot=\" apos='" > /dev/null && echo "passed" || echo "FAILED" -echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ - CURLRESP="
amp=& lt=< gt=> quot=" apos='
" ./gtransl.retro | \ - head -n 8 | tail -n 1 | \ - grep "^amp=& lt=< gt=> quot=\" apos='" > /dev/null && echo "passed" || echo "FAILED" -echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ - CURLRESP="
amp=& lt=< gt=> quot=" apos='
" ./gtransl.retro | \ - head -n 8 | tail -n 1 | \ - grep "^amp=& lt=< gt=> quot=\" apos='" > /dev/null && echo "passed" || echo "FAILED" -echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ - CURLRESP="
lt=< gt=>
" ./gtransl.retro | \ - head -n 8 | tail -n 1 | \ - grep "^lt=< gt=>" > /dev/null && echo "passed" || echo "FAILED" - -echo "Multiline tests..." -echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ - CURLRESP="
hello -world
" ./gtransl.retro | \ - head -n 8 | tail -n 1 | \ - grep "^hello$" > /dev/null && echo "passed" || echo "FAILED" -echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ - CURLRESP="
hello -world
" ./gtransl.retro | \ - head -n 9 | tail -n 1 | \ - grep "^world" > /dev/null && echo "passed" || echo "FAILED" blob - /dev/null blob + 8fec8baad98ef610e4eb3dc983a414fa8dc107c8 (mode 644) Binary files /dev/null and tests/.tests.sh.swp differ blob - /dev/null blob + 4f65256c3c73a6da75fefcffe460af7b7ae6a46b (mode 644) --- /dev/null +++ tests/resp_escaped-01.html @@ -0,0 +1,112 @@ +Google Translate
Translate
amp=& lt=< gt=> quot=" apos='
blob - /dev/null blob + e32d49970a329b89d1ca194944db520f548d1908 (mode 644) --- /dev/null +++ tests/resp_escaped-02.html @@ -0,0 +1,112 @@ +Google Translate
Translate
amp=& lt=< gt=> quot=" apos='
blob - /dev/null blob + 3f9e776a95240db4099fa1a7f2c2386ccb44f4c2 (mode 644) --- /dev/null +++ tests/resp_escaped-03.html @@ -0,0 +1,112 @@ +Google Translate
Translate
amp=& lt=< gt=> quot=" apos='
blob - /dev/null blob + e6f55a4a57507487e053336af9f1a15cd8189d6b (mode 644) --- /dev/null +++ tests/resp_escaped-04.html @@ -0,0 +1,112 @@ +Google Translate
Translate
lt=< gt=>
blob - /dev/null blob + bdff46dee47af68c5023bffe70612e834f0ac3a2 (mode 644) --- /dev/null +++ tests/resp_multiline-01.html @@ -0,0 +1,113 @@ +Google Translate
Translate
hello +world
blob - /dev/null blob + da2a7cd65e33a46bf0e8919df18a0640a8d30a9b (mode 755) --- /dev/null +++ tests/tests.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +echo "Not found tests..." +echo "gemini://any-key.press/vgi" | ./gtransl.retro | head -n 1 | \ + grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" +echo "gemini://any-key.press/vgi/gtransl" | ./gtransl.retro | \ + grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" +echo "gemini://any-key.press/vgi/gtransl/" | ./gtransl.retro | \ + head -n 1 | grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" +echo "gemini://any-key.press/vgi/gtransX/auto/ru/?hello" | ./gtransl.retro | head -n 1 | \ + grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" +echo "gemini://any-key.press/vgi/gtransl/?\" ; ls/ru/?hello" | ./gtransl.retro | head -n 1 | \ + grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" +echo "gemini://any-key.press/vgi/gtransl/auto/?\" ; ls/?hello" | ./gtransl.retro | head -n 1 | \ + grep "^51 Not found" > /dev/null && echo "passed" || echo "FAILED" + +echo "Bad request tests..." +echo "gemini://any-key.press/vgi/gtransl/auto/ru/?\" ; ls" | ./gtransl.retro | head -n 1 | \ + grep "^59 Bad request" > /dev/null && echo "passed" || echo "FAILED" + +echo "Escaping tests..." +echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ + GTRANSLRESPFILE=tests/resp_escaped-01.html ./gtransl.retro | \ + head -n 8 | tail -n 1 | grep "^amp=& lt=< gt=> quot=\" apos='" > /dev/null && echo "passed" || echo "FAILED" +echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ + GTRANSLRESPFILE=tests/resp_escaped-02.html ./gtransl.retro | head -n 8 | tail -n 1 | \ + grep "^amp=& lt=< gt=> quot=\" apos='" > /dev/null && echo "passed" || echo "FAILED" +echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ + GTRANSLRESPFILE=tests/resp_escaped-03.html ./gtransl.retro | head -n 8 | tail -n 1 | \ + grep "^amp=& lt=< gt=> quot=\" apos='" > /dev/null && echo "passed" || echo "FAILED" +echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ + GTRANSLRESPFILE=tests/resp_escaped-04.html ./gtransl.retro | head -n 8 | tail -n 1 | \ + grep "^lt=< gt=>" > /dev/null && echo "passed" || echo "FAILED" + +echo "Multiline tests..." +echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ + GTRANSLRESPFILE=tests/resp_multiline-01.html ./gtransl.retro | head -n 8 | tail -n 1 | \ + grep "^hello$" > /dev/null && echo "passed" || echo "FAILED" +echo "gemini://any-key.press/vgi/gtransl/sl/tl/?query" | \ + GTRANSLRESPFILE=tests/resp_multiline-01.html ./gtransl.retro | head -n 9 | tail -n 1 | \ + grep "^world" > /dev/null && echo "passed" || echo "FAILED"