logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

echo.c (2799B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include <stdbool.h>
  6. #include <stdio.h> // perror
  7. #include <stdlib.h> // malloc
  8. #include <string.h> // strlen
  9. #include <unistd.h> // write
  10. int
  11. main(int argc, char *argv[])
  12. {
  13. size_t arg_len = 0;
  14. bool opt_n = false, opt_e = false;
  15. argc--;
  16. argv++;
  17. for(; argc > 0;)
  18. {
  19. if(argv[0][0] != '-') break;
  20. /* '-' and '--' needs to be passed as-is so no argc--,argv++ */
  21. if(argv[0][1] == '\0' || (argv[0][1] == '-' && argv[0][2] == '\0')) break;
  22. for(int i = 1; argv[0][i] != '\0'; i++)
  23. {
  24. switch(argv[0][i])
  25. {
  26. case 'n':
  27. opt_n = true;
  28. break;
  29. case 'e':
  30. opt_e = true;
  31. break;
  32. case 'E':
  33. opt_e = false;
  34. break;
  35. default:
  36. fprintf(stderr, "echo: warning: unknown option '-%c'\n", argv[0][i]);
  37. break;
  38. }
  39. }
  40. argc--;
  41. argv++;
  42. }
  43. for(int i = 0; i < argc; i++)
  44. {
  45. size_t len = strlen(argv[i]);
  46. argv[i][len] = ' ';
  47. arg_len += len + 1; // str + space
  48. }
  49. if(arg_len == 0)
  50. {
  51. if(opt_n) return 0;
  52. if(write(1, "\n", 1) < 1)
  53. {
  54. perror("echo: error: Failed writing");
  55. return 1;
  56. }
  57. return 0;
  58. }
  59. else
  60. argv[0][arg_len - 1] = '\n';
  61. if(opt_n) arg_len--; // no newline
  62. char *d = *argv;
  63. size_t d_len = arg_len;
  64. char *newd = NULL;
  65. if(opt_e)
  66. {
  67. newd = malloc(arg_len);
  68. if(!newd)
  69. {
  70. fprintf(stderr, "echo: error: Failed to allocate for a copy of the strings\n");
  71. return 1;
  72. }
  73. int di = 0;
  74. for(size_t argi = 0; argi < arg_len; argi++)
  75. {
  76. if(d[argi] != '\\')
  77. {
  78. newd[di++] = d[argi];
  79. continue;
  80. }
  81. switch(d[++argi])
  82. {
  83. case 'a':
  84. newd[di++] = '\a';
  85. break;
  86. case 'b':
  87. newd[di++] = '\b';
  88. break;
  89. case 'c':
  90. newd[di++] = '\0';
  91. goto p_break;
  92. case 'f':
  93. newd[di++] = '\f';
  94. break;
  95. case 'n':
  96. newd[di++] = '\n';
  97. break;
  98. case 'r':
  99. newd[di++] = '\r';
  100. break;
  101. case 't':
  102. newd[di++] = '\t';
  103. break;
  104. case 'v':
  105. newd[di++] = '\v';
  106. break;
  107. case '\\':
  108. newd[di++] = '\\';
  109. break;
  110. case '0': /* \0 \0n \0nn \0nnn */
  111. {
  112. int nl = 1; // skip leading 0
  113. int num = 0;
  114. for(; nl < 4; nl++)
  115. {
  116. if(d[argi + nl] >= '0' && d[argi + nl] <= '7')
  117. {
  118. num = (num * 8) + (d[argi + nl] - '0');
  119. }
  120. else
  121. break;
  122. }
  123. newd[di++] = num;
  124. argi += (nl - 1);
  125. break;
  126. }
  127. default:
  128. newd[di++] = d[argi];
  129. break;
  130. }
  131. }
  132. newd[di] = '\0';
  133. p_break:
  134. d = newd;
  135. d_len = di;
  136. }
  137. ssize_t nwrite = write(1, d, d_len);
  138. if(nwrite < (ssize_t)d_len)
  139. {
  140. perror("echo: error: Failed writing");
  141. if(opt_e) free(newd);
  142. return 1;
  143. }
  144. if(opt_e) free(newd);
  145. return 0;
  146. }