summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--navit/main.c15
-rw-r--r--navit/navit.c104
-rw-r--r--navit/util.c343
-rw-r--r--navit/util.h8
4 files changed, 458 insertions, 12 deletions
diff --git a/navit/main.c b/navit/main.c
index 3db493201..3c6c6184b 100644
--- a/navit/main.c
+++ b/navit/main.c
@@ -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();
diff --git a/navit/navit.c b/navit/navit.c
index b80d5c7f4..b75f4006d 100644
--- a/navit/navit.c
+++ b/navit/navit.c
@@ -66,6 +66,10 @@
#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)},
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;
+}
+
+
diff --git a/navit/util.h b/navit/util.h
index f39606091..1f8919b1d 100644
--- a/navit/util.h
+++ b/navit/util.h
@@ -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