1/* See LICENSE file for copyright and license details. */
2
3
4#include <sys/stat.h>
5
6#include <stdio.h>
7#include <string.h>
8
9#include "util.h"
10
11static unsigned int
12b64e(unsigned char *b)
13{
14 unsigned int o, p = b[2] | (b[1] << 8) | (b[0] << 16);
15 const char b64et[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
16
17 o = b64et[p & 0x3f]; p >>= 6;
18 o = (o << 8) | b64et[p & 0x3f]; p >>= 6;
19 o = (o << 8) | b64et[p & 0x3f]; p >>= 6;
20 o = (o << 8) | b64et[p & 0x3f];
21
22 return o;
23}
24
25static void
26uuencodeb64(FILE *fp, const char *name, const char *s)
27{
28 struct stat st;
29 ssize_t n, m = 0;
30 unsigned char buf[45], *pb;
31 unsigned int out[sizeof(buf)/3 + 1], *po;
32
33 if (fstat(fileno(fp), &st) < 0)
34 eprintf("fstat %s:", s);
35 printf("begin-base64 %o %s\n", st.st_mode & 0777, name);
36 /* write line by line */
37 while ((n = fread(buf, 1, sizeof(buf), fp))) {
38 /* clear old buffer if converting with non-multiple of 3 */
39 if (n != sizeof(buf) && (m = n % 3) != 0) {
40 buf[n] = '\0'; /* m == 2 */
41 if (m == 1) buf[n+1] = '\0'; /* m == 1 */
42 }
43 for (pb = buf, po = out; pb < buf + n; pb += 3)
44 *po++ = b64e(pb);
45 if (m != 0) {
46 unsigned int mask = 0xffffffff, dest = 0x3d3d3d3d;
47 /* m==2 -> 0x00ffffff; m==1 -> 0x0000ffff */
48 mask >>= ((3-m) << 3);
49 po[-1] = (po[-1] & mask) | (dest & ~mask);
50 }
51 *po++ = '\n';
52 fwrite(out, 1, (po - out) * sizeof(unsigned int) - 3, stdout);
53 }
54 if (ferror(fp))
55 eprintf("'%s' read error:", s);
56 puts("====");
57}
58
59static void
60uuencode(FILE *fp, const char *name, const char *s)
61{
62 struct stat st;
63 unsigned char buf[45], *p;
64 ssize_t n;
65 int ch;
66
67 if (fstat(fileno(fp), &st) < 0)
68 eprintf("fstat %s:", s);
69 printf("begin %o %s\n", st.st_mode & 0777, name);
70 while ((n = fread(buf, 1, sizeof(buf), fp))) {
71 ch = ' ' + (n & 0x3f);
72 putchar(ch == ' ' ? '`' : ch);
73 for (p = buf; n > 0; n -= 3, p += 3) {
74 if (n < 3) {
75 p[2] = '\0';
76 if (n < 2)
77 p[1] = '\0';
78 }
79 ch = ' ' + ((p[0] >> 2) & 0x3f);
80 putchar(ch == ' ' ? '`' : ch);
81 ch = ' ' + (((p[0] << 4) | ((p[1] >> 4) & 0xf)) & 0x3f);
82 putchar(ch == ' ' ? '`' : ch);
83 ch = ' ' + (((p[1] << 2) | ((p[2] >> 6) & 0x3)) & 0x3f);
84 putchar(ch == ' ' ? '`' : ch);
85 ch = ' ' + (p[2] & 0x3f);
86 putchar(ch == ' ' ? '`' : ch);
87 }
88 putchar('\n');
89 }
90 if (ferror(fp))
91 eprintf("'%s' read error:", s);
92 printf("%c\nend\n", '`');
93}
94
95static void
96usage(void)
97{
98 eprintf("usage: %s [-m] [file] name\n", argv0);
99}
100
101// ?man uuencode: encode binary file
102// ?man arguments: file] name
103// ?man encode a binary file into ascii text
104int
105main(int argc, char *argv[])
106{
107 FILE *fp = NULL;
108 void (*uuencode_f)(FILE *, const char *, const char *) = uuencode;
109 int ret = 0;
110
111 ARGBEGIN {
112 // ?man -m: specify mode or limit
113 case 'm':
114 uuencode_f = uuencodeb64;
115 break;
116 default:
117 usage();
118 } ARGEND
119
120 if (!argc || argc > 2)
121 usage();
122
123 if (argc == 1 || !strcmp(argv[0], "-")) {
124 uuencode_f(stdin, argv[0], "<stdin>");
125 } else {
126 if (!(fp = fopen(argv[0], "r")))
127 eprintf("fopen %s:", argv[0]);
128 uuencode_f(fp, argv[1], argv[0]);
129 }
130
131 ret |= fp && fshut(fp, argv[0]);
132 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
133
134 return ret;
135}