henry_flower: A melancholy wolf (Default)
henry_flower ([personal profile] henry_flower) wrote2023-07-18 05:17 am

Code golf для мінімального cp(1)

Пачіняючі прімус, тобто шукаючи причину чому прибацаний файловий диспетчер 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

kondybas: (Default)

[personal profile] kondybas 2023-07-18 09:54 am (UTC)(link)
Тернарний ? то й вигадано було для інлайнової лаконічності.

Але респект, я так ніколи не міг.
juan_gandhi: (Default)

[personal profile] juan_gandhi 2023-07-18 11:04 am (UTC)(link)

Популярный трюк в шелл-скриптах.

kondybas: (Default)

[personal profile] kondybas 2023-07-18 11:12 am (UTC)(link)
В шеллі, переважно, бінарне A || B, і воно не дуже читабельне, я в себе в якийсь момент пройшовся і все поміняв на if A then B.

В SQL ще можна юзати тризначний XOR із NULL, і я навіть в одному проекті його дуже ефективно використав замість каскаду CASE WHHEN THEN, але читабельність коду впала до нуля і мені досі соромно за той код.

[personal profile] sassa_nf 2023-07-18 01:31 pm (UTC)(link)
Also,

> a?b():0;

Можна a&&b();

(Хоча в даному випадку не допоможе)

[personal profile] sassa_nf 2023-07-18 02:28 pm (UTC)(link)
А, допоможе

c-3||!strcmp(...)?errx(...):0;



c>2&&strcmp(...)||errx(...);
Edited 2023-07-18 14:29 (UTC)

[personal profile] sassa_nf 2023-07-18 08:34 pm (UTC)(link)
Так, я не перевіряв. А як же тоді тернарний працює?

[personal profile] sassa_nf 2023-07-19 02:03 pm (UTC)(link)
:)

Ну, воно мабуть обох до void зводить.
juan_gandhi: (Default)

[personal profile] juan_gandhi 2023-07-18 10:31 am (UTC)(link)

Why s = open(v[1], O_RDWR)? Saving on one byte? You could replace it with 0 (and use 1 for open(v[2]...).

Instead, replace 65536 with something 100 or 1000 times bigger.

juan_gandhi: (Default)

[personal profile] juan_gandhi 2023-07-18 03:15 pm (UTC)(link)

Ага!

waqur: (Default)

[personal profile] waqur 2023-07-18 12:33 pm (UTC)(link)
Нету обработки EINTR, некошерно. И короткие записи — тоже не всегда индикатор ошибки, они могут происходить в результате доставки сигналов.

И ещё интересно, почему массив int'ов, а не char'ов (для минимизации? ну ок, только тогда аргумент у read должен быть 65536*sizeof(int) или 262144 или 1<<18, либо размер массива 16384 элементов).
Edited 2023-07-18 13:01 (UTC)