system_xsb.c

00001 /* File:      system_xsb.c
00002 ** Author(s): David S. Warren, Jiyang Xu, Kostis F. Sagonas, kifer
00003 ** Contact:   xsb-contact@cs.sunysb.edu
00004 ** 
00005 ** Copyright (C) The Research Foundation of SUNY, 1986, 1993-1999
00006 ** Copyright (C) ECRC, Germany, 1990
00007 ** 
00008 ** XSB is free software; you can redistribute it and/or modify it under the
00009 ** terms of the GNU Library General Public License as published by the Free
00010 ** Software Foundation; either version 2 of the License, or (at your option)
00011 ** any later version.
00012 ** 
00013 ** XSB is distributed in the hope that it will be useful, but WITHOUT ANY
00014 ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00015 ** FOR A PARTICULAR PURPOSE.  See the GNU Library General Public License for
00016 ** more details.
00017 ** 
00018 ** You should have received a copy of the GNU Library General Public License
00019 ** along with XSB; if not, write to the Free Software Foundation,
00020 ** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00021 **
00022 ** $Id: system_xsb.c,v 1.45 2005/11/16 17:32:05 dwarren Exp $
00023 ** 
00024 */
00025 
00026 #include "xsb_config.h"
00027 
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <signal.h>
00031 #include <string.h>
00032 #include <sys/types.h>
00033 #include <sys/stat.h>
00034 #include <errno.h>
00035 #include <ctype.h>
00036 
00037 #ifdef WIN_NT
00038 #include <windows.h>
00039 #include <direct.h>
00040 #include <io.h>
00041 #include <process.h>
00042 #include <winbase.h>
00043 #else
00044 #include <unistd.h>     
00045 #include <stddef.h>
00046 #include <sys/wait.h>
00047 #include <dirent.h>
00048 #endif
00049 
00050 #include <fcntl.h>
00051 
00052 #include "auxlry.h"
00053 #include "cell_xsb.h"
00054 #include "error_xsb.h"
00055 #include "cinterf.h"
00056 #include "syscall_xsb.h"
00057 #include "io_builtins_xsb.h"
00058 /* wind2unix.h must be included after sys/stat.h */
00059 #include "wind2unix.h"
00060 #include "system_xsb.h"
00061 #include "system_defs_xsb.h"
00062 #include "register.h"
00063 #include "psc_xsb.h"
00064 #include "memory_xsb.h"
00065 
00066 #define MAX_CMD_LEN 1024
00067 
00068 static int xsb_spawn (char *prog, char *arg[], int callno,
00069                       int pipe1[], int pipe2[], int pipe3[],
00070                       FILE *toprocess_fptr, FILE *fromprocess_fptr,
00071                       FILE *fromproc_stderr_fptr);
00072 static void concat_array(char *array[], char *separator,
00073                          char *result_str, int maxsize);
00074 static int get_free_process_cell(void);
00075 static void init_process_table(void);
00076 static int process_status(int pid);
00077 static void split_command_arguments(char *string, char *params[], char *callname);
00078 static char *get_next_command_argument(char **buffptr, char **cmdlineprt);
00079 static int file_copy(char *, char *);
00080 static int copy_file_chunk(FILE *, FILE *, unsigned long);
00081 #ifndef WIN_NT
00082 static char *xreadlink(const char *, int *);
00083 #endif
00084 
00085 static struct proc_table_t {
00086   int search_idx;              /* index where to start search for free cells */
00087   struct proc_array_t {
00088     int pid;
00089     int to_stream;             /* XSB stream to process stdin    */
00090     int from_stream;           /* XSB stream from process stdout */
00091     int stderr_stream;         /* XSB stream from process stderr */
00092     char cmdline[MAX_CMD_LEN];  /* the cmd line used to invoke the process */
00093   } process[MAX_SUBPROC_NUMBER];
00094 } xsb_process_table;
00095 
00096 static xsbBool file_stat(CTXTdeclc int callno, char *file);
00097 
00098 static int xsb_find_first_file(CTXTdeclc prolog_term, char*, prolog_term);
00099 static int xsb_find_next_file(CTXTdeclc prolog_term, char*, prolog_term);
00100 
00101 int sys_syscall(CTXTdeclc int callno)
00102 {
00103   int result=-1;
00104   struct stat stat_buff;
00105 
00106   switch (callno) {
00107   case SYS_exit: {
00108     int exit_code;
00109     exit_code = ptoc_int(CTXTc 3);
00110     xsb_mesg("\nXSB exited with exit code: %d", exit_code);
00111     exit(exit_code); break;
00112   }
00113 #if (!defined(WIN_NT))
00114   case SYS_getpid : result = getpid(); break; 
00115   case SYS_link  : result = link(ptoc_longstring(CTXTc 3), ptoc_longstring(CTXTc 4)); break;
00116 #endif
00117   case SYS_mkdir: {
00118 #ifndef WIN_NT
00119     /* create using mode 700 */
00120     result = mkdir(ptoc_longstring(CTXTc 3), 0700); 
00121 #else
00122     result = _mkdir(ptoc_longstring(CTXTc 3)); 
00123 #endif
00124     break;
00125   }
00126   case SYS_rmdir: {
00127 #ifndef WIN_NT
00128     result = rmdir(ptoc_longstring(CTXTc 3)); 
00129 #else
00130     result = _rmdir(ptoc_longstring(CTXTc 3)); 
00131 #endif
00132     break;
00133   }
00134   case SYS_unlink: result = unlink(ptoc_longstring(CTXTc 3)); break;
00135   case SYS_chdir : result = chdir(ptoc_longstring(CTXTc 3)); break;
00136   case SYS_access: {
00137     switch(*ptoc_string(CTXTc 4)) {
00138     case 'r': /* read permission */
00139       result = access(ptoc_longstring(CTXTc 3), R_OK_XSB);
00140       break;
00141     case 'w': /* write permission */
00142       result = access(ptoc_longstring(CTXTc 3), W_OK_XSB);
00143       break;
00144     case 'x': /* execute permission */
00145       result = access(ptoc_longstring(CTXTc 3), X_OK_XSB);
00146       break;
00147     default:
00148       result = -1;
00149     }
00150     break;
00151   }
00152   case SYS_stat  : {
00153     /* Who put this in??? What did s/he expect to get out of this call?
00154        stat_buff is never returned (and what do you do with it in Prolog?)!!!
00155     */
00156     result = stat(ptoc_longstring(CTXTc 3), &stat_buff);
00157     break;
00158   }
00159   case SYS_rename: 
00160     result = rename(ptoc_longstring(CTXTc 3), ptoc_longstring(CTXTc 4)); 
00161     break;
00162   case SYS_cwd: {
00163     char current_dir[MAX_CMD_LEN];
00164     /* returns 0, if != NULL, 1 otherwise */
00165     result = (getcwd(current_dir, MAX_CMD_LEN-1) == NULL);
00166     if (result == 0)
00167       ctop_string(CTXTc 3,string_find(current_dir,1));
00168     break;
00169   }
00170   case SYS_filecopy: {
00171     char *from = ptoc_longstring(CTXTc 3);
00172     char *to = ptoc_longstring(CTXTc 4);
00173     result = file_copy(from,to);
00174     break;
00175   }
00176   case SYS_create: {
00177     result = open(ptoc_longstring(CTXTc 3),O_CREAT|O_EXCL,S_IREAD|S_IWRITE);
00178     if (result >= 0) close(result);
00179     break;
00180   }
00181 
00182   default: xsb_abort("[SYS_SYSCALL] Unknown system call number, %d", callno);
00183   }
00184   return result;
00185 }
00186 
00187 xsbBool sys_system(CTXTdeclc int callno)
00188 {
00189   int pid;
00190 
00191   switch (callno) {
00192   case PLAIN_SYSTEM_CALL: /* dumb system call: no communication with XSB */
00193     /* this call is superseded by shell and isn't used */
00194     ctop_int(CTXTc 3, system(ptoc_string(CTXTc 2)));
00195     return TRUE;
00196   case SLEEP_FOR_SECS:
00197 #ifdef WIN_NT
00198     Sleep(ptoc_int(CTXTc 2) * 1000);
00199 #else
00200     sleep(ptoc_int(CTXTc 2));
00201 #endif
00202     return TRUE;
00203   case GET_TMP_FILENAME:
00204     ctop_string(CTXTc 2,string_find(tmpnam(NULL),1));
00205     return TRUE;
00206   case IS_PLAIN_FILE:
00207   case IS_DIRECTORY:
00208   case STAT_FILE_TIME:
00209   case STAT_FILE_SIZE:
00210     return file_stat(CTXTc callno, ptoc_longstring(CTXTc 2));
00211   case EXEC: {
00212 #ifdef HAVE_EXECVP
00213     /* execs a new process in place of XSB */
00214     char *params[MAX_SUBPROC_PARAMS+2];
00215     prolog_term cmdspec_term;
00216     int index = 0;
00217     
00218     cmdspec_term = reg_term(CTXTc 2);
00219     if (islist(cmdspec_term)) {
00220       prolog_term temp, head;
00221       char *string_head=NULL;
00222 
00223       if (isnil(cmdspec_term))
00224         xsb_abort("[exec] Arg 1 must not be an empty list.");
00225       
00226       temp = cmdspec_term;
00227       do {
00228         head = p2p_car(temp);
00229         temp = p2p_cdr(temp);
00230         if (isstring(head)) 
00231           string_head = string_val(head);
00232         else
00233           xsb_abort("[exec] non-string argument passed in list.");
00234         
00235         params[index++] = string_head;
00236         if (index > MAX_SUBPROC_PARAMS)
00237           xsb_abort("[exec] Too many arguments.");
00238       } while (!isnil(temp));
00239       params[index] = NULL;
00240     } else if (isstring(cmdspec_term)) {
00241       char *string = string_val(cmdspec_term);
00242       split_command_arguments(string, params, "exec");
00243     } else
00244       xsb_abort("[exec] 1st argument should be term or list of strings.");
00245 
00246     if (execvp(params[0], params)) 
00247       xsb_abort("[exec] Exec call failed.");
00248 #else
00249     xsb_abort("[exec] builtin not supported in this architecture.");
00250 #endif
00251   }
00252     
00253   case SHELL: /* smart system call: like SPAWN_PROCESS, but returns error code
00254                  instead of PID. Uses system() rather than execvp.
00255                  Advantage: can pass arbitrary shell command. */
00256   case SPAWN_PROCESS: { /* spawn new process, reroute stdin/out/err to XSB */
00257     /* +CallNo=2, +ProcAndArgsList,
00258        -StreamToProc, -StreamFromProc, -StreamFromProcStderr,
00259        -Pid */
00260     static int pipe_to_proc[2], pipe_from_proc[2], pipe_from_stderr[2];
00261     int toproc_stream=-1, fromproc_stream=-1, fromproc_stderr_stream=-1;
00262     int pid_or_status;
00263     FILE *toprocess_fptr=NULL,
00264       *fromprocess_fptr=NULL, *fromproc_stderr_fptr=NULL;
00265     char *params[MAX_SUBPROC_PARAMS+2]; /* one for progname--0th member,
00266                                        one for NULL termination*/
00267     prolog_term cmdspec_term, cmdlist_temp_term;
00268     prolog_term cmd_or_arg_term;
00269     xsbBool toproc_needed=FALSE, fromproc_needed=FALSE, fromstderr_needed=FALSE;
00270     char *cmd_or_arg=NULL, *shell_cmd=NULL;
00271     int idx = 0, tbl_pos;
00272     char *callname=NULL;
00273     xsbBool params_are_in_a_list=FALSE;
00274 
00275     init_process_table();
00276 
00277     if (callno == SPAWN_PROCESS)
00278       callname = "SPAWN_PROCESS";
00279     else
00280       callname = "SHELL";
00281 
00282     cmdspec_term = reg_term(CTXTc 2);
00283     if (islist(cmdspec_term))
00284       params_are_in_a_list = TRUE;
00285     else if (isstring(cmdspec_term))
00286       shell_cmd = string_val(cmdspec_term);
00287     else
00288       xsb_abort("[%s] Arg 1 must be an atom or a list [command, arg, ...]",
00289                 callname);
00290 
00291     /* the user can indicate that he doesn't want either of the streams created
00292        by putting an atom in the corresponding argument position */
00293     if (isref(reg_term(CTXTc 3)))
00294       toproc_needed = TRUE;
00295     if (isref(reg_term(CTXTc 4)))
00296       fromproc_needed = TRUE;
00297     if (isref(reg_term(CTXTc 5)))
00298       fromstderr_needed = TRUE;
00299 
00300     /* if any of the arg streams is already used by XSB, then don't create
00301        pipes --- use these streams instead. */
00302     if (isinteger(reg_term(CTXTc 3))|isboxedinteger(reg_term(CTXTc 3))) {
00303       SET_FILEPTR(toprocess_fptr, int_val(reg_term(CTXTc 3)));
00304     }
00305     if (isinteger(reg_term(CTXTc 4))|isboxedinteger(reg_term(CTXTc 4))) {
00306       SET_FILEPTR(fromprocess_fptr, int_val(reg_term(CTXTc 4)));
00307     }
00308     if (isinteger(reg_term(CTXTc 5))|isboxedinteger(reg_term(CTXTc 5))) {
00309       SET_FILEPTR(fromproc_stderr_fptr, int_val(reg_term(CTXTc 5)));
00310     }
00311 
00312     if (!isref(reg_term(CTXTc 6)))
00313       xsb_abort("[%s] Arg 5 (process id) must be a variable", callname);
00314 
00315     if (params_are_in_a_list) {
00316       /* fill in the params[] array */
00317       if (isnil(cmdspec_term))
00318         xsb_abort("[%s] Arg 1 must not be an empty list", callname);
00319       
00320       cmdlist_temp_term = cmdspec_term;
00321       do {
00322         cmd_or_arg_term = p2p_car(cmdlist_temp_term);
00323         cmdlist_temp_term = p2p_cdr(cmdlist_temp_term);
00324         if (isstring(cmd_or_arg_term)) {
00325           cmd_or_arg = string_val(cmd_or_arg_term);
00326         }
00327         else 
00328           xsb_abort("[%s] Non string list member in the Arg",
00329                     callname);
00330         
00331         params[idx++] = cmd_or_arg;
00332         if (idx > MAX_SUBPROC_PARAMS)
00333           xsb_abort("[%s] Too many arguments passed to subprocess",
00334                     callname);
00335         
00336       } while (!isnil(cmdlist_temp_term));
00337 
00338       params[idx] = NULL; /* null termination */
00339 
00340     } else { /* params are in a string */
00341       if (callno == SPAWN_PROCESS)
00342         split_command_arguments(shell_cmd, params, callname);
00343       else {
00344         /* if callno==SHELL => call system() => don't split shell_cmd */
00345         params[0] = shell_cmd;
00346         params[1] = NULL;
00347       }
00348     }
00349     
00350     /* -1 means: no space left */
00351     if ((tbl_pos = get_free_process_cell()) < 0) {
00352       xsb_warn("Can't create subprocess because XSB process table is full");
00353       return FALSE;
00354     }
00355 
00356     /* params[0] is the progname */
00357     pid_or_status = xsb_spawn(params[0], params, callno,
00358                               (toproc_needed ? pipe_to_proc : NULL),
00359                               (fromproc_needed ? pipe_from_proc : NULL),
00360                               (fromstderr_needed ? pipe_from_stderr : NULL),
00361                               toprocess_fptr, fromprocess_fptr,
00362                               fromproc_stderr_fptr);
00363 
00364     if (pid_or_status < 0) {
00365       xsb_warn("[%s] Subprocess creation failed", callname);
00366       return FALSE;
00367     }
00368 
00369     if (toproc_needed) {
00370       toprocess_fptr = fdopen(pipe_to_proc[1], "w");
00371       toproc_stream =  xsb_intern_fileptr(toprocess_fptr,callname,"pipe","w"); 
00372       ctop_int(CTXTc 3, toproc_stream);
00373     }
00374     if (fromproc_needed) {
00375       fromprocess_fptr = fdopen(pipe_from_proc[0], "r");
00376       fromproc_stream =  xsb_intern_fileptr(fromprocess_fptr,callname,"pipe","r"); 
00377       ctop_int(CTXTc 4, fromproc_stream);
00378     }
00379     if (fromstderr_needed) {
00380       fromproc_stderr_fptr = fdopen(pipe_from_stderr[0], "r");
00381       fromproc_stderr_stream
00382         = xsb_intern_fileptr(fromproc_stderr_fptr,callname,"pipe","r"); 
00383       ctop_int(CTXTc 5, fromproc_stderr_stream);
00384     }
00385     ctop_int(CTXTc 6, pid_or_status);
00386 
00387     xsb_process_table.process[tbl_pos].pid = pid_or_status;
00388     xsb_process_table.process[tbl_pos].to_stream = toproc_stream;
00389     xsb_process_table.process[tbl_pos].from_stream = fromproc_stream;
00390     xsb_process_table.process[tbl_pos].stderr_stream = fromproc_stderr_stream;
00391     concat_array(params, " ",
00392                  xsb_process_table.process[tbl_pos].cmdline,MAX_CMD_LEN);
00393     
00394     return TRUE;
00395   }
00396 
00397   case GET_PROCESS_TABLE: { /* sys_system(3, X). X is bound to the list
00398                of the form [process(Pid,To,From,Stderr,Cmdline), ...] */
00399     int i;
00400     prolog_term table_term_tail, listHead;
00401     prolog_term table_term=reg_term(CTXTc 2);
00402 
00403     init_process_table();
00404 
00405     if (!isref(table_term))
00406       xsb_abort("[GET_PROCESS_TABLE] Arg 1 must be a variable");
00407 
00408     table_term_tail = table_term;
00409     for (i=0; i<MAX_SUBPROC_NUMBER; i++) {
00410       if (!FREE_PROC_TABLE_CELL(xsb_process_table.process[i].pid)) {
00411         c2p_list(CTXTc table_term_tail); /* make it into a list */
00412         listHead = p2p_car(table_term_tail);
00413 
00414         c2p_functor(CTXTc "process", 5, listHead);
00415         c2p_int(CTXTc xsb_process_table.process[i].pid, p2p_arg(listHead,1));
00416         c2p_int(CTXTc xsb_process_table.process[i].to_stream, p2p_arg(listHead,2));
00417         c2p_int(CTXTc xsb_process_table.process[i].from_stream, p2p_arg(listHead,3));
00418         c2p_int(CTXTc xsb_process_table.process[i].stderr_stream,
00419                 p2p_arg(listHead,4));
00420         c2p_string(CTXTc xsb_process_table.process[i].cmdline, p2p_arg(listHead,5));
00421 
00422         table_term_tail = p2p_cdr(table_term_tail);
00423       }
00424     }
00425     c2p_nil(CTXTc table_term_tail); /* bind tail to nil */
00426     return p2p_unify(CTXTc table_term, reg_term(CTXTc 2));
00427   }
00428 
00429   case PROCESS_STATUS: {
00430     prolog_term pid_term=reg_term(CTXTc 2), status_term=reg_term(CTXTc 3);
00431 
00432     init_process_table();
00433 
00434     if (!(isinteger(pid_term)|isboxedinteger(pid_term)))
00435       xsb_abort("[PROCESS_STATUS] Arg 1 (process id) must be an integer");
00436     pid = int_val(pid_term);
00437 
00438     if (!isref(status_term))
00439       xsb_abort("[PROCESS_STATUS] Arg 2 (process status) must be a variable");
00440     
00441     switch (process_status(pid)) {
00442     case RUNNING:
00443       c2p_string(CTXTc "running", status_term);
00444       break;
00445     case STOPPED:
00446       c2p_string(CTXTc "stopped", status_term);
00447       break;
00448     case EXITED_NORMALLY:
00449       c2p_string(CTXTc "exited_normally", status_term);
00450       break;
00451     case EXITED_ABNORMALLY:
00452       c2p_string(CTXTc "exited_abnormally", status_term);
00453       break;
00454     case ABORTED:
00455       c2p_string(CTXTc "aborted", status_term);
00456       break;
00457     case INVALID:
00458       c2p_string(CTXTc "invalid", status_term);
00459       break;
00460     default:
00461       c2p_string(CTXTc "unknown", status_term);
00462     }
00463     return TRUE;
00464   }
00465 
00466   case PROCESS_CONTROL: {
00467     /* sys_system(PROCESS_CONTROL, +Pid, +Signal). Signal: wait, kill */
00468     int status;
00469     prolog_term pid_term=reg_term(CTXTc 2), signal_term=reg_term(CTXTc 3);
00470 
00471     init_process_table();
00472 
00473     if (!(isinteger(pid_term)|isboxedinteger(pid_term)))
00474       xsb_abort("[PROCESS_CONTROL] Arg 1 (process id) must be an integer");
00475     pid = int_val(pid_term);
00476 
00477     if (isstring(signal_term) && strcmp(string_val(signal_term), "kill")==0) {
00478       if (KILL_FAILED(pid))
00479         return FALSE;
00480 #ifdef WIN_NT
00481         CloseHandle((HANDLE) pid);
00482 #endif
00483       return TRUE;
00484     }
00485     if (isconstr(signal_term)
00486         && strcmp(p2c_functor(signal_term),"wait") == 0
00487         && p2c_arity(signal_term)==1) {
00488       int exit_status;
00489 
00490       if (WAIT(pid, status) < 0)
00491         return FALSE;
00492 
00493 #ifdef WIN_NT
00494       exit_status = status;
00495 #else
00496       if (WIFEXITED(status))
00497         exit_status = WEXITSTATUS(status);
00498       else
00499         exit_status = -1;
00500 #endif
00501 
00502       p2p_unify(CTXTc p2p_arg(signal_term,1), makeint(exit_status));
00503       return TRUE;
00504     }
00505 
00506     xsb_warn("[PROCESS_CONTROL] Arg 2: Invalid signal specification. Must be `kill' or `wait(Var)'");
00507     return FALSE;
00508   }
00509    
00510   case LIST_DIRECTORY: {
00511     /* assume all type- and mode-checking is done in Prolog */
00512     prolog_term handle = reg_term(CTXTc 2); /* ref for handle */
00513     char *dir_name = ptoc_longstring(CTXTc 3); /* +directory name */
00514     prolog_term filename = reg_term(CTXTc 4); /* reference for name of file */
00515     
00516     if (is_var(handle)) 
00517       return xsb_find_first_file(CTXTc handle,dir_name,filename);
00518     else
00519       return xsb_find_next_file(CTXTc handle,dir_name,filename);
00520   }
00521 
00522   default:
00523     xsb_abort("[SYS_SYSTEM] Wrong call number (an XSB bug)");
00524   } /* end case */
00525   return TRUE;
00526 }
00527 
00528 
00529 /* spawn a subprocess PROGNAME and pass it the arguments ARGV[]
00530    ARGV must be a NULL-terminated array of strings.
00531    Also pass it two arrays of strings: PIPE_TO_PROC[2] and PIPE_FROM_PROC[2].
00532    These are going to be the arrays of fds for the communication pipes 
00533 */
00534 static int xsb_spawn (char *progname, char *argv[], int callno,
00535                       int pipe_to_proc[],
00536                       int pipe_from_proc[],int pipe_from_stderr[],
00537                       FILE *toprocess_fptr, FILE *fromprocess_fptr,
00538                       FILE *fromproc_stderr_fptr)
00539 {
00540   int pid;
00541   int stdin_saved, stdout_saved, stderr_saved;
00542   static char shell_command[MAX_CMD_LEN];
00543 
00544   if ( (pipe_to_proc != NULL) && PIPE(pipe_to_proc) < 0 ) {
00545     /* can't open pipe to process */
00546     xsb_warn("[SPAWN_PROCESS] Can't open pipe for subprocess input");
00547     return PIPE_TO_PROC_FAILED;
00548   }
00549   if ( (pipe_from_proc != NULL) && PIPE(pipe_from_proc) < 0 ) {
00550     /* can't open pipe from process */
00551     xsb_warn("[SPAWN_PROCESS] Can't open pipe for subprocess output");
00552     return PIPE_FROM_PROC_FAILED;
00553   }
00554   if ( (pipe_from_stderr != NULL) && PIPE(pipe_from_stderr) < 0 ) {
00555     /* can't open stderr pipe from process */
00556     xsb_warn("[SPAWN_PROCESS] Can't open pipe for subprocess errors");
00557     return PIPE_FROM_PROC_FAILED;
00558   }
00559 
00560   /* The following is due to the awkwardness of windoze process creation.
00561      We commit this atrocity in order to be portable between Unix and Windows.
00562      1. Save stdio of the parent process.
00563      2. Redirect main process stdio to the pipes.
00564      3. Spawn subprocess. The subprocess inherits the redirected I/O
00565      4. Restore the original stdio for the parent process.
00566 
00567      On the bright side, this trick allowed us to cpature the I/O streams of
00568      the shell commands invoked by system()
00569   */
00570 
00571   /* save I/O */
00572   stdin_saved  = dup(fileno(stdin));
00573   stdout_saved = dup(fileno(stdout));
00574   stderr_saved = dup(fileno(stderr));
00575   if ((fileno(stdin) < 0) || (stdin_saved < 0))
00576     xsb_warn("[SPAWN_PROCESS] Bad stdin=%d; stdin closed by mistake?",
00577              fileno(stdin));
00578   if ((fileno(stdout) < 0) || (stdout_saved < 0))
00579     xsb_warn("[SPAWN_PROCESS] Bad stdout=%d; stdout closed by mistake?",
00580              fileno(stdout));
00581   if ((fileno(stderr) < 0) || (stderr_saved < 0))
00582     xsb_warn("[SPAWN_PROCESS] Bad stderr=%d; stderr closed by mistake?",
00583              fileno(stderr));
00584 
00585   if (pipe_to_proc != NULL) {
00586     /* close child stdin, bind it to the reading part of pipe_to_proc */
00587     if (dup2(pipe_to_proc[0], fileno(stdin)) < 0) {
00588       xsb_warn("[SPAWN_PROCESS] Can't connect pipe %d to subprocess stdin",
00589                pipe_to_proc[0]);
00590       return PIPE_TO_PROC_FAILED;
00591     }
00592     close(pipe_to_proc[0]); /* close the parent read end of pipe */
00593   }
00594   /* if stdin must be captured in an existing I/O port -- do it */
00595   if (toprocess_fptr != NULL)
00596     if (dup2(fileno(toprocess_fptr), fileno(stdin)) < 0) {
00597       xsb_warn("[SPAWN_PROCESS] Can't connect stream %d to subprocess stdin",
00598                fileno(toprocess_fptr));
00599       return PIPE_TO_PROC_FAILED;
00600     }
00601   
00602   if (pipe_from_proc != NULL) {
00603     /* close child stdout, bind it to the write part of pipe_from_proc */
00604     if (dup2(pipe_from_proc[1], fileno(stdout)) < 0) {
00605       xsb_warn("[SPAWN_PROCESS] Can't connect subprocess stdout to pipe %d",
00606                pipe_from_proc[1]);
00607       return PIPE_TO_PROC_FAILED;
00608     }
00609     close(pipe_from_proc[1]); /* close the parent write end of pipe */
00610   }
00611   /* if stdout must be captured in an existing I/O port -- do it */
00612   if (fromprocess_fptr != NULL)
00613     if (dup2(fileno(fromprocess_fptr), fileno(stdout)) < 0) {
00614       xsb_warn("[SPAWN_PROCESS] Can't connect subprocess stdout to stream %d",
00615                fileno(fromprocess_fptr));
00616       return PIPE_TO_PROC_FAILED;
00617     }
00618 
00619   if (pipe_from_stderr != NULL) {
00620     /* close child stderr, bind it to the write part of pipe_from_proc */
00621     if (dup2(pipe_from_stderr[1], fileno(stderr)) < 0) {
00622       xsb_warn("[SPAWN_PROCESS] Can't connect subprocess stderr to pipe %d",
00623                pipe_from_stderr[1]);
00624       return PIPE_TO_PROC_FAILED;
00625     }
00626     close(pipe_from_stderr[1]); /* close the parent write end of pipe */
00627   }
00628   /* if stderr must be captured in an existing I/O port -- do it */
00629   if (fromproc_stderr_fptr != NULL)
00630     if (dup2(fileno(fromproc_stderr_fptr), fileno(stderr)) < 0) {
00631       xsb_warn("[SPAWN_PROCESS] Can't connect subprocess stderr to stream %d",
00632                fileno(fromproc_stderr_fptr));
00633       return PIPE_TO_PROC_FAILED;
00634     }
00635 
00636   if (callno == SPAWN_PROCESS) {
00637 #ifdef WIN_NT
00638 
00639     static char bufQuoted[MAX_CMD_LEN + 2*(MAX_SUBPROC_PARAMS + 2)];
00640     const char * argvQuoted[MAX_SUBPROC_PARAMS + 2];
00641     char *  argq = bufQuoted;
00642     char *  arge = bufQuoted + sizeof(bufQuoted);
00643     char ** argp = argv;
00644     size_t  len  = 0; 
00645     int     i;
00646 
00647     for (i = 0; i < MAX_SUBPROC_PARAMS + 2; ++i) {
00648       if (*argp && (argq + (len = strlen(*argp)) + 4 < arge)) {
00649          argvQuoted[i] = argq;
00650          *argq++ = '"';
00651          strncpy(argq, *argp, len);
00652          argq += len;
00653          *argq++ = '"';
00654          *argq++ = '\0';
00655          ++argp;
00656       } else {
00657          *argq = '\0';
00658          argvQuoted[i] = 0;
00659          break;
00660       }
00661     }
00662     pid = spawnvp(P_NOWAIT, progname, argvQuoted);
00663 #else
00664     pid = fork();
00665 #endif
00666 
00667     if (pid < 0) {
00668       /* failed */
00669       xsb_warn("[SPAWN_PROCESS] Can't fork off subprocess");
00670       return pid;
00671     } else if (pid == 0) {
00672       /* child process */
00673 
00674       /* Close the writing side of child's in-pipe. Must do this or else the
00675          child won't see EOF when parent closes its end of this pipe. */
00676       if (pipe_to_proc != NULL) close(pipe_to_proc[1]);
00677       /* Close the reading part of child's out-pipe and stderr-pipe */
00678       if (pipe_from_proc != NULL) close(pipe_from_proc[0]);
00679       if (pipe_from_stderr != NULL) close(pipe_from_stderr[0]);
00680       
00681 #ifndef WIN_NT  /* Unix: must exec */
00682       execvp(progname, argv);
00683       /* if we ever get here, this means that invocation of the process has
00684          failed */
00685       exit(SUB_PROC_FAILED);
00686 #endif
00687     }
00688   } else { /* SHELL command */
00689     /* no separator */
00690     concat_array(argv, "", shell_command, MAX_CMD_LEN);
00691     pid = system(shell_command);
00692   }
00693 
00694   /* main process continues */
00695 
00696   /* duplicate saved copies of stdio fds back into main process stdio */
00697   if (dup2(stdin_saved, fileno(stdin)) < 0) {
00698     perror("SPAWN_PROCESS");
00699     close(stdin_saved); close(stdout_saved); close(stderr_saved);
00700     return PIPE_TO_PROC_FAILED;
00701   }
00702   if (dup2(stdout_saved, fileno(stdout)) < 0) {
00703     perror("SPAWN_PROCESS");
00704     close(stdin_saved); close(stdout_saved); close(stderr_saved);
00705     return PIPE_TO_PROC_FAILED;
00706   }
00707   if (dup2(stderr_saved, fileno(stderr)) < 0) {
00708     perror("SPAWN_PROCESS");
00709     close(stdin_saved); close(stdout_saved); close(stderr_saved);
00710     return PIPE_TO_PROC_FAILED;
00711   }
00712 
00713   close(stdin_saved); close(stdout_saved); close(stderr_saved);
00714   return pid;
00715 }
00716 
00717 /* array is a NULL terminated array of strings. Concat it and return string */
00718 static void concat_array(char *array[], char *separator,
00719                          char *result_str, int maxsize)
00720 {
00721   int space_left = maxsize-1, separator_size = strlen(separator);
00722   char *current_pos=result_str;
00723   int idx=0, len;
00724 
00725   /* init result_str */
00726   *current_pos='\0';
00727 
00728   /* Die, he who neglects to NULL-terminate an array */
00729   while ((array[idx] != NULL) && (space_left > 0)) {
00730     len = strlen(array[idx]);
00731 
00732     strncat(current_pos, array[idx], space_left);
00733     current_pos = current_pos + (len < space_left ? len : space_left);
00734     *current_pos='\0';
00735     space_left = space_left - len;
00736     /* insert space separator */
00737     strncat(current_pos, separator, space_left);
00738     current_pos += separator_size;
00739     *current_pos='\0';
00740     space_left -= separator_size;
00741     idx++;
00742   }
00743 }
00744 
00745 
00746 static int get_free_process_cell(void) 
00747 {
00748   int possible_free_cell = xsb_process_table.search_idx;
00749   int pid;
00750 
00751   do {
00752     pid = xsb_process_table.process[possible_free_cell].pid;
00753     if (FREE_PROC_TABLE_CELL(pid)) {
00754       /* free cell found */
00755       xsb_process_table.search_idx =
00756         (possible_free_cell + 1) % MAX_SUBPROC_NUMBER;
00757       return possible_free_cell;
00758     }
00759     possible_free_cell = (possible_free_cell + 1) % MAX_SUBPROC_NUMBER;
00760   } while (possible_free_cell != xsb_process_table.search_idx);
00761 
00762   /* no space */
00763   return -1;
00764 }
00765 
00766 
00767 static void init_process_table(void)
00768 {
00769   static xsbBool process_table_initted = FALSE;
00770   int i;
00771 
00772   if (!process_table_initted) {
00773     for (i=0; i<MAX_SUBPROC_NUMBER; i++) {
00774       xsb_process_table.process[i].pid = -1;
00775     }
00776     xsb_process_table.search_idx = 0;
00777     process_table_initted = TRUE;
00778   }
00779 }
00780 
00781 
00782 /* check process status */
00783 int process_status(int pid)
00784 {
00785 #ifdef WIN_NT
00786   long status;
00787   if (GetExitCodeProcess((HANDLE) pid, &status)) {
00788     if (status == STILL_ACTIVE)
00789       return RUNNING;
00790     else if (status == 0)
00791       return EXITED_NORMALLY;
00792     else
00793       return EXITED_ABNORMALLY;
00794   } else
00795     return INVALID;
00796 #else
00797   int retcode;
00798   int status;
00799   /* don't wait for children that run or are stopped */
00800   retcode = waitpid(pid, &status, WNOHANG | WUNTRACED);
00801 
00802   if (retcode == 0)        return RUNNING; /* running                        */
00803   if (retcode < 0)         return INVALID; /* doesn't exist or isn't a child */
00804   if (WIFSTOPPED(status))  return STOPPED; /* stopped                        */
00805   if (WIFEXITED(status)) {                 /* exited by an exit(code) stmt   */
00806     if (WEXITSTATUS(status))
00807       return EXITED_ABNORMALLY;
00808     else
00809       return EXITED_NORMALLY;
00810   }
00811   if (WIFSIGNALED(status)) return ABORTED; /* aborted                        */
00812 
00813   return UNKNOWN; /*  unknown status */
00814 #endif
00815 }
00816 
00817 
00818 /* split STRING at spaces, \t, \n, and put components
00819    in a NULL-terminated array.
00820    Take care of quoted strings and escaped symbols
00821    If you call it twice, the old split is forgotten.
00822    STRING is the string to split
00823    PARAMS is the array of substrings obtained as a result of the split
00824           these params are all sitting in a static variable, buffer.
00825    CALLNAME - the name of the system call. Used in error messages.
00826 */
00827 static void split_command_arguments(char *string, char *params[], char *callname)
00828 {
00829   int buflen = strlen(string);
00830   int idx = 0;
00831   char *buf_ptr, *arg_ptr;
00832   static char buffer[MAX_CMD_LEN];
00833 
00834   if (buflen > MAX_CMD_LEN - 1)
00835     xsb_abort("[%s] Command string too long, %s", callname, string);
00836 
00837   buf_ptr = buffer;
00838 
00839   /* Debugging
00840   fprintf(stderr,"%s\n", string);
00841   */
00842   do {
00843     arg_ptr = get_next_command_argument(&buf_ptr,&string);
00844     params[idx] = arg_ptr;
00845     /* Debugging
00846     fprintf(stderr,"%s\n", arg_ptr);
00847     */
00848     idx++;
00849   } while (arg_ptr != NULL && idx <= MAX_SUBPROC_PARAMS);
00850   /* note: params has extra space, so not to worry about <= */
00851 
00852   return;
00853 }
00854 
00855 
00856 /*
00857   Copies next command argument from CMD_LINE to the next free place in buffer
00858   and returns the pointer to the null-terminated string that represents that
00859   argument. Advances the pointers into the CMD_LINE and BUFFER so that the
00860   caller can use them to call get_next_command_argument again.
00861 */
00862 static char *get_next_command_argument(char **buffptr, char **cmdlineprt)
00863 {
00864   short escaped=FALSE;
00865   char quoted = '\0';
00866   char *returnptr = *buffptr;
00867 
00868   /* skip white space */
00869   while (isspace(**cmdlineprt))
00870     (*cmdlineprt)++;
00871 
00872   /* loop as long as not end of cmd line or until the next command argument has
00873      been found and extracted */
00874   while ((!isspace(**cmdlineprt) || quoted) && **cmdlineprt != '\0') {
00875     if (escaped) {
00876       switch (**cmdlineprt) {
00877       case 'b': **buffptr='\b'; break;
00878       case 'f': **buffptr='\f'; break;
00879       case 'n': **buffptr='\n'; break;
00880       case 'r': **buffptr='\r'; break;
00881       case 't': **buffptr='\t'; break;
00882       case 'v': **buffptr='\v'; break;
00883       default:
00884         **buffptr=**cmdlineprt;
00885       }
00886       (*buffptr)++;
00887       (*cmdlineprt)++;
00888       escaped=FALSE;
00889       continue;
00890     }
00891     switch (**cmdlineprt) {
00892     case '"':
00893     case '\'':
00894       if (!quoted) {
00895         /* begin quoted string */
00896         quoted = **cmdlineprt;
00897       } else if (quoted == **cmdlineprt) {
00898         /* end the quoted part of the argument */
00899         quoted = '\0';
00900       } else {
00901         /* quote symbol inside string quoted by a different symbol */
00902         **buffptr=**cmdlineprt;
00903         (*buffptr)++;
00904       }
00905       (*cmdlineprt)++;
00906       break;
00907 #ifndef WIN_NT
00908     case '\\':
00909       escaped = TRUE;
00910       (*cmdlineprt)++;
00911       break;
00912 #endif
00913     default:
00914       **buffptr=**cmdlineprt;
00915       (*buffptr)++;
00916       (*cmdlineprt)++;
00917     }
00918   }
00919 
00920   if (returnptr==*buffptr)
00921     return NULL;
00922 
00923   **buffptr='\0';
00924   (*buffptr)++;
00925   return returnptr;
00926 }
00927 
00928 
00929 
00930 /* use stat() to get file mod time, size, and other things */
00931 /* file_stat(+FileName, +FuncNumber, -Result)              */
00932 xsbBool file_stat(CTXTdeclc int callno, char *file)
00933 {
00934   struct stat stat_buff;
00935   int retcode = stat(file, &stat_buff);
00936 
00937   switch (callno) {
00938   case IS_PLAIN_FILE: {
00939     if (retcode == 0 && S_ISREG(stat_buff.st_mode)) 
00940       return TRUE;
00941     else return FALSE;
00942   }
00943   case IS_DIRECTORY: {
00944     if (retcode == 0 && S_ISDIR(stat_buff.st_mode)) 
00945       return TRUE;
00946     else return FALSE;
00947   }
00948   case STAT_FILE_TIME: {
00949     /* This is DSW's hack to get 32 bit time values.
00950        The idea is to call this builtin as file_time(File,time(T1,T2))
00951        where T1 represents the most significant 8 bits and T2 represents
00952        the least significant 24.
00953        ***This probably breaks 64 bit systems, so David will look into it!
00954        */
00955     int functor_arg3 = isconstr(reg_term(CTXTc 3));
00956     if (!retcode && functor_arg3) {
00957       /* file exists & arg3 is a term, return 2 words*/
00958       c2p_int(CTXTc stat_buff.st_mtime >> 24,p2p_arg(reg_term(CTXTc 3),1));
00959       c2p_int(CTXTc 0xFFFFFF & stat_buff.st_mtime,p2p_arg(reg_term(CTXTc 3),2));
00960     } else if (!retcode) {
00961       /* file exists, arg3 non-functor:  issue an error */
00962       xsb_warn("Arg 3 (the time argument) must be of the form time(X,Y)");
00963       ctop_int(CTXTc 3, (0x7FFFFFF & stat_buff.st_mtime));
00964     } else if (functor_arg3) {
00965       /* no file, and arg3 is functor: return two 0's */
00966       c2p_int(CTXTc 0, p2p_arg(reg_term(CTXTc 3),2));
00967       c2p_int(CTXTc 0, p2p_arg(reg_term(CTXTc 3),1));
00968     } else {
00969       /* no file, no functor: return 0 */
00970       xsb_warn("Arg 3 (the time argument) must be of the form time(X,Y)");
00971       ctop_int(CTXTc 3, 0);
00972     }
00973     return TRUE;
00974   }
00975   case STAT_FILE_SIZE: { /* Return size in bytes. */
00976     /*** NOTE: Same hack as with time. However, return a list
00977          because the path_sysop() interface in file_io returns lists
00978          both for time and size. */
00979     prolog_term size_lst = p2p_new(CTXT);
00980     prolog_term elt1, elt2, tail;
00981     if (!retcode) {
00982       /* file exists */
00983       c2p_list(CTXTc size_lst);
00984       elt1 = p2p_car(size_lst);
00985       tail = p2p_cdr(size_lst);
00986       c2p_list(CTXTc tail);
00987       elt2 = p2p_car(tail);
00988       tail = p2p_cdr(tail); c2p_nil(CTXTc tail);
00989       c2p_int(CTXTc stat_buff.st_size >> 24, elt1);
00990       c2p_int(CTXTc 0xFFFFFF & stat_buff.st_size, elt2);
00991       p2p_unify(CTXTc size_lst,reg_term(CTXTc 3));
00992       return TRUE;
00993     } else  /* no file */
00994       return FALSE;
00995   }
00996   default:
00997     xsb_abort("Unsupported file_stat code: %d\n", callno);
00998     return FALSE;
00999   } /* switch */
01000 }
01001 
01002 static int xsb_find_first_file(CTXTdeclc prolog_term handle,
01003                                char *dir,
01004                                prolog_term file)
01005 {
01006 #ifdef WIN_NT
01007   WIN32_FIND_DATA filedata;
01008   HANDLE filehandle;
01009 
01010   filehandle = FindFirstFile(dir,&filedata);
01011   if (filehandle == INVALID_HANDLE_VALUE)
01012     return FALSE;
01013   c2p_int(CTXTc (Integer)filehandle,handle);
01014   c2p_string(CTXTc filedata.cFileName,file);
01015   return TRUE;
01016 #else
01017   DIR *dirhandle;
01018   struct dirent *dir_entry;
01019   
01020   dirhandle = opendir(dir);
01021   if (!dirhandle)
01022     return FALSE;
01023   dir_entry = readdir(dirhandle);
01024   if (!dir_entry) {
01025     closedir(dirhandle);
01026     return FALSE;
01027   }
01028   c2p_int(CTXTc (Integer)dirhandle,handle);
01029   c2p_string(CTXTc dir_entry->d_name,file);
01030   return TRUE;
01031 #endif
01032 }
01033 
01034 static int xsb_find_next_file(CTXTdeclc prolog_term handle,
01035                               char *dir,
01036                               prolog_term file)
01037 {
01038 #ifdef WIN_NT
01039   WIN32_FIND_DATA filedata;
01040   HANDLE filehandle;
01041   
01042   filehandle = (HANDLE) p2c_int(handle);
01043   if (!FindNextFile(filehandle,&filedata)) {
01044     FindClose(filehandle);
01045     return FALSE;
01046   }
01047   c2p_string(CTXTc filedata.cFileName,file);
01048   return TRUE;
01049 #else
01050   DIR *dirhandle = (DIR *) p2c_int(handle);
01051   struct dirent *dir_entry;
01052 
01053   dir_entry = readdir(dirhandle);
01054   if (!dir_entry) {
01055     closedir(dirhandle);
01056     return FALSE;
01057   }
01058   c2p_string(CTXTc dir_entry->d_name,file);
01059   return TRUE;
01060 #endif
01061 }
01062 
01063 /* file_copy: based on code from busybox (www.busybox.net) */
01064 static int file_copy(char *source, char *dest)
01065 {
01066   struct stat source_stat;
01067   struct stat dest_stat;
01068   int dest_exists = 0;
01069   int status = 1;
01070   
01071   if (stat(source, &source_stat) < 0) {
01072     xsb_warn("[file_copy] Source file not found: %s\n",
01073              source);
01074     return 0;
01075   }
01076 
01077 #ifndef WIN_NT
01078   if (lstat(dest, &dest_stat) < 0) {
01079 #else
01080   if (stat(dest, &dest_stat) < 0) {
01081 #endif
01082     if (errno != ENOENT) {
01083       xsb_warn("[file_copy] Unable to stat destination: %s\n", dest);
01084       return 0;
01085     }
01086   } else {
01087 #ifdef WIN_NT
01088     if (!strcmp(source,dest)) {
01089       xsb_warn("[file_copy] %s and %s are the same file.\n", source,dest);
01090       return 0;
01091     }
01092 #else
01093     if (source_stat.st_dev == dest_stat.st_dev &&
01094         source_stat.st_ino == dest_stat.st_ino) {
01095       xsb_warn("[file_copy] %s and %s are the same file.\n", source,dest);
01096       return 0;
01097     }
01098 #endif
01099     dest_exists = 1;
01100   }
01101   
01102   if (S_ISDIR(source_stat.st_mode)) {
01103     xsb_warn("[file_copy] Source is a directory: %s\n",source);
01104     return 0;
01105   } else if (S_ISREG(source_stat.st_mode)) {
01106     FILE *sfp, *dfp=NULL;
01107     if ((sfp = fopen(source, "r")) == NULL) {
01108       xsb_warn("[file_copy] Unable to open source file: %s\n", source);
01109       return 0;
01110     }
01111 
01112     if (dest_exists) {
01113       if ((dfp = fopen(dest, "w")) == NULL) {
01114         if (unlink(dest) < 0) {
01115           xsb_warn("[file_copy] Unable to remove destination: %s\n",
01116                    dest);
01117           fclose (sfp);
01118           return 0;
01119         }
01120         dest_exists = 0;
01121       }
01122     }
01123 
01124     if (!dest_exists) {
01125       int fd;
01126       
01127       if ((fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode)) < 0 ||
01128           (dfp = fdopen(fd, "w")) == NULL) {
01129         if (fd >= 0)
01130           close(fd);
01131         xsb_warn("[file_copy] Unable to open destination: %s\n",dest);
01132         fclose (sfp);
01133         return 0;
01134       }
01135     }
01136 
01137     if (copy_file_chunk(sfp, dfp, -1) < 0)
01138       status = 0;
01139 
01140     if (fclose(dfp) < 0) {
01141       xsb_warn("[file_copy] Unable to close destination: %s\n", dest);
01142       status = 0;
01143     }
01144 
01145     if (fclose(sfp) < 0) {
01146       xsb_warn("[file_copy] Unable to close source: %s\n", source);
01147       status = 0;
01148     }
01149   } 
01150 #ifndef WIN_NT
01151   else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
01152            || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) 
01153            || S_ISLNK(source_stat.st_mode)
01154              ) {
01155 
01156     if (dest_exists && unlink(dest) < 0) {
01157       xsb_warn("[file_copy] Unable to remove destination: %s\n", dest);
01158       return 0;
01159     }
01160   } 
01161 #endif
01162   else {
01163     xsb_warn("[file_copy] Unrecognized source file type: %s\n", source);
01164     return 0;
01165   }
01166 #ifndef WIN_NT
01167   if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) ||
01168       S_ISSOCK(source_stat.st_mode)) {
01169     if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
01170       xsb_warn("[file_copy] Unable to create destination: %s\n", dest);
01171       return 0;
01172     }
01173   } else if (S_ISFIFO(source_stat.st_mode)) {
01174     if (mkfifo(dest, source_stat.st_mode) < 0) {
01175       xsb_warn("[file_copy] Unable to create FIFO: %s\n", dest);
01176       return 0;
01177     }
01178   } else if (S_ISLNK(source_stat.st_mode)) {
01179     char *lpath; 
01180     int lpath_len;
01181 
01182     lpath = xreadlink(source,&lpath_len);
01183     if (symlink(lpath, dest) < 0) {
01184       xsb_warn("[file_copy] Cannot create symlink %s", dest);
01185       return 0;
01186     }
01187     mem_dealloc(lpath,lpath_len,OTHER_SPACE);
01188     return 1;
01189   }
01190 #endif
01191   return status;
01192 }
01193 
01194 /* Copy CHUNKSIZE bytes (or until EOF if CHUNKSIZE equals -1) from SRC_FILE
01195  * to DST_FILE.  */
01196 static int copy_file_chunk(FILE *src_file, FILE *dst_file, unsigned long chunksize)
01197 {
01198   size_t nread, nwritten, size;
01199   char buffer[BUFSIZ];
01200 
01201   while (chunksize != 0) {
01202     if (chunksize > BUFSIZ)
01203       size = BUFSIZ;
01204     else
01205       size = chunksize;
01206 
01207     nread = fread (buffer, 1, size, src_file);
01208 
01209     if (nread != size && ferror (src_file)) {
01210       xsb_warn("[file_copy] Internal error: read.\n");
01211       return -1;
01212     } else if (nread == 0) {
01213       if (chunksize != -1) {
01214         xsb_warn("[file_copy] Internal error: Unable to read all data.\n");
01215         return -1;
01216       }
01217       return 0;
01218     }
01219 
01220     nwritten = fwrite (buffer, 1, nread, dst_file);
01221 
01222     if (nwritten != nread) {
01223       if (ferror (dst_file))
01224         xsb_warn("[file_copy] Internal error: write.\n");
01225       else
01226         xsb_warn("[file_copy] Internal error: Unable to write all data.\n");
01227       return -1;
01228     }
01229 
01230     if (chunksize != -1)
01231       chunksize -= nwritten;
01232   }
01233 
01234   return 0;
01235 }
01236 
01237 #ifndef WIN_NT
01238 static char *xreadlink(const char *path, int *bufsize)
01239 {                       
01240   static const int GROWBY = 80; /* how large we will grow strings by */
01241 
01242   char *buf = NULL;   
01243   int readsize = 0;
01244   *bufsize = 0;
01245 
01246   do {
01247     buf = mem_realloc(buf, *bufsize, *bufsize + GROWBY,OTHER_SPACE);
01248     *bufsize += GROWBY;
01249     readsize = readlink(path, buf, *bufsize); /* 1st try */
01250     if (readsize == -1) {
01251       xsb_warn("[file_copy] Internal error: xreadlink.\n");
01252       return NULL;
01253     }
01254   }           
01255   while (*bufsize < readsize + 1);
01256 
01257   buf[readsize] = '\0';
01258 
01259   return buf;
01260 }       
01261 #endif

Generated on Wed Jul 26 13:30:42 2006 for XSB by  doxygen 1.4.5