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