1 /********************************************************************
2  * $Author: jgoerzen $
3  * $Revision: 1.2 $
4  * $Date: 2000/12/20 01:19:20 $
5  * $Source: /home/jgoerzen/tmp/gopher-umn/gopher/head/object/fileio.c,v $
6  * $State: Exp $
7  *
8  * Paul Lindner, University of Minnesota CIS.
9  *
10  * Copyright 1991, 92, 93, 94 by the Regents of the University of Minnesota
11  * see the file "Copyright" in the distribution for conditions of use.
12  *********************************************************************
13  * MODULE: fileio.c
14  * Socket/file input output routines.
15  *********************************************************************
16  * Revision History:
17  * $Log: fileio.c,v $
18  * Revision 1.2  2000/12/20 01:19:20  jgoerzen
19  * Added patches from David Allen <s2mdalle@titan.vcu.edu>
20  *
21  * Revision 1.1.1.1  2000/08/19 00:28:56  jgoerzen
22  * Import from UMN Gopher 2.3.1 after GPLization
23  *
24  * Revision 3.41  1996/01/04  18:25:12  lindner
25  * Updates for autoconf
26  *
27  * Revision 3.40  1995/11/03  18:01:28  lindner
28  * Fix for waitpid stuff
29  *
30  * Revision 3.39  1995/09/25  22:07:23  lindner
31  * Ansification
32  *
33  * Revision 3.38  1995/06/12  15:32:41  lindner
34  * Close correct file descriptor
35  *
36  * Revision 3.37  1995/06/08  05:17:54  lindner
37  * From Fote: Deal with UCX's funny EPIPE return value on EOF
38  * immediately after each socket_read().
39  *
40  * Ensure that file is closed on fstat() error in FIOopenUFS()
41  *
42  * Revision 3.36  1995/04/15  06:53:39  lindner
43  * Different way of returning errors
44  *
45  * Revision 3.35  1995/03/09  05:28:19  lindner
46  * Insure files get closed in FIO routines
47  *
48  * Revision 3.34  1995/02/27  17:45:52  lindner
49  * Fix for sco
50  *
51  * Revision 3.33  1995/01/02  21:09:46  lindner
52  * Fix for UCX menu ordering
53  *
54  * Revision 3.32  1994/12/20  17:09:46  lindner
55  * Fix for binary file retrievals
56  *
57  * Revision 3.31  1994/12/03  02:14:13  lindner
58  * Correct fix for waitpid() code
59  *
60  * Revision 3.30  1994/12/02  00:37:05  lindner
61  * Fix for mail and misc process execution
62  *
63  * Revision 3.29  1994/12/01  00:30:13  lindner
64  * Remove EINTR wait for FIOwaitpid.. sigh..
65  *
66  * Revision 3.28  1994/11/29  06:30:04  lindner
67  * Make waitpid keep going after an interrupted system call
68  *
69  * Revision 3.27  1994/11/29  04:59:38  lindner
70  * Fix for broken shell scripts with Shell Index type
71  *
72  * Revision 3.26  1994/10/21  02:42:47  lindner
73  * fix for 0 length files
74  *
75  * Revision 3.25  1994/10/19  03:33:57  lindner
76  * Fix nasty bug, combine more code...
77  *
78  * Revision 3.24  1994/10/13  05:26:16  lindner
79  * Compiler complaint fixes
80  *
81  * Revision 3.23  1994/09/29  19:54:57  lindner
82  * Routines to use memory mapped I/O
83  *
84  * Revision 3.22  1994/07/21  22:19:37  lindner
85  * Add filename new and change debug msg
86  *
87  * Revision 3.21  1994/06/29  06:43:04  lindner
88  * Remove VMS message about reversed values, initialize filename
89  *
90  * Revision 3.20  1994/05/25  20:57:20  lindner
91  * Fix for piped play commands
92  *
93  * Revision 3.19  1994/04/25  03:44:42  lindner
94  * Don't block empty FIOsystem() command on VMS.  That's what's used for
95  * the '!' or '$' commands, and they've already be checked for permission
96  * in Gopher.c.
97  *
98  * Reversed DCLsystem() return values in CURCurses.c, and eliminated the
99  * reversing kludge in FIOsystem().
100  *
101  * Fixed break of open() for Alpha.  (all From F.Macrides)
102  *
103  * Revision 3.18  1994/04/25  03:37:00  lindner
104  * Modifications for Debug() and mismatched NULL arguments, added Debugmsg
105  *
106  * Revision 3.17  1994/04/25  02:18:46  lindner
107  * Fix for gcc-Wall
108  *
109  * Revision 3.16  1994/04/14  18:14:49  lindner
110  * Fix for closing null files
111  *
112  * Revision 3.15  1994/04/14  15:44:55  lindner
113  * Remove duplicate variable
114  *
115  * Revision 3.14  1994/04/08  21:07:34  lindner
116  * Put back a variable
117  *
118  * Revision 3.13  1994/04/08  20:05:57  lindner
119  * gcc -Wall fixes
120  *
121  * Revision 3.12  1994/04/08  19:15:47  lindner
122  * Fix for old union wait stuff
123  *
124  * Revision 3.11  1994/04/07  22:51:24  lindner
125  * Fix for typecast
126  *
127  * Revision 3.10  1994/04/01  04:43:31  lindner
128  * Fixes for memory leak in argv, and exit status
129  *
130  * Revision 3.9  1994/03/30  21:37:48  lindner
131  * Fix for AIX that doesn't #define unix..
132  *
133  * Revision 3.8  1994/03/11  00:10:52  lindner
134  * Fix for FIOopenUFS()
135  *
136  * Revision 3.7  1994/03/09  16:57:56  lindner
137  * Fix for other vms system() call
138  *
139  * Revision 3.6  1994/03/09  02:11:49  lindner
140  * Use DCLsystem for VMS
141  *
142  * Revision 3.5  1994/03/08  17:23:17  lindner
143  * Fix for return vals
144  *
145  * Revision 3.4  1994/03/08  06:17:15  lindner
146  * Nuke compiler warnings
147  *
148  * Revision 3.3  1994/03/08  03:22:39  lindner
149  * Additions for process management
150  *
151  * Revision 3.2  1994/03/04  17:43:36  lindner
152  * Fix for weird error
153  *
154  * Revision 3.1  1994/02/20  16:20:48  lindner
155  * New object based versions of buffered io routines
156  *
157  *
158  *********************************************************************/
159 
160 #include "fileio.h"
161 #include "Malloc.h"
162 
163 #include <errno.h>
164 #include "Debug.h"
165 #include "Wait.h"
166 #include "String.h"
167 #include "Stdlib.h"
168 #include "compatible.h"
169 
170 #ifdef VMS
171 #  include <stat.h>
172 #else
173 #  include <sys/stat.h>
174 #endif
175 
176 #ifdef MMAP_IO
177 #  include <sys/mman.h>
178 #  ifndef MAP_FILE
179 #    define MAP_FILE 0
180 #  endif
181 #endif
182 
183 static char **  FIOgetargv(char *cmd);
184 
185 
186 /*
187  * Cache of used fios
188  */
189 
190 static FileIO* FIOusedfios[FIOMAXOFILES];
191 static int  FIOnumusedfios = -1;
192 
193 /*
194  * Pop a recently used item.
195  */
196 
FIOpopUsedfio()197 static FileIO *FIOpopUsedfio()
198 {
199      if (FIOnumusedfios > 0) {
200 	  return(FIOusedfios[--FIOnumusedfios]);
201      } else
202 	  return(NULL);
203 }
204 
205 /*
206  * Push an item on our recently used stack.
207  */
208 
FIOpushUsedfio(FileIO * oldfio)209 static boolean FIOpushUsedfio(FileIO *oldfio)
210 {
211      if (FIOnumusedfios < FIOMAXOFILES) {
212 	  FIOusedfios[FIOnumusedfios++] = oldfio;
213 	  return(0);
214      } else
215 	  return(-1);
216 }
217 
218 static FileIO *
FIOnew()219 FIOnew()
220 {
221      FileIO *temp;
222 
223      if (FIOnumusedfios == -1) {
224 	  int i;
225 	  /* Initialize the usedfios struct the first time through */
226 	  for (i=0; i < FIOMAXOFILES; i++) {
227 	       FIOusedfios[i] = NULL;
228 	  }
229 	  FIOnumusedfios=0;
230      }
231 
232      temp = FIOpopUsedfio();
233      if (temp == NULL) {
234 	  temp = (FileIO *) malloc(sizeof(FileIO));
235 	  temp->filename = STRnew();
236      } else {
237 	  STRinit(temp->filename);
238      }
239 
240 
241      temp->issocket = -1;
242      temp->pid = -1;
243      temp->fd = (pid_t) -1;
244      temp->buf = NULL;
245 
246      temp->bufindex = -1;
247      temp->bufdatasize = -1;
248      temp->filename = STRnew();
249 
250 
251      return(temp);
252 }
253 
254 
255 /*
256  * Should only be called by FIOclose* functions really..
257  */
258 
259 void
FIOdestroy(FileIO * fio)260 FIOdestroy(FileIO *fio)
261 {
262      if (FIOpushUsedfio(fio)) {
263 	  /** No space in cache. **/
264 	  free(fio);
265      }
266 }
267 
268 /*
269  * Open a file, initialize data structures.
270  */
271 
272 FileIO *
FIOopenUFS(char * fname,int flags,int mode)273 FIOopenUFS(char *fname, int flags, int mode)
274 {
275      int    fd;
276      FileIO *fio;
277      struct stat buf;
278 
279      if (fname == NULL)
280 	  return(NULL);
281 
282      /* Okay, try and open up the file */
283      fd = open(fname, flags, mode );
284 
285      if (fd < 0)
286 	  return(NULL); /* Couldn't open file */
287 
288      if (fstat(fd, &buf)) {
289           close(fd);
290 	  return(NULL); /* Couldn't open anything */
291      }
292 
293      fio = FIOnew();
294 
295      FIOsetSocket(fio, FALSE);
296      FIOsetFilename(fio, fname);
297      FIOsetfd(fio,fd);
298 
299      return(fio);
300 }
301 
302 
303 /*
304  * Start FIO routines on an already open file descriptor
305  */
306 
307 FileIO*
FIOopenfd(int fd,boolean issocket)308 FIOopenfd(int fd, boolean issocket)
309 {
310      FileIO *fio;
311 
312      fio = FIOnew();
313 
314      FIOsetfd(fio, fd);
315      FIOsetSocket(fio, issocket);
316      return(fio);
317 }
318 
319 FileIO*
FIOopenProcess(char * prog,char ** args,char * rw)320 FIOopenProcess(char *prog, char **args, char   *rw)
321 {
322      int    pfd[2];
323      int    pid;
324      FileIO *fio;
325 
326      if (prog == NULL)
327 	  return(NULL);
328 
329      fio = FIOnew();
330 
331      dup(0);  /*** Arghh!!  pipe doesn't work right when all fds are closed! */
332 
333      if (pipe(pfd) < 0)
334 	  return(NULL);
335 
336 
337      switch (pid = vfork()) {
338      case -1:			/* Error */
339 	  (void) close(pfd[0]);
340 	  (void) close(pfd[1]);
341 	  break;
342      case 0:			/* Child */
343 	  if (rw == NULL || *rw == '\0') {
344 	       /** mimic system(), don't do anything **/
345 	       (void) close(pfd[0]);
346 	       (void) close(pfd[1]);
347 	  }
348 	  else if (*rw == 'r') {
349 	       if (pfd[1] != 1) {
350 		    dup2(pfd[1], 1);
351 		    (void) close(pfd[1]);
352 	       }
353 
354 	       (void) close(pfd[0]);
355 	  } else {
356 	       if (pfd[0] != 0) {
357 		    dup2(pfd[0], 0);
358 		    (void) close(pfd[0]);
359 	       }
360 	       (void) close(pfd[1]);
361 	  }
362 #ifdef VMS
363 	  execv(prog, args);
364 #else
365 	  /** Unix **/
366 
367 	  if (*prog == '/')
368 	       execv(prog, args);
369 	  else
370 	       execvp(prog, args); /* search the path for the command */
371 #endif
372 
373 	  _exit(1);
374      }
375 
376      /* parent.. */
377      if (rw == NULL || *rw == '\0') {
378 	  /** Don't do anything, mimic system() **/
379 	  FIOsetfd(fio, -1);
380 	  (void) close(pfd[0]);
381 	  (void) close(pfd[1]);
382      } else if (*rw == 'r') {
383 	  FIOsetfd(fio, pfd[0]);
384 	  (void) close(pfd[1]);
385      } else {
386 	  FIOsetfd(fio, pfd[1]);
387 	  (void) close(pfd[0]);
388      }
389      FIOsetPid(fio, pid);
390      FIOsetSocket(fio, FALSE);
391      return(fio);
392 }
393 
394 
395 /*
396  * Close a file/socket/process
397  */
398 
399 int
FIOclose(FileIO * fio)400 FIOclose(FileIO *fio)
401 {
402      int result;
403 
404      if (fio == NULL)
405 	  return(0);
406 
407      if (FIOgetPid(fio) >= 0) {
408 
409 	  close(FIOgetfd(fio));
410 	  result = FIOwaitpid(fio);
411 	  FIOdestroy(fio);
412 
413 	  return(result);
414      }
415 
416 
417 #ifdef MMAP_IO
418      /** Unmap memory mapped I/O stuff here.. **/
419      if (FIOgetFilename(fio) != NULL && fio->buf != NULL)
420 	  munmap(fio->buf, FIOgetBufDsize(fio));
421 
422 #endif
423 
424      result = FIOisSocket(fio) ? socket_close(FIOgetfd(fio)) :
425 	  close(FIOgetfd(fio));
426 
427      FIOdestroy(fio);
428 
429      return(result);
430 }
431 
432 /*
433  * A portable waitpid fcn that returns the exit value of the child process
434  *
435  * Should be better about stopped and signaled processes....
436  */
437 
438 int
FIOwaitpid(FileIO * fio)439 FIOwaitpid(FileIO *fio)
440 {
441      Portawait   status;
442      pid_t       result;
443 
444 
445      do {
446 	  errno = 0;
447 	  result = waitpid(FIOgetPid(fio), &status, 0);
448      } while (result != FIOgetPid(fio) && (errno == ECHILD));
449 
450      return(Gwaitstatus(status) & 0xf);
451 }
452 
453 /*
454  * write n bytes to an fd..
455  *
456  * returns -1 on error.
457  */
458 
459 int
FIOwriten(FileIO * fio,char * ptr,int nbytes)460 FIOwriten(FileIO *fio, char *ptr, int nbytes)
461 {
462      int nleft, nwritten;
463      int fd = FIOgetfd(fio);
464 
465      nleft = nbytes;
466      while(nleft > 0) {
467 	  nwritten = FIOisSocket(fio) ? socket_write(fd, ptr, nleft) :
468 	       write(fd, ptr, nleft);
469 
470 	  if (nwritten <= 0)
471 	       return(nwritten);	/* error */
472 
473 	  nleft	-= nwritten;
474 	  ptr	+= nwritten;
475      }
476      return(nbytes - nleft);
477 }
478 
479 /*
480  * write a string to a FileDescriptor, eventually buffer outgoing input
481  *
482  * If write fails a -1 is returned. Otherwise zero is returned.
483  */
484 
485 int
FIOwritestring(FileIO * fio,char * str)486 FIOwritestring(FileIO *fio, char *str)
487 {
488      int length;
489 
490      Debug("writing: %s\n",str);
491 
492      if (str == NULL)
493 	  return(0);
494 
495      length = strlen(str);
496      if (FIOwriten(fio, str, length) != length) {
497 	  Debugmsg("writestring: writen failed\n");
498 	  return(-1);
499      }
500      else
501 	  return(0);
502 }
503 
504 #ifdef MMAP_IO
505 static int
FIOreadbuf_mmap(FileIO * fio,char * newbuf,int newbuflen)506 FIOreadbuf_mmap(FileIO *fio, char *newbuf, int newbuflen)
507 {
508      struct stat buf;
509      char        *bytes;
510 
511      if (FIOgetFilename(fio) != NULL) {
512 	  /** Do MMAP IO if we can... **/
513 
514 	  if (fio->buf == NULL) {
515 	       if (fstat(FIOgetfd(fio), &buf)) {
516 		    close(FIOgetfd(fio));
517 		    return(-1);
518 	       }
519 
520 	       if (buf.st_size == 0)
521 		    return(0);
522 
523 	       FIOsetBufDsize(fio, buf.st_size);
524 	       FIOsetBufIndex(fio, 0);
525 
526 	       bytes = mmap(0, buf.st_size, PROT_READ,
527 			    MAP_SHARED | MAP_FILE,
528 			    FIOgetfd(fio), 0);
529 #ifdef HAVE_MADVISE
530 	       madvise(bytes, buf.st_size, MADV_SEQUENTIAL);
531 #endif
532 
533 	       if (bytes == (caddr_t) -1)
534 		    return(-1);
535 	       fio->buf = bytes;
536 	  }
537 
538 	  if (FIOgetBufIndex(fio) == -1)
539 	       return(0);
540 
541 	  /** Okay, lets return some data... **/
542 	  if (FIOgetBufIndex(fio) >= FIOgetBufDsize(fio))
543 	       return(0);
544 
545 	  if ((FIOgetBufIndex(fio) + newbuflen) > FIOgetBufDsize(fio)) {
546 	       newbuflen = FIOgetBufDsize(fio) - FIOgetBufIndex(fio);
547 	  }
548 	  memcpy(newbuf, fio->buf+FIOgetBufIndex(fio), newbuflen);
549 	  FIOsetBufIndex(fio, FIOgetBufIndex(fio) + newbuflen);
550 
551 	  return(newbuflen);
552      }
553      /** This is an error condition **/
554      return(-1);
555 
556 }
557 #endif
558 
559 /*
560  * Read through a buffer, more efficient for character at a time
561  * processing.  Not so good for block binary transfers
562  */
563 
564 int
FIOreadbuf(FileIO * fio,char * newbuf,int newbuflen)565 FIOreadbuf(FileIO *fio, char *newbuf, int newbuflen)
566 {
567      int len;
568      int fd = FIOgetfd(fio);
569      char *recvbuf;
570      int  bytesread = 0;
571 
572 #ifdef MMAP_IO
573      if (FIOisMMAPable(fio)) {
574 	  return(FIOreadbuf_mmap(fio,newbuf, newbuflen));
575      }
576 #endif
577 
578      if (FIOgetBufIndex(fio) == -1) {
579 
580 	  if (fio->buf == NULL)
581 	       fio->buf = (char *) malloc(sizeof(char) * FIOBUFSIZE);
582 
583 	  len = FIOisSocket(fio) ? socket_read(fd, fio->buf, FIOBUFSIZE) :
584 	       read(fd, fio->buf, FIOBUFSIZE);
585 
586 #if defined(FIO_NOMULTIEOF)
587 	  if (len < 0 && errno == EPIPE) { /** EOF **/
588 	       FIOsetBufIndex(fio,-1);
589 	       return(0);
590 	  }
591 #endif
592 
593 	  FIOsetBufDsize(fio, len);
594 	  FIOsetBufIndex(fio, 0);
595 
596 	  if (len == 0) {
597 	       FIOsetBufIndex(fio,-1);
598 	       return(bytesread); /** EOF **/
599 	  }
600 
601      }
602      recvbuf = fio->buf;
603 
604      while (newbuflen--) {
605 	  *newbuf++ = recvbuf[FIOgetBufIndex(fio)++];
606 	  bytesread++;
607 
608 	  if (FIOgetBufIndex(fio) == FIOgetBufDsize(fio) && newbuflen != 0) {
609 	       /** Read another buffer **/
610 	       len = FIOisSocket(fio) ? socket_read(fd, fio->buf, FIOBUFSIZE) :
611 		    read(fd, fio->buf, FIOBUFSIZE);
612 #if defined(FIO_NOMULTIEOF)
613 	       if (len < 0 && errno == EPIPE) { /** EOF **/
614 		    len = 0;
615 		    errno = 0;
616 	       }
617 #endif
618 
619 	       if (len == 0) {
620 		    FIOsetBufIndex(fio,-1);
621 		    return(bytesread); /** EOF **/
622 	       }
623 	       if (len < 0)
624 		    return(len);       /** Error **/
625 
626 	       FIOsetBufDsize(fio, len);
627 	       FIOsetBufIndex(fio, 0);
628 	  } else if (FIOgetBufIndex(fio) >= FIOgetBufDsize(fio))
629 	       /* Read a new buffer next time through */
630 	       FIOsetBufIndex(fio, -1);
631      }
632      return(bytesread);
633 
634 }
635 
636 
637 /* Read 'n' bytes from a descriptor, non buffered direct into the storage. */
638 int
FIOreadn(FileIO * fio,char * ptr,int nbytes)639 FIOreadn(FileIO *fio, char   *ptr, int    nbytes)
640 {
641      int nleft, nread;
642      int fd = FIOgetfd(fio);
643 
644      nleft = nbytes;
645      while (nleft > 0) {
646 	  nread = FIOisSocket(fio) ? socket_read(fd, ptr, nleft) :
647 	       read(fd, ptr, nleft);
648 #if defined(FIO_NOMULTIEOF)
649           if (nread < 0 && errno == EPIPE) { /** EOF **/
650 	       nread = 0;
651 	       errno = 0;
652 	  }
653 #endif
654 
655 	  if (nread < 0)
656 	       return(nread);  /* error, return < 0 */
657 	  else if (nread == 0) /* EOF */
658 	       break;
659 
660 	  nleft -= nread;
661 	  ptr   += nread;
662      }
663      return(nbytes - nleft);
664 
665 }
666 
667 
668 /*
669  * Read a line from the file/socket, Read the line one byte at a time,
670  * looking for the newline.  We store the newline in the buffer,
671  * then follow it with a null (the same as fgets(3)).
672  * We return the number of characters up to, but not including,
673  * the null (the same as strlen(3))
674  */
675 
676 int
FIOreadline(FileIO * fio,char * ptr,int maxlen)677 FIOreadline(FileIO *fio, char *ptr, int maxlen)
678 {
679      int bytesread;
680      int rc;
681      char c;
682 
683      for (bytesread=1; bytesread < maxlen; bytesread++) {
684 	  if ( (rc = FIOreadbuf(fio, &c, 1)) == 1) {
685 	       *ptr++ = c;
686 	       if (c == '\n')
687 		    break;
688 	  }
689 	  else if (rc == 0) {
690 	       if (bytesread == 1)
691 		    return(0);	/* EOF, no data read */
692 	       else
693 		    break;		/* EOF, some data was read */
694 	  }
695 	  else
696 	       return(rc);		/* error */
697      }
698 
699      *ptr = 0; 				/* Tack a NULL on the end */
700      Debug("FIOreadline: %s\n", (ptr-bytesread));
701 
702      return(bytesread);
703 }
704 
705 
706 /*
707  * This does the same as readline, except that all newlines and
708  * carriage returns are automatically zapped.
709  *
710  * More efficient than doing a readline and a ZapCRLF
711  */
712 
713 int
FIOreadlinezap(FileIO * fio,char * ptr,int maxlen)714 FIOreadlinezap(FileIO *fio, char *ptr, int maxlen)
715 {
716      int len;
717 
718      len = FIOreadtoken(fio, ptr, maxlen, '\n');
719      ptr += len;
720      ptr --;
721 
722      if (len && *ptr == '\r') {
723 	  ptr[len] = '\0';
724 	  len--;
725      }
726      return(len);
727 }
728 
729 
730 /*
731  * Read a line from the file/socket, Read the line one byte at a time,
732  * looking for the token.  We nuke the token from the returned string.
733  * We return the number of characters up to, but not including,
734  * the null (the same as strlen(3))
735  */
736 
737 int
FIOreadtoken(FileIO * fio,char * ptr,int maxlen,char zechar)738 FIOreadtoken(FileIO *fio, char *ptr, int maxlen, char zechar)
739 {
740      int bytesread;
741      int rc;
742      char c;
743 
744      for (bytesread=1; bytesread < maxlen; bytesread++) {
745 	  rc = FIOreadbuf(fio, &c, 1);
746 	  if (rc == 1) {
747 	       *ptr++ = c;
748 	       if (c == zechar) {
749 		    *(ptr - 1) = '\0';
750 		    break;
751 	       }
752 	  }
753 	  else if (rc == 0) {
754 	       if (bytesread == 1)
755 		    return(0);	/* EOF, no data read */
756 	       else
757 		    break;		/* EOF, some data was read */
758 	  }
759 	  else
760 	       return(rc);		/* error */
761      }
762 
763      *ptr = 0; 				/* Tack a NULL on the end */
764      Debug("readtoken: %s\n", (ptr-bytesread));
765      return(bytesread);
766 }
767 
768 static int
FIOexecv(char * prog,char ** args)769 FIOexecv(char *prog, char **args)
770 {
771      FileIO *fio;
772      int    result;
773 
774 #ifdef VMS
775      int     i = 0;
776      char    buffer[1024];
777 
778      /* DCL hog heaven */
779      strcpy(buffer, prog);
780      strcat(buffer, " ");
781 
782      while (i++) {
783 	  if (args[i] == NULL)
784 	       break;
785 
786 	  strcat(buffer, args[i]);
787 	  strcat(buffer, " ");
788      }
789      result = DCLsystem(buffer);
790 
791 #else
792      fio = FIOopenProcess(prog, args, NULL);
793      result = FIOclose(fio);
794 #endif /* VMS */
795 
796      return(result);
797 }
798 
799 
800 /*
801  * Do the minimal shell/dcl processing
802  */
803 
804 static char **
FIOgetargv(char * cmd)805 FIOgetargv(char *cmd)
806 {
807      int           inquote = 0;
808      int           insquote = 0;
809      int           i;
810      static char  *argv[128];		/* Sufficient for now.. */
811      int           argc = 0;
812      char          buf[256];
813      char         *cp = buf;
814 
815      if (cmd == NULL)
816 	  return(NULL);
817 
818      for (i=0; cmd[i] != '\0'; i++) {
819 
820 	  switch (cmd[i]) {
821 
822 	  case ' ': case '\t':
823 	       /* Separators */
824 	       if (insquote || inquote) {
825 		    *cp = cmd[i]; cp++;
826 		    break;
827 	       } else {
828 		    *cp = '\0';
829 		    argv[argc++] = strdup(buf);
830 		    cp = buf;
831 
832 		    /** Get rid of any other whitespace **/
833 		    while (cmd[i+1] == ' ' || cmd[i+1] == '\t')
834 			 i++;
835 	       }
836 	       break;
837 
838 	  case '"':
839 	       if (!insquote)
840 		    inquote = 1-inquote;
841 	       break;
842 
843 	  case '\'':
844 	       if (!inquote)
845 		    insquote = 1-insquote;
846 	       break;
847 
848 	  case '\\':
849 	       /* Quote next character if not in quotes */
850 	       if (insquote || inquote) {
851 		    *cp = cmd[i]; cp++;
852 	       } else {
853 		    *cp = cmd[i+1]; cp++; i++;
854 	       }
855 
856 	       break;
857 
858 	  default:
859 	       *cp = cmd[i]; cp++;
860 	       break;
861 	  }
862      }
863      if (buf != cp) {
864 	  *cp = '\0';
865 	  argv[argc++] = strdup(buf);
866      }
867      argv[argc] = NULL;
868 
869      return(argv);
870 }
871 
872 
873 /*
874  *  An emulation of the system() call without the shell
875  *  returns the exit status of the child
876  */
877 
878 int
FIOsystem(char * cmd)879 FIOsystem(char *cmd)
880 {
881      char **argv;
882      int    result, i;
883 
884 #ifdef VMS
885      return(DCLsystem(cmd));
886 #else
887 
888      if (cmd == NULL || *cmd == '\0')
889 	  return(-1);
890 
891      argv = FIOgetargv(cmd);
892 
893      result = FIOexecv(argv[0], argv);
894 
895      for (i=0; argv[i] != NULL; i++)
896 	  free(argv[i]);
897 
898      return(result);
899 #endif
900 }
901 
902 
903 
904 /*
905  * Similar to popen...
906  */
907 
908 FileIO
FIOopenCmdline(char * cmd,char * rw)909 *FIOopenCmdline(char *cmd, char *rw)
910 {
911      char   **argv;
912      FileIO *fio;
913 
914      if (cmd == NULL)
915 	  return(NULL);
916 
917      if (*cmd == '|')
918 	  cmd++;
919 
920      argv = FIOgetargv(cmd);
921 
922      fio = FIOopenProcess(argv[0], argv, rw);
923 
924      return(fio);
925 }
926 
927