Add:core:Added command function for spawn systemprocesses and string catenation,...
authorwoglinde <woglinde@ffa7fe5e-494d-0410-b361-a75ebd5db220>
Sun, 1 May 2011 22:07:06 +0000 (22:07 +0000)
committerwoglinde <woglinde@ffa7fe5e-494d-0410-b361-a75ebd5db220>
Sun, 1 May 2011 22:07:06 +0000 (22:07 +0000)
git-svn-id: https://navit.svn.sourceforge.net/svnroot/navit/trunk@4458 ffa7fe5e-494d-0410-b361-a75ebd5db220

navit/navit/main.c
navit/navit/navit.c
navit/navit/util.c
navit/navit/util.h

index 3db4932..3c6c618 100644 (file)
@@ -49,6 +49,7 @@
 #include "event.h"
 #include "callback.h"
 #include "navit_nls.h"
+#include "util.h"
 #if HAVE_API_WIN32_BASE
 #include <windows.h>
 #include <winbase.h>
@@ -59,15 +60,6 @@ struct map_data *map_data_default;
 
 struct callback_list *cbl;
 
-
-static void sigchld(int sig)
-{
-#if !defined(_WIN32) && !defined(__CEGCC__)
-       int status;
-       while (waitpid(-1, &status, WNOHANG) > 0);
-#endif
-}
-
 #ifdef HAVE_API_WIN32
 void
 setenv(char *var, char *val, int overwrite)
@@ -340,9 +332,8 @@ main_init(const char *program)
                wchar_t wfilename[MAX_PATH + 1];
 #endif
 
-#ifndef _WIN32
-       signal(SIGCHLD, sigchld);
-#endif
+       spawn_process_init();
+
        cbl=callback_list_new();
 #ifdef HAVE_API_WIN32_BASE
        win_set_nls();
index b80d5c7..b75f400 100644 (file)
 #include "vehicleprofile.h"
 #include "sunriset.h"
 #include "bookmarks.h"
+#ifdef HAVE_API_WIN32_BASE
+#include <windows.h>
+#include "util.h"
+#endif
 
 /**
  * @defgroup navit the navit core instance. navit is the object containing nearly everything: A set of maps, one or more vehicle, a graphics object for rendering the map, a gui object for displaying the user interface, a route object, a navigation object and so on. Be warned that it is theoretically possible to have more than one navit object
@@ -1155,6 +1159,104 @@ navit_cmd_fmt_coordinates(struct navit *this, char *function, struct attr **in,
        }
 }
 
+/**
+ * Join several string attributes into one
+ *
+ * @param navit The navit instance
+ * @param function unused (needed to match command function signiture)
+ * @param in input attributes in[0] - separator, in[1..] - attributes to join
+ * @param out output attribute joined attribute as string
+ * @param valid unused 
+ * @returns nothing
+ */
+static void
+navit_cmd_strjoin(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+       struct attr attr;
+       gchar *ret, *sep;
+       int i;
+       attr.type=attr_type_string_begin;
+       attr.u.str=NULL;
+       if(in[0] && in[1]) {
+               sep=attr_to_text(in[0],NULL,1);
+               ret=attr_to_text(in[1],NULL,1);
+               for(i=2;in[i];i++) {
+                       gchar *in_i=attr_to_text(in[i],NULL,1);
+                       gchar *r=g_strjoin(sep,ret,in_i,NULL);
+                       g_free(in_i);
+                       g_free(ret);
+                       ret=r;
+               }
+               g_free(sep);
+               attr.u.str=ret;
+               if(out) {
+                       *out=attr_generic_add_attr(*out, &attr);
+               }
+               g_free(ret);
+       }
+}
+
+/**
+ * Call external program
+ *
+ * @param navit The navit instance
+ * @param function unused (needed to match command function signiture)
+ * @param in input attributes in[0] - name of executable, in[1..] - parameters
+ * @param out output attribute unused
+ * @param valid unused 
+ * @returns nothing
+ */
+static void
+navit_cmd_spawn(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+       int i,j, nparms, nvalid;
+       const char ** argv=NULL;
+       struct spawn_process_info *pi;
+
+       nparms=0;
+       nvalid=0;
+       if(in) {
+               while(in[nparms]) {
+                       if (in[nparms]->type!=attr_none) 
+                               nvalid++;
+                       nparms++;
+               }
+       }
+       
+       if(nvalid>0) {
+               argv=g_new(char*,nvalid+1);
+               for(i=0,j=0;in[i];i++) {
+                       if(in[i]->type!=attr_none ) {
+                               argv[j++]=attr_to_text(in[i],NULL,1);
+                       } else {
+                               dbg(0,"Parameter #%i is attr_none - skipping\n",i);
+                       }
+               }
+               argv[j]=NULL;
+               pi=spawn_process(argv);
+               
+               // spawn_process() testing suite - uncomment following code to test.
+               //sleep(3);
+               // example of non-blocking wait
+               //int st=spawn_process_check_status(pi,0);dbg(0,"status %i\n",st);
+               // example of blocking wait
+               //st=spawn_process_check_status(pi,1);dbg(0,"status %i\n",st);
+               // example of wait after process is finished and status is
+               // already tested
+               //st=spawn_process_check_status(pi,1);dbg(0,"status %i\n",st);
+               // example of wait after process is finished and status is
+               // already tested - unblocked
+               //st=spawn_process_check_status(pi,0);dbg(0,"status %i\n",st);
+               
+               // End testing suite
+               spawn_process_info_free(pi);
+               for(i=0;argv[i];i++)
+                       g_free(argv[i]);
+               g_free(argv);
+       }
+}
+
+
 static struct command_table commands[] = {
        {"zoom_in",command_cast(navit_cmd_zoom_in)},
        {"zoom_out",command_cast(navit_cmd_zoom_out)},
@@ -1170,6 +1272,8 @@ static struct command_table commands[] = {
        {"pop_int",command_cast(navit_cmd_pop_int)},
        {"int_stack_size",command_cast(navit_cmd_int_stack_size)},
        {"toggle_layer",command_cast(navit_cmd_toggle_layer)},
+       {"strjoin",command_cast(navit_cmd_strjoin)},
+       {"spawn",command_cast(navit_cmd_spawn)},
        {"map_add_curr_pos",command_cast(navit_cmd_map_add_curr_pos)},
        {"map_item_set_attr",command_cast(navit_cmd_map_item_set_attr)},
        {"set_attr_var",command_cast(navit_cmd_set_attr_var)},
index f079e3f..ba35b4c 100644 (file)
 #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;
+}
+
+
index f396060..1f8919b 100644 (file)
@@ -46,5 +46,13 @@ int gettimeofday(struct timeval *time, void *);
 
 #endif
 
+struct spawn_process_info;
+char * shell_escape(char *arg);
+struct spawn_process_info* spawn_process(char **argv);
+int spawn_process_check_status(struct spawn_process_info *pi,int block);
+
+void spawn_process_info_free(struct spawn_process_info *pi);
+void spawn_process_init(void);
+
 #endif