From: Carlos Maddela <e7appew@gmail.com>
Date: Thu, 5 Jan 2017 19:11:38 +1100
Subject: Resurrect old code to support opening URLs in a browser.

Description: Resurrect old code to support opening URLs in a browser.
Author: Carlos Maddela <e7appew@gmail.com>
Forwarded: not-needed
Last-Update: 2016-12-21
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
---
 closure.c      |   2 +
 dvdisaster.h   |   7 +
 help-dialogs.c |   3 +-
 show-html.c    | 402 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 413 insertions(+), 1 deletion(-)
 create mode 100644 show-html.c

diff --git a/closure.c b/closure.c
index fa52f09..24c1e8c 100644
--- a/closure.c
+++ b/closure.c
@@ -474,6 +474,7 @@ void InitClosure()
    Closure->deviceNames = g_ptr_array_new();
    Closure->deviceNodes = g_ptr_array_new();
    Closure->viewer      = g_strdup("xdg-open");
+   Closure->browser      = g_strdup("xdg-open");
    Closure->methodList  = g_ptr_array_new();
    Closure->methodName  = g_strdup("RS01");
    Closure->dDumpDir    = g_strdup(Closure->homeDir);
@@ -589,6 +590,7 @@ void FreeClosure()
    cond_free(Closure->binDir);
    cond_free(Closure->docDir);
    cond_free(Closure->viewer);
+   cond_free(Closure->browser);
    cond_free(Closure->errorTitle);
    cond_free(Closure->simulateCD);
    cond_free(Closure->dDumpDir);
diff --git a/dvdisaster.h b/dvdisaster.h
index 9acd094..f536040 100644
--- a/dvdisaster.h
+++ b/dvdisaster.h
@@ -229,6 +229,7 @@ typedef struct _GlobalClosure
    char *binDir;        /* place where the binary resides */
    char *docDir;        /* place where our documentation resides */
    char *viewer;        /* Name of preferred PDF viewer */
+   char *browser;       /* Name of preferred browser */
 
    GMutex progressLock; /* A mutex protected the stuff below */
    char bs[256];        /* A string of 255 backspace characters */
@@ -1348,6 +1349,12 @@ int ProbeAltiVec(void);
 
 void ShowPDF(char*);
 
+/***
+ *** show-html.c
+ ***/
+
+void ShowHTML(char*);
+
 /***
  *** smart-lec.c
  ***/
diff --git a/help-dialogs.c b/help-dialogs.c
index 75a615e..dc5b440 100644
--- a/help-dialogs.c
+++ b/help-dialogs.c
@@ -599,7 +599,8 @@ static gint about_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
    {  case GDK_BUTTON_PRESS: 
         if(!inside) return FALSE; /* Defect in certain Gtk versions? */
         if(!strcmp(label,"GPL")) ShowGPL(); 
-        else if(!strcmp(label,"MODIFYING")) show_modifying(); 
+        else if(!strcmp(label,"MODIFYING")) show_modifying();
+        else if(strlen(label) > 4 && !strncmp(label, "http", 4)) ShowHTML(g_strdup(label));
         else ShowPDF(g_strdup(label));
 	break; 
       case GDK_ENTER_NOTIFY: 
diff --git a/show-html.c b/show-html.c
new file mode 100644
index 0000000..608e8ec
--- /dev/null
+++ b/show-html.c
@@ -0,0 +1,402 @@
+/*  dvdisaster: Additional error correction for optical media.
+ *  Copyright (C) 2004-2012 Carsten Gnoerlich.
+ *  Project home page: http://www.dvdisaster.com
+ *  Email: carsten@dvdisaster.com  -or-  cgnoerlich@fsfe.org
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA,
+ *  or direct your browser at http://www.gnu.org.
+ */
+
+#include "dvdisaster.h"
+
+#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_NETBSD)
+#include <sys/wait.h>
+#endif
+
+#ifdef SYS_MINGW
+#include "windows.h"
+#include "shellapi.h"
+#endif
+
+/***
+ *** Ask user to specify his browser
+ ***/
+
+#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_NETBSD)
+
+#define SEARCH_BUTTON 1
+
+typedef struct
+{  GtkWidget *dialog;
+   GtkWidget *entry;
+   GtkWidget *search;
+   GtkWidget *filesel;
+   GtkWidget *fileok;
+   GtkWidget *filecancel;
+   char *url;
+} browser_dialog_info;
+
+static void response_cb(GtkWidget *widget, int response, gpointer data)
+{  browser_dialog_info *bdi = (browser_dialog_info*)data; 
+
+   switch(response)
+   {  case GTK_RESPONSE_ACCEPT:
+	if(Closure->browser) g_free(Closure->browser);
+	Closure->browser = g_strdup(gtk_entry_get_text(GTK_ENTRY(bdi->entry)));
+	ShowHTML(bdi->url);
+	break;
+
+      case GTK_RESPONSE_REJECT:
+	if(bdi->url) g_free(bdi->url);
+        break;
+   }
+   gtk_widget_destroy(widget);
+   if(bdi->filesel)
+     gtk_widget_destroy(bdi->filesel);
+   g_free(bdi);
+}
+
+static void search_cb(GtkWidget *widget, gpointer data)
+{  browser_dialog_info *bdi = (browser_dialog_info*)data; 
+
+   if(widget == bdi->search) 
+   {  bdi->filesel = gtk_file_selection_new(_utf("windowtitle|Choose a browser"));
+      bdi->fileok = GTK_FILE_SELECTION(bdi->filesel)->ok_button;
+      bdi->filecancel = GTK_FILE_SELECTION(bdi->filesel)->cancel_button;
+      ReverseCancelOK(GTK_DIALOG(bdi->filesel));
+      gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(bdi->filesel));
+      g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(bdi->filesel)->ok_button), "clicked", 
+		       G_CALLBACK(search_cb), bdi);
+    
+      g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(bdi->filesel)->cancel_button), "clicked", 
+		       G_CALLBACK(search_cb), bdi);
+      
+      gtk_widget_show(bdi->filesel);
+   }
+
+   if(widget == bdi->fileok)
+   {
+      if(Closure->browser) g_free(Closure->browser);
+      Closure->browser = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(bdi->filesel)));
+      ShowHTML(bdi->url);
+      gtk_widget_destroy(bdi->filesel);
+      gtk_widget_destroy(bdi->dialog);
+      g_free(bdi);
+      return;
+   }
+
+   if(widget == bdi->filecancel)
+   {  gtk_widget_destroy(bdi->filesel);
+      bdi->filesel = NULL;
+   }
+}
+
+static void browser_dialog(char *url)
+{  GtkWidget *dialog, *vbox, *hbox, *label, *entry, *button;
+   browser_dialog_info *bdi = g_malloc0(sizeof(browser_dialog_info));
+
+   /* Create the dialog */
+
+   dialog = gtk_dialog_new_with_buttons(_utf("windowtitle|Browser required"), 
+				       Closure->window, GTK_DIALOG_DESTROY_WITH_PARENT,
+				       GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 
+				       GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
+   bdi->dialog = dialog;
+   if(url)
+   {  bdi->url = g_strdup(url);
+   }
+
+   vbox = gtk_vbox_new(FALSE, 0);
+   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, FALSE, FALSE, 0);
+   gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
+
+   /* Insert the contents */
+
+   label = gtk_label_new(NULL);
+   gtk_label_set_markup(GTK_LABEL(label), _utf("<b>Could not find a suitable browser.</b>\n\n"
+                                               "Which browser would you like to use\n"
+                                               "for reading the online documentation?\n\n"
+			                       "Please enter its name (e.g. mozilla) or\n"
+			                       "use the \"Search\" button for a file dialog.\n")),
+			      gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 10);
+
+   hbox = gtk_hbox_new(FALSE, 0);
+   gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 10);
+
+   bdi->entry = entry = gtk_entry_new();
+   gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 10);
+
+   bdi->search = button = gtk_button_new_with_label(_utf("Search"));
+   g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(search_cb), bdi);
+   gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 10);
+
+   /* Show it */
+
+   g_signal_connect(dialog, "response", G_CALLBACK(response_cb), bdi);
+
+   gtk_widget_show_all(dialog);
+}
+#endif /* SYS_ unix-like */
+
+/***
+ *** Show the manual in an external browser
+ ***/
+
+/*
+ * Check the child processes exit status
+ * to find whether the browser could be invoked.
+ */
+
+typedef struct
+{  pid_t pid;
+   char *url;
+   GtkWidget *msg;
+   int seconds;
+} browser_info;
+
+
+static void msg_destroy_cb(GtkWidget *widget, gpointer data)
+{  browser_info *bi = (browser_info*)data;
+
+   bi->msg = NULL; 
+}
+
+#if defined(SYS_LINUX) || defined(SYS_FREEBSD) ||  defined(SYS_NETBSD)
+
+/* 
+ * The following list of browsers and html wrappers
+ * will be tried one at a time until one entry succeeds by:
+ * - returning zero
+ * - not returning within 60 seconds
+ */
+
+static int browser_index;
+static void try_browser(browser_info*);
+
+static char *browsers[] = 
+{  "user-selection",
+   "xdg-open",
+   "gnome-open",
+   "htmlview",
+   "firefox",
+   "mozilla",
+   "konqueror",
+   "epiphany",
+   "opera",
+   "/Applications/Safari.app/Contents/MacOS/Safari",  /* better way to do this? */
+   NULL
+};
+
+static gboolean browser_timeout_func(gpointer data)
+{  browser_info *bi = (browser_info*)data;
+   int status;
+
+   waitpid(bi->pid, &status, WNOHANG);
+
+   /* At least mozilla returns random values under FreeBSD on success,
+      so we can't rely on the return value exept our own 110 one. */
+
+   if(WIFEXITED(status))
+   {
+      switch(WEXITSTATUS(status))
+      {  case 110: /* browser did not execute */
+	   browser_index++;
+	   if(!browsers[browser_index]) /* all browsers from the list failed */
+	   {  browser_dialog(bi->url);
+
+	      if(bi->msg) 
+		gtk_widget_destroy(bi->msg);
+	      if(bi->url) 
+		g_free(bi->url);
+	      g_free(bi);
+	   }
+	   else                        /* try next browser from list */
+	   {  bi->seconds = 0;  
+	      try_browser(bi);
+	   }
+	   return FALSE;
+
+         case 0:  /* browser assumed to be successful */
+         default:
+	   if(bi->msg) 
+	     gtk_widget_destroy(bi->msg);
+	   if(bi->url) 
+	     g_free(bi->url);
+	   g_free(bi);
+	   return FALSE;
+      }
+   }
+
+   bi->seconds++;
+   if(bi->seconds == 10 && bi->msg)
+   {  gtk_widget_destroy(bi->msg);
+      bi->msg = NULL;
+   }
+
+   return bi->seconds > 60 ? FALSE : TRUE;
+}
+#endif /* SYS_ unix-like */
+
+#ifdef SYS_MINGW
+static gboolean browser_timeout_func(gpointer data)
+{  browser_info *bi = (browser_info*)data;
+   
+   bi->seconds++;
+
+   if(bi->seconds >= 10)
+   {  if(bi->msg)
+      {  gtk_widget_destroy(bi->msg);
+         bi->msg = NULL;
+      }
+      if(bi->url) g_free(bi->url);
+      g_free(bi);
+      return FALSE;
+   }
+
+   return TRUE;
+}
+#endif /* SYS_MINGW */
+
+/*
+ * Invoke the browser
+ */
+
+#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_NETBSD)
+static void try_browser(browser_info *bi)
+{  pid_t pid;
+
+   bi->pid = pid = fork();
+
+   if(pid == -1)
+   {  printf("fork failed\n");
+      return;
+   }
+
+   /* make the parent remember and wait() for the browser */
+
+   if(pid > 0)  
+   {  g_timeout_add(1000, browser_timeout_func, (gpointer)bi);
+
+      if(browser_index)
+      {  g_free(Closure->browser);
+	 Closure->browser = g_strdup(browsers[browser_index]);
+      }
+   }
+
+   /* try calling the browser */
+
+   if(pid == 0)
+   {  char *argv[10];
+      int argc = 0;
+
+      argv[argc++] = browser_index ? browsers[browser_index] : Closure->browser;
+      argv[argc++] = bi->url;
+      argv[argc++] = NULL;
+      execvp(argv[0], argv);
+
+      _exit(110); /* couldn't execute */
+   }
+}
+#endif /* SYS_ unix-like */
+
+
+void ShowHTML(char *target)
+{  browser_info *bi = g_malloc0(sizeof(browser_info));
+   guint64 ignore;
+   const char *lang;
+   char *path = NULL;
+   int http_url;
+
+   /* If no target is given, select between translations of the manual. */
+
+   if(!target) target = g_strdup("index.html");
+
+   http_url = strlen(target) > 4 && !strncmp(target, "http", 4);
+
+   if(!http_url && !strchr(target, '/'))  /* create full path */
+   { 
+      if(!Closure->docDir)
+      {  
+	 CreateMessage(_("Documentation not installed."), GTK_MESSAGE_ERROR);
+         g_free(bi);
+         return;
+      }
+
+      lang = g_getenv("LANG");
+
+      if(lang)
+      {  if(!strncmp(lang, "ru", 2)) 
+#ifdef SYS_MINGW
+	     path = g_strdup_printf("%s\\ru\\%s",Closure->docDir,target); 
+#else
+	     path = g_strdup_printf("%s/ru/%s",Closure->docDir,target); 
+#endif
+         else if(!strncmp(lang, "de", 2)) 
+#ifdef SYS_MINGW
+	     path = g_strdup_printf("%s\\de\\%s",Closure->docDir,target); 
+#else
+	     path = g_strdup_printf("%s/de/%s",Closure->docDir,target); 
+#endif
+      }
+
+      if(!path)
+      {
+#ifdef SYS_MINGW
+         path = g_strdup_printf("%s\\en\\%s",Closure->docDir,target); 
+#else
+         path = g_strdup_printf("%s/en/%s",Closure->docDir,target); 
+#endif
+      }
+
+#ifdef SYS_MINGW      
+      if(!LargeStat(path, &ignore))
+      {  
+	 g_free(path);  /* the local dir is Windows specific */
+	 path = g_strdup_printf("%s\\local\\%s",Closure->docDir,target);
+      }
+#endif
+      g_free(target);
+      bi->url = path;
+   }
+   else bi->url = target;
+
+   if(!http_url && !LargeStat(bi->url, &ignore))
+   {  
+      CreateMessage(_("Documentation file\n%s\nnot found.\n"), GTK_MESSAGE_ERROR, bi->url);
+      g_free(bi);
+      g_free(bi->url);
+      return;
+   }
+
+   /* Lock the help button and show a message for 10 seconds. */
+
+   TimedInsensitive(Closure->helpButton, 10000);
+   bi->msg = CreateMessage(_("Please hang on until the browser comes up!"), GTK_MESSAGE_INFO);
+   g_signal_connect(G_OBJECT(bi->msg), "destroy", G_CALLBACK(msg_destroy_cb), bi);
+
+#ifdef SYS_MINGW
+   /* Okay, Billy wins big time here ;-) */
+
+   ShellExecute(NULL, "open", bi->url, NULL, NULL, SW_SHOWNORMAL);
+   g_timeout_add(1000, browser_timeout_func, (gpointer)bi);
+#endif
+
+#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_NETBSD)
+   /* Try the first browser */
+
+   browser_index = 0;
+   try_browser(bi);
+#endif
+}
