El otro día me encontraba a mi mismo programando en C de nuevo. Quería ejercitar mis capacidades de bajo nivel creando newish, un pequeño terminal. En este momento, claro, las cosas aún están muy verdes.
El caso es que una de las cuestiones que tenía que abordar era el parsing del comando del usuario. Mi idea con newish es aplicar mis convicciones sobre que añadir espacios como parte del nombre de archivos es una mala idea, utilizando una convención (el carácter subrayado), en su lugar... solo cuando sea necesario. Es decir, para visualizaciones newish ignorará los subrayados, sustituyéndolos por espacios.
Así, dada la ejecución siguiente:
$ touch this_is_a_test.txt
$ ls
newish.c parser.h this_is_a_test.txt util.h
parser.c README.md util.c ...
newish lo mostrará de la siguiente manera:
$ newish
newiSH v0.1 20240801
/home/baltasarq/Prys/newish
~: cat
Makefile f 782 2025-06-18 10:40:18
appinfo.c f 498 2025-06-18 10:27:57
appinfo.h f 265 2025-06-18 10:27:57
this is a test.txt f 0 2025-06-18 10:48:03
error.h f 1062 2025-06-18 10:27:57
...
La cuestión es que es necesario hacer el parsing del comando introducido por el usuario, como decía más arriba. Como no va a haber espacios en los nombres de los archivos, el parsing es trivial. Por ejemplo, en C# sería:
List<string> Parse(string cmd)
{
return cmd.Trim().Split();
}
Pero... ¿Y en C? En C volvemos a los viejos tiempos en cuanto a la manipulación de cadenas. Todas las cadenas tienen un tamaño fijo en memoria que en el caso del comando del usuario, se trata de 8191 caracteres (más el cero que marca el final).
Si no quieres utilizar el heap. como es mi caso (o al menos, tan poco como sea posible), tendrías que crear un vector de cadenas de caracteres. Cada una de estas cadenas tendría una longitud de... pues potencialmente, 8181 caracteres. Así que la idea sería crear, para tan solo el nombre del comando y nueve argumentos, unos 80 Kb de espacio. Si se quieren soportar más parámetros, el tamaño se dispara. ¿Qué podemos hacer?
La programación en bajo nivel, y aquí está la lección que aprendí, no es limpia. Tomemos como ejemplo strtok(cadena, delimitadores), que "divide" una cadena de caracteres en tokens... ¡añadiendo ceros en la propia cadena!
Efectivamente, si tenemos una cadena como la siguiente (el símbolo de dólar marca el final de la cadena, donde hay un cero), en el vector de caracteres cmd
,
ls ../ -la$
La transformación a realizar es la siguiente:
ls$../$-la$
Crearíamos los siguientes punteros:
char * cmd_name;
char * args[MAX_CMD_ARGS];
Y mantendríamos la siguiente estructura:
cmd_name = cmd;
args[ 0 ] = cmd + 3;
args[ 1 ] = cmd + 7;
args[ 2 ] = NULL;
Es mucho más complejo, ¿no?... ¡Aunque también más divertido!