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