logo

utils

~/.local/bin tools and git-hooks git clone https://hacktivis.me/git/utils.git

base64.c (3229B)


  1. // Collection of Unix tools, comparable to coreutils
  2. // SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include <assert.h> /* assert */
  6. #include <errno.h> /* errno */
  7. #include <fcntl.h> /* open(), O_RDONLY */
  8. #include <stdint.h> /* uint8_t */
  9. #include <stdio.h> /* fprintf(), BUFSIZ */
  10. #include <string.h> /* strerror(), strncmp() */
  11. #include <unistd.h> /* read(), write(), close(), getopt(), opt* */
  12. // 64(26+26+10+2)
  13. static char b64_encmap[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  14. static size_t c_out = 0;
  15. // TODO: -w option ; 76 is lowest of all base64 related RFCs
  16. static size_t wrap_nl = 76;
  17. static inline uint8_t
  18. b64_get(uint8_t pos)
  19. {
  20. assert(pos <= 64);
  21. return b64_encmap[pos];
  22. }
  23. static void
  24. b64encode(uint8_t out[4], uint8_t in[3])
  25. {
  26. out[0] = b64_get(in[0] >> 2);
  27. out[1] = b64_get((uint8_t)(((in[0] & 0x03) << 4) | (in[1] >> 4)));
  28. out[2] = b64_get((uint8_t)(((in[1] & 0x0f) << 2) | (in[2] >> 6)));
  29. out[3] = b64_get(in[2] & 0x3f);
  30. }
  31. static int
  32. encode(int fd, const char *fdname)
  33. {
  34. ssize_t c = 0;
  35. char ibuf[3];
  36. while((c = read(fd, ibuf, sizeof(ibuf))) > 0)
  37. {
  38. uint8_t obuf[4] = "----";
  39. ssize_t pad = 0;
  40. assert(pad >= 0);
  41. for(; c < 3; pad++, c++)
  42. {
  43. ibuf[c] = 0;
  44. }
  45. assert(c == 3);
  46. b64encode(obuf, (uint8_t *)ibuf);
  47. for(; pad > 0; pad--)
  48. {
  49. obuf[4 - pad] = '=';
  50. }
  51. if(write(1, (char *)obuf, 4) != 4)
  52. {
  53. fprintf(stderr, "base64: Error writing: %s\n", strerror(errno));
  54. return 1;
  55. }
  56. c_out += 4;
  57. if(wrap_nl != 0 && c_out >= wrap_nl)
  58. {
  59. c_out = 0;
  60. if(write(1, "\n", 1) != 1)
  61. {
  62. fprintf(stderr, "base64: Error writing: %s\n", strerror(errno));
  63. return 1;
  64. }
  65. }
  66. }
  67. if(c < 0)
  68. {
  69. fprintf(stderr, "base64: Error reading ‘%s’: %s\n", fdname, strerror(errno));
  70. return 1;
  71. }
  72. return 0;
  73. }
  74. static int
  75. decode(int fd, const char *fdname)
  76. {
  77. return 1;
  78. }
  79. int
  80. main(int argc, char *argv[])
  81. {
  82. int (*process)(int, const char *) = &encode;
  83. int c = 0, ret = 0;
  84. while((c = getopt(argc, argv, ":d")) != -1)
  85. {
  86. switch(c)
  87. {
  88. case 'd':
  89. process = &decode;
  90. break;
  91. case ':':
  92. fprintf(stderr, "base64: Error: Missing operand for option: ‘-%c’\n", optopt);
  93. return 1;
  94. case '?':
  95. fprintf(stderr, "base64: Error: Unrecognised option: ‘-%c’\n", optopt);
  96. return 1;
  97. }
  98. }
  99. argc -= optind;
  100. argv += optind;
  101. if(argc <= 0)
  102. {
  103. ret = process(0, "<stdin>");
  104. goto end;
  105. }
  106. for(int argi = 0; argi < argc; argi++)
  107. {
  108. if(strncmp(argv[argi], "-", 2) == 0)
  109. {
  110. if(process(0, "<stdin>") != 0)
  111. {
  112. ret = 1;
  113. goto end;
  114. }
  115. }
  116. else if(strncmp(argv[argi], "--", 3) == 0)
  117. {
  118. continue;
  119. }
  120. else
  121. {
  122. int fd = open(argv[argi], O_RDONLY);
  123. if(fd < 0)
  124. {
  125. fprintf(stderr, "base64: Error opening ‘%s’: %s\n", argv[argi], strerror(errno));
  126. ret = 1;
  127. goto end;
  128. }
  129. if(process(fd, argv[argi]) != 0)
  130. {
  131. ret = 1;
  132. goto end;
  133. }
  134. if(close(fd) < 0)
  135. {
  136. fprintf(stderr, "base64: Error closing ‘%s’: %s\n", argv[argi], strerror(errno));
  137. ret = 1;
  138. goto end;
  139. }
  140. }
  141. }
  142. end:
  143. if(c_out > 0)
  144. {
  145. printf("\n");
  146. }
  147. return ret;
  148. }