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];
|
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 }
|