henry_flower: A melancholy wolf (Default)

Ґноум втратив директора:

"Holly Million was named the GNOME Foundation Executive Director in late October. Part of her focus was going to be on helping the GNOME Foundation attain more funding. But coming today as a surprise, she will be departing her role with GNOME effective the end of July. She is stepping down on the basis of pursuing a PhD in psychology and dedicating herself to her own private practice."

Намагання знайти фінансування для Ґімпу викликає такий рівень психоемоційного стресу, який лікується лише отриманням PhD ув психології.

До погодження стати директоркою Ґноума, вона працювала "професійним шаманом, артистом, виробником трав'яних ліків та займалася мікро-гомстедингом". Останнє є не сексуальне збочення, а тип сільського господарства ув Гамериці.

"I do energy healing work on behalf of individuals, land, houses, and businesses.", розказувала про себе пані Мілліон, "I can do this work remotely because everything is connected. We live in a quantum universe."

Гомстединг був дуже ув нагоді: вирощування лікарських трав допомагало "with my shamanic work". Якщо у вас врожай з'їдали комахи, пані Мілліон проводила цілющі церемонії та ритуали по телефону: $250 за годину енергетичної роботи 1:1 з вирівнюванням чакри, або $500 за повне очищення енергії у вашому домі чи охфісі.

Наразі рада директорів Фундацьйоне Ґноума, запаливши пахощі та свічки, шукає нового директора.

henry_flower: A melancholy wolf (Default)

Як хутко довести дівопсу або хмарному провайдеру що завантаження з серверу є повільне? Можна заміряти час загальний, чи копіювати все що curl написав на stderr, або вислати кєно зроблене на телефоні (вертикальне, тремтячою рукою, під кутом > 10°, на відстані > 1м від монітора).

Не пам'ятаю ув якій версії віндюка його іксплорер почав малювати графіка під час копіювання хфайлів (Vista?), але він (віндюк) так і не почав цього робити ані ув класичних своїх бовзерах минулого, ані ув своєму поточному безглуздому різновиді кроума для блінка.

Найнахабнішим повідомленням дівопсу був би svg:

та/чи рядки

00:00:01 1000k
00:00:02 2000k
00:00:03 2500k

ув якості доказу.

2й варяінт є найпростіший; curl, наприклад, друкує

fprintf(tool_stderr,
        "\r"
        "%-3s " /* percent downloaded */
        "%-3s " /* percent uploaded */
        "%s " /* Dled */
        "%s " /* Uled */
        "%5" CURL_FORMAT_CURL_OFF_T " " /* Xfers */
        "%5" CURL_FORMAT_CURL_OFF_T " " /* Live */
        " %s "  /* Total time */
        "%s "  /* Current time */
        "%s "  /* Time left */
        "%s "  /* Speed */
        "%5s" /* final newline */,
        …

кожну секунду, тому лише замінивши \r на \n можна відправляти повного логу:

$ curl http://example.com/1.zip -o 1.zip 2>&1 | tr \\r \\n

та вимагати сатисфакції.

Щоб намалювати графіка з gnuplot, нам потрібні Current time та Speed. Останнє доведеться конвертувати з 1234k чи 4567M ув байти. Спочатку я думав малоелегантно замінювати суфікси:

awk 'function expr(s) {
  h["k"]=2**10; h["M"]=2**20; h["G"]=2**30; h["T"]=2**40; h["P"]=2**50
  for (k in h) sub(k, "*"h[k], s); return s
}
/[0-9.][kMGTP]?$/ { print expr($NF) | "bc"; close("bc") }'

але, виявляється, саме для цього coreutils має ютіліту numfmt(1), про яку я ніколи не чув (додали недавно, лише 12 років тому, не встигаєш слідкувати за всіма ціма новими штуками):

$ echo lol 10M | numfmt --from iec --field 2
lol 10485760

Так, звичайно, зараз не роблять, а кожного разу ретельно пишуть нудні, багатослівні простирадла на пáйфоні. Те що має фітнутися ув 5 рядків шелових макс + 6 рядків скрипта gnuplot'у, потрібно ретельно розмазувати на 70+, ніби працюєш на ойбіем ув році 1984 і тобі платять за кількість рядків коду.

Повна версія іграшкового візуалайзатора пускається як

$ ./curlbench http://example.com/1.zip | gnuplot --persist

де gnuplot дозволяє зберегти графіка ув потрібному форматі.

$ cat curlbench
#!/usr/bin/env -S stdbuf -o0 bash

set -e -o pipefail
numfmt=`type -p gnumfmt numfmt;:`; test "${numfmt:?}"

cat <<E
set xdata time
set timefmt "%H:%M:%S"
set xlabel "Time"
set format y "%.0s%cB"
set grid
plot "-" using 1:2 with lines title ""
E

curl "$@" -fL -o /dev/null 2>&1 | tr \\r \\n | awk '
/[0-9.][kMGTP]?$/ {
  time = index($10, ":") == 0 ? $11 : $10
  if (time != "--:--:--") print time, $NF
}' | tr k K | $numfmt --from iec --field 2

Має працювати також на fbsd та маку (як були встановлені coreutils), але мені є лінь перевіряти.

henry_flower: A melancholy wolf (Default)

Побачив ув твіторі:

Robert @RobertZeltinsh · Jun 6

Діди самодєлкіни, викликаю вас. Є задача, зібрати якусь шляпу щоб коли вмикається і пропадає світло, вона відправляла про це смс.
Знайшов якісь вумні розетки з gsm, але воно криве і косе. Все що з розумним будинком не підходе, бо інтернет відсутній.

З будь-яким непотрібним ондроїдом з встановленим Termux можна написати нескладного скрипта, який буде використовувати 2 ютіліти:

  • termux-battery-status
  • termux-sms-send

(apt-get install termux-api, який вимагає також окрему ондроїдну аплікацію termux-api.)

1ша вертає json:

{
  "health": "GOOD",
  "percentage": 42,
  "plugged": "PLUGGED_AC",
  "status": "CHARGING",
  "temperature": 31.0,
  "current": -564452
}

2га відправляє рядок на телехфонний номер. Ув якості тесту я надіслав sms собі, після чого Київстар життєрадісно відповів що це коштувало мені 3 гривені (~7 євроцентів). Останній раз я таке робив, напевно, ув 2010 році, тому з подивом дізнався, що sms не тільки не є безплатні, а продаються пакетами по 300 штук.

Це означає, що якщо ув гаражі станеться перевантаження ланцюга і світло почне мерехтіти, за годину можна зайняти 1ше місце у Європі за кількістю відправлених sms та сертифікат від Київстару 'ідіот року'.

Отже скрипта має пам'ятати про sms ліміт на день.

Найпростіша скіма з опитуванням батареї може виглядати так: телехфон вмикається до розетки, на телехфоні запускається termux, ув якому запускається:

#!/usr/bin/env bash

set -e
poll_time=3
battery=${battery:-termux-battery-status}
sms=${sms:-termux-sms-send}

...

type jq $battery $sms > /dev/null
number=${1:?no phone number}

last=
while true; do
    status=`$battery | jq -r .plugged`; [ "$status" ]
    [ "$status" = "$last" ] && continue

    [ "$last" = "" ] || case $status in
        UNPLUGGED) send No electricity ;;
        PLUGGED_AC) send A keen and shared excitement ;;
        *) log "Unknown status: $status"
    esac

    last=$status
    sleep $poll_time
done

де poll_time ліпше виставляти не 3 секунди, а ув залежності від ліміту sms, наприклад для 100 штук на день--864 секунди (опитування кожні 14.4 хв).

Найнудніша частина є ув відстеженні кількості відісланих sms. Ви знали що bash вміє порівнювати рядки лексикоґрафічно? Я не знав!

$ [ 2024-06-11 \> 2024-06-10 ] ; echo $?
0
$ [ 2024-06-11 \> 2024-06-12 ] ; echo $?
1

Тоді перевіряючи mtime хфайла, де записується кількість sms, можна скидати ліміта:

sms_limit_file="${XDG_CACHE_HOME:-$HOME/.cache}/elektrokharchuvannia"
sms_limit=10

sms_limit_get() {
    local r=$sms_limit
    [ -r "$sms_limit_file" ] && {
        r=`head -c4 "$sms_limit_file" | awk '{print $0+0 == $0 ? $0 : 0}'`
        local today="`date +%Y-%m-%d`"
        local mtime="$(date -d "@$(stat -c %Y "$sms_limit_file")" +%Y-%m-%d)"
        [ "$today" \> "$mtime" ] && r=$sms_limit
    }
    echo "$r"
}

sms_limit_decr() {
    mkdir -p "`dirname "$sms_limit_file"`"
    echo $((`sms_limit_get` - 1)) > "$sms_limit_file"
}

log() { printf '%s: %s\n' "`date`" "$*"; }

send() {
    [ "`sms_limit_get`" -lt 1 ] && { log SMS dayly limit reached; return; }
    local msg="$* (SMS left: $((`sms_limit_get` - 1)))"
    log "$msg"; log "$msg" | xargs -d\\n $sms -n "$number";
    sms_limit_decr
}

Тестування цього ковгоспу без відсилання повідомлень:

$ sms=true ./elektrokharchuvannia +380148800000

Тут воно повинно показувати нічого. Далі, висмикування дроту з телехфону та вставляння його знову, друкує:

Wed 12 Jun 01:25:15 EEST 2024: No electricity (SMS left: 2)
Wed 12 Jun 01:25:20 EEST 2024: A keen and shared excitement (SMS left: 1)
Wed 12 Jun 01:25:25 EEST 2024: No electricity (SMS left: 0)
Wed 12 Jun 01:25:28 EEST 2024: SMS dayly limit reached
henry_flower: A melancholy wolf (Default)

Коли фасебоок іноді попереджує про непозбувану бентегу, якщо клікнути на ікстернальний лінка (як вам не соромно, you're going to a link outside facebook), то минулого літа ув багатьох газетах від цього відбувався розрив дупи (Facebook is now treating content links like malware).

jwz колись розповідав, що оригінальний Mosaic був піонером цього епроачу, маючи налаштування, з яким бовзер малював попереджувального даялога, коли нещасний користувач клікав на будь-якому лінку.

Виявляється, Mosaic 2.7 можна зібрати на сучасному лайнаксі, що я зробив щоб даялога побачити. Це motif аплікація! Вікна зі налаштуванням там немає, тому що вся конфіґурація очікується ув X resources, якщо хтось пам'ятає цей навіки забутий механізма користувацьких параметрів:

$ grep Mosaic ~/.Xdefaults
Mosaic*protectMeFromMyself: true
Mosaic*XmDialogShell*fontList: -*-helvetica-medium-r-*-*-24-*-*-*-*-*-*-*

Були часи. Щоб змінити розмір шрифта, інакше розгледіти діфолтний на сучасних екранах можна лише за допомогою лупи, довелося передивлятися дерево мотіфних віджетів за допомогою editres.

Mosaic protectMeFromMyself dialog

Salacious materials, так.

henry_flower: A melancholy wolf (Default)

Кожного разу як я перевіряю чи буде працювати лайнаксна аплікація на відюку, то жалкую що спробував.

Наприклад, є ноудний майкро-сервіс foo, який від ОС вимагає лише підтримку TCP/IP, робить жодних змін ув файловій системі, вимагає 0 суто лайнаксних API, пише logs до stdout, вважає що його будуть байнднути до умовного 12345 порту, що привілей спеціяльних не вимагає.

На лайнаксі для такого майкро-сервісу пишуть елементарного юніт-хфайлу ув ~/.config/systemd/user/foo.ini і Боб є ваш дядько--користувач лайнаксу (регулярний) контролює foo за допомогою звичних команд systemctl.

Ув віндюку, починаючи з 10 (чи 8?), з'явилися "per-user" сервіси, наприклад cbdhsvc (це так, у якості конспірації, зашифровується словосполучення сервіс кліпбоарду), які, незважаючи на назву, вимагають привілеї адміністраторські для свого створення і затишні поради "інсталюйте нові ваші аплікації до $env:APPDATA" зіштовхуються зі суворою реальністю майкрософтського ідіотизму.

Чи складно тоді сгенерувати інсталятора, що

  1. перепише директорію з хфайлами майкро-сервісу до $env:PROGRAMFILES/foo;
  2. створить ув реґістрі key з описом сервісу;
  3. створить на десктопі ув паблік фолдері (тобто для всіх користувачів) шортката на http://127.0.0.1:12345?

Нескладно, але я би поставив оцінку 3/10: не рекомендую.

До появи UWP аплікації, які у нас час роблять для майкрософтського магазину істоти що народилися лузерами, генерувався .msi хфайл. Більше 10 років тому для цього був wix toolset--декілька феноменально огидних CLI-утіліт, написаних людьми, для яких поняття смак є так само близьке, як відстань сузір'я Андромеди до планети Земля.

З того часу змінилося нічого. Є інші способи створювати інсталятори, наприклад Inno Setup (якщо вас не дратує даялекта паскалю (так), на якому доводиться писати шматки скриптів для будь-якої нетривіяльної дії), або InstallShield, якщо ви міцно поїхали головою та бажаєте сабскріпшона $2474/рік годувати якихось індусів.

Питати про wix будь-яку LLM--гаяти час. Гоогл забитий порадами для версії 3.x, яка малює даялоги для моніторів 2006 року з 96 dpi. Нова версія 5 має неймовірне поліпшення: вміє створювати список файлів сама, без допомоги ікстернального препроцесора або (я вибачаюсь) xslt! Для цього грандіозного leap forward їм знадобилося 20 років. А також:

  • "інторнет" шортката, який генерує wix, потім неможливо відредагувати (до permissions це стосунку не має), url є вшитий намертво;

  • якщо воно не може стартувати сервіса ув процесі інсталяції--настає павза та rollback, жодні твікі з vital=no, wait=no і т.і. не допомагають, хоча згідно пародії на документацію повинні;

  • інтерхфейсьні лейбли можуть стискатися ув залежності від різолюшену монітору та dpi--дуже зручно якщо ти повідомляєш користувачу "дивись logs ув $env:SystemRoot/Temp/foo*, а згенерований даялог малює .../foo*.

Куди правильно писати logs для користувача NT Authority\Local Service я так і не зрозумів. Від блискучих за точністю порад що він має limited write access to the file system у мене бажання перекваліфікуватися на ілектришона та до кінця своїх днів лагодити розетки ув субурбії Калґарі.

henry_flower: A melancholy wolf (Default)

Хтось пам'ятає Kingpin?

Цього року вийшов рімейка, але кажуть що суттєво гірший за оригінала, який я ностальгічно спробував запустити на w7 vm, але безуспішно.

Іграшка вийшла ув 1999, коли у мене була riva tnt2, яку я влітку того року вициганював у батьків (успішно). Віндюка 98 для Kingpin неможливо встановити на хайпервайзорах сучасних cpu; є патчі на гітхабі від якогось божевільного, хоча на amd вони не працюють.

Але є такий собі 86box. Ніколи про нього не чув! Воно емулює (вибачте) ПіСі від 8088 до pentium 2 з voodoo3 і вміє робити приємне crisp nearest neighbor скейлінґа (замість огидного linear як ув qemu та vmware).

Коли встановився w98, я пустив сльозу споглядаючи GUI. Такої краси зараз не роблять. З деяким зусиллям поборов бажання встановити туди Visual Studio 6 и писати вам тут shareware html-редактора на C++ з MFC.

Цієї неділі пройшов 1й рівень, поїхавши на мацациклі зі Skidrow; далі не хочеться і дратує що немає ріалтаймової мапи.

henry_flower: A melancholy wolf (Default)

Побачив ув твіторі:

✙ цар криса
@retraut

блять, немає curl думав зараз завантажу. А як завантажити коли немає curl, wget?

Це про деб'янвського контейнера. В останніх є зазвичай баш, його ставить debootstrap навіть ув мінімальній конфігурації.

Як відкрити TCP сокета, якщо немає ніяких Рубі інстальованих, жодного компайлера, контейнера є пустий та порожній, і темрява над лайнаксом, і демон ssh ширяє над поверхнею контейнера без форми?

Якщо баш був зкомпайлений з --enable-net-redirections, він вміє оперувати з файлами /dev/tcp/example.com/80:

$ exec 3<> /dev/tcp/www.gnu.org/80
$ printf "%s\r\n" 'HEAD /robots.txt HTTP/1.1' >&3
$ printf "%s\r\n\r\n" 'Host: www.gnu.org' >&3
$ cat <&3
HTTP/1.1 200 OK
Date: Sun, 11 Feb 2024 07:02:40 GMT
Server: Apache/2.4.29
Content-Type: text/plain
Content-Language: non-html
…

Все це є вкрай цікаво, але позбавлене сенсу: сучасний інтервеб, а не уйоб сайти Столмана, розмовляє лише через TLS. Завантажити статичного байнарника curl з ґітхабу так не вийде.

Але можна на нормальному сервері запустити форвардний TLS проксі і під'єднуватися до нього. Ув мінімальному вигляді це може бути

$ ncat -vk -l 10.10.10.10 1234 -e proxy.sh

де proxy.sh:

#!/bin/sh
read -r host
openssl s_client -connect "$host":443 -quiet -no_ign_eof

Баш-клайент відправляє ім'я хоста, який ми на нашому проксі-сервері зчитуємо і за допомогою openssl під'єднуємося до нього. ncat тут працює ув галантному стилі inetd--форкає себе і під'єднує сокета башу до stdin/stdout скрипта proxy.sh, про що було ув цьому бложику на початку року.

Зі сторони баша-клайента залишається декілька питань:

  1. будь-який файл з "releases" сторінок ґітхабу мандрує через 302 рідайректа, тому щоб щось завантажити, треба робити 2 ріквести;

  2. як парсити відповіді? Як мінімум треба знаходити \r\n\r\n, який відділяє headers від омріяних байтів curl.tar.xz; це означає передивлятися байнарний стрім, що є напрочуд неприємно робити ув шелі;

  3. як парсити URL? це є необхідно для реакції на 302.

Почнемо з останнього.

В ідеалі, хотілося би мати хфункцію ув баші, яка би працювала як джаваскриптова URL():

$ node -pe 'new URL("https://q.example.com:8080/foo?q=1&w=2#lol")'
URL {
  href: 'https://q.example.com:8080/foo?q=1&w=2#lol',
  origin: 'https://q.example.com:8080',
  protocol: 'https:',
  username: '',
  password: '',
  host: 'q.example.com:8080',
  hostname: 'q.example.com',
  port: '8080',
  pathname: '/foo',
  search: '?q=1&w=2',
  searchParams: URLSearchParams { 'q' => '1', 'w' => '2' },
  hash: '#lol'
}

На SO є багато відповідей з прикладом ріґекспів, але жоден з них не пройшов у мене тестів з URL вище. Ріґекспи там такої довжини і такі страхітливі, що фіксити їх я не намагався, а спитав чатаджипіті, який зробив лише гірше, і гооглівського джéменай, який з Nї спроби його ніби полагодив.

$ cat lib.bash
declare -A URL

url_parse() {
    local pattern='^(([^:/?#]+):)?(//((([^:/?#]+)@)?([^:/?#]+)(:([0-9]+))?))?(/([^?#]*))?(\?([^#]*))?(#(.*))?'
    [[ "$1" =~ $pattern ]] && [ "${BASH_REMATCH[2]}" ] && [ "${BASH_REMATCH[4]}" ] || return 1
    URL=(
        [proto]=${BASH_REMATCH[2]}
        [host]=${BASH_REMATCH[4]}
        [hostname]=${BASH_REMATCH[7]}
        [port]=${BASH_REMATCH[9]}
        [pathname]=${BASH_REMATCH[10]:-/}
        [search]=${BASH_REMATCH[12]}
        [hash]=${BASH_REMATCH[14]}
    )
}

Пошук \r\n\r\n. Лайнаксний grep має опції -a -b -o, які разом друкують byte offset знайденого патерну. Busybox'сний grep про -ab не знає. Нарід каже про od(1), але без прикладів. Якщо їм друкувати файла як

0000000 68
0000001 20
0000002 3a
…

де лівий стовпчик то є offset, то можна 1ші 32КБ ріспонзу такого формату конвертнути в 1 рядок і шукати \r\n\r\n grep'ом:

offset_calc() {
    od -N $((32*1024)) -t x1 -Ad -w1 -v "$tmp" | tr '\n' ' ' | \
        grep -o '....... 0d ....... 0a ....... 0d ....... 0a' | \
        head -1 | cut -d' ' -f7 || printf -- -1
}

Успішна відповідь там буде 0000345, що вимагає потім видаляння leading zeroes.

Повна версія баш-клайенту, яка підтримує лише https та спочатку зберігає кожну відповідь ув темпоральному файлі, а результат друкує на stdout:

#!/usr/bin/env bash

set -e -o pipefail
. "$(dirname "$(readlink -f "$0")")/lib.bash"

tmp=`mktemp fetch.XXXXXX`
trap 'rm -f $tmp' 0 1 2 15
eh() { echo "$*" 1>&2; exit 2; }

[ $# = 3 ] || eh Usage: fetch.bash proxy_host proxy_port url
proxy_host=$1
proxy_port=$2
url=$3

get() {
    url_parse "$1"; [ "${URL[proto]}" = https ] || return 2

    exec 3<> "/dev/tcp/$proxy_host/$proxy_port" || return 2
    echo "${URL[hostname]}" >&3
    printf "GET %s HTTP/1.1\r\n" "${URL[pathname]}${URL[search]}${URL[hash]}" >&3
    printf '%s: %s\r\n' Host "${URL[hostname]}" Connection close >&3
    printf '\r\n' >&3
    cat <&3
}

get "$url" > "$tmp" || eh ':('

offset_calc() {
    if strings "`command -v sh`" | grep busybox >/dev/null; then
        od -N $((32*1024)) -t x1 -Ad -w1 -v "$tmp" | tr '\n' ' ' | \
            grep -o '....... 0d ....... 0a ....... 0d ....... 0a' | \
            awk '{if (NR==1) print $7+0}'
    else
        grep -aobx $'\r' "$tmp" | head -1 | tr -d '\r\n:' | \
            xargs -r expr 1 +
    fi || echo -1
}
offset=`offset_calc`
headers() { head -c "$offset" "$tmp" | tr -d '\r'; }
hdr() { headers | grep -m1 -i "^$1:" | cut -d' ' -f2; }

status=`head -1 "$tmp" | cut -d' ' -f2`
case "$status" in
    200) [ "$offset" = -1 ] && offset=-2 # invalid responce, dump all
         tail -c+$((offset + 2)) "$tmp" ;;
    302)
        headers 1>&2; echo 1>&2
        hdr location | xargs "$0" "$1" "$2" ;;
    *) headers 1>&2; exit 1
esac

Коли воно бачить 302, то викалупує Location: і рекурсивно кличе самого себе з новою URL. Має працювати навіть на вбогому Alpine Linux.

$ grep PRETTY /etc/os-release
PRETTY_NAME="Alpine Linux v3.19"
$ ./fetch.bash 192.168.11.109 1234 https://github.com/stunnel/static-curl/releases/download/8.6.0/curl-linux-arm64-8.6.0.tar.xz > curl.tar.xz
HTTP/1.1 302 Found
Location: https://objects.githubusercontent.com/…
…

$ file curl.tar.xz
curl.tar.xz: XZ compressed data, checksum CRC64
$ tar xf curl.tar.xz
$ file curl
curl: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, stripped

xcron

Feb. 8th, 2024 18:21
henry_flower: A melancholy wolf (Default)

Знайшов dump $HOME 2008 року, який був зроблений на fbsd. Всередині побачив файла з назвою xcron. Це шелóвий скрипта, який я ув 2008 (і раніше) пускав для нагадування дій як:

% xcron 4 кофе стынет

де через 4 хвилини воно малювало X11 даялога з відповідним повідомленням.

Скрипта є настільки смішний, що заслуговує на зображення тут. Яка це була ітерація його, на жаль, залишається невідомим--про ґіта я тоді не чув, а тримати ув svn різноманітний мотлох було ліньки, тому що створювати ріпо ув svn то була Процедура. (Не пам'ятаю чи існували тоді аналоги ґітхабу для приватних ріпос; якщо і так, то грошей на них все одно не було.)

Біхолд:

xcron script from 2008

Найкращій блока--"cat all arguments", ггг

Не знаю чи може він сперечатися з шедеврами на кшталт

return (foo == "bar" ? true : false)

але мені здається що on a par з ними, так би мовити.

Скрипта, звичайно, є непридатний до сучасний лайнаксів: xmessage то була Xaw ютіліта (кінця 80х?) для створювання елементарних даялогів yes/no. Ув 2008 році користувалися нею майже ніхто, а вірогідність того що вона буде інстальована зараз є 0.

З графічними аналогами dialog(1) за цей час ліпше також не стало, їх популярність залишається дуже низька. Tk помер, байдінґами до gtk/qt ув ruby/python користуються 10 людей на планеті Земля.

Можна було би кликати термінального емулятора:

 $ xterm -fs 20 -e 'whiptail --msgbox KURWA\ BÓBR 0 0'

але по-1ше, xterm буває тільки у поціновувачів середовищ вільних від середовищ стільниці (страшно далєкі оні от народа), по-2ге, портабельного поняття діфолтний термінала не існує--ув кожному середовищі стільниці він є свій зі своїм механізмом з'ясування який саме є улюбленцем користувача. Можна тримати таблицю "éкзек'ютабл аргументи-для-зміни-шрифта", але це є забагато клопоту задля такої дрібниці.

Або:

  • ембедити маленьку C gtk4 програму (яка малює даялога) та компілювати її кожного разу перед закінченням таймера;

  • переписати скрипта на расті з використанням якогось GUI тулкіта типу Slint (який лінкується статично) та розповсюджувати байнарника;

  • писати повідомлення ув $(mktemp /tmp/XXXXXX.html) та кликати бовзера через xdg-open;

  • через D-Bus кликати метода org.freedesktop.Notifications.Notify на віддаленому об'єкті демона нотіфікацій. Кожне середовище стільниці останнього має своє, але API є стандартизований.

Варіянт 2024 з ді-басом:

#!/bin/sh

set -e
eh() { echo "$*" 1>&2; exit 1; }

command -v notify-send >/dev/null || eh no notify-send
[ $# -ge 2 ] || eh "Usage: xcron N msg ..."
sec=$1; [ "$sec" -ge 0 ] 2>&- || eh "Invalid int: $sec"; shift
summary=`ngettext "1 minute is up" "$sec minutes are up" "$sec"`

(
    sleep $((60*sec))
    notify-send -i modem -- "xcron: $summary" "$*"
) &
henry_flower: A melancholy wolf (Default)

Шо тут відбувається? (Підказка: ніякого кроскомпайлінґу)

$ make
cc -std=c17 -Wall -Wextra -Wpedantic -O2    flperc.c   -o flperc
$ readelf -h flperc | grep Machine
  Machine:                           Advanced Micro Devices X86-64

$ rm flperc
$ nspawn-run ~/vm/systemd/debian64-aarch64 make
cc -std=c17 -Wall -Wextra -Wpedantic -O2    flperc.c   -o flperc
$ readelf -h flperc | grep Machine
  Machine:                           AArch64

nspawn-run--невеликий врапер (21 LOC шелóвих) над лайнаксним systemd-nspawn, який з'явився того ж року що і докер (2013), але не став популярним.

Врапер на федорі пускає systemd-nspawn над контейнером--директорією з aarch64 деб'яновським дістро і тоді

  • байндить $HOME до контейнера, в якому заздалегідь було

    • створено користувача з таким самим ім'ям/uid/gid як на федорі;
    • встановлено gcc/make/&c.
  • перед виконуванням make, змінює директорію ув контейнері на таку саму як поточна ув федорі.

Все виглядає так, ніби $HOME був на окремому драйві, який переставили до іншої воркстейшон (яка має CPU іншої архітектури). Там запустили make, потім зе драйва повернули на місце.

(Нового тут є 0, все це давно роблять з докером, але треба же чимось виділятися.)

aarch64 gcc ув контейнері працює на федорі x86_64 через

$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64-static
flags: OCF
offset 0
magic 7f454c460201010000000000000000000200b700
mask ffffffffffffff00fffffffffffffffffeffffff

Я донедавна на qemu-*-static не звертав уваги, аж поки випадково не виконав aarch64 éкзек'ютабл на федорі (рєзультатом чєґо бил нєобичайно удівльон). Загалом, binfmt_misc мені ніколи не подобався з точки зору безпеки, але задля перетворювання на льоту інструкцій aarch64→x86_64, такі записи binfmt_misc вирішив не видаляти.

Команда

$ nspawn-run -b ~/vm/systemd/debian64-aarch64

запустить контейнера як псевдо-віртуальну мошину, яка всередені має systemd з (я вибачаюся) підом 1, але яка з федори виглядає ось так:

На відміну від стародавнього chroot'у, процеси хоста воно не бачить. Хост навпаки, бачить геть усе, а systemd навіть вміє оперувати над таким контейнером:

$ machinectl
MACHINE          CLASS     SERVICE        OS     VERSION ADDRESSES
debian64-aarch64 container systemd-nspawn debian 12      -

$ sudo systemctl -M debian64-aarch64
...
$ sudo journalctl -M debian64-aarch64
...

Шкода що qemu-aarch64-static підтримує не всі лайнаксні сісколи, і деякі речі не працюють взагалі:

$ nspawn-run ~/vm/systemd/debian64-aarch64 strace -e t=uname arch
/usr/bin/strace: test_ptrace_get_syscall_info: PTRACE_TRACEME: Function not implemented
/usr/bin/strace: ptrace(PTRACE_TRACEME, ...): Function not implemented
/usr/bin/strace: PTRACE_SETOPTIONS: Function not implemented
/usr/bin/strace: detach: waitpid(12): No child processes
/usr/bin/strace: Process 12 detached

Хто хоче зробити собі те саме, то archwiki має інструкцію для debootstrap, а врапер є осьо нижче:

#!/bin/sh

usage() {
    echo Usage: nspawn-run [-rb] [-d dir_to_bind] container_dir [cmd] 2>&1
    exit 1
}

dirs_to_bind=$HOME
bind=--bind
boot="-u $USER"
while getopts rbd: opt; do
    case $opt in
        r) bind=--bind-ro ;;
        b) boot=-b ;;
        d) dirs_to_bind="$dirs_to_bind $OPTARG" ;;
        *) usage
    esac
done
shift $((OPTIND-1))

[ -d "$1" ] || usage; container=$1; shift
[ 0 = $#  ] || boot="$boot -a"

dirs_to_bind=`printf -- "$bind %s " $dirs_to_bind`
exec sudo systemd-nspawn -q -D "$container" $dirs_to_bind \
     --chdir="$PWD" $boot "$@"

-d /foo -d /bar додає екстра директорії, окрім $HOME; -r монтує їх зсередини контейнера read-only.

henry_flower: A melancholy wolf (Default)

На жаль, це не про двигуни. Тобто про двигуни, але не ті.

Вікіпідійна сторінка аркхайвних форматів ув таблиці Compression only має назви яких я не чув: rzip (шо?), snappy (га?) і деякі інші, про які чув, але користувався ніколи.

Може я missing out?? Протестувавши їх 9 штук можу повторити банальне спостереження про пробіг, що може відрізнятися.

Наприклад, для 15MB éкзек'ютаблу:

$ ./comprtest /usr/libexec/gdb | tee small.txt
szip             0.07  44.73          8617411
gzip             0.60  61.17          6054706
compress         0.16  44.36          8675673
bzip2            0.97  63.54          5684994
rzip             1.15  64.00          5613217
lzip             5.71  70.52          4596226
lzop             0.03  47.27          8220932
xz               5.45  70.58          4586452
zstd             0.06  61.42          6015779

де стовпчик

  • 2--час ув секундах;
  • 3 це 100(1-compressedorig) , тобто чим вищий %, тим є ліпший результата;
  • 4--фінальний розмір.

або барчартом:

$ sort -nk3 small.txt | cpp -P plot.gp | gnuplot -persist

comprtest--пролетарський шелóвський скрипта, який ув лупі годує або всі 9ти аркхайвери, або лише ті, які користувач вказав:

$ ./comprtest /etc/services xz bzip2
xz               0.18  84.65           107724
bzip2            0.03  81.24           131627

Нічого екстра-цікавого він не містить, окрім хфункції cc(), від якої у мене палає:

#!/bin/sh
# shellcheck disable=2016,2086,2068
set -e

archivers='szip gzip compress bzip2 rzip lzip lzop xz zstd'
input=${1:?Usage: comprtest file}; shift
isize=`wc -c < "$input"`

cc() { for c; do command -v $c>/dev/null || { echo no $c 1>&2;return 1;}; done;}
cc time ${@:-$archivers}
tmp=`mktemp -u tmp.XXXXXX`
trap 'rm -f $tmp' 0 1 2 15

for c in ${@:-$archivers}; do
    echo $c
    case $c in
        szip ) args='< "$input" > $tmp' ;;
        rzip ) args='-k -o $tmp "$input"' ;;
        *    ) args='-c "$input" > $tmp'
    esac

    eval "time -p $c $args" 2>&1 | awk '/real/ {print $2}'
    osize=`wc -c < $tmp`

    echo $isize $osize | awk '{print 100*(1-$2/($1==0?$2:$1))}'
    echo $osize
    rm $tmp
done | xargs -n4 printf "%-8s  %11.2f  %6.2f  %15d\n"

Чому ув всіх борн-лайк шелах, окрім башу, команда type друкує помилки на stdout? Замість мінімального

type a b c >/dev/null

який поверне 0, якщо всі з a, b, c є присутні (або відріпортує на stderr якщо хтось не знайшовся і поверне >= 1), треба писати ось цей маразм:

cc() { for c; do command -v $c>/dev/null || { echo no $c 1>&2;return 1;}; done;}

Підозрюю все почалосі з ash, з якого виросли більшість опенсоурсних шелів. Навіть busybox скопіював то майже verbatim, я спеціяльно подивився.

До речі, якщо хтось знає як написати cc() коротше за 80 символів, буду вдячний.

Добро. (Не буде.) Барчарта генерується простим пенсільванісійським ґнуплотом, дейта якому віддає cpp (ґнуплот не бажає зберігати stdin собі до буфера і на графіку з 2ма абсцисами з stdin отримує інфаркта дупи).

$ cat plot.gp
$data <<E
#include "/dev/stdin"
E
set key tmargin
set xtics rotate by -30 left
set y2tics
set ylabel "Seconds"
set y2label "%"
set style data histograms
set style fill solid
plot $data using 2 axis x1y1 title "Time", \
     "" using 3:xticlabels(1) axis x1y2 title "Space saving"

На xml-файлах запхнутих до .tar розміром 314MB, xz програє rzip'у по всім параметрам, а швидкість lzop'у вражає:

$ ./comprtest ~/Downloads/emacs.stackexchange.tar
szip             1.78  63.70        119429565
gzip             7.19  77.59         73724710
compress         4.06  67.17        108015563
bzip2           21.27  83.36         54751478
rzip            17.51  85.93         46304199
lzip           120.57  85.06         49151518
lzop             0.69  63.63        119667058
xz             125.91  85.55         47559464
zstd             1.35  79.40         67766890

Ув якості домашнього завдання, можете додати перевірку до comprtest чи є foo та bar

$ ./comprtest file foo bar

ув змінній archivers.

henry_flower: A melancholy wolf (Default)

Який можна створити лайнаксом аркхайв, такий щоб (а) його нічим було відкоркувати з-під віндюка (без wsl2 або сіґвіну), але (б) який можна прочитати ув fbsd та маку?

Ув старі добрі часи було достатньо cpio, але зараз ув кожного є інстальований 7-зіп, а як ні, то віндюка має bsdtar і такий фокуса більше не працює.

7-зіп читає навіть образи дискóві заформатовані ув ext4, тобто

$ truncate -s 10M file.img
$ mkfs.ext4 file.img
$ sudo mount -o loop file.img /десь
$ sudo cp шось /десь
$ sudo umount /десь

допоможе ніяк. Так само буде з fat, udf та hfsplus.

Але це працює з mkfs.btrfs! 7-зіп каже що воно "Cannot open file 'foo.btrfs' as archive", а ув fbsd мені вдалося замонтувати його через lklfuse (з пекеджу fusefs-lkl).

Мінімальний розмір фс там має бути 114,294,784 байт, що є трохи занадто, але з bzip2, наприклад, фс та пейлоад з 2323 байтів перетворюється разом на 7214 байт.

Осьо він: hello.btrfs.bz2. Всередині є скрипта, який той образ дискóвий згенерував (ув деб'яні йому треба btrfs-progs і libarchive-tools), та побажання українське від щирого серця.

henry_flower: A melancholy wolf (Default)

Коли хтось раптом потребує статичного HTTP сервера для тестів, йому зазвичай рекомендують python -m http.server 8000; колись навіть список таких команд був для багатьох мов.

Закінчується це зазвичай так: людина або цим користується, або починає їхати дахом--писати для себе статичний сервер на awk, на netcat, на whitespace чи brainfuck. Кількість божевільних серед уйоб погромістів загалом вражає і чим простіша задача, тим більше часу вони їй приділяють.

Примітивність an ad hoc http static server in your current directory також є очевидна: цю іграшку викидають як тільки з'являється потреба тестувати сценарії реального життя, наприклад CORS чи клаентську аутентифікацію з сертифікатом. Приклад з пáйфоном не дозволяє навіть перевірити TLS хендшейка.

Чому ув якості http server one-liner не використовувати справжні сервера? Тому що жоден з них (окрім Caddy) такого використання не передбачає, а вагон файлів ув /etc, які очікує хеві-д'юті сервер, його сістемді юніти та мотлох ув /run на спроби не надихають.

Пригадуєте як працює опач з

$ lol() { curl -sL "$1" | nokogiri -e "p \$_.css('$2').size"; }
$ lol https://httpd.apache.org/docs/2.4/mod/directives.html li
733

дайрективами ув

$ lol https://httpd.apache.org/docs/2.4/mod 'dt a'
133

модулях?

Щоб стартувати йому потрібно окрема директорія, в якій він буде шукати конфігурацію, плюватися логами та записувати підфайла.

Сценарій: користувач інсталює опач звичним методом dnf install httpd, &c, а чи використовує він його за призначення на порту 80, нас не цікавить. Той самий системно-інстальйований опач можна змусити читати інший файл конфігурації, слухати на іншому порту і конфліктувати зі системною конфігурацією ніяк.

На жаль, це потребує скриптування, але такий скрипт може служити все життя, тому що нові http сервера з'являються та щезають кожен рік, а опач є вічний.

Я спочатку написав наївний шелівський враппер ув федорі, який робив темпоральну директорію, куди копіював крихітний httpd.conf. Все запрацювало, але коли я спробував той враппер на деб'яні, то на деякий час закрив обличчя руками.

Кожного разу 1 і те саме: мейнтейнер вважає що він знає краще, ніж апстрім. Сабсета модулів які .so, деб'ян компайлить ін (щоб однакові конфіґі на різних ОС працювали ніколи), ім'я дефолтного конфіґу є свое (звичайно), éкзек'ютабл називається опач2 замість httpd (чому ні).

Всі завжди з цим погоджуються, тому шо інакше не дай бог запишуть ув ко-мейнтейнери а це я вибачаюсь! Не для того штани ув фаанґ просиджувати щоб майнтейнерить якийсь пекедж як лох, най 15-річний румун за опачем слідкує, ув перерві між алгеброю та фізикою.

(Вибачте.)

З цікавості подивився на fbsd. Не настільки погано, але всі шляхи до модулей, наприклад, з лайнаксом не співпадають.

Поліпшена версія враперу читає конфігурацію з json.

$ find * -type f | xargs wc -l
  30 conf/httpd.conf
  52 httpd-test
  26 os.json
 108 total

це є забагато щоб постити тут, але ось вам svg (чув що ув журналах, які давно збанкрутували, колись друкували вихідний код програм, який читач мав вдома самотужки ввести):

$ httpd-test ~/Downloads
ID            fedora
log           tail -f /tmp/tmp.7PTHVzUhE5/logs/access_log
conf          /tmp/tmp.7PTHVzUhE5/conf/httpd.conf
TypesConfig   /etc/mime.types
DocumentRoot  /home/alex/Downloads
Listen        127.0.0.1:8000

До речі, всі 133 модуля опачу можна йому скомпайлити статично (або вибірково, лише ті що потрібні: configure --enable-modules=none --enable-mods-static='auth_basic …'). Чому це не використовують усіляки ембедні дівайси замість пародій tiny/micro/nano-something, у яких тече пам'ять і які половину слів http/1.1 не знають, залишається незрозумілим.

tcplol

Jan. 8th, 2024 06:04
henry_flower: A melancholy wolf (Default)

ncat -lk -e cmd 127.0.0.1 8000 з попереднього посту, яке може слухати на 127.0.0.1:8000 і форкати cmd (під'єднуючи діскріптора 0 (stdin) cmd до сокету для для читання та діскріптора 1 (stdout) для писання ув нього), не є чимось оригінальним: саме так працював супер-пупер-сервер inetd або Бернштайновський tcpserver.

Steps performed by inetd

(Кожного разу коли хтось посилається на Бернштайна, я згадую його подорожній нарис як його труїли газом ув готелі СПб літом 2000 року.)

З появою т.з. сокет октивації ув systemd, деякі контори (ойбіем) перестали (x)inetd поставляти взагалі: користувачам залишили або переписування файлів конфігурації inetd ув юніти systemd, або плигання на крижину убунту.

Корисний аспекта від цього був у тому, що супер-пупер-сервер inetd хоча міг зміювати юзера у форку, сам запускався під рута і конфігурацію тримав ув /etc (що вимагало права адміністратора для ігр зі своїми експериментальними сервісами), а systemd запровадив режима --user і міг шукати юніти ув ~/.config/systemd/user/.

Наприклад, складний мережевий сервіс, який питає ім'я клаенту і вітається з ним:

$ cat hello.sh
#!/bin/sh
uname 1>&2
while [ -z "$name" ]; do
    printf "What is your name? "
    read -r name || exit 1
done
echo "Hello, $name!"

можна писати з 0м рядків коду які оперують сокетами, а замість того додати 2 юніт файла:

$ cat ~/.config/systemd/user/hello.socket
[Unit]
Description=hello.sh socket

[Socket]
ListenStream=12345
Accept=yes

[Install]
WantedBy=sockets.target

та

$ cat ~/.config/systemd/user/hello@.service
[Unit]
Description=hello.sh

[Service]
ExecStart=-/home/alex/lib/software/example/ruby/fork/hello.sh
StandardInput=socket

Після чого:

$ systemctl --user daemon-reload
$ systemctl --user start hello.socket
$ ncat 127.0.0.1 12345
Linux
What is your name? Dude
Hello, Dude!

1й рядок з "Linux" з'явився тому що systemd по-замовчуванню під'єднує діскриптора 2 до сокету також, що я вважаю неввічливим.

Замість ncat тут краще перевіряти socat'ом, т.я. ncat не закриває з'єднання після того як systemd закрив сокета і продовжує висіти ув CLOSE_WAIT:

$ netstat -4an | grep 12345
tcp    0  0 127.0.0.1:34552     127.0.0.1:12345     CLOSE_WAIT

(Так само дивно поводиться nc ув busybox; що робить nc з bsd перевіряти лінь.)

Кому подобається писанина з ini-файлами є ув захваті. Всім іншим залишається журитися та писати форкові або тредові сервери вручну.

Емулювати ncat -lk можна також на Рубі, де аналога вдається втиснути ув 37 рядків коду. Той самий діалог

$ socat - TCP4:127.0.0.1:8000
What is your name? Dude
Hello, Dude!

Зі сторони сервера виглядає так:

$ ./tcplol -v -p 8000 ./hello.sh
Client 127.0.0.1:36512
Linux
Client 127.0.0.1:36512, pid 42528: disconnect

stderr під'єднується до сокету тільки з опцією -2.

$ cat tcplol
#!/usr/bin/env ruby

require 'optparse'
require 'socket'

usage = 'Usage: tcplol [-2v] [-h 127.0.0.1] -p 1234 program [args...]'
opt = ARGV.getopts "h:p:2v"
abort usage unless opt['p'] && ARGV[0]
$VERBOSE = nil unless opt['v']

at_exit do
  Thread.list.filter {|t| t != Thread.current}.each do |t|
    warn "\n" + "Waiting for " + t[:cid]
    t.join
  end
end

server = TCPServer.new opt['h'] || '127.0.0.1', opt['p']
loop do
  begin
    client = server.accept
    cid = client.remote_address.ip_unpack.join ':'
  rescue
    warn "Client error: #{$!}"
    next
  end

  warn "Client #{cid}"
  pid = fork do
    $stdin.reopen client
    $stdout.reopen client
    $stderr.reopen client if opt['2']
    client.close
    exec(*ARGV)
  end
  client.close
  cid += ", pid #{pid}"
  Thread.new(cid, pid) do
    Thread.current[:cid] = cid
    Process.wait pid
    warn "Client #{cid}: disconnect"
  end
end

Щоб не залишати зімбі нам доводиться створювати окремого треда для кожного форку. at_exit потрібен лише коли tcplol спілкується з клаентами, але отримує щось на кшталт SIGINT.

Єдина цікава деталь тут є ув чеканні на іксепшона поряд з server.accept. Не знаю чому, але я вважав що лайнаксне ядро деталі TCP handshake ховає та унеможливлює його ламання з сокетного інтерхфейсу (але не з raw sockets, звичайно). Еге. Як прибрати begin…rescue, tcplol гепнеться на спробі дізнатися адресу хоста клаента-злодія, якщо ув tcplol штирнути nmap'ом:

$ nmap -sT -p 8000 127.0.0.1
henry_flower: A melancholy wolf (Default)

Щоб стрімати якесь .mp4 чи .mkv, достатньо будь-якого статичного HTTP сервера, який підтримує range requests. Тоді будь-який бовзер або mpv чи vlc зможе грати кєно з seeking (як це українською?), як буде дозволяти пропускна здатність і майже так гарно, ніби .mkv є локальний файла.

Колись нарід писав спеціяльні сервери для стрімінгу, бороли лейтенсі, оптимізували кійфреймні інтервали, але все це швидко померло як тільки веб бовзери отримали <video> елемент.

Якщо десь на хмарній VM ув країні, якій все одно на те що ви торентите, є колекція святкових фільмів, то дивитися її можна сказавши на сервері

$ ruby -run -ehttpd . -b 127.0.0.1 -p 8000

а дома, на клаенті:

$ ssh -L 127.0.0.1:12345:127.0.0.1:8000 -Nv example.com

щоб форвардити з клаенту 127.0.0.1:12345 на сервер example.com HTTP ріквести і читати відповідь, а потім

$ mpv http://127.0.0.1:12345/movie.mkv

щоб кєно дивитися.

Як зробити щось подібне без HTTP серверу?

mpv вміє читати raw TCP потік. Можна просто чекати підключення на якогось порта та зразу відправляти йому .mkv файла. Але тоді mpv з'їсть увесь bandwidth який є, тому наш сервер ліпше має рейт лімітувати потік байтів.

Спочатку я хотів написати елементарний форк-сервер, але згадав про pv та ncat. З цим дуо достатньо пролетарського шелу:

$ cat mickeymousetube
#!/bin/sh

export movie="${1:?Usage: ${0##*/} file.mkv [port]}"
port=${2:-61001}
type pv ncat || exit 1

__dirname=$(dirname "$(readlink -f "$0")")
ncat -vlk -e "$__dirname/libexec.stream.sh" 127.0.0.1 $port

ncat вміє форкати себе та виконувати інший exe, коли клаент підключається.

$ cat libexec.stream.sh
#!/bin/sh
pv -L2M "$movie"

-L2M тут означає макс 2МБ за секунду.

$ mpv tcp://127.0.0.1:61001

буде файла грати.

Недоліком цього є неможливість seeking на клаенті та якщо щось станеться з сокетом (сусід посвердлить вам інторнет кабеля), то дивитися кєно доведеться з самого початку.

З іншої сторони, якщо робити конкурента тіктоку або ютубних шортс, то seeking там є непотрібно, контін ґєнта таких сервісів його відсутності не помітить.

henry_flower: A melancholy wolf (Default)

Колись на початку 2000х були популярні сервіси віртуальних откриток, де були колекції чиїхось котів с бльосткамі і формою для поздоровлень.

Зараз емулятор терміналу є на будь-якій ОС майже зразу після її інсталяції. Можна штампувати текстові поштівки, які будуть працювати будь-де. Наприклад:

$ fold -w40 hello
tail -c+37 "$0"|base64 -d|gzip -cd
#H4sIAC1EjWUAA62TzQqDMAyA7z71/tA5GLLbdjK
pwthtJ90Y2HfJk8ymrbTaThlCkDQ/X9I0UiVpDUl
mIlASbJSIz2JQnyNGlJIRQHgIsPx4B6Qrjyna0rL
36IH8+DgIMj521sJH8foDxJeKt7AMhJP6ypiuBcq
WgaqaJQ4CH+TGh/bobktFxHi7UWJoIYNbY5oqmPK
cuuY2+4cgf4ULwoLEmwvmtnJOwBY8KwVKEg+q+nZ
uvNC5SoELYUNw0rmJGa1amdrMEvYEV3M79a/tWNc
FUhJbNSDzGji8ib5aa4fXsN5YY8PSWX1wSUfXATL
5AjV3WkpHBAAA

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

$ ./hello
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⡿⢿⣿⣿⣿⣿⡟⠀⢠⡄⠀⢻⣿⣿⣿⣿⡿⢿⣿⣿⣿
⣿⣿⣿⠀⠀⣿⣿⣿⡟⠀⢀⣿⣷⠀⠈⣿⣿⣿⣿⠀⠀⣿⣿⣿
⣿⣿⣿⠀⠀⣿⣿⣿⠇⠀⣾⣿⣿⣇⠀⢸⣿⣿⣿⠀⠀⣿⣿⣿
⣿⣿⣿⠀⠀⣿⣿⣿⠀⢠⣿⣿⣿⣿⠀⠀⣿⣿⣿⠀⠀⣿⣿⣿
⣿⣿⣿⠀⠀⣿⣿⡇⠀⢸⣿⣿⣿⣿⡆⠀⣿⣿⣿⠀⠀⣿⣿⣿
⣿⣿⣿⠀⠀⣿⣿⡇⠀⢸⣿⣿⣿⣿⡇⠀⣿⣿⣿⠀⠀⣿⣿⣿
⣿⣿⣿⣤⣤⣿⣿⡇⠀⢸⣿⣿⣿⣿⠇⠀⣿⣿⣿⣤⣤⣿⣿⣿
⣿⣿⣿⣿⣿⣧⠀⠀⣀⣀⣀⣀⣀⣀⣀⣀⠀⠀⣾⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⡄⠀⢻⣿⣿⣿⣿⣿⣿⠏⠀⣰⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣿⢿⣿⣿
⣿⡏⢹⣿⠉⣿⣿⠉⠹⣿⡍⠹⠟⢩⣏⠙⡿⢉⡏⠑⡶⠊⢹⣿
⣿⡇⢠⣤⠀⣿⠃⠘⠀⢻⡿⠂⠀⢿⣿⠆⢁⣾⡇⠀⢡⠀⢸⣿
⣿⣷⣾⣿⣶⣷⣶⣿⣷⣶⣶⣾⣷⣶⣷⣶⣿⣿⣷⣶⣿⣶⣾⣿

(Цей ascii art я знайшов на гітхабі.)

Я би залишив результат gzip'у ув коментарі і так, без base64, але тоді у bash стається інфаркт, коли він бачить '\0'. Shebang, якщо хтось забув, у багатьох випадках є необов'язковим.

hello був згенерований ось цим скриптом, який читає повідомлення для користувача з stdin:

#!/bin/sh

prefix() { printf 'tail -c+37 "$0"|base64 -d|gzip -cd\n#'; }

script=`mktemp`
trap 'rm -f $script' 0

prefix > "$script"
gzip -c | base64 -w0 >> "$script"
chmod +x "$script"
mv "$script" "${1:-hello}"

Можна піти ще далі та генерувати секретну поштівку яка вимагає пароля для показу поздоровлення. На жаль, у такому випадку, гарантія на присутність потрібної утілити (openssl) є відсутня. Є варіянт з xor cipher'ом на awk, чи з тасканням вихідного коду якогось chacha20 на C (але тоді отримувач має мати компілятора), або з вбудованим ув скрипта αcτµαlly pδrταblε εxεcµταblε, який буде байти розшифровувати.

Про тулчейна Cosmopolitan Libc чули майже всі під час кетайської чуми, але я не бачив щоб хтось його використовував.

Наприклад, мінімальна реалізація rc4:

#define S ,t=s[i],s[i]=s[j],s[j]=t /* rc4 hexkey <file */
unsigned char k[256],s[256],i,j,t;main(c,v,e)char**v;{++v;while(++i)s[
i]=i;for(c=0;*(*v)++;k[c++]=e)sscanf((*v)++-1,"%2x",&e);while(j+=s[i]
+k[i%c]S,++i);for(j=0;c=~getchar();putchar(~c^s[t+=s[i]]))j+=s[++i]S;}

компілюється ув такий exe

$ file rc4
rc4: DOS/MBR boot sector; partition 1 : ID=0x7f, active,
start-CHS (0x0,0,1), end-CHS (0x3ff,255,63), startsector 0, 4294967295 sectors
$ du rc4
404K    rc4

який запускається на лайнаксі (x86_64 чи aarch64) або fbsd (має також на маку, але я не перевіряв).

Тоді секретна поштівка буде виглядати так:

  1. байнарі rc4 пхається ув коментар;
  2. ув наступному коментарі--зашифроване поздоровлення;
  3. скрипта слайсить потрібні діапазони самого себе ($0) щоб видобути пп. 1-2;
  4. питає пароля, розшифрофує текта.

Щоб згенерувати це, простіше всього використати якийсь templating engine, наприклад рубівський erb:

$ cat message.erb
#<%= rc4=File.read(rc4) %>
#<%= message=File.read(message) %>
slice() { tail -c+$1 "$0" | head -c $2 | base64 -d | gzip -cd; }
cleanup() { rm -f "$rc4"; stty echo; }
set -e
rc4=`mktemp`
trap cleanup 0
trap 'cleanup; echo 1>&2; exit 1' 1 2 15
slice 2 '<%= rc4.length %>' > "$rc4"
chmod +x "$rc4"
stty -echo
printf 'Password: ' 1>&2; read -r password; printf "\n" 1>&2
[ -n "$password" ]
hexkey=`printf '%s' "$password" | od -A n -t x1 -v | tr -d ' \n'`
slice '<%= rc4.length+4 %>' '<%= message.length %>' | "$rc4" $hexkey

erb пускається з параметрами rc4= та message=

$ erb rc4=rc4.text message=message.text message.erb

де *.text є файли з підготовленим base64.

Мейкфайла:

out := _out
password := monkey
message := hello1.txt
CC := ~/opt/s/cosmocc/bin/cosmocc

all: $(out)/message

$(out)/%: %.c
    @mkdir -p $(dir $@)
    $(CC) --std=c89 -o $@ $<

$(out)/message: message.erb $(out)/rc4
    gzip -c < $(out)/rc4 | base64 -w0 > rc4.text
    $(out)/rc4 $(hexkey) < $(message) | gzip -c | base64 -w0 >message.text
    erb rc4=rc4.text message=message.text $< > $@
    chmod +x $@
    rm *.text

.DELETE_ON_ERROR:
hexkey = $(shell printf '%s' $(call se,$(password)) | od -A n -t x1 -v | tr -d ' \n')
se = '$(subst ','\'',$1)'

Космополітанський тулчейна очікується ув ~/opt/s/cosmocc/.

$ make password="всім нам" message=hello2.txt
...
$ du _out/message
304K    _out/message

Так, 304KB здається трохи забагато, але по сьогоднішнім стандартам це є ніщо.

$ _out/message
Password:
               ▁
              ▃ ▅▖
             ▘    ▄
    ┏▃      ▌  ▘▌  ▖     ▃▄▌
    ▎ ▌    ▌  ▌  ▖ ▊    ▊  ▎
    ▎ ▌    ▎ ▗   ▌  ▌   ▊  ▎
    ▎ ▌   ▌  ▌    ▎ ▊   ▊  ▎
    ▎ ▌   ▌ ▊     ▊  ▎  ▊  ▎
    ▎ ▌   ▎ ▌     ▌  ▎  ▊  ▎
    ▎ ▌  ▊  ▌     ▊  ▌  ▊  ▎
    ▎ ▌  ▊  ▌     ▊  ▌  ▊  ▎
    ▅▅▘▂▂▂▎ ▊▂▂▂▂▂▂  ▂▂▂▖▅▅▘
       ▌  ▁▁▁▁▁▁▁▁▁▁▁   ▎
       ▝▖ ▝         ▌  ▗
        ▝▄▄▃       ╺▄▄▄
                         ▗▂▂▄
 ▊▅▌ ┏▖  ▃▅▖ ▄▅▄▃▅╸▄▄▁▃▄▌▅▌▗▅▖
 ▊ ▆▆ ▊ ▗ ▄ ▖ ▊  ▌ ▝▖▅▗▘▊ ▄▗ ▌
 ▊▂▌ ▂┛▌▂▅▅▂▂▄▂▅▃▂▖▄▂▄  ▌▂▄▊▂▌

Бовзери це рендерять погано.

henry_flower: A melancholy wolf (Default)

Як ви пишете 25м3? Ув маркдауні це виглядає рівно так, як можна очікувати:

25м<sup>3</sup>

Деякі редактори (імакс) дозволяють друкувати superscript цифри; наприклад для надрядкового 2 треба віртуозно натиснути C-x 8 ^ 2. Надрядкову a так надрукувати не вийде--замість неї з'явиться літера â.

Юнікод мав би мати надрядкові та підрядкові симболи для кожної абетки, але цього не сталося. Присутній сабсет--для, наприклад, латини та безнадійної кирилиці--розмазан по випадкових місцях. Якщо підрядкові цифри можна отримати знаючи офсета, то для абеток це працювати не буде: e.g., підрядкова літера j сидить ув секції Latin Extended-C, втиснута поміж загадковою фінно-угорською (яка дуже нагадує мотематичний квантор існування) та надрядковою великою V.

Юнікод ніби має спеціяльну секцію Superscript and Subscripts, але вона виглядає як поганий жарт:

Superscript and Subscripts

(Пусті світлі клітинки це не помилка рендерінґу симбола, а "not assigned" місця.)

Врешті-решт, якщо ретельно пошукати, знайти локацію більшості (не всіх) над/підрядкових літер можна, і тоді написавши елементарний скрипт що робить заміну звичайних літер на над/підрядкові, будь-який пристойний редактор дозволить замінити виділений текста на результат роботи скрипта.

Це є звичайно, цікаво, але було би цікавіше виділяти маркапний варіянт де трансформувалися би лише елементи <sup> чи <sub>, наприклад для хформули y = x2a:

Працює це так. 1й скрипт робить заміну відповідних симболів:

$ lsb_release -d | ./supsub sub
dₑₛ𞁞ᵣᵢₚₜᵢₒₙ:    fₑdₒᵣₐ ᵣₑₗₑₐₛₑ ₃₉ ₍ₜₕᵢᵣₜᵧ ₙᵢₙₑ₎
$ lsb_release -d | ./supsub sub | ./supsub invert
ᵈ𞀵ˢ𞀿ʳⁱ𞀾ᵗⁱ𞀼ⁿ:    ᶠ𞀵ᵈ𞀼ʳ𞀰 ʳ𞀵ˡ𞀵𞀰ˢ𞀵 ³⁹ ⁽ᵗʰⁱʳᵗʸ ⁿⁱⁿ𞀵⁾
$ lsb_release -d | ./supsub sub | ./supsub invert | ./supsub restore
dеsсrірtіоn:    fеdоrа rеlеаsе 39 (thіrty nіnе)

(Рендерінґ бовзерами бажає залишати кращого.)

$ cat supsub
#!/usr/bin/env -S ruby --disable-gems

db = DATA.read.split(/\s+/).filter {|v| v}
$supa = db.map {|v| [v[0], v[1]] }.to_h
$sub = db.map {|v| [v[0], v[2]] }.to_h

mode = 'sup|sub|invert|restore'
abort "Usage: supsub #{mode} < file.txt" unless ARGV[0] =~ /^#{mode}$/

def tr mode, chars
  case mode
  when 'sup'
    chars.map { |ch| $supa[ch.downcase] || ch }.join
  when 'sub'
    chars.map { |ch| $sub[ch.downcase] || ch }.join
  when 'invert'
    supa_v = $supa.invert
    sub_v = $sub.invert
    chars.map do |ch|
      if supa_v[ch]
        $sub[supa_v[ch]] || ch
      elsif sub_v[ch]
        $supa[sub_v[ch]] || ch
      else
        ch
      end
    end.join
  else # restore
    supa_v = $supa.invert
    sub_v = $sub.invert
    chars.map { |ch| supa_v[ch] || sub_v[ch] || ch}.join
  end
end

while (line = STDIN.gets)
  print tr(ARGV[0], line.chars)
end

__END__
0⁰₀ 1¹₁ 2²₂ 3³₃ 4⁴₄ 5⁵₅ 6⁶₆ 7⁷₇ 8⁸₈ 9⁹₉ +⁺₊ -⁻₋ =⁼₌ (⁽₍ )⁾₎
aᵃₐ bᵇb cᶜ𞁞 dᵈd eᵉₑ fᶠf gᵍg hʰₕ iⁱᵢ jʲⱼ kᵏₖ lˡₗ mᵐₘ nⁿₙ oᵒₒ
pᵖₚ q𐞥q rʳᵣ sˢₛ tᵗₜ uᵘᵤ vᵛᵥ wʷw xˣₓ yʸᵧ zᶻz
а𞀰ₐ б𞀱𞁒 в𞀲𞁓 г𞀳𞁔 ґґ𞁧 д𞀴𞁕 е𞀵ₑ ж𞀶𞁗 з𞀷𞁘 и𞀸𞁙 іⁱ𞁨 їїї ййй
к𞀹𞁚 л𞀺𞁛 м𞀻ₘ нᵸн о𞀼ₒ п𞀽𞁝 р𞀾ₚ с𞀿𞁞 т𞁀т у𞁁𞁟 ф𞁂𞁠 х𞁃𞁡 ц𞁄𞁢
ч𞁅𞁣 ш𞁆𞁤 щщщ ьꚝь ю𞁉ю яяя

Замінювати воно може лише цифри, симболи латини та української абетки. Сили на пошук голок ув юнікоді у мене закінчилися.

2й скрипта є трохи цікавіший:

$ echo '<sub>test</sub> <i>lol</i> <sup>haha</sup> but <sup>it works</sup>!' | ./supsub-xml
ₜₑₛₜ <i>lol</i> ʰᵃʰᵃ but ⁱᵗ ʷᵒʳᵏˢ!

За допомогою 鋸 парситься фрагменти xml та викликається попередній скрипта (supsub) коли зустрічається елемент <sup> чи <sub>. Версія 1 скрипта supsub-xml може бути такою примітивною як

#!/usr/bin/env ruby

require 'nokogiri'

cmd = ARGV[0] || File.join(__dir__, 'supsub')
doc = Nokogiri::HTML.fragment STDIN.read

doc.css('sup,sub').each do |node|
  IO.popen("#{cmd} #{node.name}", 'w+') do |t|
    t.write node.text
    t.close_write
    node.replace t.gets
  end
end

print doc.to_s

Воно працює, і, для більшості випадків його можна залишити як є, але форкання на кожну трансформацію виглядає варварським.

Стівенс в своїй APUE розповідає про coprocesses, які він описує як процесси які живуть одночасно з головною програмою (parent), і до яких parent під'єднується через 2 пайпи, які він сам створює:

Driving a coprocess

Bash підтримує коупроцеси, але використовують їх ~ніхто, тому що ⓐ нарід ув більшості не гребе шо то за коупроцеси такі, ⓑ пише майкросервіси на расті для калькуляції 6*8, ⓒ це є забута стародавня технологія предків.

Найпростіший приклад виглядає так: parent запускає звичайну утіліту tr(1), яка має робити upcase для всього, шо надходить на її stdin:

$ cat coprocess
#!/usr/bin/env ruby

parent_in, parent_out = IO.pipe
child_in, child_out = IO.pipe
spawn "tr '[a-z]' '[A-Z]'", in: child_in, out: parent_out
parent_out.close
child_in.close

child_out.puts "lol"
print parent_in.gets
child_out.puts "haha"
print parent_in.gets

На жаль, як запустити ./coprocess, ми не отримаємо LOL та HAHA: скрипта зависне ув дедлоку на рядку print parent_in.gets: tr з'їсть свій stdin і напише результат ув свій stdout, використовуючи fwrite(2) з libc (принаймі лайнаксна версія з coreutils), яка буферує результат, тому parent буде чекати на байти з пайпу вічно.

Зарадити цьому можна пустивши tr через утіліту stdbuf(1):

spawn "stdbuf -i0 -o0 tr '[a-z]' '[A-Z]'", in: child_in, out: parent_out

тоді дейта почне рухатися пайпами:

$ ./coprocess
LOL
HAHA

Цей хфінт вухами працює лише для хфункцій з libc. Рубі (як і інші мови) має свій IO механізм, тому stdbuf йому допоможе як дикобразу вогнемета.

Але вихід є: якщо змусити коупроцесс думати що той є підключений до терміналу, тоді спрощена схема роботи supsub-xml що форкає supsub буде виглядати на кшталт (NSFW):

coprocess via pty

Рубі має чудове вбудоване ікстеншона pty, яке дозволяє замість IO.pipe для parent писати PTY.open, не чіпаючи все інше.

$ cat supsub-xml
#!/usr/bin/env ruby

require 'nokogiri'
require 'pty'

cmd = ARGV[0] || File.join(__dir__, 'supsub')

class Coprocess
  def initialize cmd
    @master, slave = PTY.open
    read, @write = IO.pipe
    spawn cmd, in: read, out: slave
    read.close
    slave.close
  end

  def puts str; @write.puts str; end
  def gets; @master.gets; end
end

transforms = {
  "sup" => Coprocess.new("#{cmd} sup"),
  "sub" => Coprocess.new("#{cmd} sub")
}

doc = Nokogiri::HTML.fragment STDIN.read
doc.css('sup,sub').each do |node|
  tr = transforms[node.name]
  tr.puts node.text
  node.replace tr.gets.chomp
rescue
  warn "transforming `#{node.text}` failed: #{$!}"
end

print doc.to_s

Ув версії 2 ми створюємо 2 коупроцесси для елементів <sup> та <sub> заздалегідь і використовуємо їх як локальні майкросервіси. Якщо додати sleep ув кінець скрипта, можна подивитися як це виглядає фізично:

$ $$
bash: 42396: command not found
$ echo '<sub>test</sub> <i>lol</i> <sup>haha</sup>!' | ./supsub-xml
ₜₑₛₜ <i>lol</i> ʰᵃʰᵃ!

поки воно спить, з іншого терміналу:

$ pstree -p 42396 -al
bash,42396
  └─ruby,100032 ./supsub-xml
      ├─ruby,100033 --disable-gems ... sup
      └─ruby,100034 --disable-gems ... sub

$ file /proc/100032/fd/* # supsub-xml
...
$ file /proc/100033/fd/* # supsub sup
...
henry_flower: A melancholy wolf (Default)

Не пам'ятаю як, але натрапив вчора на Торвальдське ріпо з його текстовим редактором, яким, окрім нього, користується ніхто. Цікавого ув ріпо було небагато, якщо не рахувати файла з назвою UTF-8-demo.txt.

Кроум рендерить його майже коректно за вийнятком блоку з формулами. Імакс робить так само, але додатково інкоректно вишиковує судоґрафіку.

Близький до ідеального рендерінґ роблять xterm, gvim та gedit.

(Це є векторний скріншота, який можна зробити через завдяки за допомогою спеціяльного плагіну libgtk-vector-screenshot.so; на жаль, лише для gtk3.)

Якщо придивитися до символів, видно що все перелічені аплікації шукають ґліфи, яких немає ув поточному шрифті, деінде. На федорі, кроумівські дівелопер тулз доповідають список використаних шрифтів так:

Liberation Mono     — Local file (5,438 glyphs)
DejaVu Sans         — Local file (1,179 glyphs)
Droid Sans Thai     — Local file (415 glyphs)
Droid Sans Ethiopic — Local file (320 glyphs)
Segoe UI Historic   — Local file (45 glyphs)
Noto Sans Math      — Local file (7 glyphs)
Droid Sans Fallback — Local file (5 glyphs)
Segoe UI Symbol     — Local file (1 glyph)
Noto Color Emoji    — Local file (1 glyph)
Times New Roman     — Local file (1 glyph)

де Liberation Mono--мій основний діфолтний моноспейсний шрифт.

Я захотів перевірити чи є у мене інстальваний шрифта, який має, якщо не весь, то хоча би значний сабсет UTF-8-demo.txt. Як змусити cairo+pango+harfbuzz+fontconfig не робити font fallback я не знаю (життя є занадто коротким), але можна взяти pandoc, який по діфолту таких фокусів не знає.

Ось ця шелівська хфункція конфертує текста ув pdf:

# input output font
txt2pdf() {
    awk '{print "    " $0}' < "${1:-/dev/null}" | pandoc --pdf-engine=xelatex \
     -V "monofont:${3:-Roboto Mono}" -V "mainfont:${3:-Roboto Mono}" \
     -V geometry:"top=1cm,left=1cm,bottom=1.5cm,right=1cm" \
     -t pdf -o "${2:-${1%.*}.pdf}"
}

(Так, воно змушує pandoc думати шо інпута є маркдаун.)

Тоді після

$ txt2pdf UTF-8-demo.txt lol.pdf 'Ubuntu Mono'

видно, що контемпоральний стиль шрифта Убунту, який не лише contains characteristics unique to the Ubuntu brand, але також conveys a precise, reliable and free attitude, натрапляє на деякі труднощі, щоб не сказати вульгарніше.

ОК, отримати увесь список моноспейсних шрифтів можна за допомогою fontconfig:

$ type fc.mono
fc.mono is aliased to `fc-list :mono family | awk -F, "{print \$1}" | sort -u'
$ fc.mono
Bitstream Vera Sans Mono
Courier 10 Pitch
Courier New
Cursor
DejaVu Sans Mono
Droid Sans Mono
Inconsolata
Liberation Mono
Ligconsolata
Material Icons
Material Icons Outlined
Material Icons Round
Material Icons Sharp
Material Icons Two Tone
Nimbus Mono PS
Noto Color Emoji
Source Code Pro
Terminus
Ubuntu Mono

(Тут має бути також Roboto Mono v2, але.)

Тоді

$ (IFS=$'\n'; for fn in `fc.mono`; do txt2pdf UTF-8-demo.txt "$fn".pdf "$fn"; done)
$ ls *pdf -1
'Bitstream Vera Sans Mono.pdf'
'Courier New.pdf'
'DejaVu Sans Mono.pdf'
'Droid Sans Mono.pdf'
Inconsolata.pdf
'Liberation Mono.pdf'
Ligconsolata.pdf
'Nimbus Mono PS.pdf'
'Source Code Pro.pdf'
'Ubuntu Mono.pdf'

з яких 'DejaVu Sans Mono.pdf' має найкращий результат. Цього всього можна було не робити, а замість того спитати чатджипіті, який на питання 'Which monospace font can render all glyphs from the famous "UTF-8-demo.txt" file?' відповів 'One such font is DejaVu Sans Mono', падлюка.

Якби це був сучасний блоґ, тобто канадійський ютубний ченел як у всіх нормальний людей, я би додав "напешіть ув коментс ЯКИЙ ВАШ ШРИФТА є найюлюбленіший, ЯК У ВАС рендериться ПІДІЕФ, яку думку маєте!"

henry_flower: A melancholy wolf (Default)

Пачіняючі прімус, тобто шукаючи причину чому прибацаний файловий диспетчер KDE Dolphin не хотів копіювати файли, коли виконувався не з KDE, мені спало на думку, як би виглядав мінімальний аналог cp(1) на C, вихідний код якого <= 280 байт.

Ла спеціфікасьйон:

  • mycp src dest;
  • src та dest то є лише файли;
  • компілюватися "clang -Wall mycp.c -o mycp" має з 0 попереджень з лайнакса чи мак'ос;
  • показувати помилку коли (та вертати exit code >= 1):
    • src/dest є неприпустимі (файл не існує, є директорією, немає дозволу на читання/запис, тощо);
    • src == dest (ув мінімальному вигляді, не треба різолвіти шлях/сімлінка);
    • шось погане трапилося під час копіювання.
  • не створювати dest розміром 0 байт, якщо src є дефективне.

Версія clang у мене

$ clang --version | head -1
clang version 16.0.5 (Fedora 16.0.5-1.fc38)

Хфінальний результата виглядає ось так

$ cminify mycp.c | wc -c
280

$ cminify mycp.c | clang-format
#include <err.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(int c, char **v) {
  c - 3 || !strcmp(v[1], v[2]) ? errx(1, "?") : 0;
  int b[65536], s = open(v[1], O_RDWR), d;
  s < 0 ? err(2, "") : 0;
  d = open(v[2], O_RDWR | O_CREAT | O_TRUNC, 420);
  while ((c = read(s, b, 65536)))
    c - write(d, b, c) ? err(3, "") : 0;
}

cminify то є якийсь пáйфонівський скрипта з ґітхабу, якщо хтось знає ліпший варіянт, скажіть. Чому для clang-format немає якогось uglify стилю ув комплекті, то це є цікаве питання також.

Енівей, code golf технікс, які були використані:

  • страшенна економія ув 1 байт:

    • октал 0644 → десяткове 420
    • a==ba-b
    • if (a)b();a?b():0; (шкода що не можна опустити нуль після :)
  • кома;

  • хвункції з err.h, які не є позіксом per se, але є всюди, окрім віндюка.

Дуже дратує, що не виходить позбутися #include ... без вказання версії C якось печерного 1989 року. Замість O_RDWR і ко можна було б вказати число, але портáбільність тоді миттєво гасне.

Тести:

$ ./mycp
mycp: ?
$ ./mycp 1 1
mycp: ?
$ ./mycp /root/nope 1
mycp: : Permission denied
$ ./mycp mycp.c .
mycp: : Bad file descriptor
$ ./mycp . 1
mycp: : Is a directory
$ ./mycp BUGUS 1
mycp: : No such file or directory

1 GB мотлоху має скопіюватися без помилок:

$ head -c $((1024*1024*1024)) /dev/urandom > 1
$ ./mycp 1 2
$ cmp 1 2
$ echo $?
0

Мініфікаційна версія як гарний svg:

$ pango-view -q --font='Ubuntu Mono 24' --rotate 10 --margin=0 <(cminify mycp.c | fold -w18) -o mycp.svg

henry_flower: A melancholy wolf (Default)

Автор маловідомої мови Python, перед тим як переїхати до Гамерики, був членом пацифістської партії Голландії.

Про цікаві зв'язки цієї групи людей з комуністами ув вікіпідіа все є ретельно вимито пральним порошком, але можна знайти деталі в інших статтях:

  • The party was formed from Marxists in the left wing of the Labour Party.
  • Ще раніше існувала така собі Socialistische Werkers Partij, яка "proclaimed its solidarity with the Soviet Union", але КДБ не звертала на них увагу, тому вони хутко приєдналися до партії, в якій потім з'явився Гвідо ван Россум.

Про пейзаж 80х та свої погляди на життя Гвідо розповідає так:

"... there was a big movement in the Netherlands to kick out American weapons that were posted in the Netherlands and throughout all Western European countries as long as they were part of NATO. Because that—it wasn't always completely crystal clear that the Russians were the bad guys. It sometimes felt more like, "Well, there are two sides, and they're clearly disagreeing with each other, but it's not clear who's right." And so I never felt comfortable with the communist system, but I did feel that a socialist system was better than capitalism. And that, yeah, that probably colored some of my views on life in general."

Розумієте, тоді не завжди було зрозуміло хто є поганий, русскіє чи Нейто. Обидві сторони мали слушні думки, але Гвідо завжди здавалося що капіталізме є гірший за сосіалізме.

З Нейто загалом та гамериканською войенщиною конкретно, Гвідо боровся так:

  • ув 90х хоробро приїхав до Вірджинії та вибив ґранта на свою мову пограмування у DARPA. Щоб останні нічого не запідозрили, ґрант був про лілове "комп'ютерне пограмування для всіх".

Для прославлення соціялізму та доказу про неналежність капіталізму до сучасного світу, він:

  • приєднався до 2х каліфорнійських стартапів;
  • працював ув Гооглі та Дропбоксі;
  • наразі допомає знищити останні паростки буржуазії ув Майкрософті.

"Politically, my parents leaned left, my mother a bit more than my dad", згадує Гвідо. Його батько настільки зневажав кровожерливу Королівську Армію Нідерландів, що добровільно сів ув в'язницу, в якості альтернативи призиву.

henry_flower: A melancholy wolf (Default)

З бритишського ріпорту від Royal United Services Institute for Defence and Security Studies:

План монголів для Києва, яких їм вдалося здійснити на півдні України:

The Russian counterintelligence regime on the occupied territories had compiled lists that divided Ukrainians into four categories:

  • Those to be physically liquidated.
  • Those in need of suppression and intimidation.
  • Those considered neutral who could be induced to collaborate.
  • Those prepared to collaborate.

For those in the top category, the FSB had conducted wargames with detachments of the Russian Airborne Forces (VDV) to conduct kill-or-capture missions. In many cases, the purpose of capture was to put individuals involved in the 2014 Revolution of Dignity (often referred to as the Maidan Revolution) on trial to be executed. Although initial lists of persons in the second category existed, the approach was to be more methodical, with the registration of the population through door-to-door sweeps and the use of filtration camps to establish counterintelligence files on large portions of the population in the occupied territories. Filtration would be used to intimidate people, to determine whether they needed to be displaced into Russia, and to lay the groundwork for records to monitor and disrupt resistance networks. Over time, Russia would bring teachers and other officials from Russia itself to engage in the re-education of Ukrainians.

src

Згідно русскіх, всі, хто хотів посилення економічних з'вязків з ЄС у 2013, заслуговують смерті.

henry_flower: A melancholy wolf (Default)

Знайшов 2 коротких оповідання початку 20го століття як імігрували до канадщини.

Пішла поговірка по селах, що приїхав чоловік Бог знає звідки і хоче провадити людей до якоїсь Америки і хоче провадити людей Бог зна куди.

...

Судия каже: «Ти нарід провадиш, щоб їхав».

Я кажу: «Ні, вони сами хотять їхати».

Судия до мене: «Ти чому язик не тримав за зубами? Треба їхати самому, а не стягати других людей за собою. Ти продав нарід аґентови. Наш найяснійший цісар поміг людям вернути з Арґентини, трийцять родин на свій власний кошт, а ти хочеш, щоби цісар знову помагав, як людям щось злого станеться?»

Epub

henry_flower: A melancholy wolf (Default)

Зробив epub з мемуарів Gary Kildall'а. Це є дядько, який написав CP/M, віріянт якої купив Ґейтс у іншої контори і перейменував на DOS. GK помер у 1994, а вікіпідіа має конспіративну версію, що його було вбито.

Текст є дуже цікавий і місцями дотепний. Епізод, де його батько відмазав його від В'єтнаму, дуже смішний.

Про БҐ:

"He had no problem competing with his customers. And, this is where we differed. Monetarily, Bill had the proper approach to get a stack of bucks, and lose a bunch of friends in the meantime."

...

"I was always apprehensive of his business moves, as I found his manner too abrasive and deterministic, although he mostly carried a smile through a discussion of any sort.

"Gates is more an opportunist than a technical type and severely opinionated even when the opinion he holds is absurd."

Мемуари короткі (на кілька годин макс, дії закінчуються у 1980), тому що його родичи дали CHM музею ув 2016 лише перші 7 глав у кострубатому pdf.

Ліцензія на оригінальний pdf є така, що якщо мене спіймають, то червертують після Аутодафе, а рештки кинуть у багаття, тому ріпо та файли epub/mobi на анонімному акаунті:

Download Here

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

henry_flower: A melancholy wolf (Default)

Цей русскій RMS і раніше кретином був, але зараз то є новий рівень:

'Коґда ґражданін (ілі імєющій ґрінкарту) США будєт вам ґоворіть что нєхорошо завоєвивать отдєлівшуюся 30 назад часть своєй страни, напомнітє єму, что Лікнольн в 1861 трідцать лєт ждать нє стал.

'Коґда амєріканци будут обвінять Россію в ґєноцідє насєлєнія прісоєдіняємих тєрріторій, напомнітє ім про індєйцєв. Єтот ґєноцід кстаті проїсходіл прімєрно тоґда жє, коґда Россія прісоєдіняла тє зємлі, на которих позднєє русскіє построїлі Хєрсон, Ніколаєв і Одєссу.

'Коґда оні возбухнут по поводу рабов, скажітє ім что єто у ніх BLM, а у нас в 1917-1920 ґодах проблєму віни потомков рабовладєльцєв пєрєд потомкамі рабов рєшілі кардінально. (скорєє всєґо єтот єміґрант которий возбухаєт- сам бєлий, поєтому пєрєнос способа рєшєнія сословноґо вопроса в Россії сто лєт назад на расовий вопрос в нинєшніх США єму нє понравітся).'

https://vitus-wagner.dreamwidth.org/2339584.html

Так він публічно раніше не виступав:

'Єто тольєо хохли, скачущіє в вишіванках мислят катєґоріяї жізнєнноґо пространства, поскольку оні вообщє мислят катєґоріямі позапрошлоґо єкономічєскоґо уклада. (хотя нєпонятно зачєм ім, прі іх рождаємості, тіпічниой дял постіндастріала єто самоє жізнєнноє простраснвтво. Но для тоґо уклада вообщє умєніє мисліть лоґічєскі нє характєрно).'

Питання є 1 і те саме:

"It is not consciousness of men that determines their being, but, on the contrary, their social being that determines their consciousness."

чи то істоти мімікрують все своє жалюгідне життя, щоб не здаватися нонконформістами, а вийшли уперед лише тому що за дупу тримати перестали і (нарешті!) тєпєрь ужє можно.

Тішусь що HIMARS'ам філосовські роздуми є нецікаві.

henry_flower: A melancholy wolf (Default)

https://nz.dreamwidth.org/15892.html

'Люди пишуть про події.

'А також постять скріншоти якихось кацапських ICQ, де море сльоз та соплей через те, що ЗСУ звільняють села і міста.

'Така драма, така драма, таке шеффсьопрапала, ніби вже в твій зауральський усть-піздюйськ зайшли і час скидать штани.'

У них пієтет перед будь-яким керівництвом або чином.

Їм пообіцяли: 'рассія в Х навсєґда'.

Потім виявляється що нє навсіґда, а навпаки.

Це є трагедія, бо хазяїн сказав неправду та повівся з ними як живоїд з собакою: прив'язав шматок м'яса до нитки.

Якщо хазяїн поганий, то русскій сумує.

Обломов писав як найулюбленіша розвага крєпостних була сперечатися один з одним у кого барін є кращий.

Русскіє вважають якщо вони мають баринів, це значить інші народи також є чиєюсь власністю.

У їх світі українці належать ЗОҐу, американцям або бритішам.

Тому 24/02 то була можливість довести самім собі що їх головний монгол--набагато кращий барін, ніж у сусідів.

Але.

henry_flower: A melancholy wolf (Default)

Не знаю навіщо тут частина народу ув DW збудилася, але якщо буря ув склянці не закінчиться, беріть приклад з лайнаксового kernel.org, де мейлін' листи покладені до гіту.

Написано автором Unicorn, називається public-inbox:

public-inbox - an "archives first" approach to mailing lists

public-inbox implements the sharing of an email inbox via git to complement or replace traditional mailing lists. Readers may read via NNTP, IMAP, Atom feeds or HTML archives.

public-inbox spawned around three main ideas:

  • Publicly accessible and archived communication is essential to Free Software development.

  • Contributing to Free Software projects should not require the use of non-Free services or software.

  • Graphical user interfaces should not be required for text-based communication. Users may have broken graphics drivers, limited eyesight, or be unable to afford modern hardware.

public-inbox aims to be easy-to-deploy and manage; encouraging projects to run their own instances with minimal overhead.

Aug. 22nd, 2022 16:15
henry_flower: A melancholy wolf (Default)

Раніше, коли пошукові рóботи не розуміли JS, деякі too clever by half індивідууми замість mykola@example.com писали \KX[YPrVLT\B_Q^[R]^>. Коли бовзери завантажували шматок JS, вони рядок розшифровували.

Хоча такий шифротекст може нагадувати rot47, зазвичай використовувався xor cipher з нескладним ключем (12345), через побоюванні що з rot* пошуковий рóбот Міг Здогадатися у чому справа і тоді бог його знає, що могло статися!

Про xor cipher я дізнався студентом зі книжки Страуструпа та навіщось марно намагався переконати кілька друзяк шифрувати своєю віндюковою аплікацією імейли.

Нещодавно я згадав про це, коли побачив як віндюковий антивайрус миттєво карантинує 'небезпечні' тільки-но завантажені файли. Чи можна тоді його надурити зберігаючи файла як curl | my-stream-cipher > file?

Найпростіший потоковий шифрувальника пишеться у кілька рядків на Рубі:

#!/usr/bin/env ruby
key = ($*[0] && $*[0].size > 0) ? $*[0] : abort
STDIN.each_byte.with_index do |c,i|
  STDOUT.write (c ^ key[i % key.size].ord).chr
end

Пароль передається як 1й аргумент:

$ echo їжачок | ./xor-cipher1.rb monkey | hexdump -C
00000000  bc f8 be dd b5 c9 bc e8  be d5 b5 c3 67           |............g|
0000000d
$ echo їжачок | ./xor-cipher1.rb monkey | ./xor-cipher1.rb monkey
їжачок

Єдиний помірно цікавий момента є лише у циклічності перебору символів у паролі. Наприклад, для паролю abc:

$ ruby -e "p='abc'; i=0; while (i < 10); print p[i % p.length]; i+=1; end"
abcabcabca

На жаль, така реалізація є дуже повільна. Для 100MB файлу і рубі 3.1.2:

$ head -c $((1024*1024*100)) /dev/urandom > 100M.bin
$ time cat 100M.bin | ./xor-cipher1.rb monkey >/dev/null
real    0m39.860s
user    0m39.729s
sys     0m0.655s

Я спробував читати блоками по 8KB, але різниці у швидкості не побачив. Вмикаючі jit (ruby --jit), вдалося зекономити 5.8 сек (14.5%).

Righty-ho, що у звичайного користувача є поряд завжди? Варіянт на sh+awk:

#!/bin/sh

[ -z "$1" ] && exit 1
export LC_ALL=C

dec() { od -An -vtu1 | awk '{for (i=1; i <= NF; i++) print $i}'; }
dec | gawk -v keydec="`printf '%s' "$1" | dec`" 'BEGIN {
  key_len=split(keydec, key); k=0
} {printf "%c", xor($1, key[k+1]); k = (k+1) % key_len}'

(Чомусь хфункція split() вертає хеш з ключами індексів починаючи з 1.)

dec() використовується для друкування потоку ув decimal:

$ printf abc | od -An -vtu1 | awk '{for (i=1; i <= NF; i++) print $i}'
97
98
99

На жаль, результат є маргінально ліпший за рубі:

$ time cat 100M.bin | ./xor-cipher7.sh monkey >/dev/null
real    0m36.538s
user    0m56.546s
sys     0m1.208s

Варіянт на ноуді:

#!/usr/bin/env node

let key = process.argv[2]?.length ? process.argv[2] : process.exit(1)
let keygen = key => {
    let idx = 0
    return () => key[idx++ % key.length].charCodeAt()
}
let k = keygen(key)
let enc = buf => buf.map( c => c ^ k())
process.stdin.on('data', chunk => process.stdout.write(enc(chunk)))

Нарешті щось приємне:

$ time cat 100M.bin | ./xor-cipher5.js monkey >/dev/null
real    0m1.507s
user    0m1.496s
sys     0m0.146s

Можна ще швидше? C з 0м витонченості:

#include <unistd.h>
#include <string.h>

void enc(char *key, char *buf, int len) {
  int key_len = strlen(key);
  for (int idx = 0; idx < len; idx++)
    buf[idx] = buf[idx] ^ key[idx % key_len];
}

int main(int _, char **argv) {
  char *key = argv[1]; if ( !(key && strlen(key))) return 1;
  int n;
  char buf[8*1024];
  while ( (n = read(0, buf, sizeof buf))) {
    enc(key, buf, n);
    write(1, buf, n);
  }
}

МИКОЛА ГНАТОВИЧ
Раніш люди ніколи не умивалися. І їли сало. А захоче помидора чи диню--то так зірве, навіть і не миє. І от таки в усіх пики були!

$ time cat 100M.bin | ./xor-cipher4 monkey >/dev/null
real    0m0.474s
user    0m0.449s
sys     0m0.172s

На цьому можна було б закінчити, але мені не давала спокію сумна швидкість рубі. Спочатку я спробував, вибачаюся, треди (з трохи іншим перебором символів ув паролі--потік читається блоками і лічильник прив'язаний до блоку, а не є глобальним):

#!/usr/bin/env ruby

key = ($*[0] && $*[0].size > 0) ? $*[0] : abort

def enc key, chunk
  chunk.each_byte.map.with_index {|c,i| (c ^ key[i%key.size].ord).chr }.join ""
end

threads = []
while (chunk = STDIN.read 8*1024)
  threads << Thread.new(chunk) {|chk| enc key, chk }
end

threads.each {|t| t.join; STDOUT.write t.value }

Але з GIL ув MRI це було гірше марного. Тоді я згадав що існує jruby:

$ rvm use jruby-9.3.4.0
$ time cat 100M.bin | ./xor-cipher2.threads.rb monkey >/dev/null
real    0m5.986s
user    0m45.193s
sys     0m3.770s

Ув рубі 3 з'явилися Ractors (actor-like concurrent abstraction). На жаль, jruby їх поки що не підтримує, а ув MRI вони періодично генерують coredumps.

#!/usr/bin/env ruby

KEY = ($*[0] && $*[0].size > 0) ? $*[0] : abort

def enc chunk
  chunk.each_byte.map.with_index {|c,i| (c ^ KEY[i%KEY.size].ord).chr }.join ""
end

ractors = []
while (chunk = STDIN.read 8*1024)
  r = Ractor.new { enc Ractor.receive }
  r.send chunk, move: true
  ractors << r
end

ractors.each {|rr| STDOUT.write rr.take }

Краще за 1й варіянт на рубі, але набагато гірше за комбінацію тредів під jruby:

$ time cat 100M.bin | ruby --jit xor-cipher8.ractors.rb monkey >/dev/null
<internal:ractor>:267: warning: Ractor is experimental, and the
behavior may change in future versions of Ruby! Also there are
many implementation issues.

real    0m27.245s
user    2m5.655s
sys     0m14.194s

Що буде, якщо ми розіб'ємо великий файла на N шматків і будемо запускати nproc процессів (N > nproc) доки всі N не будуть зашифровані? Все це вміє GNU Make:

#!/usr/bin/make -sf

$(if $(p),,$(error p=))

self    := $(lastword $(MAKEFILE_LIST))
t       := $(shell openssl rand -hex 4)
make    := $(MAKE) --no-print-directory -f $(self) t=$(t)
cipher  := $(dir $(self))/xor-cipher1.rb
chunk   := chunk_$(t)_
xor     := xor_$(t)_
nproc   := sysctl -n hw.ncpu
ifeq ($(shell uname), Linux)
nproc   := nproc
endif

all:
	split -b 409600 - $(chunk)
	echo $(chunk)* | sed 's/$(chunk)/$(xor)/g' | xargs $(make) -j `$(nproc)`
	cat $(xor)*
	rm $(xor)* $(chunk)*

$(xor)%: $(chunk)%; $(cipher) $(p) < $< > $@

Для шифрування тут використовується 1й рубі варіянт:

$ time cat 100M.bin | ./xor-cipher3.mk p=monkey >/dev/null
real    0m8.219s
user    1m23.892s
sys     0m3.525s

Підсумки для 100MB файлу:

Name Score
C 0m0.474s
node 0m1.507s
jruby threads 0m5.986s
make + ruby 0m8.219s
ruby jit + ractors 0m27.245s
ruby jit 0m34.003s
sh + awk 0m36.538s
ruby 0m39.860s
henry_flower: A melancholy wolf (Default)

Вчора батько написав мені імейла з .zip файлом і питанням чому у нього на w10 імена файлів всередині архайву виглядають ієрогліфами.

Для багатьох це є щось нудно знайоме, але мені такого не присилали років 15. Я ніби поринувся ув 2004.

Формат zip ув індексі має спеціяльний біт, назвемо його uses_unicode, який == 1, якщо ім'я файлу закодовано ув utf8; якщо біт == 0, тоді мається на увазі кодування IBM Code Page 437 (так).

Хтось на Маку зробив .zip, де закодував імена файлів ув utf8, але біт uses_unicode не виставив:

Лайнаксові unzip(1) та file-roller такий варіянт розпізнають, але 10й віндюка--ні. Він чесно намагається, згідно зіповського спеку, деінкодити cp437.

Суто для лулзів, я створив файл 'толстоевский.txt' та запхнув його до 1.zip за допомогою віндюкового іксплорера (Send To → Compressed zip folder). Останній зробив хід конякою: заінкодив ім'я файлу ув cp866 (так), а біт uses_unicode не виставив.

Федорівський unzip зрозумів що коїться, але гноумівський file-roller ні, і марно намагався перекодувати cp437 до юнікодної локалі.

Загалом, найліпше себе поводить zip(1) на лайнаксі: інкодить ув utf8 та виставляє uses_unicode біт.

7-zip під віндюка робить дещо інше: uses_unicode == 0, ім'я файлу ув cp866, але додатково записує те саме ім'я файлу ув utf8 до опціонального extra field ув індексі з id 0x7075, що називається Info-ZIP Unicode Path.

Оригінальний файл--квінтесенція СНД:

__MACOSX/Рецензия смет докум/._~$сьмоdoc.doc
Рецензия смет докум/247_ДЦ_ДЦ_3.rtf
henry_flower: A melancholy wolf (Default)
$ ./google-complete us does ukr
["does ukraine have nuclear weapons",
 "does ukraine have oil",
 "does ukraine have a navy",
 "does ukraine speak russian",
 "does ukraine have an air force",
 "does ukraine have to pay back aid",
 "does ukraine want to join nato",
 "does ukraine have nukes",
 "does ukraine have its own language",
 "does ukraine have submarines"]

Колись гоогл autocomplete видавав результата ув json'і, але зараз там кількість підказок менша за варіянт з тулбару ув кроумі, тому

#!/usr/bin/env -S ruby -ropen-uri -rnokogiri

abort "Usage: #{$0} [RUBYOPT=-v] country_code query …" if $*.size < 2

u = URI 'https://google.com/complete/search'
u.query = URI.encode_www_form({output:'toolbar',gl:$*[0], q:$*[1..]&.join(' ')})
warn u.to_s if $VERBOSE

pp Nokogiri(URI.open(u.to_s).read).css('suggestion').map {|v| v['data']}

(Скрипту потрібно gem install nokogiri.)

1й параметр там country codes, з яким можна бачити що цікавить русню:

$ ./google-complete ru когда
["когда день пива",
 "когда день железнодорожника",
 "когда убирать лук",
 "когда троица в 2022 году",
 "когда день строителя",
 "когда закончится война",
 "когда убирать чеснок",
 "когда откроется икеа",
 "когда закончится война на украине",
 "когда плачут цикады"]

vs.

$ ./google-complete ua когда
["когда день пива",
 "когда откроется макдональдс в киеве",
 "когда международный день пива",
 "когда гарри встретил салли",
 "когда началась война в украине",
 "когда началась вторая мировая война",
 "когда включат горячую воду киев",
 "когда плачут цикады",
 "когда откроют макдональдс",
 "когда его совсем не ждешь"]

Upd

Майже те саме, тільки коротше:

#!/bin/bash
curl 'https://google.com/complete/search?output=toolbar' \
     --data-urlencode "gl=$1" \
     --data-urlencode "q=${*:2}" -Gs | \
    nokogiri -e 'pp $_.css("suggestion").map {|v| v["data"]}'
henry_flower: A melancholy wolf (Default)

У француза з 1839 є розповідь про повстання крєпостних на Волзі. Я намагався щось нагооглити про ті події, але знайшов нічого, крім картофельних бунтів кілька років перед цим.

З опису подій це схоже не на Нігерію ув снігу, а на Гаїти, де замість відвантажених негрів--автохтонні мужікі.

Найголовнішим прагненням русскоґо кріпака була не свобода, а бути проданим, разом зі всім селом (пардон, дєрєвнєй), державі. Бути невільником приватної особи вважалося злою долею, належати ґосударю--вдачею.

"The crown lately purchased a considerable estate in the district that has since revolted. Immediately, the peasants sent deputies from every part of the surrounding country to the new superintendents of the Imperial lands, to supplicate the emperor to purchase them also. The serfs chosen as ambassadors were sent on to Petersburg.

"The emperor received them and treated them with kindness; but, to their great regret, he did not buy them. 'I cannot,' he said to them, 'purchase all Russia, but a time will come, I hope, when each peasant of this empire will be free: if it depended only upon me, the Russians should enjoy, from this day forth, the independence which I wish for them; and to procure them which at a future period, I am laboring with all my power.' "

"Well, this answer seems to me full of reason, candor, and humanity."

"No doubt: but the emperor should have known the men to whom he addressed such words; and not have murdered his noblemen out of tenderness towards his serfs. These words, interpreted by barbarous and envious peasants, have set a whole province on fire; and thus has it become necessary to punish a people for crimes which they were instigated to commit.

"'Our father desires our deliverance,' cried the returned deputies on the borders of the Volga; 'he wishes for nothing but our happiness; he said so to us, himself: it is, then, only the nobles and their agents who are our enemies, and who oppose the good designs of our father! Let us avenge the emperor!'

"After this, the peasants believed they were performing a pious work in rising upon their masters, and thus all the nobles of a canton, and all their agents were massacred, together with their families. They spitted one, and roasted him alive; they boiled another in a cauldron; they disemboweled, and killed, in various other ways, the stewards and agents of the estates; they murdered all they met, burnt whole towns, and, in short, devastated a province; not in the name of liberty, for they do not know what liberty means, but in the name of deliverance and of the emperor."

henry_flower: A melancholy wolf (Default)

https://twitter.com/kremen_x/status/1501800804352675842

Юрки @undead_anarch більше немає. Інсульт і 2
тижня в комі. Сьогодні вночі він помер. Хто вміє,
помоліться за нього будь ласка.
Бажаючим допомогти в пп дам картку мами.

@undead_anarch це наш jurgen у DW.

(Я дізнався тільки сьогодні.)

henry_flower: A melancholy wolf (Default)

Потрібно таке саме на кордонах з монголами

тільки з кулеметом на спині.

dhs.gov/science-and-technology

henry_flower: A melancholy wolf (Default)

Не знаю для чого, зробив собі gpu passthrough для віндюкових vm на десктопі, яким майже не користуюся (сенсу звертати увагу на віндюка, після того як вони покинули оригінальний Edge, немає). Замість vmware ws тепер plain qemu.

Експериментував зі cpu pinning (через libvirt), різниці 0. У минулому році хтось робив стаді з форкнутим qemu:

'the support of parallelism in QEMU scales well, and that, somewhat counter intuitively, pinning does not improve performance'

so yeah.

w8.1 працює ніби на справжньому hardware (для сучасного gpu passthrough потрібна ос з uefi boot, тому w7 була дискваліфікована). w11 відчувається трохи повільніше у vm (можливо треба було ставити на host drive, замість qcow2, але перевіряти є лінь). Різницю між віртуальним та справжнім лайнаксом помітити неможливо.

Спитав гоогла де лежить конфіга "нового" терміналу ув w11, але натрапив на ось це:

vs studio license

Дуже приємно, що навіть у часи тяжкої скрути, плинності та непостійності, оточення віндюка залишається міцною, непорушною скалою. The 1 thing in life you can always count on.

henry_flower: A melancholy wolf (Default)

Топ поїхавший т.з. "russian statesman".

Спочатку погрожує викрасти гамериканського конгресмена (dem) до Московії, після відповіді конгресмена "Fuck around and find out", погрожує його вбити на території Гамериці:

“This guy, even though we can’t get him right now, we can issue our verdict in a Russian court… So, you know what, dear friend? We can sentence you here and we have plenty of people like [Ramón] Mercader—former Hero of the Soviet Union—who will carry out our verdict. Mercader is the one who ice-axed Trotsky to death, because the USSR sentenced him to die. You, [obscenity], look at that, American pindos, you’ll be sentenced here and there is nothing you can do. You’ll be running all over [obscenity] America in a bulletproof vest, because there’ll be people who will force you to respect [Russia].”

src

Сміявсь:

May 1997: NATO-Russia Founding Act

Presidents Clinton and Yeltsin met in May, 1997, at the Élysée Palace in Paris to sign the NATO-Russia Founding Act. In the Founding Act's preamble, NATO and Russia stated that they no longer considered each other as adversaries.

https://2001-2009.state.gov/r/pa/ho/pubs/fs/85962.htm

henry_flower: A melancholy wolf (Default)

Контекстне меню свіжих версіях кроуму має пункт 'Copy link to highlight'. Над цією грандіозною функцією гооглівці страждали кілька років та написали специфікацію для неї--Text Fragments.

З гарного для власників сайтів:

  • робити треба нічого.

З поганого для власників сайтів:

  • контроль над функцією є відсутнім;
  • вимкнути її не можна.

Працює вона так: коли користувач виділяє текста і клікає 'Copy link to highlight', до URL додається hash:

http://example.com/hello/#:~:text=world

Якщо бовзера підтримує TF, той шукає рядок world на сторінці та виділяє його. З точки зору devtools жодних змін ув dom не відбувається, автор сторінки вплинути на пошук з виділенням може ніяк.

Кроум 96 має очікуваний бага: якщо URL мав hash виду #/?route_to=heaven (це було популярно використовувати до того як history api з'явився ув Сафарі), кроум його нахабно змінить на #:~:text=..., хоча специфікація дозволяє додавати маркер :~:text=... у кінець.

Власнику сайта хвилюватися про TF є непотрібним: коли він подивиться, наприклад, на location.hash, маркера там не буде--бовзер його заздалегідь видалить самотужки.

Що навело мене на думку: чи можна детектити TF?

Виявляється, через Navigation Timing API можна отримати анмолестед URL зі всіма маркерами:

function has_text_fragment() {
    let url = performance.getEntries().find( v => v.type === 'navigate')?.name || location.href
    return /^#.*:~:text=/.test(new URL(url).hash)
}

Маючи детектор, як можна подратувати too clever by half користувача?

Що може бути більш дратівливим ніж модальні даялоги, які є неможливо закрити? Після намагань запхнути такий модуль ув кілька твітів, я зупинився на

export default (z_index = 999) => has_text_fragment() && draw(z_index)

function has_text_fragment() {
    let url = performance.getEntries().find( v => v.type === 'navigate')?.name || location.href
    return /^#.*:~:text=/.test(new URL(url).hash)
}

function draw(z_index) {
    let d = document, w = d.createElement('div')
    w.style.cssText = `position:fixed;top:0;left:0;height:100%;width:100%;z-index:${z_index};display:flex;justify-content:center;align-items:center;background:rgba(0,0,0,.2)`
    w.innerHTML = '<img src="data:image/gif;base64,R0lGODlhFAAQAOMKAAAAAFp3N3RhMVplU41VL1iNPnyQbKGumMLHuv////b27/b27/b27/b27/b27/b27yH5BAEKAA8ALAAAAAAUABAAAARl8MmJjEUSHTz7O0UYGmJxeFmpiuhDrqrBSSAMc7U6GId1DIPDCRFLHAADTQKBTH5gwEFgZAicHjbbNZctGSSvruoUDgUIaEFgzQ4MniUBek5HX8sFtmAvt3e4NgEzFBo9FocbHhEAOw==">'
    w.querySelector('img').style.cssText = 'image-rendering:pixelated;max-height:100%;max-width:100%;height:500px;width:auto'
    d.body.appendChild(w)
    return w
}

Воно малює модальне жабеня Пепе, коли хтось заходить на сторінку з Text Fragments ув URL:

Має влізти ув 3 твіти (280*3 = 840):

$ terser pepe-the-text-fragment.js --module -mc | wc -c
832
henry_flower: A melancholy wolf (Default)

Найсмішніше ув книжці про Тексас було в останніх главах.

У кінці 19 ст там з'явилася 3тя партія: Популісти. За описом членів нагадувала міцний сплав сьогоднішніх ліваків з альтернативними правими.

Їх боротьба:

'Rhetorical hatred was directed toward the East, the source of all evil, the place from which Yankee money flowed. Wall Street, whose workings not one farmer in a million understood, became an enduring, odious symbol.

'The shadowy figure of the "Jewish banker" became involved; although not one Texan in a thousand had ever even seen a Jew. … Shylock the banker was an allusion every farmer immediately understood, and every farmer had figuratively, with great anguish, been relieved of his pound of flesh.

'Someone, inevitably, had to serve as scapegoats for this long pain. The rancor against the Shylocks was to remain rhetorical, because the Jews remained out of reach.'

У всьому винні українські єврейські лихварі з нюйорку, але т.я. у нашому селі біля Ріо-Ґранде українців ніколи не було, собраніє постановіло призначити євреєм Девіда Морґана з Ель-Пасо. Він торгує друкарськими машинками і не спілкується з сусідами.

henry_flower: A melancholy wolf (Default)

Почитавши про бурю ув склянці з утілітою which(1) ув деб'яні, пограв сам з собою ув code golf: на якій мові вийде написати найкоротший аналог which(1), який

  1. їсть декілька агрументів, e.g.

     my-which ls BOGUS cat
  2. має зупинитися після 1го неіснуючого виконуваного файлу та повернути, у такому разі, exit code > 0.

Найкрихітніший результат вийшов, як завжди, на GNU Make:

#!/usr/bin/make -f
f = $(firstword $(wildcard $(addsuffix /$1,$(subst :, ,$(PATH)))))
%:;@echo $(or $(call f,$@),$(error $@ not found in PATH))

Працює так:

$ ./which.mk ls BOGUS cat
/usr/bin/ls
which.mk:3: *** BOGUS not found in PATH.  Stop.
$ echo $?
2

На 2му місці Рубі:

#!/usr/bin/env ruby
def f e; (ENV['PATH'] || '').split(?:).filter{|d| File.executable?(d+'/'+e)}.map{|d| d+'/'+e}[0]; end
ARGV.each {|i| puts(f(i) || abort("#{$0}: #{i} not found in PATH")) }

Дуже посередній результат на sh:

#!/bin/sh

f() {
    IFS=':'
    for p in $PATH; do
        [ -x "$p/$1" ] && { echo "$p/$1"; return; }
    done
    return 1
}

for i in "$@"; do
    f "$i" || { echo "$0: $i not found in PATH" 1>&2; exit 1; }
done

А найогиднійший, як завжди, на ноуді (v17.0.1):

#!/usr/bin/env node

let {access} = require('fs/promises')

let afilter = async (arr, predicate) => { // та за шо
    return (await Promise.allSettled(arr.map(predicate)))
        .filter( v => v.status === 'fulfilled').map( v => v.value)
}

let f = e => afilter((process.env.PATH || '').split(':'), async p => {
    await access(p+'/'+e, 1)
    return p+'/'+e
})

async function main() {
    let args = process.argv.slice(2).map( async p => {
        return {exe: p, location: await f(p)}
    })

    for await (let r of args) {
        if (!r.location.length) {
            console.error(`${process.argv[1]}: ${r.exe} not found in PATH`)
            process.exitCode = 1
            break
        }
        console.log(r.location[0])
    }
}

main()

Можна було 1 в 1 скопіювати версію на рубі, але я хотів уникнути ⓐ try/catch (fs.accessSync() кидає іксепшона) та ⓑ process.exit().

Upd: не витримав, переписав останній приклад традиційними колбеками, стилем також відомим як як діди:

#!/usr/bin/env node
let fs = require('fs')

let f = (ok, error) => {
    let dirs = (process.env.PATH || '').split(':')
    return function dive(e) {
        let dir = dirs.shift() || error(e); if (!dir) return
        let file = dir+'/'+e
        fs.access(file, fs.constants.X_OK, err => err ? dive(e) : ok(file))
    }
}

let args = process.argv.slice(2)
let main = exe => exe && f( e => (console.log(e), main(args.shift())), e => {
    console.error(`${process.argv[1]}: ${e} not found in PATH`)
    process.exitCode = 1
})(exe)

main(args.shift())

Чудовими _ = _ => _ && _( _ => (_, _), _ => { ... })(_) не насолоджуватися неможливо.

henry_flower: A melancholy wolf (Default)

Список країн, в яких нерухомість краще не купувати:

$ sed -n '/^\/\/ /{:start /};/!{N;b start};/yandex/p}' \
    components/search_engines/template_url_prepopulate_data.cc | grep ^//
// Albania
// Bulgaria
// Burundi
// Belarus
// Algeria
// Estonia
// Egypt
// Finland
// Greece
// Croatia
// Indonesia
// Israel
// Iraq
// Jordan
// Kuwait
// Kazakhstan
// Lebanon
// Lithuania
// Latvia
// Libya
// Morocco
// Moldova
// Montenegro
// Pakistan
// Romania
// Serbia
// Russia
// Saudi Arabia
// Syria
// Tunisia
// Turkey
// Ukraine
// Yemen

Пояснення: кроміум має діфолтні списки пошуковиків для різних країн. У списку вище країни, для яких запопадливо додан yandex. Наприклад для України це виглядає так:

const PrepopulatedEngine* const engines_UA[] = {
    &google,
    &yandex_ua,
    &bing,
    &duckduckgo,
    &yahoo,
};

а для Хранції

const PrepopulatedEngine* const engines_FR[] = {
    &google,
    &bing,
    &yahoo_fr,
    &qwant,
    &ecosia,
};

qwant--то є шось місцеве для хранцузів. Якщо там набрати, як радить пан bytebuster, слово бойлер, то, ви не повірите, буде 0 (нуль) результатів російською, а будуть лінки на болгарські та українські сайти! Уявіть, є світ, де Московії не існує.

Щодо кроміума, там цікаво. Вони спокійно мерджать підстановку GetYandexReferralID, щоб робити життя добрим людям простіше. Чому не допомогти those good russian guys, since we are all in here together, RESIST CAPITALISM, SUBVERT CORPORATE INTERESTS! щоб їм ув Москві було менше мороки з підтриманням їх форку.

henry_flower: A melancholy wolf (Default)

... або як отримати jpg файли меншого розміру, не змінюючи таблицю квантування (параметр компресії -quality NNN).

ImageMagick для маніпуляцій з jpeg використовує бібліотеку libjpeg-turbo. Мозілла форкнула останню і назвала результат mozjpeg:

'MozJPEG is compatible with the libjpeg API and ABI. It is intended to be a drop-in replacement for libjpeg.'

Тобто, в теорії, можна після збірки mozjpeg або ① перекомпілювати ImageMagick, вказавши ув CPPFLAGS та LDFLAGS шлях до mozjpeg, або ② через LD_LIBRARY_PATH змусити вже встановлений, системний ImageMagick використовувати мозилівського форка.

Я спробував обидва варіянти. У 1му випадку, ImageMagick вдалося надурити лише з версією mozjpeg 3.2; у 2му запрацювала найсвіжіша master гілка (пардон, main).

У нових версіях mozjpeg перейшов з autotools на cmake, тому генерація мейкфайлів тепер надійно працює лише на мошинах qa-калік ув мозиллі.

$ git clone https://github.com/ImageMagick/ImageMagick
$ mkdir ImageMagick/_out
$ cd !$
$ cmake -DCMAKE_INSTALL_PREFIX:PATH=`pwd`/1 -DENABLE_STATIC=0 -G"Unix Makefiles" ..
$ make install

Конвертації файлу src.ppm:

  1. Утілітою cjpeg (ув федорі--libjpeg-turbo-utils)

     $ cjpeg -progressive -quality 60 src.ppm > cjpeg-libjpeg-turbo.jpg
  2. Системним ImageMagick без трюків:

     $ convert -interlace plane -quality 60 src.ppm \
         imagemagick-libjpeg-turbo.jpg

    де аргумента -interlace plane означає progressive.

  3. Системним ImageMagick, але використовуючи свіжий mozjpeg:

     $ LD_LIBRARY_PATH=~/tmp/mozjpeg/_out/1/lib64 convert \
         -quality 60 src.ppm imagemagick-mozjpeg.jpg

    аргумента -interlace plane тут є непотрібним, т.я. mozjpeg його вмикає автоматично по замовчуванню.

Результат:

$ du -b * | sort -n
40854   imagemagick-mozjpeg.jpg
54183   imagemagick-libjpeg-turbo.jpg
54302   cjpeg-libjpeg-turbo.jpg
2764816 src.ppm

m2v

Oct. 6th, 2021 13:52
henry_flower: A melancholy wolf (Default)

Знайшов чудовий формат кортинок для знущань: m2v. Це є video/mpeg з 90х. Наприклад, 1 самотній фрейм ув контейнері mpg2.

Робиться звичайним imagemagick'ом: convert my.jpg my.m2v.

Або галерея: convert *.jpg not-realy-a-real-movie.m2v.

Таке pictures-in-a-box з вельми непоганою компресією.

Бовзери m2v не розуміють, припизжене w10 photos також. VLC читає, але за діфолтом не робить павзу на останньому фреймі, тобто мамкин хакір спостерігає лише миттєве блимання.

Так само поводиться "${env:ProgramFiles(x86)}/Windows Media Player/wmplayer.exe" з w7 (діди таке мають пам'ятати), але не грається взагалі ув w10 (w11 ще не пробував).

(До речі, wmplayer ув w7 був дуже дотепний. якщо натиснути Ctrl-T він буде робити loop фреймів, але якщо фрейм лише 1, він рірендерить його безкінечно, з'їдая під ~70% одного ядра cpu.)

Користуватися з комфортом можна з mpv:

Додати keep-open=yes ув ~/.config/mpv/mpv.conf та тиснути , та . для перегляду всіх фреймів.

henry_flower: A melancholy wolf (Default)

Прийшло сьогодні:

Date: Wed, 29 Sep 2021 08:07:43 -0700 (PDT)
From: norflus@cs.fsu.edu
To: [redacted]
Subject: Academic Survey on Discriminatory Language in Software
Message-ID: <6154813f.1c69fb81.f7547.12a6SMTPIN_ADDED_MISSING@mx.google.com>

Hello,

Our research group is currently performing a study on the
prevalence and impact of discriminatory language in Software
Engineering artifacts and developer communications.

You have been identified as a contributor to [redacted] which is a
project we are using to investigate this topic.

We would be grateful if you could participate in our survey, which
consists of a short background questionnaire, and reviewing and
responding to a list of identified non-inclusive terms. There will also
be an opportunity to contribute to our list if you have encountered
any additional terms not included in the survey.

…

гагага

Спеціяльно витратив 5 хв щоб відповісти 'strongly disagree' по всім питанням. Про жахливі речі типу блеклістів і Мастера з його відданим слейвом я, звичайно, чув, але не знав що man hour та dummy value зараз також є дискримінаційними.

Скільки деякі люди ув Флориді мають вільного часу.

May 2025

M T W T F S S
   12 34
5678910 11
1213 1415 161718
19202122232425
262728293031 

Expand Cut Tags

No cut tags