Loading ...
Sorry, an error occurred while loading the content.

Patch: prevent certain cscope errors from locking up vim

Expand Messages
  • Gary Johnson
    When :cscope add is executed and certain files expected by cscope are missing, cscope issues a message like this /opt/fw/dev_tool_bin/cscope/cscope.hpux:
    Message 1 of 7 , Jan 29, 2004
    • 0 Attachment
      When ":cscope add" is executed and certain files expected by
      'cscope' are missing, 'cscope' issues a message like this

      /opt/fw/dev_tool_bin/cscope/cscope.hpux: cannot open file /tmp/garyjohn_c2.in
      Press the RETURN key to continue:

      and waits for the user to press Return before it will issue the
      ">> " prompt. The ":cscope add" command, however, waits for the
      ">> " prompt before it completes, leaving 'vim' and 'cscope'
      deadlocked. The most graceful way out of this has been to kill the
      'cscope' process. 'Vim' doesn't display that message to the user
      until after 'cscope' has been killed, so it's not obvious to the
      user why 'vim' has hung.

      The attached patch fixes this problem by allowing the ":cscope add"
      command to recognize the string "Press the RETURN key to continue:"
      and to send a newline to 'cscope' in response, thereby allowing
      'cscope' to continue and issue the expected ">> " prompt.

      The 'cscope' error message is displayed to make the user aware of
      the error, but the "Press the RETURN key to continue:" string is not
      displayed since the user doesn't need to do that. The ":cscope add"
      command completes successfully, allowing the user to use the
      'cscope' connection anyway if the error is not serious.

      (In the example above, the inverted index files created by the
      'cscope' -q option had been deleted. 'Cscope' could still search
      the database, just not as quickly as it could with those files.)

      I am using 'cscope' version 15.4. This patch assumes that the
      "Press the RETURN key to continue:" prompt is the same in all
      versions. If that string ever changes, this patch will not prevent
      the deadlock problem and vim's behavior and performance will be the
      same as without the patch.

      I have already written to Sergey Khorev about this one, too, and he
      suggested that I post the patch here.

      The changes were made to version:

      VIM - Vi IMproved 6.2 (2003 Jun 1, compiled Jan 27 2004 02:37:58)
      Included patches: 1-14, 76, 101-103, 106

      Gary

      --
      Gary Johnson | Agilent Technologies
      garyjohn@... | Wireless Division
      | Spokane, Washington, USA
    • Bram Moolenaar
      ... I m glad you could fix this annoying problem. Looking at the code I spot a few other problems. Using perror() is bad, because it can mess up the display.
      Message 2 of 7 , Jan 30, 2004
      • 0 Attachment
        Gary Johnson wrote:

        > When ":cscope add" is executed and certain files expected by
        > 'cscope' are missing, 'cscope' issues a message like this
        >
        > /opt/fw/dev_tool_bin/cscope/cscope.hpux: cannot open file /tmp/garyjohn_c2.in
        > Press the RETURN key to continue:
        >
        > and waits for the user to press Return before it will issue the
        > ">> " prompt. The ":cscope add" command, however, waits for the
        > ">> " prompt before it completes, leaving 'vim' and 'cscope'
        > deadlocked. The most graceful way out of this has been to kill the
        > 'cscope' process. 'Vim' doesn't display that message to the user
        > until after 'cscope' has been killed, so it's not obvious to the
        > user why 'vim' has hung.
        >
        > The attached patch fixes this problem by allowing the ":cscope add"
        > command to recognize the string "Press the RETURN key to continue:"
        > and to send a newline to 'cscope' in response, thereby allowing
        > 'cscope' to continue and issue the expected ">> " prompt.
        >
        > The 'cscope' error message is displayed to make the user aware of
        > the error, but the "Press the RETURN key to continue:" string is not
        > displayed since the user doesn't need to do that. The ":cscope add"
        > command completes successfully, allowing the user to use the
        > 'cscope' connection anyway if the error is not serious.
        >
        > (In the example above, the inverted index files created by the
        > 'cscope' -q option had been deleted. 'Cscope' could still search
        > the database, just not as quickly as it could with those files.)
        >
        > I am using 'cscope' version 15.4. This patch assumes that the
        > "Press the RETURN key to continue:" prompt is the same in all
        > versions. If that string ever changes, this patch will not prevent
        > the deadlock problem and vim's behavior and performance will be the
        > same as without the patch.
        >
        > I have already written to Sergey Khorev about this one, too, and he
        > suggested that I post the patch here.

        I'm glad you could fix this annoying problem.

        Looking at the code I spot a few other problems. Using perror() is bad,
        because it can mess up the display. The "maxlen" is computed before
        translating the message, if translation makes it longer this may cause a
        problem.

        I also made the check for EOF halfway the prompt work better.

        Please try out the alternative patch below. Especially that it still
        avoids the hang!


        *** ../vim-6.2.214/src/if_cscope.c Wed Sep 10 21:35:55 2003
        --- src/if_cscope.c Fri Jan 30 11:54:22 2004
        ***************
        *** 1974,2027 ****
        int ch;
        char *buf = NULL; /* buffer for possible error message from cscope */
        int bufpos = 0;
        ! static char *cs_emsg = N_("E609: Cscope error: %s");
        ! /* maximum allowed len for Cscope error message */
        ! int maxlen = IOSIZE - strlen(_(cs_emsg));

        for (;;)
        {
        while ((ch = getc(csinfo[i].fr_fp)) != EOF && ch != CSCOPE_PROMPT[0])
        ! /* if verbose, have space and char is printable */
        ! if (p_csverbose && bufpos < maxlen - 1 && vim_isprintc(ch))
        {
        if (buf == NULL) /* lazy buffer allocation */
        buf = (char *)alloc(maxlen);
        !
        ! if (buf != NULL) /* append character to a string */
        {
        buf[bufpos++] = ch;
        buf[bufpos] = NUL;
        }
        }

        ! if (ch == EOF)
        {
        ! perror("cs_read_prompt EOF(1)");
        ! if (buf != NULL && buf[0] != NUL)
        ! (void)EMSG2(_(cs_emsg), buf);
        ! else if (p_csverbose)
        ! cs_reading_emsg(i); /* don't have additional information */
        ! cs_release_csp(i, TRUE);
        ! vim_free(buf);
        ! return CSCOPE_FAILURE;
        ! }

        ! ch = getc(csinfo[i].fr_fp);
        ! if (ch == EOF)
        ! perror("cs_read_prompt EOF(2)");
        ! if (ch != CSCOPE_PROMPT[1])
        ! continue;

        - ch = getc(csinfo[i].fr_fp);
        if (ch == EOF)
        ! perror("cs_read_prompt EOF(3)");
        ! if (ch != CSCOPE_PROMPT[2])
        ! continue;
        ! break;
        }
        vim_free(buf);
        return CSCOPE_SUCCESS;
        ! } /* cs_read_prompt */


        /*
        --- 1974,2053 ----
        int ch;
        char *buf = NULL; /* buffer for possible error message from cscope */
        int bufpos = 0;
        ! char *cs_emsg;
        ! int maxlen;
        ! static char *eprompt = "Press the RETURN key to continue:";
        ! int epromptlen = strlen(eprompt);
        ! int n;
        !
        ! cs_emsg = _("E609: Cscope error: %s");
        ! /* compute maximum allowed len for Cscope error message */
        ! maxlen = (int)(IOSIZE - strlen(cs_emsg));

        for (;;)
        {
        while ((ch = getc(csinfo[i].fr_fp)) != EOF && ch != CSCOPE_PROMPT[0])
        ! /* if there is room and char is printable */
        ! if (bufpos < maxlen - 1 && vim_isprintc(ch))
        {
        if (buf == NULL) /* lazy buffer allocation */
        buf = (char *)alloc(maxlen);
        ! if (buf != NULL)
        {
        + /* append character to the message */
        buf[bufpos++] = ch;
        buf[bufpos] = NUL;
        + if (bufpos >= epromptlen
        + && strcmp(&buf[bufpos - epromptlen], eprompt) == 0)
        + {
        + /* remove eprompt from buf */
        + buf[bufpos - epromptlen] = NUL;
        +
        + /* print message to user */
        + (void)EMSG2(cs_emsg, buf);
        +
        + /* send RETURN to cscope */
        + (void)putc('\n', csinfo[i].to_fp);
        + (void)fflush(csinfo[i].to_fp);
        +
        + /* clear buf */
        + bufpos = 0;
        + buf[bufpos] = NUL;
        + }
        }
        }

        ! for (n = 0; n < (int)strlen(CSCOPE_PROMPT); ++n)
        {
        ! if (n > 0)
        ! ch = getc(csinfo[i].fr_fp);
        ! if (ch == EOF)
        ! {
        ! /* perror("cs_read_prompt EOF(1)"); */
        ! if (buf != NULL && buf[0] != NUL)
        ! (void)EMSG2(cs_emsg, buf);
        ! else if (p_csverbose)
        ! cs_reading_emsg(i); /* don't have additional information */
        ! cs_release_csp(i, TRUE);
        ! vim_free(buf);
        ! return CSCOPE_FAILURE;
        ! }

        ! if (ch != CSCOPE_PROMPT[n])
        ! {
        ! ch = EOF;
        ! break;
        ! }
        ! }

        if (ch == EOF)
        ! continue; /* didn't find the prompt */
        ! break; /* did find the prompt */
        }
        +
        vim_free(buf);
        return CSCOPE_SUCCESS;
        ! }


        /*


        --
        Engineers will go without food and hygiene for days to solve a problem.
        (Other times just because they forgot.)
        (Scott Adams - The Dilbert principle)

        /// Bram Moolenaar -- Bram@... -- http://www.Moolenaar.net \\\
        /// Sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
        \\\ Project leader for A-A-P -- http://www.A-A-P.org ///
        \\\ Help AIDS victims, buy here: http://ICCF-Holland.org/click1.html ///
      • Gary Johnson
        ... Yes, it did mess up the display, but the information it provided was quite useful in diagnosing the problem. Do you think it would be OK to use strerror()
        Message 3 of 7 , Jan 30, 2004
        • 0 Attachment
          On 2004-01-30, Bram Moolenaar <Bram@...> wrote:

          > I'm glad you could fix this annoying problem.
          >
          > Looking at the code I spot a few other problems. Using perror() is bad,
          > because it can mess up the display.

          Yes, it did mess up the display, but the information it provided was
          quite useful in diagnosing the problem. Do you think it would be OK
          to use strerror() instead? Perhaps like this (untested)?

          if (ch == EOF)
          {
          /* perror("cs_read_prompt EOF(1)"); */
          (void)EMSG2("cs_read_prompt EOF", strerror(errno));
          if (buf != NULL && buf[0] != NUL)
          (void)EMSG2(cs_emsg, buf);

          > The "maxlen" is computed before
          > translating the message, if translation makes it longer this may cause a
          > problem.

          It looked to me like maxlen was already being computed on the
          translated message:

          int maxlen = IOSIZE - strlen(_(cs_emsg));

          but I don't really understand how the gettext _() macros work.

          > I also made the check for EOF halfway the prompt work better.

          I like that better, too. Thanks.

          > Please try out the alternative patch below. Especially that it still
          > avoids the hang!

          I haven't been able to test this yet, but I'll get back to you as
          soon as I do.

          Gary

          --
          Gary Johnson | Agilent Technologies
          garyjohn@... | Wireless Division
          | Spokane, Washington, USA
        • Bram Moolenaar
          ... OK. But strerror() isn t always available, thus this requires an #ifdef. And the %s is missing in the message. If we do this, we should do the same for
          Message 4 of 7 , Jan 30, 2004
          • 0 Attachment
            Gary Johnson wrote:

            > On 2004-01-30, Bram Moolenaar <Bram@...> wrote:
            >
            > > I'm glad you could fix this annoying problem.
            > >
            > > Looking at the code I spot a few other problems. Using perror() is bad,
            > > because it can mess up the display.
            >
            > Yes, it did mess up the display, but the information it provided was
            > quite useful in diagnosing the problem. Do you think it would be OK
            > to use strerror() instead? Perhaps like this (untested)?
            >
            > if (ch == EOF)
            > {
            > /* perror("cs_read_prompt EOF(1)"); */
            > (void)EMSG2("cs_read_prompt EOF", strerror(errno));
            > if (buf != NULL && buf[0] != NUL)
            > (void)EMSG2(cs_emsg, buf);

            OK. But strerror() isn't always available, thus this requires an
            #ifdef. And the "%s" is missing in the message.

            If we do this, we should do the same for he other perror() calls.

            Here is a new patch that includes the previous one:

            *** ../vim-6.2.215/src/if_cscope.c Wed Sep 10 21:35:55 2003
            --- src/if_cscope.c Fri Jan 30 21:39:18 2004
            ***************
            *** 754,764 ****
            --- 754,776 ----
            err_save = dup(STDERR_FILENO);
            #endif
            if (dup2(to_cs[0], STDIN_FILENO) == -1)
            + #ifdef HAVE_STRERROR
            + (void)EMSG2("cs_create_connection 1: %s", strerror(errno));
            + #else
            perror("cs_create_connection 1");
            + #endif
            if (dup2(from_cs[1], STDOUT_FILENO) == -1)
            + #ifdef HAVE_STRERROR
            + (void)EMSG2("cs_create_connection 2: %s", strerror(errno));
            + #else
            perror("cs_create_connection 2");
            + #endif
            if (dup2(from_cs[1], STDERR_FILENO) == -1)
            + #ifdef HAVE_STRERROR
            + (void)EMSG2("cs_create_connection 3: %s", strerror(errno));
            + #else
            perror("cs_create_connection 3");
            + #endif

            /* close unused */
            #if defined(UNIX)
            ***************
            *** 839,845 ****
            --- 851,862 ----

            #if defined(UNIX)
            if (execl("/bin/sh", "sh", "-c", cmd, NULL) == -1)
            + # ifdef HAVE_STRERROR
            + (void)EMSG2(_("cs_create_connection exec failed: %s"),
            + strerror(errno));
            + # else
            perror(_("cs_create_connection exec failed"));
            + # endif

            exit(127);
            /* NOTREACHED */
            ***************
            *** 889,896 ****
            --- 906,918 ----
            # endif
            if (ph == -1)
            {
            + # ifdef HAVE_STRERROR
            + (void)EMSG2(_("E623: Could not spawn cscope process: %s"),
            + strerror(errno));
            + # else
            perror(_("cs_create_connection exec failed"));
            (void)EMSG(_("E623: Could not spawn cscope process"));
            + # endif
            goto err_closing;
            }
            /* else */
            ***************
            *** 903,911 ****
            --- 925,943 ----
            * reopen as streams.
            */
            if ((csinfo[i].to_fp = fdopen(to_cs[1], "w")) == NULL)
            + #ifdef HAVE_STRERROR
            + (void)EMSG2(_("cs_create_connection: fdopen for to_fp failed: %s"),
            + strerror(errno));
            + #else
            perror(_("cs_create_connection: fdopen for to_fp failed"));
            + #endif
            if ((csinfo[i].fr_fp = fdopen(from_cs[0], "r")) == NULL)
            + #ifdef HAVE_STRERROR
            + (void)EMSG2(_("cs_create_connection: fdopen for fr_fp failed: %s"),
            + strerror(errno));
            + #else
            perror(_("cs_create_connection: fdopen for fr_fp failed"));
            + #endif

            #if defined(UNIX)
            /* close unused */
            ***************
            *** 1974,2027 ****
            int ch;
            char *buf = NULL; /* buffer for possible error message from cscope */
            int bufpos = 0;
            ! static char *cs_emsg = N_("E609: Cscope error: %s");
            ! /* maximum allowed len for Cscope error message */
            ! int maxlen = IOSIZE - strlen(_(cs_emsg));

            for (;;)
            {
            while ((ch = getc(csinfo[i].fr_fp)) != EOF && ch != CSCOPE_PROMPT[0])
            ! /* if verbose, have space and char is printable */
            ! if (p_csverbose && bufpos < maxlen - 1 && vim_isprintc(ch))
            {
            if (buf == NULL) /* lazy buffer allocation */
            buf = (char *)alloc(maxlen);
            !
            ! if (buf != NULL) /* append character to a string */
            {
            buf[bufpos++] = ch;
            buf[bufpos] = NUL;
            }
            }

            ! if (ch == EOF)
            {
            ! perror("cs_read_prompt EOF(1)");
            ! if (buf != NULL && buf[0] != NUL)
            ! (void)EMSG2(_(cs_emsg), buf);
            ! else if (p_csverbose)
            ! cs_reading_emsg(i); /* don't have additional information */
            ! cs_release_csp(i, TRUE);
            ! vim_free(buf);
            ! return CSCOPE_FAILURE;
            ! }

            ! ch = getc(csinfo[i].fr_fp);
            ! if (ch == EOF)
            ! perror("cs_read_prompt EOF(2)");
            ! if (ch != CSCOPE_PROMPT[1])
            ! continue;

            - ch = getc(csinfo[i].fr_fp);
            if (ch == EOF)
            ! perror("cs_read_prompt EOF(3)");
            ! if (ch != CSCOPE_PROMPT[2])
            ! continue;
            ! break;
            }
            vim_free(buf);
            return CSCOPE_SUCCESS;
            ! } /* cs_read_prompt */


            /*
            --- 2006,2089 ----
            int ch;
            char *buf = NULL; /* buffer for possible error message from cscope */
            int bufpos = 0;
            ! char *cs_emsg;
            ! int maxlen;
            ! static char *eprompt = "Press the RETURN key to continue:";
            ! int epromptlen = strlen(eprompt);
            ! int n;
            !
            ! cs_emsg = _("E609: Cscope error: %s");
            ! /* compute maximum allowed len for Cscope error message */
            ! maxlen = (int)(IOSIZE - strlen(cs_emsg));

            for (;;)
            {
            while ((ch = getc(csinfo[i].fr_fp)) != EOF && ch != CSCOPE_PROMPT[0])
            ! /* if there is room and char is printable */
            ! if (bufpos < maxlen - 1 && vim_isprintc(ch))
            {
            if (buf == NULL) /* lazy buffer allocation */
            buf = (char *)alloc(maxlen);
            ! if (buf != NULL)
            {
            + /* append character to the message */
            buf[bufpos++] = ch;
            buf[bufpos] = NUL;
            + if (bufpos >= epromptlen
            + && strcmp(&buf[bufpos - epromptlen], eprompt) == 0)
            + {
            + /* remove eprompt from buf */
            + buf[bufpos - epromptlen] = NUL;
            +
            + /* print message to user */
            + (void)EMSG2(cs_emsg, buf);
            +
            + /* send RETURN to cscope */
            + (void)putc('\n', csinfo[i].to_fp);
            + (void)fflush(csinfo[i].to_fp);
            +
            + /* clear buf */
            + bufpos = 0;
            + buf[bufpos] = NUL;
            + }
            }
            }

            ! for (n = 0; n < (int)strlen(CSCOPE_PROMPT); ++n)
            {
            ! if (n > 0)
            ! ch = getc(csinfo[i].fr_fp);
            ! if (ch == EOF)
            ! {
            ! #ifdef HAVE_STRERROR
            ! (void)EMSG2("cs_read_prompt EOF: %s", strerror(errno));
            ! #else
            ! perror("cs_read_prompt EOF");
            ! #endif
            ! if (buf != NULL && buf[0] != NUL)
            ! (void)EMSG2(cs_emsg, buf);
            ! else if (p_csverbose)
            ! cs_reading_emsg(i); /* don't have additional information */
            ! cs_release_csp(i, TRUE);
            ! vim_free(buf);
            ! return CSCOPE_FAILURE;
            ! }

            ! if (ch != CSCOPE_PROMPT[n])
            ! {
            ! ch = EOF;
            ! break;
            ! }
            ! }

            if (ch == EOF)
            ! continue; /* didn't find the prompt */
            ! break; /* did find the prompt */
            }
            +
            vim_free(buf);
            return CSCOPE_SUCCESS;
            ! }


            /*


            --
            A consultant is a person who takes your money and annoys your employees while
            tirelessly searching for the best way to extend the consulting contract.
            (Scott Adams - The Dilbert principle)

            /// Bram Moolenaar -- Bram@... -- http://www.Moolenaar.net \\\
            /// Sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
            \\\ Project leader for A-A-P -- http://www.A-A-P.org ///
            \\\ Help AIDS victims, buy here: http://ICCF-Holland.org/click1.html ///
          • Gary Johnson
            ... {patch deleted] I tested this one, including removing the cscope.out.in file, and replacing cscope with a shell script that sent all or part of the
            Message 5 of 7 , Jan 31, 2004
            • 0 Attachment
              On 2004-01-30, Bram Moolenaar <Bram@...> wrote:
              > Gary Johnson wrote:
              >
              > > On 2004-01-30, Bram Moolenaar <Bram@...> wrote:
              > >
              > > > I'm glad you could fix this annoying problem.
              > > >
              > > > Looking at the code I spot a few other problems. Using perror() is bad,
              > > > because it can mess up the display.
              > >
              > > Yes, it did mess up the display, but the information it provided was
              > > quite useful in diagnosing the problem. Do you think it would be OK
              > > to use strerror() instead? Perhaps like this (untested)?
              > >
              > > if (ch == EOF)
              > > {
              > > /* perror("cs_read_prompt EOF(1)"); */
              > > (void)EMSG2("cs_read_prompt EOF", strerror(errno));
              > > if (buf != NULL && buf[0] != NUL)
              > > (void)EMSG2(cs_emsg, buf);
              >
              > OK. But strerror() isn't always available, thus this requires an
              > #ifdef. And the "%s" is missing in the message.
              >
              > If we do this, we should do the same for he other perror() calls.
              >
              > Here is a new patch that includes the previous one:

              {patch deleted]

              I tested this one, including removing the cscope.out.in file, and
              replacing cscope with a shell script that sent all or part of the
              ">> " before terminating, and everything seemed to work as it
              should. Thank you for all the work you did on this.

              Regards,
              Gary

              --
              Gary Johnson | Agilent Technologies
              garyjohn@... | Wireless Division
              | Spokane, Washington, USA
            • Gary Johnson
              ... As I was looking at this some more I realized that while this works, having all these #ifdef s really muddies the code. I ve also noticed that perror() is
              Message 6 of 7 , Jan 31, 2004
              • 0 Attachment
                On 2004-01-30, Bram Moolenaar <Bram@...> wrote:
                > Gary Johnson wrote:
                >
                > > On 2004-01-30, Bram Moolenaar <Bram@...> wrote:

                > > > Looking at the code I spot a few other problems. Using perror() is bad,
                > > > because it can mess up the display.
                > >
                > > Yes, it did mess up the display, but the information it provided was
                > > quite useful in diagnosing the problem. Do you think it would be OK
                > > to use strerror() instead? Perhaps like this (untested)?
                > >
                > > if (ch == EOF)
                > > {
                > > /* perror("cs_read_prompt EOF(1)"); */
                > > (void)EMSG2("cs_read_prompt EOF", strerror(errno));
                > > if (buf != NULL && buf[0] != NUL)
                > > (void)EMSG2(cs_emsg, buf);
                >
                > OK. But strerror() isn't always available, thus this requires an
                > #ifdef. And the "%s" is missing in the message.
                >
                > If we do this, we should do the same for he other perror() calls.
                >
                > Here is a new patch that includes the previous one:
                >
                > *** ../vim-6.2.215/src/if_cscope.c Wed Sep 10 21:35:55 2003
                > --- src/if_cscope.c Fri Jan 30 21:39:18 2004
                > ***************

                > + #ifdef HAVE_STRERROR
                > + (void)EMSG2("cs_create_connection 1: %s", strerror(errno));
                > + #else
                > perror("cs_create_connection 1");
                > + #endif

                As I was looking at this some more I realized that while this works,
                having all these #ifdef's really muddies the code. I've also
                noticed that perror() is used in other source files as well. So I
                would like to propose that a PERROR() macro be added to vim.h,
                perhaps like this:

                #ifdef HAVE_STRERROR
                # define PERROR(msg) (void)EMSG2("%s: %s", msg, strerror(errno))
                #else
                # define PERROR(msg) perror(msg)
                #endif

                and that all the calls to perror() be changed to use PERROR()
                instead, at least where it is appropriate to use EMSG2().

                Gary

                --
                Gary Johnson | Agilent Technologies
                garyjohn@... | Wireless Division
                | Spokane, Washington, USA
              • Bram Moolenaar
                ... That looks like a good idea. EMSG2() only handles two arguments though. I ll add emsg3() for this. -- login: yes password: I don t know, please tell me
                Message 7 of 7 , Feb 1, 2004
                • 0 Attachment
                  Gary Johnson wrote:

                  > As I was looking at this some more I realized that while this works,
                  > having all these #ifdef's really muddies the code. I've also
                  > noticed that perror() is used in other source files as well. So I
                  > would like to propose that a PERROR() macro be added to vim.h,
                  > perhaps like this:
                  >
                  > #ifdef HAVE_STRERROR
                  > # define PERROR(msg) (void)EMSG2("%s: %s", msg, strerror(errno))
                  > #else
                  > # define PERROR(msg) perror(msg)
                  > #endif
                  >
                  > and that all the calls to perror() be changed to use PERROR()
                  > instead, at least where it is appropriate to use EMSG2().

                  That looks like a good idea. EMSG2() only handles two arguments though.
                  I'll add emsg3() for this.

                  --
                  login: yes
                  password: I don't know, please tell me
                  password is incorrect
                  login: yes
                  password: incorrect

                  /// Bram Moolenaar -- Bram@... -- http://www.Moolenaar.net \\\
                  /// Sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
                  \\\ Project leader for A-A-P -- http://www.A-A-P.org ///
                  \\\ Help AIDS victims, buy here: http://ICCF-Holland.org/click1.html ///
                Your message has been successfully submitted and would be delivered to recipients shortly.