1. Си / Говнокод #4236

    +127

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    UINT32 GetHostName(char *hostName, UINT32 hostNameBufSize)
    {
            if (hostName == NULL ){
                    OSALTRACE(OSAL_ERROR, ("Error: Input parameter hostName(null)."));
                    return -1;
            }
    
            FILE *fp = NULL;
            static char buffer[512];
            char tag[64];
    
            // hope this size will be OK for one line to read from the fileOB
            char line[1000];
            char *linep=line;
    
            int buffSize = sizeof(buffer);
    
            int found = 0;
    
    
            fp = fopen("/etc/resolv.conf", "r");
    
            if ( fp == NULL)
            {
    
                    OSALTRACE(OSAL_ERROR, ("failed to open resolver config file."));
                    return -1;
            }
    
            while ( ((*linep=getc(fp)) != EOF) && !found )
            {
                    if (*linep++ == '\n')
                    {
                            *linep = '\0';
                            sscanf(line, "%s %s", tag, buffer);
                            if (tag[0] && (!strcmp(tag, "search") || !strcmp(tag, "domain") ) ) {
                                    found = 1;
                                    break;
                            }
                            linep = line;
                    }
            }
    
            fclose(fp);
    
            if ( found )
            {
                    strcpy(hostName,buffer);
                    OSALTRACE(OSAL_DEBUG, ("DHCP domain is  %s.", buffer));
            }
            else
            {
                    OSALTRACE(OSAL_ERROR, ("Could not find dhcp domain in resolv.conf."));
                    return -1;
            }
    
            return !found;
    }

    Intel WiMAX Network Service, не какая-то пионерская поделка...

    Запостил: raorn, 13 Сентября 2010

    Комментарии (60) RSS

    • Хм... Как правило говнокод можно обяснить некоей извращенной логикой. Эта логика, пусть даже извращенная, диктует определенное постоянство стиля написания кода. Т.е. одни и те же кривые приемы исползуются везде.

      Но чем объяснить вот это: буфер `line` объявлен автоматическим, в то время как буфер `buffer` - статическим. Зачем? Почему? Какой логикой, пусть даже и извращенной, можно руководствоваться при принятии такого решения?

      Я бы еще мог уловить какой-то намек на логику, если бы массив большего размера был объявлен статически - мол, негоже большие буфера в стеке выделять. Но тут-то наоборот!

      Могу лишь предположить, что писало два разных человека... В пользу этого предроложения также свидетельствует манера по разному оформлять условия в `if`.
      Ответить
      • По-моему называть стековые переменные "автоматическими" -- извращение.
        А так, возможно не два разных человека, а один человек который спиздил код другого человек(а/ов).
        Но все равно, не думаю, что из-за использования статика это становится говнокодом.
        Ответить
        • Если уж не "автоматические", то тогда "локальные". А "стековые" - это пионерство какое-то.
          Ответить
          • С локальными согласен, но
            > негоже большие буфера в стеке
            Ответить
            • "В стеке" - это детали реализации. С точки зрения чистого языка невозможно объяснить, почему нехорошо объявлять гигантские локальные объекты. Приходится привлекать детали реализации, типа "в стеке" и т.п.

              А вот когда мы говорим о типах памяти (статическая, автоматическая, динамическая), лезть в детали реализаци совсем не обязательно.
              Ответить
              • Я видимо дико туплю с утра. То есть, если мы говорим о реализации, то можно говорить "в стеке", а если о способе объявления переменных, то пионерство?
                Ответить
    • Что же касается явных ошибок: функция `getc` в библиотеке языка C возвращает `int` (а не `char`) неспроста, т.е. значение EOF не обязано быть представимым в рамках типа `char` (например, `char` может быть беззнаковым, а `EOF` в то же время - отрицательным). По этой причине читать значение `getc` в `char` и потом сравнивать его с `EOF` - хрестоматийный пример говнокода.

      Умиляет еще проверка условия `!found` в условии повторения цикла, в то время как при установке `found` в 1 сразу делается `break` :) Опять же наводит на мысль, что писали два человека - один сторонник "структурного программирования", другой - явных выходов из цикла по `break`.
      Ответить
      • Ты писатель?
        Ответить
      • В C же нет ансигнед, да?
        Ответить
        • Чего в С нет? Unsigned? Как это нет? Есть, разумеется.
          Ответить
          • Хм. По-твоему char и unsigned char это одинаковые типы?
            Ответить
            • Нет. Это разные типы.

              В языке С есть три типа группы `char`: `signed char`, `char` и `unsigned char`. Все эти типы являются самостоятельными отдельными типами. (В отличие, например, от `int`, где `signed int` и `int` - один и тот же тип).

              Так вот, тип `char` является самостоятельным типом, который по внутреннему предствлению совпадает либо с `signed char`, либо с `unsigned char`. С каким конкретно - определяется реализацией. Т.е. с точки зрения языка тип `char` может быть беззнаковым. (При этом он по-прежнему остается отдельным типом, отличным от `unsigned char`.)
              Ответить
              • По твоей логике мы должны всегда писать signed char вместо char, если имеем ввиду знаковый? Это пиздец по-моему. Я, видимо, не застал уже таких "внутренних представлений".
                Ответить
                • Если нам строго нужен именно знаковый `char`, то да - надо писать именно `signed char`. Ибо просто `char` может оказаться беззнаковым. Разумеется, если ты уверен, что в используемой реализации просто `char` - знаковый, то можно писать просто `char`. Но надо иметь в виду, что потом, при переходе к платформе с беззнаковым `char` могут возникнуть проблемы.

                  Что имеется в виду под "не застал" мне не ясно. Такими свойствами тип `char` обладает как в современном С, так и в современном С++.
                  Ответить
                  • Назови мне конкретный случай, когда char становится беззнаковым.
                    Ответить
                    • Что значит "конкретный случай"? Знаковость `char` определяется реализацией. На практике это обычно выливается в то, что у компилятора есть опция: сделать `char` знаковым или беззнаковым. Возьми к примеру GCC или MSVC - оба компилятора имеют такую опцию. Выставляем беззнаковый режим - и готов твой "конкретный случай".

                      Наличия такой опции, разумеется, никто не гарантирует. В каком-нибудь другом компиляторе `char` может быть жестко прошит как всегда знаковый. Или как всегда беззнаковый.
                      Ответить
                      • Так вот и я говорил про то, что не видел реализации, где char есть unsigned по умолчанию. А опции -- это опции, а не реализация. Я могу для любого проекта такие опции поставить, что он не скомпилится, однако это же не означает, что это говнокод? Любой проект всегда затачивается под определенные условия.
                        Ответить
                        • У языков С и С++ есть спецификация - стандарт ANSI (стандарт ISO и т.п.) Так вот в этой спецификации черным по белому написано, что `char`, `signed char` и `unsigned char` - это три разных типа, и что знаковость `char` зависит от реализации. Вот по этой причине реализациям и разрешается делать такую опцию (или не делать, а жестко прошивать знаковость `char`). Т.е. эта опция существует только потому, что стандарт языка явно разрешает ее существование. Это относится и ко всем другим опциям компиляции. Портабельно и грамотно написанный код будет работать нормально независимо от этих опций (за редким исключением). Так что твое утверждение о том, что можно поставить такие опции, что любой код не скомпилится, говорит лишь о грязно написанном коде. Грамотно написанный код - скомпилится, никуда не денется, и работать будет нормально.

                          Это во-первых. А во-вторых, ты упускаешь из виду тот факт, что вышенаписанный код является говнокодом потому, что будет работать криво к любом случае: что со знаковым `char`, что с беззнаковым. Пусть даже `char` у нас знаковый. И пусть во входном потоке встретился байт со значением 255. На традиционной машине байт 255 силой засунутый в знаковый `char` превратится в значение `-1`. А константа `EOF` во многих реализациях определена именно как `-1`. В результате вышеприведенная программа, наткнувшись на 255 в файле, решит что файл закончился, когда на самом деле он не заканчивался.

                          В данном случае байта 255 в файле, наверное, встретиться не должно, но я лишь иллюстрирую общую проблему. Возврат функции `getc` надо читать в переменную типа `int`, сравнивать с `EOF` именно как `int`, и только потом пихать в `char`. Это, вообще то, классика C FAQ.
                          Ответить
                          • Все равно не убедил.
                            Код написан под определенные цели -> 255 в тексте не встретится. Хотя, даже если найдется идиот, который запихнет это в текстовый файл, пусть программа решит, что файл закончился -- она должна проверить валидность считанного.
                            Код будет скомпилирован автором -> он сам решит знаковый char или нет (думаю, догадается выбрать верный вариант).
                            Ты давно видел компилятор соответствующий стандарту?
                            К тому же, это очень хуевый стандарт, раз говорит "делайте как хотите". И все равно, даже с такими раскладами большинство приложений компилится и работает нормально, пусть даже благодаря неофициальным, но общепринятым правилам.
                            Дело, в общем, Ваше. Я всё равно не буду использовать getc, когда можно прочитать raw-данные в тип, который мне нужен, а не зависимый от положения звезд. Тогда я смогу учесть все выебоны компилятора, в т.ч. и знаковость.
                            Ну, и еще один аспект: я не C-программист))
                            Ответить
                            • 255 и -1 - это лишь частный пример. Можно зайти с другого конца: `EOF` может имет значение `-400` или любое другое значение типа `int`. При попытке пихать эти значения в `char` может получаться что угодно. В результате получится либо что файл никогда не кончится, либо что он будет "кончаться" на некотором невинном символе: на букве `a` например.

                              В мире С практически все компиляторы соответствуют стандарту (хотя бы стандарту C89/90). Да и в С++ с этим сейчас дела обстоят очень неплохо.
                              Ответить
                              • показать все, что скрытоЙопт, а с чего int станет единицей размера? Не, некорректный вопрос. Зачем кому-то может понадобится делать индикатором конца файла константу, большую, чем минимальная единица?
                                Конечно, по-хорошему этой константы вообще, наверно, быть не должно. А для простого текста и 0 сканает. Хотя звучит бредово. Мозг замерзает. Пальцы обледенели. Сопли стекают по клаве на пол. Апокалипсис бля какой-то.

                                А о компиляторах. Имхо, если почти все компиляторы абсолютно соответствуют стандарту, то это слишком расплывчатый стандарт. Если бы он регламентировал всё четко, то почти все компиляторы бы стремились к соблюдению. Однако, с каждым продуктом, строящим воркэраунды к воркэраундам, шансов конкретизировать эту всю шнягу становится все меньше. Самый простой пример -- винапи. Это пиздец, хаос и каша. И чем больше там сказано "может зависеть от реализации" тем больше непоняток будет. Но это всё имхо. Эх, лирика.
                                Ответить
                                • `EOF` - это специальное значение, возвражаемое `getc` в случае достижения корнца файла. Это не некий "байт" прочитанный из файла. Никакого "байта" обозначающего конец файла в самом файле нет (в бинарном режиме как минимум).

                                  По этой простой причине индикатор конца файла надо делать таковым, чтобы он не совпадал ни с одним значением прочитанным из файла. Чтобы ты мог отличить, что же ты получил от `getc` - что-то только что прочитанное из файла, или `EOF`.

                                  Если у тебя платформа с безззнаковым `char` (0-225), то `EOF = -1` - хорошая идея. Если же у тебя платформа со знаковым `char` (-128 - + 127), то `EOF = -1` - плохая идея, ибо невозможно будет отличить честно прочитанный из файла `-1` от `EOF`. В такой ситуации надо брать `EOF` за пределами диапазона -128 - + 127. Хоть `-400`, хоть `+328`, но не `-1`. Именно для того функции `getc` сделали тип возврата `int`, а не `char`. Я уже черт знает сколько это твержу.
                                  Ответить
                                  • Я заебался повторять, что речь идет о текстовом файле, а не о бинарном.
                                    Я заебался повторять, что речь идет о конкретном коде.
                                    Я прекрасно понимаю, в чем разница между знаковыми и беззнаковыми и как они конвертятся. Я знаю, что "байта конца файла" нет. Однако я также на 95% уверен, каких кодов символов не будет в "/etc/resolv.conf".
                                    Мы обсуждали конкретный случай, текстовый файл, конкретную уебанскую функцию getc, которую я не юзал и не буду. Он блять не бинарный. И знАком по умолчанию, как ты писал выше, управляет тот, кто компилирует.
                                    Ты меня не слушаешь -> ничему не научишь. А я тебя тем более. Ветка в тупике половину времени её существования.
                                    Ответить
                                    • >Ты меня не слушаешь -> ничему не научишь.
                                      "дать возможность пользователям получать советы, а не только быть обкладеными говнокоментами к накладенному ими говнокодом" ещё пока только ЗАПЛАНИРОВАНО.
                                      http://govnokod.reformal.ru/proj/?ia=27365
                                      Ответить
                                      • Видел это, и не претендую на вход в этот раздел) Просто иначе мне нет смысла говорить на эту не интересную тему)
                                        Ответить
                                    • Это не важно, бинарный он или не бинарный. На POSIX системах, например, вообще все файлы формально бинарные.

                                      Чтобы этот код "работал", необходимо соблюдение следующего условия: входной файл некогда не будет содержать байтового значения, которое при приведении к типу `char` данной платформы даст величину `EOF` данной платформы. В этом условии фигурируют три "переменных": содержимое входного файла, знаковость `char` и фактическое значение `EOF`.

                                      Ты будешь утверждать, что в данном случае все эти три переменных сложатся так, что "все будет хорошо"? Прекрасно, пусть сложатся. Я охотно верю, что такое сочетание обстоятельств вполне возможно. Тем не менее завязка в коде на подобные сочетания всегда было и будет для меня классическим говнокодом. Тем более что написать правильно (т.е. портабельно, без завязок на какие-то посторонние обстоятельства) - легко и просто.
                                      Ответить
                                      • Я сейчас скажу и заткнусь (аутотренинг).
                                        Все файлы бинарные. На POSIX и non-POSIX. Для них не существует константы EOF. Вообще. Однако, если ты пишешь код, предполагая, что этот поток байт является текстовыми данными в однобайтной кодировке, то для такой интерпретации данных есть как минимум одно значение байта, не являющегося валидными текстовыми данными. Если ты читаешь данные как бинарные, то ты должен явно указать, куда ты их читаешь, и по-моему это должен быть unsigned char. Явно указать.
                                        Я говорю, что использовать EOF нельзя, ты говоришь, что использовать EOF нельзя, не заметил? Пиздос, спорим.
                                        Ответить
                                        • Я не понимаю вышесказанного. Я говорю, что использовать `EOF` нельзя? Это где это?

                                          Во-первых, константа `EOF` есть всегда, что для POSIX, что для не-POSIX. Константе `EOF` плевать на POSIX. Константа `EOF` есть специальный код возврата функции `getc` (и аналогичных), означающий, что файл закончился. Никакой прямой связи с содержимым байтов в файле этот код возврата не имеет. Бинарный это файл или не бинарный, есть ли в этом файле спец. символ конца файла или нет и чему он равен - никакой роли не играет и на значение `EOF` никак не влияет. Любой файл рано или поздно заканчивается - это факт, и когда он заканчивается, функция `getc` возвращает `EOF`. Вот и все.

                                          (Например, `EOF` как правило практически во всех реализациях равен просто `-1`, в то время как символ конца текстового файла на Windows-системах - 26 (0x1A). Улавливаешь связь, точнее полное ее отсутствие?)

                                          Во-вторых, я никогда не говорил, что использовать `EOF` нельзя. Это фигня какая-то. Использовать `EOF` можно и нужно. Обязательно. Без вариантов. Вот чего делать нельзя, так это сразу безусловно пихать результат `getc` в переменную типа `char`. Потому что тем самым мы потенциально уничтожаем значение `EOF` и делаем его неотличимым от обычного байта прочитанного из файла. Всегда сначала надо прверить результат на `EOF` (еще в типе `int`), и только потом пихать его в `char`.

                                          Это все, что я хотел сказать.

                                          Опять же, в этом примере кода может быть "все будет работать". Но это не повод писать такой код, тем более что нормальный корректный вариант будет всего на 10-15 символов длиннее.
                                          Ответить
                                          • >Любой файл рано или поздно заканчивается
                                            Бывают нюансы, а-ля /dev/zero.
                                            Ответить
                                            • /dev/null в смысле? Я думаю, это пайп, а не файл. Даже если я ошибаюсь, это не имелось ввиду.
                                              Ответить
                                          • Также не стоит забывать, что независимо от знаковости `char` на данной системе, функция `getc` всегда ведет себя так, как будто читает из файла значения именно `unsigned char`. Т.е. пока чтение идет успешно, гарантируется, что возврат `getc` неотрицателен (0-255 на традиционных платформах). Т.е. даже если чтение идет успешно (никакого EOF), все равно пихать такие значения сразу напрямую в `char`, который как правило знаковый (т.е. -128 - +127) - тоже практика весьма сомнительная.
                                            Ответить
                                            • >гарантируется, что возврат `getc` неотрицателен
                                              Докажи, пожалуйста.
                                              Ответить
                                              • Это черным по белому сказано в спецификации `fgetc` в стандарте языка. В моем переводе: "Функция fgetc читает следующий символ как unsigned char сконвертированный в int и сдвигает указатель текущей позиции в потоке" (см. раздел 7.19.7.1). Ключевой момент - символы читаются как `unsigned char`, т.е. они всегда положительны.

                                                Функция `getc` эквивалентна функции `fgetc`, как сказано в той же спецификации (идея в том, что `getc` может быть макросом, а `fgetc` - всегда функция).

                                                P.S. Педантично настроенный оппонент может заметить, что на какой-нибудь экзотической платформе с sizeof(unsigned char) == sizeof(int) вполне возможно, что функция `fgetc` будет возвращать отрцательные значения даже при успешном чтении. Это так, но я не хочу переусложнять обсуждение.
                                                Ответить
                                                • Ок.

                                                  ЗЫ: Представляю себе эту платформу. (sizeof(char) <= sizeof(short) <= sizeof(int)) и все это =1.
                                                  Ответить
                        • Вот она, эта классика

                          http://c-faq.com/stdio/getcharc.html
                          Ответить
      • Сорри, оффтоп. Меня не было на выходных. Вопрос к тебе: в WinAPI довольно большая часть функций возвращает 0 в случае успешного выполнения, в Xlib есть что-то типа typedef int Status; и #define Success 0, которые возвращает очень большое количество функций. Как ты на это смотришь? Что об этом думаешь?
        Ответить
        • (Это скорее к предыдущей дискуссии?)

          Пусть возвращают. Но я лично использование таких функций в условных выражениях как `if (!some_function(...))` для определения успеха буду называть индусокодом. Правильно: `if (some_function(...) == Success)` - и становится соврешенно не важно, что за значение они там используют для обозначения успеха.
          Ответить
          • Понял, про что ты. Однако, индусокод, по-простому -- это неоправданно раздутый код. Не совсем правильно его так называть. Ну да ладно, я тебя понял.
            Ответить
        • > в WinAPI довольно большая часть функций возвращает 0
          вообще-то бОльшая часть возвращает NULL как признак облома
          Ответить
    • Помимо всего уже сказанного, обратил внимание ещё вот на что:
      static char buffer[512];
      int buffSize = sizeof(buffer);

      Я бы назвал это "обратное именование величины". Логичнее было бы сделать так:
      const int buffSize = 512;
      static char buffer[buffSize];

      ну, или вариант с #define если const для Си не прокатит.
      С другой стороны, buffSize вообще дальше в коде не используется.

      Ещё последние ветвления:
      if ( found )
      {
         ...
      }
      else
      {
        return -1;
      }
      return !found;

      Если уж возвращаемый тип UINT32, то и возвращать надо целое.
      А тут какая-то мешанина из -1 и !found. В имеющемся раскладе функция возвращает либо 0, либо -1.
      Третьего не дано.
      P.S. код действительно похож на сборную солянку. Тыренные куски из разных мест собраны в одной функции.
      Ответить
      • С размером действительно.
        Кстати, возможно, возврат 0 при хорошем исходе по той же логике, что и strcmp() и подобные. Стайл увиден и повторяем.
        А еще, кстати, учитываем три формы вывода ошибки и смешанные пробелы вокруг скобок if и выдаем вердикт: тру солянко.
        Стремно видеть это у интела, но до говнокода не дотягивает все таки.. Мелковаты наши претензии.
        Ответить
        • Относительно Intel WiMAX Network Service.
          Хотелось бы убедиться насколько это является истиной.
          А говно бывает разное. От сформированного до поноса с кровью.
          Ответить
          • Что является истиной? Что это действительно Intel WiMAX Network Service?

            Да пожалуйста - http://git.kernel.org/?p=linux/networking/wimax/wimax-network-service.git;a=blob;f=InfraStack/OSDependent/Linux/OSAL/Services/wimax_osal_services.c#l421
            Ответить
            • Мда... есть на что посмотреть:
              return 0;       // Whats the convention??
              UINT32 Obfuscate_or_not = 0;    // allow obfuscation by default
              UINT32 renew_IP_or_not = 1;     // allow renewing by default
               if ( !strcasecmp(ip_str, "0.0.0.0") )

              И ещё разнообразные "дефулты" :)
              #define         OSAL_DEFUALT_STR_LEN    1024            // temporary (will go away)
               char interface_name[DEFULT_STR_SIZE];
              Ответить
        • во1 strcmp() реализует тернарную логику ( > 0, == 0, < 0). такое поведение в корне отличается от (== 0 -> не было ошибок, != 0 -> были ошибки)

          во2 не знаю "дотягивает" ли до говнокода сия поделка, но три ляпа налицо:
          1) авторов не беспокоет, что char tag[] может переполнится
          2) вместо выражения надежды что char line[] не переполнится, почему бы не запихнуть linep - line < 1000 в while()?
          3) вообще-то еще hostName может переполнится если strlen(buffer) > hostNameBufSize
          Ответить
      • sizeof(buffer) - константа времени компиляции
        const int buffSize - константа выполнения

        потом -1 - тоже целое, и для uint32_t будет равно 0xffffffff

        далее, функция может вернуть одно из трех: 0, !0, 0xffffffff
        Ответить
        • >const int buffSize - константа выполнения
          Кто сказал ?
          >функция может вернуть одно из трех: 0, !0, 0xffffffff
          !0 - в каком месте ?
          Ответить
          • gcc 3.4.2 сказал

            не заметил return в else. функция не может вернуть !0. мой косяк
            Ответить
            • Если значение константы известно на момент компиляции она не будет константой времени выполнения.
              Могут быть всякие нюансы относительно взятия адреса от такой константы, и настроек компилятора на этот счет.
              Но в большинстве случаев const int buffSize = 512 будет именно константой времени компиляции.
              Ответить
              • Только в С++.

                В С `const int` - это не константа вообще никогда. Термин "константа" в С обозначает только литеральные константы - 5, 10, 2.5, 0xFF - и т.п. Поэтому использовать `const int` объект для обозначания размера массива в С не получится.

                В С99 ввели Variable Length Arrays и разрешили использовать неконстантный размер для объявления локальных массивов (GCC разрешал это и раньше в качестве нестандартного расширения). Но в любом случае, если массив не локальный, то такой номер не пройдет.
                Ответить
                • >`const int` объект для обозначания размера массива в С не получится.
                  Именно поэтому было сказано "ну, или вариант с #define если const для Си не прокатит."
                  Насчет С/С++ согласен.
                  Ответить
              • это не так
                Ответить
        • В языке С++ чем будет `const int buffSize` зависит от того, чем оно проинициализировано. `const int buffSize = 512` - это константа времени компиляции. А `const int buffSize = rand()` - константа времени выполнения.

          В язке С `const int buffSize` - это вообще никогда не константа, а условно выражаясь "константная переменная". В классическом С (C89/90) такой `buffSize` не может быть использован для задания размера массива. В C99 такой `buffSize` может быть использован только для задания размера локального массива (и получится в результате так называемый Variable Length Array). Но вот для задания размера `static` массива такой `buffSize` не может быть использован даже в С99. Поэтому в С для этих целей используется #define-константы.
          Ответить
      • `return !found` для возврата 0 и обозначения тем самым успеха поиска - это, конечно, сказка. Чем то похоже на мое любимое `if (!strcmp(...`.
        Ответить

    Добавить комментарий