Портирование — дело тонкое: проверка Far Manager под Linux

    Одним из популярных файловых менеджеров в среде Microsoft Windows является Far Manager, принявший эстафету у Norton Commander, созданной еще для DOS. Far Manager позволяет облегчить работу с файловой системой (создание, редактирование, просмотр, копирование, перемещение, поиск, удаление файлов), а также расширяет стандартный функционал (работа с сетью, архивами, резервными копиями и т.д.). Недавно была произведена работа по портированию Far Manager на Linux, и на текущий момент была выпущена альфа-версия. Команда PVS-Studio не могла обойти стороной данное событие и решила проверить качество адаптированного кода.
    Picture 24

    Немного о Far Manager


    Far Manager — консольный файловый менеджер для операционных систем семейства Microsoft Windows, ориентированный на работу с клавиатурой. FAR Manager наследует двухоконную идеологию, стандартную (по умолчанию) расцветку и систему команд (управление с клавиатуры) у известного файлового менеджера Norton Commander и предоставляет удобный интерфейс пользователя для работы с файлами (создания файлов и каталогов, просмотра, редактирования, копирования, переименования, удаления и т.д.).

    Рисунок 1 - Far Manager 2 на Windows (нажмите на картинку для увеличения)


    Рисунок 1 — Far Manager 2 на Windows (нажмите на картинку для увеличения)

    Автор программы — Евгений Рошал. Первая версия была выпущена 10 сентября 1996 года. Последняя версия, к которой приложил руку Рошал, датируется 23 июня 2000 года (версия 1.65). Фактически с того момента разработкой FAR Manager начала заниматься группа «FAR Group». Следующий релиз v1.70 датируется уже 29 марта 2006 года. 13 декабря 2008 года выходит версия 2.0, программа стала бесплатной (open source) и распространяется под модифицированной лицензией BSD. Все версии Far от 1.70 до 2.0 практически не имеют внешних отличий, и не требуют от пользователя каких-либо дополнительных усилий по освоению программы при переходе на новую версию. Начиная с версии 1.80 появилась поддержка Unicode. Последней выпущенной версией считается 3.0 от 4 ноября 2016 года.

    10 августа 2016 года была опубликована первая тестовая сборка файлового менеджера Far2l (Linux). На данный момент сборка содержит встроенный работающий терминал, а также плагины Align, AutoWrap, Colorer, DrawLine, Editcase, FarFTP, FarLng, MultiArc, NetBox, SimpleIndent, TmpPanel. Код распространяется под лицензией GPLv2.

    Рисунок 2 - Far Manager 2 на Linux (нажмите на картинку для увеличения)


    Рисунок 2 — Far Manager 2 на Linux (нажмите на картинку для увеличения)

    От слов к делу


    После проверки проекта Far2l было получено 1038 предупреждений общего назначения. На следующей диаграмме представлено распределение предупреждений по уровням достоверности:

    Рисунок 1 - Распределение предупреждений по уровням достоверности (важности)


    Рисунок 1 — Распределение предупреждений по уровням достоверности (важности)

    Кратко прокомментируем приведенную диаграмму: было получено 153 предупреждения уровня High, 336 предупреждений уровня Medium, 549 предупреждений уровня Low.

    Несмотря на достаточно большое число предупреждений, стоит помнить, что не каждое из них является реальной ошибкой. Просмотрев отчет, содержащий только предупреждения уровня High и Medium, мной было выделено 250 случаев, являющихся скорее всего реальными ошибками.

    Если брать уровни High и Medium, то получается, что процент ложных срабатываний составил около 49%. Другими словами, каждое второе предупреждение выявляет дефект в коде.

    Теперь определим относительную плотность реальных ошибок, найденных анализатором PVS-Studio. Суммарное количество строк исходного кода (SLOC) — 538675. Следовательно, плотность будет равна 0.464 ошибки на 1000 строк кода. Когда-нибудь мы соберем эти числа и напишем обобщающую статью о качестве кода различных проектов.

    Стоит отметить, что данный показатель не демонстрирует общую плотность ошибок по всему проекту — она может быть, как больше (анализатор не сработал на реальной ошибке), так и меньше (анализатор сработал на правильном коде). Как правило, на других проектах мы получаем большую плотность найденных ошибок. Можно сказать, что с точки зрения качества кода портирование прошло успешно. Впрочем, настоятельно рекомендуется исправить найденные ошибки, ведь они далеко небезобидные.

    Результаты проверки


    Хочу заранее предупредить, что для удобства чтения код будет подвергаться рефакторингу. Также, в статье содержатся далеко не все ошибки, обнаруженные в ходе проверки, а только наиболее интересные из них.

    Copy-Paste


    Picture 28


    Предупреждение анализатора: V501 There are identical sub-expressions 'Key == MCODE_F_BM_GET' to the left and to the right of the '||' operator. macro.cpp 4819

    int KeyMacro::GetKey()
    {
      ....
      DWORD Key = !MR ? MCODE_OP_EXIT : GetOpCode(MR, Work.ExecLIBPos++);
      ....
      switch (Key)
      {
      ....
      case MCODE_F_BM_POP:
      {
        TVar p1, p2;
    
        if (Key == MCODE_F_BM_GET)
          VMStack.Pop(p2);
    
        if (   Key == MCODE_F_BM_GET    // <=
            || Key == MCODE_F_BM_DEL 
            || Key == MCODE_F_BM_GET    // <=
            || Key == MCODE_F_BM_GOTO)
        {
          VMStack.Pop(p1);
        }
        ....
      }
      }
    }

    Переменную Key дважды сравнили с константой MCODE_F_BM_GET. Вероятно, это опечатка, и Key следовало сравнить ещё c какой-то константой. Анализатор нашел еще 3 похожих места:

    • V501 There are identical sub-expressions '!StrCmpN(CurStr, L"!/", 2)' to the left and to the right of the '||' operator. fnparce.cpp 291
    • V501 There are identical sub-expressions '!StrCmpN(CurStr, L"!=/", 3)' to the left and to the right of the '||' operator. fnparce.cpp 291
    • V501 There are identical sub-expressions 'KEY_RCTRL' to the left and to the right of the '|' operator. keyboard.cpp 1830

    Предупреждение анализатора: V581 The conditional expressions of the 'if' operators situated alongside each other are identical. Check lines: 267, 268. APIStringMap.cpp 268

    static BOOL WINPORT(GetStringType)( DWORD type,
                                        LPCWSTR src,
                                        INT count,
                                        LPWORD chartype )
    {
      ....
      while (count--)
      {
        int c = *src;
        WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
        ....
        if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH; // <=
        if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;    // <=
        ....
      }
      ....
    }

    Видимо, второе условие писалось по принципу Copy-Paste и оно абсолютно идентично первому. Однако, если замысел в этом и заключался, то можно упростить код, убрав второе условие:

    ....
    if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH | C3_SYMBOL; 
    ....

    Найденная ошибка оказалась далеко не единственной:

    • V581 The conditional expressions of the 'if' operators situated alongside each other are identical. Check lines: 272, 273. APIStringMap.cpp 273
    • V581 The conditional expressions of the 'if' operators situated alongside each other are identical. Check lines: 274, 275. APIStringMap.cpp 275
    • V581 The conditional expressions of the 'if' operators situated alongside each other are identical. Check lines: 6498, 6503. macro.cpp 6503
    • V581 The conditional expressions of the 'if' operators situated alongside each other are identical. Check lines: 1800, 1810. vmenu.cpp 1810
    • V581 The conditional expressions of the 'if' operators situated alongside each other are identical. Check lines: 3353, 3355. wrap.cpp:3355

    Предупреждение анализатора: V523 The 'then' statement is equivalent to the 'else' statement. Queque.cpp 358

    void FTP::AddToQueque(FAR_FIND_DATA* FileName, 
                          LPCSTR Path, 
                          BOOL Download)
    {
      ....
      char *m;
      ....
      if(Download)
        m = strrchr(FileName->cFileName, '/'); // <=
      else
        m = strrchr(FileName->cFileName, '/'); // <=
      ....
    }

    Подозреваю, что и здесь условие писалось по принципу «Copy-Paste»: вне зависимости от значения Download (TRUE, FALSE), в указатель m будет сохранена позиция последнего вхождения символа '/'.

    Неопределенное поведение


    Picture 37


    Предупреждение анализатора: V567 Undefined behavior. The 'Item[FocusPos]->Selected' variable is modified while being used twice between sequence points. dialog.cpp 3827

    int Dialog::Do_ProcessSpace()
    {
      ....
      if (Item[FocusPos]->Flags & DIF_3STATE)
        (++Item[FocusPos]->Selected) %= 3;       // <=
      else
        Item[FocusPos]->Selected = !Item[FocusPos]->Selected;
      ....
    }

    Здесь налицо явное неопределенное поведение: переменную Item[FocusPos]->Selected дважды меняют в одной точке следования (инкремент и деление по модулю 3 с присвоением результата).

    Было найдено еще одно место с подобным неопределенным поведением:

    • V567 Undefined behavior. The '::ViewerID' variable is modified while being used twice between sequence points. viewer.cpp 117

    Предупреждение анализатора: V610 Undefined behavior. Check the shift operator '<<'. The right operand 'sizeof (wchar_t) * 8' is greater than or equal to the length in bits of the promoted left operand. RegExp.cpp 4467

    #define rechar wchar_t
    #define RE_CHAR_COUNT (1 << sizeof(rechar) * 8)
    
    int RegExp::Optimize()
    {
      ....
      for (op=code; ; op=op->next)
      {
        switch (OP.op)
        {
        ....
        case opType:
        {
          for (int i = 0; i < RE_CHAR_COUNT; i++)    // <=
          {
            if (ISTYPE(i, OP.type))
            {
              first[i]=1;
            }
          }
          
          break;
        }
        }
        ....
      }
      ....
    }

    Суть ошибки заключается в следующем: в Linux тип wchar_t имеет размер 4 байта. Следовательно, происходит сдвиг знакового int (4 байта) на 32 бита влево. Согласно стандарту C++11, если левый операнд является знаковым положительным числом, сдвиг влево на N бит приведет к неопределенному поведению, если N не меньше, чем количество бит в левом операнде. Корректный код будет выглядеть следующим образом:

    #define rechar wchar_t
    #define RE_CHAR_COUNT (static_cast<int64_t>(1) << sizeof(rechar) * 8)
    
    int RegExp::Optimize()
    {
      ....
      for (int64_t i = 0; i < RE_CHAR_COUNT; i++)
      {
        ....
      }
      ....
    }

    Были найдены еще несколько мест, ведущие к неопределенному поведению при сдвиге влево:

    • V610 Undefined behavior. Check the shift operator '<<'. The right operand 'sizeof (wchar_t) * 8' is greater than or equal to the length in bits of the promoted left operand. RegExp.cpp 4473
    • V610 Undefined behavior. Check the shift operator '<<'. The right operand 'sizeof (wchar_t) * 8' is greater than or equal to the length in bits of the promoted left operand. RegExp.cpp 4490
    • V610 Undefined behavior. Check the shift operator '<<'. The right operand 'sizeof (wchar_t) * 8' is greater than or equal to the length in bits of the promoted left operand. RegExp.cpp 4537
    • V610 Undefined behavior. Check the shift operator '<<'. The right operand 'sizeof (wchar_t) * 8' is greater than or equal to the length in bits of the promoted left operand. RegExp.cpp 4549
    • V610 Undefined behavior. Check the shift operator '<<'. The right operand 'sizeof (wchar_t) * 8' is greater than or equal to the length in bits of the promoted left operand. RegExp.cpp 4561

    Неверная работа с памятью


    Picture 41



    Начнем новый раздел с небольшой разминки. Предлагаю попытаться найти ошибку самостоятельно (подсказка — она в функции TreeItem::SetTitle).

    class UnicodeString
    {
      ....
      UnicodeString(const wchar_t *lpwszData) 
      { 
        SetEUS(); 
        Copy(lpwszData); 
      }
      ....
      const wchar_t *CPtr() const { return m_pData->GetData(); }
      operator const wchar_t *() const { return m_pData->GetData(); }
      ....
    }
    
    typedef UnicodeString FARString;
    
    struct TreeItem
    {
      FARString strName;
      ....
    }
    
    TreeItem **ListData;
    
    void TreeList::SetTitle()
    {
      ....
      if (GetFocus())
      {
        FARString strTitleDir(L"{");
        const wchar_t *Ptr = ListData 
                             ? ListData[CurFile]->strName
                             : L""; 
        ....
      }
      ....
    }

    Предупреждение анализатора: V623 Consider inspecting the '?:' operator. A temporary object of the 'UnicodeString' type is being created and subsequently destroyed. Check third operand. treelist.cpp 2093

    Неочевидная ошибка, не правда ли? В текущем контексте переменная ListData[CurFile]->strName является объектом класса UnicodeString. В классе UnicodeString перегружен оператор неявного преобразования в тип const wchar_t*. Теперь обратите внимание на тернарный оператор в функции TreeList::SetTitle: второй и третий операнды являются разными типами (UnicodeString и const char[1] соответственно). Подразумевалось, что если первый операнд вернет false, то указатель Ptr будет адресоваться на пустую строку. Поскольку конструктор класса UnicodeString не объявлен как explicit, на самом деле будет создан временный объект, содержащий пустую строку, который неявно приведется к типу const wchar_t*; далее временный объект уничтожится и Ptr будет указывать на невалидные данные. Исправленный код будет выглядеть таким образом:

    ....
    const wchar_t *Ptr = ListData 
                         ? ListData[CurFile]->strName.CPtr()
                         : L"";
    ....

    Следующий код примечателен тем, что на него сработали одновременно две диагностики.

    Предупреждения анализатора:

    • V779 Unreachable code detected. It is possible that an error is present. 7z.cpp 203
    • V773 The function was exited without releasing the 't' pointer. A memory leak is possible. 7z.cpp 202

    BOOL WINAPI _export SEVENZ_OpenArchive(const char *Name,
                                           int *Type)
    {
      Traverser *t = new Traverser(Name);
      if (!t->Valid()) 
      {
        return FALSE;
        delete t;
      }
      
      delete s_selected_traverser;
      s_selected_traverser = t;
      return TRUE;
    }

    Что же здесь можно обнаружить? Во-первых, действительно, в операторе if имеется недостижимый код: если условие выполняется, то функция возвращает FALSE, завершая свою работу. А из-за недостижимого кода всего-навсего произошла утечка памяти — объект по указателю t не удаляется. Чтобы исправить ошибки, достаточно поменять два оператора местами внутри блока.

    Следующий код покажет, как можно ошибиться при определении размера объекта класса (структуры) через указатель.

    Предупреждения анализатора:

    • V568 It's odd that 'sizeof()' operator evaluates the size of a pointer to a class, but not the size of the 'PInfo' class object. filelist.cpp 672
    • V568 It's odd that 'sizeof()' operator evaluates the size of a pointer to a class, but not the size of the 'PInfo' class object. filelist.cpp 673

    int64_t FileList::VMProcess(int OpCode,
                                void *vParam,
                                int64_t iParam)
    {
      switch (OpCode)
      {
      ....
      case MCODE_V_PPANEL_PREFIX:           // PPanel.Prefix
      {
        PluginInfo *PInfo = (PluginInfo *)vParam;
        memset(PInfo, 0, sizeof(PInfo));          // <=
        PInfo->StructSize = sizeof(PInfo);        // <=
        ....
      }
      ....
      }
    }

    Обе ошибки заключаются в том, что sizeof(PInfo) вместо ожидаемого размера структуры вернет размер указателя (4 или 8 байт). Соответственно, memset заполнит нулями только первые 4 (8) байт структуры, а также в поле PInfo->StructSize запишется размер указателя. Корректный код будет выглядеть следующим образом:

    ....
    PluginInfo *PInfo = (PluginInfo*)vParam;
    memset(PInfo, 0, sizeof(*PInfo));
    PInfo->StructSize = sizeof(*PInfo);
    ....

    Анализатор обнаружил еще пару похожих мест:

    • V568 It's odd that 'sizeof()' operator evaluates the size of a pointer to a class, but not the size of the 'HistoryItem' class object. history.cpp 594
    • V568 It's odd that 'sizeof()' operator evaluates the size of a pointer to a class, but not the size of the 'handle' class object. plugins.cpp 682

    Странные условия


    Picture 39


    И снова предлагаю небольшую разминку — попробуйте самостоятельно найти ошибку в следующей части кода:

    int FTP::ProcessKey(int Key, unsigned int ControlState)
    {
      ....
      if(   !ShowHosts 
         && (ControlState == 0 || ControlState == PKF_SHIFT) 
         && Key == VK_F6)
      {
        FTP *ftp = OtherPlugin(this);
        int  rc;
    
        if(   !ftp 
           && ControlState == 0 
           && Key == VK_F6)
        {
          return FALSE;
        }
        ....
      }
      ....
    }

    Предупреждение анализатора: V560 A part of conditional expression is always true: Key == 0x75. Key.cpp 493

    Обратите внимание на внешнее и внутреннее условия: в них Key сравнивается с константой VK_F6. Если поток управления достигает внутреннего условия, то переменная Key гарантировано будет равна VK_F6 и вторая проверка этой переменной будет лишней. В упрощённом виде второе условие будет выглядеть так:

    ....
    if(   !ftp 
       && ControlState == 0)
    {
      return FALSE;
    }
    ....

    Анализатор предупреждает об этой ошибке и о некоторых подобных:

    • V560 A part of conditional expression is always true: !cps. DString.cpp 47
    • V560 A part of conditional expression is always true: !ShowHosts. FGet.cpp 139
    • V560 A part of conditional expression is always false: !wsz. cnDownload.cpp 190
    • V560 A part of conditional expression is always true: !UserReject. extract.cpp 485
    • And 8 additional diagnostic messages.

    Предупреждение анализатора: V503 This is a nonsensical comparison: pointer <= 0. fstd_exSCPY.cpp 8

    char *WINAPI StrCpy(char *dest, LPCSTR src, int dest_sz)
    {
      if(dest <= 0)   // <=
        return NULL;
      ....
    }

    Данный код содержит бессмысленное сравнение указателя с отрицательным числом (указатели не работают с областями памяти с отрицательными адресами). Исправленный код может выглядеть следующим образом:

    ....
    if(dest == nullptr)
      return NULL;
    ....

    Предупреждение анализатора: V584 The FADC_ALLDISKS value is present on both sides of the '==' operator. The expression is incorrect or it can be simplified. findfile.cpp 3116

    enum FINDASKDLGCOMBO
    {
      FADC_ALLDISKS,
      FADC_ALLBUTNET,
      ....
    };
    
    FindFiles::FindFiles()
    {
      
      ....
      if (   FADC_ALLDISKS + SearchMode == FADC_ALLDISKS     // <=
          || FADC_ALLDISKS + SearchMode == FADC_ALLBUTNET)
      {
        ....
      }
      ....
    }

    Анализатор обнаружил подозрительное первое подусловие. Исходя из перечисления FINDASKDLGCOMBO, константа FADC_ALLDISKS равна 0, FADC_ALLBUTNET равна 1. Если подставить численные значения констант в условные выражение, то получим следующее:

    if (   0 + SearchMode == 0
        || 0 + SearchMode == 1)
    {
      ....
    }

    Исходя из кода выше, все условие можно упростить:

    if (   SearchMode == FADC_ALLDISKS
        || SearchMode == FADC_ALLBUTNET)
    {
      ....
    }

    Некорректная работа с форматной строкой


    Picture 40


    Предупреждение анализатора: V576 Incorrect format. Consider checking the fourth actual argument of the 'swprintf' function. The char type argument is expected. FarEditor.cpp 827

    void FarEditor::showOutliner(Outliner *outliner)
    {
      ....
      wchar_t cls = 
        Character::toLowerCase((*region)[region->indexOf(':') + 1]);
      
      si += swprintf(menuItem+si, 255-si, L"%c ", cls); // <=
      ....
    }

    Вероятно, это ошибка портирования. Проблема заключается в том, что в Visual C++ в функциях вывода широких строк нестандартно интерпретируются спецификаторы в форматной строке: %c ожидает широкий символ (wide char, wchar_t). В Linux дела обстоят иначе: в соответствии со стандартом по спецификатору %c будет ожидаться многобайтовый символ (multibyte symbol, char). Корректный код будет выглядеть следующим образом:

    si += swprintf(menuItem+si, 255-si, L"%lc ", cls);

    Предупреждение анализатора: V576 Incorrect format. Consider checking the fourth actual argument of the 'swprintf' function. The pointer to string of char type symbols is expected. cmddata.cpp 257

    void CommandData::ReadConfig()
    {
      ....
      wchar Cmd[16];
      ....
      wchar SwName[16+ASIZE(Cmd)];
      swprintf(SwName,ASIZE(SwName), L"switches_%s=", Cmd);  // <=
      ....
    }

    Похожая ситуация: форматная строка содержит спецификатор %s, следовательно, ожидается многобайтовая строка (char*). Однако, следующим параметром была передана широкая строка (wchar_t*). Корректный код будет выглядеть следующим образом:

    swprintf(SwName,ASIZE(SwName), L"switches_%ls=", Cmd);

    Анализатор также предупреждает и о двух других неверных способах передачи параметров в соответствии с форматной строкой:

    • V576 Incorrect format. Consider checking the third actual argument of the 'fprintf' function. The char type argument is expected. vtansi.cpp 1033
    • V576 Incorrect format. Consider checking the third actual argument of the 'fprintf' function. The char type argument is expected. vtansi.cpp 1038

    Выводы


    Что можно сказать про порт Far на Linux? Да, ошибок было найдено достаточно, однако не стоит забывать, что проект находится в состоянии альфа-версии и продолжает развиваться. Применяя методологию статического анализа кода, ошибки можно найти еще на раннем этапе и не допустить их в репозиторий. Стоит отметить, что все преимущества статического анализа будут проявляться только при его регулярном использовании (в крайнем случае — во время «ночных» сборок).

    От своего лица я предлагаю оценить пользу статического анализа с помощью PVS-Studio: продукт доступен для Microsoft Windows и deb/rpm-based дистрибутивов Linux, позволяя быстро и регулярно проверять Ваш проект. Также, если Вы студент, индивидуальный разработчик, или участвуете в разработке открытого некоммерческого проекта, то предлагается возможность бесплатного использования PVS-Studio.

    В этом ознакомительном видео Вы можете узнать, как установить PVS-Studio для Linux и быстро проверить свой проект (на примере Far Manager):



    Также, если Вы знаете интересный проект, который следовало бы проверить, то Вы можете предложить его нам на GitHub. Подробнее в статье: "Предложи проект для проверки анализатором PVS-Studio: теперь и на GitHub".



    Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Phillip Khandeliants. Porting is a Delicate Matter: Checking Far Manager under Linux

    Прочитали статью и есть вопрос?
    Часто к нашим статьям задают одни и те же вопросы. Ответы на них мы собрали здесь: Ответы на вопросы читателей статей про PVS-Studio, версия 2015. Пожалуйста, ознакомьтесь со списком.
    PVS-Studio
    199.63
    Static Code Analysis for C, C++, C# and Java
    Share post

    Similar posts

    Comments 61

      –2
      MC не ?!
        +1
          0
          Я имел ввиду, зачем портировать Far если уже есть Midnight Commander.
            +5
            Ой, это не к нам. Мы просто разместили объяву проверили проект.
              +10

              image

                0
                рекомендую file commander линуксовый, хоть и не совсем фришный как замену мц в линуксе.
                  0
                  У меня не получилось запустить его в полноэкранной консоли, в ubuntu mate пробовал. Может где-то настраивается, но не нашел.
                    0
                    С такой необходимостью не сталкивался. Да, возможно, что не выйдет. :(
                      0
                      github.com/elfmz/far2l/issues/276

                      Там есть ссылка на экспериментальную ветку. В ней так уже работает. Не полностью и не всё пока, впрочем.
                +8
                Ну вот я из тех, кто пользуется Far под Windows и MC под Linux. Мне было бы удобно, если бы Far был портирован под Linux, тем более что большую часть времени я провожу как раз в Windows, и пересаживаясь на Linux, бывает на автомате жму там горячие клавиши, которые в MC отличаются. Теоретически Far под wine можно запустить, но надо будет на досуге и Far2l попробовать.
                  +5
                  Да, чёрт возьми, если я в Far-е просмотрел файл по F3 то нажимаю (что логично) Esc чтобы вернуться в консоль. Какого хрена mc мне гадит в командную строку?

                  +в Far-е горячие клавиши работают вне зависимости от текущей раскладки (по крайней мере для ENG+RUS). Вот что бывает, когда программист пользуется программой, которую пишет.
                    +1
                    Скорее, что бывает, когда у программиста на машине более одной локали.
                      +2
                      Это не совсем MC, это терминал.
                      По спецификации VT102 должны быть непечатные символы для разнообразной фигни типа управления терминалом, цветами, положением на экране и всё такое — http://man7.org/linux/man-pages/man4/console_codes.4.html
                      MC не пытается перехватывать эти коды и работает на том, что до него докатывается. 8-)

                      Но Far/win + MC/lin это, конечно, боль.
                      Меня вот только ConEmu + mintty как-то спасают. 8-)
                        +3
                        Блин, ка графическую подсистему менять каждые 2 года (это я про ubuntu) — так нормально — «развиваем опенсорс». А как тащить десятилетиями VT102 — так это обратная совместимость?

                        А можно подробнее — как Вы используете ConEmu + mintty?
                          0
                          конема управляет табами, в табах фар и mintty
                          mintty запускает баш от вингита, и в принципе служит только для запуска ssh — а все локальные задачи выполняются в фаре.
                  +5
                  Использую и то и другое ежедневно, mc в основном на маке т.к. пока там с фаром проблемы. Вот пара примеров:

                  В фаре я просто набираю «ff» и высвечивается уже фильтрованная история всех моих запусков ffmpeg. В mc надо alt-h и искать самому в полной истории, где может быть много мусора.

                  Заливка по sftp, mc: F9-L-SFTP Link, появляется окно выбора, по дефолту там последний, чтобы выбрать другой, надо мышкой нажать дропдавн бокс хистори, табом на нем не фокусируется (или alt-h). Причем т.к. это хистори — редактировать этот список нельзя.
                  Far: Alt-F1->Netbox появляется панель со всеми сохраненными сетевыми путями, Enter и все (под линь он правда еще полностью не портирован, так что это сравнение с виндовой версией).

                  Проблемы которые вставляет консоль из-за чего надо нажимать esc-esc как я понял тоже принципиально не решаются, не ломая какие то другие приложения, можно только закостылить. В консоли по ssh это можно терпеть, но для локального файл менеджера — это можно решить и будет работать везде из коробки без настроек, что и было сделано. Сейчас фар позиционируется именно как локальный ФМ, но консоль и веб версии позже тоже присоединятся.

                  Так же не маловажная вещь — палгины. У фара есть мощный и гибкий API плагинов.
                    0
                    Вот насчет Alt-F1 — не уверен, что удастся перенести.
                      +2
                      Ну оно уже работает, я просто отключил эти горячие клавиши в настройках, на маке это требуется и для нормальной работы в mc тоже. Вот если кто-то к ним уже привык — тут да, придется или переучиваться или в фаре комбинацию поменять.
                        0

                        Может завезёте "липкий" Alt? К примеру комбинацией Alt+A. Для чего: в современных DE да и привычки ради Alt+F1, Alt+F2 делают уже полезную работу. Начнём с того, что вы выше сказали о теоретической возможности портироваться на консоль, а там это будет отрабатывать как переключение виртуальных терминалов. В иксах Alt+F2 часто используется для вызова диалого запуска программ, всяких gmrun или krunner и компания.


                        Смысл: нажимаем Alt+A, отпускаем, но Far начинает считать, что клавиша Alt зажата и не отпущена. И следующая клавиша даст комбинацию с Alt.


                        Таким образом Alt+F1 можно будет вызвать: Alt+A, F1.


                        Для завершения режима: Esc, или повторный Alt+A.

                      0
                      5 копеек про дропдаун в mc: по нему можно гонять через CTRL-Up/Down
                      Но не везде.
                      в SLES12 (mc 4.8.11), RH7 (4.8.7), RH6 (4.7.0.2), ubuntu14 (mc 4.8.11) — можно, SLES11 (mc 4.6.2) — не получается. 8-)
                      По виду эту фичу где-то в mc 4.7 прикрутили.
                        0
                        Да, я знаю это, но чтобы высветить весь список — надо или мышкой нажать или alt-h.
                          0
                          О, а про alt-h я не знал (или, скорее, забыл). Спасибо. 8-)
                      +1
                      Еще одна попытка затащить виндузятников на Linux :-)
                        0
                        Нет, скорее попытка сделать жизнь виндузятников, которые иногда пользуются линуксом, комфортнее.
                          0
                          Скорее попытка хоть как-то облагородить то что есть в линуксе.
                          +3
                          Вот именно поэтому.
                        0
                        Разве его не забросили в прошлом году?
                        +1
                        Можно немного оффтопа?
                        Вот с недавних пор PVS-Studio можно использовать бесплатно для личных проектов. Меня интересует такой аспект (описанная ситуация — гипотетическая, я пока не использовал анализатор) — вот допустим я форкнул какой-то проект на гитхаб. И для своего форка пользуюсь PVS-Studio, соответственно добавил требуемые комментарии. Всё у меня хорошо и замечательно. А потом я допустим нахожу баг в коде исходного проекта. Могу ли я отправить владельцу исходного кода пул-реквест? Ну, то есть, технически меня никто не остановит, но не будет ли по факту это нарушением вашей лицензии — ведь владелец-то никаких комментариев в код не добавлял. Или тут как у матроскина — корова государственная, а всё что она даёт — молоко или телят — это уже наше?
                          +7
                          Можете отправить пул-реквест. Если еще и в комментарии к нему укажете PVS-Studio — вообще хорошо.
                          0
                          А не планировали ввести упрощенные пороговые критерии по результатам проверки? Типа «Очень хорошо», «Так себе» и «Говнокод»?
                            +3
                            Критерии и так понятны, вот чего действительно не хватает:

                            «Пометить как ложное срабатывание говнокод»
                              +5
                              Кстати, у нас же есть в контекстном меню «Add TODO comment selected messages». Можно в настройках указать, что-бы вставлялось: "// Это говнокод, переделывай!". :)
                            +5
                            Люблю фар, не могу без него жить) Первое, что я поставил на мак, когда в 2010 году мигрировал на него с винды — это фар. По сей день им пользуюсь. Один нюанс — работает он под wine'ом.
                            Новость о том, что кто-то взялся за портирование под *nix взволновала.
                            Если удастся добиться стабильной работы, сложно ли будет сделать то же самое для macOS?
                              +4
                              Он уже компилится под macos, осталось только решить проблему скорости рендера текста, но уже можно пользоваться.
                                +1
                                это прекрасная новость.
                                как можно подписаться на вас, чтобы следить за развитием событий?
                                  +1
                                  Ну можете на гитхабе, и на автора и на меня. Конкретно по скорости рендера обсуждение сейчас идет в этом issue.
                                  0

                                  Если бы он ещё консольным был… Кстати, раз уж он на Linux, стоит прикрутить прокрутку буффера в консоли, как это сделано в ConEmu+Far Plugin. Про F3/F4 вкурсе. Не удобно.

                                +1

                                А никто не подскажет, почему под Linux стали портировать вторую версию, ведь давно уже третья в ходу?

                                  0
                                  Вот тут от автора пояснения http://forum.farmanager.com/viewtopic.php?p=139821#p139821
                                    0
                                    Мда. Судя по тому что там написано, этот порт теряет главное (лично для меня) достоинство far'а — консольность. То есть работать это будет только под иксами, а значит (опять же, лично для меня) полностью бесполезно. Или это устаревший пост и сейчас что-то поменялось?
                                      0
                                      Поменялось, консольная версия планируется.
                                        0

                                        Ох, удачи вам на этом пути. Искренне! Буду ждать, как минимум, из-за "Рисование линий" :-D

                                +2
                                Far для меня это всегда первая устанавливаемая программа, если теперь это заработает под Linux, то препядствий для перехода на Линукс станет на 1 меньше, спасибо вам за вклад.
                                  0
                                  Почитав дальше понял что это не тру консольная версия а графическая, что конечно огорчило.
                                    0
                                    Консольная версия будет.
                                  +2

                                  Раз пошла такая пьянка, попрошу проверить еще один файловый менеджер, благодаря которому переход на Линукс существенно облегчился.
                                  http://doublecmd.sourceforge.net/

                                    +1

                                    Он написан на FreePascal/Lazarus, с целью совместимости с плагинами к Total Commander под Windows. PVS-Studio его проверить не сможет

                                      0
                                      А вот жаль, что для Паскаля нет хорошего анализатора. Причина понятна, но все же печально.
                                        –1

                                        Программисты на Паскале смотрят на ряд диагностик PVS-Studio с ухмылкой: то что в C/C++ требует статических анализаторов, в Паскале просто не пропускается компилятором. К примеру, случайное присваивание в условии, отсутствие скобок в низкоприоритетной логической операции и другое.

                                          0
                                          Ну, логические ошибки типа условий которые всегда верны, либо сравнения с самим собой, либо несколько одинаковых условий подряд — компилятор не отловит. Так что статический анализатор таки полезен и для паскаля. Но да, возможностей выстрелить себе в ногу там таки поменьше.
                                    0
                                    А ffmpeg не проверяли? Очень хороший инструмент, интересно насколько хорош изнутри
                                    –3

                                    Ну когда уже портируют Тотал Комманднер под *nix? Он же Фару (при всем уважении) во многом фору даст. На андроиде уже есть.
                                    Пусть бы даже денег стоил сколько-то.

                                      0
                                      Far на Андроиде тоже есть. А самая главная фича Far'а — это его консольность.
                                        +1
                                        Total Commander под Windows и под Android — ну, имхо, это как десктопный фотошоп и мобильный.
                                          +3
                                          Давайте называть вещи своими именами. Не «во многом фору даст» а «лично мне больше нравится». Потому что, например, для меня — тотал коммандер неудобен и практически бесполезен, а фаром при этом пользуюсь активно.
                                          А если вам так нужен аналог тотала на линуксе — то вон выше ссылку давали на double commander — это бесплатный аналг тотала, совместимый с плагинами от него (чем и интересен, имхо).
                                          +3
                                          Уряяяя!!! Far на Line!
                                          Под Ubuntu За пару минут завелся:
                                          sudo apt-get install gawk m4 libglib2.0-dev libwxgtk3.0-dev cmake g++
                                          git clone https://github.com/elfmz/far2l
                                          mkdir build
                                          cd build
                                          cmake -DCMAKE_BUILD_TYPE=Release ../far2l
                                          sudo make -j4
                                          ls
                                          cd install/
                                          ls
                                          ./far2l
                                          

                                          ВАУ!
                                            0
                                            Сделал пакеты для i386 и amd64, чтобы ставить по фен шую.
                                            Проверено на Mint 18.1 и Ubuntu 16.04.2.

                                          Only users with full accounts can post comments. Log in, please.