summaryrefslogtreecommitdiff
path: root/Build/extra/djgpp/popen.c
blob: 67250d6ed7c8c0e2c8baa1364cb7b93d0bab5528 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/* 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 */
/*
   This is popen() and pclose() for MSDOS.  They were developed using
   Microsoft C, but should port easily to DOS C any compiler.
   
   Original author: pacetti@fl-ngnet.army.mil

   These routines are hacks, that is they SIMULATE thier UNIX
   counterparts.  Since MSDOS and won't allow concurrent process spawning,
   you can't really pipe.  I came up with this nearly stupid way around
   piping because I wanted to have some portability between UNIX and MSDOS.
   I'm probably not the first person to have this idea or implement it, but
   I think I'm the first to give it away for free (no points?!).

   The functions simulate popen() and pclose() by redirecting stdin or
   stdout, then spawning a child processes via system().

   If you popen() for read, the stdout is redirected to a temporary
   file, and the child is spawned.  The stdout is reopened via dup2(), the
   temporary file is opened for read and a file pointer to it is returned.

   If you popen() for write, a temporary file is opened for write, and
   a file pointer to it is returned.  When you pclose(), the stdin is
   redirected to the temporary file and the child is spawned.

   In both cases, pclose() closes and unlinks the temporary file.

   A static linked list of C structures is built to store necessary
   info for all popen()ed files so you can popen() more than one file at
   a time.

   The popen() function will return NULL on an error, or a valid FILE
   *pointer on a successful open.  The pclose() function will return
   negative one (-1) on an error or zero (0) on a successful close.

   The function prototypes are:

   FILE *popen(command, mode)
        char *command, char *mode;

   int pclose(pp)
       FILE *pp;

   Where command is a character string equivilant to a MSDOS command
   line, mode is "r" for read or "w" for write, and pp is a pointer to a
   file opened through popen().

   A main() function has been included for testing purposes, to compile
   it define the preprocessor token TEST at compile time.
 */

#include <libc/stubs.h>
#include <io.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libc/file.h>

/* hold file pointer, descriptor, command, mode, temporary file name,
   and the status of the command  */
struct pipe_list {
  FILE *fp;
  int fd;
  int exit_status;
  char *command, mode[10], temp_name[L_tmpnam];
  struct pipe_list *next;
};

/* static, global list pointer */
static struct pipe_list *pl = NULL;

FILE *
popen (const char *cm, const char *md) /* program name, pipe mode */
{
  struct pipe_list *l1, *l2;

  /* make new node */
  if ((l1 = (struct pipe_list *) malloc (sizeof (struct pipe_list))) == NULL)
    return NULL;

  /* zero out elements to we'll get here */
  l1->fd = 0;
  l1->fp = NULL;
  l1->next = NULL;

  /* if empty list - just grab new node */
  if (!pl)
    pl = l1;
  else
  {
    /* otherwise, find last node in list */
    ++(l1->fd);
    l2 = pl;
    while (l2->next)
    {
      ++(l1->fd);
      l2 = l2->next;
    };
    /* add new node to list */
    l2->next = l1;
  }

  /* stick in elements we know already */
  l1->exit_status = -1;
  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)))
  {
    strcpy(l1->command, cm);
    /* if caller wants to read */
    if (l1->mode[0] == 'r')
    {
      /* dup stdout */
      if ((l1->fd = dup (fileno (stdout))) == EOF)
	l1->fp = NULL;
      else if (!(l1->fp = freopen (l1->temp_name, "wb", stdout)))
	l1->fp = NULL;
      else
	/* exec cmd */
	if ((l1->exit_status = system (cm)) == EOF)
	  l1->fp = NULL;
      /* reopen real stdout */
      if (dup2 (l1->fd, fileno (stdout)) == EOF)
	l1->fp = NULL;
      /* if cmd couldn't be run, make sure we return NULL */
      else if (l1->exit_status != EOF)
	/* open file for reader */
	l1->fp = fopen (l1->temp_name, l1->mode);
      close(l1->fd);
    }
    else
      /* if caller wants to write */
      if (l1->mode[0] == 'w')
        /* open temp file */
        l1->fp = fopen (l1->temp_name, l1->mode);
      else
        /* unknown mode */
        l1->fp = NULL;
  }
  return l1->fp;              /* return == NULL ? ERROR : OK */
}

int
pclose (FILE *pp)
{
  struct pipe_list *l1, *l2;    /* list pointers */
  int retval=0;			/* function return value */

  /* if pointer is first node */
  if (pl->fp == pp)
  {
    /* save node and take it out the list */
    l1 = pl;
    pl = l1->next;
  }
  else
    /* if more than one node in list */
    if (pl->next)
    {
      /* find right node */
      for (l2 = pl, l1 = pl->next; l1; l2 = l1, l1 = l2->next)
        if (l1->fp == pp)
          break;

      /* take node out of list */
      l2->next = l1->next;
    }
    else
      return -1;

  /* if FILE not in list - return error */
  if (l1->fp == pp)
  {
    /* close the (hopefully) popen()ed file */
    fclose (l1->fp);

    /* if pipe was opened to write */
    if (l1->mode[0] == 'w')
    {
      /* dup stdin */
      if ((l1->fd = dup (fileno (stdin))) == EOF)
	retval = -1;
      else
	/* open temp stdin */
	if (!(l1->fp = freopen (l1->temp_name, "rb", stdin)))
	  retval = -1;
	else
	  /* exec cmd */
          if ((retval = system (l1->command)) != EOF)
	  {
            /* reopen stdin */
	    if (dup2 (l1->fd, fileno (stdin)) == EOF)
	      retval = -1;
	  }
      close(l1->fd);
    }
    else
      /* if pipe was opened to read, return the exit status we saved */
      if (l1->mode[0] == 'r')
        retval = l1->exit_status;
      else
        /* invalid mode */
        retval = -1;
  }
  remove (l1->temp_name);       /* remove temporary file */
  free (l1->command);           /* dealloc memory */
  free (l1);                    /* dealloc memory */
  l1 = NULL;                    /* make pointer bogus */

  return retval;              /* retval==0 ? OK : ERROR */
}