Библиотека эмуляции терминала ROTE и Lua привязки

    boxshell

    ROTE — простая библиотека на языке C, служащая для эмуляции терминала VT100. Она создает терминал и предоставляет доступ к его состоянию в виде структуры языка C. В терминале можно запустить дочерний процесс, «нажимать» в нем клавиши и смотреть, что он рисует на терминале. Кроме того, есть функция для отрисовки состояния терминала в окне curses.

    Зачем на практике может потребоваться эмулировать терминал и взаимодействовать через него с дочерним процессом? В первую очередь это нужно для автоматического тестирования программ, рисующих что-то на терминале с помощью curses, по моему мнению. Как иначе написать тесты для программы, которая ждёт, что пользователь нажмёт клавишу, и выводит результаты в определенное место экрана средствами curses?

    Несмотря на всё удобство и внутреннюю красоту ROTE, использовать её напрямую в тестах было бы громоздко. Поэтому я решил упростить задачу, привязав ROTE к языку Lua, который я очень люблю и знаю, как писать тесты. Так и родилась библиотека lua-rote, о которой я хочу рассказать.

    Установка


    Потребуется Linux, curses, Lua версии от 5.1 до 5.3 или LuaJIT, пакетный менеджер luarocks с установленными пакетом luaposix и, собственно, сама библиотека ROTE.

    ROTE устанавливается простым ./configure && make && make install. Надо отследить, чтобы она установилась туда, где её увидит система сборки. Я использую для этого ./configure --prefix=/usr. Чтобы не замусоривать систему бесхозными файлами, можно сделать пакет, для этого подойдёт программа checkinstall.

    lua-rote добавлен в luarocks, поэтому для его установки достаточно набрать следующую команду:

    $ sudo luarocks install lua-rote
    

    Если ROTE установили в /usr/local, то об этом надо сообщить luarocks'у посредством опции:

    $ sudo luarocks install lua-rote ROTE_DIR=/usr/local
    

    Чтобы установить версию с GitHub, введите следующие команды:

    $ git clone https://github.com/starius/lua-rote.git
    $ cd lua-rote
    $ sudo luarocks make
    

    Чтобы устанавливать пакеты в luarocks локально (то есть в домашнюю папку пользователя, а не в системные папки), добавьте опцию --local. В таком случае потребуется изменить кое-какие переменные окружения, чтобы Lua увидел эти пакеты:

    $ luarocks make --local
    $ luarocks path > paths 
    $ echo 'PATH=$PATH:~/.luarocks/bin' >> paths
    $ . paths
    

    Использование


    Вся библиотека lua-rote находится в модуле rote, так что для начала подключим его:

    rote = require 'rote'
    

    Основная часть библиотеки — класс RoteTerm, представляющий терминал.
    Создадим терминал из 24 строк и 80 столбцов:

    rt = rote.RoteTerm(24, 80)
    

    Чтобы удалить терминал, надо просто удалить переменную, в которой он живёт. В Lua работает сборщик мусора, который при очередном проходе сделает всю работу по удалению.

    Запустим дочерний процесс:

    pid = rt:forkPty('less /some/file')
    

    Команда запускается при помощи '/bin/sh -c'. В переменную pid попадает идентификатор дочернего процесса. Позже его можно выяснить с помощью метода childPid(). В случае ошибки метод возвращает -1. Если попытаться запустить неправильную команду, то ошибка не будет отловлена на этом уровне: shell попытается запустить её и завершится со статусом 127. Чтобы перехватывать подобные ошибки, надо устанавливать обработчик сигнала SIGCHLD. Чтобы игнорировать завершение дочерних процессов, надо установить обработчик SIGCHLD в значение SIG_IGN. В Lua всё это можно сделать с помощью библиотеки luaposix:

    signal = require 'posix.signal'
    
    signal.signal(signal.SIGCHLD, function(signo)
      -- do smth
    end)
    
    signal.signal(signal.SIGCHLD, signal.SIG_IGN)
    

    Взаимодействие с терминалом, в котором дочерний процесс завершился, не является ошибкой, хотя вряд ли имеет смысл. Тем не менее, стоит уведомить ROTE о завершении дочернего процесса, вызвав метод forsakeChild().

    Чтение содержимого терминала


    У терминала есть ряд методов, возвращающих его параметры и состояние:

    • rt:rows() и rt:cols() — число строк и столбцов терминала
    • rt:row() и rt:col() — текущие координаты курсора
    • rt:update() — применяет изменения, пришедшие от дочернего процесса; вызывать перед чтением содержимого терминала
    • rt:cellChar(row, col) — символ ячейки (row, col) в форме строки длины 1
    • rt:cellAttr(row, col) — атрибуты ячейки (row, col) в форме числа (см. ниже, что с ним делать)
    • rt:attr() — текущие атрибуты, которые применяются к новым символам
    • rt:rowText(row) — строка терминала номер row, без "\n" на конце
    • rt:termText() — строка, представляющая весь терминал; ряды завершаются "\n"

    Ещё есть метод draw для рисования содержимого терминала в окне curses:

    curses = require 'posix.curses'
    -- инициализация curses, см. ниже demo/boxshell.lua
    window = ...
    rt = ...
    start_row = 0
    start_col = 0
    rt:draw(window, start_row, start_col)
    


    Запись в терминал


    Есть несколько методов, позволяющих менять состояние терминала напрямую:

    • rt:setCellChar(row, col, character) — заменяет символ ячейки (row, col)
    • rt:setCellAttr(row, col, attr) — заменяет атрибуты ячейки (row, col)
    • rt:setAttr(attr) — меняет текущие атрибуты, которые применяются к новым символам
    • rt:inject(data) — вводит данные в терминал

    Более важны методы, посылающие данные в дочерний процесс:

    -- Отправляет последовательность ':wq\n' в терминал
    -- Если есть дочерний процесс, данные передаются ему.
    -- Иначе данные напрямую вставляются в терминал при помощи inject()
    rt:write(':wq\n') -- сохраняем документ и закрываем vim
    
    -- Отправляет нажатие клавиши дочернему процессу через write()
    local keycode = string.byte('\n') -- число
    rt:keyPress(keycode)
    

    Коллекцию кодов клавиш для keyPress можно найти в curses. К сожалению, эти константы появляются в модуле только после инициализации curses, которую часто производить нежелательно (например, в коде тестов). Чтобы как-то жить с этим, был сделан модуль rote.cursesConsts, запускающий curses в дочернем процессе через ROTE и возвращающий все константы.

    Снимки состояния терминала


    Метод rt:takeSnapshot() возвращает объект-снимок, а метод rt:restoreSnapshot(snapshot) восстанавливает состояние терминала согласно снимку. Объект-снимок также удаляется автоматически сборщиком мусора.

    Атрибуты и цвета


    Атрибут — это 8-битное число, в котором хранится цвет букв, цвет фона, бит полужирного текста (bold bit) и бит мигающего текста (blink bit). Порядок битов следующий:

     бит:         7 6 5 4 3 2 1 0
     содержимое:  S F F F H B B B
                  | `-,-' | `-,-'
                  |   |   |   |
                  |   |   |   `----- 3 бита цвета фона (0 - 7)
                  |   |   `--------- бит мигающего текста
                  |   `------------- 3 бита цвета букв (0 - 7)
                  `----------------- бит полужирного текста
    

    Есть пара функций для упаковки и распаковки значения атрибута:

    foreground, background, bold, blink = rote.fromAttr(attr)
    attr = rote.toAttr(foreground, background, bold, blink)
    -- foreground и background - числа (0 - 7)
    -- bold и blink - логические переменные
    


    Коды цветов:
    • 0 = черный
    • 1 = красный
    • 2 = зеленый
    • 3 = желтый
    • 4 = синий
    • 5 = фиолетовый
    • 6 = голубой
    • 7 = белый


    В модуле rote есть таблицы перевода между кодами цветов и названиями цветов:
    rote.color2name[2] -- возвращает "green"
    rote.name2color.green -- возвращает 2
    


    Пример использования



    DNA alignment

    А ещё я занимаюсь биоинформатикой :)

    Давно хотелось иметь программу для просмотра выравниваний вроде Jalview, но прямо в терминале, так как часто файлы находятся на сервере, к которому я подключён через ssh. В таких случаях нужно что-то вроде less для fasta-файлов. Всё, что мне удалось найти на эту тему, — программа tview для просмотра ридов, но это немного не то.

    В результате я написал программу alnbox, которая именно это и делает: показывает выравнивание ДНК в curses, позволяет «ходить» по нему стрелочками, перемещаться в начало и в конец. Названия последовательностей отображаются слева, номера позиций — сверху, консенсус — снизу. Код написан несколько шире, поэтому может пригодиться не только для выравниваний, но и любых less-подобных программ с заголовками вдоль всех 4-ех сторон терминала. Весь код программы написан на Lua, без использования C.

    С помощью lua-rote и busted написаны тесты для alnbox, в которых проигрываются все возможные варианты работы с программой. За основу кода интеграции тестов в Travis CI взят костяк lua-travis-example от moteus.

    Проект пока незавершён, но смотреть выравнивания уже можно. Зависимости те же + сам lua-rote. Для установки наберите команду luarocks make.

    Ещё один пример использования


    Вместе с библиотекой ROTE распространяется файл demo/boxshell.c. Это по сути терминал в терминале: bash запускается внутри ROTE, а состояние ROTE рисуется в curses при помощи метода draw(). Этот пример я перенёс в Lua. В начале статьи показан пример работы в этом терминале.

    В Lua-версию boxshell внесено несколько исправлений. Во-первых, можно запустить в качестве дочернего процесса любую команду, а не только bash. Во-вторых, переделано чтение нажатых клавиш от пользователя: вместо nodelay используется halfdelay, то есть ожидание нажатия клавиши с таймаутом. Благодаря этому нагрузка на процессор со стороны boxshell снижена с 100% до менее чем 1%.

    Баги


    • Нет поддержки юникода.
    • Метод draw() может чудить при запуске в Travis CI. Воспроизвести этот баг у себя не удаётся. Точной причины я не знаю, но подозреваю, что дело в особенностях терминала, который предоставляет Travis CI.
    • Возвращает неправильные данные, если у терминала мало столбцов (пример: терминал 1x2).

    Сообщить о баге

    Исходный код ROTE был написан в 2004 году Бруно Т. К. де Оливейра (Bruno T. C. de Oliveira) и опубликован под лицензией GNU Lesser General Public License 2.1. Исходный код lua-rote опубликован под той же лицензией. Автор ROTE пишет, что разработка библиотеки завершена и обновления стоит искать в библиотеке libvterm, которая основана на ROTE. Есть ещё один проект с названием libvterm, который развивается активнее и есть модификация для проекта NeoVim. Для моих текущих целей ROTE хватило, и она выглядит более простой, поэтому пока я остановился именно на ней. Возможно, потом перейду к одному из libvterm.

    Ссылки


    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 23
    • 0
      В первую очередь это нужно для автоматического тестирования программ, рисующих что-то на терминале с помощью curses, по моему мнению. Как иначе написать тесты для программы, которая ждёт, что пользователь нажмёт клавишу, и выводит результаты в определенное место экрана средствами curses?

      В линуксе можно посылать символы в терминал через ioctl. Насчет читать оттуда символы и атрибуты — не интересовался, но теоретически тоже можно. Вот так, к примеру, можно «напечатать» хелловорлд:

      #!/usr/bin/perl -w
      use strict;
      
      open(HC, "+</dev/tty");
      my $s = "Hello, world!\n";
      
      for(my $i=0; $i < length($s); ++$i)
      {
         ioctl HC, 0x5412, substr( $s, $i, 1 );
      }
      
      


      Естественно, если хочется, чтобы введенные символы попали в другое приложение, придется запускать его как дочерний процесс.
      • 0
        До ROTE я довольно долго пробовал другие решения. Моё внимание также привлёк expect с virterm, но видимо у меня и у expect шарики в мозгах крутятся в разных направлениях.

        По крайней мере, я не нашел других библиотек, кроме ROTE и libvterm, которые одновременно удовлетворяли бы следующим требованиям:
        • умели запускать дочерний процесс,
        • умели получать содержимое терминала,
        • умели передавать данные в дочерний процесс, имитируя нажатия клавиш,
        • самое главное: были бы достаточно просты в использовании.
        • 0
          Мне кажется, у вас переплетаются функции эмулятора терминала и оболочки (shell). Терминал должен отвечать только за отображение информации: это односторонний канал коммуникации который получает поток символов и, иногда, последовательности управления. Переместить курсор, там, или подсветить красненьким. Важно, что состояние курсора хранится в оболочке, а эмулятору терминала оно нужно для того, чтобы знать с какого места продолжать выводить данные. Большинство PTY используют ANSI для control sequences, но я бы все же не полагался на это, и доверял terminfo. Ncurses, например, использует terminfo.

          По сути, эмулятор терминала — это пара устройств, с которыми можно обращаться как с файлами. Вот, например, как писать в терминал без всяких библиотек, just for fun.:

          $ tty # In one session: find out this terminal's output device.
          $ printf "$(tput setaf 1)Follow the $(tput setaf 7)white$(tput setaf 1) rabbit." > ttys006 # In another session: write to the first one.
          


          Кстати, tput выводит правильную для вашего терминала последовательность, а не hard-coded ANSI.
          • +1
            Большинство PTY используют ANSI для control sequences, но я бы все же не полагался на это, и доверял terminfo.
            В ROTE заявлена эмуляция VT100, который использует ANSI, так что пока остается жить с этим. Надо посмотреть, как обстоят дела в libvterm.

            Вот, например, как писать в терминал без всяких библиотек, just for fun.
            Насколько я понимаю, ROTE примерно так и делает и обрабатывает то, что поступает в файл терминала, применяя это к своим структурам данных, в которых хранится состояние терминала. Хочется по (x, y) ячейки получать её символ, цвет текста, цвет фона, это в тестах и используется.
            • 0
              Лучше всего взять новый libvterm, так как он ещё разрабатывается и если нужной функциональности нету, её вполне могут добавить. Пример кода, использующего новый libvterm именно для тестов: vterm.py, terminal.py и, собственно, код тестов. На luajit это можно переложить практически 1‐в‐1, т.к. здесь есть cffi и где‐то есть аналог pexpect для lua.
            • 0
              Тем не менее, ни один из известных мне эмуляторов терминала (ROTE и оба libvterm — это только библиотеки для создания оных) не занимается только отображением, а ещё и позволяет запуск: xterm -e my_command. Терминалы <A-Fn$gt; такого действительно не позволяют, забирая информацию о том, что нужно запускать, из /etc.

              И не нужно путать «эмулятор терминала» и «устройство терминала». /dev/ttys006 — это устройство терминала, само по себе оно не имеет никакого отношения к эмулятору терминала. Эмулятор терминала обычно создаёт это устройство и читает из него, чтобы определить, что нужно отображать, но, к примеру, эмулятор терминала на libvterm (который новый, а не который из ROTE) вполне может обойтись без устройства /dev/…: можно просто скормить ему строку, зашитую в исполняемый файл, который использует libvterm. Или читать из pipe. Правда, часто программы изменяют своё поведение, если определяют, что один из потоков std* подключён не к устройству терминала (проверяют обычно stdout или stderr).

              «Пары устройств» также нету. Терминал — это одно устройство терминала.
              • 0
                Спасибо за ответ. Что такое <A-FN$>?

                Возможно, я неправильно понял man, но мое представление об эмуляторах терминала отсюда: linux.die.net/man/4/ptmx
                Согласно описанию, нужно открыть /dev/ptmx, получить a file descriptor (первое устройство, в которое писать), потом сделать некие телодвижения и получить второе устройство, /dev/pts* — из которого читать.

                Вы правы, теоретически можно обойтись без устройств. Но практически так никто делать не станет. Не говоря уже о ls, который обрезает control sequences, su и passwd не читают из pip'ов.
                • 0
                  Спасибо за ответ. Что такое <A-FN$>?
                  Там $gt; — я просто случайно написал $ вместо амперсанда. Я имел ввиду эмуляторы терминала, создаваемые самим ядром при запуске (в X11 доступны через <C-A-Fn, в самих эмуляторах или после какого‐то <A-SysRq-…> без <C-).

                  Возможно, я неправильно понял man, но мое представление об эмуляторах терминала отсюда: linux.die.net/man/4/ptmx
                  Согласно описанию, нужно открыть /dev/ptmx, получить a file descriptor (первое устройство, в которое писать), потом сделать некие телодвижения и получить второе устройство, /dev/pts* — из которого читать.
                  Таким образом вы никак не получите эмулятор терминала. Эмулятор терминала — эта та программа, которая читает, а затем показывает обратно. То, из чего она читает эмулятором терминала не является и в man 4 ptmx нигде не написано, что это эмулятор. Терминал ≠ эмулятор терминала; обычно в man страницах в linux терминал = устройство терминала, не более.

                  Хотя в одном вы правы, а я нет — терминал — это два устройства, а не одно.

                  Вы правы, теоретически можно обойтись без устройств. Но практически так никто делать не станет. Не говоря уже о ls, который обрезает control sequences, su и passwd не читают из pip'ов.
                  Во‐первых ls не обрезает, а не выводит. Во‐вторых, ls можно заставить, так же как и grep и много других программ со схожим поведением (тот же pv (pipe viewer) и bash тоже можно — они ничего не делают с цветами, но поведение терминал/не терминал различается). В‐четвёртых, я сильно подозреваю, что так делали и делают, когда получают несанкционированный shell на сервере: pty создать там не особо нужно, и может быть просто невозможно из‐за отсутствия прав.
                  • 0
                    Несмотря на то, что я сумбурно изъясняюсь, я таки понимаю, что иметь устройства ввода-вывода еще недостаточно, чтобы быть эмулятором терминала. Но они — публичный интерфейс эмулятора, если проводить параллели с OOP. Все, что внутри — неважно для внешнего наблюдателя. Ну да, умеет он как-то преобразовывать ANSI и показывать его.

                    С ls та же самая история. Обрезает ли он ANSI, или не добавляет его с самого начала, результат один: если output не pty, вывод отличается.

                    Я в свободное время пытаюсь написать эмулятор терминала, и уверен, что в реальном мире, единственный способ обеспечить пользователям предсказуемое поведение — прикидаться PTY.

                    То же и с последовательностями управления. Хотелось бы иметь возможность написать свой terminfo, и забыть о совместимости с ANSI, но программисты идут самым простым путем, и существует вагон и маленькая тележка утилит, у которых захардкоджены эти последовательности.

                    Если позволите, я хотел бы попросить совет. В современно мире, не имея наследия visual terminals и таких ограничений ресурсов, нужно ли передавать управляющие последовательности in-bound, или стоит их вынести в отдельный поток? Извините за off-topic.
        • 0
          ROTE устанавливается простым ./configure && make && make install. Надо отследить, чтобы она установилась туда, где её увидит система сборки. Я использую для этого make install prefix=/usr
          Обычно если есть configure, то корректный вариант никак не make install prefix=/usr, а ./configure --prefix=/usr.

          Ну и я не советую вам использовать --prefix=/usr и, уж точно, никогда и никому не рекомендовать так делать. Если вам нравиться помойка в системе — это ваше право, нормальные люди же либо создают пакет (в данном случае — скорее используют имеющийся), либо ставят куда‐нибудь в /opt, откуда можно удалить практически одной командой (+ вычищение мусора из /etc, но /etc можно организовать так, что это это очищение сведётся к hg backout --commit {offending-revision}, да и мусор строго в двух известных местах — там, где определяется $PATH и другие переменные окружения (e.g. MANPATH) и там, где определяются пути до библиотек (в Gentoo это вообще можно иметь в одном месте: /etc/env.d)).
          • 0
            Можете обосновать, почему make install prefix=/usr не является корректным вариантом? Я понимаю, что можно указать на уровне configure, но это не делает автоматически первый вариант некорректным.

            Дополнил инструкцию упоминанием про checkinstall. Впрочем, пока всё происходит на компьютере разработчика, вряд ли кому-то навредит файл /usr/lib/librote.so, а в серьёзном месте должен быть строгий сисадмин, который следит, чтобы такого не было.
            • +2
              Часто в проектах есть пути вроде /usr/share/zsh/{version}, которые прописываются прямо в бинарнике. Здесь /usr — это prefix, а в указанном каталоге находятся очень нужные функции. Если в библиотеке ROTE такого нет (скорее всего, действительно нет) — то вам повезло.

              Но также, как и в случае с «вряд ли кому-то навредит файл /usr/lib/librote.so» нужно формировать правильные привычки. Указанная библиотека сейчас устанавливает /usr/lib/librote.so, а в следующей версии наследники напишут установку с /usr/lib/librote.so.0, /usr/lib/librote.so, /usr/share/doc/rote/README, /usr/share/terminfo/r/rote, /usr/share/man/man3/{много страниц для каждой из функций}.3, /usr/share/info/rote.info, /usr/bin/rote-dump и /etc/rote.conf. И хрен вы теперь что вычистите, особенно если нет make uninstall (cmake такого, к примеру, не предоставляет) или вы забыли, какую версию устанавливали, а make uninstall от новой версии не подходит.

              Суть в том, что 1. вы считаете автора достаточно порядочным, чтобы он устанавливал именно туда, куда вы говорите. И 2. тем не менее, вы не знаете, что именно устанавливается. Или, с правильными привычками вам не нужно знать, что именно устанавливается.

              Также с правильными привычками вам не нужно знать, захардкоден ли где‐либо prefix. Скажите честно, вы выполняли анализ скрипта установки перед тем, как заключить, что он устанавливает только /usr/lib/librote.so? А анализ кода с целью убедиться, что prefix в коде нигде не используется? Если нет, то то, что вы делаете есть подбрасывания кубика с целью угадывания «загадит систему — не загадит», «будет работать корректно — будет работать некорректно — не будет работать вовсе».
              • 0
                А если да, но анализ не был частью более общего вроде «а не подсунули ли мне malware» (или «а не сделал ли автор глупых ошибок при написании сборочных скриптов, которые испортят мне систему ненамеренно»), то вы потратили кучу времени на то, на что время можно было не тратить.
                • 0
                  Лучше ставить в виртуалке, если есть обоснованные подозрения на malware. В данном случае их нет, так как пакет 10 лет лежит на SF. Разумеется, сверку контрольных сумм никто не отменял.
                  • 0
                    Во‐первых, этот комментарий не про то. Во‐вторых, я не говорил про метод анализа. Что куда устанавливается и работает ли библиотека, если вы не давали ей корректный prefix вплоть до установки можно проверить и глядя на скрипт, и на виртуалке. Только если вы этого не делали, а используете свой метод, то подставляете свою систему под проблемы даже если вы установили не malware. А если вы делали анализ, но анализировали только корректность и результат (установленные файлы) установки именно с помощью ваших методов при указанных настройках, то вы потратили время на менее тривиальный (метод указания prefix при make prefix=… install нестандартный) анализ в то время, как могли этого избежать.
                    • 0
                      Так вы отвечали на первую часть комментария, а не на вторую. То есть, обосновывали некорректность make prefix=… install. Достаточно одного взгляда на Makefile, чтобы понять, что кроме prefix ничего менять не надо. Сколько времени занимает прийти к такому умозаключению, не влияет на корректность. Таким образом, некорректность метода make install prefix=/usr не доказана.

                      К слову, лично у меня от файлов configure волосы на голове дыбом и разбираться с ними и тем более помнить, какие у них опции, заняло бы куда больше времени, чем заглянуть в Makefile и увидеть там prefix=/usr/local.
                      • +1
                        Вы не поняли. Вам не нужно заглядывать в Makefile. Вам нужно, по крайней мере, в тот самый файл configure и убедиться, что некорректное значение prefix ни в одном из мест, где configure его сохраняет (даже у rote configure сохраняет prefix не только в Makefile), не влияет фатально на работу библиотеки.

                        Часто configure сохраняет prefix (точнее, не он сам, а какая‐то производная вроде libdir=$prefix/lib) в config.h, информация о prefix из которого используется для определения каталога, откуда нужно загружать необходимые ресурсы.

                        При использовании стандартного способа с ./configure --prefix вам нужно прочитать ./configure --help. При использовании вашего способа вам нужно прочитать configure.ac и неопределённое количество файлов с исходным кодом (или, по крайней мере, определить «на живую», куда был сохранён prefix (здесь — три файла) и где это «куда» используется). Только так вы можете проверить корректность make prefix=. Прочитав Makefile вы не можете проверить корректность вашего метода, потому что Makefile не является единственным файлом, в который этот prefix используется.
                        • +2
                          Спасибо, вы правы. Выставлять prefix при make install чревато ошибками. Заменил инструкции и тестирующие скрипты.
                        • 0
                          Я выразился как‐то длинно. Кратко будет: вы не можете проверить корректность использования make prefix=… install, прочитав Makefile, так как Makefile не является единственным файлом, в котором configure сохраняет prefix.
                          • 0
                            И ещё: попытайтесь использовать ваш способ с Vim. Убедитесь, что он нифига не поддерживает подсветку синтаксиса, так как не может найти свои файлы.

                            ./configure --prefix=… работает всегда, когда используется configure, сгенерированный с помощью autotools (других configure я, к счастью, не видел). make prefix=… install есть опасная привычка, которая гарантированно ведёт к некорректному результату в случае проектов с ресурсами, импортируемыми из $prefix/share (примеры: Vim, zsh, fish).
              • 0
                По поводу libvterm: во‐первых, вы указали репозиторий с исходным кодом, модифицированным для проекта NeoVim. Правильный URL: www.leonerd.org.uk/code/libvterm/. Во‐вторых, этот libvterm не основан на ROTE (или основан, но автор не признаётся). На ROTE официально основан sourceforge.net/projects/libvterm/.
                • 0
                  И последний, кстати, тоже мёртв, просто пролежал меньше (2 vs 8) лет. В репозитории всего 11 ревизий, бо́льшая часть из них связана с исправлением ошибок. Судя по всему, раньше репозиторий был в другом месте и менялся активнее, но историю не импортировали; первая ревизия — 2010, последняя — 2013.

                  На странице rote.sourceforge.net/ явным образом ссылаются на второй проект (на SF).
                  • 0
                    Спасибо, исправил.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.