1 3baf066a 2024-07-28 continue #!/usr/bin/env retro
4 3baf066a 2024-07-28 continue '\r\n s:format 'CRLF s:const
5 3b323cde 2024-08-02 continue :vgi:print-line (s-) CRLF s:append s:put ;
6 8806e87f 2024-08-05 continue :vgi:user-input '10_Text_🔁: vgi:print-line #0 unix:exit ;
7 3b323cde 2024-08-02 continue :vgi:not-found '51_Not_found vgi:print-line #0 unix:exit ;
8 3b323cde 2024-08-02 continue :vgi:bad-request '59_Bad_request vgi:print-line #0 unix:exit ;
9 3b323cde 2024-08-02 continue :vgi:tmp-failure '40_Unexpected_error vgi:print-line #0 unix:exit ;
12 3b323cde 2024-08-02 continue По спецификации Gemini запрашиваемый URL не может превышать 1024 байта.
13 3b323cde 2024-08-02 continue Поэтому на хранение URL выделяем 1024 ячеек + ячейку для терминирующего нуля.
15 3b323cde 2024-08-02 continue 'url:Buffer d:create #1025 allot
16 3b323cde 2024-08-02 continue :url:get (-)
17 3b323cde 2024-08-02 continue url:Buffer buffer:set
18 3b323cde 2024-08-02 continue #1024 [ c:get dup ASCII:CR eq? [ drop ] if; dup ASCII:LF eq? [ drop ] if; buffer:add ] times
23 3baf066a 2024-07-28 continue Разбираем запрашиваемый URL из стандратного потока ввода:
24 3baf066a 2024-07-28 continue * всё, что до /gtransl/ слева отбрасываем вместе с этим компонентом пути;
25 3b323cde 2024-08-02 continue * следующий компонент пути - исходный язык (константа url:SL);
26 3b323cde 2024-08-02 continue * следующий компонент пути - целевой язык перевода (константа url:TL);
27 3baf066a 2024-07-28 continue * если входной URL закончен, то нужно выдать пользователю запрос на ввод переводимой строки;
28 3b323cde 2024-08-02 continue * последним извлекаем query строку с текстом, который требуется перевести (константа url:QUERY).
30 3b323cde 2024-08-02 continue '/gtransl/ 'url:REQUIRED-PATH s:const
31 3b323cde 2024-08-02 continue :url:required-path (s-s)
32 3b323cde 2024-08-02 continue dup url:REQUIRED-PATH s:index/string dup n:negative? [ vgi:not-found ] if
33 3b323cde 2024-08-02 continue + url:REQUIRED-PATH s:length + n:inc
35 3b323cde 2024-08-02 continue :url:path-part (ss-s)
36 3b323cde 2024-08-02 continue swap dup $/ s:index/char dup n:negative? [ vgi:not-found ] if
37 3b323cde 2024-08-02 continue dup @TempStringMax gteq? [ vgi:bad-request ] if
38 3b323cde 2024-08-02 continue over over s:left
39 3b323cde 2024-08-02 continue rot rot + rot rot swap s:const
42 3b323cde 2024-08-02 continue :url:check-user-input (s-s) dup s:length n:zero? [ vgi:user-input ] if ;
43 3b323cde 2024-08-02 continue :url:required-query (s-s) dup fetch $? -eq? [ vgi:not-found ] if ;
44 3b323cde 2024-08-02 continue :url:query (s-) url:check-user-input url:required-query n:inc 'url:QUERY const ;
46 3b323cde 2024-08-02 continue url:Buffer url:required-path 'url:SL url:path-part 'url:TL url:path-part url:query
49 3baf066a 2024-07-28 continue Проверяем валидность содержимого строк,
50 3baf066a 2024-07-28 continue что бы исключить возможность исполнения произвольной команды.
51 3baf066a 2024-07-28 continue (Command Injection)
53 3b323cde 2024-08-02 continue :url:check-lang (s-) [ $a $z n:between? [ vgi:not-found ] -if ] s:for-each ;
54 3b323cde 2024-08-02 continue url:SL url:check-lang
55 3b323cde 2024-08-02 continue url:TL url:check-lang
57 3b323cde 2024-08-02 continue :url:check-query (s-)
59 3baf066a 2024-07-28 continue dup $a $z n:between? [ drop ] if;
60 3baf066a 2024-07-28 continue dup $A $Z n:between? [ drop ] if;
61 3baf066a 2024-07-28 continue dup $0 $9 n:between? [ drop ] if;
62 3baf066a 2024-07-28 continue dup $% eq? [ drop ] if;
63 3baf066a 2024-07-28 continue dup $/ eq? [ drop ] if;
64 3baf066a 2024-07-28 continue dup $_ eq? [ drop ] if;
65 3baf066a 2024-07-28 continue dup $. eq? [ drop ] if;
66 3baf066a 2024-07-28 continue dup $- eq? [ drop ] if;
67 81be1742 2024-08-05 continue dup $: eq? [ drop ] if;
68 3baf066a 2024-07-28 continue $~ eq? [ ] if;
69 3a493068 2024-08-01 continue vgi:bad-request
70 3baf066a 2024-07-28 continue ] s:for-each
72 3b323cde 2024-08-02 continue url:QUERY url:check-query
75 3baf066a 2024-07-28 continue Результат выполнения команды curl будем хранить в буфере из 16-ти килоячеек
76 3baf066a 2024-07-28 continue (+ ячейка для ASCII:NULL)
78 3a493068 2024-08-01 continue :html:BUFFER_SIZE #16384 ;
79 3b323cde 2024-08-02 continue 'html:Buffer d:create html:BUFFER_SIZE n:inc allot
82 3a493068 2024-08-01 continue Для выполнения HTTPS-запроса используем curl или (для тестов) cat из существующего файла с HTML-ответом.
83 3baf066a 2024-07-28 continue Чтение результата выполнения команды curl по переданному описателю пайпа происходит до чтения
84 3baf066a 2024-07-28 continue 0 символа (не байта). Считаем, что в этом случае пайп закрыт другой стороной.
86 3b323cde 2024-08-02 continue :pipe:read (h-h) html:Buffer buffer:set html:BUFFER_SIZE [ dup file:read/c 0; buffer:add ] times ;
87 3b323cde 2024-08-02 continue 'CurlCommand d:create #3072 allot
88 3b323cde 2024-08-02 continue :buffer:append (s-) [ buffer:add ] s:for-each ;
89 3b323cde 2024-08-02 continue :pipe:command-curl (-s)
90 3b323cde 2024-08-02 continue CurlCommand buffer:set
91 3b323cde 2024-08-02 continue 'curl_-m_5_-s_--url-query_sl=" buffer:append
92 3b323cde 2024-08-02 continue url:SL buffer:append
93 3b323cde 2024-08-02 continue '"_--url-query_tl=" buffer:append
94 3b323cde 2024-08-02 continue url:TL buffer:append
95 3b323cde 2024-08-02 continue '"_"https://translate.google.com/m?q= buffer:append
96 3b323cde 2024-08-02 continue url:QUERY buffer:append
97 3b323cde 2024-08-02 continue '" buffer:append
98 3b323cde 2024-08-02 continue CurlCommand
100 3b323cde 2024-08-02 continue :get-env (s-s) s:empty [ unix:getenv ] sip ;
101 3b323cde 2024-08-02 continue :zero-length? (s-s) dup s:length n:zero? ;
102 3b323cde 2024-08-02 continue :pipe:command (-s) 'GTRANSLRESPFILE get-env zero-length? [ drop pipe:command-curl ] [ 'cat_%s s:format ] choose ;
103 3b323cde 2024-08-02 continue :pipe:request pipe:command file:R unix:popen pipe:read unix:pclose ;
106 3baf066a 2024-07-28 continue Рабоче-крестьянский парсинг HTML:
107 3baf066a 2024-07-28 continue извлекаем текст из тэга <div class="result-container">ИЗВЛЕКИ ЭТОТ ТЕКСТ</div>
108 3baf066a 2024-07-28 continue ^(1) ^(2) ^(3)
109 3baf066a 2024-07-28 continue * (1) находим подстроку "result-container"
110 3baf066a 2024-07-28 continue * (2) вслед за ней находит символ > (могут быть пробельные символы или другие атрибуты элемента)
111 3baf066a 2024-07-28 continue * (3) и извлекаем все, до следующего символа открывающего тэга (<)
113 3baf066a 2024-07-28 continue Затем в результате заменяем наиболее часто-используемые escape-последовательности HTML на символы.
115 3b323cde 2024-08-02 continue :html:index/char (sc-n) s:index/char dup n:negative? [ vgi:tmp-failure ] if ;
116 3b323cde 2024-08-02 continue :html:index/result-container (s-n) '"result-container" s:index/string dup n:negative? [ vgi:tmp-failure ] if ;
117 3b323cde 2024-08-02 continue :buffer:append-n (sn-) [ dup I s:fetch buffer:add ] indexed-times drop ;
118 3b323cde 2024-08-02 continue :html:cut-head (sn-s) over buffer:set + repeat dup fetch 0; buffer:add n:inc again ;
119 3b323cde 2024-08-02 continue :html:unquote? (ssc-sf)
120 3b323cde 2024-08-02 continue rot rot over over s:begins-with? [ drop drop drop FALSE ] -if;
121 3b323cde 2024-08-02 continue s:length n:dec rot rot store swap dup n:inc rot html:cut-head drop TRUE
123 3b323cde 2024-08-02 continue :html:unquote-head (s-s)
124 3b323cde 2024-08-02 continue dup '& $& html:unquote? [ ] if;
125 3b323cde 2024-08-02 continue dup '& $& html:unquote? [ ] if;
126 3b323cde 2024-08-02 continue dup '& $& html:unquote? [ ] if;
128 3b323cde 2024-08-02 continue dup '< $< html:unquote? [ ] if;
129 3b323cde 2024-08-02 continue dup '< $< html:unquote? [ ] if;
130 3b323cde 2024-08-02 continue dup '< $< html:unquote? [ ] if;
131 3b323cde 2024-08-02 continue dup '< $< html:unquote? [ ] if;
133 3b323cde 2024-08-02 continue dup '> $> html:unquote? [ ] if;
134 3b323cde 2024-08-02 continue dup '> $> html:unquote? [ ] if;
135 3b323cde 2024-08-02 continue dup '> $> html:unquote? [ ] if;
136 3b323cde 2024-08-02 continue dup '> $> html:unquote? [ ] if;
138 3b323cde 2024-08-02 continue dup '" $" html:unquote? [ ] if;
139 3b323cde 2024-08-02 continue dup '" $" html:unquote? [ ] if;
140 3b323cde 2024-08-02 continue dup '" $" html:unquote? [ ] if;
142 3b323cde 2024-08-02 continue dup '' $' html:unquote? [ ] if;
143 3b323cde 2024-08-02 continue dup '' $' html:unquote? [ ] if;
144 3b323cde 2024-08-02 continue dup '' $' html:unquote? [ ] if;
146 3b323cde 2024-08-02 continue :html:unquote (s-s)
148 3b323cde 2024-08-02 continue dup fetch 0; $& eq? [ html:unquote-head ] if n:inc
151 3b323cde 2024-08-02 continue :html:get-result-container (-s)
152 3b323cde 2024-08-02 continue pipe:request
153 3b323cde 2024-08-02 continue html:Buffer html:index/result-container html:Buffer +
154 3b323cde 2024-08-02 continue dup $> html:index/char + n:inc
155 3b323cde 2024-08-02 continue dup $< html:index/char
156 3b323cde 2024-08-02 continue html:Buffer buffer:set buffer:append-n
157 3b323cde 2024-08-02 continue html:Buffer html:unquote drop
158 3b323cde 2024-08-02 continue html:Buffer
162 3baf066a 2024-07-28 continue Отдаём результат в формате "text/gemini" в стандартный поток вывода
164 3b323cde 2024-08-02 continue html:get-result-container (*1)
165 3b323cde 2024-08-02 continue '20_text/gemini vgi:print-line
166 8806e87f 2024-08-05 continue '#_GTransl_🔁 vgi:print-line
167 3baf066a 2024-07-28 continue CRLF s:put
168 3b323cde 2024-08-02 continue CRLF url:SL 'From:_%s%s s:format s:put
169 3b323cde 2024-08-02 continue CRLF url:TL 'To:___%s%s s:format s:put
170 3baf066a 2024-07-28 continue CRLF s:put
171 3b323cde 2024-08-02 continue '``` vgi:print-line
172 3b323cde 2024-08-02 continue (*1) vgi:print-line
173 3b323cde 2024-08-02 continue '``` vgi:print-line