💾 Archived View for it.omarpolo.com › articoli › rant-printf.gmi captured on 2022-04-29 at 12:19:05. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2022-03-01)

-=-=-=-=-=-=-

Rant su printf, utility e argomenti

L'altro giorno dopo una modifica gmid non compilava più su FreeBSD. L'errore?

printf: illegal option -- i
usage: printf format [arguments ...]

L'hack ^W^W La soluzione non è nulla di che:

blob - 4d88d235a3b7fab68000216ca0c17d78b7cac86e
blob + b08dd3b7991d633d0dff24a28b5bfb2882d4c63c
--- configure
+++ configure
@@ -473,11 +473,11 @@ printf "\n\n"

 # Include dependency info
 for src in ${ALL_SRCS}; do
-       printf "-include ${src%.c}.d\n"
+       printf "%s\n" "-include ${src%.c}.d"
 done

 for comp in ${COMPAT}; do
-       printf "-include ${comp%.o}.d\n"
+       printf "%s\n" "-include ${comp%.o}.d"
 done

 echo "file Makefile.local: written" 1>&2

ma il punto è un altro: un giro del genere dovrebbe essere necessario? In altri termini, perchè printf(1) dovrebbe avere dei flag?

Su UNIX è norma per i programmi da riga di comando (e non solo) fornire dei "flag" per modificare il comportamento. I lettori saranno familiari a cose come `ls -lah': invocare il programma "ls" con i flag "l", "a" ed "h".

Non voglio finire col riscrivere "cat -v considered harmful" ma quando mi sono trovato davanti a quell'errore su FreeBSD ho provato una sensazione che credo fosse simile a quella provata da Kernighan quando si trovò davanti a BSD cat. O almeno così mi piace pensare.

UNIX Style, cat -v Considered Harmful

Il fastidio principale che ho provato è che i dev di freebsd non hanno aggiunto opzioni a printf, ma comunque chiamano getopt.

while ((ch = getopt(argc, argv, "")) != -1)
	switch (ch) {
	case '?':
	default:
		usage();
		return (1);
	}
argc -= optind;
argv += optind;

Se alcune implementazioni popolari di printf -- oppure lo standard stesso -- prevedessero delle opzioni e una specifica implementazione non le supportasse, in quel caso non ci vedrei nulla di male in un costrutto del genere. Ma, per quanto ne so, lo standard non prevede nessun flag per questa utility. Per confronto, OpenBSD ha soltanto:

/* Need to accept/ignore "--" option. */
if (argc > 1 && strcmp(argv[1], "--") == 0) {
	argc--;
	argv++;
}

Il printf di NetBSD è più complesso: se ci sono più argomenti e il primo non contiene nessun caratter '%' allora chiamano getopt in modo simile a FreeBSD giustificando il tutto con:

/*
 * except that if there are multiple args and
 * the first (the nominal format) contains no '%'
 * conversions (which we will approximate as no '%'
 * characters at all, conversions or not) then the
 * results are unspecified, and we can do what we
 * like.   So in that case, for some backward compat
 * to scripts which (stupidly) do:
 *	printf -- format args
 * process this case the old way.
 */

FreeBSD: usr.bin/printf/printf.c

OpenBSD: usr.bin/printf/

NetBSD: usr.bin/printf/printf.c

Nonostante a malincuore riconosca la necessita di ignorare perlomeno "--" per qualche forma di retrocompatibilità, usare getopt in printf lo considero una sorta di fallimento.

Almeno per come mi è stata venduta, printf(1) è stato pensato per ovviare ad alcune limitazioni/problematiche di echo. Costrutti come `echo $foo` non sono "sicuri" se $foo è controllata -anche solo parzialmente- dall'utente per via di come funziona l'espansione della shell. Printf dovrebbe essere la versione "sicura", nel senso di non avere problemi se l'utente afferma di chiamarsi davvero "-n Antonio" :P

(Questa situazione ricorda in parte l'SQL injection, solo con risultati meno catastrofici)

Per cercare di dare una sorta di conclusione a questo rant, il punto credo sia l'aspettativa che i programmi accettino dei flag. No, un programma senza flag non è un reato. Non serve trasformare ogni utility in una sorta di "jack of all trades" a forza di flag. Programmi semplici come printf -utility che fanno una e una cosa sola- non hanno bisogno di flag (nel caso in questione la stringa "format" è già di suo più che sufficiente per fornire opzioni.)

Lati poco chiari come questo sono il motivo per il quale scrivere script shell portabili è noioso: ci sono troppi dettagli da tenere a mente per via della pazzia dell'implementatore di turno.

Cronache dal GNU-verse

Ovviamente printf(1) in coreutils ha --version e --help. Perchè è necessiario sapere quale versione di printf(1) è installata. Ovviamente.

$BlogIt: rant-printf.gmi,v 1.3 2022/01/10 15:33:23 op Exp $