99 бутылок: cURL

На сайте «Оупеннет», который я давно читаю, стараясь зажмуриваться на комментариях (там чистые Содом и Гоморра), увидел сегодня статью «В конфигурационных файлах curl случайно реализована полнота по Тьюрингу». Естественно, мимо такого названия я пройти не мог. Не беда, что «Песня о пиве» уже реализована, в первый раз что ли? Реализую свою!

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

#!/usr/bin/env curl -qsK 99.conf
# Written by Evgeny Stepanischev, Nov 2026

# 99.conf

url = 0
variable = b=xxxxxxxxx
variable = B=xxxxxxxxx
variable = "bottles=no bottles"
variable = output@final.txt
variable = r1=x0
variable = run=step.conf
expand-variable = dc[0-0]={{b}}{{B}}
expand-variable = run{{dc}}=/dev/null
expand-config = {{run}}
expand-write-out = {{output}}

# inc.conf

expand-variable = digit[1-1]={{c}}
expand-variable = c[2-]={{c}}
expand-variable = i={{carry}}{{digit}}
expand-variable = digit[{{i}}-{{i}}]=01234567891234567890
expand-variable = carry[{{i}}-{{i}}]=00000000000000000001
expand-variable = n1={{digit}}{{n1}}
expand-variable = r1={{r1}}x{{digit}}
expand-variable = dc[0-0]={{c}}
expand-variable = inc{{dc}}=/dev/null
expand-config = {{inc}}

# step.conf

expand-variable = dc[0-0]={{b}}
expand-variable = b{{dc}}=xxxxxxxxxx
expand-variable = B{{dc}}[1-]={{B}}
expand-variable = b[1-]={{b}}
expand-variable = dc[0-0]={{b}}{{B}}
expand-variable = run{{dc}}=/dev/null
expand-config = {{run}}

expand-variable = next={{bottles}}
variable = carry=1
expand-variable = c={{r1}}
variable = r1=
variable = n1=
variable = inc=inc.conf
config = inc.conf
expand-variable = n{{carry}}=1{{n1}}
expand-variable = r{{carry}}={{r1}}x1
expand-variable = "bottles={{n1}} bottle{{plural}}"
expand-variable = "output={{next}} of beer on the wall.\n\n{{output}}"
expand-variable = "output=Take one down and pass it around, {{output}}"
expand-variable = "output={{bottles}} of beer.\n{{output}}"
expand-variable = "output={{bottles}} of beer on the wall, {{output}}"
variable = plural=s

# final.txt

No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.

Для запуска нужен cURL версий 8.12…8.16, в более поздних рекурсию ограничили до пяти уровней, так что пример работать не будет.

У меня тут используется несколько трюков.

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

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

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

При помощи строковой конструкции var[1-] урезается контрольная строка, содержащая буквы «x». Таких строк две — одна содержит количество равное десяткам, другое — единицам.

Эта строки нужны для ветвления. Если взять первый символ конструкцией var[0-0], то добавив его к имени конфигурационного файла, можно получить условный переход — мы перейдём на config, если буквы закончились и на configx, если нет.

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

На этих трюках основан весь код программы.

Комментарии 4

Bažbanajzer Šalabajzer № 1
мне так не хватает полноты по тьюрингу в конфигах quake3
Евгений Степанищев автор № 3
Так можно же дописать, вроде исходный код опубликован?
Bažbanajzer Šalabajzer № 4
и везде таскать свой билд опенсорсного движка?

плюс учитывая что у квача своя виртуалка, это звучит еще менее реальным((