Deleted Added
full compact
ctm_rmail.c (6576) ctm_rmail.c (6658)
1/*
2 * Accept one (or more) ASCII encoded chunks that together make a compressed
3 * CTM delta. Decode them and reconstruct the deltas. Any completed
4 * deltas may be passed to ctm for unpacking.
5 *
6 * Author: Stephen McKay
7 *
8 * NOTICE: This is free software. I hope you get some use from this program.
9 * In return you should think about all the nice people who give away software.
10 * Maybe you should write some free software too.
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <errno.h>
17#include <unistd.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20#include "error.h"
21#include "options.h"
22
23#define CTM_STATUS ".ctm_status"
24
25char *piece_dir = NULL; /* Where to store pieces of deltas. */
26char *delta_dir = NULL; /* Where to store completed deltas. */
27char *base_dir = NULL; /* The tree to apply deltas to. */
28int delete_after = 0; /* Delete deltas after ctm applies them. */
29
30void apply_complete(void);
31int read_piece(char *input_file);
32int combine_if_complete(char *delta, int pce, int npieces);
33int decode_line(char *line, char *out_buf);
34
35/*
36 * If given a '-p' flag, read encoded delta pieces from stdin or file
37 * arguments, decode them and assemble any completed deltas. If given
38 * a '-b' flag, pass any completed deltas to 'ctm' for application to
39 * the source tree. The '-d' flag is mandatory, but either of '-p' or
40 * '-b' can be omitted. If given the '-l' flag, notes and errors will
41 * be timestamped and written to the given file.
42 *
43 * Exit status is 0 for success or 1 for indigestible input. That is,
44 * 0 means the encode input pieces were decoded and stored, and 1 means
45 * some input was discarded. If a delta fails to apply, this won't be
46 * reflected in the exit status. In this case, the delta is left in
47 * 'deltadir'.
48 */
49int
50main(int argc, char **argv)
51 {
52 char *log_file = NULL;
53 int status = 0;
54
55 err_prog_name(argv[0]);
56
57 OPTIONS("[-D] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
58 FLAG('D', delete_after)
59 STRING('p', piece_dir)
60 STRING('d', delta_dir)
61 STRING('b', base_dir)
62 STRING('l', log_file)
63 ENDOPTS
64
65 if (delta_dir == NULL)
66 usage();
67
68 if (piece_dir == NULL && (base_dir == NULL || argc > 1))
69 usage();
70
71 if (log_file != NULL)
72 err_set_log(log_file);
73
74 if (argc <= 1)
75 {
76 if (piece_dir != NULL)
77 status = read_piece(NULL);
78 }
79 else
80 {
81 while (*++argv != NULL)
82 status |= read_piece(*argv);
83 }
84
85 if (base_dir != NULL)
86 apply_complete();
87
88 return status;
89 }
90
91
92/*
93 * Construct the file name of a piece of a delta.
94 */
95#define mk_piece_name(fn,d,p,n) \
96 sprintf((fn), "%s/%s+%d-%d", piece_dir, (d), (p), (n))
97
98/*
99 * Construct the file name of an assembled delta.
100 */
101#define mk_delta_name(fn,d) \
102 sprintf((fn), "%s/%s", delta_dir, (d))
103
104/*
105 * If the next required delta is now present, let ctm lunch on it and any
106 * contiguous deltas.
107 */
108void
109apply_complete()
110 {
111 int i, dn;
112 FILE *fp, *ctm;
113 struct stat sb;
114 char class[20];
115 char delta[30];
116 char fname[1000];
117 char buf[2000];
118 char junk[2];
119 char here[1000];
120
121 sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
122 if ((fp = fopen(fname, "r")) == NULL)
123 return;
124
125 i = fscanf(fp, "%s %d %c", class, &dn, junk);
126 fclose(fp);
127 if (i != 2)
128 return;
129
130 /*
131 * We might need to convert the delta filename to an absolute pathname.
132 */
133 here[0] = '\0';
134 if (delta_dir[0] != '/')
135 {
136 getcwd(here, sizeof(here)-1);
137 i = strlen(here) - 1;
138 if (i >= 0 && here[i] != '/')
139 {
140 here[++i] = '/';
141 here[++i] = '\0';
142 }
143 }
144
145 /*
146 * Keep applying deltas until we run out or something bad happens.
147 */
148 for (;;)
149 {
150 sprintf(delta, "%s.%04d.gz", class, ++dn);
151 mk_delta_name(fname, delta);
152
153 if (stat(fname, &sb) < 0)
154 return;
155
156 sprintf(buf, "(cd %s && /usr/sbin/ctm %s%s) 2>&1",
157 base_dir, here, fname);
158 if ((ctm = popen(buf, "r")) == NULL)
159 {
160 err("ctm failed to apply %s", delta);
161 return;
162 }
163
164 while (fgets(buf, sizeof(buf), ctm) != NULL)
165 {
166 i = strlen(buf) - 1;
167 if (i >= 0 && buf[i] == '\n')
168 buf[i] = '\0';
169 err("ctm: %s", buf);
170 }
171
172 if (pclose(ctm) != 0)
173 {
174 err("ctm failed to apply %s", delta);
175 return;
176 }
177
178 if (delete_after)
179 unlink(fname);
180
181 err("%s applied%s", delta, delete_after ? " and deleted" : "");
182 }
183 }
184
185
186/*
187 * This cheap plastic checksum effectively rotates our checksum-so-far
188 * left one, then adds the character. We only want 16 bits of it, and
189 * don't care what happens to the rest. It ain't much, but it's small.
190 */
191#define add_ck(sum,x) \
192 ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
193
194
195/*
196 * Decode the data between BEGIN and END, and stash it in the staging area.
197 * Multiple pieces can be present in a single file, bracketed by BEGIN/END.
198 * If we have all pieces of a delta, combine them. Returns 0 on success,
199 * and 1 for any sort of failure.
200 */
201int
202read_piece(char *input_file)
203 {
204 int status = 0;
205 FILE *ifp, *ofp = 0;
206 int decoding = 0;
207 int line_no = 0;
208 int i, n;
209 int pce, npieces;
210 unsigned claimed_cksum;
211 unsigned short cksum = 0;
212 char out_buf[200];
213 char line[200];
214 char delta[30];
215 char pname[1000];
1/*
2 * Accept one (or more) ASCII encoded chunks that together make a compressed
3 * CTM delta. Decode them and reconstruct the deltas. Any completed
4 * deltas may be passed to ctm for unpacking.
5 *
6 * Author: Stephen McKay
7 *
8 * NOTICE: This is free software. I hope you get some use from this program.
9 * In return you should think about all the nice people who give away software.
10 * Maybe you should write some free software too.
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <errno.h>
17#include <unistd.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20#include "error.h"
21#include "options.h"
22
23#define CTM_STATUS ".ctm_status"
24
25char *piece_dir = NULL; /* Where to store pieces of deltas. */
26char *delta_dir = NULL; /* Where to store completed deltas. */
27char *base_dir = NULL; /* The tree to apply deltas to. */
28int delete_after = 0; /* Delete deltas after ctm applies them. */
29
30void apply_complete(void);
31int read_piece(char *input_file);
32int combine_if_complete(char *delta, int pce, int npieces);
33int decode_line(char *line, char *out_buf);
34
35/*
36 * If given a '-p' flag, read encoded delta pieces from stdin or file
37 * arguments, decode them and assemble any completed deltas. If given
38 * a '-b' flag, pass any completed deltas to 'ctm' for application to
39 * the source tree. The '-d' flag is mandatory, but either of '-p' or
40 * '-b' can be omitted. If given the '-l' flag, notes and errors will
41 * be timestamped and written to the given file.
42 *
43 * Exit status is 0 for success or 1 for indigestible input. That is,
44 * 0 means the encode input pieces were decoded and stored, and 1 means
45 * some input was discarded. If a delta fails to apply, this won't be
46 * reflected in the exit status. In this case, the delta is left in
47 * 'deltadir'.
48 */
49int
50main(int argc, char **argv)
51 {
52 char *log_file = NULL;
53 int status = 0;
54
55 err_prog_name(argv[0]);
56
57 OPTIONS("[-D] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
58 FLAG('D', delete_after)
59 STRING('p', piece_dir)
60 STRING('d', delta_dir)
61 STRING('b', base_dir)
62 STRING('l', log_file)
63 ENDOPTS
64
65 if (delta_dir == NULL)
66 usage();
67
68 if (piece_dir == NULL && (base_dir == NULL || argc > 1))
69 usage();
70
71 if (log_file != NULL)
72 err_set_log(log_file);
73
74 if (argc <= 1)
75 {
76 if (piece_dir != NULL)
77 status = read_piece(NULL);
78 }
79 else
80 {
81 while (*++argv != NULL)
82 status |= read_piece(*argv);
83 }
84
85 if (base_dir != NULL)
86 apply_complete();
87
88 return status;
89 }
90
91
92/*
93 * Construct the file name of a piece of a delta.
94 */
95#define mk_piece_name(fn,d,p,n) \
96 sprintf((fn), "%s/%s+%d-%d", piece_dir, (d), (p), (n))
97
98/*
99 * Construct the file name of an assembled delta.
100 */
101#define mk_delta_name(fn,d) \
102 sprintf((fn), "%s/%s", delta_dir, (d))
103
104/*
105 * If the next required delta is now present, let ctm lunch on it and any
106 * contiguous deltas.
107 */
108void
109apply_complete()
110 {
111 int i, dn;
112 FILE *fp, *ctm;
113 struct stat sb;
114 char class[20];
115 char delta[30];
116 char fname[1000];
117 char buf[2000];
118 char junk[2];
119 char here[1000];
120
121 sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
122 if ((fp = fopen(fname, "r")) == NULL)
123 return;
124
125 i = fscanf(fp, "%s %d %c", class, &dn, junk);
126 fclose(fp);
127 if (i != 2)
128 return;
129
130 /*
131 * We might need to convert the delta filename to an absolute pathname.
132 */
133 here[0] = '\0';
134 if (delta_dir[0] != '/')
135 {
136 getcwd(here, sizeof(here)-1);
137 i = strlen(here) - 1;
138 if (i >= 0 && here[i] != '/')
139 {
140 here[++i] = '/';
141 here[++i] = '\0';
142 }
143 }
144
145 /*
146 * Keep applying deltas until we run out or something bad happens.
147 */
148 for (;;)
149 {
150 sprintf(delta, "%s.%04d.gz", class, ++dn);
151 mk_delta_name(fname, delta);
152
153 if (stat(fname, &sb) < 0)
154 return;
155
156 sprintf(buf, "(cd %s && /usr/sbin/ctm %s%s) 2>&1",
157 base_dir, here, fname);
158 if ((ctm = popen(buf, "r")) == NULL)
159 {
160 err("ctm failed to apply %s", delta);
161 return;
162 }
163
164 while (fgets(buf, sizeof(buf), ctm) != NULL)
165 {
166 i = strlen(buf) - 1;
167 if (i >= 0 && buf[i] == '\n')
168 buf[i] = '\0';
169 err("ctm: %s", buf);
170 }
171
172 if (pclose(ctm) != 0)
173 {
174 err("ctm failed to apply %s", delta);
175 return;
176 }
177
178 if (delete_after)
179 unlink(fname);
180
181 err("%s applied%s", delta, delete_after ? " and deleted" : "");
182 }
183 }
184
185
186/*
187 * This cheap plastic checksum effectively rotates our checksum-so-far
188 * left one, then adds the character. We only want 16 bits of it, and
189 * don't care what happens to the rest. It ain't much, but it's small.
190 */
191#define add_ck(sum,x) \
192 ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
193
194
195/*
196 * Decode the data between BEGIN and END, and stash it in the staging area.
197 * Multiple pieces can be present in a single file, bracketed by BEGIN/END.
198 * If we have all pieces of a delta, combine them. Returns 0 on success,
199 * and 1 for any sort of failure.
200 */
201int
202read_piece(char *input_file)
203 {
204 int status = 0;
205 FILE *ifp, *ofp = 0;
206 int decoding = 0;
207 int line_no = 0;
208 int i, n;
209 int pce, npieces;
210 unsigned claimed_cksum;
211 unsigned short cksum = 0;
212 char out_buf[200];
213 char line[200];
214 char delta[30];
215 char pname[1000];
216 char tname[1000];
216 char junk[2];
217
218 ifp = stdin;
219 if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
220 {
221 err("cannot open '%s' for reading", input_file);
222 return 1;
223 }
224
225 while (fgets(line, sizeof(line), ifp) != NULL)
226 {
227 line_no++;
228
229 /*
230 * Look for the beginning of an encoded piece.
231 */
232 if (!decoding)
233 {
234 char *s;
235
236 if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c",
237 delta, &pce, &npieces, junk) != 3)
238 continue;
239
240 while ((s = strchr(delta, '/')) != NULL)
241 *s = '_';
242
243 mk_piece_name(pname, delta, pce, npieces);
217 char junk[2];
218
219 ifp = stdin;
220 if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
221 {
222 err("cannot open '%s' for reading", input_file);
223 return 1;
224 }
225
226 while (fgets(line, sizeof(line), ifp) != NULL)
227 {
228 line_no++;
229
230 /*
231 * Look for the beginning of an encoded piece.
232 */
233 if (!decoding)
234 {
235 char *s;
236
237 if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c",
238 delta, &pce, &npieces, junk) != 3)
239 continue;
240
241 while ((s = strchr(delta, '/')) != NULL)
242 *s = '_';
243
244 mk_piece_name(pname, delta, pce, npieces);
244 if ((ofp = fopen(pname, "w")) == NULL)
245 sprintf(tname,"tmp.%s",pname);
246 if ((ofp = fopen(tname, "w")) == NULL)
245 {
247 {
246 err("cannot open '%s' for writing", pname);
248 err("cannot open '%s' for writing", tname);
247 status++;
248 continue;
249 }
250
251 cksum = 0xffff;
252 decoding++;
253 continue;
254 }
255
256 /*
257 * We are decoding. Stop if we see the end flag.
258 */
259 if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
260 {
261 int e;
262
263 decoding = 0;
264
265 fflush(ofp);
266 e = ferror(ofp);
267 fclose(ofp);
268
269 if (e)
249 status++;
250 continue;
251 }
252
253 cksum = 0xffff;
254 decoding++;
255 continue;
256 }
257
258 /*
259 * We are decoding. Stop if we see the end flag.
260 */
261 if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
262 {
263 int e;
264
265 decoding = 0;
266
267 fflush(ofp);
268 e = ferror(ofp);
269 fclose(ofp);
270
271 if (e)
270 err("error writing %s", pname);
272 err("error writing %s", tname);
271
272 if (cksum != claimed_cksum)
273 err("checksum: read %d, calculated %d", claimed_cksum, cksum);
274
275 if (e || cksum != claimed_cksum)
276 {
277 err("%s %d/%d discarded", delta, pce, npieces);
273
274 if (cksum != claimed_cksum)
275 err("checksum: read %d, calculated %d", claimed_cksum, cksum);
276
277 if (e || cksum != claimed_cksum)
278 {
279 err("%s %d/%d discarded", delta, pce, npieces);
278 unlink(pname);
280 unlink(tname);
279 status++;
280 continue;
281 }
282
281 status++;
282 continue;
283 }
284
285 if (rename(tname, pname) < 0)
286 {
287 err("error renaming %s to %s",tname,pname);
288 unlink(tname);
289 status++;
290 continue;
291 }
292
283 err("%s %d/%d stored", delta, pce, npieces);
284
285 if (!combine_if_complete(delta, pce, npieces))
286 status++;
287 continue;
288 }
289
290 /*
291 * Must be a line of encoded data. Decode it, sum it, and save it.
292 */
293 n = decode_line(line, out_buf);
294 if (n < 0)
295 {
296 err("line %d: illegal character: '%c'", line_no, line[-n]);
297 err("%s %d/%d discarded", delta, pce, npieces);
298
299 fclose(ofp);
300 unlink(pname);
301
302 status++;
303 decoding = 0;
304 continue;
305 }
306
307 for (i = 0; i < n; i++)
308 add_ck(cksum, out_buf[i]);
309
310 fwrite(out_buf, sizeof(char), n, ofp);
311 }
312
313 if (decoding)
314 {
315 err("truncated file");
316 err("%s %d/%d discarded", delta, pce, npieces);
317
318 fclose(ofp);
319 unlink(pname);
320
321 status++;
322 }
323
324 if (ferror(ifp))
325 {
326 err("error reading %s", input_file == NULL ? "stdin" : input_file);
327 status++;
328 }
329
330 if (input_file != NULL)
331 fclose(ifp);
332
333 return (status != 0);
334 }
335
336
337/*
338 * Put the pieces together to form a delta, if they are all present.
339 * Returns 1 on success (even if we didn't do anything), and 0 on failure.
340 */
341int
342combine_if_complete(char *delta, int pce, int npieces)
343 {
344 int i;
345 FILE *dfp, *pfp;
346 int c;
347 struct stat sb;
348 char pname[1000];
349 char dname[1000];
350
351 /*
352 * All here?
353 */
354 for (i = 1; i <= npieces; i++)
355 {
356 if (i == pce)
357 continue;
358 mk_piece_name(pname, delta, i, npieces);
359 if (stat(pname, &sb) < 0)
360 return 1;
361 }
362
363 mk_delta_name(dname, delta);
364
365 /*
366 * We can probably just rename() it in to place if it is a small delta.
367 */
368 if (npieces == 1)
369 {
370 mk_piece_name(pname, delta, 1, 1);
371 if (rename(pname, dname) == 0)
372 {
373 err("%s complete", delta);
374 return 1;
375 }
376 }
377
378 if ((dfp = fopen(dname, "w")) == NULL)
379 {
380 err("cannot open '%s' for writing", dname);
381 return 0;
382 }
383
384 /*
385 * Ok, the hard way. Reconstruct the delta by reading each piece in order.
386 */
387 for (i = 1; i <= npieces; i++)
388 {
389 mk_piece_name(pname, delta, i, npieces);
390 if ((pfp = fopen(pname, "r")) == NULL)
391 {
392 err("cannot open '%s' for reading", pname);
393 fclose(dfp);
394 unlink(dname);
395 return 0;
396 }
397 while ((c = getc(pfp)) != EOF)
398 putc(c, dfp);
399 fclose(pfp);
400 }
401 fflush(dfp);
402 if (ferror(dfp))
403 {
404 err("error writing '%s'", dname);
405 fclose(dfp);
406 unlink(dname);
407 return 0;
408 }
409 fclose(dfp);
410
411 /*
412 * Throw the pieces away.
413 */
414 for (i = 1; i <= npieces; i++)
415 {
416 mk_piece_name(pname, delta, i, npieces);
417 unlink(pname);
418 }
419
420 err("%s complete", delta);
421 return 1;
422 }
423
424
425/*
426 * MIME BASE64 decode table.
427 */
428static unsigned char from_b64[0x80] =
429 {
430 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
431 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
432 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
433 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
434 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
435 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
436 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
437 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
438 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
439 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
440 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
441 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
442 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
443 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
444 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
445 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
446 };
447
448
449/*
450 * Decode a line of ASCII into binary. Returns the number of bytes in
451 * the output buffer, or < 0 on indigestable input. Error output is
452 * the negative of the index of the inedible character.
453 */
454int
455decode_line(char *line, char *out_buf)
456 {
457 unsigned char *ip = (unsigned char *)line;
458 unsigned char *op = (unsigned char *)out_buf;
459 unsigned long bits;
460 unsigned x;
461
462 for (;;)
463 {
464 if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
465 break;
466 bits = x << 18;
467 ip++;
468 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
469 {
470 bits |= x << 12;
471 *op++ = bits >> 16;
472 ip++;
473 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
474 {
475 bits |= x << 6;
476 *op++ = bits >> 8;
477 ip++;
478 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
479 {
480 bits |= x;
481 *op++ = bits;
482 ip++;
483 }
484 }
485 }
486 }
487
488 if (*ip == '\0' || *ip == '\n')
489 return op - (unsigned char *)out_buf;
490 else
491 return -(ip - (unsigned char *)line);
492 }
293 err("%s %d/%d stored", delta, pce, npieces);
294
295 if (!combine_if_complete(delta, pce, npieces))
296 status++;
297 continue;
298 }
299
300 /*
301 * Must be a line of encoded data. Decode it, sum it, and save it.
302 */
303 n = decode_line(line, out_buf);
304 if (n < 0)
305 {
306 err("line %d: illegal character: '%c'", line_no, line[-n]);
307 err("%s %d/%d discarded", delta, pce, npieces);
308
309 fclose(ofp);
310 unlink(pname);
311
312 status++;
313 decoding = 0;
314 continue;
315 }
316
317 for (i = 0; i < n; i++)
318 add_ck(cksum, out_buf[i]);
319
320 fwrite(out_buf, sizeof(char), n, ofp);
321 }
322
323 if (decoding)
324 {
325 err("truncated file");
326 err("%s %d/%d discarded", delta, pce, npieces);
327
328 fclose(ofp);
329 unlink(pname);
330
331 status++;
332 }
333
334 if (ferror(ifp))
335 {
336 err("error reading %s", input_file == NULL ? "stdin" : input_file);
337 status++;
338 }
339
340 if (input_file != NULL)
341 fclose(ifp);
342
343 return (status != 0);
344 }
345
346
347/*
348 * Put the pieces together to form a delta, if they are all present.
349 * Returns 1 on success (even if we didn't do anything), and 0 on failure.
350 */
351int
352combine_if_complete(char *delta, int pce, int npieces)
353 {
354 int i;
355 FILE *dfp, *pfp;
356 int c;
357 struct stat sb;
358 char pname[1000];
359 char dname[1000];
360
361 /*
362 * All here?
363 */
364 for (i = 1; i <= npieces; i++)
365 {
366 if (i == pce)
367 continue;
368 mk_piece_name(pname, delta, i, npieces);
369 if (stat(pname, &sb) < 0)
370 return 1;
371 }
372
373 mk_delta_name(dname, delta);
374
375 /*
376 * We can probably just rename() it in to place if it is a small delta.
377 */
378 if (npieces == 1)
379 {
380 mk_piece_name(pname, delta, 1, 1);
381 if (rename(pname, dname) == 0)
382 {
383 err("%s complete", delta);
384 return 1;
385 }
386 }
387
388 if ((dfp = fopen(dname, "w")) == NULL)
389 {
390 err("cannot open '%s' for writing", dname);
391 return 0;
392 }
393
394 /*
395 * Ok, the hard way. Reconstruct the delta by reading each piece in order.
396 */
397 for (i = 1; i <= npieces; i++)
398 {
399 mk_piece_name(pname, delta, i, npieces);
400 if ((pfp = fopen(pname, "r")) == NULL)
401 {
402 err("cannot open '%s' for reading", pname);
403 fclose(dfp);
404 unlink(dname);
405 return 0;
406 }
407 while ((c = getc(pfp)) != EOF)
408 putc(c, dfp);
409 fclose(pfp);
410 }
411 fflush(dfp);
412 if (ferror(dfp))
413 {
414 err("error writing '%s'", dname);
415 fclose(dfp);
416 unlink(dname);
417 return 0;
418 }
419 fclose(dfp);
420
421 /*
422 * Throw the pieces away.
423 */
424 for (i = 1; i <= npieces; i++)
425 {
426 mk_piece_name(pname, delta, i, npieces);
427 unlink(pname);
428 }
429
430 err("%s complete", delta);
431 return 1;
432 }
433
434
435/*
436 * MIME BASE64 decode table.
437 */
438static unsigned char from_b64[0x80] =
439 {
440 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
441 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
442 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
443 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
444 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
445 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
446 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
447 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
448 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
449 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
450 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
451 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
452 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
453 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
454 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
455 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
456 };
457
458
459/*
460 * Decode a line of ASCII into binary. Returns the number of bytes in
461 * the output buffer, or < 0 on indigestable input. Error output is
462 * the negative of the index of the inedible character.
463 */
464int
465decode_line(char *line, char *out_buf)
466 {
467 unsigned char *ip = (unsigned char *)line;
468 unsigned char *op = (unsigned char *)out_buf;
469 unsigned long bits;
470 unsigned x;
471
472 for (;;)
473 {
474 if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
475 break;
476 bits = x << 18;
477 ip++;
478 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
479 {
480 bits |= x << 12;
481 *op++ = bits >> 16;
482 ip++;
483 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
484 {
485 bits |= x << 6;
486 *op++ = bits >> 8;
487 ip++;
488 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
489 {
490 bits |= x;
491 *op++ = bits;
492 ip++;
493 }
494 }
495 }
496 }
497
498 if (*ip == '\0' || *ip == '\n')
499 return op - (unsigned char *)out_buf;
500 else
501 return -(ip - (unsigned char *)line);
502 }