All streams
Search
Write a publication
Pull to refresh
40
0
Павлов Николай Александрович @ZyXI

Инженер

Send message

Так в этом и проблема предложения. В Rust чтобы так сделать вам придётся постараться и задействовать макросы. В предложении это стандартная функциональность. Ещё мне здесь не понятно, что предлагается делать с ситуацией, когда какой‐то обработчик перестал быть актуален далее по коду: ввод булевой переменной и оборачивание тела обработчика в if как‐то не очень выглядит.

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

И какая же «ошибка записи» возможна? man 3p write, раздел ERRORS описывает 23 возможных ошибки, хотя часть с одними кодами, часть применима только к write() или только к pwrite() (кстати, ещё могли сообразить обёртку над writev(), в определённых случаях функция более удобная, тем более если нужно писать более одной строки), и всё применимо только к linux. Какие из этих ошибок может вернуть fmt.Println, а какие либо не возникнут вообще, либо будут обработаны самой fmt.Println и не дойдут до пользователя (я про EINTR в первую очередь, пользователя обёрток практически никогда не заморачивают обработкой этой ошибки)? Что она вообще вернёт конкретно и будет ли она возвращать одинаковой результат для схожих ошибок на Windows и linux?

Если в Altium в середину пустого места поставить via, назначить ей земляную сеть и перезалить полигоны, то на месте пустого места возникнет полигон. Так как пустое место видно, а несоединённые островки — нет, то вы абсолютно не правы.

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

Для того, чтобы помнить, как правильно, есть соглашения о стиле именования. На linux все переменные среды с особым значением в UPPER_CASE. Вам сложно запомнить одно соглашение? Кроме того, заметьте, что ни %userprofile%, ни $PATH не имеют ни пространства имён, из которого их нужно импортировать, ни просто какого‐то префикса. Т.е. в Windows вы не можете знать, не будет ли переменная из вашего скрипта в будущем иметь какое‐то особенное значение. В linux вы можете писать в своём скрипте на POSIX shell for path in * ; do … done и быть уверенным, что специального значения у path нет.


Правда, я не зря уточнил про POSIX shell: я не помню, чтобы какая‐то программа забрала под себя переменную окружения вида LuaInit вместо «правильного» LUA_INIT, но вот zsh в «режиме эмуляции zsh» $path содержит массив путей из $PATH — хороший пример того, как нормальные скрипты ломают поведение другой программы из‐за внезапно возникшего специального значения используемой ими переменной — при редактировании массива $PATH также обновляется и в случае с for path in * там просто окажется последнее имя файла без пути. Также показывает, зачем вообще нужны такие соглашения.

Давайте правильно сформулируем: строка маски(пути/regexp/чеготамещё) — это и есть сериализованная структура, всё это экранирование — это про правила сериализации. Инициализировать структуры без промежуточного строкового представления — удовольствие так себе. Или вы знаете другой удобный способ ввести маску в поле с именем файла в том же диалоге открытия?

Да, это сериализованная структура. Инициализировать структуры в C — удовольствие действительно так себе. Но сейчас есть другие варианты.


Что вы под статической типизацией тут понимаете — для меня загадка, структуры статической/динамической типизации ортогональны.

Я имею ввиду, что вместо строки "PDF files\0*.pdf\0\0" для представления маски будет использоваться


DialogFilters filters = {
    .size = 1,
    .filters = (DialogFilter []) {
        {
            .name = "PDF files",
            .globs_count = 1,
            .globs = (GlobPattern []) {
                {
                    .size = 2,
                    .parts = (GlobPatternPart []) {
                        { .type = kFilterGlobStar },
                        { .type = kFilterGlobLiteral, .data.text = ".pdf" },
                    }
                },
            }
        },
    }
};

С этой структурой вполне очевидно, почему никто так не делал: большая и невыразительная, даже в случае с C99 лучше оставить первый вариант, в случае же с C89 не следует даже пытаться такое писать — будет много непонятных магических цифр и никаких подсказок вроде .size, так что при редактировании вы ещё больше раз забудете обновить размеры. На Rust выглядит гораздо лучше, и не нужно ни ставить размеры, ни менять их на терминаторы просто чтобы не заморачиваться с их обновлением:


let filters = [
    DialogFilter {
        name: "PDF files",
        globs: &[globpattern!("*.pdf")],
        // Имеет смысл, если вместо ".pdf" у вас переменная:
        //globs: &[
        //    &[GlobPatternPart::Star, GlobPatternPart::Literal(".pdf")],
        //],
    },
];
«Практически всё» — это «редко» или «часто»? Я не говорил, что что-то не справиться, я сказал, что плохая идея.

«Практически всё» — это все GUI программы, что я использовал, все программы на C (я в основном про coreutils), но не некоторые скрипты (бо́льшая часть — это от того, что у меня ещё не было привычки всё экранировать, то что из скриптов есть в системе не только обычно хорошо написано, но ещё и не имеет причин лезть в $HOME).


«обычно» — это «часто» или «редко»? Если надо необычно — надо другой тулкит искать? Удобно.

«Обычно» — это я ни разу не видел, чтобы маска определялась из источника, который может содержать произвольное имя файла. Так что на вопрос лучше ответите вы. И с какой радости нужно искать новый тулкит, а не функцию экранирования в старом?


Там нужно, тут не нужно. Опять удобно!

Если бы вместо масок была нормальная статическая типизация, то не было бы нужно/не нужно. Было бы «здесь у нас вход типа „имя каталога“ (ну или хотя бы просто строка), здесь — структура, определяющая маску».


А так всегда нужно читать документацию.


Редкая ситуация — это когда пользователь способен вспомнить как ему надо заэскейпить в этом очередном тулките. Не программист — пользователь.

Редкая ситуация — это когда пользователь вообще что‐то там пишет. Обычно проще просто натыкать мышкой. Я вот про то, что поле с именем файла принимает маски не только в Vivaldi (где какой‐то нестандартный диалог) только сейчас узнал, когда пошёл проверять.

Я, кстати, знаю, что у какого‐то студента МИФИ в качестве дипломного проекта была материнская плата — и он её сделал. Правда я не интересовался деталями, просто за чаем на работе проскакивало «какие бывают умные люди» — как сложно сделать материнскую плату с большой пачкой ВЧ деталей у нас понимают.

Ок, а моёВаси личное дело не эскейпить, так что если приложение упадёт при встрече с вашим файлом — это ваше личное дело. У вас там опенсорс, форкните-почините, если что.

Неиспользование экранирования сразу вывешивает флаг «здесь водятся уязвимости», а такие фразы ещё вызовут реакцию «этот чувак не Линус Торвальдс, но чего‐то он заборзел». Если у вас нет большой базы пользователей, а ПО открытое, то позволить вы себе такого не можете. Если есть большая база, а ПО всё ещё открытое, то можете получить форк‐конкурента, а не форк для PR с исправлением. Иногда они даже успешные.

1) Это как-бы свершившийся факт, а не я предлагаю. Подозреваю, что даже в линуксе назвать файл "*" не будет удачной идеей.

Не замечал такого. Я как‐то некоторое время назад решил, что вместо того, чтобы просто иметь свою и чужую (т.н. dotfiles) помойку в $HOME лучше иметь чужую помойку в $HOME, свою в $HOME/tmp, и переносить всё нужное куда‐то под $HOME/.. Практически всё с таким именем каталога нормально справлялось, пока экранирование звёздочек при наборе команд в оболочке не надоело мне.


С чем будут реальные проблемы в огромном количестве мест — это с символом \n (LF) в имени файла: если нормально написанные программы на нормальных языках программирования (т.е. большинство что я видел, кроме того, что написано на *sh (т.е. скриптов) и VimL (т.е. дополнений для Vim)) такое имя файла отлично переварят, то написать даже bash скрипт, корректно обрабатывающий такие имена, часто может быть проблемой. Я уж не говорю про POSIX shell. Всё остальное нормально работает.


Ненене, когда я в html использую > — это язык HTML, с такой вот специальной разметкой. Меня интригует появление специального glob-языка для экранирования glob. Он же уникален для конкретного вот места применения, а язык должен быть переиспользуемым, универсальным.

А где, простите, общий стандарт на glob, чтобы можно было говорить об универсальности? Вот man glob ссылается на POSIX.1-2001, POSIX.1-2008, POSIX.2 — если я напишу в Windows [[=а=]], он меня поймёт?

В маске обычно просто статические строки, которые разработчик выбирает сам ещё на этапе компиляции. Там может быть что‐то вроде статического .htaccess, если нужный тип файла вдобавок имеет фиксированное имя, но предзаданное генерируемое имя файла в маске я никогда не видел, видел имя файла по‐умолчанию в поле File name. Это отдельное поле, и, да, здесь‐таки нужно экранирование, если диалог поддерживает маски от пользователя в этом поле — а, насколько я понимаю, это нередкая ситуация.

Я не говорю, что это одно и то же. Я говорю, что это схожие случаи: «нужно запихнуть имя файла куда‐то, где часть символов обрабатываются особенным образом». Это решается экранированием, а не запретом символов. И wildcard, кстати, под капотом часто преобразуются в регулярные выражения: и glob из Python, и реализация из Vim поступают именно так.


Кстати, то что программа чтения pdf показывает *.pdf в выпадающем списке совершенно не означает, что внутри она вообще использует что‐то похожее на glob.glob(). Если диалог открытия файлов не самописный, то об этом будет заботиться тулкит, создающий диалог, который нужно написать один раз и которому на вход пойдёт wildcard для файлов в каталоге отдельно от собственно каталога — здесь не нужно никакого экранирования вообще. Ни в читалке — она просто передаёт каталог отдельно от фильтров (иногда неявно: к примеру, стандартный диалог открытия файлов для Windows использует текущий каталог процесса), ни в собственно тулките: вариант с os.listdir в таком случае более чем уместен, особенно учитывая, что тулкиты обычно написаны на C++.

Давайте тогда с терминами определимся. У вас слеши 2 раза — это не экранирование?

Это тоже экранирование, но на другом уровне: оно для того, чтобы в glob вообще попала обратная косая черта. Экранирование вроде []] — это уже для glob. Так что я несколько не прав и моё утверждение о том, что в примере ничего не экранируется вообще‐то следовало написать как «ничего не экранируется специально для glob».


Так там прямым текстом, что она про спецсимволы ('?', '*' and '[') — у меня таких быть не может, запрещены (Ну, '[' — нет, но он нужен для экранирования, которое не нужно).

То есть эти все заморочки растут из того, что оптимизаторы не запретили '?' и '*' и теперь их надо от wildcard отличать с приседанием.

На сколько затормозиться ядро в среднем, если при создании файла сделать проверку на наличие десятка символов — это вообще страшные цифры (нет).

А теперь вы хотите вместо glob поискать имя файла где‐то регулярным выражением. Или вместо очень ограниченных шаблонов с ?, * и [ хотите написать шаблон для zsh, где в список спецсимволов попадают ещё %, ( и < (я не про перенаправление), также #, |, ^ и ~ с включённым EXTENDED_GLOB или @, + и ! с включённым KSH_GLOB.


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

Если у вас в конфиге в одном месте экранируется потому, что в коде glob, а в другом нет, потому, что os.listdir — у вас проблемы с конфигом. Или с языком. Или с библиотекой. Или с ОС. Но что-то пора менять, искать другой способ.

Что?! Экранирование происходит в коде, а не конфиге. В случае с glob.glob("C:\\foo\\*") ничего не экранируется, потому что нечего и это сразу видно. В случае с glob.glob(os.path.join(globescape(config.foodir), '*')) вам нужно иметь функцию globescape. И не забывать её применять. В случае с glob.glob([config.foodir, glob.STAR]) вы не можете забыть её применить, поскольку она не нужна (предполагается, что glob.STAR — это экземпляр специального класса, а не строка '*').


Единственное, что в данном случае сделает запрет специальных символов, с которым «жить было бы легче» — это позволит засунуть проверку на них туда, где вы читаете из файла настроек, создав контракт «в настройке типа „путь к каталогу“ не может быть символов из набора …» и таким образом убрав необходимость писать globescape. И то только до тех пор, пока вы не захотите вместо glob.glob подсунуть строку, к примеру, в re.compile (если, конечно, вы действительно не предлагаете убрать все символы, кроме a-zA-Z0-9_). К тому же, это хорошо только при условии, что вы точно знаете, что ваше ПО не будет запускаться на других платформах.


Т.е. жить сильно легче не станет, зато добавится головная боль разработчикам ядра и оно, к тому же, станет работать медленнее.

Вы предлагаете в ответ прочитать лекцию почему придумали нестрогую?

Если эта лекция поддержит вашу позицию по поводу необходимости запрета определённых символов — пожалуйста, почему нет.


Действительно внезапно. Но нет, я обхожусь без экранирования — мне сложно разделить вашу боль.

Это значит что вы либо получаете уязвимости или плохую привычку (первое если вы распространяете своё ПО, второе — если нет, но можете в будущем), либо используете языки и библиотеки, в которых оно не требуется. В примере с glob вполне можно обойтись без экранирования, если переписать код с использованием os.listdir, но он будет длиннее.

Это не «не нужно», это «зачем люди придумали строгую типизацию». А прямые косые черты принимаются не везде. Вынос путей из исходников никак не решает проблему, скорее наоборот: пока в вашем скрипте пути внутри вы можете написать glob.glob("C:\\Foo\\*"). Когда вместо C:\Foo у вас путь из настроек вам внезапно нужно экранирование этого пути. Я не говорю, что это не нужно делать: даже не предоставляя настроек в скрипте я часто выношу пути в отдельные константы, определяемые наверху файла. Но проблему «если вы собираете код/регулярное выражение/команду shell/… из кусочков, то вам нужно думать об экранировании» ни вынос части этих кусочков в конфиг, ни запрет символов не решит. Сборка же не в строку, а в более сложную структуру — решит.

Заказывал четырёхслойную плату (с попарным прессованием) в прошлом месяце, на синюю маску+белую шелкографию веб‐интерфейс показывал +1 500 рублей за «нестандартную маску». Они что‐то в этом месяце поменяли?

Я не слышал про проблемы с экранированием, к примеру, в C, которые бы реально решались запретом символов. Пока имя файла — это имя файла или, хотя бы, просто одна строка никаких проблем нет. Проблемы начинаются, либо когда вы используете eval в той или иной форме (в т.ч. формируете snprintf’ом строку для system() вместо использования execv() (или аналога из libuv, если в системе execv() не поддерживается)) и должны сформировать для него строку, либо когда строка неявно преобразовывается во что‐то (в вашем примере glob должен реально получить что‐то вроде списка ["C:\\abc\\afolderwith[test]\\", glob.STAR], потому что семантически вход — отнюдь не простая строка; — но это, очевидно, неудобно). На POSIX shell написать сложный скрипт без eval невозможно, вдобавок все *sh (в т.ч. те, где eval не так нужен) любят внезапно преобразовывать строку в список glob’ов, которые они же сразу раскроют.


И здесь «десяток символов» вам не поможет. Насколько я знаю, безопасный набор — это ASCII, причём только буквы, цифры и подчёркивание. Всё остальное либо имеет где‐то какое‐либо специальное значение, либо может быть неверно воспринято из‐за проблем с кодировками. Особенно приколен Windows с его обратными косыми чертами, которые так же являются символом экранирования в большинстве языков программирования.


Так что зачем запрещать символы, если всё, на что способен запрет — это улучшить несколько частных случаев, при этом как ухудшив производительность файловой системы, так и взаимодействие с пользователем? К примеру, в названиях книг встречаются двоеточия, поэтому нельзя взять и сохранить книгу в файле с именем, совпадающим с названием книги — нужно его как‐то преобразовывать. Результат, как правило, выглядит хуже, чем оригинальное название.

Ещё бывают ситуации, когда что‐то не должно работать, а работает (но плохо). У нас как‐то студент развёл землю для сенсора температуры тремя разными кусками (не соединёнными между собой). Долго пытались понять, почему датчик показывает неправильную температуру, пока то ли кто‐то не использовал мультиметр, то ли я не заметил в проекте линию сети (ту, которой соединяются несоединённые части) и не догадался‐таки прогнать DRC. Насколько я помню, там ножка земли не соединялась с землёй источника питания и земля, на которые посадили лишние аналоговые входы не соединялась ни с источником питания, ни с ножками земли. Таким образом, единственное, откуда можно было взять ноль — это SPI (конечно, только пока по нему не начиналась передача). Как это вообще работало — непонятно.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity