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