--- pgp/src/fileio.c 2018/04/24 16:39:12 1.1.1.3 +++ pgp/src/fileio.c 2018/04/24 16:44:32 1.1.1.7 @@ -1,28 +1,29 @@ -/* fileio.c - I/O routines for PGP. - PGP: Pretty Good(tm) Privacy - public key cryptography for the masses. +/* fileio.c - I/O routines for PGP. + PGP: Pretty Good(tm) Privacy - public key cryptography for the masses. - (c) Copyright 1990-1992 by Philip Zimmermann. All rights reserved. - The author assumes no liability for damages resulting from the use - of this software, even if the damage results from defects in this - software. No warranty is expressed or implied. - - All the source code Philip Zimmermann wrote for PGP is available for - free under the "Copyleft" General Public License from the Free - Software Foundation. A copy of that license agreement is included in - the source release package of PGP. Code developed by others for PGP - is also freely available. Other code that has been incorporated into - PGP from other sources was either originally published in the public - domain or was used with permission from the various authors. See the - PGP User's Guide for more complete information about licensing, - patent restrictions on certain algorithms, trademarks, copyrights, - and export controls. - - Modified 16 Apr 92 - HAJK - Mods for support of VAX/VMS file system - - Modified 17 Nov 92 - HAJK - Change to temp file stuff for VMS. -*/ + (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved. + The author assumes no liability for damages resulting from the use + of this software, even if the damage results from defects in this + software. No warranty is expressed or implied. + + Note that while most PGP source modules bear Philip Zimmermann's + copyright notice, many of them have been revised or entirely written + by contributors who frequently failed to put their names in their + code. Code that has been incorporated into PGP from other authors + was either originally published in the public domain or is used with + permission from the various authors. + + PGP is available for free to the public under certain restrictions. + See the PGP User's Guide (included in the release package) for + important information about licensing, patent restrictions on + certain algorithms, trademarks, copyrights, and export controls. + + Modified 16 Apr 92 - HAJK + Mods for support of VAX/VMS file system + + Modified 17 Nov 92 - HAJK + Change to temp file stuff for VMS. + */ #include #include @@ -37,9 +38,10 @@ #include #endif extern int errno; -#endif /* UNIX */ +#endif /* UNIX */ #ifdef VMS #include +#include #endif #include "random.h" #include "mpilib.h" @@ -55,12 +57,44 @@ extern int errno; #include #endif -#if (defined(VMS) || defined(UNIX)) && !defined(F_OK) +#ifndef F_OK #define F_OK 0 #define X_OK 1 #define W_OK 2 #define R_OK 4 -#endif /* !F_OK */ +#endif /* !F_OK */ + +/* + * DIRSEPS is a string of possible directory-separation characters + * The first one is the preferred one, which goes in between + * PGPPATH and the file name if PGPPATH is not terminated with a + * directory separator. + */ + +#if defined(MSDOS) || defined(__MSDOS__) || defined(OS2) +static char const DIRSEPS[] = "\\/:"; +#define BSLASH + +#elif defined(ATARI) +static char const DIRSEPS[] = "\\/:"; +#define BSLASH + +#elif defined(UNIX) +static char const DIRSEPS[] = "/"; +#define MULTIPLE_DOTS + +#elif defined(AMIGA) +static char const DIRSEPS[] = "/:"; +#define MULTIPLE_DOTS + +#elif defined(VMS) +static char const DIRSEPS[] = "]:"; /* Any more? */ + +#else +/* #error is not portable, this has the same effect */ +#include "Unknown OS" +#endif + /* 1st character of temporary file extension */ #define TMP_EXT '$' /* extensions are '.$##' */ @@ -71,718 +105,805 @@ static char PGPPATH[] = "PGPPATH"; /* Disk buffers, used here and in crypto.c */ byte textbuf[DISKBUFSIZE]; -static byte textbuf2[2*DISKBUFSIZE]; +static unsigned textbuf2[2 * DISKBUFSIZE / sizeof(unsigned)]; boolean file_exists(char *filename) -/* Returns TRUE iff file exists. */ +/* Returns TRUE iff file exists. */ { -#if defined(UNIX) || defined(VMS) - return(access(filename, F_OK) == 0); -#else - FILE *f; - /* open file f for read, in binary (not text) mode...*/ - if ((f = fopen(filename,FOPRBIN)) == NULL) - return(FALSE); - fclose(f); - return(TRUE); -#endif -} /* file_exists */ + return access(filename, F_OK) == 0; +} /* file_exists */ static boolean is_regular_file(char *filename) { #ifdef S_ISREG - struct stat st; - return(stat(filename, &st) != -1 && S_ISREG(st.st_mode)); + struct stat st; + return stat(filename, &st) != -1 && S_ISREG(st.st_mode); #else - return TRUE; + return TRUE; #endif } -static int wipeout(FILE *f) -{ /* Completely overwrite and erase file, so that no sensitive - information is left on the disk. - NOTE: File MUST be open for read/write. - */ - long flength; - int count = 0; +/* + * This wipes a file with pseudo-random data. The purpose of this is to + * make sure no sensitive information is left on the disk. The use + * of pseudo-random data is to defeat disk compression drivers (such as + * Stacker and dblspace) so that we are guaranteed that the entire file + * has been overwritten. + * + * Note that the file MUST be open for read/write. + * + * It may not work to eliminate everything from non-volatile storage + * if the OS you're using does its own paging or swapping. Then + * it's an issue of how the OS's paging device is wiped, and you can + * only hope that the space will be reused within a few seconds. + * + * Also, some file systems (in particular, the Logging File System + * for Sprite) do not write new data in the same place as old data, + * defeating this wiping entirely. Fortunately, such systems + * usually don't need a swap file, and for small temp files, they + * do write-behind, so if you create and delete a file fast enough, + * it never gets written to disk at all. + */ - fseek(f, 0L, SEEK_END); - flength = ftell(f); - rewind(f); +/* + * The data is randomly generated with the size of the file as a seed. + * The data should be random and not leak information. If someone is + * examining deleted files, presumably they can reconstruct the file size, + * so that's not a secret. H'm... this wiping algorithm makes it easy to, + * given a block of data, find the size of the file it came from + * and the offset of this block within it. That in turn reveals + * something about the state of the disk's allocation tables when the + * file was used, possibly making it easier to find other files created + * at neaby times - such as plaintext files. Is this acceptable? + */ + +/* + * Theory of operation: We use the additive congruential RNG + * r[i] = r[i-24] + r[i-55], from Knuth, Vol. 2. This is fast + * and has a long enough period that there should be no repetitions + * in even a huge file. It is seeded with r[-55] through r[-1] + * using another polynomial-based RNG. We seed a linear feedback + * shift register (CRC generator) with the size of the swap file, + * and clock in 0 bits. Each 32 bits, the value of the generator is + * taken as the next integer. This is just to ensure a reasonably + * even mix of 1's and 0's in the initialization vector. + */ - fill0(textbuf, sizeof(textbuf)); - while (flength > 0L) - { /* write zeros to the whole file... */ - if (flength < (word32) DISKBUFSIZE) - count = (int)flength; - else - count = DISKBUFSIZE; - fwrite(textbuf,1,count,f); - flength -= count; +/* + * This is the CRC-32 polynomial, which should be okay for random + * number generation. + * 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 + * = 1 0000 0100 1100 0001 0001 1101 1011 0111 + * = 0x04c11db7 + */ +#define POLY 0x04c11db7 + +static void wipeout(FILE * f) +{ + unsigned *p1, *p2, *p3; + unsigned long len; + unsigned long t; + int i; + + /* Get the file size */ + fseek(f, 0L, SEEK_END); + len = ftell(f); + rewind(f); + + /* Seed of first RNG. Inverted to get more 1 bits */ + t = ~len; + + /* Initialize first 55 words of buf with pseudo-random stuff */ + p1 = (unsigned *) textbuf2 + 55; + do { + for (i = 0; i < 32; i++) + t = (t & 0x80000000) ? t << 1 ^ POLY : t << 1; + *--p1 = (unsigned) t; + } while (p1 > (unsigned *) textbuf2); + + while (len) { + /* Fill buffer with pseudo-random integers */ + + p3 = (unsigned *) textbuf2 + 55; + p2 = (unsigned *) textbuf2 + 24; + p1 = (unsigned *) textbuf2 + sizeof(textbuf2) / sizeof(*p1); + do { + *--p1 = *--p2 + *--p3; + } while (p2 > (unsigned *) textbuf2); + + p2 = (unsigned *) textbuf2 + sizeof(textbuf2) / sizeof(*p1); + do { + *--p1 = *--p2 + *--p3; + } while (p3 > (unsigned *) textbuf2); + + p3 = (unsigned *) textbuf2 + sizeof(textbuf2) / sizeof(*p3); + do { + *--p1 = *--p2 + *--p3; + } while (p1 > (unsigned *) textbuf2); + + /* Write it out - yes, we're ignoring errors */ + if (len > sizeof(textbuf2)) { + fwrite((char const *) textbuf2, sizeof(textbuf2), 1, f); + len -= sizeof(textbuf2); + } else { + fwrite((char const *) textbuf2, len, 1, f); + len = 0; } - rewind(f); /* maybe this isn't necessary */ - return(0); /* normal return */ -} /* wipeout */ + } +} +/* + * Completely overwrite and erase file, so that no sensitive + * information is left on the disk. + */ int wipefile(char *filename) -{ /* Completely overwrite and erase file, so that no sensitive - information is left on the disk. - */ - FILE *f; - /* open file f for read/write, in binary (not text) mode...*/ - if ((f = fopen(filename,FOPRWBIN)) == NULL) - return(-1); /* error - file can't be opened */ - wipeout(f); - fclose(f); - return(0); /* normal return */ -} /* wipefile */ +{ + FILE *f; + /* open file f for read/write, in binary (not text) mode... */ + if ((f = fopen(filename, FOPRWBIN)) == NULL) + return -1; /* error - file can't be opened */ + wipeout(f); + fclose(f); + return 0; /* normal return */ +} /* wipefile */ -char *file_tail (char *filename) -/* Returns the after-slash part of filename. Also skips backslashes, - * colons, and right brackets. */ -{ - char *bslashPos = strrchr(filename, '\\');/* Find last bslash in filename */ - char *slashPos = strrchr(filename, '/');/* Find last slash in filename */ - char *colonPos = strrchr(filename, ':'); - char *rbrakPos = strrchr(filename, ']'); - - if (!slashPos || bslashPos > slashPos) - slashPos = bslashPos; - if (!slashPos || colonPos > slashPos) - slashPos = colonPos; - if (!slashPos || rbrakPos > slashPos) - slashPos = rbrakPos; - return( slashPos?(slashPos+1):filename ); -} -/* Define BSLASH for machines that use backslash, FSLASH for machines - * that use forward slash as separators. +/* + * Returns the part of a filename after all directory specifiers. */ +char *file_tail(char *filename) +{ + char *p; + char const *s = DIRSEPS; -#ifdef MSDOS -#define BSLASH -#endif -#ifdef ATARI -#define BSLASH -#endif -#ifdef UNIX -#define FSLASH -#define MULTIPLE_DOTS -#endif -#ifdef AMIGA -#define FSLASH -#define MULTIPLE_DOTS -#endif + while (*s) { + p = strrchr(filename, *s); + if (p) + filename = p + 1; + s++; + } + + return filename; +} + +/* return TRUE if extension matches the end of filename */ boolean has_extension(char *filename, char *extension) -{ /* return TRUE if extension matches the end of filename */ - int lf = strlen(filename); - int lx = strlen(extension); - - if (lf <= lx) - return(FALSE); - return(!strcmp(filename + lf - lx, extension)); +{ + int lf = strlen(filename); + int lx = strlen(extension); + + if (lf <= lx) + return FALSE; + return !strcmp(filename + lf - lx, extension); } +/* return TRUE if path is a filename created by tempfile() */ +/* Filename matches "*.$[0-9][0-9]" */ boolean is_tempfile(char *path) -{ /* return TRUE if path is a filename created by tempfile() */ - char *p; - - return((p = strrchr(path, '.')) != NULL && - p[1] == TMP_EXT && strlen(p) == 4); +{ + char *p = strrchr(path, '.'); + + return p != NULL && p[1] == TMP_EXT && + isdigit(p[2]) && isdigit(p[3]) && p[4] == '\0'; } +/* + * Returns TRUE if user left off file extension, allowing default. + * Note that the name is misleading if multiple dots are allowed. + * not_pgp_extension or something would be better. + */ boolean no_extension(char *filename) -/* Returns TRUE if user left off file extension, allowing default. */ { -#ifdef MULTIPLE_DOTS /* filename can have more than one dot */ - if (has_extension(filename, ASC_EXTENSION) || - has_extension(filename, PGP_EXTENSION) || - has_extension(filename, SIG_EXTENSION) || - is_tempfile(filename)) - return(FALSE); - else - return(TRUE); -#else -#ifdef BSLASH - char *slashPos = strrchr(filename, '\\'); /* Find last slash in filename */ - - /* Look for the filename after the last slash if there is one */ - return(strchr((slashPos != NULL ) ? slashPos : filename, '.') == NULL); -#else -#ifdef FSLASH - char *slashPos = strrchr(filename, '/'); /* Find last slash in filename */ - - /* Look for the filename after the last slash if there is one */ - return(strchr((slashPos != NULL ) ? slashPos : filename, '.') == NULL); +#ifdef MULTIPLE_DOTS /* filename can have more than one dot */ + if (has_extension(filename, ASC_EXTENSION) || + has_extension(filename, PGP_EXTENSION) || + has_extension(filename, SIG_EXTENSION) || + is_tempfile(filename)) + return FALSE; + else + return TRUE; #else -#ifdef VMS - char *slashPos = strrchr(filename,']'); /* Locate end of directory spec */ + filename = file_tail(filename); - /* Look for last period in filename */ - return(strrchr((slashPos != NULL) ? slashPos : filename, '.') == NULL ); -#else - return( (strrchr(filename,'.')==NULL) ? TRUE : FALSE ); -#endif /* VMS */ -#endif /* FSLASH */ -#endif /* BSLASH */ -#endif /* MULTIPLE_DOTS */ -} /* no_extension */ + return strrchr(filename, '.') == NULL; +#endif +} /* no_extension */ +/* deletes trailing ".xxx" file extension after the period. */ void drop_extension(char *filename) -{ /* deletes trailing ".xxx" file extension after the period. */ - if (!no_extension(filename)) - *strrchr(filename,'.') = '\0'; -} /* drop_extension */ +{ + if (!no_extension(filename)) + *strrchr(filename, '.') = '\0'; +} /* drop_extension */ +/* append filename extension if there isn't one already. */ void default_extension(char *filename, char *extension) -{ /* append filename extension if there isn't one already. */ - if (no_extension(filename)) - strcat(filename,extension); -} /* default_extension */ +{ + if (no_extension(filename)) + strcat(filename, extension); +} /* default_extension */ #ifndef MAX_NAMELEN -#if defined(AMIGA) || defined(NeXT) || (defined(BSD) && BSD > 41) +#if defined(AMIGA) || defined(NeXT) || (defined(BSD) && BSD > 41) || (defined(sun) && defined(i386)) #define MAX_NAMELEN 255 #else #include #endif #endif +/* truncate the filename so that an extension can be tacked on. */ static void truncate_name(char *path, int ext_len) -{ /* truncate the filename so that an extension can be tacked on. */ -#ifdef UNIX /* for other systems this is a no-op */ - char *p; -#ifdef MAX_NAMELEN /* overrides the use of pathconf() */ - int namemax = MAX_NAMELEN; +{ +#ifdef UNIX /* for other systems this is a no-op */ + char *p; +#ifdef MAX_NAMELEN /* overrides the use of pathconf() */ + int namemax = MAX_NAMELEN; #else - int namemax; + int namemax; #ifdef _PC_NAME_MAX - char dir[MAX_PATH]; + char dir[MAX_PATH]; - strcpy(dir, path); - if ((p = strrchr(dir, '/')) == NULL) - strcpy(dir, "."); - else - { if (p == dir) - ++p; - *p = '\0'; - } - if ((namemax = pathconf(dir, _PC_NAME_MAX)) <= ext_len) - return; + strcpy(dir, path); + if ((p = strrchr(dir, '/')) == NULL) { + strcpy(dir, "."); + } else { + if (p == dir) + ++p; + *p = '\0'; + } + if ((namemax = pathconf(dir, _PC_NAME_MAX)) <= ext_len) + return; #else #ifdef NAME_MAX - namemax = NAME_MAX; + namemax = NAME_MAX; #else - namemax = 14; -#endif /* NAME_MAX */ -#endif /* _PC_NAME_MAX */ -#endif /* MAX_NAMELEN */ - - if ((p = strrchr(path, '/')) == NULL) - p = path; - else - ++p; - if (strlen(p) > namemax - ext_len) - { - if (verbose) - fprintf(pgpout, "Truncating filename '%s' ", path); - p[namemax - ext_len] = '\0'; - if (verbose) - fprintf(pgpout, "to '%s'\n", path); - } -#endif /* UNIX */ + namemax = 14; +#endif /* NAME_MAX */ +#endif /* _PC_NAME_MAX */ +#endif /* MAX_NAMELEN */ + + if ((p = strrchr(path, '/')) == NULL) + p = path; + else + ++p; + if (strlen(p) > namemax - ext_len) { + if (verbose) + fprintf(pgpout, "Truncating filename '%s' ", path); + p[namemax - ext_len] = '\0'; + if (verbose) + fprintf(pgpout, "to '%s'\n", path); + } +#endif /* UNIX */ } +/* change the filename extension. */ void force_extension(char *filename, char *extension) -{ /* change the filename extension. */ - drop_extension(filename); /* out with the old */ - truncate_name(filename, strlen(extension)); - strcat(filename,extension); /* in with the new */ -} /* force_extension */ +{ + drop_extension(filename); /* out with the old */ + truncate_name(filename, strlen(extension)); + strcat(filename, extension); /* in with the new */ +} /* force_extension */ +/* + * Get yes/no answer from user, returns TRUE for yes, FALSE for no. + * First the translations are checked, if they don't match 'y' and 'n' + * are tried. + */ boolean getyesno(char default_answer) -/* Get yes/no answer from user, returns TRUE for yes, FALSE for no. - First the translations are checked, if they don't match 'y' and 'n' - are tried. -*/ -{ char buf[8]; - static char yes[8], no[8]; - - if (yes[0] == '\0') - { strncpy(yes, PSTR("y"), 7); - strncpy(no, PSTR("n"), 7); - } - if (!batchmode) /* return default answer in batchmode */ - { flush_input(); - getstring(buf,6,TRUE); /* echo keyboard input */ - strlwr(buf); - if (!strncmp(buf, no, strlen(no))) - return(FALSE); - if (!strncmp(buf, yes, strlen(yes))) - return(TRUE); - if (buf[0] == 'n') - return(FALSE); - if (buf[0] == 'y') - return(TRUE); - } - return(default_answer == 'y' ? TRUE : FALSE); -} /* getyesno */ - +{ + char buf[8]; + static char yes[8], no[8]; + if (yes[0] == '\0') { + strncpy(yes, LANG("y"), 7); + strncpy(no, LANG("n"), 7); + } + if (!batchmode) { /* return default answer in batchmode */ + getstring(buf, 6, TRUE); /* echo keyboard input */ + strlwr(buf); + if (!strncmp(buf, no, strlen(no))) + return FALSE; + if (!strncmp(buf, yes, strlen(yes))) + return TRUE; + if (buf[0] == 'n') + return FALSE; + if (buf[0] == 'y') + return TRUE; + } + return default_answer == 'y' ? TRUE : FALSE; +} /* getyesno */ +/* if user consents to it, change the filename extension. */ char *maybe_force_extension(char *filename, char *extension) -{ /* if user consents to it, change the filename extension. */ - static char newname[MAX_PATH]; - if (!has_extension(filename,extension)) - { strcpy(newname,filename); - force_extension(newname,extension); - if (!file_exists(newname)) - { fprintf(pgpout,PSTR("\nShould '%s' be renamed to '%s' [Y/n]? "), - filename,newname); - if (getyesno('y')) - return(newname); - } - } - return(NULL); -} /* maybe_force_extension */ +{ + static char newname[MAX_PATH]; + if (!has_extension(filename, extension)) { + strcpy(newname, filename); + force_extension(newname, extension); + if (!file_exists(newname)) { + fprintf(pgpout, LANG("\nShould '%s' be renamed to '%s' [Y/n]? "), + filename, newname); + if (getyesno('y')) + return newname; + } + } + return NULL; +} /* maybe_force_extension */ + +/* + * Add a trailing directory separator to a name, if absent. + */ +static void addslash(char *name) +{ + int i = strlen(name); + if (i != 0 && !strchr(DIRSEPS, name[i - 1])) { + name[i] = DIRSEPS[0]; + name[i + 1] = '\0'; + } +} + +/* + * Builds a filename with a complete path specifier from the environmental + * variable PGPPATH. + */ char *buildfilename(char *result, char *fname) -/* Builds a filename with a complete path specifier from the environmental - variable PGPPATH. -*/ -{ char *s = getenv(PGPPATH); - - if ( s==NULL || strlen(s) > 50) /* undefined, or too long to use */ - s=""; - strcpy(result,s); - if (strlen(result) != 0) -#ifdef BSLASH - if (result[strlen(result)-1] != '\\') - strcat(result,"\\"); -#else -#ifdef FSLASH - if (result[strlen(result)-1] != '/') - strcat(result,"/"); -#else -#ifdef VMS - if (result[strlen(result)-1] != ']') - strcat(result,"]"); -#endif +{ + char const *s = getenv(PGPPATH); + + result[0] = '\0'; + + if (s && strlen(s) <= 50) { + strcpy(result, s); + } +#ifdef UNIX + /* On Unix, default to $HOME/.pgp, otherwise, current directory. */ + else { + s = getenv("HOME"); + if (s && strlen(s) <= 50) { + strcpy(result, s); + addslash(result); + strcat(result, ".pgp"); + } + } +#endif /* UNIX */ + + addslash(result); + strcat(result, fname); + return result; +} /* buildfilename */ + +char *buildsysfilename(char *result, char *fname) +{ + buildfilename(result, fname); +#ifdef PGP_SYSTEM_DIR + if (file_exists(result)) + return result; + strcpy(result, PGP_SYSTEM_DIR); + strcat(result, fname); + if (file_exists(result)) + return result; + buildfilename(result, fname); /* Put name back for error */ #endif -#endif /* Various OS-specific defines */ - strcat(result,fname); - return(result); -} /* buildfilename */ + return result; +} +/* Convert filename to canonical form, with slashes as separators */ void file_to_canon(char *filename) -{ /* Convert filename to canonical form, with slashes as separators */ +{ #ifdef BSLASH - while (*filename) { - if (*filename == '\\') - *filename = '/'; - ++filename; - } + while (*filename) { + if (*filename == '\\') + *filename = '/'; + ++filename; + } #endif } -int write_error(FILE *f) +int write_error(FILE * f) { - fflush(f); - if (ferror(f)) - { + fflush(f); + if (ferror(f)) { #ifdef ENOSPC - if (errno == ENOSPC) - fprintf(pgpout, PSTR("\nDisk full.\n")); - else + if (errno == ENOSPC) + fprintf(pgpout, LANG("\nDisk full.\n")); + else #endif - fprintf(pgpout, PSTR("\nFile write error.\n")); - return -1; - } - return 0; + fprintf(pgpout, LANG("\nFile write error.\n")); + return -1; + } + return 0; } -int copyfile(FILE *f, FILE *g, word32 longcount) -{ /* copy file f to file g, for longcount bytes */ - int count, status = 0; - do /* read and write the whole file... */ - { - if (longcount < (word32) DISKBUFSIZE) - count = (int)longcount; - else - count = DISKBUFSIZE; - count = fread(textbuf,1,count,f); - if (count>0) - { - if (CONVERSION != NO_CONV) - { int i; - for (i = 0; i < count; i++) - textbuf[i] = (CONVERSION == EXT_CONV) ? - EXT_C(textbuf[i]) : - INT_C(textbuf[i]); - } - if (fwrite(textbuf,1,count,g) != count ) - { /* Problem: return error value */ - status = -1; - break; - } - longcount -= count; - } - /* if text block was short, exit loop */ - } while (count==DISKBUFSIZE); - burn(textbuf); /* burn sensitive data on stack */ - return(status); -} /* copyfile */ +/* copy file f to file g, for longcount bytes */ +int copyfile(FILE * f, FILE * g, word32 longcount) +{ + int count, status = 0; + do { /* read and write the whole file... */ + if (longcount < (word32) DISKBUFSIZE) + count = (int) longcount; + else + count = DISKBUFSIZE; + count = fread(textbuf, 1, count, f); + if (count > 0) { + if (CONVERSION != NO_CONV) { + int i; + for (i = 0; i < count; i++) + textbuf[i] = (CONVERSION == EXT_CONV) ? + EXT_C(textbuf[i]) : + INT_C(textbuf[i]); + } + if (fwrite(textbuf, 1, count, g) != count) { + /* Problem: return error value */ + status = -1; + break; + } + longcount -= count; + } + /* if text block was short, exit loop */ + } while (count == DISKBUFSIZE); + burn(textbuf); /* burn sensitive data on stack */ + return status; +} /* copyfile */ -int copyfilepos (FILE *f, FILE *g, word32 longcount, word32 fpos) -/* Like copyfile, but takes a position for file f. Returns with +/* + * Like copyfile, but takes a position for file f. Returns with * f and g pointing just past the copied data. */ +int copyfilepos(FILE * f, FILE * g, word32 longcount, word32 fpos) { - fseek (f, fpos, SEEK_SET); - return copyfile (f, g, longcount); + fseek(f, fpos, SEEK_SET); + return copyfile(f, g, longcount); } -int copyfile_to_canon (FILE *f, FILE *g, word32 longcount) -{ /* copy file f to file g, for longcount bytes. Convert to - canonical form as we go. f is open in text mode. Canonical - form uses crlf's as line separators. */ - int count, status = 0; - byte c, *tb1, *tb2; - int i, nbytes; - int nspaces = 0; - do /* read and write the whole file... */ - { - if (longcount < (word32) DISKBUFSIZE) - count = (int)longcount; - else - count = DISKBUFSIZE; - count = fread(textbuf,1,count,f); - if (count>0) - { /* Convert by adding CR before LF */ - tb1 = textbuf; - tb2 = textbuf2; - for (i=0; i 0) { + /* Convert by adding CR before LF */ + tb1 = textbuf; + tb2 = (byte *) textbuf2; + for (i = 0; i < count; ++i) { + switch (CONVERSION) { + case EXT_CONV: + c = EXT_C(*tb1++); + break; + case INT_CONV: + c = INT_C(*tb1++); + break; + default: + c = *tb1++; } - /* if text block was short, exit loop */ - } while (count==DISKBUFSIZE); - burn(textbuf); /* burn sensitive data on stack */ - burn(textbuf2); - return(status); -} /* copyfile_to_canon */ - - -int copyfile_from_canon (FILE *f, FILE *g, word32 longcount) -{ /* copy file f to file g, for longcount bytes. Convert from - canonical to local form as we go. g is open in text mode. Canonical - form uses crlf's as line separators. */ - int count, status = 0; - byte c, *tb1, *tb2; - int i, nbytes; - do /* read and write the whole file... */ - { - if (longcount < (word32) DISKBUFSIZE) - count = (int)longcount; - else - count = DISKBUFSIZE; - count = fread(textbuf,1,count,f); - if (count>0) - { /* Convert by removing CR's */ - tb1 = textbuf; - tb2 = textbuf2; - for (i=0; i 0) { + /* Convert by removing CR's */ + tb1 = textbuf; + tb2 = (byte *) textbuf2; + for (i = 0; i < count; ++i) { + switch (CONVERSION) { + case EXT_CONV: + c = EXT_C(*tb1++); + break; + case INT_CONV: + c = INT_C(*tb1++); + break; + default: + c = *tb1++; + } + if (c != '\r') + *tb2++ = c; + } + nbytes = tb2 - (byte *) textbuf2; + if (fwrite(textbuf2, 1, nbytes, g) != nbytes) { + /* Problem: return error value */ status = -1; - fclose(g); - return(status); -} /* copyfiles_by_name */ - + break; + } + longcount -= count; + } + /* if text block was short, exit loop */ + } while (count == DISKBUFSIZE); + burn(textbuf); /* burn sensitive data on stack */ + burn(textbuf2); + return status; +} /* copyfile_from_canon */ -int make_canonical(char *srcFile, char *destFile) -/* Copy srcFile to destFile, converting to canonical text form */ +/* Copy srcFile to destFile */ +int copyfiles_by_name(char *srcFile, char *destFile) { - FILE *f, *g; - int status = 0; - long fileLength; - - if (((f=fopen(srcFile,FOPRTXT)) == NULL) || - ((g=fopen(destFile,FOPWBIN)) == NULL)) - /* Can't open files */ - return(-1); - - /* Get file length and copy it */ - fseek(f,0L,SEEK_END); - fileLength = ftell(f); - rewind(f); - CONVERSION = INT_CONV; - status = copyfile_to_canon(f,g,fileLength); - CONVERSION = NO_CONV; + FILE *f, *g; + int status = 0; + long fileLength; + + f = fopen(srcFile, FOPRBIN); + if (f == NULL) + return -1; + g = fopen(destFile, FOPWBIN); + if (g == NULL) { fclose(f); - if (write_error(g)) - status = -1; - fclose(g); - return(status); -} /* make_canonical */ + return -1; + } + /* Get file length and copy it */ + fseek(f, 0L, SEEK_END); + fileLength = ftell(f); + rewind(f); + status = copyfile(f, g, fileLength); + fclose(f); + if (write_error(g)) + status = -1; + fclose(g); + return status; +} /* copyfiles_by_name */ +/* Copy srcFile to destFile, converting to canonical text form */ +int make_canonical(char *srcFile, char *destFile) +{ + FILE *f, *g; + int status = 0; + long fileLength; + + if (((f = fopen(srcFile, FOPRTXT)) == NULL) || + ((g = fopen(destFile, FOPWBIN)) == NULL)) + /* Can't open files */ + return -1; + + /* Get file length and copy it */ + fseek(f, 0L, SEEK_END); + fileLength = ftell(f); + rewind(f); + CONVERSION = INT_CONV; + status = copyfile_to_canon(f, g, fileLength); + CONVERSION = NO_CONV; + fclose(f); + if (write_error(g)) + status = -1; + fclose(g); + return status; +} /* make_canonical */ +/* + * Like rename() but will try to copy the file if the rename fails. + * This is because under OS's with multiple physical volumes if the + * source and destination are on different volumes the rename will fail + */ int rename2(char *srcFile, char *destFile) -/* Like rename() but will try to copy the file if the rename fails. - This is because under OS's with multiple physical volumes if the - source and destination are on different volumes the rename will fail */ -{ - FILE *f, *g; - int status = 0; - long fileLength; +{ + FILE *f, *g; + int status = 0; + long fileLength; #ifdef VMS - if (rename(srcFile,destFile) != 0) + if (rename(srcFile, destFile) != 0) #else - if (rename(srcFile,destFile) == -1) + if (rename(srcFile, destFile) == -1) #endif - { /* Rename failed, try a copy */ - if (((f=fopen(srcFile,FOPRBIN)) == NULL) || - ((g=fopen(destFile,FOPWBIN)) == NULL)) - /* Can't open files */ - return(-1); - - /* Get file length and copy it */ - fseek(f,0L,SEEK_END); - fileLength = ftell(f); - rewind(f); - status = copyfile(f,g,fileLength); - if (write_error(g)) - status = -1; - - /* Zap source file if the copy went OK, otherwise zap the (possibly - incomplete) destination file */ - if (status >= 0) - { wipeout(f); /* Zap source file */ - fclose(f); - remove(srcFile); - fclose(g); - } - else - { if (is_regular_file(destFile)) - { wipeout(g); /* Zap destination file */ - fclose(g); - remove(destFile); - } else - fclose(g); - fclose(f); - } + { + /* Rename failed, try a copy */ + if (((f = fopen(srcFile, FOPRBIN)) == NULL) || + ((g = fopen(destFile, FOPWBIN)) == NULL)) + /* Can't open files */ + return -1; + + /* Get file length and copy it */ + fseek(f, 0L, SEEK_END); + fileLength = ftell(f); + rewind(f); + status = copyfile(f, g, fileLength); + if (write_error(g)) + status = -1; + + /* Zap source file if the copy went OK, otherwise zap the (possibly + incomplete) destination file */ + if (status >= 0) { + wipeout(f); /* Zap source file */ + fclose(f); + remove(srcFile); + fclose(g); + } else { + if (is_regular_file(destFile)) { + wipeout(g); /* Zap destination file */ + fclose(g); + remove(destFile); + } else { + fclose(g); + } + fclose(f); } - return(status); + } + return status; } - -int readPhantomInput(char *filename) /* read the data from stdin to the phantom input file */ -{ FILE *outFilePtr; - byte buffer[ 512 ]; - int bytesRead, status = 0; - - if (verbose) - fprintf(pgpout, "writing stdin to file %s\n", filename); - if ((outFilePtr = fopen(filename,FOPWBIN)) == NULL) - return(-1); +int readPhantomInput(char *filename) +{ + FILE *outFilePtr; + byte buffer[512]; + int bytesRead, status = 0; + + if (verbose) + fprintf(pgpout, "writing stdin to file %s\n", filename); + if ((outFilePtr = fopen(filename, FOPWBIN)) == NULL) + return -1; #if defined(MSDOS) || defined(OS2) - /* Under DOS must set input stream to binary mode to avoid data mangling */ - setmode(fileno(stdin), O_BINARY); -#endif /* MSDOS || OS2 */ - while ((bytesRead = fread (buffer, 1, 512, stdin)) > 0) - if (fwrite (buffer, 1, bytesRead, outFilePtr) != bytesRead) - { status = -1; - break; - } - if (write_error(outFilePtr)) - status = -1; - fclose (outFilePtr); + /* Under DOS must set input stream to binary mode to avoid data mangling */ + setmode(fileno(stdin), O_BINARY); +#endif /* MSDOS || OS2 */ + while ((bytesRead = fread(buffer, 1, 512, stdin)) > 0) + if (fwrite(buffer, 1, bytesRead, outFilePtr) != bytesRead) { + status = -1; + break; + } + if (write_error(outFilePtr)) + status = -1; + fclose(outFilePtr); #if defined(MSDOS) || defined(OS2) - setmode(fileno(stdin), O_TEXT); /* Reset stream */ -#endif /* MSDOS || OS2 */ - return(status); + setmode(fileno(stdin), O_TEXT); /* Reset stream */ +#endif /* MSDOS || OS2 */ + return status; } - -int writePhantomOutput(char *filename) /* write the data from the phantom output file to stdout */ -{ FILE *outFilePtr; - byte buffer[ 512 ]; - int bytesRead, status = 0; - - if (verbose) - fprintf(pgpout, "writing file %s to stdout\n", filename); - /* this can't fail since we just created the file */ - outFilePtr = fopen(filename,FOPRBIN); +int writePhantomOutput(char *filename) +{ + FILE *outFilePtr; + byte buffer[512]; + int bytesRead, status = 0; + + if (verbose) + fprintf(pgpout, "writing file %s to stdout\n", filename); + /* this can't fail since we just created the file */ + outFilePtr = fopen(filename, FOPRBIN); #if defined(MSDOS) || defined(OS2) - setmode(fileno(stdout), O_BINARY); -#endif /* MSDOS || OS2 */ - while ((bytesRead = fread (buffer, 1, 512, outFilePtr)) > 0) - if (fwrite (buffer, 1, bytesRead, stdout) != bytesRead) - { status = -1; - break; - } - fclose (outFilePtr); - fflush(stdout); - if (ferror(stdout)) - { status = -1; - fprintf(pgpout, PSTR("\007Write error on stdout.\n")); - } + setmode(fileno(stdout), O_BINARY); +#endif /* MSDOS || OS2 */ + while ((bytesRead = fread(buffer, 1, 512, outFilePtr)) > 0) + if (fwrite(buffer, 1, bytesRead, stdout) != bytesRead) { + status = -1; + break; + } + fclose(outFilePtr); + fflush(stdout); + if (ferror(stdout)) { + status = -1; + fprintf(pgpout, LANG("\007Write error on stdout.\n")); + } #if defined(MSDOS) || defined(OS2) - setmode(fileno(stdout), O_TEXT); -#endif /* MSDOS || OS2 */ + setmode(fileno(stdout), O_TEXT); +#endif /* MSDOS || OS2 */ - return(status); + return status; } /* Return the size from the current position of file f to the end */ -word32 fsize (FILE *f) +word32 fsize(FILE * f) { - long fpos = ftell (f); - long fpos2; + long fpos = ftell(f); + long fpos2; - fseek (f, 0L, SEEK_END); - fpos2 = ftell (f); - fseek (f, fpos, SEEK_SET); - return (word32)(fpos2 - fpos); + fseek(f, 0L, SEEK_END); + fpos2 = ftell(f); + fseek(f, fpos, SEEK_SET); + return (word32) (fpos2 - fpos); } /* Return TRUE if file filename looks like a pure text file */ -int is_text_file (char *filename) +int is_text_file(char *filename) { - FILE *f = fopen(filename,"r"); /* FOPRBIN gives problem with VMS */ - int i, n, bit8 = 0; - unsigned char buf[512]; - unsigned char *bufptr = buf; - unsigned char c; - - if (!f) - return(FALSE); /* error opening it, so not a text file */ - i = n = fread (buf, 1, sizeof(buf), f); - fclose(f); - if (n <= 0) - return(FALSE); /* empty file or error, not a text file */ - if (compressSignature(buf) >= 0) - return(FALSE); - while (i--) - { c = *bufptr++; - if (c & 0x80) - ++bit8; - else /* allow BEL BS HT LF VT FF CR EOF control characters */ - if (c < '\007' || (c > '\r' && c < ' ' && c != '\032')) - return(FALSE); /* not a text file */ - } - if (strcmp(language, "ru") == 0) - return(TRUE); - /* assume binary if more than 1/4 bytes have 8th bit set */ - return(bit8 < n / 4); -} /* is_text_file */ + FILE *f = fopen(filename, "r"); /* FOPRBIN gives problem with VMS */ + int i, n, bit8 = 0; + unsigned char buf[512]; + unsigned char *bufptr = buf; + unsigned char c; + + if (!f) + return FALSE; /* error opening it, so not a text file */ + i = n = fread(buf, 1, sizeof(buf), f); + fclose(f); + if (n <= 0) + return FALSE; /* empty file or error, not a text file */ + if (compressSignature(buf) >= 0) + return FALSE; + while (i--) { + c = *bufptr++; + if (c & 0x80) + ++bit8; + else /* allow BEL BS HT LF VT FF CR EOF control characters */ + if (c < '\007' || (c > '\r' && c < ' ' && c != '\032')) + return FALSE; /* not a text file */ + } + if (strcmp(language, "ru") == 0) + return TRUE; + /* assume binary if more than 1/4 bytes have 8th bit set */ + return bit8 < n / 4; +} /* is_text_file */ VOID *xmalloc(unsigned size) -{ VOID *p; - if (size == 0) - ++size; - if ((p = malloc(size)) == NULL) - { fprintf(stderr, PSTR("\n\007Out of memory.\n")); - exitPGP(1); - } - return(p); +{ + VOID *p; + if (size == 0) + ++size; + p = malloc(size); + if (p == NULL) { + fprintf(stderr, LANG("\n\007Out of memory.\n")); + exitPGP(1); + } + return p; } /*---------------------------------------------------------------------- @@ -794,15 +915,16 @@ VOID *xmalloc(unsigned size) #define TMP_INUSE 2 -static struct -{ char path[MAX_PATH]; - int flags; - int num; +static struct { + char path[MAX_PATH]; + int flags; + int num; } tmpf[MAXTMPF]; static char tmpdir[256]; /* temporary file directory */ static char outdir[256]; /* output directory */ -static char tmpbasename[64] = "pgptemp"; /* basename for temporary files */ +static char tmpbasename[64] = "pgptemp"; /* basename for + temporary files */ /* @@ -811,21 +933,21 @@ static char tmpbasename[64] = "pgptemp"; */ void settmpdir(char *path) { - char *p; + char *p; - if (path == NULL || *path == '\0') - { tmpdir[0] = '\0'; - return; - } - strcpy(tmpdir, path); - p = tmpdir + strlen(tmpdir)-1; - if (*p != '/' && *p != '\\' && *p != ']' && *p != ':') - { /* append path separator, either / or \ */ - if ((p = strchr(tmpdir, '/')) == NULL && - (p = strchr(tmpdir, '\\')) == NULL) - p = "/"; /* path did not contain / or \, use / */ - strncat(tmpdir, p, 1); - } + if (path == NULL || *path == '\0') { + tmpdir[0] = '\0'; + return; + } + strcpy(tmpdir, path); + p = tmpdir + strlen(tmpdir) - 1; + if (*p != '/' && *p != '\\' && *p != ']' && *p != ':') { + /* append path separator, either / or \ */ + if ((p = strchr(tmpdir, '/')) == NULL && + (p = strchr(tmpdir, '\\')) == NULL) + p = "/"; /* path did not contain / or \, use / */ + strncat(tmpdir, p, 1); + } } /* @@ -835,23 +957,24 @@ void settmpdir(char *path) */ void setoutdir(char *filename) { - char *p; + char *p; - if (filename == NULL) - { strcpy(outdir, tmpdir); - return; - } - strcpy(outdir, filename); - p = file_tail(outdir); - strcpy(tmpbasename, p); - *p = '\0'; - drop_extension(tmpbasename); + if (filename == NULL) { + strcpy(outdir, tmpdir); + return; + } + strcpy(outdir, filename); + p = file_tail(outdir); + strcpy(tmpbasename, p); + *p = '\0'; + drop_extension(tmpbasename); #if !defined(BSD42) && !defined(BSD43) && !defined(sun) - /* we don't depend on pathconf here, if it returns an incorrect value - * for NAME_MAX (like Linux 0.97 with minix FS) finding a unique name - * for temp files can fail. - */ - tmpbasename[10] = '\0'; /* 14 char limit */ + /* + * we don't depend on pathconf here, if it returns an incorrect value + * for NAME_MAX (like Linux 0.97 with minix FS) finding a unique name + * for temp files can fail. + */ + tmpbasename[10] = '\0'; /* 14 char limit */ #endif } @@ -860,217 +983,214 @@ void setoutdir(char *filename) */ char *tempfile(int flags) { - int i, j; - int num; - int fd; + int i, j; + int num; + int fd; #ifndef UNIX - FILE *fp; + FILE *fp; #endif - for (i = 0; i < MAXTMPF; ++i) - if (tmpf[i].flags == 0) - break; - - if (i == MAXTMPF) - { /* message only for debugging, no need for PSTR */ - fprintf(stderr, "\n\007Out of temporary files\n"); - return(NULL); - } - -again: - num = 0; - do { - for (j = 0; j < MAXTMPF; ++j) - if (tmpf[j].flags && tmpf[j].num == num) - break; - if (j < MAXTMPF) - continue; /* sequence number already in use */ - sprintf(tmpf[i].path, "%s%s.%c%02d", - ((flags & TMP_TMPDIR) && *tmpdir ? tmpdir : outdir), - tmpbasename, TMP_EXT, num); - if (!file_exists(tmpf[i].path)) - break; - } - while (++num < 100); - - if (num == 100) - { fprintf(pgpout, "\n\007tempfile: cannot find unique name\n"); - return(NULL); - } - + for (i = 0; i < MAXTMPF; ++i) + if (tmpf[i].flags == 0) + break; + + if (i == MAXTMPF) { + /* message only for debugging, no need for LANG */ + fprintf(stderr, "\n\007Out of temporary files\n"); + return NULL; + } + again: + num = 0; + do { + for (j = 0; j < MAXTMPF; ++j) + if (tmpf[j].flags && tmpf[j].num == num) + break; + if (j < MAXTMPF) + continue; /* sequence number already in use */ + sprintf(tmpf[i].path, "%s%s.%c%02d", + ((flags & TMP_TMPDIR) && *tmpdir ? tmpdir : outdir), + tmpbasename, TMP_EXT, num); + if (!file_exists(tmpf[i].path)) + break; + } + while (++num < 100); + + if (num == 100) { + fprintf(pgpout, "\n\007tempfile: cannot find unique name\n"); + return NULL; + } #if defined(UNIX) || defined(VMS) - if ((fd = open(tmpf[i].path, O_EXCL|O_RDWR|O_CREAT, 0600)) != -1) - close(fd); + if ((fd = open(tmpf[i].path, O_EXCL | O_RDWR | O_CREAT, 0600)) != -1) + close(fd); #else - if ((fp = fopen(tmpf[i].path, "w")) != NULL) - fclose(fp); - fd = (fp == NULL ? -1 : 0); + if ((fp = fopen(tmpf[i].path, "w")) != NULL) + fclose(fp); + fd = (fp == NULL ? -1 : 0); #endif - if (fd == -1) - { if (!(flags & TMP_TMPDIR)) - { flags |= TMP_TMPDIR; - goto again; - } -#ifdef UNIX - else if (tmpdir[0] == '\0') - { strcpy(tmpdir, "/tmp/"); - goto again; - } -#endif + if (fd == -1) { + if (!(flags & TMP_TMPDIR)) { + flags |= TMP_TMPDIR; + goto again; } - if (fd == -1) - { fprintf(pgpout, PSTR("\n\007Cannot create temporary file '%s'\n"), tmpf[i].path); - user_error(); +#ifdef UNIX + else if (tmpdir[0] == '\0') { + strcpy(tmpdir, "/tmp/"); + goto again; } +#endif + } + if (fd == -1) { + fprintf(pgpout, LANG("\n\007Cannot create temporary file '%s'\n"), + tmpf[i].path); + user_error(); + } #ifdef VMS - remove(tmpf[i].path); + remove(tmpf[i].path); #endif - tmpf[i].num = num; - tmpf[i].flags = flags | TMP_INUSE; - if (verbose) - fprintf(pgpout, "tempfile: created '%s'\n", tmpf[i].path); - return(tmpf[i].path); -} /* tempfile */ + tmpf[i].num = num; + tmpf[i].flags = flags | TMP_INUSE; + if (verbose) + fprintf(pgpout, "tempfile: created '%s'\n", tmpf[i].path); + return tmpf[i].path; +} /* tempfile */ /* * remove temporary file, wipe if necessary. */ void rmtemp(char *name) { - int i; + int i; - for (i = 0; i < MAXTMPF; ++i) - if (tmpf[i].flags && strcmp(tmpf[i].path, name) == 0) - break; - - if (i < MAXTMPF) - { if (strlen(name) > 3 && name[strlen(name)-3] == TMP_EXT) - { /* only remove file if name hasn't changed */ - if (verbose) - fprintf(pgpout, "rmtemp: removing '%s'\n", name); - if (tmpf[i].flags & TMP_WIPE) - wipefile(name); - if (!remove(name)) { - tmpf[i].flags = 0; - } else if (verbose) { - fprintf(stderr,"\nrmtemp: Failed to remove %s",name); - perror ("\nError"); - } - } else if (verbose) - fprintf(pgpout, "rmtemp: not removing '%s'\n", name); - } -} /* rmtemp */ + for (i = 0; i < MAXTMPF; ++i) + if (tmpf[i].flags && strcmp(tmpf[i].path, name) == 0) + break; + + if (i < MAXTMPF) { + if (strlen(name) > 3 && name[strlen(name) - 3] == TMP_EXT) { + /* only remove file if name hasn't changed */ + if (verbose) + fprintf(pgpout, "rmtemp: removing '%s'\n", name); + if (tmpf[i].flags & TMP_WIPE) + wipefile(name); + if (!remove(name)) { + tmpf[i].flags = 0; + } else if (verbose) { + fprintf(stderr, "\nrmtemp: Failed to remove %s", name); + perror("\nError"); + } + } else if (verbose) + fprintf(pgpout, "rmtemp: not removing '%s'\n", name); + } +} /* rmtemp */ /* * make temporary file permanent, returns the new name. */ char *savetemp(char *name, char *newname) { - int i, overwrite; - static char buf[MAX_PATH]; + int i, overwrite; + static char buf[MAX_PATH]; - if (strcmp(name, newname) == 0) - return(name); - - for (i = 0; i < MAXTMPF; ++i) - if (tmpf[i].flags && strcmp(tmpf[i].path, name) == 0) - break; - - if (i < MAXTMPF) - { if (strlen(name) < 4 || name[strlen(name)-3] != TMP_EXT) - { if (verbose) - fprintf(pgpout, "savetemp: not renaming '%s' to '%s'\n", - name, newname); - return(name); /* return original file name */ - } - } + if (strcmp(name, newname) == 0) + return name; - while (file_exists(newname)) - { - if (batchmode && !force_flag) - { fprintf(pgpout,PSTR("\n\007Output file '%s' already exists.\n"),newname); - return NULL; - } - if (is_regular_file(newname)) - { - if (force_flag) - { /* remove without asking */ - remove(newname); - break; - } - fprintf(pgpout,PSTR("\n\007Output file '%s' already exists. Overwrite (y/N)? "), - newname); - overwrite = getyesno('n'); - } - else - { fprintf(pgpout,PSTR("\n\007Output file '%s' already exists.\n"),newname); - if (force_flag) /* never remove special file */ - return NULL; - overwrite = FALSE; - } - - if (!overwrite) - { fprintf(pgpout, PSTR("\nEnter new file name: ")); - getstring(buf, MAX_PATH - 1, TRUE); - if (buf[0] == '\0') - return(NULL); - newname = buf; - } - else - remove(newname); - } - if (verbose) - fprintf(pgpout, "savetemp: renaming '%s' to '%s'\n", name, newname); - if (rename2(name, newname) < 0) - { /* errorLvl = UNKNOWN_FILE_ERROR; */ - fprintf(pgpout, PSTR("Can't create output file '%s'\n"), newname); - return(NULL); - } - if (i < MAXTMPF) - tmpf[i].flags = 0; - return(newname); -} /* savetemp */ + for (i = 0; i < MAXTMPF; ++i) + if (tmpf[i].flags && strcmp(tmpf[i].path, name) == 0) + break; + + if (i < MAXTMPF) { + if (strlen(name) < 4 || name[strlen(name) - 3] != TMP_EXT) { + if (verbose) + fprintf(pgpout, "savetemp: not renaming '%s' to '%s'\n", + name, newname); + return name; /* return original file name */ + } + } + while (file_exists(newname)) { + if (batchmode && !force_flag) { + fprintf(pgpout, LANG("\n\007Output file '%s' already exists.\n"), + newname); + return NULL; + } + if (is_regular_file(newname)) { + if (force_flag) { + /* remove without asking */ + remove(newname); + break; + } + fprintf(pgpout, + LANG("\n\007Output file '%s' already exists. Overwrite (y/N)? "), + newname); + overwrite = getyesno('n'); + } else { + fprintf(pgpout, + LANG("\n\007Output file '%s' already exists.\n"), newname); + if (force_flag) /* never remove special file */ + return NULL; + overwrite = FALSE; + } + + if (!overwrite) { + fprintf(pgpout, LANG("\nEnter new file name: ")); + getstring(buf, MAX_PATH - 1, TRUE); + if (buf[0] == '\0') + return NULL; + newname = buf; + } else { + remove(newname); + } + } + if (verbose) + fprintf(pgpout, "savetemp: renaming '%s' to '%s'\n", name, newname); + if (rename2(name, newname) < 0) { + /* errorLvl = UNKNOWN_FILE_ERROR; */ + fprintf(pgpout, LANG("Can't create output file '%s'\n"), newname); + return NULL; + } + if (i < MAXTMPF) + tmpf[i].flags = 0; + return newname; +} /* savetemp */ /* * like savetemp(), only make backup of destname if it exists */ int savetempbak(char *tmpname, char *destname) { - char bakpath[MAX_PATH]; + char bakpath[MAX_PATH]; #ifdef UNIX - int mode = -1; + int mode = -1; #endif - if (is_tempfile(destname)) - remove(destname); - else - { if (file_exists(destname)) - { + if (is_tempfile(destname)) { + remove(destname); + } else { + if (file_exists(destname)) { #ifdef UNIX - struct stat st; - if (stat(destname, &st) != -1) - mode = st.st_mode & 07777; -#endif - strcpy(bakpath, destname); - force_extension(bakpath, BAK_EXTENSION); - remove(bakpath); + struct stat st; + if (stat(destname, &st) != -1) + mode = st.st_mode & 07777; +#endif + strcpy(bakpath, destname); + force_extension(bakpath, BAK_EXTENSION); + remove(bakpath); #ifdef VMS - if (rename(destname, bakpath) != 0) + if (rename(destname, bakpath) != 0) #else - if (rename(destname, bakpath) == -1) + if (rename(destname, bakpath) == -1) #endif - return(-1); - } + return -1; } - if (savetemp(tmpname, destname) == NULL) - return(-1); + } + if (savetemp(tmpname, destname) == NULL) + return -1; #ifdef UNIX - if (mode != -1) - chmod(destname, mode); + if (mode != -1) + chmod(destname, mode); #endif - return(0); + return 0; } /* @@ -1078,9 +1198,203 @@ int savetempbak(char *tmpname, char *des */ void cleanup_tmpf(void) { - int i; + int i; - for (i = 0; i < MAXTMPF; ++i) - if (tmpf[i].flags) - rmtemp(tmpf[i].path); -} /* cleanup_tmpf */ + for (i = 0; i < MAXTMPF; ++i) + if (tmpf[i].flags) + rmtemp(tmpf[i].path); +} /* cleanup_tmpf */ + +/* + * Routines to search for the manuals. + * + * Why all this code? + * + * Some people may object to PGP insisting on finding the manual somewhere + * in the neighborhood to generate a key. They bristle against this + * seemingly authoritarian attitude. Some people have even modified PGP + * to defeat this feature, and redistributed their hotwired version to + * others. That creates problems for me (PRZ). + * + * Here is the problem. Before I added this feature, there were maimed + * versions of the PGP distribution package floating around that lacked + * the manual. One of them was uploaded to Compuserve, and was + * distributed to countless users who called me on the phone to ask me why + * such a complicated program had no manual. It spread out to BBS systems + * around the country. And a freeware distributor got hold of the package + * from Compuserve and enshrined it on CD-ROM, distributing thousands of + * copies without the manual. What a mess. + * + * Please don't make my life harder by modifying PGP to disable this + * feature so that others may redistribute PGP without the manual. If you + * run PGP on a palmtop with no memory for the manual, is it too much to + * ask that you type one little extra word on the command line to do a key + * generation, a command that is seldom used by people who already know + * how to use PGP? If you can't stand even this trivial inconvenience, + * can you suggest a better method of reducing PGP's distribution without + * the manual? + */ + +static unsigned ext_missing(char *prefix) +{ + static char const *const extensions[] = +#ifdef VMS + { ".doc", ".txt", ".man", ".tex", ".", 0 }; +#else + { ".doc", ".txt", ".man", ".tex", "", 0 }; +#endif + char const *const *p; + char *end = prefix + strlen(prefix); + + for (p = extensions; *p; p++) { + strcpy(end, *p); +#if 0 /* Debugging code */ + fprintf(pgpout, "Looking for \"%s\"\n", prefix); +#endif + if (file_exists(prefix)) + return 0; + } + return 1; +} + +/* + * Returns mask of files missing + */ +static unsigned files_missing(char *prefix) +{ + static char const *const names[] = + {"pgpdoc1", "pgpdoc2", 0}; + char const *const *p; + unsigned bit, mask = 3; + int len = strlen(prefix); + +#ifndef VMS + /* + * Optimization: if directory doesn't exist, stop. But access() + * (used internally by file_exists()) doesn't work on dirs under VMS. + */ + if (prefix[0] && !file_exists(prefix)) /* Directory doesn't exist? */ + return mask; +#endif /* VMS */ + if (len && strchr(DIRSEPS, prefix[len - 1]) == 0) + prefix[len++] = DIRSEPS[0]; + for (p = names, bit = 1; *p; p++, bit <<= 1) { + strcpy(prefix + len, *p); + if (!ext_missing(prefix)) + mask &= ~bit; + } + + return mask; /* Bitmask of which files exist */ +} + +/* + * Search prefix directory and doc subdirectory. + */ +static unsigned doc_missing(char *prefix) +{ + unsigned mask; + int len = strlen(prefix); + + mask = files_missing(prefix); + if (!mask) + return 0; +#ifdef VMS + if (len && prefix[len - 1] == ']') { + strcpy(prefix + len - 1, ".doc]"); + } else { + assert(!len || prefix[len - 1] == ':'); + strcpy(prefix + len, "[doc]"); + } +#else + if (len && prefix[len - 1] != DIRSEPS[0]) + prefix[len++] = DIRSEPS[0]; + strcpy(prefix + len, "doc"); +#endif + + mask &= files_missing(prefix); + + prefix[len] = '\0'; + return mask; +} + +/* + * Expands a leading environment variable. Returns 0 on success; + * <0 if there is an error. + */ +static int expand_env(char const *src, char *dest) +{ + char const *var, *suffix; + unsigned len; + + if (*src != '$') { + strcpy(dest, src); + return 0; + } + /* Find end of variable */ + if (src[1] == '{') { /* ${FOO} form */ + var = src + 2; + len = strchr(var, '}') - var; + suffix = src + 2 + len + 1; + } else { /* $FOO form - allow $ for VMS */ + var = src + 1; + len = strspn(var, "ABCDEFGHIJKLMNOPQRSTUVWXYZ$_"); + suffix = src + 1 + len; + } + + memcpy(dest, var, len); /* Copy name */ + dest[len] = '\0'; /* Null-terminate */ + + var = getenv(dest); + if (!var || !*var) + return -1; /* No env variable */ + + /* Copy expanded form to destination */ + strcpy(dest, var); + + /* Add tail */ + strcat(dest, suffix); + + return 0; +} + +/* Don't forget to change 'pgp26' whenever you update rel_version past 2.6 */ +char const *const manual_dirs[] = +{ +#if defined(VMS) + "$PGPPATH", "", "[pgp]", "[pgp26]", "[pgp262]", + PGP_SYSTEM_DIR, "SYS$LOGIN:", "SYS$LOGIN:[pgp]", + "SYS$LOGIN:[pgp26]", "SYS$LOGIN:[pgp262]", "[-]", +#elif defined(UNIX) + "$PGPPATH", "", "pgp", "pgp26", "pgp262", PGP_SYSTEM_DIR, + "$HOME/.pgp", "$HOME", "$HOME/pgp", "$HOME/pgp26", "..", +#elif defined(AMIGA) + "$PGPPATH", "", "pgp", "pgp26", ":pgp", ":pgp26", ":pgp262", + ":", "/", +#else /* MSDOS or ATARI */ + "$PGPPATH", "", "pgp", "pgp26", "\\pgp", "\\pgp26", "\\pgp262", + "\\", "..", "c:\\pgp", "c:\\pgp26", +#endif + 0}; + +unsigned manuals_missing(void) +{ + char buf[256]; + unsigned mask = ~((unsigned)0); + char const *const *p; + + for (p = manual_dirs; *p; p++) { + if (expand_env(*p, buf) < 0) + continue; /* Ignore */ + mask &= doc_missing(buf); + if (!mask) + break; + } + + return mask; +} + +/* + * Why all this code? + * + * See block of comments above. + */