]> git.armaanb.net Git - chorizo.git/blob - browser.c
c8b93533f672bd99e62f223297e548fbdd77797a
[chorizo.git] / browser.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <string.h>
7
8 #include <gtk/gtk.h>
9 #include <gdk/gdkx.h>
10 #include <gdk/gdkkeysyms.h>
11 #include <gio/gio.h>
12 #include <webkit/webkit.h>
13
14
15 static void adblock(WebKitWebView *, WebKitWebFrame *, WebKitWebResource *,
16                     WebKitNetworkRequest *, WebKitNetworkResponse *, gpointer);
17 static void adblock_load(void);
18 static void client_destroy(GtkWidget *obj, gpointer data);
19 static gboolean client_destroy_request(WebKitWebView *, gpointer);
20 static WebKitWebView *client_new(const gchar *uri);
21 static WebKitWebView *client_new_request(WebKitWebView *, WebKitWebFrame *,
22                                          gpointer);
23 static void cooperation_setup(void);
24 static void changed_load_status(GObject *obj, GParamSpec *pspec,
25                                 gpointer data);
26 static void changed_title(GObject *, GParamSpec *, gpointer);
27 static void changed_uri(GObject *, GParamSpec *, gpointer);
28 static gboolean download_request(WebKitWebView *, WebKitWebFrame *,
29                                  WebKitNetworkRequest *, gchar *,
30                                  WebKitWebPolicyDecision *, gpointer);
31 static gboolean download_wget(WebKitWebView *, WebKitDownload *, gpointer);
32 static gchar *ensure_url_scheme(const gchar *);
33 static void grab_environment_configuration(void);
34 static void hover_web_view(WebKitWebView *, gchar *, gchar *, gpointer);
35 static gboolean key_location(GtkWidget *, GdkEvent *, gpointer);
36 static gboolean key_web_view(GtkWidget *, GdkEvent *, gpointer);
37 static gboolean remote_msg(GIOChannel *, GIOCondition, gpointer);
38 static void search(gpointer, gint);
39 static void scroll(GtkAdjustment *, gint, gdouble);
40 static Window tabbed_launch(void);
41 static void usage(void);
42
43
44 struct Client
45 {
46         GtkWidget *win;
47         GtkWidget *vbox;
48         GtkWidget *location;
49         GtkWidget *status;
50         GtkWidget *scroll;
51         GtkWidget *web_view;
52 };
53
54
55 static gchar *accepted_language = "en-US";
56 static GSList *adblock_patterns = NULL;
57 static gint clients = 0;
58 static gboolean cooperative_alone = TRUE;
59 static gboolean cooperative_instances = TRUE;
60 static int cooperative_pipe_fp = 0;
61 static gchar *download_dir = "/tmp";
62 static Window embed = 0;
63 static gchar *first_uri = NULL;
64 static gdouble global_zoom = 1.0;
65 static gboolean language_is_set = FALSE;
66 static gchar *search_text = NULL;
67 static gboolean show_all_requests = FALSE;
68 static gboolean tabbed_automagic = TRUE;
69
70
71 void
72 adblock(WebKitWebView *web_view, WebKitWebFrame *frame,
73         WebKitWebResource *resource, WebKitNetworkRequest *request,
74         WebKitNetworkResponse *response, gpointer data)
75 {
76         GSList *it = adblock_patterns;
77         const gchar *uri;
78
79         (void)web_view;
80         (void)frame;
81         (void)resource;
82         (void)response;
83         (void)data;
84
85         uri = webkit_network_request_get_uri(request);
86         if (show_all_requests)
87                 fprintf(stderr, "   -> %s\n", uri);
88
89         while (it)
90         {
91                 if (g_regex_match((GRegex *)(it->data), uri, 0, NULL))
92                 {
93                         webkit_network_request_set_uri(request, "about:blank");
94                         if (show_all_requests)
95                                 fprintf(stderr, "            BLOCKED!\n");
96                         return;
97                 }
98                 it = g_slist_next(it);
99         }
100 }
101
102 void
103 adblock_load(void)
104 {
105         GRegex *re = NULL;
106         GError *err = NULL;
107         GIOChannel *channel = NULL;
108         gchar *path = NULL;
109         gchar *buf = NULL;
110
111         path = g_build_filename(g_get_user_config_dir(), __NAME__, "adblock.black",
112                                 NULL);
113         channel = g_io_channel_new_file(path, "r", &err);
114         if (channel != NULL)
115         {
116                 while (g_io_channel_read_line(channel, &buf, NULL, NULL, NULL)
117                        == G_IO_STATUS_NORMAL)
118                 {
119                         g_strstrip(buf);
120                         re = g_regex_new(buf,
121                                          G_REGEX_CASELESS | G_REGEX_OPTIMIZE,
122                                          G_REGEX_MATCH_PARTIAL, &err);
123                         if (err != NULL)
124                         {
125                                 fprintf(stderr, __NAME__": Could not compile regex: %s\n", buf);
126                                 g_error_free(err);
127                                 err = NULL;
128                         }
129                         adblock_patterns = g_slist_append(adblock_patterns, re);
130
131                         g_free(buf);
132                 }
133         }
134         g_free(path);
135 }
136
137 void
138 client_destroy(GtkWidget *obj, gpointer data)
139 {
140         struct Client *c = (struct Client *)data;
141
142         (void)obj;
143         (void)data;
144
145         free(c);
146         clients--;
147
148         if (clients == 0)
149                 gtk_main_quit();
150 }
151
152 gboolean
153 client_destroy_request(WebKitWebView *web_view, gpointer data)
154 {
155         struct Client *c = (struct Client *)data;
156
157         (void)web_view;
158
159         gtk_widget_destroy(c->win);
160
161         return TRUE;
162 }
163
164 WebKitWebView *
165 client_new(const gchar *uri)
166 {
167         struct Client *c;
168         gchar *f;
169
170         if (uri != NULL && cooperative_instances && !cooperative_alone)
171         {
172                 write(cooperative_pipe_fp, uri, strlen(uri));
173                 write(cooperative_pipe_fp, "\n", 1);
174                 return NULL;
175         }
176
177         c = malloc(sizeof(struct Client));
178         if (!c)
179         {
180                 fprintf(stderr, __NAME__": fatal: malloc failed\n");
181                 exit(EXIT_FAILURE);
182         }
183
184         c->win = NULL;
185         if (embed != 0)
186         {
187                 c->win = gtk_plug_new(embed);
188                 if (!gtk_plug_get_embedded(GTK_PLUG(c->win)))
189                 {
190                         fprintf(stderr, __NAME__": Can't plug-in to XID %ld.\n", embed);
191                         gtk_widget_destroy(c->win);
192                         c->win = NULL;
193                         embed = 0;
194                 }
195         }
196
197         if (c->win == NULL)
198         {
199                 c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
200                 gtk_window_set_wmclass(GTK_WINDOW(c->win), __NAME__, __NAME_CAPITALIZED__);
201         }
202
203         /* When using Gtk2, it only shows a white area when run in suckless'
204          * tabbed. It appears we need to set a default window size for this
205          * to work. This is not needed when using Gtk3. */
206         gtk_window_set_default_size(GTK_WINDOW(c->win), 1024, 768);
207
208         g_signal_connect(G_OBJECT(c->win), "destroy", G_CALLBACK(client_destroy), c);
209         gtk_window_set_title(GTK_WINDOW(c->win), __NAME__);
210
211         c->web_view = webkit_web_view_new();
212
213         /* XXX I really do want to enable this option. However, I get
214          * reproducable crashes with it enabled. I've seen bug reports from
215          * 2010 about this... WebKit crashes in libpixman, so maybe it's not
216          * a WebKit issue.
217          * Yeah, well. I'll turn it off for now. */
218         /*webkit_web_view_set_full_content_zoom(WEBKIT_WEB_VIEW(c->web_view), TRUE);*/
219
220         webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(c->web_view), global_zoom);
221         g_signal_connect(G_OBJECT(c->web_view), "notify::title",
222                          G_CALLBACK(changed_title), c);
223         g_signal_connect(G_OBJECT(c->web_view), "notify::uri",
224                          G_CALLBACK(changed_uri), c);
225         g_signal_connect(G_OBJECT(c->web_view), "notify::load-status",
226                          G_CALLBACK(changed_load_status), c);
227         g_signal_connect(G_OBJECT(c->web_view), "create-web-view",
228                          G_CALLBACK(client_new_request), NULL);
229         g_signal_connect(G_OBJECT(c->web_view), "close-web-view",
230                          G_CALLBACK(client_destroy_request), c);
231         g_signal_connect(G_OBJECT(c->web_view),
232                          "mime-type-policy-decision-requested",
233                          G_CALLBACK(download_request), NULL);
234         g_signal_connect(G_OBJECT(c->web_view), "download-requested",
235                          G_CALLBACK(download_wget), NULL);
236         g_signal_connect(G_OBJECT(c->web_view), "key-press-event",
237                          G_CALLBACK(key_web_view), c);
238         g_signal_connect(G_OBJECT(c->web_view), "button-press-event",
239                          G_CALLBACK(key_web_view), c);
240         g_signal_connect(G_OBJECT(c->web_view), "hovering-over-link",
241                          G_CALLBACK(hover_web_view), c);
242         g_signal_connect(G_OBJECT(c->web_view), "resource-request-starting",
243                          G_CALLBACK(adblock), NULL);
244
245         if (!language_is_set)
246         {
247                 g_object_set(webkit_get_default_session(), "accept-language",
248                              accepted_language, NULL);
249                 language_is_set = TRUE;
250         }
251
252         c->scroll = gtk_scrolled_window_new(NULL, NULL);
253
254         gtk_container_add(GTK_CONTAINER(c->scroll), c->web_view);
255
256         c->location = gtk_entry_new();
257         g_signal_connect(G_OBJECT(c->location), "key-press-event",
258                          G_CALLBACK(key_location), c);
259
260         c->status = gtk_statusbar_new();
261         gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(c->status), FALSE);
262
263         c->vbox = gtk_vbox_new(FALSE, 2);
264         gtk_box_pack_start(GTK_BOX(c->vbox), c->location, FALSE, FALSE, 0);
265         gtk_container_add(GTK_CONTAINER(c->vbox), c->scroll);
266         gtk_box_pack_end(GTK_BOX(c->vbox), c->status, FALSE, FALSE, 0);
267
268         gtk_container_add(GTK_CONTAINER(c->win), c->vbox);
269
270         gtk_widget_grab_focus(c->web_view);
271         gtk_widget_show_all(c->win);
272
273         if (uri != NULL)
274         {
275                 f = ensure_url_scheme(uri);
276                 if (show_all_requests)
277                         fprintf(stderr, "====> %s\n", uri);
278                 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), f);
279                 g_free(f);
280         }
281
282         clients++;
283
284         return WEBKIT_WEB_VIEW(c->web_view);
285 }
286
287 WebKitWebView *
288 client_new_request(WebKitWebView *web_view, WebKitWebFrame *frame, gpointer data)
289 {
290         (void)web_view;
291         (void)frame;
292         (void)data;
293
294         return client_new(NULL);
295 }
296
297 void
298 cooperation_setup(void)
299 {
300         GIOChannel *towatch;
301         gchar *fifopath;
302
303         fifopath = g_build_filename(g_get_user_runtime_dir(), __NAME__".fifo", NULL);
304
305         if (!g_file_test(fifopath, G_FILE_TEST_EXISTS))
306                 mkfifo(fifopath, 0600);
307
308         cooperative_pipe_fp = open(fifopath, O_WRONLY | O_NONBLOCK);
309         if (!cooperative_pipe_fp)
310         {
311                 fprintf(stderr, __NAME__": Can't open FIFO at all.\n");
312         }
313         else
314         {
315                 if (write(cooperative_pipe_fp, "", 0) == -1)
316                 {
317                         /* Could not do an empty write to the FIFO which means there's
318                          * no one listening. */
319                         close(cooperative_pipe_fp);
320                         towatch = g_io_channel_new_file(fifopath, "r+", NULL);
321                         g_io_add_watch(towatch, G_IO_IN, (GIOFunc)remote_msg, NULL);
322                 }
323                 else
324                         cooperative_alone = FALSE;
325         }
326
327         g_free(fifopath);
328 }
329
330 void
331 changed_load_status(GObject *obj, GParamSpec *pspec, gpointer data)
332 {
333         struct Client *c = (struct Client *)data;
334
335         (void)obj;
336         (void)pspec;
337
338         if (webkit_web_view_get_load_status(WEBKIT_WEB_VIEW(c->web_view))
339             == WEBKIT_LOAD_FINISHED)
340         {
341                 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
342                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Finished.");
343         }
344         else
345         {
346                 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
347                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Loading...");
348         }
349 }
350
351 void
352 changed_title(GObject *obj, GParamSpec *pspec, gpointer data)
353 {
354         const gchar *t;
355         struct Client *c = (struct Client *)data;
356
357         (void)obj;
358         (void)pspec;
359
360         t = webkit_web_view_get_title(WEBKIT_WEB_VIEW(c->web_view));
361         gtk_window_set_title(GTK_WINDOW(c->win), (t == NULL ? __NAME__ : t));
362 }
363
364 void
365 changed_uri(GObject *obj, GParamSpec *pspec, gpointer data)
366 {
367         const gchar *t;
368         struct Client *c = (struct Client *)data;
369
370         (void)obj;
371         (void)pspec;
372
373         t = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view));
374         gtk_entry_set_text(GTK_ENTRY(c->location), (t == NULL ? __NAME__ : t));
375 }
376
377 gboolean
378 download_request(WebKitWebView *web_view, WebKitWebFrame *frame,
379                  WebKitNetworkRequest *request, gchar *mime_type,
380                  WebKitWebPolicyDecision *policy_decision, gpointer data)
381 {
382         (void)frame;
383         (void)request;
384         (void)data;
385
386         if (!webkit_web_view_can_show_mime_type(web_view, mime_type))
387         {
388                 webkit_web_policy_decision_download(policy_decision);
389                 return TRUE;
390         }
391         return FALSE;
392 }
393
394 gboolean
395 download_wget(WebKitWebView *web_view, WebKitDownload *download, gpointer data)
396 {
397         const gchar *uri;
398         char id[16] = "";
399         gint ret;
400
401         (void)web_view;
402         (void)data;
403
404         uri = webkit_download_get_uri(download);
405         if (fork() == 0)
406         {
407                 chdir(download_dir);
408                 if (embed == 0)
409                         ret = execlp("xterm", "xterm", "-hold", "-e", "wget", uri, NULL);
410                 else
411                 {
412                         if (snprintf(id, 16, "%ld", embed) >= 16)
413                         {
414                                 fprintf(stderr, __NAME__": id for xterm embed truncated!\n");
415                                 exit(EXIT_FAILURE);
416                         }
417                         ret = execlp("xterm", "xterm", "-hold", "-into", id, "-e", "wget",
418                                      uri, NULL);
419                 }
420
421                 if (ret == -1)
422                 {
423                         fprintf(stderr, __NAME__": exec'ing xterm for download");
424                         perror(" failed");
425                         exit(EXIT_FAILURE);
426                 }
427         }
428
429         return FALSE;
430 }
431
432 gchar *
433 ensure_url_scheme(const gchar *t)
434 {
435         gchar *f;
436
437         f = g_ascii_strdown(t, -1);
438         if (!g_str_has_prefix(f, "http:") &&
439             !g_str_has_prefix(f, "https:") &&
440             !g_str_has_prefix(f, "file:") &&
441             !g_str_has_prefix(f, "about:"))
442         {
443                 g_free(f);
444                 f = g_strdup_printf("http://%s", t);
445                 return f;
446         }
447         else
448                 return g_strdup(t);
449 }
450
451 void
452 grab_environment_configuration(void)
453 {
454         const gchar *e;
455
456         e = g_getenv(__NAME_UPPERCASE__"_ACCEPTED_LANGUAGE");
457         if (e != NULL)
458                 accepted_language = g_strdup(e);
459
460         e = g_getenv(__NAME_UPPERCASE__"_DOWNLOAD_DIR");
461         if (e != NULL)
462                 download_dir = g_strdup(e);
463
464         e = g_getenv(__NAME_UPPERCASE__"_ZOOM");
465         if (e != NULL)
466                 global_zoom = atof(e);
467 }
468
469 void
470 hover_web_view(WebKitWebView *web_view, gchar *title, gchar *uri, gpointer data)
471 {
472         struct Client *c = (struct Client *)data;
473
474         (void)web_view;
475         (void)title;
476
477         gtk_statusbar_pop(GTK_STATUSBAR(c->status), 0);
478         if (uri != NULL)
479                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 0, uri);
480 }
481
482 gboolean
483 key_location(GtkWidget *widget, GdkEvent *event, gpointer data)
484 {
485         struct Client *c = (struct Client *)data;
486         const gchar *t;
487         gchar *f;
488
489         (void)widget;
490
491         if (event->type == GDK_KEY_PRESS)
492         {
493                 switch (((GdkEventKey *)event)->keyval)
494                 {
495                         case GDK_KEY_Return:
496                                 gtk_widget_grab_focus(c->web_view);
497                                 t = gtk_entry_get_text(GTK_ENTRY(c->location));
498                                 if (t != NULL && t[0] == '/')
499                                 {
500                                         if (search_text != NULL)
501                                                 g_free(search_text);
502                                         search_text = g_strdup(t + 1);  /* XXX whacky */
503                                         search(c, 1);
504                                 }
505                                 else
506                                 {
507                                         f = ensure_url_scheme(t);
508                                         if (show_all_requests)
509                                                 fprintf(stderr, "====> %s\n", f);
510                                         webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), f);
511                                         g_free(f);
512                                 }
513                                 return TRUE;
514                         case GDK_KEY_Escape:
515                                 t = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view));
516                                 gtk_entry_set_text(GTK_ENTRY(c->location),
517                                                    (t == NULL ? __NAME__ : t));
518                                 return TRUE;
519                 }
520         }
521
522         return FALSE;
523 }
524
525 gboolean
526 key_web_view(GtkWidget *widget, GdkEvent *event, gpointer data)
527 {
528         struct Client *c = (struct Client *)data;
529         WebKitHitTestResultContext ht_context;
530         WebKitHitTestResult *ht_result = NULL;
531         gchar *ht_uri = NULL, *f;
532
533         (void)widget;
534
535         if (event->type == GDK_KEY_PRESS)
536         {
537                 if (((GdkEventKey *)event)->state & GDK_CONTROL_MASK)
538                 {
539                         switch (((GdkEventKey *)event)->keyval)
540                         {
541                                 case GDK_KEY_o:
542                                         gtk_widget_grab_focus(c->location);
543                                         return TRUE;
544                                 case GDK_KEY_h:
545                                         scroll(gtk_scrolled_window_get_hadjustment(
546                                                GTK_SCROLLED_WINDOW(c->scroll)), 0, -1);
547                                         return TRUE;
548                                 case GDK_KEY_j:
549                                         scroll(gtk_scrolled_window_get_vadjustment(
550                                                GTK_SCROLLED_WINDOW(c->scroll)), 0, 1);
551                                         return TRUE;
552                                 case GDK_KEY_k:
553                                         scroll(gtk_scrolled_window_get_vadjustment(
554                                                GTK_SCROLLED_WINDOW(c->scroll)), 0, -1);
555                                         return TRUE;
556                                 case GDK_KEY_l:
557                                         scroll(gtk_scrolled_window_get_hadjustment(
558                                                GTK_SCROLLED_WINDOW(c->scroll)), 0, 1);
559                                         return TRUE;
560                                 case GDK_KEY_f:
561                                         scroll(gtk_scrolled_window_get_vadjustment(
562                                                GTK_SCROLLED_WINDOW(c->scroll)), 1, 0.5);
563                                         return TRUE;
564                                 case GDK_KEY_b:
565                                         scroll(gtk_scrolled_window_get_vadjustment(
566                                                GTK_SCROLLED_WINDOW(c->scroll)), 1, -0.5);
567                                         return TRUE;
568                                 case GDK_KEY_n:
569                                         search(c, 1);
570                                         return TRUE;
571                                 case GDK_KEY_p:
572                                         search(c, -1);
573                                         return TRUE;
574                                 case GDK_KEY_g:
575                                         f = ensure_url_scheme(first_uri);
576                                         if (show_all_requests)
577                                                 fprintf(stderr, "====> %s\n", f);
578                                         webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), f);
579                                         g_free(f);
580                                         return TRUE;
581                         }
582                 }
583                 else if (((GdkEventKey *)event)->keyval == GDK_KEY_Escape)
584                 {
585                         webkit_web_view_stop_loading(WEBKIT_WEB_VIEW(c->web_view));
586                         gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
587                         gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Aborted.");
588                 }
589         }
590         else if (event->type == GDK_BUTTON_PRESS)
591         {
592                 switch (((GdkEventButton *)event)->button)
593                 {
594                         case 2:
595                                 ht_result = webkit_web_view_get_hit_test_result(
596                                                                    WEBKIT_WEB_VIEW(c->web_view),
597                                                                        (GdkEventButton *)event);
598                                 g_object_get(ht_result, "context", &ht_context, NULL);
599                                 if (ht_context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
600                                 {
601                                         g_object_get(ht_result, "link-uri", &ht_uri, NULL);
602                                         client_new(ht_uri);
603                                         g_object_unref(ht_result);
604                                         return TRUE;
605                                 }
606                                 g_object_unref(ht_result);
607                                 return FALSE;
608                         case 8:
609                                 webkit_web_view_go_back(WEBKIT_WEB_VIEW(c->web_view));
610                                 return TRUE;
611                         case 9:
612                                 webkit_web_view_go_forward(WEBKIT_WEB_VIEW(c->web_view));
613                                 return TRUE;
614                 }
615         }
616
617         return FALSE;
618 }
619
620 gboolean
621 remote_msg(GIOChannel *channel, GIOCondition condition, gpointer data)
622 {
623         gchar *uri = NULL;
624
625         (void)condition;
626         (void)data;
627
628         g_io_channel_read_line(channel, &uri, NULL, NULL, NULL);
629         if (uri)
630         {
631                 g_strstrip(uri);
632                 client_new(uri);
633                 g_free(uri);
634         }
635         return TRUE;
636 }
637
638 void
639 search(gpointer data, gint direction)
640 {
641         struct Client *c = (struct Client *)data;
642
643         if (search_text == NULL)
644                 return;
645
646         webkit_web_view_search_text(WEBKIT_WEB_VIEW(c->web_view), search_text,
647                                     FALSE, direction == 1, TRUE);
648 }
649
650 void
651 scroll(GtkAdjustment *a, gint step_type, gdouble factor)
652 {
653         gdouble new, lower, upper, step;
654         lower = gtk_adjustment_get_lower(a);
655         upper = gtk_adjustment_get_upper(a) - gtk_adjustment_get_page_size(a) + lower;
656         if (step_type == 0)
657                 step = gtk_adjustment_get_step_increment(a);
658         else
659                 step = gtk_adjustment_get_page_increment(a);
660         new = gtk_adjustment_get_value(a) + factor * step;
661         new = new < lower ? lower : new;
662         new = new > upper ? upper : new;
663         gtk_adjustment_set_value(a, new);
664 }
665
666 Window
667 tabbed_launch(void)
668 {
669         gint tabbed_stdout;
670         GIOChannel *tabbed_stdout_channel;
671         GError *err = NULL;
672         gchar *output = NULL;
673         char *argv[] = { "tabbed", "-c", "-d", "-p", "s1", "-n", __NAME__, NULL };
674         Window plug_into;
675
676         if (!g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
677                                       NULL, NULL, NULL, &tabbed_stdout, NULL,
678                                       &err))
679         {
680                 fprintf(stderr, __NAME__": Could not launch tabbed: %s\n", err->message);
681                 g_error_free(err);
682                 return 0;
683         }
684
685         tabbed_stdout_channel = g_io_channel_unix_new(tabbed_stdout);
686         g_io_channel_read_line(tabbed_stdout_channel, &output, NULL, NULL, NULL);
687         if (output == NULL)
688         {
689                 fprintf(stderr, __NAME__": Could not read XID from tabbed\n");
690                 return 0;
691         }
692
693         g_io_channel_shutdown(tabbed_stdout_channel, FALSE, NULL);
694
695         g_strstrip(output);
696         plug_into = strtol(output, NULL, 16);
697         g_free(output);
698         return plug_into;
699 }
700
701 void
702 usage(void)
703 {
704         fprintf(stderr, "Usage: "__NAME__" [OPTION]... <URI>...\n");
705         exit(EXIT_FAILURE);
706 }
707
708
709 int
710 main(int argc, char **argv)
711 {
712         int opt, i;
713
714         gtk_init(&argc, &argv);
715
716         grab_environment_configuration();
717
718         while ((opt = getopt(argc, argv, "e:rCT")) != -1)
719         {
720                 switch (opt)
721                 {
722                         case 'e':
723                                 embed = atol(optarg);
724                                 tabbed_automagic = FALSE;
725                                 break;
726                         case 'r':
727                                 show_all_requests = TRUE;
728                                 break;
729                         case 'C':
730                                 cooperative_instances = FALSE;
731                                 break;
732                         case 'T':
733                                 tabbed_automagic = FALSE;
734                                 break;
735                         default:
736                                 usage();
737                 }
738         }
739
740         if (optind >= argc)
741                 usage();
742
743         adblock_load();
744         cooperation_setup();
745
746         if (tabbed_automagic && !(cooperative_instances && !cooperative_alone))
747                 embed = tabbed_launch();
748
749         first_uri = g_strdup(argv[optind]);
750         for (i = optind; i < argc; i++)
751                 client_new(argv[i]);
752         if (!cooperative_instances || cooperative_alone)
753                 gtk_main();
754         exit(EXIT_SUCCESS);
755 }