int main() { char **list = malloc(sizeof(char*) * 3); list[0] = "Hello, world"; list[1] = "cat is very cute"; list[2] = NULL; char **list2 = malloc(sizeof(char*) * 3); copy_array(list2, list); printf("%s\n%s\n%s\n", list[0], list[1], list[2]); printf("%s\n%s\n%s\n", list2[0], list2[1], list2[2]); char **list3 = concat_array(list, list2); char **iter = list3; while (*iter != NULL) { printf("%s\n", *iter); iter++; } printf("%d\n", array_length(list)); printf("%d\n", array_length(list2)); printf("%d\n", array_length(list3)); /* free_array(list); free_array(list2); free_array(list3); */ return 0; }
static HRESULT concat_obj(jsdisp_t *array, IDispatch *obj, DWORD *len) { jsdisp_t *jsobj; HRESULT hres; jsobj = iface_to_jsdisp((IUnknown*)obj); if(jsobj) { if(is_class(jsobj, JSCLASS_ARRAY)) { hres = concat_array(array, (ArrayInstance*)jsobj, len); jsdisp_release(jsobj); return hres; } jsdisp_release(jsobj); } return jsdisp_propput_idx(array, (*len)++, jsval_disp(obj)); }
static HRESULT concat_obj(jsdisp_t *array, IDispatch *obj, DWORD *len, jsexcept_t *ei) { jsdisp_t *jsobj; VARIANT var; HRESULT hres; jsobj = iface_to_jsdisp((IUnknown*)obj); if(jsobj) { if(is_class(jsobj, JSCLASS_ARRAY)) { hres = concat_array(array, (ArrayInstance*)jsobj, len, ei); jsdisp_release(jsobj); return hres; } jsdisp_release(jsobj); } V_VT(&var) = VT_DISPATCH; V_DISPATCH(&var) = obj; return jsdisp_propput_idx(array, (*len)++, &var, ei); }
/* spawn a subprocess PROGNAME and pass it the arguments ARGV[] ARGV must be a NULL-terminated array of strings. Also pass it two arrays of strings: PIPE_TO_PROC[2] and PIPE_FROM_PROC[2]. These are going to be the arrays of fds for the communication pipes This function is protected from being called by more than one thread at a time by a mutex: the context is passed in for error handling. */ static int xsb_spawn (CTXTdeclc char *progname, char *argv[], int callno, int pipe_to_proc[], int pipe_from_proc[],int pipe_from_stderr[], FILE *toprocess_fptr, FILE *fromprocess_fptr, FILE *fromproc_stderr_fptr) { int pid; int stdin_saved, stdout_saved, stderr_saved; static char shell_command[MAX_CMD_LEN]; if ( (pipe_to_proc != NULL) && PIPE(pipe_to_proc) < 0 ) { /* can't open pipe to process */ xsb_warn(CTXTc "[SPAWN_PROCESS] Can't open pipe for subprocess input"); return PIPE_TO_PROC_FAILED; } if ( (pipe_from_proc != NULL) && PIPE(pipe_from_proc) < 0 ) { /* can't open pipe from process */ xsb_warn(CTXTc "[SPAWN_PROCESS] Can't open pipe for subprocess output"); return PIPE_FROM_PROC_FAILED; } if ( (pipe_from_stderr != NULL) && PIPE(pipe_from_stderr) < 0 ) { /* can't open stderr pipe from process */ xsb_warn(CTXTc "[SPAWN_PROCESS] Can't open pipe for subprocess errors"); return PIPE_FROM_PROC_FAILED; } /* The following is due to the awkwardness of windoze process creation. We commit this atrocity in order to be portable between Unix and Windows. 1. Save stdio of the parent process. 2. Redirect main process stdio to the pipes. 3. Spawn subprocess. The subprocess inherits the redirected I/O 4. Restore the original stdio for the parent process. On the bright side, this trick allowed us to cpature the I/O streams of the shell commands invoked by system() */ /* save I/O */ stdin_saved = dup(fileno(stdin)); stdout_saved = dup(fileno(stdout)); stderr_saved = dup(fileno(stderr)); if ((fileno(stdin) < 0) || (stdin_saved < 0)) xsb_warn(CTXTc "[SPAWN_PROCESS] Bad stdin=%d; stdin closed by mistake?", fileno(stdin)); if ((fileno(stdout) < 0) || (stdout_saved < 0)) xsb_warn(CTXTc "[SPAWN_PROCESS] Bad stdout=%d; stdout closed by mistake?", fileno(stdout)); if ((fileno(stderr) < 0) || (stderr_saved < 0)) xsb_warn(CTXTc "[SPAWN_PROCESS] Bad stderr=%d; stderr closed by mistake?", fileno(stderr)); if (pipe_to_proc != NULL) { /* close child stdin, bind it to the reading part of pipe_to_proc */ if (dup2(pipe_to_proc[0], fileno(stdin)) < 0) { xsb_warn(CTXTc "[SPAWN_PROCESS] Can't connect pipe %d to subprocess stdin", pipe_to_proc[0]); return PIPE_TO_PROC_FAILED; } close(pipe_to_proc[0]); /* close the parent read end of pipe */ } /* if stdin must be captured in an existing I/O port -- do it */ if (toprocess_fptr != NULL) if (dup2(fileno(toprocess_fptr), fileno(stdin)) < 0) { xsb_warn(CTXTc "[SPAWN_PROCESS] Can't connect stream %d to subprocess stdin", fileno(toprocess_fptr)); return PIPE_TO_PROC_FAILED; } if (pipe_from_proc != NULL) { /* close child stdout, bind it to the write part of pipe_from_proc */ if (dup2(pipe_from_proc[1], fileno(stdout)) < 0) { xsb_warn(CTXTc "[SPAWN_PROCESS] Can't connect subprocess stdout to pipe %d", pipe_from_proc[1]); return PIPE_TO_PROC_FAILED; } close(pipe_from_proc[1]); /* close the parent write end of pipe */ } /* if stdout must be captured in an existing I/O port -- do it */ if (fromprocess_fptr != NULL) if (dup2(fileno(fromprocess_fptr), fileno(stdout)) < 0) { xsb_warn(CTXTc "[SPAWN_PROCESS] Can't connect subprocess stdout to stream %d", fileno(fromprocess_fptr)); return PIPE_TO_PROC_FAILED; } if (pipe_from_stderr != NULL) { /* close child stderr, bind it to the write part of pipe_from_proc */ if (dup2(pipe_from_stderr[1], fileno(stderr)) < 0) { xsb_warn(CTXTc "[SPAWN_PROCESS] Can't connect subprocess stderr to pipe %d", pipe_from_stderr[1]); return PIPE_TO_PROC_FAILED; } close(pipe_from_stderr[1]); /* close the parent write end of pipe */ } /* if stderr must be captured in an existing I/O port -- do it */ if (fromproc_stderr_fptr != NULL) if (dup2(fileno(fromproc_stderr_fptr), fileno(stderr)) < 0) { xsb_warn(CTXTc "[SPAWN_PROCESS] Can't connect subprocess stderr to stream %d", fileno(fromproc_stderr_fptr)); return PIPE_TO_PROC_FAILED; } if (callno == SPAWN_PROCESS) { #ifdef WIN_NT static char bufQuoted[MAX_CMD_LEN + 2*(MAX_SUBPROC_PARAMS + 2)]; const char * argvQuoted[MAX_SUBPROC_PARAMS + 2]; char * argq = bufQuoted; char * arge = bufQuoted + sizeof(bufQuoted); char ** argp = argv; size_t len = 0; int i; for (i = 0; i < MAX_SUBPROC_PARAMS + 2; ++i) { if (*argp && (argq + (len = strlen(*argp)) + 4 < arge)) { argvQuoted[i] = argq; *argq++ = '"'; strncpy(argq, *argp, len); argq += len; *argq++ = '"'; *argq++ = '\0'; ++argp; } else { *argq = '\0'; argvQuoted[i] = 0; break; } } // MK: make the children ignore SIGINT SetConsoleCtrlHandler(NULL, TRUE); pid = (int)_spawnvp(P_NOWAIT, progname, argvQuoted);//should pid be Integer? // MK: restore the normal processing of SIGINT in the parent SetConsoleCtrlHandler(NULL, FALSE); #else pid = fork(); #endif if (pid < 0) { /* failed */ xsb_warn(CTXTc "[SPAWN_PROCESS] Can't fork off subprocess"); return pid; } else if (pid == 0) { /* child process */ /* Close the writing side of child's in-pipe. Must do this or else the child won't see EOF when parent closes its end of this pipe. */ if (pipe_to_proc != NULL) close(pipe_to_proc[1]); /* Close the reading part of child's out-pipe and stderr-pipe */ if (pipe_from_proc != NULL) close(pipe_from_proc[0]); if (pipe_from_stderr != NULL) close(pipe_from_stderr[0]); #ifdef WIN_NT // MK: Not used. Leaving as an example for possible future use // The below call isn't used. We execute SetConsoleCtrlHandler(NULL,TRUE) // in the parent, which passes the handling (Ctrl-C ignore) to children. // Executing SetConsoleCtrlHandler in the child DOES NOT do much. // SetConsoleCtrlHandler((PHANDLER_ROUTINE) ctrl_C_handler, TRUE); #else /* Unix: must exec */ // don't let keyboard interrupts kill subprocesses signal(SIGINT, SIG_IGN); execvp(progname, argv); /* if we ever get here, this means that invocation of the process has failed */ exit(SUB_PROC_FAILED); #endif } } else { /* SHELL command */ /* no separator */ concat_array(CTXTc argv, "", shell_command, MAX_CMD_LEN); pid = system(shell_command); } /* main process continues */ /* duplicate saved copies of stdio fds back into main process stdio */ if (dup2(stdin_saved, fileno(stdin)) < 0) { perror("SPAWN_PROCESS"); close(stdin_saved); close(stdout_saved); close(stderr_saved); return PIPE_TO_PROC_FAILED; } if (dup2(stdout_saved, fileno(stdout)) < 0) { perror("SPAWN_PROCESS"); close(stdin_saved); close(stdout_saved); close(stderr_saved); return PIPE_TO_PROC_FAILED; } if (dup2(stderr_saved, fileno(stderr)) < 0) { perror("SPAWN_PROCESS"); close(stdin_saved); close(stdout_saved); close(stderr_saved); return PIPE_TO_PROC_FAILED; } close(stdin_saved); close(stdout_saved); close(stderr_saved); return pid; }
/* TLS: making a conservative guess at which system calls need to be mutexed. I'm doing it whenever I see the process table altered or affected, so this is the data structure that its protecting. At some point, the SET_FILEPTRs should be protected against other threads closing that stream. Perhaps for such things a thread-specific stream table should be used. */ xsbBool sys_system(CTXTdeclc int callno) { // int pid; Integer pid; switch (callno) { case PLAIN_SYSTEM_CALL: /* dumb system call: no communication with XSB */ /* this call is superseded by shell and isn't used */ ctop_int(CTXTc 3, system(ptoc_string(CTXTc 2))); return TRUE; case SLEEP_FOR_SECS: #ifdef WIN_NT Sleep((int)iso_ptoc_int_arg(CTXTc 2,"sleep/1",1) * 1000); #else sleep(iso_ptoc_int_arg(CTXTc 2,"sleep/1",1)); #endif return TRUE; case GET_TMP_FILENAME: ctop_string(CTXTc 2,tempnam(NULL,NULL)); return TRUE; case IS_PLAIN_FILE: case IS_DIRECTORY: case STAT_FILE_TIME: case STAT_FILE_SIZE: return file_stat(CTXTc callno, ptoc_longstring(CTXTc 2)); case EXEC: { #ifdef HAVE_EXECVP /* execs a new process in place of XSB */ char *params[MAX_SUBPROC_PARAMS+2]; prolog_term cmdspec_term; int index = 0; cmdspec_term = reg_term(CTXTc 2); if (islist(cmdspec_term)) { prolog_term temp, head; char *string_head=NULL; if (isnil(cmdspec_term)) xsb_abort("[exec] Arg 1 must not be an empty list."); temp = cmdspec_term; do { head = p2p_car(temp); temp = p2p_cdr(temp); if (isstring(head)) string_head = string_val(head); else xsb_abort("[exec] non-string argument passed in list."); params[index++] = string_head; if (index > MAX_SUBPROC_PARAMS) xsb_abort("[exec] Too many arguments."); } while (!isnil(temp)); params[index] = NULL; } else if (isstring(cmdspec_term)) { char *string = string_val(cmdspec_term); split_command_arguments(string, params, "exec"); } else xsb_abort("[exec] 1st argument should be term or list of strings."); if (execvp(params[0], params)) xsb_abort("[exec] Exec call failed."); #else xsb_abort("[exec] builtin not supported in this architecture."); #endif } case SHELL: /* smart system call: like SPAWN_PROCESS, but returns error code instead of PID. Uses system() rather than execvp. Advantage: can pass arbitrary shell command. */ case SPAWN_PROCESS: { /* spawn new process, reroute stdin/out/err to XSB */ /* +CallNo=2, +ProcAndArgsList, -StreamToProc, -StreamFromProc, -StreamFromProcStderr, -Pid */ static int pipe_to_proc[2], pipe_from_proc[2], pipe_from_stderr[2]; int toproc_stream=-1, fromproc_stream=-1, fromproc_stderr_stream=-1; int pid_or_status; FILE *toprocess_fptr=NULL, *fromprocess_fptr=NULL, *fromproc_stderr_fptr=NULL; char *params[MAX_SUBPROC_PARAMS+2]; /* one for progname--0th member, one for NULL termination*/ prolog_term cmdspec_term, cmdlist_temp_term; prolog_term cmd_or_arg_term; xsbBool toproc_needed=FALSE, fromproc_needed=FALSE, fromstderr_needed=FALSE; char *cmd_or_arg=NULL, *shell_cmd=NULL; int idx = 0, tbl_pos; char *callname=NULL; xsbBool params_are_in_a_list=FALSE; SYS_MUTEX_LOCK( MUTEX_SYS_SYSTEM ); init_process_table(); if (callno == SPAWN_PROCESS) callname = "spawn_process/5"; else callname = "shell/[1,2,5]"; cmdspec_term = reg_term(CTXTc 2); if (islist(cmdspec_term)) params_are_in_a_list = TRUE; else if (isstring(cmdspec_term)) shell_cmd = string_val(cmdspec_term); else if (isref(cmdspec_term)) xsb_instantiation_error(CTXTc callname,1); else xsb_type_error(CTXTc "atom or list e.g. [command, arg, ...]",cmdspec_term,callname,1); // xsb_abort("[%s] Arg 1 must be an atom or a list [command, arg, ...]", // callname); /* the user can indicate that he doesn't want either of the streams created by putting an atom in the corresponding argument position */ if (isref(reg_term(CTXTc 3))) toproc_needed = TRUE; if (isref(reg_term(CTXTc 4))) fromproc_needed = TRUE; if (isref(reg_term(CTXTc 5))) fromstderr_needed = TRUE; /* if any of the arg streams is already used by XSB, then don't create pipes --- use these streams instead. */ if (isointeger(reg_term(CTXTc 3))) { SET_FILEPTR(toprocess_fptr, oint_val(reg_term(CTXTc 3))); } if (isointeger(reg_term(CTXTc 4))) { SET_FILEPTR(fromprocess_fptr, oint_val(reg_term(CTXTc 4))); } if (isointeger(reg_term(CTXTc 5))) { SET_FILEPTR(fromproc_stderr_fptr, oint_val(reg_term(CTXTc 5))); } if (!isref(reg_term(CTXTc 6))) xsb_type_error(CTXTc "variable (to return process id)",reg_term(CTXTc 6),callname,5); // xsb_abort("[%s] Arg 5 (process id) must be a variable", callname); if (params_are_in_a_list) { /* fill in the params[] array */ if (isnil(cmdspec_term)) xsb_abort("[%s] Arg 1 must not be an empty list", callname); cmdlist_temp_term = cmdspec_term; do { cmd_or_arg_term = p2p_car(cmdlist_temp_term); cmdlist_temp_term = p2p_cdr(cmdlist_temp_term); if (isstring(cmd_or_arg_term)) { cmd_or_arg = string_val(cmd_or_arg_term); } else xsb_abort("[%s] Non string list member in the Arg", callname); params[idx++] = cmd_or_arg; if (idx > MAX_SUBPROC_PARAMS) xsb_abort("[%s] Too many arguments passed to subprocess", callname); } while (!isnil(cmdlist_temp_term)); params[idx] = NULL; /* null termination */ } else { /* params are in a string */ if (callno == SPAWN_PROCESS) split_command_arguments(shell_cmd, params, callname); else { /* if callno==SHELL => call system() => don't split shell_cmd */ params[0] = shell_cmd; params[1] = NULL; } } /* -1 means: no space left */ if ((tbl_pos = get_free_process_cell()) < 0) { xsb_warn(CTXTc "Can't create subprocess because XSB process table is full"); SYS_MUTEX_UNLOCK( MUTEX_SYS_SYSTEM ); return FALSE; } /* params[0] is the progname */ pid_or_status = xsb_spawn(CTXTc params[0], params, callno, (toproc_needed ? pipe_to_proc : NULL), (fromproc_needed ? pipe_from_proc : NULL), (fromstderr_needed ? pipe_from_stderr : NULL), toprocess_fptr, fromprocess_fptr, fromproc_stderr_fptr); if (pid_or_status < 0) { xsb_warn(CTXTc "[%s] Subprocess creation failed, Error: %d, errno: %d, Cmd: %s", callname,pid_or_status,errno,params[0]); SYS_MUTEX_UNLOCK( MUTEX_SYS_SYSTEM ); return FALSE; } if (toproc_needed) { toprocess_fptr = fdopen(pipe_to_proc[1], "w"); toproc_stream = xsb_intern_fileptr(CTXTc toprocess_fptr,callname,"pipe","w",CURRENT_CHARSET); ctop_int(CTXTc 3, toproc_stream); } if (fromproc_needed) { fromprocess_fptr = fdopen(pipe_from_proc[0], "r"); fromproc_stream = xsb_intern_fileptr(CTXTc fromprocess_fptr,callname,"pipe","r",CURRENT_CHARSET); ctop_int(CTXTc 4, fromproc_stream); } if (fromstderr_needed) { fromproc_stderr_fptr = fdopen(pipe_from_stderr[0], "r"); fromproc_stderr_stream = xsb_intern_fileptr(CTXTc fromproc_stderr_fptr,callname,"pipe","r",CURRENT_CHARSET); ctop_int(CTXTc 5, fromproc_stderr_stream); } ctop_int(CTXTc 6, pid_or_status); xsb_process_table.process[tbl_pos].pid = pid_or_status; xsb_process_table.process[tbl_pos].to_stream = toproc_stream; xsb_process_table.process[tbl_pos].from_stream = fromproc_stream; xsb_process_table.process[tbl_pos].stderr_stream = fromproc_stderr_stream; concat_array(CTXTc params, " ", xsb_process_table.process[tbl_pos].cmdline,MAX_CMD_LEN); SYS_MUTEX_UNLOCK( MUTEX_SYS_SYSTEM ); return TRUE; } case GET_PROCESS_TABLE: { /* sys_system(3, X). X is bound to the list of the form [process(Pid,To,From,Stderr,Cmdline), ...] */ int i; prolog_term table_term_tail, listHead; prolog_term table_term=reg_term(CTXTc 2); SYS_MUTEX_LOCK( MUTEX_SYS_SYSTEM ); init_process_table(); if (!isref(table_term)) xsb_abort("[GET_PROCESS_TABLE] Arg 1 must be a variable"); table_term_tail = table_term; for (i=0; i<MAX_SUBPROC_NUMBER; i++) { if (!FREE_PROC_TABLE_CELL(xsb_process_table.process[i].pid)) { c2p_list(CTXTc table_term_tail); /* make it into a list */ listHead = p2p_car(table_term_tail); c2p_functor(CTXTc "process", 5, listHead); c2p_int(CTXTc xsb_process_table.process[i].pid, p2p_arg(listHead,1)); c2p_int(CTXTc xsb_process_table.process[i].to_stream, p2p_arg(listHead,2)); c2p_int(CTXTc xsb_process_table.process[i].from_stream, p2p_arg(listHead,3)); c2p_int(CTXTc xsb_process_table.process[i].stderr_stream, p2p_arg(listHead,4)); c2p_string(CTXTc xsb_process_table.process[i].cmdline, p2p_arg(listHead,5)); table_term_tail = p2p_cdr(table_term_tail); } } c2p_nil(CTXTc table_term_tail); /* bind tail to nil */ SYS_MUTEX_UNLOCK( MUTEX_SYS_SYSTEM ); return p2p_unify(CTXTc table_term, reg_term(CTXTc 2)); } case PROCESS_STATUS: { prolog_term pid_term=reg_term(CTXTc 2), status_term=reg_term(CTXTc 3); SYS_MUTEX_LOCK( MUTEX_SYS_SYSTEM ); init_process_table(); if (!(isointeger(pid_term))) xsb_abort("[PROCESS_STATUS] Arg 1 (process id) must be an integer"); pid = (int)oint_val(pid_term); if (!isref(status_term)) xsb_abort("[PROCESS_STATUS] Arg 2 (process status) must be a variable"); switch (process_status(pid)) { case RUNNING: c2p_string(CTXTc "running", status_term); break; case STOPPED: c2p_string(CTXTc "stopped", status_term); break; case EXITED_NORMALLY: c2p_string(CTXTc "exited_normally", status_term); break; case EXITED_ABNORMALLY: c2p_string(CTXTc "exited_abnormally", status_term); break; case ABORTED: c2p_string(CTXTc "aborted", status_term); break; case INVALID: c2p_string(CTXTc "invalid", status_term); break; default: c2p_string(CTXTc "unknown", status_term); } SYS_MUTEX_UNLOCK( MUTEX_SYS_SYSTEM ); return TRUE; } case PROCESS_CONTROL: { /* sys_system(PROCESS_CONTROL, +Pid, +Signal). Signal: wait, kill */ int status; prolog_term pid_term=reg_term(CTXTc 2), signal_term=reg_term(CTXTc 3); SYS_MUTEX_LOCK( MUTEX_SYS_SYSTEM ); init_process_table(); if (!(isointeger(pid_term))) xsb_abort("[PROCESS_CONTROL] Arg 1 (process id) must be an integer"); pid = (int)oint_val(pid_term); if (isstring(signal_term) && strcmp(string_val(signal_term), "kill")==0) { if (KILL_FAILED(pid)) { SYS_MUTEX_UNLOCK( MUTEX_SYS_SYSTEM ); return FALSE; } #ifdef WIN_NT CloseHandle((HANDLE) pid); #endif SYS_MUTEX_UNLOCK( MUTEX_SYS_SYSTEM ); return TRUE; } if (isconstr(signal_term) && strcmp(p2c_functor(signal_term),"wait") == 0 && p2c_arity(signal_term)==1) { int exit_status; if (WAIT(pid, status) < 0) { SYS_MUTEX_UNLOCK( MUTEX_SYS_SYSTEM ); return FALSE; } #ifdef WIN_NT exit_status = status; #else if (WIFEXITED(status)) exit_status = WEXITSTATUS(status); else exit_status = -1; #endif p2p_unify(CTXTc p2p_arg(signal_term,1), makeint(exit_status)); SYS_MUTEX_UNLOCK( MUTEX_SYS_SYSTEM ); return TRUE; } xsb_warn(CTXTc "[PROCESS_CONTROL] Arg 2: Invalid signal specification. Must be `kill' or `wait(Var)'"); return FALSE; } case LIST_DIRECTORY: { /* assume all type- and mode-checking is done in Prolog */ prolog_term handle = reg_term(CTXTc 2); /* ref for handle */ char *dir_name = ptoc_longstring(CTXTc 3); /* +directory name */ prolog_term filename = reg_term(CTXTc 4); /* reference for name of file */ if (is_var(handle)) return xsb_find_first_file(CTXTc handle,dir_name,filename); else return xsb_find_next_file(CTXTc handle,dir_name,filename); } default: xsb_abort("[SYS_SYSTEM] Wrong call number (an XSB bug)"); } /* end case */ return TRUE; }