summaryrefslogtreecommitdiff
path: root/navit/util.c
diff options
context:
space:
mode:
authorwoglinde <woglinde@ffa7fe5e-494d-0410-b361-a75ebd5db220>2011-05-01 22:07:06 +0000
committerwoglinde <woglinde@ffa7fe5e-494d-0410-b361-a75ebd5db220>2011-05-01 22:07:06 +0000
commitafe55be0ddcbfc2bae98e60aaa6f1a30bf16cc5c (patch)
tree620132893a7908704e29d0a1700dfccc5579615b /navit/util.c
parent6cb39f98ed8dc585f589d4036de495df68357003 (diff)
downloadnavit-afe55be0ddcbfc2bae98e60aaa6f1a30bf16cc5c.tar.gz
Add:core:Added command function for spawn systemprocesses and string catenation, patch provided by tryagain
git-svn-id: http://svn.code.sf.net/p/navit/code/trunk/navit@4458 ffa7fe5e-494d-0410-b361-a75ebd5db220
Diffstat (limited to 'navit/util.c')
-rw-r--r--navit/util.c343
1 files changed, 343 insertions, 0 deletions
diff --git a/navit/util.c b/navit/util.c
index f079e3fb8..ba35b4cd8 100644
--- a/navit/util.c
+++ b/navit/util.c
@@ -23,7 +23,15 @@
#include <stdarg.h>
#include <time.h>
#include <limits.h>
+#include <string.h>
+
+#ifdef _POSIX_C_SOURCE
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
#include "util.h"
+#include "debug.h"
void
strtoupper(char *dest, const char *src)
@@ -348,3 +356,338 @@ current_to_iso8601(void)
#endif
return timep;
}
+
+
+struct spawn_process_info {
+#ifdef HAVE_API_WIN32_BASE
+ PROCESS_INFORMATION pr;
+#else
+ pid_t pid; // = -1 if non-blocking spawn isn't supported
+ int status; // exit status if non-blocking spawn isn't supported
+#endif
+};
+
+
+/**
+ * Escape and quote string for shell
+ *
+ * @param in arg string to escape
+ * @returns escaped string
+ */
+char *
+shell_escape(char *arg)
+{
+ char *r;
+ int arglen=strlen(arg);
+ int i,j,rlen;
+#ifdef HAVE_API_WIN32_BASE
+ {
+ int bscount=0;
+ rlen=arglen+3;
+ r=g_new(char,rlen);
+ r[0]='"';
+ for(i=0,j=1;i<arglen;i++) {
+ if(arg[i]=='\\') {
+ bscount++;
+ if(i==(arglen-1)) {
+ // Most special case - last char is
+ // backslash. We can't escape it inside
+ // quoted string due to Win unescaping
+ // rules so quote should be closed
+ // before backslashes and these
+ // backslashes shouldn't be doubled
+ rlen+=bscount;
+ r=g_realloc(r,rlen);
+ r[j++]='"';
+ memset(r+j,'\\',bscount);
+ j+=bscount;
+ }
+ } else {
+ //Any preceeding backslashes will be doubled.
+ bscount*=2;
+ // Double quote needs to be preceeded by
+ // at least one backslash
+ if(arg[i]=='"')
+ bscount++;
+ if(bscount>0) {
+ rlen+=bscount;
+ r=g_realloc(r,rlen);
+ memset(r+j,'\\',bscount);
+ j+=bscount;
+ bscount=0;
+ }
+ r[j++]=arg[i];
+ if(i==(arglen-1)) {
+ r[j++]='"';
+ }
+ }
+ }
+ r[j++]=0;
+ }
+#else
+ {
+ // Will use hard quoting for the whole string
+ // and replace each singular quote found with a '\'' sequence.
+ rlen=arglen+3;
+ r=g_new(char,rlen);
+ r[0]='\'';
+ for(i=0,j=1;i<arglen;i++) {
+ if(arg[i]=='\'') {
+ rlen+=3;
+ r=g_realloc(r,rlen);
+ g_strlcpy(r+j,"'\\''",rlen-j);
+ } else {
+ r[j++]=arg[i];
+ }
+ }
+ r[j++]='\'';
+ r[j++]=0;
+ }
+#endif
+ return r;
+}
+
+#ifndef _POSIX_C_SOURCE
+static char*
+spawn_process_compose_cmdline(char **argv)
+{
+ int i,j;
+ char *cmdline=shell_escape(argv[0]);
+ for(i=1,j=strlen(cmdline);argv[i];i++) {
+ char *arg=shell_escape(argv[i]);
+ int arglen=strlen(arg);
+ cmdline[j]=' ';
+ cmdline=g_realloc(cmdline,j+1+arglen+1);
+ memcpy(cmdline+j+1,arg,arglen+1);
+ g_free(arg);
+ j=j+1+arglen;
+ }
+ return cmdline;
+}
+#endif
+
+#ifdef _POSIX_C_SOURCE
+
+#ifdef _POSIX_THREADS
+#define spawn_process_sigmask(how,set,old) pthread_sigmask(how,set,old)
+#else
+#define spawn_process_sigmask(how,set,old) sigprocmask(how,set,old)
+#endif
+
+GList *spawn_process_children=NULL;
+
+#endif
+
+
+/**
+ * Call external program
+ *
+ * @param in argv NULL terminated list of parameters,
+ * zeroeth argument is program name
+ * @returns 0 - success, >0 - return code, -1 - error
+ */
+struct spawn_process_info*
+spawn_process(char **argv)
+{
+ struct spawn_process_info*r=g_new(struct spawn_process_info,1);
+#ifdef _POSIX_C_SOURCE
+ {
+ pid_t pid;
+
+ sigset_t set, old;
+ sigemptyset(&set);
+ sigaddset(&set,SIGCHLD);
+ spawn_process_sigmask(SIG_BLOCK,&set,&old);
+ pid=fork();
+ if(pid==0) {
+ execvp(argv[0], argv);
+ /*Shouldn't reach here*/
+ exit(1);
+ } else if(pid>0) {
+ r->status=-1;
+ r->pid=pid;
+ spawn_process_children=g_list_prepend(spawn_process_children,r);
+ } else {
+ dbg(0,"fork() returned error.");
+ g_free(r);
+ r=NULL;
+ }
+ spawn_process_sigmask(SIG_SETMASK,&old,NULL);
+ return r;
+ }
+#else
+#ifdef HAVE_API_WIN32_BASE
+ {
+ char *cmdline;
+ LPCWSTR cmd,args;
+ DWORD dwRet;
+
+ // For [desktop] Windows it's adviceable not to use
+ // first CreateProcess parameter because PATH is not used
+ // if it is defined.
+ //
+ // On WinCE 6.0 I was unable to launch anything
+ // without first CreateProcess parameter, also it seems that
+ // no WinCE program has support for quoted strings in arguments.
+ // So...
+#ifdef HAVE_API_WIN32_CE
+ cmdline=g_strjoinv(" ",argv+1);
+ args=newSysString(cmdline);
+ cmd = newSysString(argv[0]);
+ dwRet=CreateProcess(cmd, args, NULL, NULL, 0, 0, NULL, NULL, NULL, &(r->pr));
+ dbg(0, "CreateProcess(%s,%s), PID=%i\n",argv[0],cmdline,r->pr.dwProcessId);
+ g_free(cmd);
+#else
+ cmdline=spawn_process_compose_cmdline(argv);
+ args=newSysString(cmdline);
+ dwRet=CreateProcess(NULL, args, NULL, NULL, 0, 0, NULL, NULL, NULL, &(r->pr));
+ dbg(0, "CreateProcess(%s), PID=%i\n",cmdline,r->pr.dwProcessId);
+#endif
+ g_free(cmdline);
+ g_free(args);
+ return r;
+ }
+#else
+ {
+ char *cmdline=spawn_process_compose_cmdline(argv);
+ int status;
+ dbg(0,"Unblocked spawn_process isn't availiable on this platform.\n");
+ status=system(cmdline);
+ g_free(cmdline);
+ r->status=status;
+ r->pid=0;
+ return r;
+ }
+#endif
+#endif
+}
+
+/**
+ * Check external program status
+ *
+ * @param in *pi pointer to spawn_process_info structure
+ * @param in block =0 do not block =1 block until child terminated
+ * @returns -1 - still running, >=0 program exited,
+ * =255 trminated abnormally or wasn't run at all.
+ *
+ */
+int spawn_process_check_status(struct spawn_process_info *pi, int block)
+{
+ if(pi==NULL) {
+ dbg(0,"Trying to get process status of NULL, assuming process is terminated.\n");
+ return 255;
+ }
+#ifdef HAVE_API_WIN32_BASE
+ {int failcount=0;
+ while(1){
+ DWORD dw;
+ if(GetExitCodeProcess(pi->pr.hProcess,&dw)) {
+ if(dw!=STILL_ACTIVE) {
+ return dw;
+ break;
+ }
+ } else {
+ dbg(0,"GetExitCodeProcess failed. Assuming the process is terminated.");
+ return 255;
+ }
+ if(!block)
+ return -1;
+
+ dw=WaitForSingleObject(pi->pr.hProcess,INFINITE);
+ if(dw==WAIT_FAILED && failcount++==1) {
+ dbg(0,"WaitForSingleObject failed twice. Assuming the process is terminated.");
+ return 0;
+ break;
+ }
+ }
+ }
+#else
+#ifdef _POSIX_C_SOURCE
+ if(pi->status!=-1) {
+ return pi->status;
+ }
+ while(1) {
+ int status;
+ pid_t w=waitpid(pi->pid,&status,block?0:WNOHANG);
+ if(w>0) {
+ if(WIFEXITED(status))
+ pi->status=WEXITSTATUS(status);
+ return pi->status;
+ if(WIFSTOPPED(status)) {
+ dbg(0,"child is stopped by %i signal\n",WSTOPSIG(status));
+ } else if (WIFSIGNALED(status)) {
+ dbg(0,"child terminated by signal %i\n",WEXITSTATUS(status));
+ pi->status=255;
+ return 255;
+ }
+ if(!block)
+ return -1;
+ } else if(w==0) {
+ if(!block)
+ return -1;
+ } else {
+ if(pi->status!=-1) // Signal handler has changed pi->status while in this function
+ return pi->status;
+ dbg(0,"waitpid() indicated error, reporting process termination.\n");
+ return 255;
+ }
+ }
+#else
+ dbg(0, "Non-blocking spawn_process isn't availiable for this platform, repoting process exit status.\n");
+ return pi->status;
+#endif
+#endif
+}
+
+void spawn_process_info_free(struct spawn_process_info *pi)
+{
+ if(pi==NULL)
+ return;
+#ifdef HAVE_API_WIN32_BASE
+ CloseHandle(pi->pr.hProcess);
+ CloseHandle(pi->pr.hThread);
+#endif
+#ifdef _POSIX_C_SOURCE
+ {
+ sigset_t set, old;
+ sigemptyset(&set);
+ sigaddset(&set,SIGCHLD);
+ spawn_process_sigmask(SIG_BLOCK,&set,&old);
+ spawn_process_children=g_list_remove(spawn_process_children,pi);
+ spawn_process_sigmask(SIG_SETMASK,&old,NULL);
+ }
+#endif
+ g_free(pi);
+}
+
+#ifdef _POSIX_C_SOURCE
+static void spawn_process_sigchld(int sig)
+{
+ int status;
+ pid_t pid;
+ while ((pid=waitpid(-1, &status, WNOHANG)) > 0) {
+ GList *el=g_list_first(spawn_process_children);
+ while(el) {
+ struct spawn_process_info *p=el->data;
+ if(p->pid==pid) {
+ p->status=status;
+ }
+ el=g_list_next(el);
+ }
+ }
+}
+#endif
+
+void spawn_process_init()
+{
+#ifdef _POSIX_C_SOURCE
+ struct sigaction act;
+ act.sa_handler=spawn_process_sigchld;
+ act.sa_flags=0;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGCHLD, &act, NULL);
+#endif
+ return;
+}
+
+