Diff of /trunk/src/geotoad.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 199 by harbaum, Thu Nov 19 13:33:35 2009 UTC revision 206 by harbaum, Tue Nov 24 12:42:31 2009 UTC
# Line 19  Line 19 
19    
20  #include "gpxview.h"  #include "gpxview.h"
21    
22    #define __USE_GNU
23    #include <string.h>
24    
25  #include <fcntl.h>  #include <fcntl.h>
26  #include <sys/types.h>  #include <sys/types.h>
27  #include <sys/wait.h>  #include <sys/wait.h>
# Line 37  Line 40 
40    
41  #define BUFFER_SIZE  256  #define BUFFER_SIZE  256
42    
43    typedef enum {
44      GT_STATE_ILLEGAL = 0,
45      GT_STATE_STARTED,
46      GT_STATE_PREMATURE_END,
47      GT_STATE_SAVE_FOUND,
48    } gt_state_t;
49    
50  typedef struct {  typedef struct {
51    appdata_t *appdata;    appdata_t *appdata;
52    
53      GPtrArray *argv;
54      gchar *info;
55      GMutex *update_mutex;
56      GCond *update_cond;
57    
58    GtkWidget *dialog;    gt_state_t state;
59    
60    char buf[BUFFER_SIZE];    GtkWidget *dialog;
   int bused;  
61    
62    /** gdk input tag for async IO */    long pid;
   gint stdout_tag, stderr_tag;  
   gint stdin_fd, stdout_fd, stderr_fd;  
63    
64    struct log_s {    struct log_s {
65      GtkTextBuffer *buffer;      GtkTextBuffer *buffer;
# Line 59  typedef struct { Line 71  typedef struct {
71    
72  } gt_context_t;  } gt_context_t;
73    
74    static void arg_free(gpointer data, gpointer user_data) {
75      if(data) g_free(data);
76    }
77    
78    static void context_free(gt_context_t *context) {
79      printf("freeing context\n");
80    
81      if(context->info) g_free(context->info);
82    
83      g_ptr_array_foreach(context->argv, arg_free, NULL);
84      g_ptr_array_free (context->argv, TRUE);
85    
86      g_free(context);
87    }
88    
89  static void appendf(struct log_s *log, char *colname,  static void appendf(struct log_s *log, char *colname,
90                      const char *fmt, ...) {                      const char *fmt, ...) {
91    va_list args;    va_list args;
# Line 66  static void appendf(struct log_s *log, c Line 93  static void appendf(struct log_s *log, c
93    char *buf = g_strdup_vprintf(fmt, args);    char *buf = g_strdup_vprintf(fmt, args);
94    va_end( args );    va_end( args );
95    
96    printf("append: %s", buf);    //   printf("append: %s", buf);
97    
98    GtkTextTag *tag = NULL;    GtkTextTag *tag = NULL;
99    if(colname)    if(colname)
# Line 83  static void appendf(struct log_s *log, c Line 110  static void appendf(struct log_s *log, c
110    
111    g_free(buf);    g_free(buf);
112    
   gtk_text_buffer_get_end_iter(log->buffer, &end);  
113    gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(log->view),    gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(log->view),
114                                 &end, 0.0, FALSE, 0, 0);                                 &end, 0.0, TRUE, 0, 1.0);
115  }  }
116    
117  /* watch child process and receive events */  // This function receives a requst from a worker thread asking to
118  static void child_state_cb(GPid pid, gint status, gpointer data) {  // update the gui with the required info.
119    gt_context_t *context = (gt_context_t*)data;  gboolean cb_update_job(gt_context_t *context) {
120      if (context->info) {
121    puts("child state");      char *color = NULL;
122        if(strstr(context->info, "Saved to ") != NULL) {
123    if(WIFEXITED(status)) {        context->state = GT_STATE_SAVE_FOUND;
124      printf("child exited with return code %d\n", WEXITSTATUS(status));        color = COLOR_OK;
125    } else      }
     printf("child failed\n");  
   
   puts("gt exited");  
   
   appendf(&context->log, COLOR_SYSTEM, "GeoToad finished\n");  
126    
127    appendf(&context->log, COLOR_SYSTEM, "TODO: free context!!!\n");      if(strcasestr(context->info, "error") != NULL)
128    //    printf("freeing context\n");        color = COLOR_ERR;
   //    g_free(context);  
129    
130    /* Reap child if needed. */      appendf(&context->log, color, context->info);
131    waitpid (pid, NULL, WNOHANG);  
132        g_free(context->info);
133        context->info = NULL;
134      }
135    
136      // Indicate that the update is done
137      g_mutex_lock(context->update_mutex);
138      g_cond_signal(context->update_cond);
139      g_mutex_unlock(context->update_mutex);
140    
141      return FALSE;
142  }  }
143    
144  static gboolean child_input_cb(GIOChannel *source,  // A helper function run in the job thread receiving the string that
145                                 GIOCondition condition,  // should be displayed in the textview.
146                                 gpointer data) {  void job_add_to_text_viewer(gt_context_t *context,
147    gt_context_t *context = (gt_context_t*)data;                              const char *info)
148    int fd = g_io_channel_unix_get_fd(source);  {
149    ssize_t bytes;    /* TODO: make this cope with format strings */
150      /* TODO: make this cope with colors */
151    g_assert(condition == G_IO_IN);    context->info = g_strdup(info);
152    
153      // Lock mutex to make sure that we will receive the condition signal
154      g_mutex_lock(context->update_mutex);
155      g_idle_add((GSourceFunc)cb_update_job, context);
156    
157      // Wait for cb_update_job to tell me that the update is done
158      g_cond_wait(context->update_cond,
159                  context->update_mutex);
160      g_mutex_unlock(context->update_mutex);
161    }
162    
163    /* append to current buffer content */  /* custom version of popen to get access to the pid and to be able */
164    while( (bytes = read(fd, context->buf+context->bused,  /* to slaughter the child process */
165                   sizeof(context->buf) - context->bused - 1)) > 0) {  static FILE *gt_popen(char **args, const char *type, long *tid) {
166      context->bused += bytes;    int   p[2];
167      context->buf[context->bused]='\0';    FILE *fp;
168    
169      if (*type != 'r' && *type != 'w')
170        return NULL;
171    
172      if (pipe(p) < 0)
173        return NULL;
174    
175      if ((*tid = fork()) > 0) { /* then we are the parent */
176        if (*type == 'r') {
177          close(p[1]);
178          fp = fdopen(p[0], type);
179        } else {
180          close(p[0]);
181          fp = fdopen(p[1], type);
182        }
183    
184        return fp;
185      } else if (*tid == 0) {  /* we're the child */
186    
187      /* consume line by line */      /* make our thread id the process group leader */
188      gchar *ptr = context->buf;      setpgid(0, 0);
189    
190        if (*type == 'r') {
191          fflush(stdout);
192          fflush(stderr);
193          close(1);
194          if (dup(p[1]) < 0)
195            perror("dup of write side of pipe failed");
196          close(2);
197          if (dup(p[1]) < 0)
198            perror("dup of write side of pipe failed");
199        } else {
200          close(0);
201          if (dup(p[0]) < 0)
202            perror("dup of read side of pipe failed");
203        }
204    
205        close(p[0]); /* close since we dup()'ed what we needed */
206        close(p[1]);
207    
208      /* parse as long as the buffer contains a newline */      execve(args[0], args, NULL);
209      while( strchr(ptr, '\n') != NULL) {  
210        char *p = strchr(ptr, '\n');      printf("gt_popen(): execve(%s) failed!\n", args[0]);
211        *p = '\0';    } else {         /* we're having major problems... */
212        close(p[0]);
213        char *color = NULL;      close(p[1]);
214        if(strstr(ptr, "Saved to ") != NULL)      printf("gt_popen(): fork() failure!\n");
215          color = COLOR_OK;    }
216    
217      return NULL;
218    }
219    
220        appendf(&context->log, color, "%s\n", ptr);  // The thread entry point. It will do the job, send the data to the
221    // GUI and self destruct when it is done.
222    static gpointer thread_worker(gt_context_t *context)
223    {
224      FILE *fh;
225      GIOStatus status;
226      GError *error = NULL;
227      gsize length;
228      gsize terminator_pos;
229      gchar *str_return;
230    
231      fh = gt_popen((char**)(context->argv->pdata),"r", &context->pid);
232      if(!fh) {
233        appendf(&context->log, COLOR_ERR, _("Failed to run GeoToad"));
234        return NULL;
235      }
236    
237        ptr = p+1;    /* switch to line buffered mode */
238      }    if(setvbuf(fh, NULL, _IOLBF, 0))
239        perror("setvbuf(_IOLBF)");
240    
241      GIOChannel *gh = g_io_channel_unix_new(fileno(fh));
242    
243      /* move remaining bytes down the buffer */    while( (status = g_io_channel_read_line(gh,
244      memmove(context->buf, ptr, sizeof(context->buf)-(ptr-context->buf));                                            &str_return,
245      context->bused -= ptr - context->buf;                                            &length,
246                                              &terminator_pos,
247      /* check if buffer is full but doesn't contain any newline */                                            &error)) == G_IO_STATUS_NORMAL) {
248      if(context->bused >= sizeof(context->buf)-1) {      job_add_to_text_viewer(context, str_return);
249        printf("ERROR: reply buffer overflow\n");      g_free(str_return);
       context->bused = 0;  
     }  
250    }    }
251    
252      g_io_channel_unref(gh);
253      pclose(fh);
254      job_add_to_text_viewer(context, "Job done!");
255    
256      g_thread_exit(NULL);
257      context_free(context);
258    
259      return NULL;
260    }
261    
262    return TRUE;  
263    static void arg_dsp(gpointer data, gpointer user_data) {
264      gt_context_t *context = (gt_context_t*)user_data;
265    
266      if(data)
267        appendf(&context->log, COLOR_SYSTEM, "%s\n", data);
268  }  }
269    
270  static void run(gt_context_t *context) {  static void run(gt_context_t *context) {
271    /* setup context */    GError *error = NULL;
272    context->bused = 0;    char str[8];
   context->stdout_tag = -1;  
   context->stderr_tag = -1;  
   context->stdin_fd  = -1;  
   context->stderr_fd = -1;  
   context->stderr_fd = -1;  
273    
274    /* build list of arguments to call geotoad */    /* build list of arguments to call geotoad */
275    GPtrArray *gt_argv = g_ptr_array_new();    context->argv = g_ptr_array_new();
276    g_ptr_array_add (gt_argv, GEOTOAD);    g_ptr_array_add (context->argv, g_strdup_printf(GEOTOAD "a"));
277    g_ptr_array_add (gt_argv, "--distanceMax=1.0");    g_ascii_dtostr(str, sizeof(str), context->appdata->gt.distance);
278    g_ptr_array_add (gt_argv, "--output=gtoad.gpx");    g_ptr_array_add (context->argv, g_strdup_printf("--distanceMax=%s", str));
279    g_ptr_array_add (gt_argv, "--password=winterblume");    g_ptr_array_add (context->argv, g_strdup_printf("--output=%s", context->appdata->gt.filename));
280    g_ptr_array_add (gt_argv, "--queryType=coord");    g_ptr_array_add (context->argv, g_strdup_printf("--password=%s", context->appdata->gt.password));
281    g_ptr_array_add (gt_argv, "--user=Tantil");    g_ptr_array_add (context->argv, g_strdup_printf("--queryType=coord"));
282      g_ptr_array_add (context->argv, g_strdup_printf("--user=%s", context->appdata->username));
283    
284    /* check if we need to add proxy config */    /* check if we need to add proxy config */
285    char *proxy = NULL;    char *proxy = NULL;
# Line 182  static void run(gt_context_t *context) { Line 288  static void run(gt_context_t *context) {
288         context->appdata->proxy->authentication_user &&         context->appdata->proxy->authentication_user &&
289         context->appdata->proxy->authentication_password)         context->appdata->proxy->authentication_password)
290        proxy = g_strdup_printf("--proxy=http://%s:%s@%s:%d",        proxy = g_strdup_printf("--proxy=http://%s:%s@%s:%d",
291                                context->appdata->proxy->authentication_user,                                context->appdata->proxy->authentication_user,
292                                context->appdata->proxy->authentication_password,                                context->appdata->proxy->authentication_password,
293                                context->appdata->proxy->host,                                context->appdata->proxy->host,
294                                context->appdata->proxy->port);                                context->appdata->proxy->port);
295      else      else
296        proxy = g_strdup_printf("--proxy=http://%s:%d",        proxy = g_strdup_printf("--proxy=http://%s:%d",
297                                context->appdata->proxy->host,                                context->appdata->proxy->host,
298                                context->appdata->proxy->port);                                context->appdata->proxy->port);
299    
300      appendf(&context->log, COLOR_SYSTEM, "Using proxy: %s\n", proxy);      g_ptr_array_add (context->argv, proxy);
     g_ptr_array_add (gt_argv, proxy);  
301    }    }
302    
303    g_ptr_array_add (gt_argv, "N49 00.000 E008 23.000");    /* convert coordinates into simple ascii format */
304    g_ptr_array_add (gt_argv, NULL);    char n = (context->appdata->gt.lat >= 0)?'N':'S';
305      char e = (context->appdata->gt.lon >= 0)?'E':'W';
306    GError *error=NULL;    float lat = fabs(context->appdata->gt.lat);
307    GPid pid;    float lon = fabs(context->appdata->gt.lon);
308    GSource *gt_watch;    float lat_mint, lat_int, lat_frac = modff(lat, &lat_int);
309      float lon_mint, lon_int, lon_frac = modff(lon, &lon_int);
310    if (!g_spawn_async_with_pipes (NULL, /* CWD */    lat_frac = modff(lat_frac*60.0, &lat_mint);
311                                   (char **) gt_argv->pdata, /* argv */    lon_frac = modff(lon_frac*60.0, &lon_mint);
312                                   NULL, /* envp */  
313                                   G_SPAWN_DO_NOT_REAP_CHILD, /* flags */    g_ptr_array_add (context->argv,
314                                   NULL, /* child setup */             g_strdup_printf("%c%02u %02u.%03u %c%03u %02u.%03u",
315                                   NULL, /* user data */             n, (int)lat_int, (int)lat_mint, (int)(lat_frac*1000.0+0.5),
316                                   &pid,             e, (int)lon_int, (int)lon_mint, (int)(lon_frac*1000.0+0.5)));
317                                   &context->stdin_fd,  
318                                   &context->stdout_fd,    //  g_ptr_array_add (context->argv, g_strdup_printf("2>&1"));
319                                   &context->stderr_fd,    g_ptr_array_add (context->argv, NULL);
320                                   &error)) {  
321      g_ptr_array_free(gt_argv, TRUE);    /* show all entries */
322      if(proxy) g_free(proxy);    g_ptr_array_foreach(context->argv, arg_dsp, context);
323      appendf(&context->log, COLOR_ERR,  
324              _("GeoToad failed to start!\n%s\n"), error->message);    g_thread_create((GThreadFunc)thread_worker, context, FALSE, &error);
325      if (error) {
326        appendf(&context->log, COLOR_ERR, "Error: %s\n", error->message);
327      g_error_free(error);      g_error_free(error);
     return;  
328    }    }
   
   g_ptr_array_free (gt_argv, TRUE);  
   if(proxy) g_free(proxy);  
   
   gt_watch = g_child_watch_source_new(pid);  
   g_source_set_callback(gt_watch, (GSourceFunc) child_state_cb, context, NULL);  
   
   g_source_attach(gt_watch, NULL);  
   g_source_unref(gt_watch);  
   
   /* make nonblocking */  
   if(fcntl(context->stdout_fd, F_SETFL, O_NONBLOCK) == -1)  
     perror("fcntl failed");  
   
   if(fcntl(context->stderr_fd, F_SETFL, O_NONBLOCK) == -1)  
     perror("fcntl failed");  
   
   GIOChannel *ioc = g_io_channel_unix_new(context->stdout_fd);  
   g_io_channel_set_close_on_unref (ioc, TRUE);  
   g_io_channel_set_encoding (ioc, NULL, NULL);  
   g_io_add_watch(ioc, G_IO_IN,  child_input_cb, context);  
   g_io_channel_unref(ioc);  
   
   //  ioc = g_io_channel_unix_new(context->stderr_fd);  
   //  g_io_add_watch(ioc, G_IO_IN,  child_input_cb, context);  
   //  g_io_channel_unref(ioc);  
329  }  }
330    
331  /* show text window and display output of running geotoad */  /* show text window and display output of running geotoad */
# Line 299  static void gui_run(gt_context_t *contex Line 379  static void gui_run(gt_context_t *contex
379    gtk_dialog_run(GTK_DIALOG(dialog));    gtk_dialog_run(GTK_DIALOG(dialog));
380    
381    gtk_widget_destroy(dialog);    gtk_widget_destroy(dialog);
382    
383      errorf("Fertig!\n");
384    
385  }  }
386    
387  static void on_browse(GtkWidget *widget, gpointer data) {  static void on_browse(GtkWidget *widget, gpointer data) {
# Line 385  static gboolean gui_setup(gt_context_t * Line 468  static gboolean gui_setup(gt_context_t *
468    context->lon = lon_entry_new(appdata->gt.lon);    context->lon = lon_entry_new(appdata->gt.lon);
469    gtk_box_pack_start_defaults(GTK_BOX(ihbox), context->lon);    gtk_box_pack_start_defaults(GTK_BOX(ihbox), context->lon);
470    gtk_box_pack_start_defaults(GTK_BOX(vbox), ihbox);    gtk_box_pack_start_defaults(GTK_BOX(vbox), ihbox);
471    context->dst = dist_entry_new(appdata->gt.distance, appdata->imperial);    float dst = appdata->gt.distance;  // distance is given in kilometers
472      if(appdata->imperial) dst /= 1.609344;
473      context->dst = dist_entry_new(dst, appdata->imperial);
474    gtk_box_pack_start_defaults(GTK_BOX(vbox), context->dst);    gtk_box_pack_start_defaults(GTK_BOX(vbox), context->dst);
475    gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);    gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
476    
# Line 419  static gboolean gui_setup(gt_context_t * Line 504  static gboolean gui_setup(gt_context_t *
504    gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);    gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
505    
506    vbox = gtk_vbox_new(FALSE, 0);    vbox = gtk_vbox_new(FALSE, 0);
507  #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)  #ifndef FREMANTLE
508    context->username = gtk_entry_new();    context->username = gtk_entry_new();
509    context->password = gtk_entry_new();    context->password = gtk_entry_new();
510  #else  #else
# Line 433  static gboolean gui_setup(gt_context_t * Line 518  static gboolean gui_setup(gt_context_t *
518    gtk_entry_set_visibility(GTK_ENTRY(context->password), FALSE);    gtk_entry_set_visibility(GTK_ENTRY(context->password), FALSE);
519    
520    /* set saved defaults */    /* set saved defaults */
521    if(appdata->gt.username)    if(appdata->username)
522      gtk_entry_set_text(GTK_ENTRY(context->username),      gtk_entry_set_text(GTK_ENTRY(context->username),
523                         appdata->gt.username);                         appdata->username);
524    
525    if(appdata->gt.password)    if(appdata->gt.password)
526      gtk_entry_set_text(GTK_ENTRY(context->password),      gtk_entry_set_text(GTK_ENTRY(context->password),
# Line 453  static gboolean gui_setup(gt_context_t * Line 538  static gboolean gui_setup(gt_context_t *
538    
539    if(gtk_dialog_run(GTK_DIALOG(context->dialog)) == GTK_RESPONSE_OK) {    if(gtk_dialog_run(GTK_DIALOG(context->dialog)) == GTK_RESPONSE_OK) {
540    
541        /* parse coordinates */
542        appdata->gt.lat = lat_get(context->lat);
543        appdata->gt.lon = lon_get(context->lon);
544    
545      /* save values */      /* save values */
546      if(appdata->gt.username) g_free(appdata->gt.username);      if(appdata->username) g_free(appdata->username);
547      appdata->gt.username =      appdata->username =
548        g_strdup(gtk_entry_get_text(GTK_ENTRY(context->username)));        g_strdup(gtk_entry_get_text(GTK_ENTRY(context->username)));
549    
550      if(appdata->gt.password) g_free(appdata->gt.password);      if(appdata->gt.password) g_free(appdata->gt.password);
# Line 466  static gboolean gui_setup(gt_context_t * Line 555  static gboolean gui_setup(gt_context_t *
555      appdata->gt.filename =      appdata->gt.filename =
556        g_strdup(gtk_label_get_text(GTK_LABEL(context->filename)));        g_strdup(gtk_label_get_text(GTK_LABEL(context->filename)));
557    
558      ok = TRUE;      /* get distance in kilometers */
559        appdata->gt.distance = dist_get(context->dst, FALSE);
560    
561    
562        /* check for valid entries */
563        if(isnan(appdata->gt.lat) || isnan(appdata->gt.lon) ||
564           isnan(appdata->gt.distance) || !appdata->gt.filename ||
565           !appdata->username || !appdata->gt.password)
566          errorf(_("GeoToad setup not complete"));
567        else
568          ok = TRUE;
569    }    }
570    
571    gtk_widget_destroy(context->dialog);    gtk_widget_destroy(context->dialog);
# Line 484  void geotoad(appdata_t *appdata) { Line 583  void geotoad(appdata_t *appdata) {
583    gt_context_t *context = g_new0(gt_context_t, 1);    gt_context_t *context = g_new0(gt_context_t, 1);
584    context->appdata = appdata;    context->appdata = appdata;
585    
586      context->update_mutex = g_mutex_new();
587      context->update_cond = g_cond_new();
588      //  context->mutex_to_run = mutex_to_run;
589    
590    printf("geoToad\n");    printf("geoToad\n");
591    
592    if(gui_setup(context))    if(gui_setup(context))

Legend:
Removed from v.199  
changed lines
  Added in v.206