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 }