home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / elm / elm2.4 / src / syscall.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-08  |  18.9 KB  |  676 lines

  1.  
  2. static char rcsid[] = "@(#)$Id: syscall.c,v 5.7 1993/01/20 03:48:08 syd Exp $";
  3.  
  4. /*******************************************************************************
  5.  *  The Elm Mail System  -  $Revision: 5.7 $   $State: Exp $
  6.  *
  7.  *            Copyright (c) 1988-1992 USENET Community Trust
  8.  *            Copyright (c) 1986,1987 Dave Taylor
  9.  *******************************************************************************
  10.  * Bug reports, patches, comments, suggestions should be sent to:
  11.  *
  12.  *    Syd Weinstein, Elm Coordinator
  13.  *    elm@DSI.COM            dsinc!elm
  14.  *
  15.  *******************************************************************************
  16.  * $Log: syscall.c,v $
  17.  * Revision 5.7  1993/01/20  03:48:08  syd
  18.  * Fix not to use vfork if SY_ENV_SHELL is set, as this causes the
  19.  * parent environment to be modified.
  20.  * From: Syd
  21.  *
  22.  * Revision 5.7  1993/01/20  03:48:08  syd
  23.  * Fix not to use vfork if SY_ENV_SHELL is set, as this causes the
  24.  * parent environment to be modified.
  25.  * From: Syd
  26.  *
  27.  * Revision 5.6  1992/12/20  05:29:33  syd
  28.  * Fixed where when doing ! or | and ti/te is enabled, one doesn't see the
  29.  * "Press any key to return to ELM:" message. because the screens are
  30.  * switched before the message is printed.
  31.  * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
  32.  *
  33.  * Revision 5.5  1992/12/11  02:05:26  syd
  34.  * List_folder knew only about '=' but nothing about the rest
  35.  * of [+=%] as one would have expected.
  36.  * From: Jukka Antero Ukkonen <ukkonen@venus.csc.fi>
  37.  *
  38.  * Revision 5.4  1992/12/11  01:58:50  syd
  39.  * Allow for use from restricted shell by putting SHELL=/bin/sh in the
  40.  * environment of spawned mail transport program.
  41.  * From: chip@tct.com (Chip Salzenberg)
  42.  *
  43.  * Revision 5.3  1992/11/07  20:45:39  syd
  44.  * add no tite flag on options that should not use ti/te
  45.  * Hack by Syd
  46.  *
  47.  * Revision 5.2  1992/11/07  19:37:21  syd
  48.  * Enhanced printing support.  Added "-I" to readmsg to
  49.  * suppress spurious diagnostic messages.
  50.  * From: chip@chinacat.unicom.com (Chip Rosenthal)
  51.  *
  52.  * Revision 5.1  1992/10/03  22:58:40  syd
  53.  * Initial checkin as of 2.4 Release at PL0
  54.  *
  55.  *
  56.  ******************************************************************************/
  57.  
  58. /** These routines are used for user-level system calls, including the
  59.     '!' command and the '|' commands...
  60.  
  61. **/
  62.  
  63. #include "headers.h"
  64. #include "s_elm.h"
  65.  
  66. #include <errno.h>
  67.  
  68. #ifdef BSD
  69. #  include <sys/wait.h>
  70. #endif
  71.  
  72. char *argv_zero();    
  73. void  _exit();
  74.  
  75. #ifdef ALLOW_SUBSHELL
  76.  
  77. int
  78. subshell()
  79. {
  80.     /** spawn a subshell with either the specified command
  81.         returns non-zero if screen rewrite needed
  82.     **/
  83.  
  84.     char command[SLEN];
  85.     int  old_raw, helpful, ret;
  86.  
  87.     helpful = (user_level == 0);
  88.  
  89.     if (helpful)
  90.       PutLine0(LINES-3, COLUMNS-40, catgets(elm_msg_cat, ElmSet, ElmUseShellName,
  91.         "(Use the shell name for a shell.)"));
  92.     PutLine0(LINES-2, 0, catgets(elm_msg_cat, ElmSet, ElmShellCommand,
  93.         "Shell command: "));
  94.     CleartoEOS();
  95.     command[0] = '\0';
  96.     (void) optionally_enter(command, LINES-2, 15, FALSE, FALSE);
  97.     if (command[0] == 0) {
  98.       if (helpful)
  99.         MoveCursor(LINES-3,COLUMNS-40);
  100.       else
  101.         MoveCursor(LINES-2,0);
  102.       CleartoEOS();
  103.       return 0;
  104.     }
  105.  
  106.     MoveCursor(LINES,0);
  107.     CleartoEOLN();
  108.  
  109.     if ((old_raw = RawState()) == ON)
  110.       Raw(OFF);
  111.     softkeys_off();
  112.     if (cursor_control)
  113.       transmit_functions(OFF);
  114.  
  115.     umask(original_umask);    /* restore original umask so users new files are ok */
  116.     ret = system_call(command, SY_USER_SHELL|SY_ENAB_SIGINT|SY_DUMPSTATE);
  117.     umask(077);        /* now put it back to private for mail files */
  118.  
  119.     SetXYLocation(0, 40);    /* a location not near the next request, so an absolute is used */
  120.     PutLine0(LINES, 0, catgets(elm_msg_cat, ElmSet, ElmPressAnyKeyToReturn,
  121.         "\n\nPress any key to return to ELM: "));
  122.     Raw(ON | NO_TITE);
  123.     (void) getchar();
  124.     printf("\r\n");
  125.     Raw(OFF | NO_TITE); /* Done even if old_raw == ON, to get ti/te right */
  126.     if (old_raw == ON)
  127.       Raw(ON);
  128.  
  129.     softkeys_on();
  130.     if (cursor_control)
  131.       transmit_functions(ON);
  132.  
  133.     if (ret)
  134.       error1(catgets(elm_msg_cat, ElmSet, ElmReturnCodeWas,
  135.         "Return code was %d."), ret);
  136.  
  137.     return 1;
  138. }
  139.  
  140. #endif /* ALLOW_SUBSHELL */
  141.  
  142. int system_call(string, options)
  143. char *string;
  144. int options;
  145. {
  146.     /** execute 'string', setting uid to userid... **/
  147.  
  148.     /** The following might be encoded into the "options" parameter:
  149.  
  150.         SY_USER_SHELL    When set, we will use the user-defined
  151.                 "shell" instead of "/bin/sh" for the
  152.                 shell escape.
  153.  
  154.         SY_ENV_SHELL    When set, put "SHELL=[name-of-shell]" in
  155.                 the child's environment.  This hack makes
  156.                 mail transport programs work right even
  157.                 for users with restricted shells.
  158.  
  159.         SY_ENAB_SIGHUP    When set, we will set SIGHUP, SIGTSTP, and
  160.                 SIGCONT to their default behaviour during
  161.                 the shell escape rather than ignoring them.
  162.                 This is particularly important with stuff
  163.                 like `vi' so it can preserve the session on
  164.                 a SIGHUP and do its thing with job control.
  165.  
  166.         SY_ENAB_SIGINT    This option implies SY_ENAB_SIGHUP.  In
  167.                 addition to the signals listed above, this
  168.                 option will also set SIGINT and SIGQUIT
  169.                 to their default behaviour rather than
  170.                 ignoring them.
  171.  
  172.         SY_DUMPSTATE    Create a state file for use by the "readmsg"
  173.                 program.  This is so that if "readmsg" is
  174.                 invoked it can figure out what folder we are
  175.                 in and what message(s) are selected.
  176.     **/
  177.  
  178.     int pfd[2], stat, pid, w, iteration;
  179.     char *sh;
  180. #if defined(BSD) && !defined(WEXITSTATUS)
  181.     union wait status;
  182. #else
  183.     int status;
  184. #endif
  185.     register SIGHAND_TYPE (*istat)(), (*qstat)();
  186. #ifdef SIGTSTP
  187.     register SIGHAND_TYPE (*oldstop)(), (*oldstart)();
  188. #endif
  189.     extern int errno;
  190.  
  191.     /* flush any pending output */
  192.     fflush(stdout);
  193.  
  194.     /* figure out what shell we are using here */
  195.     sh = ((options & SY_USER_SHELL) ? shell : "/bin/sh");
  196.     dprint(2, (debugfile, "System Call: %s\n\t%s\n", sh, string));
  197.  
  198.     /* if we aren't reading a folder then a state dump is meaningless */
  199.     if (mail_only)
  200.         options &= ~SY_DUMPSTATE;
  201.  
  202.     /* see if we need to dump out the folder state */
  203.     if (options & SY_DUMPSTATE) {
  204.         if (create_folder_state_file() != 0)
  205.         return -1;
  206.     }
  207.  
  208.     /*
  209.      * Note the neat trick with close-on-exec pipes.
  210.      * If the child's exec() succeeds, then the pipe read returns zero.
  211.      * Otherwise, it returns the zero byte written by the child
  212.      * after the exec() is attempted.  This is the cleanest way I know
  213.      * to discover whether an exec() failed.   --CHS
  214.      */
  215.  
  216.     if (pipe(pfd) == -1) {
  217.       perror("pipe");
  218.       return -1;
  219.     }
  220.     fcntl(pfd[0], F_SETFD, 1);
  221.     fcntl(pfd[1], F_SETFD, 1);
  222.  
  223.     istat = signal(SIGINT, SIG_IGN);
  224.     qstat = signal(SIGQUIT, SIG_IGN);
  225. #ifdef SIGTSTP
  226.     oldstop = signal(SIGTSTP, SIG_DFL);
  227.     oldstart = signal(SIGCONT, SIG_DFL);
  228. #endif
  229.  
  230.     stat = -1;        /* Assume failure. */
  231.  
  232.     for (iteration = 0; iteration < 5; ++iteration) {
  233.       if (iteration > 0)
  234.         sleep(2);
  235.  
  236. #ifdef VFORK
  237.       if (options&SY_ENV_SHELL)
  238.         pid = fork();
  239.       else
  240.         pid = vfork();
  241. #else
  242.       pid = fork();
  243. #endif
  244.  
  245.       if (pid != -1)
  246.         break;
  247.     }
  248.  
  249.     if (pid == -1) {
  250.       perror("fork");
  251.     }
  252.     else if (pid == 0) {
  253.       /*
  254.        * Set group and user back to their original values.
  255.        * Note that group must be set first.
  256.        */
  257.       setgid(groupid);
  258.       setuid(userid);
  259.  
  260.       /*
  261.        * Program to exec may or may not be able to handle
  262.        * interrupt, quit, hangup and stop signals.
  263.        */
  264.       if (options&SY_ENAB_SIGINT)
  265.         options |= SY_ENAB_SIGHUP;
  266.       (void) signal(SIGHUP,  (options&SY_ENAB_SIGHUP) ? SIG_DFL : SIG_IGN);
  267.       (void) signal(SIGINT,  (options&SY_ENAB_SIGINT) ? SIG_DFL : SIG_IGN);
  268.       (void) signal(SIGQUIT, (options&SY_ENAB_SIGINT) ? SIG_DFL : SIG_IGN);
  269. #ifdef SIGTSTP
  270.       (void) signal(SIGTSTP, (options&SY_ENAB_SIGHUP) ? SIG_DFL : SIG_IGN);
  271.       (void) signal(SIGCONT, (options&SY_ENAB_SIGHUP) ? SIG_DFL : SIG_IGN);
  272. #endif
  273.  
  274.       /* Optionally override the SHELL environment variable. */
  275.       if (options&SY_ENV_SHELL) {
  276.         static char sheq[] = "SHELL=";
  277.         char *p = malloc(sizeof(sheq) + strlen(sh));
  278.         if (p) {
  279.           sprintf(p, "%s%s", sheq, sh);
  280.           putenv(p);
  281.         }
  282.       }
  283.  
  284.       /* Go for it. */
  285.       if (string) execl(sh, argv_zero(sh), "-c", string, (char *) 0);
  286.       else execl(sh, argv_zero(sh), (char *) 0);
  287.  
  288.       /* If exec fails, we write a byte to the pipe before exiting. */
  289.       perror(sh);
  290.       write(pfd[1], "", 1);
  291.       _exit(127);
  292.     }
  293.     else {
  294.       int rd;
  295.       char ch;
  296.  
  297.       /* Try to read a byte from the pipe. */
  298.       close(pfd[1]);
  299.       rd = read(pfd[0], &ch, 1);
  300.       close(pfd[0]);
  301.  
  302.       while ((w = wait(&status)) != pid)
  303.           if (w == -1 && errno != EINTR)
  304.           break;
  305.  
  306.       /* If we read a byte from the pipe, the exec failed. */
  307.       if (rd > 0)
  308.         stat = -1;
  309.       else if (w == pid) {
  310. #ifdef    WEXITSTATUS
  311.         stat = WEXITSTATUS(status);
  312. #else
  313. # ifdef    BSD
  314.         stat = status.w_retcode;
  315. # else
  316.         stat = status;
  317. # endif
  318. #endif
  319.       }
  320.       }
  321.   
  322.     (void) signal(SIGINT, istat);
  323.     (void) signal(SIGQUIT, qstat);
  324. #ifdef SIGTSTP
  325.     (void) signal(SIGTSTP, oldstop);
  326.     (void) signal(SIGCONT, oldstart);
  327. #endif
  328.  
  329.     /* cleanup any folder state file we made */
  330.     if (options & SY_DUMPSTATE)
  331.         (void) remove_folder_state_file();
  332.  
  333.     return(stat);
  334. }
  335.  
  336. int
  337. do_pipe()
  338. {
  339.     /** pipe the current message or tagged messages to
  340.         the specified sequence.. **/
  341.  
  342.     char command[SLEN], buffer[SLEN], *prompt;
  343.     register int  ret;
  344.     int    old_raw;
  345.  
  346.     prompt = catgets(elm_msg_cat, ElmSet, ElmPipeTo, "Pipe to: ");
  347.         PutLine0(LINES-2, 0, prompt);
  348.     command[0] = '\0';
  349.     (void) optionally_enter(command, LINES-2, strlen(prompt), FALSE, FALSE);
  350.     if (command[0] == '\0') {
  351.       MoveCursor(LINES-2,0);
  352.       CleartoEOLN();
  353.       return(0);
  354.     }
  355.  
  356.     MoveCursor(LINES,0);
  357.     CleartoEOLN();
  358.     if (( old_raw = RawState()) == ON)
  359.       Raw(OFF);
  360.  
  361.     if (cursor_control)
  362.       transmit_functions(OFF);
  363.     
  364.     sprintf(buffer, "%s -Ih|%s", readmsg, command);
  365.     ret = system_call(buffer, SY_USER_SHELL|SY_ENAB_SIGINT|SY_DUMPSTATE);
  366.  
  367.     SetXYLocation(0, 40);    /* a location not near the next request, so an absolute is used */
  368.     PutLine0(LINES, 0, catgets(elm_msg_cat, ElmSet, ElmPressAnyKeyToReturn,
  369.         "\n\nPress any key to return to ELM: "));
  370.  
  371.     Raw(ON | NO_TITE);
  372.     (void) getchar();
  373.     printf("\r\n");
  374.     Raw(OFF | NO_TITE); /* Done even if old_raw == ON, to get ti/te right */
  375.     if (old_raw == ON)
  376.       Raw(ON);
  377.  
  378.     if (cursor_control)  transmit_functions(ON);
  379.  
  380.     if (ret != 0)
  381.       error1(catgets(elm_msg_cat, ElmSet, ElmReturnCodeWas,
  382.         "Return code was %d."), ret);
  383.     return(1);
  384. }
  385.  
  386. int print_msg(pause_on_scroll)
  387. int pause_on_scroll;
  388. {
  389.     /*
  390.      * Print the tagged messages, or the current message if none are
  391.      * tagged.  Message(s) are passed passed into the command specified
  392.      * by "printout".  An error is given if "printout" is undefined.
  393.      *
  394.      * Printing will be done through a pipe so we can print the number
  395.      * of lines output.  This is used to determine whether the screen
  396.      * got trashed by the print command.  One limitation is that only
  397.      * stdout lines are counted, not stderr output.  A nonzero result
  398.      * is returned if we think enough output was generated to trash
  399.      * the display, a zero result indicates the display is probably
  400.      * alright.  Further, if the display is trashed and "pause_on_scroll"
  401.      * is true then we'll give a "hit any key" prompt before returning.
  402.      *
  403.      * This routine has two modes of behavior, depending upon whether
  404.      * there is a "%s" embedded in the "printout" string.  If there,
  405.      * the old Elm behavior is used (a temp file is used, all output
  406.      * from the print command is chucked out).  If there isn't a "%s"
  407.      * then the new behavior is used (message(s) piped right into
  408.      * print command, output is left attached to the terminal).
  409.      *
  410.      * The old behaviour is bizarre.  I hope we can ditch it someday.
  411.      */
  412.  
  413.     char buffer[SLEN], filename[SLEN], printbuffer[SLEN];
  414.     int  nlines, retcode, old_raw;
  415.     FILE *fp;
  416.  
  417.     /*
  418.      * Make sure we know how to print.
  419.      */
  420.     if (printout[0] == '\0') {
  421.         error(catgets(elm_msg_cat, ElmSet, ElmPrintDontKnowHow,
  422.         "Don't know how to print - option \"printmail\" undefined!"));
  423.         return 0;
  424.     }
  425.  
  426.     /*
  427.      * Temp file name used by "old style" printing.
  428.      */
  429.         sprintf(filename,"%s%s%d", temp_dir, temp_print, getpid());
  430.  
  431.     /*
  432.      * Setup print command.  Select old or new behavior based
  433.      * upon the presence of "%s" in the print command string.
  434.      */
  435.     if (in_string(printout, "%s")) {
  436.         sprintf(printbuffer, printout, filename);
  437.         sprintf(buffer,"(%s -Ip > %s; %s 2>&1) > /dev/null",
  438.         readmsg, filename, printbuffer);
  439.     } else {
  440.         sprintf(buffer,"%s -Ip | %s", readmsg, printout);
  441.     }
  442.  
  443.     /*
  444.      * Create information for "readmsg" command.
  445.      */
  446.     if (create_folder_state_file() != 0)
  447.         return 0;
  448.  
  449.     /*
  450.      * Put keyboard into normal state.
  451.      */
  452.     if ((old_raw = RawState()) == ON)
  453.         Raw(OFF | NO_TITE);
  454.     softkeys_off();
  455.     if (cursor_control)
  456.         transmit_functions(OFF);
  457.  
  458.     /*
  459.      * Run the print command in a pipe and grab the output.
  460.      */
  461.     putchar('\n');
  462.     fflush(stdout);
  463.     nlines = 0;
  464.     if ((fp = popen(buffer, "r")) == NULL) {
  465.         error(catgets(elm_msg_cat, ElmSet, ElmPrintPipeFailed,
  466.         "Cannot create pipe to print command."));
  467.         goto done;
  468.     }
  469.     while (fgets(buffer, sizeof(buffer), fp) != NULL) {
  470.         fputs(buffer, stdout);
  471.         ++nlines;
  472.     }
  473.  
  474.     /*
  475.      * See if there were enough lines printed to trash the screen.
  476.      */
  477.     if (pause_on_scroll && nlines > 1) {
  478.         printf("\n%s ", catgets(elm_msg_cat, ElmSet, ElmPrintPressAKey,
  479.         "Press any key to continue:"));
  480.         fflush(stdout);
  481.         Raw(ON | NO_TITE);
  482.         (void) getchar();
  483.     }
  484.  
  485.     /*
  486.      * Display a status message.
  487.      */
  488.     if ((retcode = pclose(fp)) == 0) {
  489.         error(catgets(elm_msg_cat, ElmSet, ElmPrintJobSpooled,
  490.         "Print job has been spooled."));
  491.     } else if ((retcode & 0xFF) == 0) {
  492.         error1(catgets(elm_msg_cat, ElmSet, ElmPrintFailCode,
  493.         "Printout failed with return code %d."), (retcode>>8));
  494.     } else {
  495.         error1(catgets(elm_msg_cat, ElmSet, ElmPrintFailStatus,
  496.         "Printout failed with status 0x%04x."), (retcode>>8));
  497.     }
  498.  
  499.     /*
  500.      * Hack alert:  The only place we use "pause_on_scroll" false is when
  501.      * printing while reading a mail message.  This newline prevents the
  502.      * above message from being wiped out by the command prompt.
  503.      */
  504.     if (!pause_on_scroll)
  505.         putchar('\n');
  506.  
  507. done:
  508.     Raw(old_raw | NO_TITE);
  509.     softkeys_on();
  510.     if (cursor_control)
  511.         transmit_functions(ON);
  512.     (void) unlink(filename);
  513.     (void) remove_folder_state_file();
  514.     return (nlines > 1);
  515. }
  516.  
  517.  
  518. list_folders(numlines, helpmsg, wildcard)
  519. unsigned numlines;
  520. char *helpmsg;
  521. char *wildcard;
  522. {
  523.     /** list the folders in the users FOLDERHOME directory.  This is
  524.         simply a call to "ls -C" unless there is a wildcard, in
  525.         which case it's "ls -C wildcard".  Note that wildcards can
  526.         refer either to the folder directory (in which case they
  527.         start with an '=') or a general directory, in which case we
  528.         take them at face value.
  529.         Numlines is the number of lines to scroll afterwards. This is
  530.         useful when a portion of the screen needs to be cleared for
  531.         subsequent prompts, but you don't want to overwrite the
  532.         list of folders.
  533.         Helpmsg is what should be printed before the listing if not NULL.
  534.     **/
  535.  
  536.     char buffer[SLEN];
  537.  
  538.     Raw(OFF | NO_TITE);
  539.     ClearScreen();
  540.     MoveCursor(LINES, 0);
  541.     if(helpmsg)
  542.       printf(helpmsg);
  543.     if ( NULL == wildcard )
  544.     {
  545.       sprintf(buffer, "cd %s;ls -C", folders);
  546.       printf(catgets(elm_msg_cat, ElmSet, ElmContentsOfYourFolderDir,
  547.         "\n\rContents of your folder directory:\n\r\n\r"));
  548.       (void) system_call(buffer, 0); 
  549.     }
  550.     else
  551.     {
  552.       if (( *wildcard == '=' ) ||
  553.           ( *wildcard == '+' ) || ( *wildcard == '%' ))
  554.       {
  555.         sprintf(buffer, "cd %s;ls -C %s", folders, wildcard+1);
  556.         printf(catgets(elm_msg_cat, ElmSet, ElmFoldersWhichMatch,
  557.         "\n\rFolders which match `%s':\n\r\n\r"), wildcard+1);
  558.       }
  559.           else
  560.       {
  561.         sprintf(buffer, "ls -C %s", wildcard);
  562.         printf(catgets(elm_msg_cat, ElmSet, ElmFilesWhichMatch,
  563.         "\n\rFiles which match `%s':\n\r\n\r"), wildcard);
  564.       }
  565.       (void) system_call(buffer, 0); 
  566.     }
  567.     while(numlines--)
  568.         printf("\n\r");
  569.     Raw(ON | NO_TITE);
  570. }
  571.  
  572.  
  573. static char folder_state_env_param[SLEN], *folder_state_fname;
  574.  
  575. /*
  576.  * Setup a folder state file for external utilities (e.g. "readmsg").
  577.  * Returns zero if the file was created, -1 if an error occurred.  A
  578.  * diagnostic will have been printed on an error return.
  579.  *
  580.  * The state file contains the following:
  581.  *
  582.  * - An "F" record with the pathname to the current folder.
  583.  *
  584.  * - An "N" record with a count of the number of messages in the folder.
  585.  *
  586.  * - A set of "I" records indicating the seek index of the messages
  587.  *   in the folder.  The first "I" record will contain the seek index
  588.  *   of message number one, and so on.  The "I" records will be in
  589.  *   sorting order and not necessarily mbox order.  The number of "I"
  590.  *   records will match the value indicated in the "N" record.
  591.  *
  592.  * - A "C" record with a count of the total number of messages selected.
  593.  *
  594.  * - A set of "S" records indicating message number(s) which have been
  595.  *   selected.  If messages have been tagged then there will be one
  596.  *   "S" record for each selected message.  If no messages have been
  597.  *   tagged then either:  there will be a single "S" record with the
  598.  *   current message number, or there will be no "S" records if the
  599.  *   folder is empty.  The number of "S" records will match the value
  600.  *   indicated in the "C" record.
  601.  */
  602. int create_folder_state_file()
  603. {
  604.     int count, i;
  605.     FILE *fp;
  606.  
  607.     /* format an environ param with the state file and pick out file name */
  608.     sprintf(folder_state_env_param, "%s=%s%s%d",
  609.     FOLDER_STATE_ENV, default_temp, temp_state, getpid());
  610.     folder_state_fname = folder_state_env_param + strlen(FOLDER_STATE_ENV) +1;
  611.  
  612.     /* open up the folder state file for writing */
  613.     if ((fp = fopen(folder_state_fname, "w")) == NULL) {
  614.     error1(catgets(elm_msg_cat, ElmSet, ElmCannotCreateFolderState,
  615.         "Cannot create folder state file \"%s\"."), folder_state_fname);
  616.     return -1;
  617.     }
  618.  
  619.     /* write out the pathname of the folder */
  620.     fprintf(fp, "F%s\n",
  621.     (folder_type == NON_SPOOL ? cur_folder : cur_tempfolder));
  622.  
  623.     /* write out the folder size and message indices */
  624.     fprintf(fp, "N%d\n", message_count);
  625.     for (i = 0 ; i < message_count ; ++i)
  626.     fprintf(fp, "I%ld\n", headers[i]->offset);
  627.  
  628.     /* count up the number of tagged messages */
  629.     count = 0;
  630.     for (i = 0 ; i < message_count ; i++)  {
  631.     if (headers[i]->status & TAGGED)
  632.         ++count;
  633.     }
  634.  
  635.     /* write out selected messages */
  636.     if (count > 0) {
  637.     /* we found tagged messages - write them out */
  638.     fprintf(fp, "C%d\n", count);
  639.     for (i = 0 ; i < message_count ; i++) {
  640.         if (headers[i]->status & TAGGED)
  641.         fprintf(fp, "S%d\n", i+1);
  642.     }
  643.     } else if (current > 0) {
  644.     /* no tagged messages - write out the selected message */
  645.     fprintf(fp, "C1\nS%d\n", current);
  646.     } else {
  647.     /* hmmm...must be an empty mailbox */
  648.     fprintf(fp, "C0\n");
  649.     }
  650.  
  651.     /* file is done */
  652.     (void) fclose(fp);
  653.  
  654.     /* put pointer to the file in the environment */
  655.     if (putenv(folder_state_env_param) != 0) {
  656.     error1(catgets(elm_msg_cat, ElmSet, ElmCannotCreateEnvParam,
  657.         "Cannot create environment parameter \"%s\"."), FOLDER_STATE_ENV);
  658.     return -1;
  659.     }
  660.  
  661.     return 0;
  662. }
  663.  
  664.  
  665. int remove_folder_state_file()
  666. {
  667.     /*
  668.      * We simply leave the FOLDER_STATE_ENV environment variable set.
  669.      * It's too much work trying to pull it out of the environment, and
  670.      * the load_folder_state_file() does not mind if the environment
  671.      * variable points to a non-existent file.
  672.      */
  673.     return unlink(folder_state_fname);
  674. }
  675.  
  676.