52 Commits

Author SHA1 Message Date
Enno Boland (tox) ca1c7f434b Adding Nibbles patch 2010-05-28 13:20:17 +02:00
Enno Boland (tox) 8cc71f8216 typo 2010-05-26 15:34:09 +02:00
Enno Boland (tox) 7330d0e261 Applying Nibbles download patch. Thanks! 2010-05-26 15:33:01 +02:00
Enno Boland (tox) 7d3d996a3d merge 2010-05-25 10:38:23 +02:00
Enno Boland (tox) d46697044e changing user agent string as suggested by Marvin Vek 2010-05-25 10:34:43 +02:00
Enno Boland (tox) 532205d7e7 removing spatial navigation. 2010-05-24 21:35:20 +02:00
Enno Boland (tox) aadcd5b3f0 implementing downloading. 2010-05-24 21:23:39 +02:00
Enno Boland (tox) 7fcf745092 enabling spatial-navigation 2010-05-24 21:23:28 +02:00
Enno Boland (tox) 5878067225 AtomHiLight is set correctly for links. 2010-05-18 14:20:24 +02:00
Enno Boland (tox) 7ce2856194 merge 2010-05-17 23:13:44 +02:00
Enno Boland (tox) 36129338cf changing xprops 2010-05-17 11:45:28 +02:00
Enno Boland (tox) 0a6eafe077 dl is not needed anymore 2010-05-17 11:01:37 +02:00
Enno Boland (tox) 6376a30379 removing context-menu, downloading. 2010-05-17 10:58:08 +02:00
Enno Boland (tox) 3dbf628964 changing sessiontime to 3600 2010-05-15 15:47:47 +02:00
Enno Boland (tox) 91d04b15f0 merge 2010-05-11 14:57:44 +02:00
Enno Boland (tox) b1e9a6e9ab fix download/history bug 2010-05-11 14:57:09 +02:00
pancake 424936e96c Fix NOBACKGROUND meaning 2010-05-09 22:51:26 +02:00
Enno Boland (tox) 8acc31bbde changing default value of NOBACKGROUND 2010-05-09 22:46:22 +02:00
Enno Boland (tox) f304d0af52 merge 2010-05-09 22:41:11 +02:00
Enno Boland (tox) 168da3730f added js-fix by Troels Henriksen. Thanks :) 2010-05-09 19:37:46 +02:00
pancake 11039e5e2b Do not set cookie session time if set to 0
Add NOBACKGROUND config.def.h option
 - requires body {background-color} in style.css
 - fixes white background when loading pages
2010-05-08 21:43:53 +02:00
Enno Boland (tox) 602eb61623 implementing naive file locking. 2010-05-06 13:58:49 +02:00
Enno Boland (tox) bef892f513 fixing compilerwarning in newer webkit versions 2010-05-06 13:09:54 +02:00
Enno Boland (tox) f9a411f22d fixing config.mk 2010-05-06 13:06:50 +02:00
Enno Boland (tox) e9c9715f52 Adding small fix by Alex Puterbaugh. Thanks. 2010-05-02 10:59:28 +02:00
pancake 4b1b1b8a20 remove empty lines 2010-04-08 20:28:32 +02:00
Enno Boland (tox) 8aabfa5b45 whoops... linking Oo 2010-03-29 17:49:34 +02:00
Enno Boland (tox) 981e52d5a7 typofix. 2010-03-26 21:44:48 +01:00
Enno Boland (tox) a7f2765da4 reimplementing cookies. Now we need some file locking. 2010-03-25 09:59:30 +01:00
Enno Boland (tox) a05bcc653e updating manpage, reformating help output. 2010-03-25 00:05:37 +01:00
Enno Boland (tox) 62dc599a04 fixing argument parsing. 2010-03-24 23:55:41 +01:00
Enno Boland (tox) 3ede103556 fixing downloads 2010-03-24 23:18:03 +01:00
Enno Boland (tox) 19aaa5a74b gotheaders will now be called correctly. 2010-03-24 22:04:36 +01:00
Enno Boland (tox) 3434ccc16a adding surf-logo. 2010-03-24 21:05:11 +01:00
Enno Boland (tox) b8105c64ed working on cookies 2010-03-24 20:56:56 +01:00
Enno Boland (tox) e2cae4d1f5 reformating 2010-03-24 19:16:10 +01:00
Enno Boland (tox) 814bf92565 adding cmd-switches for turning of images/plugins/scripts 2010-03-24 19:11:01 +01:00
Enno Boland (tox) e252a5ec63 now we've got access to cookies before they're sended. Let's start building a cookiehandler. 2010-03-24 18:17:19 +01:00
Enno Boland (tox) b2a970d04d session gets set properly in setup() 2010-03-10 21:38:07 +01:00
Enno Boland (tox) 1cbeab188d fixing progress, beginning new cookie implementation 2010-03-08 10:06:32 +01:00
Enno Boland (tox) 764550e087 small typo. 2010-03-08 09:30:26 +01:00
Enno Boland (tox) 4f7fdd1dfa removed persistent cookies, replaced deprecated eventlisteners 2010-03-08 09:24:55 +01:00
Enno Boland (tox) 94601622a1 _SURF_URI gets initialised as soon as the window opens. 2010-01-29 09:44:14 +01:00
Enno Boland (tox) b18189d016 fixing manpage 2010-01-20 14:46:22 +01:00
Enno Boland (tox) 0fb97666ec dl has to be a directory, not a file. 2010-01-04 14:39:40 +01:00
Enno Boland (tox) 2f9c554f02 adding fixes from thomas_adam. Thanks :) 2009-12-15 17:16:01 +01:00
Enno Boland (tox) f42461c3cc Backed out changeset 260fda70689f 2009-11-09 00:05:45 +01:00
Enno Boland (tox) 1d93bfa9ce changing locking algorythm for setprop. 2009-11-08 23:45:36 +01:00
Enno Boland (tox) b57eca1a2b SETPROP() macro shows now the current value in dwm. 2009-11-08 23:45:11 +01:00
Enno Boland (tox) c0510b0cc9 next version will be 0.4 2009-11-02 09:29:54 +01:00
Enno Boland (tox) f0f3ddce76 adding environment variables for proxy and useragent 2009-11-02 09:27:32 +01:00
Enno Boland (tox) 7365214bbf Added tag 0.3 for changeset 46d516fd2167 2009-10-30 13:40:49 +01:00
6 changed files with 263 additions and 258 deletions
+1
View File
@@ -4,3 +4,4 @@ d8628fc93353faa411c9fc7eef82d11a84d26ae2 0.1
3ef997607115303fe93873b592d152f0ec186f77 0.1.1 3ef997607115303fe93873b592d152f0ec186f77 0.1.1
bffb0c3c23410ac7f260d167f4b9771497fc1127 0.1.2 bffb0c3c23410ac7f260d167f4b9771497fc1127 0.1.2
a5db79d487c85615be2ecf30815c6b4977e88000 0.2 a5db79d487c85615be2ecf30815c6b4977e88000 0.2
46d516fd216731adf91740496e6823c1be4f6199 0.3
+13 -20
View File
@@ -1,17 +1,21 @@
/* modifier 0 means no modifier */ /* modifier 0 means no modifier */
static char *useragent = "Mozilla/5.0 (X11; U; Linux; en-us) AppleWebKit/531.2+ (KHTML, like Gecko, surf-"VERSION") Safari/531.2+"; static char *useragent = "Surf/"VERSION" (X11; U; Unix; en-US) AppleWebKit/531.2+ Compatible (Safari)";
static char *progress = "#FF0000"; static char *progress = "#FF0000";
static char *progress_trust = "#00FF00"; static char *progress_trust = "#00FF00";
static char *stylefile = ".surf/style.css"; static char *stylefile = ".surf/style.css";
static char *scriptfile = ".surf/script.js"; static char *scriptfile = ".surf/script.js";
static char *cookiefile = ".surf/cookies.txt"; static char *cookiefile = ".surf/cookies.txt";
static char *dldir = ".surf/dl"; static time_t sessiontime = 0;
static time_t sessiontime = 3600; #define NOBACKGROUND 0
#define SETPROP(p) { .v = (char *[]){ "/bin/sh", "-c", \ #define SETPROP(p, q) { .v = (char *[]){ "/bin/sh", "-c", \
"prop=\"`dmenu < /dev/null`\" &&" \ "prop=\"`xprop -id $2 $0 | cut -d '\"' -f 2 | dmenu`\" &&" \
"xprop -id $1 -f $0 8s -set $0 \"$prop\"", \ "xprop -id $2 -f $1 8s -set $1 \"$prop\"", \
p, winid, NULL } } p, q, winid, NULL } }
#define DOWNLOAD(d) { \
.v = (char *[]){ "/bin/sh", "-c", \
"xterm -e \"wget --load-cookies ~/.surf/cookies.txt '$0';\"", \
d, NULL } }
#define MODKEY GDK_CONTROL_MASK #define MODKEY GDK_CONTROL_MASK
static Key keys[] = { static Key keys[] = {
/* modifier keyval function arg Focus */ /* modifier keyval function arg Focus */
@@ -29,19 +33,8 @@ static Key keys[] = {
{ MODKEY, GDK_k, scroll, { .i = -1 } }, { MODKEY, GDK_k, scroll, { .i = -1 } },
{ 0, GDK_Escape, stop, { 0 } }, { 0, GDK_Escape, stop, { 0 } },
{ MODKEY, GDK_o, source, { 0 } }, { MODKEY, GDK_o, source, { 0 } },
{ MODKEY, GDK_g, spawn, SETPROP("_SURF_URI") }, { MODKEY, GDK_g, spawn, SETPROP("_SURF_URI", "_SURF_GO") },
{ MODKEY, GDK_slash, spawn, SETPROP("_SURF_FIND") }, { MODKEY, GDK_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND") },
{ MODKEY, GDK_n, find, { .b = TRUE } }, { MODKEY, GDK_n, find, { .b = TRUE } },
{ MODKEY|GDK_SHIFT_MASK,GDK_n, find, { .b = FALSE } }, { MODKEY|GDK_SHIFT_MASK,GDK_n, find, { .b = FALSE } },
}; };
static Item items[] = {
{ "Back", navigate, { .i = -1 } },
{ "Forward", navigate, { .i = +1 } },
{ "New Window", newwindow, { .v = NULL } },
{ "Reload", reload, { .b = FALSE } },
{ "Stop", stop, { 0 } },
{ "Paste URI", clipboard, { .b = TRUE } },
{ "Copy URI", clipboard, { .b = FALSE } },
{ "Download", download, { 0 } },
};
+2 -2
View File
@@ -1,5 +1,5 @@
# surf version # surf version
VERSION = 0.3 VERSION = 0.4
# Customize below to fit your system # Customize below to fit your system
@@ -18,7 +18,7 @@ LIBS = -L/usr/lib -lc ${GTKLIB} -lgthread-2.0
# flags # flags
CPPFLAGS = -DVERSION=\"${VERSION}\" CPPFLAGS = -DVERSION=\"${VERSION}\"
CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS} LDFLAGS = -g ${LIBS}
# Solaris # Solaris
#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
+24 -6
View File
@@ -3,7 +3,12 @@
surf \- simple webkit-based browser surf \- simple webkit-based browser
.SH SYNOPSIS .SH SYNOPSIS
.B surf .B surf
.RB [ \-ehvx ] .RB [-e\ xid]
.RB [-i]
.RB [-p]
.RB [-s]
.RB [-v]
.RB [-x]
.RB "URI" .RB "URI"
.SH DESCRIPTION .SH DESCRIPTION
surf is a simple Web browser based on WebKit/GTK+. It is able surf is a simple Web browser based on WebKit/GTK+. It is able
@@ -12,12 +17,17 @@ which makes it possible to embed it in another application. Furthermore,
one can point surf to another URI by setting its XProperties. one can point surf to another URI by setting its XProperties.
.SH OPTIONS .SH OPTIONS
.TP .TP
.B \-e .B \-e xid
Prints xid to standard output and waits until an application reparents the Reparents to window specified by xid.
window.
.TP .TP
.B \-h .B \-i
Prints usage information to standard output, then exits. Disable Images
.TP
.B \-p
Disable Plugins
.TP
.B \-s
Disable Javascript
.TP .TP
.B \-v .B \-v
Prints version information to standard output, then exits. Prints version information to standard output, then exits.
@@ -26,6 +36,8 @@ Prints version information to standard output, then exits.
Prints xid to standard output. This can be used to script the browser by using Prints xid to standard output. This can be used to script the browser by using
.BR xprop(1). .BR xprop(1).
.SH USAGE .SH USAGE
.B Escape
Stops loading current page or stops download.
.TP .TP
.B Ctrl\-h .B Ctrl\-h
Walks back the history. Walks back the history.
@@ -51,6 +63,12 @@ Resets Zoom
.B Ctrl\-/ .B Ctrl\-/
Opens the search-bar. Opens the search-bar.
.TP .TP
.B Ctrl\-n
Go to next search result.
.TP
.B Ctrl\-Shift\-n
Go to previous search result.
.TP
.B Ctrl\-g .B Ctrl\-g
Opens the URL-bar. Opens the URL-bar.
.TP .TP
+223 -230
View File
@@ -18,10 +18,13 @@
#include <webkit/webkit.h> #include <webkit/webkit.h>
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include <JavaScriptCore/JavaScript.h> #include <JavaScriptCore/JavaScript.h>
#include <sys/file.h>
#define LENGTH(x) (sizeof x / sizeof x[0]) #define LENGTH(x) (sizeof x / sizeof x[0])
#define CLEANMASK(mask) (mask & ~(GDK_MOD2_MASK)) #define CLEANMASK(mask) (mask & ~(GDK_MOD2_MASK))
enum { AtomFind, AtomGo, AtomUri, AtomLast };
typedef union Arg Arg; typedef union Arg Arg;
union Arg { union Arg {
gboolean b; gboolean b;
@@ -31,9 +34,7 @@ union Arg {
typedef struct Client { typedef struct Client {
GtkWidget *win, *scroll, *vbox, *indicator; GtkWidget *win, *scroll, *vbox, *indicator;
GtkWidget **items;
WebKitWebView *view; WebKitWebView *view;
WebKitDownload *download;
char *title, *linkhover; char *title, *linkhover;
const char *uri, *needle; const char *uri, *needle;
gint progress; gint progress;
@@ -55,22 +56,17 @@ typedef struct {
} Key; } Key;
static Display *dpy; static Display *dpy;
static Atom uriprop, findprop; static Atom atoms[AtomLast];
static SoupCookieJar *cookies;
static SoupSession *session;
static Client *clients = NULL; static Client *clients = NULL;
static GdkNativeWindow embed = 0; static GdkNativeWindow embed = 0;
static gboolean showxid = FALSE; static gboolean showxid = FALSE;
static int ignorexprop = 0;
static char winid[64]; static char winid[64];
static char *progname; static char *progname;
static gboolean lockcookie = FALSE; static gboolean loadimage = 1, plugin = 1, script = 1;
static char *buildpath(const char *path); static char *buildpath(const char *path);
static void changecookie(SoupCookieJar *jar, SoupCookie *o, SoupCookie *n, gpointer p);
static void cleanup(void); static void cleanup(void);
static void clipboard(Client *c, const Arg *arg); static void clipboard(Client *c, const Arg *arg);
static void context(WebKitWebView *v, GtkMenu *m, Client *c);
static char *copystr(char **str, const char *src); static char *copystr(char **str, const char *src);
static WebKitWebView *createwindow(WebKitWebView *v, WebKitWebFrame *f, Client *c); static WebKitWebView *createwindow(WebKitWebView *v, WebKitWebFrame *f, Client *c);
static gboolean decidedownload(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r, gchar *m, WebKitWebPolicyDecision *p, Client *c); static gboolean decidedownload(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r, gchar *m, WebKitWebPolicyDecision *p, Client *c);
@@ -78,31 +74,31 @@ static gboolean decidewindow(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkR
static void destroyclient(Client *c); static void destroyclient(Client *c);
static void destroywin(GtkWidget* w, Client *c); static void destroywin(GtkWidget* w, Client *c);
static void die(char *str); static void die(char *str);
static void download(Client *c, const Arg *arg);
static void drawindicator(Client *c); static void drawindicator(Client *c);
static gboolean exposeindicator(GtkWidget *w, GdkEventExpose *e, Client *c); static gboolean exposeindicator(GtkWidget *w, GdkEventExpose *e, Client *c);
static void find(Client *c, const Arg *arg); static void find(Client *c, const Arg *arg);
static const char *getatom(Client *c, Atom a); static const char *getatom(Client *c, int a);
static const char *getcookies(SoupURI *uri);
static char *geturi(Client *c); static char *geturi(Client *c);
void gotheaders(SoupMessage *msg, gpointer user_data);
static gboolean initdownload(WebKitWebView *v, WebKitDownload *o, Client *c); static gboolean initdownload(WebKitWebView *v, WebKitDownload *o, Client *c);
static void itemclick(GtkMenuItem *mi, Client *c);
static gboolean keypress(GtkWidget *w, GdkEventKey *ev, Client *c); static gboolean keypress(GtkWidget *w, GdkEventKey *ev, Client *c);
static void linkhover(WebKitWebView *v, const char* t, const char* l, Client *c); static void linkhover(WebKitWebView *v, const char* t, const char* l, Client *c);
static void loadcommit(WebKitWebView *v, WebKitWebFrame *f, Client *c); static void loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c);
static void loadstart(WebKitWebView *v, WebKitWebFrame *f, Client *c);
static void loaduri(Client *c, const Arg *arg); static void loaduri(Client *c, const Arg *arg);
static void navigate(Client *c, const Arg *arg); static void navigate(Client *c, const Arg *arg);
static Client *newclient(void); static Client *newclient(void);
static void newwindow(Client *c, const Arg *arg); static void newwindow(Client *c, const Arg *arg);
static void newrequest(SoupSession *s, SoupMessage *msg, gpointer v);
static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d); static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
static void print(Client *c, const Arg *arg); static void print(Client *c, const Arg *arg);
static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, gpointer d); static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, gpointer d);
static void progresschange(WebKitWebView *v, gint p, Client *c); static void progresschange(WebKitWebView *view, GParamSpec *pspec, Client *c);
static void reload(Client *c, const Arg *arg); static void reload(Client *c, const Arg *arg);
static void reloadcookies();
static void resize(GtkWidget *w, GtkAllocation *a, Client *c); static void resize(GtkWidget *w, GtkAllocation *a, Client *c);
static void scroll(Client *c, const Arg *arg); static void scroll(Client *c, const Arg *arg);
static void setatom(Client *c, Atom a, const char *v); static void setatom(Client *c, int a, const char *v);
static void setcookie(SoupCookie *c);
static void setup(void); static void setup(void);
static void sigchld(int unused); static void sigchld(int unused);
static void source(Client *c, const Arg *arg); static void source(Client *c, const Arg *arg);
@@ -110,7 +106,6 @@ static void spawn(Client *c, const Arg *arg);
static void stop(Client *c, const Arg *arg); static void stop(Client *c, const Arg *arg);
static void titlechange(WebKitWebView *v, WebKitWebFrame* frame, const char* title, Client *c); static void titlechange(WebKitWebView *v, WebKitWebFrame* frame, const char* title, Client *c);
static void update(Client *c); static void update(Client *c);
static void updatedownload(WebKitDownload *o, GParamSpec *pspec, Client *c);
static void updatewinid(Client *c); static void updatewinid(Client *c);
static void usage(void); static void usage(void);
static void windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame, JSContextRef js, JSObjectRef win, Client *c); static void windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame, JSContextRef js, JSObjectRef win, Client *c);
@@ -135,42 +130,33 @@ buildpath(const char *path) {
*p = '/'; *p = '/';
} }
/* creating file (gives error when apath ends with "/") */ /* creating file (gives error when apath ends with "/") */
if((f = g_fopen(apath, "a"))) if((f = fopen(apath, "a")))
fclose(f); fclose(f);
return apath; return apath;
} }
void
changecookie(SoupCookieJar *j, SoupCookie *oc, SoupCookie *c, gpointer p) {
SoupDate *e;
SoupCookieJar *jar;
if(lockcookie)
return;
if(c && c->expires == NULL) {
e = soup_date_new_from_time_t(time(NULL) + sessiontime);
c = soup_cookie_copy(c);
soup_cookie_set_expires(c, e);
}
jar = soup_cookie_jar_text_new(cookiefile, FALSE);
if(c)
soup_cookie_jar_add_cookie(jar, soup_cookie_copy(c));
else
soup_cookie_jar_delete_cookie(jar, oc);
g_object_unref(jar);
}
void void
cleanup(void) { cleanup(void) {
while(clients) while(clients)
destroyclient(clients); destroyclient(clients);
g_free(cookiefile); g_free(cookiefile);
g_free(dldir);
g_free(scriptfile); g_free(scriptfile);
g_free(stylefile); g_free(stylefile);
} }
void
runscript(WebKitWebFrame *frame, JSContextRef js) {
JSStringRef jsscript;
char *script;
JSValueRef exception = NULL;
GError *error;
if(g_file_get_contents(scriptfile, &script, NULL, &error)) {
jsscript = JSStringCreateWithUTF8CString(script);
JSEvaluateScript(js, jsscript, JSContextGetGlobalObject(js), NULL, 0, &exception);
}
}
void void
clipboard(Client *c, const Arg *arg) { clipboard(Client *c, const Arg *arg) {
gboolean paste = *(gboolean *)arg; gboolean paste = *(gboolean *)arg;
@@ -181,22 +167,6 @@ clipboard(Client *c, const Arg *arg) {
gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), c->linkhover ? c->linkhover : geturi(c), -1); gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), c->linkhover ? c->linkhover : geturi(c), -1);
} }
void
context(WebKitWebView *v, GtkMenu *m, Client *c) {
int i;
GtkContainer *parent;
gtk_widget_hide_all(GTK_WIDGET(m));
gtk_widget_show(GTK_WIDGET(m));
for(i = 0; i < LENGTH(items); i++) {
parent = GTK_CONTAINER(gtk_widget_get_parent(c->items[i]));
if(parent)
gtk_container_remove(parent, c->items[i]);
gtk_menu_shell_append(GTK_MENU_SHELL(m), c->items[i]);
gtk_widget_show(c->items[i]);
}
}
char * char *
copystr(char **str, const char *src) { copystr(char **str, const char *src) {
char *tmp; char *tmp;
@@ -239,7 +209,6 @@ decidewindow(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r, We
void void
destroyclient(Client *c) { destroyclient(Client *c) {
int i;
Client *p; Client *p;
gtk_widget_destroy(c->indicator); gtk_widget_destroy(c->indicator);
@@ -247,9 +216,6 @@ destroyclient(Client *c) {
gtk_widget_destroy(c->scroll); gtk_widget_destroy(c->scroll);
gtk_widget_destroy(c->vbox); gtk_widget_destroy(c->vbox);
gtk_widget_destroy(c->win); gtk_widget_destroy(c->win);
for(i = 0; i < LENGTH(items); i++)
gtk_widget_destroy(c->items[i]);
free(c->items);
for(p = clients; p && p->next != c; p = p->next); for(p = clients; p && p->next != c; p = p->next);
if(p) if(p)
@@ -272,21 +238,6 @@ die(char *str) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
void
download(Client *c, const Arg *arg) {
char *uri;
WebKitNetworkRequest *r;
WebKitDownload *dl;
if(arg->v)
uri = (char *)arg->v;
else
uri = c->linkhover ? c->linkhover : geturi(c);
r = webkit_network_request_new(uri);
dl = webkit_download_new(r);
initdownload(c->view, dl, c);
}
void void
drawindicator(Client *c) { drawindicator(Client *c) {
gint width; gint width;
@@ -295,7 +246,7 @@ drawindicator(Client *c) {
GdkGC *gc; GdkGC *gc;
GdkColor fg; GdkColor fg;
uri = getatom(c, uriprop); uri = geturi(c);
w = c->indicator; w = c->indicator;
width = c->progress * w->allocation.width / 100; width = c->progress * w->allocation.width / 100;
gc = gdk_gc_new(w->window); gc = gdk_gc_new(w->window);
@@ -320,13 +271,22 @@ void
find(Client *c, const Arg *arg) { find(Client *c, const Arg *arg) {
const char *s; const char *s;
s = getatom(c, findprop); s = getatom(c, AtomFind);
gboolean forward = *(gboolean *)arg; gboolean forward = *(gboolean *)arg;
webkit_web_view_search_text(c->view, s, FALSE, forward, TRUE); webkit_web_view_search_text(c->view, s, FALSE, forward, TRUE);
} }
const char * const char *
getatom(Client *c, Atom a) { getcookies(SoupURI *uri) {
const char *c;
SoupCookieJar *j = soup_cookie_jar_text_new(cookiefile, TRUE);
c = soup_cookie_jar_get_cookies(j, uri, TRUE);
g_object_unref(j);
return c;
}
const char *
getatom(Client *c, int a) {
static char buf[BUFSIZ]; static char buf[BUFSIZ];
Atom adummy; Atom adummy;
int idummy; int idummy;
@@ -334,7 +294,7 @@ getatom(Client *c, Atom a) {
unsigned char *p = NULL; unsigned char *p = NULL;
XGetWindowProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window), XGetWindowProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window),
a, 0L, BUFSIZ, False, XA_STRING, atoms[a], 0L, BUFSIZ, False, XA_STRING,
&adummy, &idummy, &ldummy, &ldummy, &p); &adummy, &idummy, &ldummy, &ldummy, &p);
if(p) if(p)
strncpy(buf, (char *)p, LENGTH(buf)-1); strncpy(buf, (char *)p, LENGTH(buf)-1);
@@ -353,42 +313,27 @@ geturi(Client *c) {
return uri; return uri;
} }
gboolean void
initdownload(WebKitWebView *view, WebKitDownload *o, Client *c) { gotheaders(SoupMessage *msg, gpointer v) {
const char *filename; SoupURI *uri;
char *uri, *html; GSList *l, *p;
stop(c, NULL); uri = soup_message_get_uri(msg);
c->download = o; for(p = l = soup_cookies_from_response(msg); p;
filename = webkit_download_get_suggested_filename(o); p = g_slist_next(p)) {
if(!strcmp("", filename)) setcookie((SoupCookie *)p->data);
filename = "index.html"; }
uri = g_strconcat("file://", dldir, "/", filename, NULL); soup_cookies_free(l);
webkit_download_set_destination_uri(c->download, uri);
c->progress = 0;
g_free(uri);
html = g_strdup_printf("Download <b>%s</b>...", filename);
webkit_web_view_load_html_string(c->view, html,
webkit_download_get_uri(c->download));
g_signal_connect(c->download, "notify::progress", G_CALLBACK(updatedownload), c);
g_signal_connect(c->download, "notify::status", G_CALLBACK(updatedownload), c);
webkit_download_start(c->download);
c->title = copystr(&c->title, filename);
update(c);
g_free(html);
return TRUE;
} }
void gboolean
itemclick(GtkMenuItem *mi, Client *c) { initdownload(WebKitWebView *view, WebKitDownload *o, Client *c) {
int i; Arg arg;
const char *label;
label = gtk_menu_item_get_label(mi); updatewinid(c);
for(i = 0; i < LENGTH(items); i++) arg = (Arg)DOWNLOAD((char *)webkit_download_get_uri(o));
if(!strcmp(items[i].label, label)) spawn(c, &arg);
items[i].func(c, &(items[i].arg)); return FALSE;
} }
gboolean gboolean
@@ -410,8 +355,9 @@ keypress(GtkWidget* w, GdkEventKey *ev, Client *c) {
void void
linkhover(WebKitWebView *v, const char* t, const char* l, Client *c) { linkhover(WebKitWebView *v, const char* t, const char* l, Client *c) {
if(l) if(l) {
c->linkhover = copystr(&c->linkhover, l); c->linkhover = copystr(&c->linkhover, l);
}
else if(c->linkhover) { else if(c->linkhover) {
free(c->linkhover); free(c->linkhover);
c->linkhover = NULL; c->linkhover = NULL;
@@ -420,44 +366,56 @@ linkhover(WebKitWebView *v, const char* t, const char* l, Client *c) {
} }
void void
loadcommit(WebKitWebView *view, WebKitWebFrame *f, Client *c) { loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
setatom(c, uriprop, geturi(c)); switch(webkit_web_view_get_load_status (c->view)) {
} case WEBKIT_LOAD_COMMITTED:
setatom(c, AtomUri, geturi(c));
void break;
loadstart(WebKitWebView *view, WebKitWebFrame *f, Client *c) { case WEBKIT_LOAD_FINISHED:
c->progress = 0; c->progress = 0;
update(c); update(c);
reloadcookies(); break;
default:
break;
}
} }
void void
loaduri(Client *c, const Arg *arg) { loaduri(Client *c, const Arg *arg) {
char *u; char *u;
const char *uri = (char *)arg->v; const char *uri = (char *)arg->v;
Arg a = { .b = FALSE };
if(strcmp(uri, "") == 0)
return;
u = g_strrstr(uri, "://") ? g_strdup(uri) u = g_strrstr(uri, "://") ? g_strdup(uri)
: g_strdup_printf("http://%s", uri); : g_strdup_printf("http://%s", uri);
webkit_web_view_load_uri(c->view, u); /* prevents endless loop */
c->progress = 0; if(c->uri && strcmp(u, c->uri) == 0) {
c->title = copystr(&c->title, u); reload(c, &a);
g_free(u); }
update(c); else {
webkit_web_view_load_uri(c->view, u);
c->progress = 0;
c->title = copystr(&c->title, u);
g_free(u);
update(c);
}
} }
void void
navigate(Client *c, const Arg *arg) { navigate(Client *c, const Arg *arg) {
gint steps = *(gint *)arg; int steps = *(int *)arg;
webkit_web_view_go_back_or_forward(c->view, steps); webkit_web_view_go_back_or_forward(c->view, steps);
} }
Client * Client *
newclient(void) { newclient(void) {
int i;
Client *c; Client *c;
WebKitWebSettings *settings; WebKitWebSettings *settings;
WebKitWebFrame *frame;
GdkGeometry hints = { 1, 1 }; GdkGeometry hints = { 1, 1 };
char *uri; char *uri, *ua;
if(!(c = calloc(1, sizeof(Client)))) if(!(c = calloc(1, sizeof(Client))))
die("Cannot malloc!\n"); die("Cannot malloc!\n");
@@ -467,23 +425,25 @@ newclient(void) {
} }
else { else {
c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
/* TA: 20091214: Despite what the GNOME docs say, the ICCCM
* is always correct, so we should still call this function.
* But when doing so, we *must* differentiate between a
* WM_CLASS and a resource on the window. By convention, the
* window class (WM_CLASS) is capped, while the resource is in
* lowercase. Both these values come as a pair.
*/
gtk_window_set_wmclass(GTK_WINDOW(c->win), "surf", "surf"); gtk_window_set_wmclass(GTK_WINDOW(c->win), "surf", "surf");
/* TA: 20091214: And set the role here as well -- so that
* sessions can pick this up.
*/
gtk_window_set_role(GTK_WINDOW(c->win), "Surf");
} }
gtk_window_set_default_size(GTK_WINDOW(c->win), 800, 600); gtk_window_set_default_size(GTK_WINDOW(c->win), 800, 600);
g_signal_connect(G_OBJECT(c->win), "destroy", G_CALLBACK(destroywin), c); g_signal_connect(G_OBJECT(c->win), "destroy", G_CALLBACK(destroywin), c);
g_signal_connect(G_OBJECT(c->win), "key-press-event", G_CALLBACK(keypress), c); g_signal_connect(G_OBJECT(c->win), "key-press-event", G_CALLBACK(keypress), c);
g_signal_connect(G_OBJECT(c->win), "size-allocate", G_CALLBACK(resize), c); g_signal_connect(G_OBJECT(c->win), "size-allocate", G_CALLBACK(resize), c);
if(!(c->items = calloc(1, sizeof(GtkWidget *) * LENGTH(items))))
die("Cannot malloc!\n");
/* contextmenu */
for(i = 0; i < LENGTH(items); i++) {
c->items[i] = gtk_menu_item_new_with_label(items[i].label);
g_signal_connect(G_OBJECT(c->items[i]), "activate",
G_CALLBACK(itemclick), c);
}
/* VBox */ /* VBox */
c->vbox = gtk_vbox_new(FALSE, 0); c->vbox = gtk_vbox_new(FALSE, 0);
@@ -495,16 +455,14 @@ newclient(void) {
/* Webview */ /* Webview */
c->view = WEBKIT_WEB_VIEW(webkit_web_view_new()); c->view = WEBKIT_WEB_VIEW(webkit_web_view_new());
g_signal_connect(G_OBJECT(c->view), "title-changed", G_CALLBACK(titlechange), c); g_signal_connect(G_OBJECT(c->view), "title-changed", G_CALLBACK(titlechange), c);
g_signal_connect(G_OBJECT(c->view), "load-progress-changed", G_CALLBACK(progresschange), c);
g_signal_connect(G_OBJECT(c->view), "load-committed", G_CALLBACK(loadcommit), c);
g_signal_connect(G_OBJECT(c->view), "load-started", G_CALLBACK(loadstart), c);
g_signal_connect(G_OBJECT(c->view), "hovering-over-link", G_CALLBACK(linkhover), c); g_signal_connect(G_OBJECT(c->view), "hovering-over-link", G_CALLBACK(linkhover), c);
g_signal_connect(G_OBJECT(c->view), "create-web-view", G_CALLBACK(createwindow), c); g_signal_connect(G_OBJECT(c->view), "create-web-view", G_CALLBACK(createwindow), c);
g_signal_connect(G_OBJECT(c->view), "new-window-policy-decision-requested", G_CALLBACK(decidewindow), c); g_signal_connect(G_OBJECT(c->view), "new-window-policy-decision-requested", G_CALLBACK(decidewindow), c);
g_signal_connect(G_OBJECT(c->view), "mime-type-policy-decision-requested", G_CALLBACK(decidedownload), c); g_signal_connect(G_OBJECT(c->view), "mime-type-policy-decision-requested", G_CALLBACK(decidedownload), c);
g_signal_connect(G_OBJECT(c->view), "download-requested", G_CALLBACK(initdownload), c);
g_signal_connect(G_OBJECT(c->view), "window-object-cleared", G_CALLBACK(windowobjectcleared), c); g_signal_connect(G_OBJECT(c->view), "window-object-cleared", G_CALLBACK(windowobjectcleared), c);
g_signal_connect(G_OBJECT(c->view), "populate-popup", G_CALLBACK(context), c); g_signal_connect(G_OBJECT(c->view), "notify::load-status", G_CALLBACK(loadstatuschange), c);
g_signal_connect(G_OBJECT(c->view), "notify::progress", G_CALLBACK(progresschange), c);
g_signal_connect(G_OBJECT(c->view), "download-requested", G_CALLBACK(initdownload), c);
/* Indicator */ /* Indicator */
c->indicator = gtk_drawing_area_new(); c->indicator = gtk_drawing_area_new();
@@ -531,14 +489,26 @@ newclient(void) {
gdk_window_set_events(GTK_WIDGET(c->win)->window, GDK_ALL_EVENTS_MASK); gdk_window_set_events(GTK_WIDGET(c->win)->window, GDK_ALL_EVENTS_MASK);
gdk_window_add_filter(GTK_WIDGET(c->win)->window, processx, c); gdk_window_add_filter(GTK_WIDGET(c->win)->window, processx, c);
webkit_web_view_set_full_content_zoom(c->view, TRUE); webkit_web_view_set_full_content_zoom(c->view, TRUE);
frame = webkit_web_view_get_main_frame(c->view);
runscript(frame, webkit_web_frame_get_global_context(frame));
settings = webkit_web_view_get_settings(c->view); settings = webkit_web_view_get_settings(c->view);
g_object_set(G_OBJECT(settings), "user-agent", useragent, NULL); if(!(ua = getenv("SURF_USERAGENT")))
ua = useragent;
g_object_set(G_OBJECT(settings), "user-agent", ua, NULL);
uri = g_strconcat("file://", stylefile, NULL); uri = g_strconcat("file://", stylefile, NULL);
g_object_set(G_OBJECT(settings), "user-stylesheet-uri", uri, NULL); g_object_set(G_OBJECT(settings), "user-stylesheet-uri", uri, NULL);
g_free(uri); g_object_set(G_OBJECT(settings), "auto-load-images", loadimage, NULL);
setatom(c, findprop, ""); g_object_set(G_OBJECT(settings), "enable-plugins", plugin, NULL);
g_object_set(G_OBJECT(settings), "enable-scripts", script, NULL);
g_object_set(G_OBJECT(settings), "enable-spatial-navigation", true, NULL);
g_free(uri);
setatom(c, AtomFind, "");
setatom(c, AtomUri, "about:blank");
if(NOBACKGROUND)
webkit_web_view_set_transparent(c->view, TRUE);
c->download = NULL;
c->title = NULL; c->title = NULL;
c->next = clients; c->next = clients;
clients = c; clients = c;
@@ -550,10 +520,23 @@ newclient(void) {
return c; return c;
} }
void
newrequest(SoupSession *s, SoupMessage *msg, gpointer v) {
SoupMessageHeaders *h = msg->request_headers;
SoupURI *uri;
const char *c;
soup_message_headers_remove(h, "Cookie");
uri = soup_message_get_uri(msg);
if((c = getcookies(uri)))
soup_message_headers_append(h, "Cookie", c);
g_signal_connect_after(G_OBJECT(msg), "got-headers", G_CALLBACK(gotheaders), NULL);
}
void void
newwindow(Client *c, const Arg *arg) { newwindow(Client *c, const Arg *arg) {
guint i = 0; guint i = 0;
const char *cmd[7], *uri; const char *cmd[10], *uri;
const Arg a = { .v = (void *)cmd }; const Arg a = { .v = (void *)cmd };
char tmp[64]; char tmp[64];
@@ -563,9 +546,14 @@ newwindow(Client *c, const Arg *arg) {
snprintf(tmp, LENGTH(tmp), "%u\n", (int)embed); snprintf(tmp, LENGTH(tmp), "%u\n", (int)embed);
cmd[i++] = tmp; cmd[i++] = tmp;
} }
if(showxid) { if(!script)
cmd[i++] = "-s";
if(!plugin)
cmd[i++] = "-p";
if(!loadimage)
cmd[i++] = "-i";
if(showxid)
cmd[i++] = "-x"; cmd[i++] = "-x";
}
cmd[i++] = "--"; cmd[i++] = "--";
uri = arg->v ? (char *)arg->v : c->linkhover; uri = arg->v ? (char *)arg->v : c->linkhover;
if(uri) if(uri)
@@ -594,26 +582,25 @@ processx(GdkXEvent *e, GdkEvent *event, gpointer d) {
if(((XEvent *)e)->type == PropertyNotify) { if(((XEvent *)e)->type == PropertyNotify) {
ev = &((XEvent *)e)->xproperty; ev = &((XEvent *)e)->xproperty;
if(ignorexprop) if(ev->state == PropertyNewValue) {
ignorexprop--; if(ev->atom == atoms[AtomFind]) {
else if(ev->state == PropertyNewValue) {
if(ev->atom == uriprop) {
arg.v = getatom(c, uriprop);
loaduri(c, &arg);
}
else if(ev->atom == findprop) {
arg.b = TRUE; arg.b = TRUE;
find(c, &arg); find(c, &arg);
return GDK_FILTER_REMOVE;
}
else if(ev->atom == atoms[AtomGo]) {
arg.v = getatom(c, AtomGo);
loaduri(c, &arg);
return GDK_FILTER_REMOVE;
} }
return GDK_FILTER_REMOVE;
} }
} }
return GDK_FILTER_CONTINUE; return GDK_FILTER_CONTINUE;
} }
void void
progresschange(WebKitWebView *v, gint p, Client *c) { progresschange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
c->progress = p; c->progress = webkit_web_view_get_progress(c->view) * 100;
update(c); update(c);
} }
@@ -626,23 +613,6 @@ reload(Client *c, const Arg *arg) {
webkit_web_view_reload(c->view); webkit_web_view_reload(c->view);
} }
void
reloadcookies() {
SoupCookieJar *jar;
GSList *l, *e;
lockcookie = TRUE;
for(l = e = soup_cookie_jar_all_cookies(cookies); e; e = e->next)
soup_cookie_jar_delete_cookie(cookies, (SoupCookie *)e->data);
soup_cookies_free(l);
jar = soup_cookie_jar_text_new(cookiefile, TRUE);
for(l = e = soup_cookie_jar_all_cookies(jar); e; e = e->next)
soup_cookie_jar_add_cookie(cookies, (SoupCookie *)e->data);
g_slist_free(l);
lockcookie = FALSE;
g_object_unref(jar);
}
void void
resize(GtkWidget *w, GtkAllocation *a, Client *c) { resize(GtkWidget *w, GtkAllocation *a, Client *c) {
double zoom; double zoom;
@@ -670,16 +640,37 @@ scroll(Client *c, const Arg *arg) {
} }
void void
setatom(Client *c, Atom a, const char *v) { setcookie(SoupCookie *c) {
int lock;
lock = open(cookiefile, 0);
flock(lock, LOCK_EX);
SoupDate *e;
SoupCookieJar *j = soup_cookie_jar_text_new(cookiefile, FALSE);
c = soup_cookie_copy(c);
if(c->expires == NULL && sessiontime) {
e = soup_date_new_from_time_t(time(NULL) + sessiontime);
soup_cookie_set_expires(c, e);
}
soup_cookie_jar_add_cookie(j, c);
g_object_unref(j);
flock(lock, LOCK_UN);
close(lock);
}
void
setatom(Client *c, int a, const char *v) {
XSync(dpy, False); XSync(dpy, False);
ignorexprop++; XChangeProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window), atoms[a],
XChangeProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window), a,
XA_STRING, 8, PropModeReplace, (unsigned char *)v, XA_STRING, 8, PropModeReplace, (unsigned char *)v,
strlen(v) + 1); strlen(v) + 1);
} }
void void
setup(void) { setup(void) {
char *proxy;
char *new_proxy;
SoupURI *puri;
SoupSession *s; SoupSession *s;
/* clean up any zombies immediately */ /* clean up any zombies immediately */
@@ -689,22 +680,33 @@ setup(void) {
g_thread_init(NULL); g_thread_init(NULL);
dpy = GDK_DISPLAY(); dpy = GDK_DISPLAY();
session = webkit_get_default_session(); s = webkit_get_default_session();
uriprop = XInternAtom(dpy, "_SURF_URI", False);
findprop = XInternAtom(dpy, "_SURF_FIND", False);
/* create dirs and files */ /* atoms */
atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False);
atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False);
atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
/* dirs and files */
cookiefile = buildpath(cookiefile); cookiefile = buildpath(cookiefile);
dldir = buildpath(dldir);
scriptfile = buildpath(scriptfile); scriptfile = buildpath(scriptfile);
stylefile = buildpath(stylefile); stylefile = buildpath(stylefile);
/* cookie persistance */ /* request handler */
s = webkit_get_default_session(); s = webkit_get_default_session();
cookies = soup_cookie_jar_new(); soup_session_remove_feature_by_type(s, soup_cookie_get_type());
soup_session_add_feature(s, SOUP_SESSION_FEATURE(cookies)); soup_session_remove_feature_by_type(s, soup_cookie_jar_get_type());
g_signal_connect(cookies, "changed", G_CALLBACK(changecookie), NULL); g_signal_connect_after(G_OBJECT(s), "request-started", G_CALLBACK(newrequest), NULL);
reloadcookies();
/* proxy */
if((proxy = getenv("http_proxy")) && strcmp(proxy, "")) {
new_proxy = g_strrstr(proxy, "http://") ? g_strdup(proxy) :
g_strdup_printf("http://%s", proxy);
puri = soup_uri_new(new_proxy);
g_object_set(G_OBJECT(s), "proxy-uri", puri, NULL);
soup_uri_free(puri);
g_free(new_proxy);
}
} }
void void
@@ -739,11 +741,7 @@ spawn(Client *c, const Arg *arg) {
void void
stop(Client *c, const Arg *arg) { stop(Client *c, const Arg *arg) {
if(c->download) webkit_web_view_stop_loading(c->view);
webkit_download_cancel(c->download);
else
webkit_web_view_stop_loading(c->view);
c->download = NULL;
} }
void void
@@ -767,17 +765,6 @@ update(Client *c) {
g_free(t); g_free(t);
} }
void
updatedownload(WebKitDownload *o, GParamSpec *pspec, Client *c) {
WebKitDownloadStatus status;
status = webkit_download_get_status(c->download);
if(status == WEBKIT_DOWNLOAD_STATUS_STARTED || status == WEBKIT_DOWNLOAD_STATUS_CREATED) {
c->progress = (gint)(webkit_download_get_progress(c->download)*100);
}
update(c);
}
void void
updatewinid(Client *c) { updatewinid(Client *c) {
snprintf(winid, LENGTH(winid), "%u", snprintf(winid, LENGTH(winid), "%u",
@@ -787,20 +774,12 @@ updatewinid(Client *c) {
void void
usage(void) { usage(void) {
fputs("surf - simple browser\n", stderr); fputs("surf - simple browser\n", stderr);
die("usage: surf [-e Window] [-x] [uri]\n"); die("usage: surf [-e xid] [-i] [-p] [-s] [-v] [-x] [uri]\n");
} }
void void
windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame, JSContextRef js, JSObjectRef win, Client *c) { windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame, JSContextRef js, JSObjectRef win, Client *c) {
JSStringRef jsscript; runscript(frame, js);
char *script;
JSValueRef exception = NULL;
GError *error;
if(g_file_get_contents(scriptfile, &script, NULL, &error)) {
jsscript = JSStringCreateWithUTF8CString(script);
JSEvaluateScript(js, jsscript, JSContextGetGlobalObject(js), NULL, 0, &exception);
}
} }
void void
@@ -816,29 +795,43 @@ zoom(Client *c, const Arg *arg) {
} }
} }
int main(int argc, char *argv[]) { int
main(int argc, char *argv[]) {
int i; int i;
Arg arg; Arg arg;
progname = argv[0]; progname = argv[0];
/* command line args */ /* command line args */
for(i = 1, arg.v = NULL; i < argc && argv[i][0] == '-'; i++) { for(i = 1, arg.v = NULL; i < argc && argv[i][0] == '-' &&
if(!strcmp(argv[i], "-x")) argv[i][1] != '\0' && argv[i][2] == '\0'; i++) {
showxid = TRUE; if(!strcmp(argv[i], "--")) {
else if(!strcmp(argv[i], "-e")) { i++;
break;
}
switch(argv[i][1]) {
case 'e':
if(++i < argc) if(++i < argc)
embed = atoi(argv[i]); embed = atoi(argv[i]);
else else
usage(); usage();
}
else if(!strcmp(argv[i], "--")) {
i++;
break; break;
} case 'i':
else if(!strcmp(argv[i], "-v")) loadimage = 0;
break;
case 'p':
plugin = 0;
break;
case 's':
script = 0;
break;
case 'x':
showxid = TRUE;
break;
case 'v':
die("surf-"VERSION", © 2009 surf engineers, see LICENSE for details\n"); die("surf-"VERSION", © 2009 surf engineers, see LICENSE for details\n");
else default:
usage(); usage();
}
} }
if(i < argc) if(i < argc)
arg.v = argv[i]; arg.v = argv[i];
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B