Untitled

НикнеймzHz (а также zHz00, zHz01 и так далее)
О чём пишубыт, аниме, программирование, компьютерная техника
(полный список тегов -- что теги обозначают)
Интересненькоесписок моих статей с разбивкой по темам
Где меня ещё читать Telegram
Как со мной связатьсяTelegram, e-mail, Jabber: [email protected],
Discord: zHz#1243
Как дать мне денегBitcoin

Что тут можно и нельзя (читать правила полностью):
1. Комментировать можно всем.
2. Читать всё можно всем.
3. Раскрывать чьи-либо личные данные нельзя.
4. Нарушать правила @дневников и законы РФ нельзя.
5. Если в записи есть ссылки "<<" или ">>" -- то эти ссылки ведут на связанные записи в цепочке.
6. Если навести мышку на зелёный текст, будет сюрприз.
URL
Записи с темой: Программирование (83)
вторник, 20 мая 2025
02:13 Муляж
Когда заходишь на сайт мошенников, типа фишинга, то там всё выглядит нормально, за исключением нюанса: за декорациями ничего нет. Чем-то похоже на камин Папы Карло.

Ну и вот, у меня две программы, которые обмениваются друг с другом по определённому протоколу. В основном, данные надо отправлять от Алисы к Бобу, но иногда надо и получать. Для отправки существует функция типа SettingsSet, которая получает идентификатор "переменной" и число. Для получения есть несколько функций типа GetA, GetB, GetC, и общая, SettingsGet.

Об'единить все функции было нельзя, т.к. данные разные по смыслу. Ну и пользовался я своими А, БЭ и ЦЭ спокойно, пока не настал момент, когда мне надо было получить что-то через SettingsGet.

Я удивился, получив ноль.

Поковыряв код, я обнаружил:
а) SettingsGet никогда раньше не использовалась. Данные и так надо было получать редко, а когда было надо, я использовал А, БЭ и ЦЭ.
б) она не только не использовалась. Она ещё и не была написана. Вместо неё была заглушка.

А проекту уже лет пять.

В общем, живу я хорошо, всё стабильно. Пришлось срочно доделывать этот кусок.

@темы: Программирование, Говнокод

URL
суббота, 03 мая 2025
05:25 Продолжаю писать телеграм-бота для модерирования
Идея проста: когда новый пользователь входит в чат, ему надо сразу выдать мьют на неделю. Это очень эффективно против спаммеров, потому что спаммеры неделю ждать не будут. Их аккаунт почти всегда удаляется раньше. Если не удаляется, то они всё равно уходят из группы. Живые же люди могут написать администраторам в личку или просто подождать.

Мой астрономический чат довольно камерный, поэтому живых людей там мало, а спаммеров много. Но вот, реально пришёл живой человек. Я это определил и по имени (у спаммепров потчти всегда женское имя-фамилия) и по аватарке (космонавт) и по отсутствию премиума (у спаммеров всегда премиум, причём они себе ставят невидимый статус, чтобы было непонятно, что это премиум, но если сразу справа от ника кликнуть, получишь окно покупки премиума).

Я пошёл в административную панель и удалил пользователя из списка заблокированных. После чего немедленно получил сообщение от бота:

"Здравствуйте, zHz01! Из-за атаки ботов всем новым пользователям отключена отправка сообщений на одну неделю. Для досрочного снятия блокировки пишите администраторам чата."

Конечно же, я не был заблокирован. Администраторов может заблокировать только вышестоящий администратор, а бот был самым младшим администратором. Тем не менее такое поведение показывало на явный баг. Почему-то бот считал, что я -- новый пользователь! Стал разбираться.

(цветная врезка: на всякий случай информирую всех, что если вы администратору чата введёте ограничения типа мьюта, т.е. запрета отправки сообщений, то администратор будет автоматически снят с административного поста; не помню, выдаётся ли об этом предупреждение или нет)

Ответы телеграм-бот-апи приходят в виде жсонов. Вот какие поля есть у сообщения о входе (chat_member):
1. chat
2. from
3. date
4. old_chat_member
5. new_chat_member

У сообщения о входе в поле from -- данные вошедшего пользователя. Видя, что у меня есть сообщение о входе, я брал данные из поля from и по ним проводил блокировку пользователя. Был ещё один признак, в new_chat_member должно быть поле status, которое содержит слово member, чтобы не перепутать с сообщением о выходе и о блокировке (тогда бы там были слова left и restricted соответственно)

Какие же поля есть у сообщения о разбане? Да те же самые! В поле new_chat_member статус тоже мембер, потому что после разблокировки статус меняется с restricted на member. А какое различие? В поле from. Если пользователя разбанили, то в поле фром будет администратор, а не сам пользователь.

Вот тут-то я и попался. Полагая, что у сообщения о входе в поле фром всегда вошедший, я совершил ошибку. Я попытался заблокировать себя. Документация говорит, что это поле содержит:

Performer of the action, which resulted in the change

Увы, такое описание лапидарно, и не даёт пояснений, кто же там может быть.

Ошибку я исправил. Теперь я выдаю мьют не по полю фром. Я сравниваю идентификаторы пользователя в поле фром и в поле new_chat_member. Если идентификаторы совпадают, то пользователь вошёл сам. Если идентификаторы различаются, то это административное действие, и реагировать на него не надо.

Тем не менее, это не значит, что проблема решена полностью. Я уже не раз сталкивался с неожиданным поведением телеграм-бот-апи, поэтому просто тестирую и жду необычных ситуаций.

@темы: Программирование, Борьба с техникой, Telegram

URL
пятница, 18 апреля 2025
04:37 Типа рыбалка
Сел писать бота для борьбы со спамом в телеграм-чате. Пока тестирую.

Ну и вот, пришёл очередной спаммер, а сообщение о входе бот не получил. Почему -- вопрос. Я думал, можно воспользоваться списком пользователей чата, или списком Recent Actions. Но ОКАЗЫВАЕТСЯ, телеграм-бот не может получить доступ ни к тому, ни к другому! Чтобы бот мог получить доступ к таким вещам, он должен работать от лица телеграм-аккаунта, и вместо АПИ бота использовать MTProto. Но это совсем другая история.

Ну и вот, я догуглился до того, что я сообщение о входе не получил из-за ошибки в запросе. Переделал запрос.

Сижу, жду. Интересно, много ли людей настолько жаждут получить спам, насколько этого сейчас хочу я?

@темы: Программирование, Восприятие, Борьба с техникой

URL
четверг, 17 апреля 2025
04:52 Фишинг в POS-терминале
Может быть, это какая-нибудь известная задача, решение которой все давно знают. Или хотя бы название которой все давно знают. Но не знаю я. Итак, вот какая проблема меня беспокоит.

Пусть: есть POS-терминал, на котором крутится какое-либо ПО. Оно показывает интерфейс. Есть исходники этого ПО, опубликованные, скажем, на гитхабе. Если ПО компилируемое, то там же есть бинарники с проверяемой сборкой (доверенный компилятор, выдающий побайтово один и тот же код).

Требуется: чтобы любой подошедший к терминалу мог надёжно убедиться, что на терминале крутится именно то ПО, которое находится на гитхабе. При этом у пользователя есть доступ только к интерфейсу ПО. Он не может посмотреть файлы и т.п.

Такая проверка направлена в первую очередь не против мошенников, которые подменили терминал, а против владельцев самого терминала, которые внесли в ПО правки, либо полностью его заменили.

Естественно, выдача любого фиксированного сообщения на терминал не подходит. Допустим, выводить контрольную сумму ПО смысла нет. Я могу выдачу такого сообщения просто включить в правки, чтобы всегда выдавало то, что надо (т.е. ту сумму, что есть на гитхабе). Значит, требуется выдача какого-то динамического кода по типу TOTP.

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

Можно привлечь доверенного посредника, но: а) с какой стати ему доверять б) а что посредник сможет сделать? Любое общение по сети может быть подделано так, будто запросы посылает настоящая программа. Можно даже держать её запущенной параллельно, чтобы она выдавала правильные запросы. Если же посредник будет крутиться на той же машине, то это даёт больше возможностей, но его тоже можно заменить.

Если даже метод проверки найдётся, можно будет фальшивую программу запускать поверх настоящей, перекрывая её интерфейс, а проверочный код показывать от настоящей через прозрачную дырку в интерфейсе фальшивой программы. Все кнопки перенаправлять на фальшивую.

В общем, не уверен я, что у этой задачи есть решение.

@темы: Программирование, Мысли

URL
пятница, 28 марта 2025
03:48 Всегда добавляйте default
В Си/Си++ оператор switch позволяет выполнить одно из N действий. Какое действие вызывается -- зависит от входного параметра. Я не буду показывать синтаксис, показываю только принцип:

Например, если тип измерения равен 1, то делать А.
Есть равен 2, то делать Б.
Если равен 3, то делать Ц.

Ну и есть волшебная опция default, которая срабатывает тогда, когда не срабатывают предыдущие.

Бывает, что по смыслу у вас особенным образом обрабатываются несколько значений, а для всех остальных подходит "общее". Тогда каждый дурак поставит default и напишет в нём то, что надо. Но что, если типов измерения всего три?

Велик соблазн поставить только три кейса и на этом закончить блок. Так делать не надо. Я много раз сталкивался с тем, что рано или поздно добавляется новый тип измерения, и надо перебрать все свитчи, чтобы они правильно срабатывали. Легко что-нибудь упустить. Кроме того, если значение параметра окажется ошибочным, программа пропустит свитч "молча", а последствия придётся долго анализировать, чтобы выяснить, где косяк.

Чтобы ошибка вылезала сразу, я стал принудительно добавлять во все свитчи секцию default. И в случаях, описанных выше, попадание в эту секцию как раз означает ошибочное значение параметра!

У меня есть специальный макрос, который я сую в default-секцию. Он выводит сообщение в лог и на экран. А сообщение содержит не просто предупреждение, а номер строки и имя файла, откуда сообщение выскакивает. Это не надо прописывать вручную, потому что макросы __FILE__ и __LINE__ сразу подставляют правильные номера, даже если эти макросы расположены в другом макросе.

Такая система позволяет не только узнать, что где-то неправильно работает свитч. В сообщении сразу указывается, ГДЕ это происходит. Я не могу сосчитать, сколько раз меня спасала такая система.

@темы: Программирование, Лайфхак

URL
пятница, 14 марта 2025
05:45 Шок! Сумма меняется от перемены мест слагаемых!
Да, причину вчерашнего бага я отыскал, но в двух словах это не об'яснить. Тут надо статью писать. Может быть, как-нибудь в другой раз.

Вместо этого расскажу то, что рассказали мне о суммировании чисел. Как известно, точность в компьютере ограничена. Если мы складывает числа примерно одного порядка, то всё ок. Если же числа отличаются на десять порядков, то маленькие числа начинают теряться на фоне больших. То есть:

1e10+1 будет примерно равно 1e10.

Если использовать более точный тип (64 бита вместо 32, или даже ещё больше), то будет доступен больший разброс диапазонов, но ограничения всё равно будут.

Если мы суммируем большой массив, и числа в нём самые разнообразные, то существует риск потерять точность при суммировании из-за явления, которое я указал выше. Если маленьких чисел мало, то пёс с ним. А если у нас большинство чисел маленьких, но есть несколько больших? Потеря точности будет уже более значительной.

И вот какой тут есть совет. Массив надо отсортировать по возрастанию. Тогда в том случае, если маленьких чисел много, они при суммировании станут больше, и уже будут заметнее при сложении с большим числом. Если же суммировать в обратном порядке, то все значения после больших будут просто потеряны.

@темы: Программирование, Лайфхак

URL
пятница, 10 января 2025
05:29 Kittens Game Buildings Calculator
Я уже не раз писал про curses, но зачем вообще мне понадобилась эта библиотека? Для нетерпеливых сразу ссылка: github.com/zHz00/KGBC

Я играю в Kittens Game, там у меня цивилизация котят, у них разные фабрики-заводы-газеты-параходы, и всё это надо строить. При этом следующая постройка стоит на 15% больше предыдущей. А иногда не на 15%. Есть ещё один нюанс: некоторые ресурсы, из которых собирают постройки, имеют "ёмкость". Допустим, уран имеет ёмкость 10000. Если следующий ускоритель стоит 11000, то хоть ты тресни, ты его не построишь, потому что не сможешь накопить 11000 урана. Сначала надо поднять ёмкость урана на 1000, для чего надо построить новый реактор. Он не требует урана, но он требует титана. И стоит на 15% больше предыдущего реактора. Есть ли у меня достаточная ёмкость титана, чтобы построить следующий реактор? Вопрос.

(читать дальше)

@темы: Игры, Статьи, Программирование, Программы

URL
среда, 08 января 2025
07:41 Ещё два прикола при работе с терминалом
Вообще, это надо в пост про curses, и я туда добавлю, но сейчас там никто не прочитает.

В винде мы привыкли, что можно определить любое сочетание клавиш. В графическом режиме линукса, по-видимому, тоже. Но если мы работаем в терминале, то всё не так. Я много поел говна, пока разбирался с сочетаниями с альтом, с хоум-эндом, с задержкой после нажатия эскейпа (а в винде всё тоже самое работает без проблем, тоже в терминале).

Но когда я решил провести углублённое тестирование, то обнаружил ещё две вещи.

1. Tab невозможно отличить от Ctrl+i. Потому что они оба возвращают один код. (это поведение присутствует и в винде, как выяснилось)
2. Ctrl+M невозможно получить. Потому что когда нажимаешь Ctrl+M, в программе получаешь Ctrl+J. То же самое происходит и при нажатии Enter (поэтому различить Ctrl+J и Enter невозможно тоже).

В некоторых терминалах поведение отличается. Там нажатие энтера генерирует Ctrl+M. Тогда не получится сгенерировать Ctrl+J, поскольку его нажатие будет генерировать Ctrl+M. Такое поведение связано с различием управляющих кодов CR/LF. И если в настройках терминала стоит одно, то во всех трёх случаях будет генерироваться Ctrl+J, а если другое -- Ctrl+M.

Есть очень окольные способы настроить такое под себя. Но если я пишу программу для терминала, которая должна более-менее работать в любом линуксе, то надо отказаться от Ctrl+J, Ctrl+M, Ctrl+i, Tab, а ещё, желательно, от Ctrl+H, который легко путается с Backspace. Но вот именно настройка управляющего кода бэкспейса в эмуляторах терминалов всегда расположена на видном месте.

@темы: Программирование, Борьба с техникой

URL
понедельник, 23 декабря 2024
04:53 Во входном канале никель, в выходном -- пуговица
(никель -- монета в 5 центов)

Выводил текстовый файл при помощи Си++. Использовал класс ofstream. Неожиданно обнаружил, что файл прерывается досрочно. При этом цикл, который туда пишет, дорабатывает до конца. А в какой момент прерывается вывод?

В общем, у меня был импровизированный ассоциативный массив. Так не делайте, используйте готовый, он в Си++ есть (std::map). Но я сделал свой, с б/дж и ш. Ключ у меня был... целое число. А значение -- текстовая строка (char*, так тоже не делайте, потому что есть std::string). Для поиска по ассоциативному массиву была сделана специальная функция, возвращающая char*.

Проблема была в тех случаях, когда ключ был в массиве не найден. Моя функция возвращала NULL, а точнее, (char*)NULL. Нулевой указатель. Который с удовольствием выводился в файл.

Файловый поток такого издевательства терпеть не мог. Он выставлял сразу биты fail и bad (но не возбуждал исключение!). Я, естественно, не проверял статус выходного потока после каждого вывода. Да и не проверял вообще. А так уж поток устроен, что если ошибку не устранить, весь остальной вывод будет молча с'едаться, что и происходило.

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

@темы: Программирование, Фейлы, Говнокод, Борьба с техникой

URL
вторник, 17 декабря 2024
01:51 Длинные условия
Сколько раз такое было, пишем оператор условия, а там:

if(flag1==True&&flag2==False&&function_call(a,b,c,d)>0)

и что-нибудь ещё.

Ну, начнём с того, что наличие таких условий -- это само по себе плохой код, потому что его сложно понять. Тем не менее, иногда условия выполнения участков кода действительно бывают заковыристые. Может быть, есть какое-то системное решение для упрощения сложных условий, но я такого не знаю. Что всё-таки можно сделать?

1. Если в условиях только флаги, то надо рефакторить всё в конечный автомат -- в этом я убедился на практике. А если там кроме флагов ещё есть диапазоны значений, вызовы функций, проверки вариантов опций? Не знаю. Может быть, это всё тоже приводимо к конечному автомату.

2. Но я хотел написать про минорное упрощение. Нужно записывать условия в столбик. Я давно так делаю. Но недавно я обнаружил, что записывать в столбик можно по-разному. Можно написать:

if(flag1==True&&
flag2==False&&
function_call(a,b,c,d)>0)

А можно написать:

if(flag1==True
&&flag2==False
&&function_call(a,b,c,d)>0)

Я всегда использовал первый вариант, но убедился, что он неудобен. Логическая операция находится в конце строки, её надо постоянно искать глазами. А концы у каждой строки находятся в разном месте. Если расположить оператор в начале, то, во-первых, будет понятнее, что происходит, а во-вторых, можно для удобства дублировать оператор и в конце предыдущей строке, но уже в комментарии, типа //&& .

Но насчёт дублирования я пока не уверен, т.к. никогда так не делал.

3. Есть ещё один метод, назначить каждому условию в выражении отдельную логическую переменную.

bool b1=(flag1==True);
bool b2=(flag2==False);
bool b3=(function_call(a,b,c,d)>0);//скобки необязательны, но пусть будут на всякий случай
if(b1&&b2&&b3)

Какие есть особенности у этого метода?
Во-первых, если удастся дать условиям краткие понятные имена, а не b1, b2, b3, то это действительно упростит читаемость. Если имена будут условными, то упрощение тоже будет условным.
Во-вторых, упрощается отладка, поскольку вы получаете непосредственный доступ к частям логического выражения, а обычно такого доступа нет, т.к. условие выполняется в отладчике как одна строка.

@темы: Программирование, Говнокод

URL
вторник, 03 декабря 2024
01:44 Второй проход
Если при добавлении функционала в программу приходится вносить массовые правки, то у программы плохая архитектура. И тем не менее, массовые правки вносить иногда приходится.

Чтобы уменьшить время последующей отладки я советую вот что: после внесения всех правок и первичной проверки на компиляцию надо не сразу начинать отлаживать, а надо все исправленные места ещё раз просмотреть долгим вдумчивым взглядом.

Потому что при массовых правках легко пропустить что-нибудь по невнимательности. А поиск этих пропусков будет гораздо быстрее, если сделать его сразу.

Одна из проблем, которая может возникнуть -- это поиск всех исправленных мест. Тут есть три варианта:
1. Все места для правок могут быть уже помечены ещё до начала (например, вы исправляете поведение программы при определённом значении какой-либо опции, и сравнение с этим значением есть во всех исправленных местах).
2. Можно вручную помечать все места, куда вносятся правки, но делать это надо было заранее
3. Система контроля версий.

@темы: Программирование, Лайфхак, Говнокод

URL
пятница, 08 ноября 2024
03:57 Парные случаи в действии
Продолжение истории про копирование строки.

Начальник делал свою задачу, доделал, рассказывает: всё работает, кроме одного окна. При этом оно безобидное. Но стоит его открыть -- программа падает.

Я прошу показать его, и он открывает... то самое окно, при открытии которого происходили события предыдущего поста.

(я там не написал; вся эта канитель с пересылкой строк была связана с инициализацией элементов управления окна)

Как выяснилось, он тоже установил тот редкий флаг, но совсем по другой причине. И так удачно совпало, что мы оба наткнулись на эту многолетнюю проблему одновременно. Но я решил её на день раньше...

Он работал в другой ветке, поэтому мои изменения ещё не получил. Но мы это быстро исправили.

@темы: Программирование, Борьба с техникой

URL
среда, 06 ноября 2024
05:30 Это не Питон
(но в Питоне тоже можно обосраться похожим образом)

В коде нашёл спящую ошибку. Есть строка типа...

char *str=new char[len];

В эту строку раз за разом копируется разный текст через strcpy

strcpy_s(str,len,src);//src каждый раз разный

Потом эта строка отправляется куда-то на обработку, а после обработки в неё копируется следующая строка. Это происходит не в цикле, а методом китайского кода, т.е. строка за строкой.

Оставим в стороне вопрос о том, почему сразу не отправлять src. Тому есть причина.

Несколько копирований проходит успешно, но во время очередного я получаю access violation. Откуда он там может быть? Я тихо копирую в свою область раз за разом. Может быть, не хватает места?

Но путём отладки я обнаруживаю, что ошибка происходит при копировании в ПЕРВЫЙ байт. А когда это возможно?

Когда пытаешься копировать в const-область или типа того. Но у меня же не константная область? Точнее, не была константная ещё три строчки назад. Хммм...

Поднимаю глаза на предыдущие строки. А там примерно следующее:

if(very_rare_option)
{
str="Very rare string";
}

Много лет редкий флаг не выставлялся, поэтому строчка не выполнялась. Но я выставил этот флаг. В результате произошло присваивание. Но в Си++ строки таким образом не присваиваются. Тип правого выражения это const char *, а тип левого -- char *. Я присвоил константный указатель на строку и успешно с ним поработал. Но при попытке записи я стал записывать не по старому адресу, на который была выделена память, а по адресу &"Very rare string", который теперь содержался в str. А там запрещённая для записи область...

Естественно, пришлось переписать:

strcpy_s(str,len,"Very rare string");

И проблема исчезла.

@темы: Программирование, Фейлы, Говнокод

URL
четверг, 31 октября 2024
02:12 Библиотека curses. Краткий обзор
Если линукс-приложение имеет симпатичный интерфейс с кнопочками в текстовом режиме, то обычно считают, что это сделано при помощи curses. Но, как оказывается, пользоваться ей не так-то просто.

Если вы хотите сделать симпатичное переносимое приложение в текстовом режиме, то, возможно, curses не лучший выбор. Да, у вас будет работать и на винде, и на линуксе, и на аналогичных системах. Но какой ценой?

Во-первых, сервис от этой библиотеки довольно ограничен. У нас есть:
-- Управление позицией вывода текста на экран
-- Включение-выключение курсора
-- Цвет текста и фона
-- Псевдо-окна. Окнам можно сделать рамочку
-- Поля ввода
-- Обработка мышки (клики, скролл)

Может показаться, что это немало, но у каждого пункта есть "но". Вот примерный список проблем библиотеки:

-- Вывести текст в правый нижний угол экрана непросто (и я не уверен, что вообще возможно)
-- Во всех функциях первый аргумент -- ИГРЕК, а второй -- икс
-- Гарантированно поддерживается только 8 цветов. На многих терминалах поддерживается большее число, но рассчитывать на это нельзя. Далее, каждый терминал видит эти 8 цветов по-своему. Поэтому если вы сделали симпатичное сочетание, то при запуске другой терминальной программы вы рискуете получить нечто вырвиглазное.
-- Атрибуты типа жирный, мигание поддерживаются, но гарантий что они будут отображаться -- нет.
-- Рамочка у псевдо-окон доступна по-умолчанию только одна, одинарная. Для рисования любой другой надо вручную передать 8 символов, которые будут служить границами. Эта рамочка после рисования сразу забывается, поэтому если вы будете выводить сплошной текст, он будет затирать рамку при переносе строки.
-- Поля ввода поддерживаются, но другие контролы типа кнопок, чекбоксов -- нет. Делайте сами.
-- Скролл мышкой гарантированно работает только в одну сторону, через BUTTON4. Скролл в другую сторону генерирует сигнал, который у меня совпал на видне, на линуксе в ssh через putty и в xfce в эмуляторе терминала. Но у этого кода нету именованной константы. Я его подобрал. Это 0x200000. Как вы понимаете, нету гарантий, что это есть на всех реализациях.
-- Проблемы с передачей сочетаний с Alt и с кнопкой Esc. В винде всё работает идеально (что забавно), а в линуксе после эскейпа идёт пауза 1-2 секунды. Видимо, ждёт ввода эскейп-последовательности. Это, кажется, можно победить. Но получить через терминал готовые сочетания типа "M_a" ("ALT_A") невозможно. Вы сначала получите код 27, потом надо ещё раз вызвать getch(). Возможно, при не-удалённой работе линукса на собственном железе этих проблем не будет, пока не проверил.
-- При работе через ssh надо следить, чтобы тип эмулятора терминала с обоих сторон совпадал. Иначе у вас будут неправильно работать клавиши F1...F12. И не только.
-- При работе в графических эмуляторах терминала происходит перехват части нажатий в пользу менюшек. Это можно отключить, но это геморрой.
-- А заставить в линуксе правильно обрабатывать Home, End пока вообще не вышло. Приходит по 3-4 нажатия. Буду разбираться.
-- Самое неприятное, что разницу в работе вы обнаруживаете только в процессе работы. Я работал с библиотекой через питон. И вот вы запускаете один и тот же исходник на одной и той же системе с одной и той же версией пакета curses -- и получаете разный результат в разных терминалах (и ещё отдельный результат через ssh).
-- Команда изменения размера терминала работает через раз. В винде она отрабатывает правильно (что забавно). В линуксе изменяется виртуальный размер терминала, но реальный -- не всегда. При этом терминальная программа сама решает, какие строчки выводить. Например, она понимает, что такое статусная строка, и будет выводить последнюю строку экрана в приоритете. Но часть другого может не понять. И как мне работать? Пользователь может запустить терминал в размере 10 на 10 символов. Иногда это приводит к падениям программы, если пытаться вывести в границах виртуального экрана, но за границами реального. Хотя вроде вызываешь изменение размера, потом проверяешь -- всё ок. Выводишь символ в разрешённую позицию -- и программа падает. *пингвин кланяется* Это происходит не всегда, в глубинной причине не разобрался.

Выводы:
-- Если ваша основная задача это вывод текста в определённой позиции экрана, то всё хорошо
-- Надеяться на стабильное оформление нельзя
-- Надо рассчитывать на минимум доступных цветов, цветовых пар и прочего. При этом ваш терминал может на самом дел всё поддерживать (например, при игре в NetHack через Putty+SSH я вижу больше цветов, у меня работают все сочетания и автоизменение размера экрана, но библиотека говорит, что больше не может)
-- Удивительно, но неплохо работает мышка, хотя это вообще не основная задача терминала

Мне уже Minoru подсказывал альтернативные библиотеки по работе с экраном (STFL, он пишет, что мёртвый). Тем не менее, если вы знаетете библиотеку, которая позволяет:
1) Работать в питоне и с виндой и с линуксом
2) Делать не только поля ввода, но и менюшки, и кнопки и прочее (привет Turbo Vision)
3) Делать стабильное оформление

То сообщите мне.

@темы: Статьи, Программирование, Борьба с техникой

URL
среда, 16 октября 2024
06:09 Дело было не в бобине
Дайри внезапно усилил защиту CloudFlare, возможно специально для меня. Я уже писал, что проверяет эта защита. А у меня долгое время работало просто по куки _identity_. Но перестало.

Я стал делать по своей же инструкции (в конце концов, для себя я её и писал), но не достиг успеха.

Через некоторое время я обнаружил, что User-agent, который я задавал в настроечном файле, не применяется. А почему он не применяется?

Потому что по моему недосмотру User-agent из настроечного файла использовался у меня всего лишь в одному месте среди всего набора скриптов. В остальных местах это значение просто не передавалось на сервер. То есть, настройка только изображала, что она работает.

***

Когда читаешь англоязычные сайты, где различные люди описывают, как они решали программные, аппаратные и другие подобные проблемы, можно периодически видеть выражение "no luck". "I tried XXXX and YYYY, but no luck".

Понятно, что это что-то вроде фразеологизма. И тем не менее, мне как-то не по себе от таких фраз. Luck -- это удача. В то время как большинство программных и аппаратных проблем имеют конкретную причину, которая срабатывает независимо от удачи. И если в результате XXXX и YYYY ничего не вышло, то это не no luck, это no success, no solution или что-нибудь такое.

@темы: Программирование, Восприятие, Борьба с техникой

URL
воскресенье, 13 октября 2024
06:06 Делать хорошо надо сразу
Пет-проектов у меня немного. Тем не менее, кое-что есть. И вот какой эффект я у себя заметил.

Как известно, одно из ключевых средств при разработке ПО -- это система контроля версий. Обычно -- гит. Новый проект лучше сразу начинать в репозитории. Тем не менее, я этого не делаю. Какое-то время файлы проекта болтаются без надзора. А почему?

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

Полагаю, что это порочная практика. Говнокод надо не писать вообще, тогда и коммитить можно будет сразу. А работать без системы контроля версий опасно, хотя конечно в зарождающемся проекте об'ём кода такой маленький, что всё легко не только сломать, но и починить.

Но... непонятно, где тогда экспериментировать с новыми средствами. Делать отдельный репозиторий для экспериментов и никому не показывать?

@темы: Программирование

URL
воскресенье, 06 октября 2024
07:09 Я еще послушник и могу ругаться сколько захочу
Попробовал библиотеку curses. Широко известно, что если текстовая программа выглядит симпатично, то она была сделана на curses.

Так вот. Я, возможно, не до конца разобрался, но пока я не нашёл в библиотеке встроенных средств для создания кнопок, полей ввода и т.п. То есть, библиотека уступает аналогичному TurboVision (кто помнит такой? я на нём не писал, но результаты его работы видел предостаточно).

Но ладно. Я нашёл способ написать то, что мне надо. А поразил меня следующий момент.

В этой библиотеке повсеместно при задании координат сначала задаётся ИГРЕК, а потом икс!

@темы: Программирование, Говнокод, Борьба с техникой

URL
вторник, 24 сентября 2024
05:04 Смотримся в бездну, первая попытка
"Неправильно формируется пдф-отчёт об измерении" -- сообщает коллега.

Я этот отчёт не делаю, но мои данные там есть. Начинаю распутывать происхождение данных и в ужасе понимаю, что отчёт формируется при помощи значений, передаваемых по цепочке между тремя разными программами с двумя промежуточными файлами. И в этом процессе задействованы четыре разных человека.

Хорошо, что большинство из них находились в одной комнате, так что я просто обошёл их с листочком и выяснил то, что мне надо.

А если бы... если бы я работал с базами данных в большом бизнесе? Я читаю e1_huev0, у него каждый день истории такого же плана, но в десять раз хуже. Э-эх.

@темы: Программирование, Борьба с техникой

URL
четверг, 05 сентября 2024
06:13 Невнятные сообщения об ошибках
Примеров таких ошибок я приводил множество, но недавно до меня дошло, что это системная проблема, и даже появилось подозрение, с чем это связано.

Впервые я встретился с описанием такой ошибки в знаменитой книжке П. Нортона Programmer's Guide to the IBM PC. В русском переводе она имела пространное название "Персональный компьютер фирмы IBM и операционная система MS-DOS". В одной из таблиц с кодами ошибок была следующая запись:

(номер кода ошибки): "Неверный формат (а чего, не говорится)".

Книжку читал я в детстве, не имея на руках ещё никакого компьютера. Меня тогда это насмешило.

Сейчас-то мне не очень смешно!

Так вот, кода ошибки недостаточно для понимания происходящего. Часто нужно указание на имя файла, на какое-то конкретное число и т.п.

Механизм кода ошибок по своей сути не предназначен для передачи дополнительной информации. Чтобы такую информацию передать, надо создавать отдельную систему, причём способную хранить значения произвольного типа.

Казалось бы, у нас есть более современный механизм: исключения. Когда возбуждается исключение, можно сделать целый об'ект и засунуть в него всё что пожелаешь. А если об'ект наследован от какого-нибудь базового CException, то там наверняка есть какой-нибудь GetAsStr(), куда можно всё записать.

И тем не менее, невнятные сообщения об отсутствующих файлах (а каких, не говорится) я получаю даже в питоне.

Что делать? Писать логи. В момент передачи кода ошибки/исключения программа точно знает, что пошло не так. Пусть выводит сообщение с контекстом ещё до того, как вернуть код ошибки/возбудить исключение. Пока ещё всё известно.

@темы: Программирование, Говнокод

URL
пятница, 12 июля 2024
06:17 Ошибка: операция успешно завершена
Многие видели такие сообщения на сайтах или в программах. Видел их и я. А иногда даже делал. Сейчас расскажу, как такое может произойти.

Конкретно у меня был случай такой. Когда возникает ошибка, у неё есть код. Если код равен нулю, то ошибки нет. Начиная с единицы идут реальные коды ошибок.

Далее, произошла ошибка или нет, определяется не её кодом, а возвращением true или false из определённой функции, в которой ошибка и произошла. Если функция сообщила, что ошибка есть, происходит запрос кода ошибки.

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

У меня всё было наоборот. Я сообщаю в соседнюю программу об ошибке, потом получаю код у ошибочной функции. Соседняя программа, узнав об ошибке, тоже запрашивает её код -- уже у меня.

В идеальном мире последовательность действий будет такая:
1. Узнаю об ошибке.
2. Сообщаю соседней программе об ошибке.
3. Получаю код ошибки у ошибочной функции.
4. Приходит запрос от соседней программы о коде ошибки.
5. Я отдаю код ошибки.

Опытные программисты уже видят, где тут косяк. Запрос от соседней программы пришёл раньше, чем я узнал о коде. То есть, последовательность оказалась такой:

1. Узнаю об ошибке.
2. Сообщаю соседней программе об ошибке.
3. Приходит запрос от соседней программы о коде ошибки.
4. Я отдаю код ошибки. (нулевой!)
5. Получаю код ошибки у ошибочной функции.

Это типичная рассинхронизация многопоточного/многопроцессного приложения. У меня косяк: я не должен был никому рассказывать об ошибке, пока не буду сам иметь её кода.

@темы: Программирование, Фейлы

URL