www.delorie.com/djgpp/bugs/show.cgi   search  
Bug 000120

When Created: 11/04/1996 09:21:18
Against DJGPP version: 2.01
By whom: wegscd@whirlpool.com
Abstract: use of popen() in program invoked via popen() garbles output
here is a program that recursively calls itself using popen:

#include <stdio.h>

void main (int argc, char *argv[])
{
  FILE *fp;
  int i;
  char cmdline[100], buf[100];

  /* how deep to go? */
  if ((argc != 1) && (sscanf (argv[1], "%d", &i) != 1)) i = 1;

  if (i > 0) {
    /* make a child, but have him make i-1 children */
    sprintf (cmdline, "%s %d", argv[0], i-1);

    printf ("(%d) opening '%s'\n", i, cmdline);
    fp = popen (cmdline, "r");
    while (fgets (buf, sizeof(buf), fp) )  {
      /* chop the newline */
      if (buf[strlen(buf)-1] == '
') buf[strlen(buf)-1] = '\0';
      printf ("(%d) read '%s'\n", i, buf);
    }
    pclose (fp);
    printf ("(%d) closed '%s'
\n", i, cmdline);
  }
  /* we got called to make no children. say that. */
  else printf ("(0) did nothing!\n");
}

-------

on Unix, we get the expected result:

> ./popent 2
(2) opening './popent 1'
(2) read '(1) opening './popent 0''
(2) read '(1) read '(0) did nothing!''
(2) read '(1) closed './popent 0''
(2) read ''
(2) closed './popent 1'

-------

with DJGPP 2.01 (10/28 version) (running under Win 3.1 or Win95), part
of the output is missing; (sometimes extra garbage characters are
thrown in). You do not always get exactly the same result...

D:
\dperl5.003	>popent 2
(2) opening 'd:/n/dperl5.003/t/popent.exe 1'
(2) read '(0) did nothing!'
(2) read 'dperl5.003/t/popent.exe 0''
(2) read '(1) read '(0) did nothing!''
(2) read '(1) closed 'd:/n/dperl5.003/t/popent.exe 0''
(2) read ''
(2) closed 'd:/n/dperl5.003/t/popent.exe 1'


-------

D:
\dperl5.003	>dir c:\djgpp201\lib\libc.a

 Volume in drive C is C @ WORK
 Volume Serial Number is 16D3-1278
 Directory of C:\A\djgpp201\lib

LIBC     A         644,496  10-28-96 10:20p libc.a
         1 file(s)        644,496 bytes
         0 dir(s)       6,692,864 bytes free

Problem also existed in 2.00, as I just reinstalled it to check...

Note added: 11/05/1996 08:49:07
By whom: wegscd@whirlpool.com
the same program core dumps if running on DOS (no DPMI).

A test confirms popen() does not necessarily generate unique filenames across programs. A patch should follow in the next day or so.

Solution added: 11/05/1996 10:56:43
By whom: wegscd@whirlpool.com
popen() gets a scratch filename from tmpnam() 'c:/a/djgpp201/tmp/dj300000' and mangles it (depending on the mode) into 'c:/a/djgpp201/tmp/dj300000.0'. If another popen() is subsequently called before pclose(), it will again get 'c:/a/djgpp201/tmp/dj300000' from tmpnam() ('c:/a/djgpp201/tmp/dj300000.0' exists, but not 'c:/a/djgpp201/tmp/dj300000'), and the 2nd popen() will (again) mangle it into 'c:/a/djgpp201/tmp/dj300000.0', resulting in a filename collision, and subsequent weirdness.

the patch uses the unique filenames from tmpnam() as they are, and doesn't mangle them into possibly non-unique filenames.

*** popen.c0    Wed Jun 12 23:44:36 1996
--- popen.c     Tue Nov  5 10:34:14 1996
***************
*** 109,113 ****
    /* stick in elements we know already */
    strcpy (l1->mode, md);
!   sprintf (l1->temp_name, "%s.%d", tn, l1->fd);

    /* if can save the program name, build temp file */
--- 109,113 ----
    /* stick in elements we know already */
    strcpy (l1->mode, md);
!   strcpy (l1->temp_name, tn);

    /* if can save the program name, build temp file */

Solution added: 06/09/1997 07:13:31
By whom: eliz@is.elta.co.il
This solution is not enough (IMHO), since every call to `popen' should
generate a new file.  Otherwise, you cannot call `popen' a second time
before `pclose' to the first time.

I suggest this patch (relative to the stock v2.01 libc sources):
*** src/libc/posix/stdio/popen.c~0      Thu Jun 13 03:44:36 1996
--- src/libc/posix/stdio/popen.c        Mon Jun  9 09:45:04 1997
***************
*** 1,3 ****
--- 1,4 ----
+ /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
  /*
***************
*** 63,69 ****
  struct pipe_list {
    FILE *fp;
    int fd;
!   char *command, mode[10], temp_name[FILENAME_MAX];
    struct pipe_list *next;
  };

--- 64,70 ----
  struct pipe_list {
    FILE *fp;
    int fd;
!   char *command, mode[10], temp_name[L_tmpnam];
    struct pipe_list *next;
  };

*************** FILE *
*** 74,84 ****
  popen (const char *cm, const char *md) /* program name, pipe mode */
  {
    struct pipe_list *l1, *l2;
-   static char *tn = NULL;       /* temporary file basename */
-
-   if (!tn)
-     if ((tn = tmpnam(0)) == NULL)
-       return NULL;

    /* make new node */
    if ((l1 = (struct pipe_list *) malloc (sizeof (struct pipe_list))) == NULL)
--- 75,80 ----
*************** popen (const char *cm, const char *md) /
*** 108,114 ****

    /* stick in elements we know already */
    strcpy (l1->mode, md);
!   sprintf (l1->temp_name, "%s.%d", tn, l1->fd);

    /* if can save the program name, build temp file */
    if ((l1->command = malloc(strlen(cm)+1)))
--- 104,111 ----

    /* stick in elements we know already */
    strcpy (l1->mode, md);
!   if (tmpnam (l1->temp_name) == NULL)
!     return NULL;

    /* if can save the program name, build temp file */
    if ((l1->command = malloc(strlen(cm)+1)))

Fixed in version on 04/13/1999 07:00:05
By whom: eliz@is.elta.co.il



  webmaster   donations   bookstore     delorie software   privacy  
  Copyright 2010   by DJ Delorie     Updated Jul 2010