morpheus-base

morpheus base system
git clone git://git.2f30.org/morpheus-base
Log | Files | Refs

uudecode.c (4131B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <errno.h>
      3 #include <limits.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <sys/stat.h>
      8 #include <sys/types.h>
      9 #include <unistd.h>
     10 
     11 #include "text.h"
     12 #include "util.h"
     13 
     14 static void uudecode(FILE *, FILE *);
     15 static void parseheader(FILE *, const char *, const char *, mode_t *, char **);
     16 static FILE *parsefile(const char *);
     17 
     18 static void
     19 usage(void)
     20 {
     21 	eprintf("usage: %s [file]\n", argv0);
     22 }
     23 
     24 int
     25 main(int argc, char *argv[])
     26 {
     27 	FILE *fp = NULL, *nfp = NULL;
     28 	char *fname;
     29 	mode_t mode = 0;
     30 
     31 	ARGBEGIN {
     32 	case 'm':
     33 		eprintf("-m not implemented\n");
     34 	default:
     35 		usage();
     36 	} ARGEND;
     37 
     38 	if (argc > 1)
     39 		usage();
     40 
     41 	if (argc == 0) {
     42 		parseheader(stdin, "<stdin>", "begin ", &mode, &fname);
     43 		if (!(nfp = parsefile(fname)))
     44 			eprintf("fopen %s:", fname);
     45 		uudecode(stdin, nfp);
     46 	} else {
     47 		if (!(fp = fopen(argv[0], "r")))
     48 			eprintf("fopen %s:", argv[0]);
     49 		parseheader(fp, argv[0], "begin ", &mode, &fname);
     50 		if (!(nfp = parsefile(fname)))
     51 			eprintf("fopen %s:", fname);
     52 		uudecode(fp, nfp);
     53 	}
     54 
     55 	if (chmod(fname, mode) < 0)
     56 		eprintf("chmod %s:", fname);
     57 	if (fp)
     58 		fclose(fp);
     59 	if (nfp)
     60 		fclose(nfp);
     61 
     62 	return 0;
     63 }
     64 
     65 static FILE *
     66 parsefile(const char *fname)
     67 {
     68 	struct stat st;
     69 	int ret;
     70 
     71 	if (strcmp(fname, "/dev/stdout") == 0)
     72 		return stdout;
     73 	ret = lstat(fname, &st);
     74 	/* if it is a new file, try to open it */
     75 	if (ret < 0 && errno == ENOENT)
     76 		goto tropen;
     77 	if (ret < 0) {
     78 		weprintf("lstat %s:", fname);
     79 		return NULL;
     80 	}
     81 	if (!S_ISREG(st.st_mode)) {
     82 		weprintf("for safety uudecode operates only on regular files and /dev/stdout\n");
     83 		return NULL;
     84 	}
     85 tropen:
     86 	return fopen(fname,"w");
     87 }
     88 
     89 static void
     90 parseheader(FILE *fp, const char *s, const char *header, mode_t *mode, char **fname)
     91 {
     92 	char bufs[PATH_MAX + 11]; /* len header + mode + maxname */
     93 	char *p, *q;
     94 	size_t n;
     95 
     96 	if (!fgets(bufs, sizeof(bufs), fp))
     97 		if (ferror(fp))
     98 			eprintf("%s: read error:", s);
     99 	if (bufs[0] == '\0' || feof(fp))
    100 		eprintf("empty or nil header string\n");
    101 	if (!(p = strchr(bufs, '\n')))
    102 		eprintf("header string too long or non-newline terminated file\n");
    103 	p = bufs;
    104 	if (strncmp(bufs, header, strlen(header)) != 0)
    105 		eprintf("malformed header prefix\n");
    106 	p += strlen(header);
    107 	if (!(q = strchr(p, ' ')))
    108 		eprintf("malformed mode string in header\n");
    109 	*q++ = '\0';
    110 	/* now mode should be null terminated, q points to fname */
    111 	*mode = parsemode(p, *mode, 0);
    112 	n = strlen(q);
    113 	while (n > 0 && (q[n - 1] == '\n' || q[n - 1] == '\r'))
    114 		q[--n] = '\0';
    115 	if (n > 0)
    116 		*fname = q;
    117 }
    118 
    119 static void
    120 uudecode(FILE *fp, FILE *outfp)
    121 {
    122 	char *bufb = NULL, *p;
    123 	size_t n = 0;
    124 	ssize_t len;
    125 	int ch, i;
    126 
    127 #define DEC(c)  (((c) - ' ') & 077) /* single character decode */
    128 #define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) )
    129 #define OUT_OF_RANGE(c) eprintf("character %c out of range: [%d-%d]", (c), 1 + ' ', 077 + ' ' + 1)
    130 
    131 	while ((len = getline(&bufb, &n, fp)) != -1) {
    132 		p = bufb;
    133 		/* trim newlines */
    134 		if (len && bufb[len - 1] != '\n')
    135 			bufb[len - 1] = '\0';
    136 		else
    137 			eprintf("no newline found, aborting\n");
    138 		/* check for last line */
    139 		if ((i = DEC(*p)) <= 0)
    140 			break;
    141 		for (++p; i > 0; p += 4, i -= 3) {
    142 			if (i >= 3) {
    143 				if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) &&
    144 				      IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
    145 					OUT_OF_RANGE(*p);
    146 
    147 				ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
    148 				putc(ch, outfp);
    149 				ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
    150 				putc(ch, outfp);
    151 				ch = DEC(p[2]) << 6 | DEC(p[3]);
    152 				putc(ch, outfp);
    153 			} else {
    154 				if (i >= 1) {
    155 					if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
    156 						OUT_OF_RANGE(*p);
    157 
    158 					ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
    159 					putc(ch, outfp);
    160 				}
    161 				if (i >= 2) {
    162 					if (!(IS_DEC(*(p + 1)) &&
    163 					      IS_DEC(*(p + 2))))
    164 						OUT_OF_RANGE(*p);
    165 
    166 					ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
    167 					putc(ch, outfp);
    168 				}
    169 			}
    170 		}
    171 		if (ferror(fp))
    172 			eprintf("read error:");
    173 	}
    174 	/* check for end or fail */
    175 	len = getline(&bufb, &n, fp);
    176 	if (len < 3 || strncmp(bufb, "end", 3) != 0 || bufb[3] != '\n')
    177 		eprintf("invalid uudecode footer \"end\" not found\n");
    178 	free(bufb);
    179 }