From e8085783d5639b93dbc7a9222d0a9134bc168fd6 Mon Sep 17 00:00:00 2001
From: J C Lawrence <claw@kanga.nu>
Date: Tue, 14 Jan 2014 10:34:00 +0000
Subject: Add -checkpoint option

The following patch when applied to the 2.4 Debian sources (note
that the current release of browser-history is 2.8) adds the
following feature:

  If "browser-history -c" is run, the currently running instance of
  browser-history will save a list of all the URLs and their titles
  that are currently being viewed by a browser-history compatible
  browser (eg Netscape, Amaya, etc) to the normal history file as a
  "Checkpoint".

I wrote this specifically as I tend to have many netscape windows
open at a time (20 - 30 is about average) and I tend to preserve
state and context within those windows (what I'm interested in, what
I want to get back to, etc).  Now, by hanging "browser-history -c"
off a root menu item, or a window manager's GUI button I can
checkpoint the exact list of sites I'm currently viewing so I can
later easily restore that context later (eg machine crash due to the
2.4.0-test* kernels I'm running).

I'll be updating this patch to the current v2.8 release and sending
it to the author as well.

Origin: other, http://lists.svlug.org/archives/svlug/2000-September/026797.html

Patch-Name: checkpoint.patch
---
 browser-history.c    | 124 +++++++++++++++++++++++++++++++++++++++++++++++++--
 browser-history.html |   5 +++
 2 files changed, 125 insertions(+), 4 deletions(-)

diff --git a/browser-history.c b/browser-history.c
index af229cf..e500e79 100644
--- a/browser-history.c
+++ b/browser-history.c
@@ -7,6 +7,7 @@
 /* Copyright Colas Nahaboo 1996, http://www.inria.fr/koala/colas. 
  * See http://www.inria.fr/koala/colas/browser-history
  */
+/* Checkpoint patch by J C Lawrence, 2000. */
 char *RCS_ID = "$Id: browser-history.c,v 2.8 2000/07/25 15:46:26 colas Exp $";
 static char VERSION[8];
 /* Compile with:
@@ -137,13 +138,16 @@ long delay = 3600L;			/* if more seconds, <hr>  */
 int tag_mode = 0;			/* if another browser-history exists:
 					 -1 aborts, 1 kills it, 0 kills unless
 					 we are an older version
-					 2 kills and exit */
+					 2 kills and exit
+					 3 sends SIGUSR1 unless we are an older
+					 version */
 int dontgrab = 0;			/* for debug */
 
 char *displayname = 0;			/* the display name */
 Display *dpy;				/* the display */
 int verbose = 0;			/* verbose mode? */
 int must_reinit = 0;			/* do we need to re-read init files? */
+int checkpoint = 0;			/* do we need to checkpoint all URLs? */
 
 Browser_win browser_windows;		/* the list of existing N wins */
 int browser_windows_size = 0;		/* its size */
@@ -203,14 +207,18 @@ int index_of_win(Window win);
 int URL_already_present(char *name, char *url);
 void record_URL(int number, char *name, char *url);
 void reinit_handler();
+void checkpoint_handler();
 int verboseXHandler(Display *dpy, XErrorEvent *error);
 int terseXHandler(Display *dpy, XErrorEvent *error);
 int is_browser_window(Window win);
 int decode_ARENA_LOCATION(char *full_name, char **namep, char **urlp);
 void init_log_file(int first, FILE *fd);
+void checkpoint_header(char *datestring, struct tm *times);
+void checkpoint_footer(char *datestring, struct tm *times);
 long add_log_entry(char *name, char *url, unsigned int win,
 		   char *datestring, struct tm *times, int separator);
 void reinit();
+void checkpointURLs();
 Window create_tag_window(int mode, int version, char *machine, int pid);
 char *date2string(struct tm *times);
 void update_tag_window(Window w, char *name, char *url, unsigned int win,
@@ -257,6 +265,8 @@ mainloop()
 	XNextEvent(dpy, &event);
 	if (must_reinit)		/* signal 1 was sent */
 	    reinit();
+	if (checkpoint)			/* signal SIGUSR1 was sent */
+	    checkpointURLs();
 	switch (event.type) {
 
 	case PropertyNotify:		/* prop change on tracked window */
@@ -368,6 +378,9 @@ initialize(int argc, char**argv)
 	    case 'D':			/* -DontGrab */
 		dontgrab = 1;
 		continue;
+	    case 'c':			/* -checkpoint */
+		tag_mode = 3;
+		continue;
 	    }
 	}
 	usage();			/* default: unknown option */
@@ -394,6 +407,17 @@ initialize(int argc, char**argv)
 	sigaction(SIGHUP, &action, 0);
     }
 
+    {   /* trap signal SIGUSR1, make it checkpoint */
+	struct sigaction action;
+	sigaction(SIGUSR1, 0, &action);
+#ifdef SA_RESTART
+	action.sa_flags |= SA_RESTART;
+	/* else if no SA_RESTART (SUNs) suppose the default behavior is Ok... */
+#endif
+	action.sa_handler = checkpoint_handler;
+	sigaction(SIGUSR1, &action, 0);
+    }
+
     /* init X */
     dpy = XOpenDisplay (displayname);
     if (!dpy) {
@@ -475,6 +499,30 @@ reinit()
     must_reinit = 0;
 }
 
+/* Checkpoint all windows */
+
+void checkpointURLs ()
+{
+    char *datestring;
+    struct tm *times;
+    time_t seconds;
+    int ndx;
+
+    time(&seconds);
+    times = localtime(&seconds);
+    datestring = date2string(times);
+
+    checkpoint_header(datestring, times);
+    for (ndx = 0; ndx < browser_windows_size; ndx++) {
+	add_URL(ndx);
+    }
+    checkpoint_footer(datestring, times);
+
+    /* Done */
+
+    checkpoint = 0;
+}
+
 int
 not_excluded_URL(char *url)
 {
@@ -560,6 +608,51 @@ init_log_file(int first, FILE *fd)
     fflush(fd);
 }
 
+void checkpoint_header(char *datestring, struct tm *times)
+{
+    FILE *fd = fopen (logfilepath, "a");
+    if (!fd) {			/* cannot open? try to make dir */
+	char logdir[1024];
+	sprintf(logdir, "%s/%s", homedir, logreldir);
+	mkdir(logdir, 0700);
+	fd = fopen(logfilepath, "a"); /* retry now */
+    }
+    if (!fd) {			/* cannot do anything */
+	P(E, "Warning: could not open %s!\n", logfilepath);
+	return;
+    }
+    if (ftell(fd) == 0) {	/* init file if empty */
+	init_log_file(1, fd);
+	last_times = *times;
+    }
+
+    fprintf(fd, "<hr noshade>\n");
+    fprintf(fd, "<h1>Currently viewed URLs (checkpoint):</h1>\n");
+    fclose(fd);
+}
+
+void checkpoint_footer(char *datestring, struct tm *times)
+{
+    FILE *fd = fopen(logfilepath, "a");
+    if (!fd) {			/* cannot open? try to make dir */
+	char logdir[1024];
+	sprintf(logdir, "%s/%s", homedir, logreldir);
+	mkdir(logdir, 0700);
+	fd = fopen(logfilepath, "a"); /* retry now */
+    }
+    if (!fd) {			/* cannot do anything */
+	P(E, "Warning: could not open %s!\n", logfilepath);
+	return;
+    }
+    if (ftell(fd) == 0) {	/* init file if empty */
+	init_log_file(1, fd);
+	last_times = *times;
+    }
+
+    fprintf(fd, "<hr noshade>\n");
+    fclose(fd);
+}
+
 long					/* current filepos in file, or -1 */
 add_log_entry(char *name, char *url, unsigned int win,
 	      char *datestring, struct tm *times, int separator)
@@ -662,6 +755,11 @@ reinit_handler(int sig, void *sip, void *uap)
     must_reinit = 1;
 }
 
+void checkpoint_handler(int sig, void *sip, void *uap)
+{
+    checkpoint = 1;
+}
+
 int
 terseXHandler(Display *dpy, XErrorEvent *error) {return 0;}
 
@@ -870,7 +968,7 @@ add_URL(int number)
     } else
 	return;				/* unknown browser */
     if (url != nilstring && not_excluded_URL(url)
-	&& !URL_already_present(name, url)) {
+	&& (!URL_already_present(name, url) || checkpoint)) {
 	char *datestring;
 	time(&seconds);
 	times = localtime(&seconds);
@@ -982,23 +1080,41 @@ create_tag_window(int mode, int version, char *machine, int pid)
     }
     if (mode == 2)
 	exit(0);
-    if (mode == 0) {			/* check version */
+    if (mode == 0 || mode == 3) {	/* check version */
 	int old_version;
+	pid_t old_pid;
+	char *pid_str;
+
 	result = XGetWindowProperty(dpy, w, PROCESS, 0, 63, 0,
 				    XA_STRING, &actual_type, &actual_format,
 				    &nitems, &bytes_after_return, &property);
 	old_version = atoi(strchr(property, '=') + 1);
+
+	/* Get PID of other process.  The PID is in the 4th string */
+	/* e.g.   VERSION=1009\0DATE=96/05/16-09:17:08\0 */
+	/*        MACHINE=koala\0PID=4345\0 */
+
+	pid_str = property;
+	pid_str += strlen(pid_str) + 1; /* DATE */
+	pid_str += strlen(pid_str) + 1; /* MACHINE */
+	pid_str += strlen(pid_str) + 1; /* PID */
+	old_pid = atoi(strchr(pid_str, '=') + 1);
+
 	XFree(property);
 	if (version > old_version) {
 	    if (verbose)
 		P(E, "killing previous version (%d.%d) of browser-history\n", 
 		  old_version/1000, old_version%1000);
 	    XKillClient(dpy, w);
-	} else {
+	} else if (mode == 0) {
 	    XUngrabServer(dpy);
 	    if (verbose)
 		P(E, "same or more recent version (%d.%d) of browser-history running, aborting\n", old_version/1000, old_version%1000);
 	    exit(0);
+	} else {
+	    /* checkpoint */
+	    kill(old_pid, SIGUSR1);
+	    exit(0);
 	}
     }
     
diff --git a/browser-history.html b/browser-history.html
index 9d480ba..b5cec2f 100644
--- a/browser-history.html
+++ b/browser-history.html
@@ -266,6 +266,11 @@ files.
     deadlocks while debugging, when browser-history or gdb tries to print on
     the grabbed xterm or emacs.
 
+<DT><TT><b>-checkpoint</b></TT> <DD>Cause the currently running instance of
+    browser-history to save a list of all the URLs and their titles that are
+    currently being viewed by a browser-history compatible browser to the
+    normal history file as a &quot;checkpoint&quot;.
+
 </DL>
 
 <h2>Specifications of the log files</h2>
