home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1998 February / PCOnline_02_1998.iso / filesbbs / os2 / pgp263.arj / PGP263I.SRC / PGP263II.ZIP / src / fileio.c < prev    next >
C/C++ Source or Header  |  1996-01-06  |  44KB  |  1,684 lines

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