*/
#include "disobedience.h"
-#include "table.h"
-#include "html.h"
-#include "manual.h"
+#include <sys/wait.h>
+#include <unistd.h>
-VECTOR_TYPE(markstack, GtkTextMark *, xrealloc);
-
-/** @brief Known tag type */
-struct tag {
- /** @brief HTML tag name */
- const char *name;
-
- /** @brief Called to set up the tag */
- void (*init)(GtkTextTag *tag);
-
- /** @brief GTK+ tag object */
- GtkTextTag *tag;
-};
-
-/** @brief Initialize the bold tag
- *
- * This doesn't seem to work on OS X though the italic and monospace tags are
- * fine, and bold is OK on Linux, even connecting to the Apple X swerver.
- */
-static void init_bold(GtkTextTag *tag) {
- g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_BOLD, (char *)0);
-}
-
-/** @brief Initialize the italic tag */
-static void init_italic(GtkTextTag *tag) {
- g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, (char *)0);
-}
-
-/** @brief Initialize the pre tag */
-static void init_pre(GtkTextTag *tag) {
- g_object_set(G_OBJECT(tag), "family", "monospace", (char *)0);
-}
-
-/** @brief Table of known tags
- *
- * Keep in alphabetical order
- */
-static struct tag tags[] = {
- { "b", init_bold, 0 },
- { "i", init_italic, 0 },
- { "pre", init_pre, 0 }
-};
-
-/** @brief Number of known tags */
-#define NTAGS (sizeof tags / sizeof *tags)
-
-/** @brief State structure for insert_html() */
-struct state {
- /** @brief The buffer to insert into */
- GtkTextBuffer *buffer;
-
- /** @brief True if we are inside <body> */
- int body;
-
- /** @brief True if inside <pre> */
- int pre;
-
- /** @brief True if a space is required before any non-space */
- int space;
-
- /** @brief Stack of marks corresponding to tags */
- struct markstack marks[1];
-
-};
-
-/** @brief Called for an open tag */
-static void html_open(const char *tag,
- hash attribute((unused)) *attrs,
- void *u) {
- struct state *const s = u;
- GtkTextIter iter[1];
-
- if(!strcmp(tag, "body"))
- ++s->body;
- else if(!strcmp(tag, "pre"))
- ++s->pre;
- if(!s->body)
- return;
- /* push a mark for the start of the region */
- gtk_text_buffer_get_iter_at_mark(s->buffer, iter,
- gtk_text_buffer_get_insert(s->buffer));
- markstack_append(s->marks,
- gtk_text_buffer_create_mark(s->buffer,
- 0/* mark_name */,
- iter,
- TRUE/*left_gravity*/));
-}
-
-/** @brief Called for a close tag */
-static void html_close(const char *tag,
- void *u) {
- struct state *const s = u;
- GtkTextIter start[1], end[1];
- int n;
-
- if(!strcmp(tag, "body"))
- --s->body;
- else if(!strcmp(tag, "pre")) {
- --s->pre;
- s->space = 0;
- }
- if(!s->body)
- return;
- /* see if this is a known tag */
- if((n = TABLE_FIND(tags, struct tag, name, tag)) < 0)
- return;
- /* pop the mark at the start of the region */
- assert(s->marks->nvec > 0);
- gtk_text_buffer_get_iter_at_mark(s->buffer, start,
- s->marks->vec[--s->marks->nvec]);
- gtk_text_buffer_get_iter_at_mark(s->buffer, end,
- gtk_text_buffer_get_insert(s->buffer));
- /* apply a tag */
- gtk_text_buffer_apply_tag(s->buffer, tags[n].tag, start, end);
- /* don't need the start mark any more */
- gtk_text_buffer_delete_mark(s->buffer, s->marks->vec[s->marks->nvec]);
-}
-
-/** @brief Called for text */
-static void html_text(const char *text,
- void *u) {
- struct state *const s = u;
-
- /* ignore header */
- if(!s->body)
- return;
- if(!s->pre) {
- char *formatted = xmalloc(strlen(text) + 1), *t = formatted;
- /* normalize spacing */
- while(*text) {
- if(isspace((unsigned char)*text)) {
- s->space = 1;
- ++text;
- } else {
- if(s->space) {
- *t++ = ' ';
- s->space = 0;
- }
- *t++ = *text++;
- }
- }
- *t = 0;
- text = formatted;
- }
- gtk_text_buffer_insert_at_cursor(s->buffer, text, strlen(text));
-}
-
-/** @brief Callbacks for insert_html() */
-static const struct html_parser_callbacks insert_html_callbacks = {
- html_open,
- html_close,
- html_text
-};
-
-/** @brief Insert @p html into @p buffer at cursor */
-static void insert_html(GtkTextBuffer *buffer,
- const char *html) {
- struct state s[1];
- size_t n;
- GtkTextTagTable *tagtable;
-
- memset(s, 0, sizeof *s);
- s->buffer = buffer;
- markstack_init(s->marks);
- /* initialize tags */
- if(!tags[0].tag)
- for(n = 0; n < NTAGS; ++n)
- tags[n].init(tags[n].tag = gtk_text_tag_new(0));
- /* add tags to this buffer */
- tagtable = gtk_text_buffer_get_tag_table(s->buffer);
- for(n = 0; n < NTAGS; ++n)
- gtk_text_tag_table_add(tagtable, tags[n].tag);
- /* convert the input */
- html_parse(&insert_html_callbacks, html, s);
-}
-
-/** @brief Create a GtkTextBuffer with @p html rendered into it */
-static GtkTextBuffer *html_buffer(const char *html) {
- GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
-
- insert_html(buffer, html);
- return buffer;
-}
-
-/** @brief The manual page window */
-static GtkWidget *help_window;
-
-/** @brief Pop up the manual page in a window */
+/** @brief Display the manual page */
void popup_help(void) {
- GtkWidget *view;
-
- if(help_window) {
- gtk_window_present(GTK_WINDOW(help_window));
- return;
+ char *path;
+ pid_t pid;
+ int w;
+
+ byte_xasprintf(&path, "%s/disobedience.html", docdir);
+ if(!(pid = xfork())) {
+ exitfn = _exit;
+ if(!xfork()) {
+ execlp(browser, browser, path, (char *)0);
+ fatal(errno, "error executing %s", browser);
+ }
+ _exit(0);
}
- help_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- g_signal_connect(help_window, "destroy",
- G_CALLBACK(gtk_widget_destroyed), &help_window);
- gtk_window_set_title(GTK_WINDOW(help_window), "Disobedience Manual Page");
- view = gtk_text_view_new_with_buffer(html_buffer(manual));
- gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
- gtk_container_add(GTK_CONTAINER(help_window), scroll_widget(view));
- gtk_window_set_default_size(GTK_WINDOW(help_window), 512, 512);
- gtk_widget_show_all(help_window);
+ while(waitpid(pid, &w, 0) < 0 && errno == EINTR)
+ ;
}
/*