Юникод в LuaFAR

Здесь обсуждается всё, что касается макросов.
Shmuel
Posts: 6840
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 530 times

Ошибка в unicode.utf-8.match ?

Post by Shmuel »

Latest post of the previous page:

John Doe wrote: Wed 04 Sep, 2019 12:40 Раньше я сочетал utf-8.find и string.sub, теперь это видимо поломалось.
Сожалею о причинённых неудобствах, но всё же у меня ощущение, что достоинства перевешивают.
Возможно, вы приспособите gmatch/gsub для той задачи, о которой вы говорите. Предлагаю не спешить, а если действительно будет видно, что чего-то не хватает, будем принимать меры.
Shmuel
Posts: 6840
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 530 times

Ошибка в unicode.utf-8.match ?

Post by Shmuel »

Ещё одно преимущество перед прежней библиотекой: эта - живая, есть автор, с которым можно взаимодействовать. Однажды он оперативно сделал фикс в ответ на мой репорт.
User avatar
John Doe
Бюрократ
Posts: 13867
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 74 times
Been thanked: 428 times
Contact:

Ошибка в unicode.utf-8.match ?

Post by John Doe »

Shmuel wrote: Wed 04 Sep, 2019 12:49 но всё же у меня ощущение, что достоинства перевешивают.
Да я тоже не против прогресса.
В данном случае неприятно то, что ошибки на глаз не слишком легко обнаружить, а всплывут они не сразу.

Насчёт живого автора полностью согласен.
User avatar
buniak_a_h
Posts: 4269
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 644 times
Contact:

Ошибка в unicode.utf-8.match ?

Post by buniak_a_h »

Shmuel wrote: Wed 04 Sep, 2019 12:49 достоинства перевешивают
Согласен, но у меня просьба (на всякий случай): хотя _G.unicode.utf8 == _G.utf8, первый вариант не убирайте. Пусть будет...
Last edited by buniak_a_h on Wed 04 Sep, 2019 13:37, edited 1 time in total.
Shmuel
Posts: 6840
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 530 times

Ошибка в unicode.utf-8.match ?

Post by Shmuel »

buniak_a_h wrote: Wed 04 Sep, 2019 13:36 хотя _G.unicode.utf8 == _G.utf8, первый вариант не убирайте. Пусть будет...
Убирать планов нет.
User avatar
John Doe
Бюрократ
Posts: 13867
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 74 times
Been thanked: 428 times
Contact:

Ошибка в unicode.utf-8.match ?

Post by John Doe »

Задал вопрос автору: https://github.com/starwing/luautf8/issues/25
(Проверьте не ошибся ли я в формулировках)
Shmuel
Posts: 6840
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 530 times

Ошибка в unicode.utf-8.match ?

Post by Shmuel »

John Doe wrote: Wed 04 Sep, 2019 14:14 (Проверьте не ошибся ли я в формулировках)
Наверное следует отредактировать код, чтобы не было синтаксиса с двоеточием - автор же не знает, что у нас переопределено. То есть использовать utf8.sub и т.п.
User avatar
John Doe
Бюрократ
Posts: 13867
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 74 times
Been thanked: 428 times
Contact:

Юникод в LuaFAR

Post by John Doe »

Спасибо, исправил.
Кстати пример реальный, из Symlink.lua
Shmuel
Posts: 6840
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 530 times

Юникод в LuaFAR

Post by Shmuel »

Пример может и реальный, но не рабочий (не самодостаточный). Нет инициализации pos, не ясно что такое str2.
User avatar
zg
zero-gravity
Posts: 6038
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 185 times
Contact:

Юникод в LuaFAR

Post by zg »

John Doe wrote: Wed 04 Sep, 2019 12:123. Другие изменения?
match тоже раньше байты принимал.
User avatar
John Doe
Бюрократ
Posts: 13867
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 74 times
Been thanked: 428 times
Contact:

Ошибки из-за ужесточения проверки UTF-8 в строках

Post by John Doe »

Code: Select all

('1234'):find('.*',1) --1,4
('1234'):find('.*',0) --0,3
Полагаю, баг в библиотеке?
https://github.com/starwing/luautf8/issues/34
Last edited by John Doe on Mon 11 Jan, 2021 15:32, edited 1 time in total.
https://t.me/FarManager — Telegram чат
Shmuel
Posts: 6840
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 530 times

Ошибки из-за ужесточения проверки UTF-8 в строках

Post by Shmuel »

Опечатка в заголовке issue.
User avatar
John Doe
Бюрократ
Posts: 13867
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 74 times
Been thanked: 428 times
Contact:

Ошибки из-за ужесточения проверки UTF-8 в строках

Post by John Doe »

С 3-его раза увидел)
Спасибо
https://t.me/FarManager — Telegram чат
Shmuel
Posts: 6840
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 530 times

Ошибки из-за ужесточения проверки UTF-8 в строках

Post by Shmuel »

John Doe, файл lutf8lib.c в репозитории Фар, заметно отличается от оригинала - там есть ряд моих (непринципиальных, но тем не менее) изменений. Поскольку у меня нет намерения делать ревизию каждого из этих различий, то мой вам совет (именно совет, а не указание) - ограничиться только изменением в одной строчке, которое, собственно, фиксит баг.
1.patch
(341 Bytes) Downloaded 49 times
User avatar
John Doe
Бюрократ
Posts: 13867
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 74 times
Been thanked: 428 times
Contact:

Ошибки из-за ужесточения проверки UTF-8 в строках

Post by John Doe »

Баг собственно говоря невелик, и не слишком меня беспокоит.
А вот принципиальная возможность обновления на свежие версии библиотеки мне видится важной.
История вся здесь: https://github.com/FarGroup/FarManager/ ... lutf8lib.c

Вот diff со всеми внесёнными изменениями:
  1. --- lutf8lib.c
  2.  
  3. +++ lutf8lib.c.txt_new
  4.  
  5. @@ -1,4 +1,4 @@
  6.  
  7. -/* vim: set ft=c nu et sw=2 fdc=2 fdm=syntax : */
  8.  
  9. +/* vim: set ft=c nu et sw=2 fdc=2 fdm=syntax : */
  10.  
  11.  #define LUA_LIB
  12.  
  13.  #include <lua.h>
  14.  
  15.  #include <lauxlib.h>
  16.  
  17. @@ -17,6 +17,7 @@
  18.  
  19.  #define UTF8_MAX     0x7FFFFFFFu
  20.  
  21.  #define UTF8_MAXCP   0x10FFFFu
  22.  
  23.  #define iscont(p)  ((*(p) & 0xC0) == 0x80)
  24.  
  25. +#define CAST(tp,expr) ((tp)(expr))
  26.  
  27.  
  28.  
  29.  #ifndef LUA_QL
  30.  
  31.  # define LUA_QL(x) "'" x "'"
  32.  
  33. @@ -103,8 +104,8 @@
  34.  
  35.  }
  36.  
  37.  
  38.  
  39.  static int utf8_range(const char *s, const char *e, lua_Integer *i, lua_Integer *j) {
  40.  
  41. -  const char *ps = utf8_relat(s, e, *i);
  42.  
  43. -  const char *pe = utf8_relat(s, e, *j);
  44.  
  45. +  const char *ps = utf8_relat(s, e, CAST(int, *i));
  46.  
  47. +  const char *pe = utf8_relat(s, e, CAST(int, *j));
  48.  
  49.    *i = (ps ? ps : (*i > 0 ? e : s)) - s;
  50.  
  51.    *j = (pe ? utf8_next(pe, e) : (*j > 0 ? e : s)) - s;
  52.  
  53.    return *i < *j;
  54.  
  55. @@ -141,7 +142,7 @@
  56.  
  57.    end = size;
  58.  
  59.  
  60.  
  61.    while (begin < end) {
  62.  
  63. -    int mid = (begin + end) / 2;
  64.  
  65. +    size_t mid = (begin + end) / 2;
  66.  
  67.      if (t[mid].last < ch)
  68.  
  69.        begin = mid + 1;
  70.  
  71.      else if (t[mid].first > ch)
  72.  
  73. @@ -160,7 +161,7 @@
  74.  
  75.    end = size;
  76.  
  77.  
  78.  
  79.    while (begin < end) {
  80.  
  81. -    int mid = (begin + end) / 2;
  82.  
  83. +    size_t mid = (begin + end) / 2;
  84.  
  85.      if (t[mid].last < ch)
  86.  
  87.        begin = mid + 1;
  88.  
  89.      else if (t[mid].first > ch)
  90.  
  91. @@ -295,6 +296,7 @@
  92.  
  93.  static int Lutf8_reverse (lua_State *L) {
  94.  
  95.    luaL_Buffer b;
  96.  
  97.    const char *prev, *pprev, *ends, *e, *s = check_utf8(L, 1, &e);
  98.  
  99. +  (void) ends;
  100.  
  101.    int lax = lua_toboolean(L, 2);
  102.  
  103.    luaL_buffinit(L, &b);
  104.  
  105.    if (lax) {
  106.  
  107. @@ -303,8 +305,8 @@
  108.  
  109.        luaL_addlstring(&b, prev, e-prev);
  110.  
  111.      }
  112.  
  113.    } else {
  114.  
  115. +    utfint code = 0;
  116.  
  117.      for (prev = e; s < prev; prev = pprev) {
  118.  
  119. -      utfint code;
  120.  
  121.        ends = utf8_safe_decode(L, pprev = utf8_prev(s, prev), &code);
  122.  
  123.        assert(ends == prev);
  124.  
  125.        if (utf8_invalid(code))
  126.  
  127. @@ -325,13 +327,13 @@
  128.  
  129.    lua_Integer posi = luaL_optinteger(L, 2, 1);
  130.  
  131.    lua_Integer pose = luaL_optinteger(L, 3, posi);
  132.  
  133.    if (utf8_range(s, e, &posi, &pose)) {
  134.  
  135. +    utfint ch = 0;
  136.  
  137.      for (e = s + pose, s = s + posi; s < e; ++n) {
  138.  
  139. -      utfint ch;
  140.  
  141.        s = utf8_safe_decode(L, s, &ch);
  142.  
  143.        lua_pushinteger(L, ch);
  144.  
  145.      }
  146.  
  147.    }
  148.  
  149. -  return n;
  150.  
  151. +  return CAST(int, n);
  152.  
  153.  }
  154.  
  155.  
  156.  
  157.  static int Lutf8_codepoint (lua_State *L) {
  158.  
  159. @@ -342,6 +344,7 @@
  160.  
  161.    int lax = lua_toboolean(L, 4);
  162.  
  163.    int n;
  164.  
  165.    const char *se;
  166.  
  167. +  utfint code = 0;
  168.  
  169.    luaL_argcheck(L, posi >= 1, 2, "out of range");
  170.  
  171.    luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of range");
  172.  
  173.    if (posi > pose) return 0;  /* empty interval; return no values */
  174.  
  175. @@ -352,7 +355,6 @@
  176.  
  177.    n = 0;  /* count the number of returns */
  178.  
  179.    se = s + pose;  /* string end */
  180.  
  181.    for (n = 0, s += posi - 1; s < se;) {
  182.  
  183. -    utfint code;
  184.  
  185.      s = utf8_safe_decode(L, s, &code);
  186.  
  187.      if (!lax && utf8_invalid(code))
  188.  
  189.        return luaL_error(L, "invalid UTF-8 code");
  190.  
  191. @@ -369,7 +371,7 @@
  192.  
  193.    for (i = 1; i <= n; ++i) {
  194.  
  195.      lua_Integer code = luaL_checkinteger(L, i);
  196.  
  197.      luaL_argcheck(L, code <= UTF8_MAXCP, i, "value out of range");
  198.  
  199. -    add_utf8char(&b, code);
  200.  
  201. +    add_utf8char(&b, CAST(utfint, code));
  202.  
  203.    }
  204.  
  205.    luaL_pushresult(&b);
  206.  
  207.    return 1;
  208.  
  209. @@ -379,13 +381,13 @@
  210.  
  211.  static int Lutf8_##name (lua_State *L) {                        \
  212.  
  213.    int t = lua_type(L, 1);                                      \
  214.  
  215.    if (t == LUA_TNUMBER)                                        \
  216.  
  217. -    lua_pushinteger(L, utf8_to##name(lua_tointeger(L, 1)));    \
  218.  
  219. +    lua_pushinteger(L, utf8_to##name(CAST(utfint, lua_tointeger(L, 1))));    \
  220.  
  221.    else if (t == LUA_TSTRING) {                                 \
  222.  
  223. +    utfint ch = 0;                                             \
  224.  
  225.      luaL_Buffer b;                                             \
  226.  
  227.      const char *e, *s = to_utf8(L, 1, &e);                     \
  228.  
  229.      luaL_buffinit(L, &b);                                      \
  230.  
  231.      while (s < e) {                                            \
  232.  
  233. -      utfint ch;                                               \
  234.  
  235.        s = utf8_safe_decode(L, s, &ch);                         \
  236.  
  237.        add_utf8char(&b, utf8_to##name(ch));                     \
  238.  
  239.      }                                                          \
  240.  
  241. @@ -421,10 +423,10 @@
  242.  
  243.  
  244.  
  245.  static int Lutf8_escape (lua_State *L) {
  246.  
  247.    const char *e, *s = check_utf8(L, 1, &e);
  248.  
  249. +  utfint ch = 0;
  250.  
  251.    luaL_Buffer b;
  252.  
  253.    luaL_buffinit(L, &b);
  254.  
  255.    while (s < e) {
  256.  
  257. -    utfint ch;
  258.  
  259.      s = utf8_safe_decode(L, s, &ch);
  260.  
  261.      if (ch == '%') {
  262.  
  263.        int hex = 0;
  264.  
  265. @@ -562,10 +564,10 @@
  266.  
  267.  
  268.  
  269.  static int iter_aux (lua_State *L, int strict) {
  270.  
  271.    const char *e, *s = check_utf8(L, 1, &e);
  272.  
  273. -  int n = lua_tointeger(L, 2);
  274.  
  275. +  int n = CAST(int, lua_tointeger(L, 2));
  276.  
  277.    const char *p = n <= 0 ? s : utf8_next(s+n-1, e);
  278.  
  279.    if (p < e) {
  280.  
  281. -    utfint code;
  282.  
  283. +    utfint code = 0;
  284.  
  285.      utf8_safe_decode(L, p, &code);
  286.  
  287.      if (strict && utf8_invalid(code))
  288.  
  289.        return luaL_error(L, "invalid UTF-8 code");
  290.  
  291. @@ -591,9 +593,9 @@
  292.  
  293.  static int Lutf8_width (lua_State *L) {
  294.  
  295.    int t = lua_type(L, 1);
  296.  
  297.    int ambi_is_single = !lua_toboolean(L, 2);
  298.  
  299. -  int default_width = luaL_optinteger(L, 3, 0);
  300.  
  301. +  int default_width = CAST(int, luaL_optinteger(L, 3, 0));
  302.  
  303.    if (t == LUA_TNUMBER) {
  304.  
  305. -    size_t chwidth = utf8_width(lua_tointeger(L, 1), ambi_is_single);
  306.  
  307. +    size_t chwidth = utf8_width(CAST(utfint, lua_tointeger(L, 1)), ambi_is_single);
  308.  
  309.      if (chwidth == 0) chwidth = default_width;
  310.  
  311.      lua_pushinteger(L, (lua_Integer)chwidth);
  312.  
  313.    } else if (t != LUA_TSTRING)
  314.  
  315. @@ -601,8 +603,8 @@
  316.  
  317.    else {
  318.  
  319.      const char *e, *s = to_utf8(L, 1, &e);
  320.  
  321.      int width = 0;
  322.  
  323. +    utfint ch = 0;
  324.  
  325.      while (s < e) {
  326.  
  327. -      utfint ch;
  328.  
  329.        int chwidth;
  330.  
  331.        s = utf8_safe_decode(L, s, &ch);
  332.  
  333.        chwidth = utf8_width(ch, ambi_is_single);
  334.  
  335. @@ -615,17 +617,17 @@
  336.  
  337.  
  338.  
  339.  static int Lutf8_widthindex (lua_State *L) {
  340.  
  341.    const char *e, *s = check_utf8(L, 1, &e);
  342.  
  343. -  int width = luaL_checkinteger(L, 2);
  344.  
  345. +  int width = CAST(int, luaL_checkinteger(L, 2));
  346.  
  347.    int ambi_is_single = !lua_toboolean(L, 3);
  348.  
  349. -  int default_width = luaL_optinteger(L, 4, 0);
  350.  
  351. +  int default_width = CAST(int, luaL_optinteger(L, 4, 0));
  352.  
  353.    size_t idx = 1;
  354.  
  355. +  utfint ch = 0;
  356.  
  357.    while (s < e) {
  358.  
  359. -    utfint ch;
  360.  
  361.      size_t chwidth;
  362.  
  363.      s = utf8_safe_decode(L, s, &ch);
  364.  
  365.      chwidth = utf8_width(ch, ambi_is_single);
  366.  
  367.      if (chwidth == 0) chwidth = default_width;
  368.  
  369. -    width -= chwidth;
  370.  
  371. +    width -= CAST(int, chwidth);
  372.  
  373.      if (width <= 0) {
  374.  
  375.        lua_pushinteger(L, idx);
  376.  
  377.        lua_pushinteger(L, width + chwidth);
  378.  
  379. @@ -712,7 +714,7 @@
  380.  
  381.  }
  382.  
  383.  
  384.  
  385.  static const char *classend (MatchState *ms, const char *p) {
  386.  
  387. -  utfint ch;
  388.  
  389. +  utfint ch = 0;
  390.  
  391.    p = utf8_safe_decode(ms->L, p, &ch);
  392.  
  393.    switch (ch) {
  394.  
  395.      case L_ESC: {
  396.  
  397. @@ -752,20 +754,20 @@
  398.  
  399.  
  400.  
  401.  static int matchbracketclass (MatchState *ms, utfint c, const char *p, const char *ec) {
  402.  
  403.    int sig = 1;
  404.  
  405. +  utfint ch = 0;
  406.  
  407. +  utfint next = 0;
  408.  
  409.    assert(*p == '[');
  410.  
  411.    if (*++p == '^') {
  412.  
  413.      sig = 0;
  414.  
  415.      p++;  /* skip the `^' */
  416.  
  417.    }
  418.  
  419.    while (p < ec) {
  420.  
  421. -    utfint ch;
  422.  
  423.      p = utf8_safe_decode(ms->L, p, &ch);
  424.  
  425.      if (ch == L_ESC) {
  426.  
  427.        p = utf8_safe_decode(ms->L, p, &ch);
  428.  
  429.        if (match_class(c, ch))
  430.  
  431.          return sig;
  432.  
  433.      } else {
  434.  
  435. -      utfint next;
  436.  
  437.        const char *np = utf8_safe_decode(ms->L, p, &next);
  438.  
  439.        if (next == '-' && np < ec) {
  440.  
  441.          p = utf8_safe_decode(ms->L, np, &next);
  442.  
  443. @@ -782,7 +784,7 @@
  444.  
  445.    if (s >= ms->src_end)
  446.  
  447.      return 0;
  448.  
  449.    else {
  450.  
  451. -    utfint ch, pch;
  452.  
  453. +    utfint ch=0, pch=0;
  454.  
  455.      utf8_safe_decode(ms->L, s, &ch);
  456.  
  457.      p = utf8_safe_decode(ms->L, p, &pch);
  458.  
  459.      switch (pch) {
  460.  
  461. @@ -796,7 +798,7 @@
  462.  
  463.  }
  464.  
  465.  
  466.  
  467.  static const char *matchbalance (MatchState *ms, const char *s, const char **p) {
  468.  
  469. -  utfint ch, begin, end;
  470.  
  471. +  utfint ch=0, begin=0, end=0;
  472.  
  473.    *p = utf8_safe_decode(ms->L, *p, &begin);
  474.  
  475.    if (*p >= ms->p_end)
  476.  
  477.      luaL_error(ms->L, "malformed pattern "
  478.  
  479. @@ -879,7 +881,7 @@
  480.  
  481.      luaL_error(ms->L, "pattern too complex");
  482.  
  483.    init: /* using goto's to optimize tail recursion */
  484.  
  485.    if (p != ms->p_end) {  /* end of pattern? */
  486.  
  487. -    utfint ch;
  488.  
  489. +    utfint ch = 0;
  490.  
  491.      utf8_safe_decode(ms->L, p, &ch);
  492.  
  493.      switch (ch) {
  494.  
  495.        case '(': {  /* start capture */
  496.  
  497. @@ -1051,7 +1053,7 @@
  498.  
  499.    const char *ep, *p = check_utf8(L, 2, &ep);
  500.  
  501.    lua_Integer idx = luaL_optinteger(L, 3, 1);
  502.  
  503.    const char *init;
  504.  
  505. -  init = utf8_relat(s, es, idx);
  506.  
  507. +  init = utf8_relat(s, es, CAST(int, idx));
  508.  
  509.    if (init == NULL) {
  510.  
  511.      if (idx > 0) {
  512.  
  513.        lua_pushnil(L);  /* cannot find anything */
  514.  
  515. @@ -1143,8 +1145,8 @@
  516.  
  517.  
  518.  
  519.  static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, const char *e) {
  520.  
  521.    const char *new_end, *news = to_utf8(ms->L, 3, &new_end);
  522.  
  523. +  utfint ch = 0;
  524.  
  525.    while (news < new_end) {
  526.  
  527. -    utfint ch;
  528.  
  529.      news = utf8_safe_decode(ms->L, news, &ch);
  530.  
  531.      if (ch != L_ESC)
  532.  
  533.        add_utf8char(b, ch);
  534.  
  535. @@ -1202,6 +1204,7 @@
  536.  
  537.    lua_Integer n = 0;
  538.  
  539.    MatchState ms;
  540.  
  541.    luaL_Buffer b;
  542.  
  543. +  utfint ch = 0;
  544.  
  545.    luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
  546.  
  547.                     tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
  548.  
  549.                        "string/function/table expected");
  550.  
  551. @@ -1224,7 +1227,6 @@
  552.  
  553.      if (e && e > s) /* non empty match? */
  554.  
  555.        s = e;  /* skip it */
  556.  
  557.      else if (s < es) {
  558.  
  559. -      utfint ch;
  560.  
  561.        s = utf8_safe_decode(L, s, &ch);
  562.  
  563.        add_utf8char(&b, ch);
  564.  
  565.      } else break;
  566.  
  567. @@ -1280,8 +1282,7 @@
  568.  
  569.  #if LUA_VERSION_NUM >= 502
  570.  
  571.    luaL_newlib(L, libs);
  572.  
  573.  #else
  574.  
  575. -  lua_createtable(L, 0, sizeof(libs)/sizeof(libs[0]));
  576.  
  577. -  luaL_register(L, NULL, libs);
  578.  
  579. +  luaL_register(L, "utf8", libs);
  580.  
  581.  #endif
  582.  
  583.  
  584.  
  585.    lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)-1);
Возможно стоит отправить PR в апстрим?
Shmuel wrote: Fri 15 Jan, 2021 13:44 Поскольку у меня нет намерения делать ревизию каждого из этих различий
Понятно. В любом случае этот дифф в теме не помешает.
Attachments
lutf8lib.c.diff
(9.22 KiB) Downloaded 43 times
Last edited by John Doe on Fri 15 Jan, 2021 15:47, edited 1 time in total.
https://t.me/FarManager — Telegram чат
Post Reply

Return to “Обсуждение макросов”