|
|
1.1.1.4 ! root 1: /* fileio.c - I/O routines for PGP. ! 2: PGP: Pretty Good(tm) Privacy - public key cryptography for the masses. ! 3: ! 4: (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved. ! 5: The author assumes no liability for damages resulting from the use ! 6: of this software, even if the damage results from defects in this ! 7: software. No warranty is expressed or implied. ! 8: ! 9: Note that while most PGP source modules bear Philip Zimmermann's ! 10: copyright notice, many of them have been revised or entirely written ! 11: by contributors who frequently failed to put their names in their ! 12: code. Code that has been incorporated into PGP from other authors ! 13: was either originally published in the public domain or is used with ! 14: permission from the various authors. ! 15: ! 16: PGP is available for free to the public under certain restrictions. ! 17: See the PGP User's Guide (included in the release package) for ! 18: important information about licensing, patent restrictions on ! 19: certain algorithms, trademarks, copyrights, and export controls. ! 20: ! 21: Modified 16 Apr 92 - HAJK ! 22: Mods for support of VAX/VMS file system ! 23: ! 24: Modified 17 Nov 92 - HAJK ! 25: Change to temp file stuff for VMS. ! 26: */ ! 27: ! 28: #include <ctype.h> ! 29: #include <stdio.h> ! 30: #include <stdlib.h> ! 31: #include <string.h> ! 32: #include <errno.h> ! 33: #ifdef UNIX ! 34: #include <sys/types.h> ! 35: #include <sys/stat.h> ! 36: #include <fcntl.h> ! 37: #ifdef _BSD ! 38: #include <sys/param.h> ! 39: #endif ! 40: extern int errno; ! 41: #endif /* UNIX */ ! 42: #ifdef VMS ! 43: #include <file.h> ! 44: #endif ! 45: #include "random.h" ! 46: #include "mpilib.h" ! 47: #include "mpiio.h" ! 48: #include "fileio.h" ! 49: #include "language.h" ! 50: #include "pgp.h" ! 51: #include "exitpgp.h" ! 52: #include "charset.h" ! 53: #include "system.h" ! 54: #if defined(MSDOS) || defined(OS2) ! 55: #include <io.h> ! 56: #include <fcntl.h> ! 57: #endif ! 58: ! 59: #ifndef F_OK ! 60: #define F_OK 0 ! 61: #define X_OK 1 ! 62: #define W_OK 2 ! 63: #define R_OK 4 ! 64: #endif /* !F_OK */ ! 65: ! 66: /* ! 67: * DIRSEPS is a string of possible directory-separation characters ! 68: * The first one is the preferred one, which goes in between ! 69: * PGPPATH and the file name if PGPPATH is not terminated with a ! 70: * directory separator. ! 71: */ ! 72: ! 73: #if defined(MSDOS) || defined(__MSDOS__) ! 74: static char const DIRSEPS[] = "\\/:"; ! 75: #define BSLASH ! 76: ! 77: #elif defined(ATARI) ! 78: static char const DIRSEPS[] = "\\/:"; ! 79: #define BSLASH ! 80: ! 81: #elif defined(UNIX) ! 82: static char const DIRSEPS[] = "/"; ! 83: #define MULTIPLE_DOTS ! 84: ! 85: #elif defined(AMIGA) ! 86: static char const DIRSEPS[] = "/:"; ! 87: #define MULTIPLE_DOTS ! 88: ! 89: #elif defined(VMS) ! 90: static char const DIRSEPS[] = "]:"; /* Any more? */ ! 91: ! 92: #else ! 93: #error Unknown OS ! 94: #endif ! 95: ! 96: ! 97: /* 1st character of temporary file extension */ ! 98: #define TMP_EXT '$' /* extensions are '.$##' */ ! 99: ! 100: /* The PGPPATH environment variable */ ! 101: ! 102: static char PGPPATH[] = "PGPPATH"; ! 103: ! 104: /* Disk buffers, used here and in crypto.c */ ! 105: byte textbuf[DISKBUFSIZE]; ! 106: static unsigned textbuf2[2*DISKBUFSIZE/sizeof(unsigned)]; ! 107: ! 108: boolean file_exists(char *filename) ! 109: /* Returns TRUE iff file exists. */ ! 110: { ! 111: return access(filename, F_OK) == 0; ! 112: } /* file_exists */ ! 113: ! 114: static boolean is_regular_file(char *filename) ! 115: { ! 116: #ifdef S_ISREG ! 117: struct stat st; ! 118: return stat(filename, &st) != -1 && S_ISREG(st.st_mode); ! 119: #else ! 120: return TRUE; ! 121: #endif ! 122: } ! 123: ! 124: ! 125: /* ! 126: * This wipes a file with pseudo-random data. The purpose of this is to ! 127: * make sure no sensitive information is left on the disk. The use ! 128: * of pseudo-random data is to defeat disk compression drivers (such as ! 129: * Stacker and dblspace) so that we are guaranteed that the entire file ! 130: * has been overwritten. ! 131: * ! 132: * Note that the file MUST be open for read/write. ! 133: * ! 134: * It may not work to eliminate everything from non-volatile storage ! 135: * if the OS you're using does its own paging or swapping. Then ! 136: * it's an issue of how the OS's paging device is wiped, and you can ! 137: * only hope that the space will be reused within a few seconds. ! 138: * ! 139: * Also, some file systems (in particular, the Logging File System ! 140: * for Sprite) do not write new data in the same place as old data, ! 141: * defeating this wiping entirely. Fortunately, such systems ! 142: * usually don't need a swap file, and for small temp files, they ! 143: * do write-behind, so if you create and delete a file fast enough, ! 144: * it never gets written to disk at all. ! 145: */ ! 146: ! 147: /* ! 148: * The data is randomly generated with the size of the file as a seed. ! 149: * The data should be random and not leak information. If someone is ! 150: * examining deleted files, presumably they can reconstruct the file size, ! 151: * so that's not a secret. H'm... this wiping algorithm makes it easy to, ! 152: * given a block of data, find the size of the file it came from ! 153: * and the offset of this block within it. That in turn reveals ! 154: * something about the state of the disk's allocation tables when the ! 155: * file was used, possibly making it easier to find other files created ! 156: * at neaby times - such as plaintext files. Is this acceptable? ! 157: */ ! 158: ! 159: /* ! 160: * Theory of operation: We use the additive congruential RNG ! 161: * r[i] = r[i-24] + r[i-55], from Knuth, Vol. 2. This is fast ! 162: * and has a long enough period that there should be no repetitions ! 163: * in even a huge file. It is seeded with r[-55] through r[-1] ! 164: * using another polynomial-based RNG. We seed a linear feedback ! 165: * shift register (CRC generator) with the size of the swap file, ! 166: * and clock in 0 bits. Each 32 bits, the value of the generator is ! 167: * taken as the next integer. This is just to ensure a reasonably ! 168: * even mix of 1's and 0's in the initialization vector. ! 169: */ ! 170: ! 171: /* ! 172: * This is the CRC-32 polynomial, which should be okay for random ! 173: * number generation. ! 174: * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1 ! 175: * = 1 0000 0100 1100 0001 0001 1101 1011 0111 ! 176: * = 0x04c11db7 ! 177: */ ! 178: #define POLY 0x04c11db7 ! 179: ! 180: static int ! 181: wipeout(FILE *f) ! 182: { ! 183: unsigned *p1, *p2, *p3; ! 184: unsigned long len; ! 185: unsigned long t; ! 186: int i; ! 187: ! 188: /* Get the file size */ ! 189: fseek(f, 0L, SEEK_END); ! 190: len = ftell(f); ! 191: rewind(f); ! 192: ! 193: /* Seed of first RNG. Inverted to get more 1 bits */ ! 194: t = ~len; ! 195: ! 196: /* Initialize first 55 words of buf with pseudo-random stuff */ ! 197: p1 = (unsigned *)textbuf2 + 55; ! 198: do { ! 199: for (i = 0; i < 32; i++) ! 200: t = (t & 0x80000000) ? t<<1 ^ POLY : t<<1; ! 201: *--p1 = (unsigned)t; ! 202: } while (p1 > (unsigned *)textbuf2); ! 203: ! 204: while (len) { ! 205: /* Fill buffer with pseudo-random integers */ ! 206: ! 207: p3 = (unsigned *)textbuf2 + 55; ! 208: p2 = (unsigned *)textbuf2 + 24; ! 209: p1 = (unsigned *)textbuf2 + sizeof(textbuf2)/sizeof(*p1); ! 210: do { ! 211: *--p1 = *--p2 + *--p3; ! 212: } while (p2 > (unsigned *)textbuf2); ! 213: ! 214: p2 = (unsigned *)textbuf2 + sizeof(textbuf2)/sizeof(*p1); ! 215: do { ! 216: *--p1 = *--p2 + *--p3; ! 217: } while (p3 > (unsigned *)textbuf2); ! 218: ! 219: p3 = (unsigned *)textbuf2 + sizeof(textbuf2)/sizeof(*p3); ! 220: do { ! 221: *--p1 = *--p2 + *--p3; ! 222: } while (p1 > (unsigned *)textbuf2); ! 223: ! 224: /* Write it out - yes, we're ignoring errors */ ! 225: if (len > sizeof(textbuf2)) { ! 226: fwrite((char const *)textbuf2, sizeof(textbuf2), 1, f); ! 227: len -= sizeof(textbuf2); ! 228: } else { ! 229: fwrite((char const *)textbuf2, len, 1, f); ! 230: len = 0; ! 231: } ! 232: } ! 233: } ! 234: ! 235: ! 236: int wipefile(char *filename) ! 237: /* ! 238: * Completely overwrite and erase file, so that no sensitive ! 239: * information is left on the disk. ! 240: */ ! 241: { ! 242: FILE *f; ! 243: /* open file f for read/write, in binary (not text) mode...*/ ! 244: if ((f = fopen(filename,FOPRWBIN)) == NULL) ! 245: return -1; /* error - file can't be opened */ ! 246: wipeout(f); ! 247: fclose(f); ! 248: return 0; /* normal return */ ! 249: } /* wipefile */ ! 250: ! 251: char *file_tail (char *filename) ! 252: /* ! 253: * Returns the part of a filename after all directory specifiers. ! 254: */ ! 255: { ! 256: char *p; ! 257: char const *s = DIRSEPS; ! 258: ! 259: while (*s) { ! 260: p = strrchr(filename, *s); ! 261: if (p) ! 262: filename = p+1; ! 263: s++; ! 264: } ! 265: ! 266: return filename; ! 267: } ! 268: ! 269: ! 270: boolean has_extension(char *filename, char *extension) ! 271: /* return TRUE if extension matches the end of filename */ ! 272: { ! 273: int lf = strlen(filename); ! 274: int lx = strlen(extension); ! 275: ! 276: if (lf <= lx) ! 277: return FALSE; ! 278: return !strcmp(filename + lf - lx, extension); ! 279: } ! 280: ! 281: boolean is_tempfile(char *path) ! 282: /* return TRUE if path is a filename created by tempfile() */ ! 283: /* Filename matches "*.$[0-9][0-9]" */ ! 284: { ! 285: char *p = strrchr(path, '.'); ! 286: ! 287: return p != NULL && p[1] == TMP_EXT && ! 288: isdigit(p[2]) && isdigit(p[3]) && p[4] == '\0'; ! 289: } ! 290: ! 291: boolean no_extension(char *filename) ! 292: /* ! 293: * Returns TRUE if user left off file extension, allowing default. ! 294: * Note that the name is misleading if multiple dots are allowed. ! 295: * not_pgp_extension or something would be better. ! 296: */ ! 297: { ! 298: #ifdef MULTIPLE_DOTS /* filename can have more than one dot */ ! 299: if (has_extension(filename, ASC_EXTENSION) || ! 300: has_extension(filename, PGP_EXTENSION) || ! 301: has_extension(filename, SIG_EXTENSION) || ! 302: is_tempfile(filename)) ! 303: return FALSE; ! 304: else ! 305: return TRUE; ! 306: #else ! 307: filename = file_tail(filename); ! 308: ! 309: return strrchr(filename, '.') == NULL; ! 310: #endif ! 311: } /* no_extension */ ! 312: ! 313: ! 314: void drop_extension(char *filename) ! 315: /* deletes trailing ".xxx" file extension after the period. */ ! 316: { ! 317: if (!no_extension(filename)) ! 318: *strrchr(filename,'.') = '\0'; ! 319: } /* drop_extension */ ! 320: ! 321: ! 322: void default_extension(char *filename, char *extension) ! 323: /* append filename extension if there isn't one already. */ ! 324: { ! 325: if (no_extension(filename)) ! 326: strcat(filename,extension); ! 327: } /* default_extension */ ! 328: ! 329: #ifndef MAX_NAMELEN ! 330: #if defined(AMIGA) || defined(NeXT) || (defined(BSD) && BSD > 41) || (defined(sun) && defined(i386)) ! 331: #define MAX_NAMELEN 255 ! 332: #else ! 333: #include <limits.h> ! 334: #endif ! 335: #endif ! 336: ! 337: static void truncate_name(char *path, int ext_len) ! 338: /* truncate the filename so that an extension can be tacked on. */ ! 339: { ! 340: #ifdef UNIX /* for other systems this is a no-op */ ! 341: char *p; ! 342: #ifdef MAX_NAMELEN /* overrides the use of pathconf() */ ! 343: int namemax = MAX_NAMELEN; ! 344: #else ! 345: int namemax; ! 346: #ifdef _PC_NAME_MAX ! 347: char dir[MAX_PATH]; ! 348: ! 349: strcpy(dir, path); ! 350: if ((p = strrchr(dir, '/')) == NULL) { ! 351: strcpy(dir, "."); ! 352: } else { ! 353: if (p == dir) ! 354: ++p; ! 355: *p = '\0'; ! 356: } ! 357: if ((namemax = pathconf(dir, _PC_NAME_MAX)) <= ext_len) ! 358: return; ! 359: #else ! 360: #ifdef NAME_MAX ! 361: namemax = NAME_MAX; ! 362: #else ! 363: namemax = 14; ! 364: #endif /* NAME_MAX */ ! 365: #endif /* _PC_NAME_MAX */ ! 366: #endif /* MAX_NAMELEN */ ! 367: ! 368: if ((p = strrchr(path, '/')) == NULL) ! 369: p = path; ! 370: else ! 371: ++p; ! 372: if (strlen(p) > namemax - ext_len) { ! 373: if (verbose) ! 374: fprintf(pgpout, "Truncating filename '%s' ", path); ! 375: p[namemax - ext_len] = '\0'; ! 376: if (verbose) ! 377: fprintf(pgpout, "to '%s'\n", path); ! 378: } ! 379: #endif /* UNIX */ ! 380: } ! 381: ! 382: void force_extension(char *filename, char *extension) ! 383: /* change the filename extension. */ ! 384: { ! 385: drop_extension(filename); /* out with the old */ ! 386: truncate_name(filename, strlen(extension)); ! 387: strcat(filename,extension); /* in with the new */ ! 388: } /* force_extension */ ! 389: ! 390: ! 391: boolean getyesno(char default_answer) ! 392: /* ! 393: * Get yes/no answer from user, returns TRUE for yes, FALSE for no. ! 394: * First the translations are checked, if they don't match 'y' and 'n' ! 395: * are tried. ! 396: */ ! 397: { ! 398: char buf[8]; ! 399: static char yes[8], no[8]; ! 400: ! 401: if (yes[0] == '\0') { ! 402: strncpy(yes, LANG("y"), 7); ! 403: strncpy(no, LANG("n"), 7); ! 404: } ! 405: if (!batchmode) { /* return default answer in batchmode */ ! 406: getstring(buf,6,TRUE); /* echo keyboard input */ ! 407: strlwr(buf); ! 408: if (!strncmp(buf, no, strlen(no))) ! 409: return FALSE; ! 410: if (!strncmp(buf, yes, strlen(yes))) ! 411: return TRUE; ! 412: if (buf[0] == 'n') ! 413: return FALSE; ! 414: if (buf[0] == 'y') ! 415: return TRUE; ! 416: } ! 417: return default_answer == 'y' ? TRUE : FALSE; ! 418: } /* getyesno */ ! 419: ! 420: ! 421: ! 422: char *maybe_force_extension(char *filename, char *extension) ! 423: /* if user consents to it, change the filename extension. */ ! 424: { ! 425: static char newname[MAX_PATH]; ! 426: if (!has_extension(filename,extension)) { ! 427: strcpy(newname,filename); ! 428: force_extension(newname,extension); ! 429: if (!file_exists(newname)) { ! 430: fprintf(pgpout,LANG("\nShould '%s' be renamed to '%s' [Y/n]? "), ! 431: filename,newname); ! 432: if (getyesno('y')) ! 433: return newname; ! 434: } ! 435: } ! 436: return NULL; ! 437: } /* maybe_force_extension */ ! 438: ! 439: /* ! 440: * Add a trailing directory separator to a name, if absent. ! 441: */ ! 442: static void ! 443: addslash(char *name) ! 444: { ! 445: int i = strlen(name); ! 446: ! 447: if (i != 0 && !strchr(DIRSEPS, name[i-1])) { ! 448: name[i] = DIRSEPS[0]; ! 449: name[i+1] = '\0'; ! 450: } ! 451: } ! 452: ! 453: char *buildfilename(char *result, char *fname) ! 454: /* ! 455: * Builds a filename with a complete path specifier from the environmental ! 456: * variable PGPPATH. ! 457: */ ! 458: { ! 459: char const *s = getenv(PGPPATH); ! 460: ! 461: result[0] = '\0'; ! 462: ! 463: if (s && strlen(s) <= 50) { ! 464: strcpy(result, s); ! 465: } ! 466: #ifdef UNIX ! 467: /* On Unix, default to $HOME/.pgp, otherwise, current directory. */ ! 468: else { ! 469: s = getenv("HOME"); ! 470: if (s && strlen(s) <= 50) { ! 471: strcpy(result, s); ! 472: addslash(result); ! 473: strcat(result, ".pgp"); ! 474: } ! 475: } ! 476: #endif /* UNIX */ ! 477: ! 478: addslash(result); ! 479: strcat(result,fname); ! 480: return result; ! 481: } /* buildfilename */ ! 482: ! 483: char *buildsysfilename(char *result, char *fname) ! 484: { ! 485: buildfilename(result, fname); ! 486: #ifdef PGP_SYSTEM_DIR ! 487: if (file_exists(result)) ! 488: return result; ! 489: strcpy(result, PGP_SYSTEM_DIR); ! 490: strcat(result, fname); ! 491: if (file_exists(result)) ! 492: return result; ! 493: buildfilename(result, fname); /* Put name back for error */ ! 494: #endif ! 495: return result; ! 496: } ! 497: ! 498: ! 499: void file_to_canon(char *filename) ! 500: /* Convert filename to canonical form, with slashes as separators */ ! 501: { ! 502: #ifdef BSLASH ! 503: while (*filename) { ! 504: if (*filename == '\\') ! 505: *filename = '/'; ! 506: ++filename; ! 507: } ! 508: #endif ! 509: } ! 510: ! 511: ! 512: int write_error(FILE *f) ! 513: { ! 514: fflush(f); ! 515: if (ferror(f)) { ! 516: #ifdef ENOSPC ! 517: if (errno == ENOSPC) ! 518: fprintf(pgpout, LANG("\nDisk full.\n")); ! 519: else ! 520: #endif ! 521: fprintf(pgpout, LANG("\nFile write error.\n")); ! 522: return -1; ! 523: } ! 524: return 0; ! 525: } ! 526: ! 527: int copyfile(FILE *f, FILE *g, word32 longcount) ! 528: /* copy file f to file g, for longcount bytes */ ! 529: { ! 530: int count, status = 0; ! 531: do { /* read and write the whole file... */ ! 532: if (longcount < (word32) DISKBUFSIZE) ! 533: count = (int)longcount; ! 534: else ! 535: count = DISKBUFSIZE; ! 536: count = fread(textbuf,1,count,f); ! 537: if (count>0) { ! 538: if (CONVERSION != NO_CONV) { ! 539: int i; ! 540: for (i = 0; i < count; i++) ! 541: textbuf[i] = (CONVERSION == EXT_CONV) ? ! 542: EXT_C(textbuf[i]) : ! 543: INT_C(textbuf[i]); ! 544: } ! 545: if (fwrite(textbuf,1,count,g) != count ) { ! 546: /* Problem: return error value */ ! 547: status = -1; ! 548: break; ! 549: } ! 550: longcount -= count; ! 551: } ! 552: /* if text block was short, exit loop */ ! 553: } while (count==DISKBUFSIZE); ! 554: burn(textbuf); /* burn sensitive data on stack */ ! 555: return status; ! 556: } /* copyfile */ ! 557: ! 558: int copyfilepos (FILE *f, FILE *g, word32 longcount, word32 fpos) ! 559: /* ! 560: * Like copyfile, but takes a position for file f. Returns with ! 561: * f and g pointing just past the copied data. ! 562: */ ! 563: { ! 564: fseek (f, fpos, SEEK_SET); ! 565: return copyfile (f, g, longcount); ! 566: } ! 567: ! 568: ! 569: int copyfile_to_canon (FILE *f, FILE *g, word32 longcount) ! 570: /* copy file f to file g, for longcount bytes. Convert to ! 571: * canonical form as we go. f is open in text mode. Canonical ! 572: * form uses crlf's as line separators. ! 573: */ ! 574: { ! 575: int count, status = 0; ! 576: byte c, *tb1, *tb2; ! 577: int i, nbytes; ! 578: int nspaces = 0; ! 579: do { /* read and write the whole file... */ ! 580: if (longcount < (word32) DISKBUFSIZE) ! 581: count = (int)longcount; ! 582: else ! 583: count = DISKBUFSIZE; ! 584: count = fread(textbuf,1,count,f); ! 585: if (count>0) { ! 586: /* Convert by adding CR before LF */ ! 587: tb1 = textbuf; ! 588: tb2 = (byte *)textbuf2; ! 589: for (i=0; i<count; ++i) { ! 590: switch (CONVERSION) { ! 591: case EXT_CONV: ! 592: c = EXT_C(*tb1++); ! 593: break; ! 594: case INT_CONV: ! 595: c = INT_C(*tb1++); ! 596: break; ! 597: default: ! 598: c = *tb1++; ! 599: } ! 600: if (strip_spaces) { ! 601: if (c == ' ') { ! 602: /* Don't output spaces yet */ ! 603: nspaces += 1; ! 604: } else { ! 605: if (c == '\n') { ! 606: *tb2++ = '\r'; ! 607: nspaces = 0; /* Delete trailing spaces */ ! 608: } ! 609: if (nspaces) { ! 610: /* Put out spaces now */ ! 611: do ! 612: *tb2++ = ' '; ! 613: while (--nspaces); ! 614: } ! 615: *tb2++ = c; ! 616: } ! 617: } else { ! 618: if (c == '\n') ! 619: *tb2++ = '\r'; ! 620: *tb2++ = c; ! 621: } ! 622: } ! 623: nbytes = tb2 - (byte *)textbuf2; ! 624: if (fwrite(textbuf2,1,nbytes,g) != nbytes ) { ! 625: /* Problem: return error value */ ! 626: status = -1; ! 627: break; ! 628: } ! 629: longcount -= count; ! 630: } ! 631: /* if text block was short, exit loop */ ! 632: } while (count==DISKBUFSIZE); ! 633: burn(textbuf); /* burn sensitive data on stack */ ! 634: burn(textbuf2); ! 635: return status; ! 636: } /* copyfile_to_canon */ ! 637: ! 638: ! 639: int copyfile_from_canon (FILE *f, FILE *g, word32 longcount) ! 640: /* copy file f to file g, for longcount bytes. Convert from ! 641: * canonical to local form as we go. g is open in text mode. Canonical ! 642: * form uses crlf's as line separators. ! 643: */ ! 644: { ! 645: int count, status = 0; ! 646: byte c, *tb1, *tb2; ! 647: int i, nbytes; ! 648: do { /* read and write the whole file... */ ! 649: if (longcount < (word32) DISKBUFSIZE) ! 650: count = (int)longcount; ! 651: else ! 652: count = DISKBUFSIZE; ! 653: count = fread(textbuf,1,count,f); ! 654: if (count>0) { ! 655: /* Convert by removing CR's */ ! 656: tb1 = textbuf; ! 657: tb2 = (byte *)textbuf2; ! 658: for (i=0; i<count; ++i) { ! 659: switch (CONVERSION) { ! 660: case EXT_CONV: ! 661: c = EXT_C(*tb1++); ! 662: break; ! 663: case INT_CONV: ! 664: c = INT_C(*tb1++); ! 665: break; ! 666: default: ! 667: c = *tb1++; ! 668: } ! 669: if (c != '\r') ! 670: *tb2++ = c; ! 671: } ! 672: nbytes = tb2 - (byte *)textbuf2; ! 673: if (fwrite(textbuf2,1,nbytes,g) != nbytes ) { ! 674: /* Problem: return error value */ ! 675: status = -1; ! 676: break; ! 677: } ! 678: longcount -= count; ! 679: } ! 680: /* if text block was short, exit loop */ ! 681: } while (count==DISKBUFSIZE); ! 682: burn(textbuf); /* burn sensitive data on stack */ ! 683: burn(textbuf2); ! 684: return status; ! 685: } /* copyfile_from_canon */ ! 686: ! 687: ! 688: int copyfiles_by_name(char *srcFile, char *destFile) ! 689: /* Copy srcFile to destFile */ ! 690: { ! 691: FILE *f, *g; ! 692: int status = 0; ! 693: long fileLength; ! 694: ! 695: f = fopen(srcFile, FOPRBIN); ! 696: if (f == NULL) ! 697: return -1; ! 698: g = fopen(destFile, FOPWBIN); ! 699: if (g == NULL) { ! 700: fclose(f); ! 701: return -1; ! 702: } ! 703: ! 704: /* Get file length and copy it */ ! 705: fseek(f,0L,SEEK_END); ! 706: fileLength = ftell(f); ! 707: rewind(f); ! 708: status = copyfile(f,g,fileLength); ! 709: fclose(f); ! 710: if (write_error(g)) ! 711: status = -1; ! 712: fclose(g); ! 713: return status; ! 714: } /* copyfiles_by_name */ ! 715: ! 716: ! 717: int make_canonical(char *srcFile, char *destFile) ! 718: /* Copy srcFile to destFile, converting to canonical text form */ ! 719: { ! 720: FILE *f, *g; ! 721: int status = 0; ! 722: long fileLength; ! 723: ! 724: if (((f=fopen(srcFile,FOPRTXT)) == NULL) || ! 725: ((g=fopen(destFile,FOPWBIN)) == NULL)) ! 726: /* Can't open files */ ! 727: return -1; ! 728: ! 729: /* Get file length and copy it */ ! 730: fseek(f,0L,SEEK_END); ! 731: fileLength = ftell(f); ! 732: rewind(f); ! 733: CONVERSION = INT_CONV; ! 734: status = copyfile_to_canon(f,g,fileLength); ! 735: CONVERSION = NO_CONV; ! 736: fclose(f); ! 737: if (write_error(g)) ! 738: status = -1; ! 739: fclose(g); ! 740: return status; ! 741: } /* make_canonical */ ! 742: ! 743: ! 744: int rename2(char *srcFile, char *destFile) ! 745: /* ! 746: * Like rename() but will try to copy the file if the rename fails. ! 747: * This is because under OS's with multiple physical volumes if the ! 748: * source and destination are on different volumes the rename will fail ! 749: */ ! 750: { ! 751: FILE *f, *g; ! 752: int status = 0; ! 753: long fileLength; ! 754: ! 755: #ifdef VMS ! 756: if (rename(srcFile,destFile) != 0) ! 757: #else ! 758: if (rename(srcFile,destFile) == -1) ! 759: #endif ! 760: { ! 761: /* Rename failed, try a copy */ ! 762: if (((f=fopen(srcFile,FOPRBIN)) == NULL) || ! 763: ((g=fopen(destFile,FOPWBIN)) == NULL)) ! 764: /* Can't open files */ ! 765: return -1; ! 766: ! 767: /* Get file length and copy it */ ! 768: fseek(f,0L,SEEK_END); ! 769: fileLength = ftell(f); ! 770: rewind(f); ! 771: status = copyfile(f,g,fileLength); ! 772: if (write_error(g)) ! 773: status = -1; ! 774: ! 775: /* Zap source file if the copy went OK, otherwise zap the (possibly ! 776: incomplete) destination file */ ! 777: if (status >= 0) { ! 778: wipeout(f); /* Zap source file */ ! 779: fclose(f); ! 780: remove(srcFile); ! 781: fclose(g); ! 782: } else { ! 783: if (is_regular_file(destFile)) { ! 784: wipeout(g); /* Zap destination file */ ! 785: fclose(g); ! 786: remove(destFile); ! 787: } else { ! 788: fclose(g); ! 789: } ! 790: fclose(f); ! 791: } ! 792: } ! 793: return status; ! 794: } ! 795: ! 796: ! 797: int readPhantomInput(char *filename) ! 798: /* read the data from stdin to the phantom input file */ ! 799: { ! 800: FILE *outFilePtr; ! 801: byte buffer[ 512 ]; ! 802: int bytesRead, status = 0; ! 803: ! 804: if (verbose) ! 805: fprintf(pgpout, "writing stdin to file %s\n", filename); ! 806: if ((outFilePtr = fopen(filename,FOPWBIN)) == NULL) ! 807: return -1; ! 808: ! 809: #if defined(MSDOS) || defined(OS2) ! 810: /* Under DOS must set input stream to binary mode to avoid data mangling */ ! 811: setmode(fileno(stdin), O_BINARY); ! 812: #endif /* MSDOS || OS2 */ ! 813: while ((bytesRead = fread (buffer, 1, 512, stdin)) > 0) ! 814: if (fwrite (buffer, 1, bytesRead, outFilePtr) != bytesRead) { ! 815: status = -1; ! 816: break; ! 817: } ! 818: if (write_error(outFilePtr)) ! 819: status = -1; ! 820: fclose (outFilePtr); ! 821: #if defined(MSDOS) || defined(OS2) ! 822: setmode(fileno(stdin), O_TEXT); /* Reset stream */ ! 823: #endif /* MSDOS || OS2 */ ! 824: return status; ! 825: } ! 826: ! 827: ! 828: int writePhantomOutput(char *filename) ! 829: /* write the data from the phantom output file to stdout */ ! 830: { FILE *outFilePtr; ! 831: byte buffer[ 512 ]; ! 832: int bytesRead, status = 0; ! 833: ! 834: if (verbose) ! 835: fprintf(pgpout, "writing file %s to stdout\n", filename); ! 836: /* this can't fail since we just created the file */ ! 837: outFilePtr = fopen(filename,FOPRBIN); ! 838: ! 839: #if defined(MSDOS) || defined(OS2) ! 840: setmode(fileno(stdout), O_BINARY); ! 841: #endif /* MSDOS || OS2 */ ! 842: while ((bytesRead = fread (buffer, 1, 512, outFilePtr)) > 0) ! 843: if (fwrite (buffer, 1, bytesRead, stdout) != bytesRead) { ! 844: status = -1; ! 845: break; ! 846: } ! 847: fclose (outFilePtr); ! 848: fflush(stdout); ! 849: if (ferror(stdout)) { ! 850: status = -1; ! 851: fprintf(pgpout, LANG("\007Write error on stdout.\n")); ! 852: } ! 853: #if defined(MSDOS) || defined(OS2) ! 854: setmode(fileno(stdout), O_TEXT); ! 855: #endif /* MSDOS || OS2 */ ! 856: ! 857: return status; ! 858: } ! 859: ! 860: /* Return the size from the current position of file f to the end */ ! 861: word32 fsize (FILE *f) ! 862: { ! 863: long fpos = ftell (f); ! 864: long fpos2; ! 865: ! 866: fseek (f, 0L, SEEK_END); ! 867: fpos2 = ftell (f); ! 868: fseek (f, fpos, SEEK_SET); ! 869: return (word32)(fpos2 - fpos); ! 870: } ! 871: ! 872: /* Return TRUE if file filename looks like a pure text file */ ! 873: int is_text_file (char *filename) ! 874: { ! 875: FILE *f = fopen(filename,"r"); /* FOPRBIN gives problem with VMS */ ! 876: int i, n, bit8 = 0; ! 877: unsigned char buf[512]; ! 878: unsigned char *bufptr = buf; ! 879: unsigned char c; ! 880: ! 881: if (!f) ! 882: return FALSE; /* error opening it, so not a text file */ ! 883: i = n = fread (buf, 1, sizeof(buf), f); ! 884: fclose(f); ! 885: if (n <= 0) ! 886: return FALSE; /* empty file or error, not a text file */ ! 887: if (compressSignature(buf) >= 0) ! 888: return FALSE; ! 889: while (i--) { ! 890: c = *bufptr++; ! 891: if (c & 0x80) ! 892: ++bit8; ! 893: else /* allow BEL BS HT LF VT FF CR EOF control characters */ ! 894: if (c < '\007' || (c > '\r' && c < ' ' && c != '\032')) ! 895: return FALSE; /* not a text file */ ! 896: } ! 897: if (strcmp(language, "ru") == 0) ! 898: return TRUE; ! 899: /* assume binary if more than 1/4 bytes have 8th bit set */ ! 900: return bit8 < n/4; ! 901: } /* is_text_file */ ! 902: ! 903: VOID *xmalloc(unsigned size) ! 904: { VOID *p; ! 905: if (size == 0) ! 906: ++size; ! 907: p = malloc(size); ! 908: if (p == NULL) { ! 909: fprintf(stderr, LANG("\n\007Out of memory.\n")); ! 910: exitPGP(1); ! 911: } ! 912: return p; ! 913: } ! 914: ! 915: /*---------------------------------------------------------------------- ! 916: * temporary file routines ! 917: */ ! 918: ! 919: ! 920: #define MAXTMPF 8 ! 921: ! 922: #define TMP_INUSE 2 ! 923: ! 924: static struct ! 925: { char path[MAX_PATH]; ! 926: int flags; ! 927: int num; ! 928: } tmpf[MAXTMPF]; ! 929: ! 930: static char tmpdir[256]; /* temporary file directory */ ! 931: static char outdir[256]; /* output directory */ ! 932: static char tmpbasename[64] = "pgptemp"; /* basename for temporary files */ ! 933: ! 934: ! 935: /* ! 936: * set directory for temporary files. path will be stored in ! 937: * tmpdir[] with an appropriate trailing path separator. ! 938: */ ! 939: void settmpdir(char *path) ! 940: { ! 941: char *p; ! 942: ! 943: if (path == NULL || *path == '\0') { ! 944: tmpdir[0] = '\0'; ! 945: return; ! 946: } ! 947: strcpy(tmpdir, path); ! 948: p = tmpdir + strlen(tmpdir)-1; ! 949: if (*p != '/' && *p != '\\' && *p != ']' && *p != ':') { ! 950: /* append path separator, either / or \ */ ! 951: if ((p = strchr(tmpdir, '/')) == NULL && ! 952: (p = strchr(tmpdir, '\\')) == NULL) ! 953: p = "/"; /* path did not contain / or \, use / */ ! 954: strncat(tmpdir, p, 1); ! 955: } ! 956: } ! 957: ! 958: /* ! 959: * set output directory to avoid a file copy when temp file is renamed to ! 960: * output file. the argument filename must be a valid path for a file, not ! 961: * a directory. ! 962: */ ! 963: void setoutdir(char *filename) ! 964: { ! 965: char *p; ! 966: ! 967: if (filename == NULL) { ! 968: strcpy(outdir, tmpdir); ! 969: return; ! 970: } ! 971: strcpy(outdir, filename); ! 972: p = file_tail(outdir); ! 973: strcpy(tmpbasename, p); ! 974: *p = '\0'; ! 975: drop_extension(tmpbasename); ! 976: #if !defined(BSD42) && !defined(BSD43) && !defined(sun) ! 977: /* ! 978: * we don't depend on pathconf here, if it returns an incorrect value ! 979: * for NAME_MAX (like Linux 0.97 with minix FS) finding a unique name ! 980: * for temp files can fail. ! 981: */ ! 982: tmpbasename[10] = '\0'; /* 14 char limit */ ! 983: #endif ! 984: } ! 985: ! 986: /* ! 987: * return a unique temporary file name ! 988: */ ! 989: char *tempfile(int flags) ! 990: { ! 991: int i, j; ! 992: int num; ! 993: int fd; ! 994: #ifndef UNIX ! 995: FILE *fp; ! 996: #endif ! 997: ! 998: for (i = 0; i < MAXTMPF; ++i) ! 999: if (tmpf[i].flags == 0) ! 1000: break; ! 1001: ! 1002: if (i == MAXTMPF) { ! 1003: /* message only for debugging, no need for LANG */ ! 1004: fprintf(stderr, "\n\007Out of temporary files\n"); ! 1005: return NULL; ! 1006: } ! 1007: ! 1008: again: ! 1009: num = 0; ! 1010: do { ! 1011: for (j = 0; j < MAXTMPF; ++j) ! 1012: if (tmpf[j].flags && tmpf[j].num == num) ! 1013: break; ! 1014: if (j < MAXTMPF) ! 1015: continue; /* sequence number already in use */ ! 1016: sprintf(tmpf[i].path, "%s%s.%c%02d", ! 1017: ((flags & TMP_TMPDIR) && *tmpdir ? tmpdir : outdir), ! 1018: tmpbasename, TMP_EXT, num); ! 1019: if (!file_exists(tmpf[i].path)) ! 1020: break; ! 1021: } ! 1022: while (++num < 100); ! 1023: ! 1024: if (num == 100) { ! 1025: fprintf(pgpout, "\n\007tempfile: cannot find unique name\n"); ! 1026: return NULL; ! 1027: } ! 1028: ! 1029: #if defined(UNIX) || defined(VMS) ! 1030: if ((fd = open(tmpf[i].path, O_EXCL|O_RDWR|O_CREAT, 0600)) != -1) ! 1031: close(fd); ! 1032: #else ! 1033: if ((fp = fopen(tmpf[i].path, "w")) != NULL) ! 1034: fclose(fp); ! 1035: fd = (fp == NULL ? -1 : 0); ! 1036: #endif ! 1037: ! 1038: if (fd == -1) { ! 1039: if (!(flags & TMP_TMPDIR)) { ! 1040: flags |= TMP_TMPDIR; ! 1041: goto again; ! 1042: } ! 1043: #ifdef UNIX ! 1044: else if (tmpdir[0] == '\0') { ! 1045: strcpy(tmpdir, "/tmp/"); ! 1046: goto again; ! 1047: } ! 1048: #endif ! 1049: } ! 1050: if (fd == -1) ! 1051: { fprintf(pgpout, LANG("\n\007Cannot create temporary file '%s'\n"), tmpf[i].path); ! 1052: user_error(); ! 1053: } ! 1054: #ifdef VMS ! 1055: remove(tmpf[i].path); ! 1056: #endif ! 1057: ! 1058: tmpf[i].num = num; ! 1059: tmpf[i].flags = flags | TMP_INUSE; ! 1060: if (verbose) ! 1061: fprintf(pgpout, "tempfile: created '%s'\n", tmpf[i].path); ! 1062: return tmpf[i].path; ! 1063: } /* tempfile */ ! 1064: ! 1065: /* ! 1066: * remove temporary file, wipe if necessary. ! 1067: */ ! 1068: void rmtemp(char *name) ! 1069: { ! 1070: int i; ! 1071: ! 1072: for (i = 0; i < MAXTMPF; ++i) ! 1073: if (tmpf[i].flags && strcmp(tmpf[i].path, name) == 0) ! 1074: break; ! 1075: ! 1076: if (i < MAXTMPF) { ! 1077: if (strlen(name) > 3 && name[strlen(name)-3] == TMP_EXT) { ! 1078: /* only remove file if name hasn't changed */ ! 1079: if (verbose) ! 1080: fprintf(pgpout, "rmtemp: removing '%s'\n", name); ! 1081: if (tmpf[i].flags & TMP_WIPE) ! 1082: wipefile(name); ! 1083: if (!remove(name)) { ! 1084: tmpf[i].flags = 0; ! 1085: } else if (verbose) { ! 1086: fprintf(stderr,"\nrmtemp: Failed to remove %s",name); ! 1087: perror ("\nError"); ! 1088: } ! 1089: } else if (verbose) ! 1090: fprintf(pgpout, "rmtemp: not removing '%s'\n", name); ! 1091: } ! 1092: } /* rmtemp */ ! 1093: ! 1094: /* ! 1095: * make temporary file permanent, returns the new name. ! 1096: */ ! 1097: char *savetemp(char *name, char *newname) ! 1098: { ! 1099: int i, overwrite; ! 1100: static char buf[MAX_PATH]; ! 1101: ! 1102: if (strcmp(name, newname) == 0) ! 1103: return name; ! 1104: ! 1105: for (i = 0; i < MAXTMPF; ++i) ! 1106: if (tmpf[i].flags && strcmp(tmpf[i].path, name) == 0) ! 1107: break; ! 1108: ! 1109: if (i < MAXTMPF) { ! 1110: if (strlen(name) < 4 || name[strlen(name)-3] != TMP_EXT) { ! 1111: if (verbose) ! 1112: fprintf(pgpout, "savetemp: not renaming '%s' to '%s'\n", ! 1113: name, newname); ! 1114: return name; /* return original file name */ ! 1115: } ! 1116: } ! 1117: ! 1118: while (file_exists(newname)) { ! 1119: if (batchmode && !force_flag) { ! 1120: fprintf(pgpout,LANG("\n\007Output file '%s' already exists.\n"),newname); ! 1121: return NULL; ! 1122: } ! 1123: if (is_regular_file(newname)) { ! 1124: if (force_flag) { ! 1125: /* remove without asking */ ! 1126: remove(newname); ! 1127: break; ! 1128: } ! 1129: fprintf(pgpout,LANG("\n\007Output file '%s' already exists. Overwrite (y/N)? "), ! 1130: newname); ! 1131: overwrite = getyesno('n'); ! 1132: } else { ! 1133: fprintf(pgpout,LANG("\n\007Output file '%s' already exists.\n"),newname); ! 1134: if (force_flag) /* never remove special file */ ! 1135: return NULL; ! 1136: overwrite = FALSE; ! 1137: } ! 1138: ! 1139: if (!overwrite) { ! 1140: fprintf(pgpout, LANG("\nEnter new file name: ")); ! 1141: getstring(buf, MAX_PATH - 1, TRUE); ! 1142: if (buf[0] == '\0') ! 1143: return NULL; ! 1144: newname = buf; ! 1145: } else { ! 1146: remove(newname); ! 1147: } ! 1148: } ! 1149: if (verbose) ! 1150: fprintf(pgpout, "savetemp: renaming '%s' to '%s'\n", name, newname); ! 1151: if (rename2(name, newname) < 0) { ! 1152: /* errorLvl = UNKNOWN_FILE_ERROR; */ ! 1153: fprintf(pgpout, LANG("Can't create output file '%s'\n"), newname); ! 1154: return NULL; ! 1155: } ! 1156: if (i < MAXTMPF) ! 1157: tmpf[i].flags = 0; ! 1158: return newname; ! 1159: } /* savetemp */ ! 1160: ! 1161: /* ! 1162: * like savetemp(), only make backup of destname if it exists ! 1163: */ ! 1164: int savetempbak(char *tmpname, char *destname) ! 1165: { ! 1166: char bakpath[MAX_PATH]; ! 1167: #ifdef UNIX ! 1168: int mode = -1; ! 1169: #endif ! 1170: ! 1171: if (is_tempfile(destname)) { ! 1172: remove(destname); ! 1173: } else { ! 1174: if (file_exists(destname)) { ! 1175: #ifdef UNIX ! 1176: struct stat st; ! 1177: if (stat(destname, &st) != -1) ! 1178: mode = st.st_mode & 07777; ! 1179: #endif ! 1180: strcpy(bakpath, destname); ! 1181: force_extension(bakpath, BAK_EXTENSION); ! 1182: remove(bakpath); ! 1183: #ifdef VMS ! 1184: if (rename(destname, bakpath) != 0) ! 1185: #else ! 1186: if (rename(destname, bakpath) == -1) ! 1187: #endif ! 1188: return -1; ! 1189: } ! 1190: } ! 1191: if (savetemp(tmpname, destname) == NULL) ! 1192: return -1; ! 1193: #ifdef UNIX ! 1194: if (mode != -1) ! 1195: chmod(destname, mode); ! 1196: #endif ! 1197: return 0; ! 1198: } ! 1199: ! 1200: /* ! 1201: * remove all temporary files and wipe them if necessary ! 1202: */ ! 1203: void cleanup_tmpf(void) ! 1204: { ! 1205: int i; ! 1206: ! 1207: for (i = 0; i < MAXTMPF; ++i) ! 1208: if (tmpf[i].flags) ! 1209: rmtemp(tmpf[i].path); ! 1210: } /* cleanup_tmpf */ ! 1211: ! 1212: /* ! 1213: * Routines to search for the manuals. ! 1214: * ! 1215: * Why all this code? ! 1216: * ! 1217: * Some dimwits have been distributing versions of PGP (especially on MS-DOS) ! 1218: * without the manuals. This frustrates users (who call Philip Zimmermann ! 1219: * at all hours of the day and night), and in addition to depriving them ! 1220: * of instructions on how to operate the program, also deprives them of ! 1221: * IMPORTANT legal notices. This is a Bad Thing, so we've gone to the ! 1222: * trouble of being fascist and *forcing* the manuals to be there. ! 1223: */ ! 1224: ! 1225: static unsigned ! 1226: ext_missing(char *prefix) ! 1227: { ! 1228: static char const * const extensions[] = { ! 1229: #ifdef VMS ! 1230: ".doc", ".txt", ".man", ".tex", ".", 0}; ! 1231: #else ! 1232: ".doc", ".txt", ".man", ".tex", "", 0}; ! 1233: #endif ! 1234: char const * const *p; ! 1235: char *end = prefix + strlen(prefix); ! 1236: ! 1237: for (p = extensions; *p; p++) { ! 1238: strcpy(end, *p); ! 1239: #if 0 /* Debugging code */ ! 1240: fprintf(pgpout, "Looking for \"%s\"\n", prefix); ! 1241: #endif ! 1242: if (file_exists(prefix)) ! 1243: return 0; ! 1244: } ! 1245: return 1; ! 1246: } ! 1247: ! 1248: /* ! 1249: * Returns mask of files missing ! 1250: */ ! 1251: static unsigned ! 1252: files_missing(char *prefix) ! 1253: { ! 1254: static char const * const names[] = {"pgpdoc1", "pgpdoc2", 0}; ! 1255: char const * const *p; ! 1256: unsigned bit, mask = 3; ! 1257: int len = strlen(prefix); ! 1258: ! 1259: if (prefix[0] && !file_exists(prefix)) /* Directory doesn't exist? */ ! 1260: return mask; ! 1261: if (len && strchr(DIRSEPS, prefix[len-1]) == 0) ! 1262: prefix[len++] = DIRSEPS[0]; ! 1263: for (p = names, bit = 1; *p; p++, bit <<= 1) { ! 1264: strcpy(prefix+len, *p); ! 1265: if (!ext_missing(prefix)) ! 1266: mask &= ~bit; ! 1267: } ! 1268: ! 1269: return mask; /* Bitmask of which files exist */ ! 1270: } ! 1271: ! 1272: /* ! 1273: * Search prefix directory and doc subdirectory. ! 1274: */ ! 1275: static unsigned ! 1276: doc_missing(char *prefix) ! 1277: { ! 1278: unsigned mask; ! 1279: int len = strlen(prefix); ! 1280: ! 1281: mask = files_missing(prefix); ! 1282: if (!mask) ! 1283: return 0; ! 1284: #ifdef VMS ! 1285: if (len && prefix[len-1] == ']') { ! 1286: strcpy(prefix+len-1, ".doc]"); ! 1287: } else { ! 1288: assert(!len || prefix[len-1] == ':'); ! 1289: strcpy(prefix+len, "[doc]"); ! 1290: } ! 1291: #else ! 1292: if (len && prefix[len-1] != DIRSEPS[0]) ! 1293: prefix[len++] = DIRSEPS[0]; ! 1294: strcpy(prefix+len, "doc"); ! 1295: #endif ! 1296: ! 1297: mask &= files_missing(prefix); ! 1298: ! 1299: prefix[len] = '\0'; ! 1300: return mask; ! 1301: } ! 1302: ! 1303: /* ! 1304: * Expands a leading environment variable. Returns 0 on success; ! 1305: * <0 if there is an error. ! 1306: */ ! 1307: static int ! 1308: expand_env(char const *src, char *dest) ! 1309: { ! 1310: char const *var, *suffix; ! 1311: unsigned len; ! 1312: ! 1313: if (*src != '$') { ! 1314: strcpy(dest, src); ! 1315: return 0; ! 1316: } ! 1317: ! 1318: /* Find end of variable */ ! 1319: if (src[1] == '{') { /* ${FOO} form */ ! 1320: var = src+2; ! 1321: len = strchr(var, '}') - var; ! 1322: suffix = src+2+len+1; ! 1323: } else { /* $FOO form - allow $ for VMS */ ! 1324: var = src+1; ! 1325: len = strspn(var, "ABCDEFGHIJKLMNOPQRSTUVWXYZ$_"); ! 1326: suffix = src+1+len; ! 1327: } ! 1328: ! 1329: memcpy(dest, var, len); /* Copy name */ ! 1330: dest[len] = '\0'; /* Null-terminate */ ! 1331: ! 1332: var = getenv(dest); ! 1333: if (!var || !*var) ! 1334: return -1; /* No env variable */ ! 1335: ! 1336: /* Copy expanded form to destination */ ! 1337: strcpy(dest, var); ! 1338: ! 1339: /* Add tail */ ! 1340: strcat(dest, suffix); ! 1341: ! 1342: return 0; ! 1343: } ! 1344: ! 1345: /* Don't forget to change 'pgp25' whenever you update rel_version past 2.5 */ ! 1346: char const * const manual_dirs[] = { ! 1347: #if defined(VMS) ! 1348: "$PGPPATH", "", "[pgp]", "[pgp25]", ! 1349: PGP_SYSTEM_DIR, "SYS$LOGIN:", "SYS$LOGIN:[pgp]", ! 1350: "SYS$LOGIN:[pgp25]", "[-]", ! 1351: #elif defined(UNIX) ! 1352: "$PGPPATH", "", "pgp", "pgp25", PGP_SYSTEM_DIR, ! 1353: "$HOME/.pgp", "$HOME", "$HOME/pgp", "$HOME/pgp25", "..", ! 1354: #elif defined(AMIGA) ! 1355: "$PGPPATH", "", "pgp", "pgp25", ":pgp", ":pgp25", ":", "/", ! 1356: #else /* MSDOS or ATARI */ ! 1357: "$PGPPATH", "", "pgp", "pgp25", "\\pgp", "\\pgp25", "\\", "..", ! 1358: "c:\\pgp", "c:\\pgp25", ! 1359: #endif ! 1360: 0 }; ! 1361: ! 1362: unsigned ! 1363: manuals_missing(void) ! 1364: { ! 1365: char buf[256]; ! 1366: unsigned mask = ~0u; ! 1367: char const * const *p; ! 1368: ! 1369: for (p = manual_dirs; *p; p++) { ! 1370: if (expand_env(*p, buf) < 0) ! 1371: continue; /* Ignore */ ! 1372: mask &= doc_missing(buf); ! 1373: if (!mask) ! 1374: break; ! 1375: } ! 1376: ! 1377: return mask; ! 1378: } ! 1379: ! 1380: /* ! 1381: * Why all this code? ! 1382: * ! 1383: * Some dimwits have been distributing versions of PGP (especially on MS-DOS) ! 1384: * without the manuals. This frustrates users (who call Philip Zimmermann ! 1385: * at all hours of the day and night), and in addition to depriving them ! 1386: * of instructions on how to operate the program, also deprives them of ! 1387: * IMPORTANT legal notices. This is a Bad Thing, so we've gone to the ! 1388: * trouble of being fascist and *forcing* the manuals to be there. ! 1389: */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.