home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / p / plbin.zip / pl / src / pl-os.c < prev    next >
C/C++ Source or Header  |  1993-02-23  |  60KB  |  2,789 lines

  1. /*  pl-os.c,v 1.18 1993/02/23 13:16:39 jan Exp
  2.  
  3.     Copyright (c) 1990 Jan Wielemaker. All rights reserved.
  4.     See ../LICENCE to find out about your rights.
  5.     jan@swi.psy.uva.nl
  6.  
  7.     Purpose: Operating System Dependencies
  8. */
  9.  
  10. /*  Modified (M) 1993 Dave Sherratt  */
  11.  
  12. #if __TOS__
  13. #include <tos.h>        /* before pl-os.h due to Fopen, ... */
  14. static long    wait_ticks;    /* clock ticks not CPU time */
  15. #endif
  16. #if OS2 && EMX
  17. #include <os2.h>                /* this has to appear before pl-incl.h */
  18. #endif
  19. #include "pl-incl.h"
  20. #include "pl-ctype.h"
  21. #include "pl-itf.h"
  22.  
  23. #if unix || EMX
  24. #include <sys/param.h>
  25. #include <sys/stat.h>
  26. #include <pwd.h>
  27. #include <sys/file.h>
  28. #include <unistd.h>
  29. #endif
  30.  
  31. #if sun
  32. extern int fstat(/*int, struct stat **/);
  33. extern int stat(/*char *, struct stat**/);
  34. extern int unlink(/*char **/);
  35. extern int link(/*char **/);
  36. extern int select(/*int *, int*, int*, struct timeval **/);
  37. extern int ioctl(/*int, int, Void*/);
  38. extern int execl(/*char *, ... */);
  39. extern int srandom P((long));
  40. extern int random P((void));
  41. #endif
  42.  
  43. #if OS2 && EMX
  44. static real initial_time;
  45. #endif /* OS2 */
  46.  
  47. forwards void    initExpand P((void));
  48. forwards void    initRandom P((void));
  49. forwards void    initEnviron P((void));
  50. forwards char *    okToExec P((char *));
  51. forwards char *    Which P((char *));
  52. forwards Char    do_get_char P((void));
  53.  
  54. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  55. This module is a contraction of functions that used to be all  over  the
  56. place.   together  with  pl-os.h  (included  by  pl-incl.h) this file
  57. should define a basic  layer  around  the  OS,  on  which  the  rest  of
  58. SWI-Prolog  is  based.   SWI-Prolog  has  been developed on SUN, running
  59. SunOs 3.4 and later 4.0.
  60.  
  61. Unfortunately some OS's simply do not offer  an  equivalent  to  SUN  os
  62. features.   In  most  cases part of the functionality of the system will
  63. have to be dropped. See the header of pl-incl.h for details.
  64. - - - - - - - - - - -  - - - - - */
  65.  
  66.         /********************************
  67.         *         INITIALISATION        *
  68.         *********************************/
  69.  
  70. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  71.     bool initOs()
  72.  
  73.     Initialise the OS dependant functions.
  74. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  75.  
  76. bool
  77. initOs()
  78. { DEBUG(1, printf("OS:initExpand() ...\n"));
  79.   initExpand();
  80.   DEBUG(1, printf("OS:initRandom() ...\n"));
  81.   initRandom();
  82.   DEBUG(1, printf("OS:initEnviron() ...\n"));
  83.   initEnviron();
  84.  
  85. #if tos
  86.   wait_ticks = clock();
  87. #endif
  88. #if OS2
  89.   {
  90.     DATETIME i;
  91.     DosGetDateTime((PDATETIME)&i);
  92.     initial_time = (i.hours * 3600.0) 
  93.                    + (i.minutes * 60.0) 
  94.            + i.seconds
  95.            + (i.hundredths / 100.0);
  96.   }
  97. #endif /* OS2 */
  98.   DEBUG(1, printf("OS:done\n"));
  99.  
  100.   succeed;
  101. }
  102.  
  103.  
  104. typedef void (*halt_function) P((int, Void));
  105. typedef struct on_halt *OnHalt;
  106.  
  107. struct on_halt
  108. { halt_function    function;
  109.   Void        argument;
  110.   OnHalt    next;
  111. };
  112.  
  113. static OnHalt on_halt_list;
  114.  
  115. void
  116. PL_on_halt(f, arg)
  117. halt_function f;
  118. Void arg;
  119. { OnHalt h = allocHeap(sizeof(struct on_halt));
  120.  
  121.   h->function = f;
  122.   h->argument = arg;
  123.   h->next = on_halt_list;
  124.   on_halt_list = h;
  125. }
  126.  
  127.  
  128. volatile void
  129. Halt(status)
  130. int status;
  131. { OnHalt h;
  132.  
  133.   for(h = on_halt_list; h; h = h->next)
  134.     (*h->function)(status, h->argument);
  135.  
  136.   dieIO();
  137.   RemoveTemporaryFiles();
  138.  
  139.   exit(status);
  140.   /*NOTREACHED*/
  141. }
  142.  
  143.         /********************************
  144.         *            OS ERRORS          *
  145.         *********************************/
  146.  
  147. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  148.     char *OsError()
  149.     Return a char *, holding a description of the last OS call error.
  150. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  151.  
  152. char *
  153. OsError()
  154. { static char errmsg[64];
  155.  
  156. #if unix
  157.   extern int sys_nerr;
  158. #if !EMX
  159.   extern char *sys_errlist[];
  160. #endif
  161.   extern int errno;
  162.  
  163.   if ( errno < sys_nerr )
  164.     return sys_errlist[errno];
  165. #endif
  166.  
  167. #if tos
  168.   if ( errno < sys_nerr )
  169.     return strerror(errno);
  170. #endif
  171.  
  172.   sprintf(errmsg, "Unknown Error (%d)", errno);
  173.   return errmsg;
  174. }
  175.  
  176.         /********************************
  177.         *    PROCESS CHARACTERISTICS    *
  178.         *********************************/
  179.  
  180. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  181.     real CpuTime()
  182.  
  183.     Returns a floating point number, representing the amount  of  (user)
  184.     CPU-seconds  used  by the process Prolog is in.  For systems that do
  185.     not allow you to obtain this information  you  may  wish  to  return
  186.     elapsed  time  since Prolog was started, as this function is used to
  187.     by consult/1 and time/1 to determine the amount of CPU time used  to
  188.     consult a file or to execute a query.
  189. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  190.  
  191. #if unix
  192. #ifdef HZ
  193. #  define Hz HZ
  194. #else
  195. #  define Hz        60
  196. #endif
  197. #include <sys/times.h>
  198. #endif
  199.  
  200.  
  201. real
  202. CpuTime()
  203. {
  204. #if unix
  205.   struct tms t;
  206.  
  207.   times(&t);
  208.  
  209.   return t.tms_utime / ( real )( Hz ) ;
  210. #endif
  211.  
  212. #if OS2 && EMX
  213.   DATETIME i;
  214.  
  215.   DosGetDateTime((PDATETIME)&i);
  216.   return (((i.hours * 3600) 
  217.                  + (i.minutes * 60) 
  218.          + i.seconds
  219.              + (i.hundredths / 100.0)) - initial_time);
  220. #endif
  221.  
  222. #if tos
  223.   return (real) (clock() - wait_ticks) / 200.0;
  224. #endif
  225. }
  226.  
  227.         /********************************
  228.         *       MEMORY MANAGEMENT       *
  229.         *********************************/
  230.  
  231. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  232.     long *Allocate(n)
  233.       long n;
  234.  
  235.     Allocate a memory area of `n' bytes from the operating system.   `n'
  236.     is a long as we need to allocate one uniform array of longs for both
  237.     the  local  stack  and  global  stack,  which  implies  it should be
  238.     possible to allocate at least a few hundred Kbytes.  If  you  cannot
  239.     implement  this  function  you  are in deep trouble.  You either can
  240.     decide to redesign large part of the data representation, or  forget
  241.     about  SWI-Prolog.   Memory  is never returned to the system.  As it
  242.     would only concern small areas,  all  over  SWI-Prolog's  memory  no
  243.     currently  available operating system (I'm aware of) will be able to
  244.     handle it anyway.  THE RETURN VALUE SHOULD BE ROUNDED TO BE A  VALID
  245.     POINTER FOR LONGS AND STRUCTURES AND AT LEAST A MULTIPLE OF 4.
  246. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  247.  
  248. Void
  249. Allocate(n)
  250. long n;
  251. { Void mem = Malloc(n);
  252.  
  253.   SECURE(assert(malloc_verify() == 1));
  254.  
  255.   return (Void) mem;
  256. }
  257.  
  258. #if hpux
  259. #include <a.out.h>
  260. int
  261. getpagesize()
  262. {  
  263. #ifdef EXEC_PAGESIZE
  264.   return EXEC_PAGESIZE;
  265. #else
  266.   return 4096;                /* not that important */
  267. #endif
  268. }
  269. #endif
  270.  
  271. #if hpux || tos
  272. void
  273. bzero(p, n)
  274. Void p;
  275. register size_t n;
  276. { char *s = p;
  277.  
  278.   while( n-- > 0 )
  279.     *s++ = '\0';
  280. }
  281. #endif
  282.  
  283.  
  284.         /********************************
  285.         *             PRINT             *
  286.         *********************************/
  287.  
  288. #if gould
  289. char *
  290. vsprintf(buf, fm, args)
  291. char *buf, *fm;
  292. va_list args;
  293. { FILE f;
  294. #define BIGBUF 10000000
  295.  
  296.   f._cnt    = BIGBUF;        /* Hack oh dear hack!!! */
  297.   f._ptr    = f._base = buf;    /* down with gould (they are in military */
  298.   f._bufsiz = BIGBUF;        /* bussiness anyway!) */
  299.   f._flag   = 0;         /* was _IOLBF; */
  300.   f._file   = '\0';
  301.   
  302.   DEBUG(9, printf("calling _doprnt(%s, ...)\n", fm));
  303.   _doprnt(fm, args, &f);
  304.   *f._ptr++ = '\0';
  305.  
  306.   return buf;
  307. }
  308.  
  309. int
  310. vfprintf(fd, fm, args)
  311. FILE *fd;
  312. char *fm;
  313. va_list args;
  314. { return _doprnt(fm, args, fd);
  315. }
  316. #endif
  317.  
  318.         /********************************
  319.         *     STRING MANIPULATION    *
  320.         ********************************/
  321.  
  322. #if sun
  323. int
  324. strcmp(s1, s2)
  325. unsigned char *s1, *s2;
  326. { while(*s1 && *s1 == *s2)
  327.     s1++, s2++;
  328.  
  329.   return *s1 - *s2;
  330. }
  331. #endif
  332.  
  333.  
  334.         /********************************
  335.         *           ARITHMETIC          *
  336.         *********************************/
  337.  
  338. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  339.     long Random()
  340.  
  341.     Return a random number. Used for arithmetic only.
  342. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  343.  
  344. static void
  345. initRandom()
  346. {
  347. #ifdef SRANDOM
  348.   SRANDOM(Time());
  349. #else
  350.   srand((unsigned)Time());
  351. #endif
  352. }
  353.  
  354. long
  355. Random()
  356. #ifdef RANDOM
  357.   return RANDOM();
  358. #else
  359.   return rand();
  360. #endif
  361. }
  362.  
  363.         /********************************
  364.         *             FILES             *
  365.         *********************************/
  366.  
  367.       /* (Everything you always wanted to know about files ...) */
  368.  
  369. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  370. Generation and administration of temporary files.  Currently  only  used
  371. by  the foreign language linker.  It might be useful to make a predicate
  372. available to the Prolog user based on these functions.  These  functions
  373. are  in  this  module as non-UNIX OS probably don't have getpid() or put
  374. temporaries on /tmp.
  375.  
  376.     Atom TemporaryFile(id)
  377.      char *id;
  378.  
  379.     The return value of this call is an atom,  whose  string  represents
  380.     the  path  name of a unique file that can be used as temporary file.
  381.     `id' is a char * that can be used to make it easier to identify  the
  382.     file as a specific kind of SWI-Prolog intermediate file.
  383.  
  384.     void RemoveTemporaryFiles()
  385.  
  386.     Remove all temporary files.  This function should be  aware  of  the
  387.     fact  that some of the file names generated by TemporaryFile() might
  388.     not be created at all, or might already have been deleted.
  389. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  390.  
  391. typedef struct tempfile * TempFile;
  392.  
  393. static struct tempfile
  394. { Atom         name;
  395.   TempFile    next;
  396. } *tempfiles, *temptail;        /* chain of temporary files */
  397.  
  398. Atom
  399. TemporaryFile(id)
  400. char *id;
  401. { static char temp[MAXPATHLEN];
  402.   TempFile tf = (TempFile) allocHeap(sizeof(struct tempfile));
  403.  
  404. #if unix
  405.   static int temp_counter = 0;
  406.   sprintf(temp, "/tmp/pl_%s_%d_%d", id, getpid(), temp_counter++);
  407. #endif
  408.  
  409. #if EMX
  410.   static int temp_counter = 0;
  411.   char *foo;
  412.  
  413.   if ( (foo = tempnam(".", (const char *)id)) )
  414.   { strcpy(temp, foo);
  415.     free(foo);
  416.   } else
  417.     sprintf(temp, "pl_%s_%d_%d", id, getpid(), temp_counter++);
  418. #endif
  419.  
  420. #if tos
  421.   tmpnam(temp);
  422. #endif
  423.  
  424.   tf->name = lookupAtom(temp);
  425.   tf->next = (TempFile) NULL;
  426.   
  427.   if ( temptail == (TempFile) NULL )
  428.   { tempfiles = temptail = tf;
  429.   } else
  430.   { temptail->next = tf;
  431.     temptail = tf;
  432.   }
  433.  
  434.   return tf->name;
  435. }
  436.  
  437. void
  438. RemoveTemporaryFiles()
  439. { TempFile tf, tf2;  
  440.  
  441.   for(tf = tempfiles; tf; tf = tf2)
  442.   { DeleteFile(stringAtom(tf->name));
  443.     tf2 = tf->next;
  444.     freeHeap(tf, sizeof(struct tempfile));
  445.   }
  446.  
  447.   tempfiles = temptail = (TempFile) NULL;
  448. }
  449.  
  450. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  451. Fortunately most C-compilers  are  sold  with  a  library  that  defines
  452. Unix-style  access  to  the  file system.  The standard functions go via
  453. macros to deal with 16-bit machines, but are not  defined  as  functions
  454. here.   Some  more  specific things SWI-Prolog wants to know about files
  455. are defined here:
  456.  
  457.     int  GetDTableSize()
  458.  
  459.     SWI-Prolog assumes it can refer to open i/o streams via  read()  and
  460.     write() by small integers, returned by open(). These integers should
  461.     be  in  the range [0, ..., GetDTableSize()). If your system does not
  462.     do this you better redefine the Open(), Read() and Write() macros so
  463.     they  do  meat  this  requirement.   Prolog  allocates  a  table  of
  464.     structures with GetDTableSize entries.
  465.  
  466.     long LastModifiedFile(path)
  467.      char *path;
  468.  
  469.     Returns the last time `path' has been modified.  Used by the  source
  470.     file administration to implement make/0.
  471.  
  472.     bool ExistsFile(path)
  473.      char *path;
  474.  
  475.     Succeeds if `path' refers to the pathname of a regular file  (not  a
  476.     directory).
  477.  
  478.     bool AccessFile(path, mode)
  479.      char *path;
  480.      int mode;
  481.  
  482.     Succeeds if `path' is the pathname of an existing file and it can
  483.     be accessed in any of the inclusive or constructed argument `mode'.
  484.  
  485.     bool ExistsDirectory(path)
  486.      char *path;
  487.  
  488.     Succeeds if `path' refers to the pathname  of  a  directory.
  489.  
  490.     bool DeleteFile(path)
  491.      char *path;
  492.  
  493.     Removes a (regular) file from the  file  system.   Returns  TRUE  if
  494.     succesful FALSE otherwise.
  495.  
  496.     bool RenameFile(old, new)
  497.      char *old, *new;
  498.  
  499.     Rename file from name `old' to name `new'. If new already exists, it is
  500.     deleted. Returns TRUE if succesful, FALSE otherwise.
  501.  
  502.     bool OpenStream(stream)
  503.      int stream;
  504.  
  505.     Succeeds if `stream' refers to an open i/o stream.
  506.  
  507.     bool MarkExecutable(path)
  508.      char *path;
  509.  
  510.     Mark `path' as an executable program.  Used by the intermediate code
  511.     compiler and the creation of stand-alone executables.
  512. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  513.  
  514. int
  515. GetDTableSize()
  516. {
  517. #ifdef DESCRIPTOR_TABLE_SIZE
  518.   return DESCRIPTOR_TABLE_SIZE;
  519. #else
  520. #  if hpux
  521. #    include <sys/resource.h>
  522.      struct rlimit rlp;
  523.      (void) getrlimit(RLIMIT_NOFILE,&rlp);
  524.      return (rlp.rlim_cur);
  525. #  else
  526.      extern int getdtablesize P((void));
  527.  
  528.      return getdtablesize();
  529. #  endif
  530. #endif
  531. }
  532.  
  533. /* ********************************************************************
  534.    Design Note -- atoenne@mpi-sb.mpg.de --
  535.  
  536.    Beware! OsPath() and PrologPath() are insecure functions.
  537.    Make sure that you copy the result of these functions to a proper location
  538.    before you call the functions again. Otherwise you will write over the
  539.    former result.
  540.    ******************************************************************** */
  541.  
  542. #if tos
  543. char *
  544. PrologPath(char *tospath)
  545. { static char path[MAXPATHLEN];
  546.   register char *s = tospath, *p = path;
  547.  
  548.   for(; *s; s++, p++)
  549.     *p = (*s == '\\' ? '/' : makeLower(*s));
  550.   *p = EOS;
  551.  
  552.   return path;
  553. }
  554.  
  555.  
  556. char *
  557. OsPath(char *unixpath)
  558. { static char path[MAXPATHLEN];
  559.   register char *s = unixpath, *p = path;
  560.  
  561.   if ( isLetter(s[0]) && s[1] == ':' )        /* drive indicator */
  562.   { *p++ = *s++;
  563.     *p++ = *s++;
  564.   }
  565.  
  566.   while(*s)
  567.   { int i, dotseen;
  568.  
  569.     for(i=0, dotseen=0; *s && !IS_DIR_SEPARATOR(*s); s++)
  570.     { if ( dotseen > 0 )        /* copy after dot */
  571.       { if ( dotseen++ <= 3 )
  572.           p[i++] = *s;
  573.       } else
  574.       { if ( *s == '.' )        /* dot; possibly backup */
  575.     { dotseen = 1;
  576.       if ( i > 8 )
  577.         i = 8;
  578.       p[i++] = '.';
  579.     } else if ( i < 12 )
  580.     { p[i++] = *s;
  581.       if ( i == 8 )
  582.         p[i++] = '.';
  583.     }
  584.       }
  585.     }
  586.  
  587.     p += i;
  588.     if ( IS_DIR_SEPARATOR(*s) )
  589.     { s++;
  590.       *p++ = '\\';
  591.     }
  592.   }
  593.  
  594.   *p = EOS;
  595.  
  596.   return path;
  597. #endif
  598.  
  599. #if OS2 && EMX
  600.  
  601. /* 
  602.    Conversion rules Prolog <-> OS/2 (using HPFS)
  603.    / <-> \
  604.    /x:/ <-> x:\  (embedded drive letter)
  605.    No length restrictions up to MAXPATHLEN, no case conversions.
  606. */
  607.  
  608. char *
  609. PrologPath(char *ospath)
  610. { static char path[MAXPATHLEN];
  611.   register char *s = ospath, *p = path;
  612.   register int limit = MAXPATHLEN-1;
  613.  
  614.   if (isLetter(s[0]) && s[1] == ':')
  615.   { *p++ = '/';
  616.     *p++ = *s++;
  617.     *p++ = *s++;
  618.     limit -= 3;
  619.   }
  620.   for(; *s && limit; s++, p++, limit--)
  621.     *p = (*s == '\\' ? '/' : *s);
  622.   *p = EOS;
  623.  
  624.   return path;
  625. }
  626.  
  627.  
  628. char *
  629. OsPath(char *unixpath)
  630. { static char path[MAXPATHLEN];
  631.   register char *s = unixpath, *p = path;
  632.   register int limit = MAXPATHLEN-1;
  633.  
  634.   if ( s[0] == '/' && isLetter(s[1]) && s[2] == ':') /* embedded drive letter*/
  635.   { s++;
  636.     *p++ = *s++;
  637.     *p++ = *s++;
  638.     if ( *s != '/' )
  639.       *p++ = '\\';
  640.     limit -= 2;
  641.   }
  642.  
  643.   for(; *s && limit; s++, p++, limit--)
  644.     *p = (*s == '/' ? '\\' : *s);
  645.   *p = EOS;
  646.  
  647.   return path;
  648. #endif /* OS2 */
  649.  
  650. #if unix
  651. char *PrologPath(p)
  652. char *p;
  653. { return p;
  654. }
  655.  
  656. char *
  657. OsPath(p)
  658. char *p;
  659. { return p;
  660. }
  661. #endif
  662.  
  663. long
  664. LastModifiedFile(f)
  665. char *f;
  666. {
  667. #if unix || EMX
  668.   struct stat buf;
  669.  
  670.   if ( stat(OsPath(f), &buf) < 0 )
  671.     return -1;
  672.  
  673.   return (long)buf.st_mtime;
  674. #endif
  675.  
  676. #if tos
  677. #define DAY    (24*60*60L)
  678.   static int msize[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  679.   long t;
  680.   int n;
  681.   struct ffblk buf;
  682.   struct dz
  683.   { unsigned int hour : 5;    /* hour (0-23) */
  684.     unsigned int min  : 6;    /* minute (0-59) */
  685.     unsigned int sec  : 5;    /* seconds in steps of 2 */
  686.     unsigned int year : 7;    /* year (0=1980) */
  687.     unsigned int mon  : 4;    /* month (1-12) */
  688.     unsigned int day  : 5;    /* day (1-31) */
  689.   } *dz;
  690.  
  691.   if ( findfirst(OsPath(f), &buf, FA_HIDDEN) != 0 )
  692.     return -1;
  693.   dz = (struct dz *) &buf.ff_ftime;
  694.   DEBUG(2, printf("%d/%d/%d %d:%d:%d\n",
  695.        dz->day, dz->mon, dz->year+1980, dz->hour, dz->min, dz->sec));
  696.  
  697.   t = (10*365+2) * DAY;        /* Start of 1980 */
  698.   for(n=0; n < dz->year; n++)
  699.     t += ((n % 4) == 0 ? 366 : 365) * DAY;
  700.   for(n=1; n < dz->mon; n++)
  701.     t += msize[n+1] * DAY;
  702.   t += (dz->sec * 2) + (dz->min * 60) + (dz->hour *60*60L);
  703.  
  704.   return t;
  705. #endif
  706. }  
  707.  
  708.  
  709. bool
  710. ExistsFile(path)
  711. char *path;
  712. {
  713. #if unix || EMX
  714.   struct stat buf;
  715.  
  716.   if ( stat(OsPath(path), &buf) == -1 || (buf.st_mode & S_IFMT) != S_IFREG )
  717.     fail;
  718.   succeed;
  719. #endif
  720.  
  721. #if tos
  722.   struct ffblk buf;
  723.  
  724.   if ( findfirst(OsPath(path), &buf, FA_HIDDEN) == 0 )
  725.   { DEBUG(2, printf("%s (%s) exists\n", path, OsPath(path)));
  726.     succeed;
  727.   }
  728.   DEBUG(2, printf("%s (%s) does not exist\n", path, OsPath(path)));
  729.   fail;
  730. #endif
  731. }
  732.  
  733. bool
  734. AccessFile(path, mode)
  735. char *path;
  736. int mode;
  737. {
  738. #if unix || EMX
  739.   int m = 0;
  740.  
  741.   if ( mode & ACCESS_READ    ) m |= R_OK;
  742.   if ( mode & ACCESS_WRITE   ) m |= W_OK;
  743.   if ( mode & ACCESS_EXECUTE ) m |= X_OK;
  744.  
  745.   return access(OsPath(path), m) == 0 ? TRUE : FALSE;
  746. #endif
  747.  
  748. #if tos
  749.   struct ffblk buf;
  750.  
  751.   if ( findfirst(OsPath(path), &buf, FA_DIREC|FA_HIDDEN) != 0 )
  752.     fail;            /* does not exists */
  753.   if ( (mode & ACCESS_WRITE) && (buf.ff_attrib & FA_RDONLY) )
  754.     fail;            /* readonly file */
  755.  
  756.   succeed;
  757. #endif
  758. }
  759.  
  760. bool
  761. ExistsDirectory(path)
  762. char *path;
  763. {
  764. #if unix || EMX
  765.   struct stat buf;
  766.  
  767.   if ( stat(OsPath(path), &buf) == -1 || (buf.st_mode & S_IFMT) != S_IFDIR )
  768.     fail;
  769.   succeed;
  770. #endif
  771.  
  772. #if tos
  773.   struct ffblk buf;
  774.  
  775.   if ( findfirst(OsPath(path), &buf, FA_DIREC|FA_HIDDEN) == 0 &&
  776.        buf.ff_attrib & FA_DIREC )
  777.     succeed;
  778.   if ( streq(path, ".") || streq(path, "..") )    /* hack */
  779.     succeed;
  780.   fail;
  781. #endif
  782. }
  783.  
  784.  
  785. long
  786. SizeFile(path)
  787. char *path;
  788. { struct stat buf;
  789.   if ( stat(OsPath(path), &buf) == -1 )
  790.     return -1;
  791.  
  792.   return buf.st_size;
  793. }
  794.  
  795.  
  796. bool
  797. DeleteFile(path)
  798. char *path;
  799. {
  800. #if unix || EMX
  801.   return unlink(OsPath(path)) == 0 ? TRUE : FALSE;
  802. #endif
  803.  
  804. #if tos
  805.   return remove(OsPath(path)) == 0 ? TRUE : FALSE;
  806. #endif
  807. }
  808.  
  809.  
  810. bool
  811. RenameFile(old, new)
  812. char *old, *new;
  813. {
  814.   char os_old[MAXPATHLEN];
  815.   char os_new[MAXPATHLEN];
  816. #if unix
  817.   int rval;
  818.  
  819.   strcpy(os_old, OsPath(old));
  820.   strcpy(os_new, OsPath(new));
  821.  
  822.   unlink(new);
  823.   if ((rval = link(os_old, os_new)) == 0 
  824.               && (rval = unlink(os_old)) != 0)
  825.     unlink(new);
  826.  
  827.   if (rval == 0)
  828.     succeed;
  829.  
  830.   fail;
  831. #endif
  832.  
  833. #if tos || EMX
  834.   return rename(os_old, os_new) == 0 ? TRUE : FALSE;
  835. #endif
  836. }
  837.  
  838.  
  839. bool
  840. SameFile(f1, f2)
  841. char *f1, *f2;
  842. { if ( streq(f1, f2) == FALSE )
  843.   { 
  844. #if unix
  845.     struct stat buf1;
  846.     struct stat buf2;
  847.  
  848.     if ( stat(OsPath(f1), &buf1) != 0 || stat(OsPath(f2), &buf2) != 0 )
  849.       fail;
  850.     if ( buf1.st_ino == buf2.st_ino && buf1.st_dev == buf2.st_dev )
  851.       succeed;
  852. #endif
  853. #if OS2 && EMX
  854.     /* Amazing! There is no simple way to check two files for identity. */
  855.     /* stat() and fstat() both return dummy values for inode and device. */
  856. #endif /* OS2 */
  857.  
  858.     fail;
  859.   }
  860.  
  861.   succeed;
  862. }
  863.  
  864.  
  865. bool
  866. OpenStream(fd)
  867. int fd;
  868. {
  869. #if unix || EMX
  870.   struct stat buf;
  871.  
  872.   return fstat(fd, &buf) == 0 ? TRUE : FALSE;
  873. #endif
  874.  
  875. #if tos
  876.   return fd < 3 ? TRUE : FALSE;    /* stdin, stdout and stderr are open */
  877. #endif
  878. }
  879.  
  880.  
  881. bool
  882. MarkExecutable(name)
  883. char *name;
  884. {
  885. #if unix
  886.   struct stat buf;
  887.   int um;
  888.  
  889.   um = umask(0777);
  890.   umask(um);
  891.   if ( stat(name, &buf) == -1 )
  892.     return warning("Can't stat(2) `%s': %s", name, OsError());
  893.  
  894.   if ( (buf.st_mode & 0111) == (~um & 0111) )
  895.     succeed;
  896.  
  897.   buf.st_mode |= 0111 & ~um;
  898.   if ( chmod(name, buf.st_mode) == -1 )
  899.     return warning("Couldn't turn %s into an executable: %s", name, OsError());
  900.  
  901.   succeed;
  902. #endif
  903.  
  904. #if tos || OS2
  905.   succeed;        /* determined by extension */
  906. #endif
  907. }
  908.  
  909. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  910.     char *AbsoluteFile(file)
  911.        char *file;
  912.  
  913.     Expand a file specification to a system-wide unique  description  of
  914.     the  file  that can be passed to the file functions that take a path
  915.     as argument.  Path should refer to the same file, regardless of  the
  916.     current  working  directory.   On  Unix absolute file names are used
  917.     for this purpose.
  918.  
  919.     This  function  is  based  on  a  similar  (primitive)  function  in
  920.     Edinburgh C-Prolog.
  921.  
  922.     char *BaseName(path)
  923.      char *path;
  924.  
  925.     Return the basic file name for a file having path `path'.
  926.  
  927.     char *DirName(path)
  928.      char *path;
  929.     
  930.     Return the directory name for a file having path `path'.
  931. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  932.  
  933. #if unix
  934. typedef struct canonical_dir *CanonicalDir;
  935.  
  936. static struct canonical_dir
  937. { char *    name;            /* name of directory */
  938.   char *    canonical;        /* canonical name of directory */
  939.   dev_t        device;            /* device number */
  940.   ino_t        inode;            /* inode number */
  941.   CanonicalDir  next;            /* next in chain */
  942. } *canonical_dirlist = NULL;            /* initialization -- atoenne -- */
  943.  
  944. forwards char    *canonisePath P((char *)); /* canonise a path-name */
  945. forwards char   *canoniseDir P((char *));
  946. #endif
  947.  
  948. static  char    CWDdir[MAXPATHLEN];       /* current directory */
  949.  
  950.  
  951. static void
  952. initExpand()
  953. #if unix
  954.   char *dir;
  955.   char *cpaths;
  956. #endif
  957.  
  958.   CWDdir[0] = EOS;
  959.  
  960. #if unix
  961.   if ( (cpaths = getenv("CANONICAL_PATHS")) )
  962.   { char buf[MAXPATHLEN];
  963.  
  964.     while(*cpaths)
  965.     { char *e;
  966.  
  967.       if ( (e = index(cpaths, ':')) )
  968.       { int l = e-cpaths;
  969.  
  970.     strncpy(buf, cpaths, l);
  971.     buf[l] = EOS;
  972.     cpaths += l+1;
  973.     canoniseDir(buf);
  974.       } else
  975.       { canoniseDir(cpaths);
  976.     break;
  977.       }
  978.     }
  979.   }
  980.  
  981.   if ( (dir = getenv("HOME")) ) canoniseDir(dir);
  982.   if ( (dir = getenv("PWD"))  ) canoniseDir(dir);
  983. #endif
  984. }
  985.  
  986. #if unix
  987.  
  988. static char *
  989. canoniseDir(path)
  990. char *path;
  991. { CanonicalDir d;
  992.   struct stat buf;
  993.  
  994.   DEBUG(1, printf("canoniseDir(%s) --> ", path); fflush(stdout));
  995.  
  996.   for(d = canonical_dirlist; d; d = d->next)
  997.   { if ( streq(d->name, path) )
  998.     { if ( d->name != d->canonical )
  999.     strcpy(path, d->canonical);
  1000.  
  1001.       DEBUG(1, printf("(lookup) %s\n", path));
  1002.       return path;
  1003.     }
  1004.   }
  1005.  
  1006.   if ( stat(OsPath(path), &buf) == 0 )
  1007.   { CanonicalDir dn = allocHeap(sizeof(struct canonical_dir));
  1008.     char dirname[MAXPATHLEN];
  1009.     char *e = path + strlen(path);
  1010.  
  1011.     dn->next   = canonical_dirlist;
  1012.     dn->name   = store_string(path);
  1013.     dn->inode  = buf.st_ino;
  1014.     dn->device = buf.st_dev;
  1015.  
  1016.     do
  1017.     { strncpy(dirname, path, e-path);
  1018.       dirname[e-path] = EOS;
  1019.       if ( stat(OsPath(dirname), &buf) < 0 )
  1020.     break;
  1021.  
  1022.       for(d = canonical_dirlist; d; d = d->next)
  1023.       { if ( d->inode == buf.st_ino && d->device == buf.st_dev )
  1024.     { canonical_dirlist = dn;
  1025.  
  1026.       strcpy(dirname, d->canonical);
  1027.       strcat(dirname, e);
  1028.       strcpy(path, dirname);
  1029.       dn->canonical = store_string(path);
  1030.       DEBUG(1, printf("(replace) %s\n", path));
  1031.       return path;
  1032.     }
  1033.       }
  1034.  
  1035.       for(e--; *e != '/' && e > path + 1; e-- )
  1036.     ;
  1037.  
  1038.     } while( e > path );
  1039.  
  1040.     dn->canonical = dn->name;
  1041.     canonical_dirlist = dn;
  1042.  
  1043.     DEBUG(1, printf("(new, existing) %s\n", path));
  1044.     return path;
  1045.   }
  1046.  
  1047.   DEBUG(1, printf("(nonexisting) %s\n", path));
  1048.   return path;
  1049. }
  1050.  
  1051. #else
  1052.  
  1053. #define canoniseDir(d)
  1054.  
  1055. #endif
  1056.  
  1057.  
  1058. static char *
  1059. canonisePath(path)
  1060. register char *path;
  1061. { register char *out = path;
  1062.   char *osave[100];
  1063.   int  osavep = 0;
  1064.   char *bsave = out;
  1065.  
  1066.   while( path[0] == '/' && path[1] == '.' &&
  1067.      path[2] == '.' && path[3] == '/')
  1068.     path += 3;
  1069.  
  1070.   while(*path)
  1071.   { if (*path == '/')
  1072.     { while(path[1] == '/')
  1073.     path++;
  1074.       while (path[1] == '.' && path[2] == '/')
  1075.     path += 2;
  1076.       while (path[1] == '.' && path[2] == '.' && path[3] == '/')
  1077.       { out = osave[--osavep];
  1078.     path += 3;
  1079.       }
  1080.       osave[osavep++] = out;
  1081.       *out++ = *path++;
  1082.     } else
  1083.       *out++ = *path++;
  1084.   }
  1085.   *out++ = *path++;
  1086.  
  1087. #if unix
  1088. { char *e;
  1089.   char dirname[MAXPATHLEN];
  1090.  
  1091.   e = bsave + strlen(bsave) - 1;
  1092.   for( ; *e != '/' && e > bsave; e-- )
  1093.     ;
  1094.   strncpy(dirname, bsave, e-bsave);
  1095.   dirname[e-bsave] = EOS;
  1096.   canoniseDir(dirname);
  1097.   strcat(dirname, e);
  1098.   strcpy(bsave, dirname);
  1099. }
  1100. #endif
  1101.  
  1102.   return bsave;
  1103. }
  1104.  
  1105. #include <ctype.h>
  1106.  
  1107. forwards char    *takeWord P((char **));
  1108. forwards int    ExpandFile P((char *, char **));
  1109.  
  1110. static char *
  1111. takeWord(string)
  1112. char **string;
  1113. { static char wrd[MAXPATHLEN];
  1114.   register char *s = *string;
  1115.   register char *q = wrd;
  1116.   register int left = MAXPATHLEN-1;
  1117.  
  1118.   while( isalnum(*s) || *s == '_' )
  1119.   { if ( --left < 0 )
  1120.     { warning("Variable or user name too long");
  1121.       return (char *) NULL;
  1122.     }
  1123.     *q++ = *s++;
  1124.   }
  1125.   *q = EOS;
  1126.   
  1127.   *string = s;
  1128.   return wrd;
  1129. }
  1130.  
  1131.  
  1132. bool
  1133. expandVars(pattern, expanded)
  1134. char *pattern, *expanded;
  1135. { int size = 0;
  1136.   char c;
  1137.  
  1138.   if ( *pattern == '~' )
  1139.   {
  1140. #if unix
  1141.     static char fred[20];
  1142.     static char fredLogin[MAXPATHLEN];
  1143.     extern struct passwd *getpwnam();
  1144. #endif
  1145.     char *user;
  1146.     char *value;
  1147.     int l;
  1148.  
  1149.     pattern++;
  1150.     user = takeWord(&pattern);
  1151.  
  1152. #if unix
  1153.     if ( user[0] != EOS || (value = getenv("HOME")) == (char *) NULL )
  1154.     { struct passwd *pwent;
  1155.  
  1156.       if ( !streq(fred, user) )
  1157.       { if ( (pwent = getpwnam(user)) == (struct passwd *) NULL )
  1158.       return warning("%s: Unknown user");
  1159.     strcpy(fred, user);
  1160.     strcpy(fredLogin, pwent->pw_dir);
  1161.       }
  1162.       value = fredLogin;
  1163.     }      
  1164. #endif
  1165. #if tos || OS2
  1166.     if ( user[0] != EOS || (value = getenv("HOME")) == (char *) NULL )
  1167.     { value = "/";    /* top directory of current drive */
  1168.     } else
  1169.     { value = PrologPath(value);
  1170.     }
  1171. #endif
  1172.     size += (l = (int) strlen(value));
  1173.     if ( size >= MAXPATHLEN )
  1174.       return warning("Path name too long");
  1175.     strcpy(expanded, value);
  1176.     expanded += l;
  1177.   }
  1178.  
  1179.   for( ;; )
  1180.   { switch( c = *pattern++ )
  1181.     { case EOS:
  1182.     break;
  1183.       case '$':
  1184.     { char *var = takeWord(&pattern);
  1185.       char *value = getenv(var);
  1186.       int l;
  1187.  
  1188.       if ( value == (char *) NULL )
  1189.         return warning("%s: Undefined variable", var);
  1190.       size += (l = (int)strlen(value));
  1191.       if ( size >= MAXPATHLEN )
  1192.         return warning("Path name too long");
  1193.       strcpy(expanded, value);
  1194.       expanded += l;
  1195.  
  1196.       continue;
  1197.     }
  1198.       default:
  1199.     if ( ++size >= MAXPATHLEN )
  1200.       return warning("Path name too long");
  1201.     *expanded++ = c;
  1202.  
  1203.     continue;
  1204.     }
  1205.     break;
  1206.   }
  1207.  
  1208.   if ( ++size >= MAXPATHLEN )
  1209.     return warning("Path name too long");
  1210.   *expanded++ = EOS;
  1211.  
  1212.   succeed;
  1213. }
  1214.  
  1215.  
  1216. static int
  1217. ExpandFile(pattern, vector)
  1218. char *pattern;
  1219. char **vector;
  1220. { static char expanded[MAXPATHLEN];
  1221.   int matches = 0;
  1222.  
  1223.   if ( expandVars(pattern, expanded) == FALSE )
  1224.     return -1;
  1225.   
  1226.   vector[matches++] = expanded;
  1227.  
  1228.   return matches;
  1229. }
  1230.  
  1231.  
  1232. char *
  1233. ExpandOneFile(spec)
  1234. char *spec;
  1235. { static char file[MAXPATHLEN];
  1236.   char *vector[256];
  1237.   
  1238.   switch( ExpandFile(spec, vector) )
  1239.   { case -1:
  1240.     return (char *) NULL;
  1241.     case 0:
  1242.     warning("%s: No match", spec);
  1243.     return (char *) NULL;
  1244.     case 1:
  1245.     strcpy(file, vector[0]);
  1246.     return file;
  1247.     default:
  1248.     warning("%s: Ambiguous", spec);
  1249.     return (char *) NULL;
  1250.   }
  1251. }
  1252.  
  1253.  
  1254. #if unix            /* convert the names to PrologPath before use !! */
  1255. #if hpux || LINUX
  1256. char    *getwd P((char *));
  1257.  
  1258. char *
  1259. getwd(buf)
  1260. char *buf;
  1261. { extern char *getcwd();
  1262.  
  1263.   return getcwd(buf, MAXPATHLEN);
  1264. }
  1265. #else
  1266. extern char *getwd P((char *));
  1267. #endif /* hpux */
  1268. #endif
  1269.  
  1270. #if OS2 && EMX
  1271. char *getwd P((char *));
  1272.  
  1273. char *getwd(buf)       /* the current directory INCLUDING the current drive */
  1274. char *buf;
  1275. { strcpy(buf, PrologPath(_getcwd2(buf, MAXPATHLEN)));
  1276.   return buf;
  1277. }
  1278. #endif /* OS2 */
  1279.  
  1280.  
  1281. #if tos
  1282. char    *getwd P((char *));
  1283.  
  1284. char *
  1285. getwd(buf)
  1286. char *buf;
  1287. { char path[MAXPATHLEN];
  1288.  
  1289.   if ( Dgetpath(path, 0) != 0 )
  1290.   { warning("Can't get current directory: %s", OsError());
  1291.     strcpy(path, "");
  1292.   }
  1293.   sprintf(buf, "%c:%s", Dgetdrv()+'a', PrologPath(path));
  1294.  
  1295.   return buf;
  1296. }
  1297. #endif
  1298.  
  1299. #if unix
  1300. #define isAbsolutePath(p) ( p[0] == '/' )
  1301. #define isRelativePath(p) ( p[0] == '.' )
  1302. #endif
  1303. #if tos
  1304. #define isAbsolutePath(p) ( isLetter(p[0]) && p[1] == ':' )
  1305. #define isRelativePath(p) ( p[0] == '.' || p[0] == '/' || p[0] == '\\' )
  1306. #endif
  1307. #if OS2
  1308. #define isAbsolutePath(p) (p[0] == '/' && isLetter(p[1]) && \
  1309.                p[2] == ':' && p[3] == '/' )
  1310. #define isDriveRelativePath(p) (p[0] == '/' && p[2] != ':')
  1311. #define isRootlessPath(p) (p[0] == '/' && isLetter(p[1]) && \
  1312.                p[2] == ':' && p[3] != '/')
  1313. #define isRelativePath(p) (p[0] == '.')
  1314. #endif /* OS2 */
  1315.  
  1316. /*
  1317.   Design Note -- atoenne --
  1318.   AbsoluteFile may only be called with a proper PrologPath. Otherwise the
  1319.   canonisePath will not work.
  1320. */
  1321.  
  1322. char *
  1323. AbsoluteFile(spec)
  1324. char *spec;
  1325. { static char path[MAXPATHLEN];
  1326.   char *file;  
  1327.  
  1328.   if ( (file = ExpandOneFile(spec)) == (char *) NULL )
  1329.     return (char *) NULL;
  1330.  
  1331.   if ( isAbsolutePath(file) )
  1332.   { strcpy(path, file);
  1333.  
  1334.     return canonisePath(path);
  1335.   }
  1336. #if OS2 && EMX
  1337.   if (isDriveRelativePath(file))
  1338.   {
  1339.     if ((strlen(file) + 4) > MAXPATHLEN)
  1340.     {
  1341.       warning("path name too long");
  1342.       return (char *) NULL;
  1343.     }
  1344.     path[0] = '/';
  1345.     path[1] = (char) _getdrive();
  1346.     path[2] = ':';
  1347.     strcpy(&path[3], file);
  1348.     return canonisePath(path);
  1349.   }
  1350. #endif /* OS2 */
  1351.   if ( CWDdir[0] == EOS )
  1352.   {
  1353.     getwd(CWDdir);
  1354.   }
  1355.  
  1356. #if OS2 && EMX
  1357.   if (isRootlessPath(file))
  1358.   {
  1359.     if ((strlen(CWDdir) + strlen(file) - 2) >= MAXPATHLEN)
  1360.     {
  1361.       warning("path name too long");
  1362.       return (char *) NULL;
  1363.     }
  1364.     strcpy(path, CWDdir);
  1365.     strcat(path, "/");
  1366.     strcat(path, &file[3]);
  1367.     return canonisePath(path);
  1368.   }
  1369. #endif /* OS2 */
  1370.   if ( (strlen(CWDdir) + strlen(file) + 2) >= MAXPATHLEN )
  1371.   { warning("path name too long");
  1372.     return (char *) NULL;
  1373.   }
  1374.   
  1375.   strcpy(path, CWDdir);
  1376.   strcat(path, "/");
  1377.   strcat(path, file);
  1378.  
  1379.   return canonisePath(path);
  1380. }
  1381.  
  1382.  
  1383. char *
  1384. BaseName(f)
  1385. register char *f;
  1386. { register char *base;
  1387.  
  1388.   for(base = f; *f; f++)
  1389.     if (*f == '/')
  1390.       base = f+1;
  1391.  
  1392.   return base;
  1393. }
  1394.  
  1395. char *
  1396. DirName(f)
  1397. char *f;
  1398. { static char dir[MAXPATHLEN];
  1399.   char *base, *p;
  1400.  
  1401.   for(base = p = f; *p; p++)
  1402.     if (*p == '/' && p[1] != EOS )
  1403.       base = p;
  1404.   strncpy(dir, f, base-f);
  1405.   dir[base-f] = EOS;
  1406.   
  1407.   return dir;
  1408. }
  1409.  
  1410. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1411.     bool ChDir(path)
  1412.      char *path;
  1413.  
  1414.     Change the current working directory to `path'.  File names may depend
  1415.     on `path'.
  1416. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1417.  
  1418. bool
  1419. ChDir(path)
  1420. char *path;
  1421. { extern int chdir(/*char**/);
  1422.   char *ospath = OsPath(path);
  1423.  
  1424.   if ( ospath[0] == EOS ||
  1425.        streq(ospath, CWDdir) ||
  1426.        streq(ospath, ".") )        /* Same directory */
  1427.     succeed;
  1428.  
  1429.   if ( chdir(ospath) == 0 )
  1430.   { CWDdir[0] = EOS;
  1431.     succeed;
  1432.   }
  1433.  
  1434.   fail;
  1435. }
  1436.  
  1437. #if minix || tos
  1438. long
  1439. getw(fd)
  1440. register FILE *fd;
  1441. { register ulong r;
  1442.  
  1443.   r = getc(fd);
  1444.   r = (r << 8) | getc(fd);
  1445.   r = (r << 8) | getc(fd);
  1446.   r = (r << 8) | getc(fd);
  1447.  
  1448.   return r;
  1449. }
  1450.  
  1451. long
  1452. putw(l, fd)
  1453. long l;
  1454. FILE *fd;
  1455. { putc((char)((l >> 24) & 0xff), fd);
  1456.   putc((char)((l >> 16) & 0xff), fd);
  1457.   putc((char)((l >>  8) & 0xff), fd);
  1458.   putc((char)((l)       & 0xff), fd);
  1459.  
  1460.   return l;
  1461. }
  1462. #endif /* minix || tos */
  1463.  
  1464.         /********************************
  1465.         *        TIME CONVERSION        *
  1466.         *********************************/
  1467.  
  1468. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1469.     struct tm *LocalTime(time)
  1470.           long *time;
  1471.  
  1472.     Convert time in Unix internal form (seconds since Jan 1 1970) into a
  1473.     structure providing easier access to the time.
  1474.  
  1475.     For non-Unix systems: struct time is supposed  to  look  like  this.
  1476.     Move  This  definition to pl-os.h and write the conversion functions
  1477.     here.
  1478.  
  1479.     struct tm {
  1480.     int    tm_sec;        / * second in the minute (0-59)* /
  1481.     int    tm_min;        / * minute in the hour (0-59) * /
  1482.     int    tm_hour;    / * hour of the day (0-23) * /
  1483.     int    tm_mday;    / * day of the month (1-31) * /
  1484.     int    tm_mon;        / * month of the year (1-12) * /
  1485.     int    tm_year;    / * year (0 = 1900) * /
  1486.     int    tm_wday;    / * day in the week (1-7, 1 = sunday) * /
  1487.     int    tm_yday;    / * day in the year (0-365) * /
  1488.     int    tm_isdst;    / * daylight saving time info * /
  1489.     };
  1490.  
  1491.     long Time()
  1492.  
  1493.     Return time in seconds after Jan 1 1970 (Unix' time notion).
  1494. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1495.  
  1496. struct tm *
  1497. LocalTime(t)
  1498. long *t;
  1499. { extern struct tm *localtime();
  1500.  
  1501.   return localtime(t);
  1502. }
  1503.  
  1504. long
  1505. Time()
  1506. {
  1507. #if !EMX
  1508. extern long time();
  1509. #endif
  1510.  
  1511.   return (long)time((time_t *) NULL);
  1512. }
  1513.  
  1514. #if tos
  1515. void
  1516. gettimeofday(tz, p)
  1517. struct timeval *tz;
  1518. void *p;
  1519. { tz->tv_usec = 0;
  1520.   tz->tv_sec  = Time();
  1521. }
  1522. #endif
  1523.  
  1524.         /********************************
  1525.         *        TERMINAL CONTROL       *
  1526.         *********************************/
  1527.  
  1528. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1529. Terminal control probably is the biggest mess.  One day (v7) things used
  1530. to be simple.  New features made a mess of it.  System V then defined  a
  1531. much  more powerful terminal interface using a single control structure.
  1532. Unfortunately at this moment every Unix system seems  to  have  its  own
  1533. driver:  v7  drivers, various extended versions of this and the System V
  1534. drivers.
  1535.  
  1536. SWI-Prolog uses the following terminal modes:
  1537.  
  1538.   - If it wants to know what action to perform when in trace mode or has
  1539.     trapped a signal (^C). In this  case  it  wants  the  input  without
  1540.     waiting for a return and without echoing.
  1541.  
  1542.   - If it is reading a Prolog term from the terminal.  In this  case  it
  1543.     wants  to  trap ESC and EOF without waiting for a return in order to
  1544.     do `atom-completion': finishing atoms after a unique prefix has been
  1545.     typed by the user.  This mode askes for some sub-modes to let Prolog
  1546.     act as if the characters are typed by the user.
  1547.  
  1548.   - Prolog assumes the terminal is initialy in  `COOKED'  mode:  the  OS
  1549.     reads  the characters from the terminal and allows for line editing.
  1550.     The line is passed as a whole if the user hits return.
  1551.  
  1552. As we usualy want to go back to the previous mode two  functions  should
  1553. be provided by this layer of the terminal driver:
  1554.  
  1555.     void PushTty(buf, mode)
  1556.      ttybuf *buf;
  1557.      int mode;
  1558.  
  1559.     Save the current settings in `buf' and switch to mode  `mode'.   The
  1560.     type ttybuf should be defined in pl-os.h. Modes:
  1561.  
  1562.      TTY_SAVE        Only save current setting: do not change
  1563.      TTY_RAW        Non-Echoing, not waiting for return
  1564.      TTY_COOKED        Initial mode (asumes echo)
  1565.      TTY_EXTEND_ATOMS    Tty flushes on ESC, EOF and NL. Used by
  1566.                 read/1.
  1567.      TTY_RETYPE        Push back characters without showing them
  1568.                 on the terminal.
  1569.      TTY_APPEND        Push back characters, showing them on the
  1570.                 terminal.
  1571.  
  1572.     The  last  three  serve  for  the  atom-completion.   This  is  only
  1573.     interesting  if  you  can  provide a function to fake input from the
  1574.     user by the program.  if this cannot be done you may wish to include
  1575.     the O_LINE_EDIT option, which  makes  Prolog  defines  it's  own  line
  1576.     editing cababilities.
  1577.  
  1578.     void PopTty(buf)
  1579.      ttybuf *buf;
  1580.  
  1581.     Restore the terminal into the mode saved in buf by a PushTty() call.
  1582. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1583.  
  1584. #if O_LINE_EDIT
  1585. #define CRLF        (0x0001)    /* map CR -> LF */
  1586. #define Empty(d)    ( (d)->in == (d)->out )
  1587. #define Advance(p)    { if ( ++(p) == QSIZE ) (p) = 0; }
  1588. #define Retreat(p)    { if ( --(p) < 0 ) (p) = QSIZE-1; }
  1589.  
  1590. struct tty_driver stdin_driver =
  1591. { Control('W'), Control('U'), Control('?'), Control('H'),
  1592.   Control('D'), Control('['), Control('R'), Control('C'),
  1593.   TTY_EXTEND_ATOMS,        /* mode */
  1594.   0,            /* no lines yet */
  1595.   TRUE,            /* line 0 is a tty */
  1596.   0, 0, 0,        /* colunm, in, out */
  1597.   CRLF            /* flags */
  1598. };
  1599. #endif /* O_LINE_EDIT */
  1600.  
  1601. #if O_TERMIOS                /* System V termio system */
  1602.  
  1603. bool
  1604. PushTty(buf, mode)
  1605. ttybuf *buf;
  1606. int mode;
  1607. { struct termio tio;
  1608.  
  1609.   if ( status.notty )
  1610.     succeed;
  1611.  
  1612.   if ( ioctl(0, TCGETA, &buf->tab) )    /* save the old one */
  1613.     fail;
  1614.   tio = buf->tab;
  1615.   buf->mode = ttymode;
  1616.  
  1617.   if ( mode != TTY_SAVE )
  1618.     ttymode = mode;
  1619.  
  1620. #if O_LINE_EDIT
  1621.   stdin_driver.mode = mode;
  1622. #endif
  1623.  
  1624.   switch( mode )
  1625.   { case TTY_SAVE:
  1626.     succeed;
  1627. #if O_EXTEND_ATOMS
  1628. #if O_LINE_EDIT
  1629.     case TTY_COOKED:
  1630.     case TTY_RAW:
  1631.     tio.c_lflag &= ~(ECHO|ICANON);
  1632.     tio.c_cc[VTIME] = 0, tio.c_cc[VMIN] = 1;
  1633.     break;
  1634.     case TTY_EXTEND_ATOMS:
  1635.     tio.c_lflag &= ~(ICANON|ECHO|ECHOE);
  1636.     tio.c_cc[VTIME] = 0, tio.c_cc[VMIN] = 1;
  1637.     stdin_driver.erase  = tio.c_cc[VERASE];
  1638.     stdin_driver.kill   = tio.c_cc[VKILL];
  1639.     break;
  1640.     case TTY_RETYPE:
  1641.     break;
  1642.     case TTY_APPEND:
  1643.     break;
  1644. #else /* O_LINE_EDIT */
  1645.     case TTY_RAW:
  1646.     tio.c_lflag &= ~(ECHO|ICANON);
  1647.     tio.c_cc[VTIME] = 0, tio.c_cc[VMIN] = 1;
  1648.     break;
  1649.     case TTY_EXTEND_ATOMS:
  1650.     tio.c_cc[VEOF]    = 0;             /* disable EOF */
  1651.     tio.c_cc[VEOL]  = ttytab.tab.c_cc[VEOF]; /* EOF: give alternatives */
  1652.     tio.c_cc[VEOL2]    = ESC;             /* ESC: complete */
  1653.     break;
  1654.     case TTY_RETYPE:
  1655.     tio.c_lflag &= ~ECHO;
  1656.     break;
  1657.     case TTY_APPEND:
  1658.     tio.c_lflag |= ECHO;
  1659.     break;
  1660. #endif /* O_LINE_EDIT */
  1661. #endif /* O_EXTEND_ATOMS */
  1662.     default:
  1663.     sysError("Unknown PushTty() mode: %d", mode);
  1664.     /*NOTREACHED*/
  1665.   }
  1666.  
  1667.   if ( ioctl(0, TCSETA, &tio) )
  1668.     fail;
  1669.  
  1670.   succeed;
  1671. }
  1672.  
  1673.  
  1674. bool
  1675. PopTty(buf)
  1676. ttybuf *buf;
  1677. {
  1678.   if ( status.notty )
  1679.     succeed;
  1680. #if O_LINE_EDIT
  1681.   stdin_driver.mode = buf->mode;
  1682. #endif
  1683.   if ( ioctl(0, TCSETA, &buf->tab) )
  1684.     fail;
  1685.   ttymode = buf->mode;
  1686.  
  1687.   succeed;
  1688. }
  1689.  
  1690. #endif
  1691.  
  1692. #if !O_TERMIOS && unix            /* Unix with (old) sgtty() driver */
  1693.  
  1694. bool
  1695. PushTty(buf, mode)
  1696. ttybuf *buf;
  1697. int mode;
  1698. { struct tchars chrs;
  1699.   struct sgttyb flgs;
  1700.   bool flags_set = FALSE;
  1701.   bool chars_set = FALSE;
  1702.  
  1703.   if ( status.notty )
  1704.     succeed;
  1705.  
  1706.   DEBUG(1, printf("PushTty(0x%x, %d)\n", buf, mode));
  1707.  
  1708.   if ( ioctl(0, TIOCGETP, &buf->tab) ||
  1709.        ioctl(0, TIOCGETC, &buf->chars) )
  1710.   { DEBUG(1, printf("Failed to get terminal parameters: %s\n", OsError()));
  1711.     fail;
  1712.   }
  1713.  
  1714.   flgs = buf->tab;
  1715.   chrs = buf->chars;
  1716.   buf->mode = ttymode;
  1717.  
  1718.   if ( mode != TTY_SAVE )
  1719.     ttymode = mode;
  1720.  
  1721. #if O_LINE_EDIT
  1722.   stdin_driver.mode = mode;
  1723. #endif
  1724.  
  1725.   switch( mode )
  1726.   { case TTY_SAVE:
  1727. #if !O_LINE_EDIT
  1728.     case TTY_COOKED:
  1729. #endif
  1730.     succeed;
  1731. #if O_LINE_EDIT
  1732.     case TTY_COOKED:
  1733. #endif
  1734.     case TTY_RAW:
  1735.     flgs.sg_flags &= ~(ECHO);
  1736.     flgs.sg_flags |= CBREAK;
  1737.     flags_set = TRUE;
  1738.     break;
  1739. #if O_LINE_EDIT
  1740.     case TTY_EXTEND_ATOMS:
  1741.     flgs.sg_flags &= ~(ECHO);
  1742.     flgs.sg_flags |= CBREAK;
  1743.     stdin_driver.erase = flgs.sg_erase;
  1744.     stdin_driver.kill  = flgs.sg_kill;
  1745.     flags_set = TRUE;
  1746.     break;
  1747.     case TTY_RETYPE:
  1748.     case TTY_APPEND:
  1749.     break;
  1750. #else /* O_LINE_EDIT */
  1751.     case TTY_EXTEND_ATOMS:
  1752.     chrs.t_brkc = ESC;        /* ESC, EOF already on 04 */
  1753.     chars_set = TRUE;
  1754.     break;
  1755.     case TTY_RETYPE:
  1756.     flgs.sg_flags &= ~ECHO;
  1757.     flags_set = TRUE;
  1758.     break;
  1759.     case TTY_APPEND:
  1760.     flgs.sg_flags |= ECHO;
  1761.     flags_set = TRUE;
  1762.     break;
  1763. #endif /* O_LINE_EDIT */
  1764.     default:
  1765.     sysError("Unknown PushTty() mode: %d", mode);
  1766.     /*NOTREACHED*/
  1767.   }
  1768.  
  1769.   if ( flags_set )
  1770.     if ( ioctl(0, TIOCSETN, &flgs) != 0 )
  1771.       return warning("Failed to set terminal flags: %s", OsError());
  1772.   if ( chars_set )
  1773.     if ( ioctl(0, TIOCSETC, &chrs) != 0 )
  1774.       return warning("Failed to set terminal characters: %s", OsError());
  1775.  
  1776.   succeed;
  1777. }
  1778.  
  1779.  
  1780. bool
  1781. PopTty(buf)
  1782. ttybuf *buf;
  1783. { if ( status.notty )
  1784.     succeed;
  1785.  
  1786.   if ( ioctl(0, TIOCSETN, &buf->tab) ||
  1787.        ioctl(0, TIOCSETC, &buf->chars) )
  1788.     fail;
  1789.   ttymode = buf->mode;
  1790.  
  1791.   succeed;
  1792. }
  1793. #endif /* unix && !O_TERMIOS */
  1794.  
  1795. #if tos                    /* ATARI_ST, running TOS */
  1796. bool
  1797. PushTty(buf, mode)
  1798. ttybuf *buf;
  1799. int mode;
  1800. { if ( mode != TTY_SAVE )
  1801.     ttymode = mode;
  1802.  
  1803.   switch( mode )
  1804.   { case TTY_SAVE:
  1805.     buf->mode = stdin_driver.mode;
  1806.     succeed;
  1807.     case TTY_RAW:
  1808.     case TTY_EXTEND_ATOMS:
  1809.     case TTY_RETYPE:
  1810.     case TTY_APPEND:
  1811.     stdin_driver.mode = mode;
  1812.     succeed;            /* to be implemented later */
  1813.     default:
  1814.     return sysError("Unknown PushTty() mode: %d", mode);
  1815.     /*NOTREACHED*/
  1816.   }
  1817. }
  1818.  
  1819. bool
  1820. PopTty(buf)
  1821. ttybuf *buf;
  1822. { stdin_driver.mode = buf->mode;
  1823.   ttymode = buf->mode;
  1824.   succeed;
  1825. }
  1826. #endif /* tos */
  1827.  
  1828. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1829.     void ResetTty()
  1830.  
  1831.     Reset terminal to a sensible state after an abort ore restore()
  1832. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1833.  
  1834. void
  1835. ResetTty()
  1836. {
  1837. #ifdef RESET_STDIN
  1838.   RESET_STDIN;
  1839. #else
  1840. #if unix && !LINUX
  1841.   stdin->_ptr = stdin->_base;
  1842.   stdin->_cnt = 0;
  1843. #endif
  1844. #if EMX
  1845.   stdin->ptr = stdin->buffer;
  1846.   stdin->rcount = 0;
  1847.   stdin->wcount = 0;
  1848. #endif
  1849.   clearerr(stdin);
  1850. #endif
  1851.  
  1852. #if O_LINE_EDIT
  1853.   stdin_driver.in = stdin_driver.out = 0;
  1854.   stdin_driver.emitting = 0;
  1855. #if unix || EMX
  1856.   stdin_driver.isatty = isatty(fileno(stdin));
  1857. #endif
  1858. #if tos
  1859.   stdin_driver.isatty = TRUE;        /* how to find out? */
  1860. #endif
  1861. #endif /* O_LINE_EDIT */
  1862. }
  1863.  
  1864. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1865.     void PretendTyped(c)
  1866.          char c;
  1867.  
  1868.     Pretend the user typed character `c'. If the tty mode is  TTY_RETYPE
  1869.     the  character  should  just  be  pushed  in  the  input buffer.  If
  1870.     TTY_APPEND it should also be echoed to the user.
  1871. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1872.  
  1873. #if O_EXTEND_ATOMS && !O_LINE_EDIT
  1874. void
  1875. PretendTyped(c)
  1876. char c;
  1877. { ioctl(0, TIOCSTI, &c);
  1878. }
  1879. #endif /* O_EXTEND_ATOMS */
  1880.  
  1881. #if O_LINE_EDIT
  1882.         /********************************
  1883.         *         LINE EDITING          *
  1884.         *********************************/
  1885.  
  1886. forwards bool    tty_erase P((struct tty_driver *));
  1887. forwards void    tty_werase P((struct tty_driver *));
  1888. forwards void    tty_kill P((struct tty_driver *));
  1889. forwards void    tty_reprint P((struct tty_driver *));
  1890. forwards bool    tty_insert P((struct tty_driver *, Char));
  1891. forwards void    add_char P((struct tty_driver *, Char));
  1892. forwards Char    get_edit_char P((struct tty_driver *));
  1893. forwards bool    tty_endsline P((struct tty_driver *, Char));
  1894. forwards void    tty_putc P((Char));
  1895. forwards void    tty_putstr P((char *));
  1896.  
  1897. #define isPrint(c) ((c >= ' ' && c < 127) || c == '\t' || c == '\n')
  1898.  
  1899. static void
  1900. tty_putc(c)
  1901. Char c;
  1902. {
  1903. #if OS2 && EMX
  1904.   if (c == '\n')
  1905.     putchar('\r');
  1906. #endif
  1907.  
  1908. #if unix || EMX
  1909.   putchar(c);
  1910. #endif
  1911.  
  1912. #if tos
  1913.   if ( c == '\n' )
  1914.     putch('\r');
  1915.   putch(c);
  1916. #endif
  1917. }
  1918.  
  1919. static void
  1920. tty_putstr(s)
  1921. char *s;
  1922. { for( ; *s; s++ )
  1923.     tty_putc(*s);
  1924. }
  1925.  
  1926. static bool
  1927. tty_erase(d)
  1928. register struct tty_driver *d;
  1929. { Char c;
  1930.  
  1931.   if ( Empty(d) )
  1932.     fail;
  1933.  
  1934.   Retreat(d->in);
  1935.   switch( c = d->queue[d->in] )
  1936.   { case '\t':        do
  1937.             { tty_putc('\b');
  1938.               d->column--;
  1939.             } while( d->column % 8 );
  1940.             break;
  1941.     default:        if ( c < ' ' || c == 127 )
  1942.             { tty_putstr("\b\b  \b\b");
  1943.               d->column -= 2;
  1944.             } else if ( c < 127 )
  1945.             { d->column--;
  1946.               tty_putstr("\b \b");
  1947.               break;
  1948.             }
  1949.   }  
  1950.  
  1951.   succeed;
  1952. }
  1953.  
  1954. static void
  1955. tty_werase(d)
  1956. register struct tty_driver *d;
  1957. { int last;
  1958.  
  1959.   do
  1960.   { last = d->in;
  1961.     Retreat(last);
  1962.   } while( isBlank(d->queue[last]) && tty_erase(d) );
  1963.  
  1964.   do
  1965.   { last = d->in;
  1966.     Retreat(last);
  1967.   } while( !isBlank(d->queue[last]) && tty_erase(d) );
  1968. }
  1969.  
  1970. static void
  1971. tty_kill(d)
  1972. register struct tty_driver *d;
  1973. { while( tty_erase(d) )
  1974.    ;
  1975. }
  1976.  
  1977. static bool
  1978. tty_endsline(d, c)
  1979. register struct tty_driver *d;
  1980. Char c;
  1981. { return c == '\n' || c == d->eol || c == d->eol2;
  1982. }
  1983.  
  1984. static bool
  1985. tty_insert(d, c)
  1986. register struct tty_driver *d;
  1987. Char c;
  1988. { d->queue[d->in] = c;
  1989.   Advance(d->in);
  1990.  
  1991.   if ( tty_endsline(d, c) )
  1992.     d->emitting++;
  1993.  
  1994.   if ( d->mode != TTY_RETYPE )
  1995.   { switch(c)
  1996.     { case '\t':
  1997.       tty_putc(c);
  1998.       d->column = ((d->column + 8)/8)*8;
  1999.       break;
  2000.       case '\n':
  2001.       tty_putc(c);
  2002.       d->column = 0;
  2003.       break;
  2004.       default:
  2005.       if ( c < ' ' || c == 127 )
  2006.       { tty_putc('^');
  2007.         tty_putc(c < ' ' ? c + '@' : '?');
  2008.         d->column += 2;
  2009.       } else if ( c < 127 )
  2010.       { tty_putc(c);
  2011.         d->column++;
  2012.       }
  2013.     }
  2014.   }
  2015.  
  2016.   succeed;
  2017. }
  2018.  
  2019. static void
  2020. tty_reprint(d)
  2021. register struct tty_driver *d;
  2022. { int n;
  2023.  
  2024.   tty_putc('\n');
  2025.   for(n = d->out; n != d->in; )
  2026.   { tty_putc(d->queue[n]);
  2027.     Advance(n)
  2028.   }
  2029. }
  2030.  
  2031. void
  2032. TtyAddChar(c)
  2033. Char c;
  2034. { add_char(&stdin_driver, c);
  2035. }
  2036.  
  2037. static void
  2038. add_char(d, c)
  2039. register struct tty_driver *d;
  2040. Char c;
  2041. { if ( d->flags & CRLF && c == '\r' )
  2042.     c = '\n';
  2043.  
  2044.   if ( c == d->werase )
  2045.     tty_werase(d);
  2046.   else if ( c == d->erase || c == d->erase2 )
  2047.     tty_erase(d);
  2048.   else if ( c == d->kill )
  2049.     tty_kill(d);
  2050.   else if ( c == d->reprint )
  2051.     tty_reprint(d);
  2052.   else if ( c == d->intr )
  2053.   { d->in = d->out = 0;        /* empty queue */
  2054.     d->emitting = 0;
  2055.     interruptHandler();
  2056.   }
  2057.   else
  2058.     tty_insert(d, c);
  2059.  
  2060.   fflush(stdout);        /* needed to get unbuffered output after a */
  2061.                       /* dump on hpux */
  2062. }
  2063.  
  2064. #if unix || EMX
  2065. #define GETC()    do_get_char()
  2066. #endif
  2067. #if tos
  2068. #define GETC()    tos_getch()
  2069.  
  2070. static Char
  2071. tos_getch(void)
  2072. { long t = clock();
  2073.   Char c = getch();
  2074.   wait_ticks += clock() - t;
  2075.  
  2076.   return c == '\r' ? '\n' : c;
  2077. }
  2078. #endif
  2079.  
  2080. static Char
  2081. GetCMap()
  2082. { Char c = GETC();
  2083.  
  2084. #if O_MAP_TAB_ON_ESC
  2085.   if ( c == '\t' )
  2086.     c = ESC;
  2087. #endif
  2088.  
  2089.   return c;
  2090. }
  2091.  
  2092.  
  2093. static Char
  2094. get_edit_char(d)
  2095. register struct tty_driver *d;
  2096. { Char c;
  2097.   
  2098.   if ( status.notty || !d->isatty )
  2099.     return do_get_char();
  2100.  
  2101.   DEBUG(3, printf("entering get_edit_char(); d->in = %d, d->out = %d\n",
  2102.           d->in, d->out));
  2103.   if ( d->mode == TTY_RAW )
  2104.   { if ( Empty(d) )
  2105.       return GetCMap();
  2106.   } else
  2107.   { while( d->emitting == 0 )
  2108.       add_char(d, GetCMap());
  2109.   }
  2110.   
  2111.   c = d->queue[d->out];
  2112.   DEBUG(3, printf("Returning %d (%c) from %d\n", c, c, d->out));
  2113.   Advance(d->out);
  2114.   if ( tty_endsline(d, c) )
  2115.     d->emitting--;    
  2116.  
  2117.   return c;
  2118. }
  2119.  
  2120. #if PROTO
  2121. void
  2122. PretendTyped(char c)
  2123. #else
  2124. void
  2125. PretendTyped(c)
  2126. char c;
  2127. #endif
  2128. { struct tty_driver *d = &stdin_driver;
  2129.  
  2130.   tty_insert(d, c);  
  2131.   d->emitting = FALSE;
  2132. }
  2133.  
  2134. #endif /* O_LINE_EDIT */
  2135.  
  2136. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2137. Read a character.   When using PCE  we  should be  prepared to  handle
  2138. notification correctly here.  Otherwise live is simple.
  2139. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2140.  
  2141. int (*PL_dispatch_events)() = NULL;
  2142.  
  2143. static Char
  2144. do_get_char()
  2145. { Char c;
  2146.  
  2147.   if ( PL_dispatch_events != NULL )
  2148.   { Atom sfn = source_file_name;    /* save over call-back */
  2149.     int  sln = source_line_no;
  2150.  
  2151.     DEBUG(3, printf("do_get_char() --> "));
  2152.     for(;;)
  2153.     { if ( (*PL_dispatch_events)() == PL_DISPATCH_INPUT )
  2154.       { char chr;
  2155.  
  2156.     if (read(0, &chr, 1) == 0)
  2157.       c = EOF;
  2158.     else
  2159.       c = (Char) chr;
  2160.     break;
  2161.       }
  2162.     }
  2163.  
  2164.     source_line_no   = sln;
  2165.     source_file_name = sfn;
  2166.     DEBUG(3, printf("%d (%c) --> ", c, c));
  2167.   } else
  2168.     c = (Char) getchar();
  2169.  
  2170.   return c;
  2171. }
  2172.  
  2173.  
  2174. Char
  2175. GetChar()
  2176. {
  2177. #if O_LINE_EDIT
  2178.   return (Char) get_edit_char(&stdin_driver);
  2179. #else
  2180.   return do_get_char();
  2181. #endif
  2182. }
  2183.  
  2184.         /********************************
  2185.         *      ENVIRONMENT CONTROL      *
  2186.         *********************************/
  2187.  
  2188. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2189. Simple  library  to  manipulate  the  Unix  environment.   The  modified
  2190. environment  will  be  passed  to  child  processes  and the can also be
  2191. requested via getenv/2 from Prolog.  Functions
  2192.  
  2193.     char *Setenv(name, value)
  2194.          char *name, *value;
  2195.     
  2196.     Set the Unix environment variable with name `name'.   If  it  exists
  2197.     its  value  is  changed, otherwise a new entry in the environment is
  2198.     created.  The return value is a pointer to the old value, or NULL if
  2199.     the variable is new.
  2200.  
  2201.     char *Unsetenv(name)
  2202.          char *name;
  2203.  
  2204.     Delete a variable from the environment.  Return  value  is  the  old
  2205.     value, or NULL if the variable did not exist.
  2206. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2207.  
  2208. #if tos
  2209. char **environ;
  2210. #else
  2211. extern char **environ;        /* Unix predefined environment */
  2212. #endif
  2213.  
  2214. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2215. Grow the environment array by one and return the (possibly  moved)  base
  2216. pointer to the new environment.
  2217. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2218.  
  2219. forwards char    **growEnviron P((char**, int));
  2220. forwards char    *matchName P((char *, char *));
  2221. forwards void    setEntry P((char **, char *, char *));
  2222.  
  2223. static char **
  2224. growEnviron(e, amount)
  2225. char **e;
  2226. int amount;
  2227. { static int filled;
  2228.   static int size = -1;
  2229.  
  2230.   if ( amount == 0 )            /* reset after a dump */
  2231.   { size = -1;
  2232.     return e;
  2233.   }
  2234.  
  2235.   if ( size < 0 )
  2236.   { register char **env, **e1, **e2;
  2237.  
  2238.     for(e1=e, filled=0; *e1; e1++, filled++)
  2239.       ;
  2240.     size = ROUND(filled+10+amount, 32);
  2241.     env = (char **)malloc(size * sizeof(char *));
  2242.     for ( e1=e, e2=env; *e1; *e2++ = *e1++ )
  2243.       ;
  2244.     *e2 = (char *) NULL;
  2245.     filled += amount;
  2246.  
  2247.     return env;
  2248.   }
  2249.  
  2250.   filled += amount;
  2251.   if ( filled + 1 > size )
  2252.   { register char **env, **e1, **e2;
  2253.   
  2254.     size += 32;
  2255.     env = (char **)realloc(e, size * sizeof(char *));
  2256.     for ( e1=e, e2=env; *e1; *e2++ = *e1++ )
  2257.       ;
  2258.     *e2 = (char *) NULL;
  2259.     
  2260.     return env;
  2261.   }
  2262.  
  2263.   return e;
  2264. }
  2265.  
  2266. static void
  2267. initEnviron()
  2268. {
  2269. #if tos
  2270.   environ = mainEnv;
  2271. #endif
  2272.   growEnviron(environ, 0);
  2273. }
  2274.  
  2275.  
  2276. static char *
  2277. matchName(e, name)
  2278. register char *e, *name;
  2279. { while( *name && *e == *name )
  2280.     e++, name++;
  2281.  
  2282.   if ( (*e == '=' || *e == EOS) && *name == EOS )
  2283.     return (*e == '=' ? e+1 : e);
  2284.  
  2285.   return (char *) NULL;
  2286. }
  2287.  
  2288.  
  2289. static void
  2290. setEntry(e, name, value)      
  2291. char **e;
  2292. char *name, *value;
  2293. { int l = (int)strlen(name);
  2294.  
  2295.   *e = (char *) malloc(l + strlen(value) + 2);
  2296.   strcpy(*e, name);
  2297.   e[0][l++] = '=';
  2298.   strcpy(&e[0][l], value);
  2299. }
  2300.  
  2301.   
  2302. char *
  2303. Setenv(name, value)
  2304. char *name, *value;
  2305. { char **e;
  2306.   char *v;
  2307.   int n;
  2308.  
  2309.   for(n=0, e=environ; *e; e++, n++)
  2310.   { if ( (v=matchName(*e, name)) != NULL )
  2311.     { if ( !streq(v, value) )
  2312.         setEntry(e, name, value);
  2313.       return v;
  2314.     }
  2315.   }
  2316.   environ = growEnviron(environ, 1);
  2317.   setEntry(&environ[n], name, value);
  2318.   environ[n+1] = (char *) NULL;
  2319.  
  2320.   return (char *) NULL;
  2321. }
  2322.  
  2323.  
  2324. char *
  2325. Unsetenv(name)
  2326. char *name;
  2327. { char **e;
  2328.   char *v;
  2329.   int n;
  2330.  
  2331.   for(n=0, e=environ; *e; e++, n++)
  2332.   { if ( (v=matchName(*e, name)) != NULL )
  2333.     { environ = growEnviron(environ, -1);
  2334.       e = &environ[n];
  2335.       do
  2336.       { e[0] = e[1];
  2337.         e++;
  2338.       } while(*e);
  2339.  
  2340.       return v;
  2341.     }
  2342.   }
  2343.  
  2344.   return (char *) NULL;
  2345. }
  2346.  
  2347.         /********************************
  2348.         *       SYSTEM PROCESSES        *
  2349.         *********************************/
  2350.  
  2351. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2352. Invokation of system commands.  We could have used Unix system() library
  2353. call.  The reason I implemented  it  using  lower  level  primitives  is
  2354. twofold.   First I want to set the environment PROLOGCHILD, which allows
  2355. us to block novice users from invoking ?-  shell.   %  pl,  ....  (which
  2356. happens  often  with new students).  Second I want to close non-terminal
  2357. related I/O in the child process.
  2358.  
  2359.     int System(command)
  2360.     char *command;
  2361.  
  2362.     Invoke a command on the operating system.  The return value  is  the
  2363.     exit  status  of  the  command.   Return  value  0 implies succesful
  2364.     completion. If you are not running Unix your C-library might provide
  2365.     an alternative.
  2366. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2367.  
  2368. #if unix
  2369. #if !v7
  2370. #include <sys/wait.h>
  2371. #endif
  2372.  
  2373. int
  2374. System(cmd)
  2375. char *cmd;
  2376. { int pid;
  2377.   char *shell;
  2378.   ttybuf buf;
  2379.   int rval;
  2380.   void (*old_int)();
  2381.   void (*old_stop)();
  2382.  
  2383.   Setenv("PROLOGCHILD", "yes");
  2384.  
  2385. /*if ((shell = getenv("SHELL")) == (char *)NULL) bourne shell for speed now */
  2386.     shell = "/bin/sh";
  2387.  
  2388.   PushTty(&buf, TTY_SAVE);
  2389.   PopTty(&ttytab);            /* restore cooked mode */
  2390.  
  2391.   if ( (pid = vfork()) == -1 )
  2392.   { return warning("Fork failed: %s\n", OsError());
  2393.   } else if ( pid == 0 )        /* The child */
  2394.   { int i;
  2395.  
  2396.     for(i = 3; i < GetDTableSize(); i++)
  2397.       close(i);
  2398.     stopItimer();
  2399.  
  2400.     execl(shell, BaseName(shell), "-c", cmd, (char *)0);
  2401.     fatalError("Failed to execute %s: %s", shell, OsError());
  2402.     fail;
  2403.     /*NOTREACHED*/
  2404.   } else
  2405. #if v7 || hpux
  2406.   { int waitstat, retstat;        /* the parent */
  2407.  
  2408.     old_int  = signal(SIGINT,  SIG_IGN);
  2409.     old_stop = signal(SIGTSTP, SIG_DFL);
  2410.  
  2411.     while( (waitstat = Wait(&retstat)) != pid && waitstat != -1 )
  2412.       ;
  2413.     if ( waitstat == -1 )
  2414.     {  warning("Failed to execute %s", cmd);
  2415.        rval = 1;
  2416.     }
  2417.  
  2418.     rval = retstat;
  2419.   }
  2420. #else /* v7 */
  2421.   { union wait status;            /* the parent */
  2422.     int n;
  2423.  
  2424.     old_int  = signal(SIGINT,  SIG_IGN);
  2425.     old_stop = signal(SIGTSTP, SIG_DFL);
  2426.  
  2427.     while((n = Wait(&status)) != -1 && n != pid);
  2428.     if (n == -1)
  2429.     { warning("Failed to execute %s", cmd);
  2430.       rval = 1;
  2431.     } else if (WIFEXITED(status))
  2432.     { rval = status.w_retcode;
  2433.     } else if (WIFSIGNALED(status))
  2434.     { warning("Child %s catched signal %d\n", cmd, status.w_termsig);
  2435.       rval = 1;
  2436.     } else
  2437.     { rval = 1;                /* make gcc happy */
  2438.       fatalError("Unknown return code from wait(3)");
  2439.       /*NOTREACHED*/
  2440.     }
  2441.   }
  2442. #endif /* v7 */
  2443.  
  2444.   signal(SIGINT,  old_int);        /* restore signal handlers */
  2445.   signal(SIGTSTP, old_stop);
  2446.   PopTty(&buf);
  2447.  
  2448.   return rval;
  2449. }
  2450. #endif /* unix */
  2451.  
  2452. #if tos
  2453. #include <aes.h>
  2454.  
  2455. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2456. The routine system_via_shell() has been written by Tom Demeijer.  Thanks!
  2457. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2458.  
  2459. #define _SHELL_P ((long *)0x4f6L)
  2460. #define SHELL_OK (do_sys != 0)
  2461.  
  2462. int cdecl (*do_sys)(const char *cmd); /* Parameter on stack ! */
  2463.  
  2464. static int
  2465. system_via_shell(const char *cmd)
  2466. { long oldssp;
  2467.  
  2468.   oldssp = Super((void *)0L);
  2469.   do_sys = (void (*))*_SHELL_P;
  2470.   Super((void *)oldssp);
  2471.  
  2472.   if(cmd==NULL && SHELL_OK)
  2473.     return 0;
  2474.  
  2475.   if (SHELL_OK)
  2476.     return do_sys(cmd);
  2477.  
  2478.   return -1;
  2479. }
  2480.  
  2481. int
  2482. System(command)
  2483. char *command;
  2484. { char       path[MAXPATHLEN];
  2485.   char       *cmd_path;
  2486.   COMMAND  commandline;
  2487.   char       *s, *q;
  2488.   int       status, l;
  2489.   char       *cmd = command;
  2490.  
  2491.   if ( (status = system_via_shell(command)) != -1 )
  2492.   { printf("\033e");        /* get cursor back */
  2493.  
  2494.     return status;
  2495.   }
  2496.  
  2497.     /* get the name of the executable and store in path */
  2498.   for(s=path; *cmd != EOS && !isBlank(*cmd); *s++ = *cmd++)
  2499.     ;
  2500.   *s = EOS;
  2501.   if ( (cmd_path = Which(path)) == NULL )
  2502.   { warning("%s: command not found", path);
  2503.     return 1;
  2504.   }
  2505.  
  2506.     /* copy the command in commandline */
  2507.   while( isBlank(*cmd) )
  2508.     cmd++;
  2509.  
  2510.   for(l = 0, s = cmd, q = commandline.command_tail; *s && l <= 126; s++ )
  2511.   { if ( *s != '\'' )
  2512.     { *q++ = (*s == '/' ? '\\' : *s);
  2513.       l++;
  2514.     }
  2515.   }
  2516.   commandline.length = l;
  2517.   *q = EOS;
  2518.   
  2519.     /* execute the command */
  2520.   if ( (status = (int) Pexec(0, OsPath(cmd_path), &commandline, NULL)) < 0 )
  2521.   { warning("Failed to execute %s: %s", command, OsError());
  2522.     return 1;
  2523.   }
  2524.  
  2525.     /* clean up after a graphics application */
  2526.   if ( strpostfix(cmd_path, ".prg") || strpostfix(cmd_path, ".tos") )
  2527.   { graf_mouse(M_OFF, NULL);        /* get rid of the mouse */
  2528.     printf("\033e\033E");        /* clear screen and get cursor */
  2529.   }  
  2530.  
  2531.   return status;
  2532. }
  2533. #endif
  2534.  
  2535. /* OS/2 does not have the same problems as Unix(tm). We simply fire up a
  2536.    shell that does the job.
  2537. */
  2538.  
  2539. #if OS2 && EMX
  2540. int
  2541. System(command)
  2542. char *command;
  2543. {
  2544.   return system(command);
  2545. }
  2546. #endif /* OS2 */
  2547.  
  2548. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2549.     char *Symbols()
  2550.  
  2551.     Return the path name of the executable of SWI-Prolog. Used by the -c
  2552.     compiler to generate the #!<path> header line and by the incremental
  2553.     loader, who gives this path to ld, using ld -A <path>.
  2554. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2555.  
  2556. #if unix || EMX
  2557. char *
  2558. Symbols()
  2559. { return Which(PrologPath(mainArgv[0]));
  2560. }
  2561. #endif
  2562.  
  2563. #if tos
  2564. char *
  2565. Symbols()
  2566. { return "pl1.5";
  2567. }
  2568. #endif
  2569.  
  2570. #if unix
  2571. static char *
  2572. okToExec(s)
  2573. char *s;
  2574. { struct stat stbuff;
  2575. #if !LINUX
  2576.   extern int access(/*char *, int*/);
  2577. #endif
  2578.  
  2579.   if (stat(s, &stbuff) == 0 &&            /* stat it */
  2580.      (stbuff.st_mode & S_IFMT) == S_IFREG &&    /* check for file */
  2581.      access(s, X_OK) == 0)            /* can be executed? */
  2582.     return s;
  2583.   else
  2584.     return (char *) NULL;
  2585. }
  2586.  
  2587. #define PATHSEP    ':'
  2588. #endif /* unix */
  2589.  
  2590. #if tos
  2591. static char *
  2592. okToExec(s)
  2593. char *s;
  2594. { static char path[MAXPATHLEN];
  2595.  
  2596.   DEBUG(2, printf("Checking %s\n", s));
  2597.   if ( strpostfix(s, ".ttp" ) || strpostfix(s, ".prg") )
  2598.     return ExistsFile(s) ? s : (char *) NULL;
  2599.  
  2600.   strcpy(path, s);
  2601.   strcat(path, ".ttp");
  2602.   DEBUG(2, printf("Checking %s\n", path));
  2603.   if ( ExistsFile(path) == TRUE )
  2604.     return path;
  2605.   strcpy(path, s);
  2606.   strcat(path, ".prg");
  2607.   DEBUG(2, printf("Checking %s\n", path));
  2608.   if ( ExistsFile(path) == TRUE )
  2609.     return path;
  2610.   return (char *) NULL;
  2611. }
  2612.  
  2613. #define PATHSEP ','
  2614. #endif /* tos */
  2615.  
  2616. #if OS2 && EMX
  2617. static char *
  2618. okToExec(s)
  2619. char *s;
  2620. { static char path[MAXPATHLEN];
  2621.  
  2622.   DEBUG(2, printf("Checking %s\n", s));
  2623.   if (strpostfix(s, ".exe") ||
  2624.       strpostfix(s, ".com") ||
  2625.       strpostfix(s, ".bat") ||
  2626.       strpostfix(s, ".cmd"))
  2627.     return ExistsFile(s) ? s : (char *) NULL;
  2628.  
  2629.   strcpy(path, s);
  2630.   strcat(path, ".exe");
  2631.   DEBUG(2, printf("Checking %s\n", path));
  2632.   if ( ExistsFile(path) == TRUE )
  2633.     return path;
  2634.   strcpy(path, s);
  2635.   strcat(path, ".com");
  2636.   DEBUG(2, printf("Checking %s\n", path));
  2637.   if ( ExistsFile(path) == TRUE )
  2638.     return path;
  2639.   strcpy(path, s);
  2640.   strcat(path, ".cmd");
  2641.   DEBUG(2, printf("Checking %s\n", path));
  2642.   if ( ExistsFile(path) == TRUE )
  2643.     return path;
  2644.   strcpy(path, s);
  2645.   strcat(path, ".bat");
  2646.   DEBUG(2, printf("Checking %s\n", path));
  2647.   if ( ExistsFile(path) == TRUE )
  2648.     return path;
  2649.   return (char *) NULL;
  2650. }
  2651.  
  2652. #define PATHSEP ';'
  2653. #endif /* OS2 */
  2654.  
  2655. static char *
  2656. Which(program)
  2657. char *program;
  2658. { static char fullname[MAXPATHLEN];
  2659.   char *path, *dir;
  2660.   char *e;
  2661.  
  2662.   if ( isAbsolutePath(program) ||
  2663. #if OS2 && EMX
  2664.        isDriveRelativePath(program) ||
  2665. #endif /* OS2 */
  2666.        isRelativePath(program) ||
  2667.        index(program, '/') )
  2668.   { if ( (e = okToExec(program)) != NULL )
  2669.     { strcpy(fullname, e);
  2670.       
  2671.       return fullname;
  2672.     }
  2673.  
  2674.     return NULL;
  2675.   }
  2676.  
  2677. #if OS2 && EMX
  2678.   if ((e = okToExec(program)) != NULL)
  2679.   {
  2680.     getwd(fullname);
  2681.     strcat(fullname, "/");
  2682.     strcat(fullname, e);
  2683.     return fullname;
  2684.   }
  2685. #endif /* OS2 */
  2686.   if  ((path = getenv("PATH") ) == 0)
  2687.     path = DEFAULT_PATH;
  2688.  
  2689.   while(*path)
  2690.   { if ( *path == PATHSEP )
  2691.     { if ( (e = okToExec(program)) != NULL)
  2692.       { strcpy(fullname, e);
  2693.  
  2694.         return fullname;
  2695.       } else
  2696.         return NULL;
  2697.     } else
  2698.     { for(dir = fullname; *path && *path != PATHSEP; *dir++ = *path++)
  2699.     ;
  2700.       if (*path)
  2701.     path++;                        /* skip : */
  2702.       if (strlen(fullname) + strlen(program)+2 > MAXPATHLEN)
  2703.         continue;
  2704.       *dir++ = '/';
  2705.       *dir = EOS;
  2706.       strcpy(dir, program);
  2707.       if ( (e = okToExec(OsPath(fullname))) != NULL )
  2708.     return e;
  2709.     }
  2710.   }
  2711.  
  2712.   return NULL;
  2713. }
  2714.  
  2715.  
  2716. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2717.     void Sleep(time)
  2718.      real time;
  2719.  
  2720.     Suspend execution `time' seconds.   Time  is  given  as  a  floating
  2721.     point,  expressing  the  time  to sleep in seconds.
  2722. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2723.  
  2724. #if unix
  2725. #if minix            /* v7 unix does not have fine granularity */
  2726. void                /* timer. Just use sleep() */
  2727. Sleep(time)
  2728. real time;
  2729. { if ( time < 0.5 )
  2730.     return;
  2731.   sleep( (int)(time+0.5) );
  2732. }
  2733.  
  2734. #else /* has select() */
  2735.  
  2736. void
  2737. Sleep(time)            /* system has select() */
  2738. real time;
  2739. { struct timeval timeout;
  2740.  
  2741.   if ( time <= 0.0 )
  2742.     return;
  2743.  
  2744.   if ( time < 60.0 )        /* select() is expensive. Does it make sense */
  2745.   { timeout.tv_sec = (int) time;
  2746.     timeout.tv_usec = (int)(time * 1000000) % 1000000;
  2747.     select(32, NULL, NULL, NULL, &timeout);
  2748.   } else
  2749.     sleep( (int)(time+0.5) );
  2750. }
  2751. #endif /* has select() */
  2752. #endif /* unix */
  2753.  
  2754. #if OS2 && EMX                  /* the OS/2 API call for DosSleep allows */
  2755. void                            /* a millisecond granualrity. */
  2756. Sleep(time)                     /* the EMX function sleep uses a seconds */
  2757. real time;                      /* granularity only. */
  2758. {                               /* the select() trick does not work at all. */
  2759.   if ( time <= 0.0 )
  2760.     return;
  2761.  
  2762.   DosSleep((ULONG)(time * 1000));
  2763. }
  2764. #endif /* OS2 */
  2765.  
  2766. #if tos
  2767. void
  2768. Sleep(t)
  2769. real t;
  2770. { long wait = (long)(t * 200.0);
  2771.   long start_tick = clock();
  2772.   long end_tick = wait + start_tick;
  2773.  
  2774.   while( clock() < end_tick )
  2775.   { if ( kbhit() )
  2776.     { wait_ticks += clock() - start_tick;
  2777.       start_tick = clock();
  2778.       TtyAddChar(getch());
  2779.     }
  2780.   }
  2781.  
  2782.   wait_ticks += end_tick - start_tick;
  2783. }
  2784. #endif
  2785.