logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/
commit: fa27634fad776bfdec053e1b92290b0b16592ad3
parent d1a535971579513d3ab6dd3b517194c1f90cef01
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Thu, 29 May 2025 09:03:47 +0200

cmd/echo: write escape() function similar to cmd/printf to avoid malloc

Diffstat:

Mcmd/echo.c153+++++++++++++++++++++++++++++++++++--------------------------------------------
1 file changed, 67 insertions(+), 86 deletions(-)

diff --git a/cmd/echo.c b/cmd/echo.c @@ -5,10 +5,73 @@ #define _POSIX_C_SOURCE 200809L #include <stdbool.h> #include <stdio.h> // perror -#include <stdlib.h> // malloc #include <string.h> // strlen #include <unistd.h> // write +static void +escape(char *str, size_t *len) +{ + char *start = str; + char *store = str; + + for(; str < (start + *len); ++str, ++store) + { + if(*str != '\\') + { + *store = *str; + continue; + } + + switch(*++str) + { + case '\\': // POSIX + default: + *store = *str; + break; + case 'a': + *store = '\a'; + break; + case 'b': + *store = '\b'; + break; + case 'c': + goto escape_end; + case 'f': + *store = '\f'; + break; + case 'n': + *store = '\n'; + break; + case 'r': + *store = '\r'; + break; + case 't': + *store = '\t'; + break; + case 'v': + *store = '\v'; + break; + case '0': /* \0 \0n \0nn \0nnn */ + { + int value = 0; + int i = 0; + for(; i < 4 && str[i] >= '0' && str[i] <= '7'; i++) + { + value <<= 3; + value += str[i] - '0'; + } + str += (i - 1); + *store = (char)value; + break; + } + } + } + +escape_end: + *store = '\0'; + *len = (size_t)(store - start); +} + int main(int argc, char *argv[]) { @@ -71,96 +134,14 @@ main(int argc, char *argv[]) if(opt_n) arg_len--; // no newline - char *d = *argv; - size_t d_len = arg_len; + if(opt_e) escape(*argv, &arg_len); - char *newd = NULL; - if(opt_e) - { - newd = malloc(arg_len); - if(!newd) - { - fprintf(stderr, "echo: error: Failed to allocate for a copy of the strings\n"); - return 1; - } - - int di = 0; - - for(size_t argi = 0; argi < arg_len; argi++) - { - if(d[argi] != '\\') - { - newd[di++] = d[argi]; - continue; - } - - switch(d[++argi]) - { - case 'a': - newd[di++] = '\a'; - break; - case 'b': - newd[di++] = '\b'; - break; - case 'c': - newd[di++] = '\0'; - goto p_break; - case 'f': - newd[di++] = '\f'; - break; - case 'n': - newd[di++] = '\n'; - break; - case 'r': - newd[di++] = '\r'; - break; - case 't': - newd[di++] = '\t'; - break; - case 'v': - newd[di++] = '\v'; - break; - case '\\': - newd[di++] = '\\'; - break; - case '0': /* \0 \0n \0nn \0nnn */ - { - int nl = 1; // skip leading 0 - int num = 0; - for(; nl < 4; nl++) - { - if(d[argi + nl] >= '0' && d[argi + nl] <= '7') - { - num = (num * 8) + (d[argi + nl] - '0'); - } - else - break; - } - newd[di++] = num; - argi += (nl - 1); - break; - } - default: - newd[di++] = d[argi]; - break; - } - } - - newd[di] = '\0'; - p_break: - d = newd; - d_len = di; - } - - ssize_t nwrite = write(1, d, d_len); - if(nwrite < (ssize_t)d_len) + ssize_t nwrite = write(1, *argv, arg_len); + if(nwrite < (ssize_t)arg_len) { perror("echo: error: Failed writing"); - if(opt_e) free(newd); return 1; } - if(opt_e) free(newd); - return 0; }