logo

utils-std

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

base64.c (7113B)


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