Implement cancellation points for sigwait, sigtimedwait and sigwaitinfo.
[dragonfly.git] / usr.bin / uudecode / uudecode.c
1 /*-
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1983, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)uudecode.c       8.2 (Berkeley) 4/2/94
35  * $FreeBSD: src/usr.bin/uudecode/uudecode.c,v 1.13.2.6 2003/04/07 20:10:33 fanf Exp $
36  * $DragonFly: src/usr.bin/uudecode/uudecode.c,v 1.3 2004/12/22 11:25:51 liamfoy Exp $
37  */
38
39 /*
40  * uudecode [file ...]
41  *
42  * create the specified file, decoding as you go.
43  * used with uuencode.
44  */
45 #include <sys/param.h>
46 #include <sys/socket.h>
47 #include <sys/stat.h>
48
49 #include <netinet/in.h>
50
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <libgen.h>
55 #include <pwd.h>
56 #include <resolv.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 static const char *infile, *outfile;
63 static FILE *infp, *outfp;
64 static int base64, cflag, iflag, oflag, pflag, rflag, sflag;
65
66 static void     usage(void);
67 static int      decode(void);
68 static int      decode2(void);
69 static int      uu_decode(void);
70 static int      base64_decode(void);
71
72 int
73 main(int argc, char *argv[])
74 {
75         int rval, ch;
76
77         if (strcmp(basename(argv[0]), "b64decode") == 0)
78                 base64 = 1;
79
80         while ((ch = getopt(argc, argv, "cimo:prs")) != -1) {
81                 switch(ch) {
82                 case 'c':
83                         if (oflag || rflag)
84                                 usage();
85                         cflag = 1; /* multiple uudecode'd files */
86                         break;
87                 case 'i':
88                         iflag = 1; /* ask before override files */
89                         break;
90                 case 'm':
91                         base64 = 1;
92                         break;
93                 case 'o':
94                         if (cflag || pflag || rflag || sflag)
95                                 usage();
96                         oflag = 1; /* output to the specified file */
97                         sflag = 1; /* do not strip pathnames for output */
98                         outfile = optarg; /* set the output filename */
99                         break;
100                 case 'p':
101                         if (oflag)
102                                 usage();
103                         pflag = 1; /* print output to stdout */
104                         break;
105                 case 'r':
106                         if (cflag || oflag)
107                                 usage();
108                         rflag = 1; /* decode raw data */
109                         break;
110                 case 's':
111                         if (oflag)
112                                 usage();
113                         sflag = 1; /* do not strip pathnames for output */
114                         break;
115                 default:
116                         usage();
117                 }
118         }
119         argc -= optind;
120         argv += optind;
121
122         if (*argv) {
123                 rval = 0;
124                 do {
125                         infp = fopen(infile = *argv, "r");
126                         if (infp == NULL) {
127                                 warn("%s", *argv);
128                                 rval = 1;
129                                 continue;
130                         }
131                         rval |= decode();
132                         fclose(infp);
133                 } while (*++argv);
134         } else {
135                 infile = "stdin";
136                 infp = stdin;
137                 rval = decode();
138         }
139         exit(rval);
140 }
141
142 static int
143 decode(void)
144 {
145         int r, v;
146
147         if (rflag) {
148                 /* relaxed alternative to decode2() */
149                 outfile = "/dev/stdout";
150                 outfp = stdout;
151                 if (base64)
152                         return (base64_decode());
153                 else
154                         return (uu_decode());
155         }
156         v = decode2();
157         if (v == EOF) {
158                 warnx("%s: missing or bad \"begin\" line", infile);
159                 return (1);
160         }
161         for (r = v; cflag; r |= v) {
162                 v = decode2();
163                 if (v == EOF)
164                         break;
165         }
166         return (r);
167 }
168
169 static int
170 decode2(void)
171 {
172         int flags, fd;
173         size_t n, m;
174         char *p, *q;
175         void *handle;
176         mode_t mode;
177         struct passwd *pw;
178         struct stat st;
179         char buf[MAXPATHLEN+1];
180
181         base64 = 0;
182         /* search for header line */
183         for (;;) {
184                 if (fgets(buf, sizeof(buf), infp) == NULL)
185                         return (EOF);
186                 p = buf;
187                 if (strncmp(p, "begin-base64 ", 13) == 0) {
188                         base64 = 1;
189                         p += 13;
190                 } else if (strncmp(p, "begin ", 6) == 0)
191                         p += 6;
192                 else
193                         continue;
194                 /* p points to mode */
195                 q = strchr(p, ' ');
196                 if (q == NULL)
197                         continue;
198                 *q++ = '\0';
199                 /* q points to filename */
200                 n = strlen(q);
201                 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r'))
202                         q[--n] = '\0';
203                 /* found valid header? */
204                 if (n > 0)
205                         break;
206         }
207
208         errno = 0;
209         if ((handle = setmode(p)) == NULL) {
210                 if (!errno)
211                         warnx("invalid file mode: %s", infile);
212                 else
213                         warn("setmode malloc failed: %s", infile);
214
215                 return(1);
216         }
217
218         mode = getmode(handle, 0) & 0666;
219         free(handle);
220
221         if (sflag) {
222                 /* don't strip, so try ~user/file expansion */
223                 p = NULL;
224                 pw = NULL;
225                 if (*q == '~')
226                         p = strchr(q, '/');
227                 if (p != NULL) {
228                         *p = '\0';
229                         pw = getpwnam(q + 1);
230                         *p = '/';
231                 }
232                 if (pw != NULL) {
233                         n = strlen(pw->pw_dir);
234                         if (buf + n > p) {
235                                 /* make room */
236                                 m = strlen(p);
237                                 if (sizeof(buf) < n + m) {
238                                         warnx("%s: bad output filename",
239                                             infile);
240                                         return (1);
241                                 }
242                                 p = memmove(buf + n, p, m);
243                         }
244                         q = memcpy(p - n, pw->pw_dir, n);
245                 }
246         } else {
247                 /* strip down to leaf name */
248                 p = strrchr(q, '/');
249                 if (p != NULL)
250                         q = p + 1;
251         }
252         if (!oflag)
253                 outfile = q;
254
255         /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */
256         if (pflag || strcmp(outfile, "/dev/stdout") == 0)
257                 outfp = stdout;
258         else {
259                 flags = O_WRONLY|O_CREAT|O_EXCL;
260                 if (lstat(outfile, &st) == 0) {
261                         if (iflag) {
262                                 warnc(EEXIST, "%s: %s", infile, outfile);
263                                 return (0);
264                         }
265                         switch (st.st_mode & S_IFMT) {
266                         case S_IFREG:
267                         case S_IFLNK:
268                                 /* avoid symlink attacks */
269                                 if (unlink(outfile) == 0 || errno == ENOENT)
270                                         break;
271                                 warn("%s: unlink %s", infile, outfile);
272                                 return (1);
273                         case S_IFDIR:
274                                 warnc(EISDIR, "%s: %s", infile, outfile);
275                                 return (1);
276                         default:
277                                 if (oflag) {
278                                         /* trust command-line names */
279                                         flags &= ~O_EXCL;
280                                         break;
281                                 }
282                                 warnc(EEXIST, "%s: %s", infile, outfile);
283                                 return (1);
284                         }
285                 } else if (errno != ENOENT) {
286                         warn("%s: %s", infile, outfile);
287                         return (1);
288                 }
289                 if ((fd = open(outfile, flags, mode)) < 0 ||
290                     (outfp = fdopen(fd, "w")) == NULL) {
291                         warn("%s: %s", infile, outfile);
292                         return (1);
293                 }
294         }
295
296         if (base64)
297                 return (base64_decode());
298         else
299                 return (uu_decode());
300 }
301
302 static int
303 getline(char *buf, size_t size)
304 {
305         if (fgets(buf, size, infp) != NULL)
306                 return (2);
307         if (rflag)
308                 return (0);
309         warnx("%s: %s: short file", infile, outfile);
310         return (1);
311 }
312
313 static int
314 checkend(const char *ptr, const char *end, const char *msg)
315 {
316         size_t n;
317
318         n = strlen(end);
319         if (strncmp(ptr, end, n) != 0 ||
320             strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) {
321                 warnx("%s: %s: %s", infile, outfile, msg);
322                 return (1);
323         }
324         if (fclose(outfp) != 0) {
325                 warn("%s: %s", infile, outfile);
326                 return (1);
327         }
328         return (0);
329 }
330
331 static int
332 uu_decode(void)
333 {
334         int i, ch;
335         char *p;
336         char buf[MAXPATHLEN+1];
337
338         /* for each input line */
339         for (;;) {
340                 switch (getline(buf, sizeof(buf))) {
341                 case 0: return (0);
342                 case 1: return (1);
343                 }
344
345 #define DEC(c)  (((c) - ' ') & 077)             /* single character decode */
346 #define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) )
347
348 #define OUT_OF_RANGE do {                                               \
349         warnx("%s: %s: character out of range: [%d-%d]",                \
350             infile, outfile, 1 + ' ', 077 + ' ' + 1);                   \
351         return (1);                                                     \
352 } while (0)
353
354                 /*
355                  * `i' is used to avoid writing out all the characters
356                  * at the end of the file.
357                  */
358                 p = buf;
359                 if ((i = DEC(*p)) <= 0)
360                         break;
361                 for (++p; i > 0; p += 4, i -= 3)
362                         if (i >= 3) {
363                                 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) &&
364                                      IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
365                                         OUT_OF_RANGE;
366
367                                 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
368                                 putc(ch, outfp);
369                                 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
370                                 putc(ch, outfp);
371                                 ch = DEC(p[2]) << 6 | DEC(p[3]);
372                                 putc(ch, outfp);
373                         }
374                         else {
375                                 if (i >= 1) {
376                                         if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
377                                                 OUT_OF_RANGE;
378                                         ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
379                                         putc(ch, outfp);
380                                 }
381                                 if (i >= 2) {
382                                         if (!(IS_DEC(*(p + 1)) &&
383                                                 IS_DEC(*(p + 2))))
384                                                 OUT_OF_RANGE;
385
386                                         ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
387                                         putc(ch, outfp);
388                                 }
389                                 if (i >= 3) {
390                                         if (!(IS_DEC(*(p + 2)) &&
391                                                 IS_DEC(*(p + 3))))
392                                                 OUT_OF_RANGE;
393                                         ch = DEC(p[2]) << 6 | DEC(p[3]);
394                                         putc(ch, outfp);
395                                 }
396                         }
397         }
398         switch (getline(buf, sizeof(buf))) {
399         case 0:  return (0);
400         case 1:  return (1);
401         default: return (checkend(buf, "end", "no \"end\" line"));
402         }
403 }
404
405 static int
406 base64_decode(void)
407 {
408         int n;
409         char inbuf[MAXPATHLEN+1];
410         unsigned char outbuf[MAXPATHLEN * 4];
411
412         for (;;) {
413                 switch (getline(inbuf, sizeof(inbuf))) {
414                 case 0: return (0);
415                 case 1: return (1);
416                 }
417                 n = b64_pton(inbuf, outbuf, sizeof(outbuf));
418                 if (n < 0)
419                         break;
420                 fwrite(outbuf, 1, n, outfp);
421         }
422         return (checkend(inbuf, "====",
423                     "error decoding base64 input stream"));
424 }
425
426 static void
427 usage(void)
428 {
429         (void)fprintf(stderr,
430 "usage: uudecode [-cimprs] [file ...]\n"
431 "       uudecode [-i] -o output_file [file]\n"
432 "       b64decode [-cimprs] [file ...]\n"
433 "       b64decode [-i] -o output_file [file]\n");
434         exit(1);
435 }