logo

utils-std

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

base64.c (6773B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-FileCopyrightText: 2018 The NetBSD Foundation, Inc.
  4. // SPDX-License-Identifier: MPL-2.0 AND BSD-2-Clause
  5. #define _POSIX_C_SOURCE 200809L
  6. #include <assert.h> /* assert */
  7. #include <ctype.h> /* isspace */
  8. #include <errno.h> /* errno */
  9. #include <stdint.h> /* uint8_t */
  10. #include <stdio.h> /* fopen(), fprintf(), BUFSIZ */
  11. #include <stdlib.h> /* abort */
  12. #include <string.h> /* strerror(), strncmp() */
  13. #include <unistd.h> /* read(), write(), close(), getopt(), opt* */
  14. // 64(26+26+10+2) + NULL
  15. static const char *b64_encmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  16. static size_t c_out = 0;
  17. // 76 is lowest of all base64 related RFCs
  18. static long wrap_nl = 76;
  19. static int
  20. xputc(int c, FILE *stream)
  21. {
  22. assert(errno == 0);
  23. if(fputc(c, stream) == EOF)
  24. {
  25. fprintf(stderr, "base64: Error writing: %s\n", strerror(errno));
  26. errno = 0;
  27. return 1;
  28. }
  29. assert(errno == 0);
  30. int err = ferror(stream);
  31. if(err != 0)
  32. {
  33. fprintf(stderr, "base64: Error writing: %s\n", strerror(err));
  34. errno = 0;
  35. return 1;
  36. }
  37. return 0;
  38. }
  39. static inline uint8_t
  40. b64_get(uint8_t pos)
  41. {
  42. assert(pos <= 64);
  43. return b64_encmap[pos];
  44. }
  45. static void
  46. b64encode(uint8_t out[4], uint8_t in[3])
  47. {
  48. out[0] = b64_get(in[0] >> 2);
  49. out[1] = b64_get((uint8_t)(((in[0] & 0x03) << 4) | (in[1] >> 4)));
  50. out[2] = b64_get((uint8_t)(((in[1] & 0x0f) << 2) | (in[2] >> 6)));
  51. out[3] = b64_get(in[2] & 0x3f);
  52. }
  53. static int
  54. encode(FILE *fin, const char *name)
  55. {
  56. while(1)
  57. {
  58. uint8_t obuf[4] = "----";
  59. uint8_t ibuf[3] = {0, 0, 0};
  60. uint8_t pad = 0;
  61. size_t c = 0;
  62. for(; c < 3; c++)
  63. {
  64. int buf = getc(fin);
  65. if(buf == EOF)
  66. {
  67. break;
  68. }
  69. ibuf[c] = buf;
  70. }
  71. if(c == 0)
  72. {
  73. return 0;
  74. };
  75. assert(errno == 0);
  76. if(ferror(fin))
  77. {
  78. fprintf(stderr, "base64: Error reading ‘%s’: %s\n", name, strerror(errno));
  79. errno = 0;
  80. return 1;
  81. }
  82. for(; c < 3; pad++, c++)
  83. {
  84. ibuf[c] = 0;
  85. }
  86. assert(c == 3);
  87. assert(pad <= 3);
  88. b64encode(obuf, (uint8_t *)ibuf);
  89. for(; pad > 0; pad--)
  90. {
  91. obuf[4 - pad] = '=';
  92. }
  93. if(fwrite((char *)obuf, 4, 1, stdout) <= 0)
  94. {
  95. fprintf(stderr, "base64: Error writing: %s\n", strerror(errno));
  96. errno = 0;
  97. return 1;
  98. }
  99. c_out += 4;
  100. assert(errno == 0);
  101. if(wrap_nl != 0 && (c_out + 4) > wrap_nl)
  102. {
  103. c_out = 0;
  104. if(xputc('\n', stdout) != 0) return 1;
  105. }
  106. if(feof(fin))
  107. {
  108. assert(errno == 0);
  109. if(fflush(stdout))
  110. {
  111. fprintf(stderr, "base64: Error writing: %s\n", strerror(errno));
  112. errno = 0;
  113. return 1;
  114. }
  115. int err = ferror(stdout);
  116. if(err != 0)
  117. {
  118. fprintf(stderr, "base64: Error writing: %s\n", strerror(err));
  119. return 1;
  120. }
  121. return 0;
  122. }
  123. }
  124. abort(); // unreachable
  125. }
  126. // This function is based on NetBSD's code, which contains the following notices:
  127. // Copyright (c) 2018 The NetBSD Foundation, Inc.
  128. //
  129. // This code is derived from software contributed to The NetBSD Foundation
  130. // by Christos Zoulas.
  131. static int
  132. decode(FILE *fin, const char *name)
  133. {
  134. int c = 0;
  135. uint8_t out = 0;
  136. int state = 0;
  137. while((c = getc(fin)) != EOF)
  138. {
  139. if(isspace(c)) continue;
  140. if(c == '=') break;
  141. uint8_t b = 65;
  142. for(uint8_t i = 0; i < 64; i++)
  143. {
  144. if(b64_encmap[i] == c)
  145. {
  146. b = i;
  147. break;
  148. }
  149. }
  150. if(b > 64)
  151. {
  152. fprintf(stderr, "base64: Invalid character %c\n", c);
  153. return 1;
  154. }
  155. //fprintf(stderr, "state: %d | c: %c (%d) | b: %d\n", state, c, c, b);
  156. switch(state)
  157. {
  158. case 0:
  159. out = (uint8_t)(b << 2);
  160. break;
  161. case 1:
  162. out |= b >> 4;
  163. if(xputc(out, stdout) != 0) return 1;
  164. out = (uint8_t)((b & 0xf) << 4);
  165. break;
  166. case 2:
  167. out |= b >> 2;
  168. if(xputc(out, stdout) != 0) return 1;
  169. out = (uint8_t)((b & 0x3) << 6);
  170. break;
  171. case 3:
  172. out |= b;
  173. if(xputc(out, stdout) != 0) return 1;
  174. out = 0;
  175. break;
  176. default:
  177. abort();
  178. }
  179. state = (state + 1) & 3;
  180. }
  181. //fprintf(stderr, "[outside] state: %d | c: %c (%d) | out: %d\n", state, c, c, out);
  182. if(c == '=')
  183. {
  184. switch(state)
  185. {
  186. case 0:
  187. case 1:
  188. fprintf(stderr, "base64: Invalid character %c (early '=')\n", c);
  189. return 1;
  190. case 2:
  191. while(isspace(c = getc(fin)))
  192. ;
  193. if(c != '=')
  194. {
  195. fprintf(stderr, "base64: Invalid character %c (not '=')\n", c);
  196. return 1;
  197. }
  198. /* fallthrough */
  199. case 3:
  200. while(isspace(c = getc(fin)))
  201. ;
  202. if(c != EOF)
  203. {
  204. fprintf(stderr, "base64: Invalid character %c (not EOF)\n", c);
  205. return 1;
  206. }
  207. return 0;
  208. default:
  209. abort();
  210. }
  211. }
  212. assert(c == EOF || state == 0);
  213. assert(errno == 0);
  214. if(fflush(stdout))
  215. {
  216. fprintf(stderr, "base64: Error writing: %s\n", strerror(errno));
  217. errno = 0;
  218. return 1;
  219. }
  220. assert(errno == 0);
  221. int err = ferror(stdout);
  222. if(err != 0)
  223. {
  224. fprintf(stderr, "base64: Error writing: %s\n", strerror(err));
  225. errno = 0;
  226. return 1;
  227. }
  228. return 0;
  229. }
  230. int
  231. main(int argc, char *argv[])
  232. {
  233. int (*process)(FILE *, const char *) = &encode;
  234. int c = 0, ret = 0;
  235. while((c = getopt(argc, argv, ":dw:")) != -1)
  236. {
  237. switch(c)
  238. {
  239. case 'd':
  240. process = &decode;
  241. break;
  242. case 'w':
  243. errno = 0;
  244. char *e = NULL;
  245. wrap_nl = strtol(optarg, &e, 10);
  246. // extraneous characters is invalid
  247. if(e && *e != 0) errno = EINVAL;
  248. if(errno != 0)
  249. {
  250. fprintf(stderr, "base64: Option `-w %s`: %s\n", optarg, strerror(errno));
  251. return 1;
  252. }
  253. break;
  254. case ':':
  255. fprintf(stderr, "base64: Error: Missing operand for option: ‘-%c’\n", optopt);
  256. return 1;
  257. case '?':
  258. fprintf(stderr, "base64: Error: Unrecognised option: ‘-%c’\n", optopt);
  259. return 1;
  260. }
  261. }
  262. argc -= optind;
  263. argv += optind;
  264. if(argc <= 0)
  265. {
  266. ret = process(stdin, "<stdin>");
  267. goto end;
  268. }
  269. for(int argi = 0; argi < argc; argi++)
  270. {
  271. if(strncmp(argv[argi], "-", 2) == 0)
  272. {
  273. if(process(stdin, "<stdin>") != 0)
  274. {
  275. ret = 1;
  276. goto end;
  277. }
  278. }
  279. else if(strncmp(argv[argi], "--", 3) == 0)
  280. {
  281. continue;
  282. }
  283. else
  284. {
  285. FILE *fin = fopen(argv[argi], "r");
  286. if(fin == NULL || ferror(fin) != 0)
  287. {
  288. fprintf(stderr, "base64: Error opening ‘%s’: %s\n", argv[argi], strerror(errno));
  289. ret = 1;
  290. goto end;
  291. }
  292. if(process(fin, argv[argi]) != 0)
  293. {
  294. ret = 1;
  295. goto end;
  296. }
  297. if(fclose(fin) < 0)
  298. {
  299. fprintf(stderr, "base64: Error closing ‘%s’: %s\n", argv[argi], strerror(errno));
  300. ret = 1;
  301. goto end;
  302. }
  303. }
  304. }
  305. end:
  306. if(wrap_nl != 0 && c_out > 0)
  307. {
  308. printf("\n");
  309. }
  310. if(fclose(stdin) != 0)
  311. {
  312. fprintf(stderr, "base64: Error closing <stdin>: %s\n", strerror(errno));
  313. ret = 1;
  314. }
  315. if(fclose(stdout) != 0)
  316. {
  317. fprintf(stderr, "base64: Error closing <stdout>: %s\n", strerror(errno));
  318. ret = 1;
  319. }
  320. return ret;
  321. }