/* * Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com) * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * CONECTIVA LINUX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Conectiva Linux shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from * Conectiva Linux. * * Author: Paulo César Pereira de Andrade * * $XFree86: xc/programs/Xserver/hw/xfree86/xf86cfg/help.c,v 1.7tsi Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* for bsearch() */ #include #include "help.h" #include "options.h" /* * Prototypes */ static void CloseCallback(Widget, XtPointer, XtPointer); static void StartHelp(void); void Html_ModeStart(Widget); /* * Initialization */ static Widget shell, text; static Bool popped_up = False; /* * Implementation */ void Help(char *topic) { Widget source; char *str = NULL; Bool error = False; static char *def_text = "

Help Error

" "No help available for the topic %s."; XtResource resource = { NULL, "HelpMessage", XtRString, sizeof(char*), 0, XtRString, NULL }; StartHelp(); source = XawTextGetSource(text); XawTextSourceClearEntities(source, 0, XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True)); if (topic != NULL) { resource.resource_name = topic; XtGetApplicationResources(shell, (XtPointer)&str, &resource, 1, NULL, 0); } if (str == NULL) { int len; error = True; if (topic == NULL) topic = "(null argument)"; str = XtMalloc(len = strlen(topic) + strlen(def_text) + 1); XmuSnprintf(str, len, def_text, topic); } XtVaSetValues(text, XtNstring, str, NULL); if (error) XtFree(str); Html_ModeStart(source); _XawTextBuildLineTable((TextWidget)text, XawTextTopPosition(text), True); XawTextDisplay(text); if (popped_up == False) { popped_up = True; XtPopup(shell, XtGrabNone); XtSetKeyboardFocus(shell, text); } } static void StartHelp(void) { static XtResource resource = { "properties", "Properties", XtRString, sizeof(char*), 0, XtRString, NULL }; if (shell == NULL) { Widget pane, commands, close; char *props; XawTextPropertyList *propl; shell = XtCreatePopupShell("help", transientShellWidgetClass, toplevel, NULL, 0); pane = XtCreateManagedWidget("pane", panedWidgetClass, shell, NULL, 0); text = XtVaCreateManagedWidget("text", asciiTextWidgetClass, pane, XtNeditType, XawtextRead, NULL); commands = XtCreateManagedWidget("commands", formWidgetClass, pane, NULL, 0); close = XtCreateManagedWidget("close", commandWidgetClass, commands, NULL, 0); XtAddCallback(close, XtNcallback, CloseCallback, NULL); XtRealizeWidget(shell); XSetWMProtocols(DPY, XtWindow(shell), &wm_delete_window, 1); XtGetApplicationResources(text, (XtPointer)&props, &resource, 1, NULL, 0); propl = XawTextSinkConvertPropertyList("html", props, toplevel->core.screen, toplevel->core.colormap, toplevel->core.depth); XtVaSetValues(XawTextGetSink(text), XawNtextProperties, propl, NULL); } } /*ARGSUSED*/ static void CloseCallback(Widget w, XtPointer user_data, XtPointer call_data) { XtPopdown(shell); popped_up = False; } /*ARGSUSED*/ void HelpCancelAction(Widget w, XEvent *event, String *params, Cardinal *num_params) { CloseCallback(w, NULL, NULL); } /* bellow is a modified version of the html-mode.c I wrote for xedit * (at least) temporarily dead. */ /* * Copyright (c) 1999 by The XFree86 Project, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of the XFree86 Project shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from the * XFree86 Project. * * Author: Paulo César Pereira de Andrade */ #define Html_Peek(parser) ((parser)->next) /* * Types */ typedef struct _Html_Parser Html_Parser; typedef struct _Html_Item Html_Item; typedef struct _Html_TagInfo { char *name; unsigned int entity : 1; /* it changes the type of the text */ unsigned int nest : 1; /* does not close tags automatically */ unsigned int end : 1; /* need a close markup */ unsigned int adnl : 1; /* add newline before/after tag contents */ unsigned int para : 1; /* changes the paragraph formatting */ unsigned long mask; /* enforce use of attributes of this tag-info */ unsigned long xlfd_mask; void (*parse_args)(Html_Parser*, Html_Item*); XawTextProperty *override; XrmQuark ident; } Html_TagInfo; struct _Html_Item { XrmQuark ident; XawTextPosition start, end; Html_TagInfo *info; XawTextProperty *combine; Bool override; int li; XtPointer replace; Html_Item *parent, *child, *next; }; struct _Html_Parser { Widget source; XawTextBlock block, replace; XawTextPosition position, offset, start, end, last; XrmQuark quark; int i, ch, next; Html_Item *item, *head; XmuScanline *mask; int space, pre, adnl, list, desc, column; Bool spc; XawTextBlock *entity; Pixel alink; }; typedef struct _Html_SourceInfo Html_SourceInfo; struct _Html_SourceInfo { Widget source; XawTextBlock block; XawTextPosition last; Html_SourceInfo *next; }; /* * Proptotypes */ void Html_ModeEnd(Widget); static void Html_ModeInit(void); static void Html_ParseCallback(Widget, XtPointer, XtPointer); static Html_TagInfo *Html_GetInfo(char*); static int Html_Get(Html_Parser*); static int Html_Parse1(Html_Parser*); static int Html_Parse2(Html_Parser*); static void Html_ParseTag(Html_Parser*); static void Html_Commit(Html_Parser*); static void Html_AddEntities(Html_Parser*, Html_Item*); static int Html_Put(Html_Parser*, int); static void Html_Puts(Html_Parser*, char*); static int Html_Format1(Html_Parser*); static int Html_Format2(Html_Parser*); static int Html_Format3(Html_Parser*); static void Html_FormatTag(Html_Parser*); static void Html_AArgs(Html_Parser*, Html_Item*); static void Html_FontArgs(Html_Parser*, Html_Item*); /* * Initialization */ static XrmQuark Qbr, Qdefault, Qdd, Qdl, Qdt, Qentity, Qetag, Qhide, Qli, Qol, Qp, Qpre, Qspace, Qtag, Qul; static Html_TagInfo tag_info[] = { {"a", 1, 0, 1, 0, 0, 0, 0, Html_AArgs}, {"address", 1, 0, 1, 0, 0, 0, XAW_TPROP_SLANT, }, {"b", 1, 0, 1, 0, 0, 0, XAW_TPROP_WEIGHT, }, {"blockquote", 0, 1, 1, 1, 1, 0, 0, }, {"body", 0, 0, 1, 0, 0, 0, 0, }, {"br", 0, 0, 0, 0, 0, }, {"code", 1, 0, 1, 0, 0, 0, XAW_TPROP_FAMILY | XAW_TPROP_PIXELSIZE, }, {"dd", 0, 1, 1, 0, 1, 0, 0}, {"dl", 0, 1, 1, 0, 0, 0, 0, }, {"dt", 0, 0, 1, 0, 0, 0, 0}, {"em", 1, 0, 1, 0, 0, 0, XAW_TPROP_SLANT, }, {"font", 1, 1, 1, 0, 0, 0, 0, Html_FontArgs}, {"h1", 1, 0, 1, 1, 0, 0, XAW_TPROP_WEIGHT | XAW_TPROP_PIXELSIZE, }, {"h2", 1, 0, 1, 1, 0, 0, XAW_TPROP_WEIGHT | XAW_TPROP_PIXELSIZE, }, {"h3", 1, 0, 1, 1, 0, 0, XAW_TPROP_WEIGHT | XAW_TPROP_PIXELSIZE, }, {"h4", 1, 0, 1, 1, 0, 0, XAW_TPROP_WEIGHT | XAW_TPROP_PIXELSIZE, }, {"h5", 1, 0, 1, 1, 0, 0, XAW_TPROP_WEIGHT | XAW_TPROP_PIXELSIZE, }, {"h6", 1, 0, 1, 1, 0, 0, XAW_TPROP_WEIGHT | XAW_TPROP_PIXELSIZE, }, {"head", 0, 0, 1, 0, 0, 0, 0, }, {"html", 0, 0, 1, 0, 0, 0, 0, }, {"i", 1, 0, 1, 0, 0, 0, XAW_TPROP_SLANT, }, {"kbd", 1, 0, 1, 0, 0, 0, XAW_TPROP_FAMILY | XAW_TPROP_PIXELSIZE, }, {"li", 0, 0, 0, 0, 0, 0, 0}, {"ol", 0, 1, 1, 0, 1, 0, 0, }, {"p", 0, 0, 0, 1, 0, }, {"pre", 1, 0, 1, 1, 0, 0, XAW_TPROP_FAMILY | XAW_TPROP_PIXELSIZE, }, {"samp", 1, 0, 1, 0, 0, 0, XAW_TPROP_FAMILY | XAW_TPROP_PIXELSIZE, }, {"strong", 1, 0, 1, 0, 0, 0, XAW_TPROP_WEIGHT, }, {"tt", 1, 0, 1, 0, 0, 0, XAW_TPROP_FAMILY | XAW_TPROP_PIXELSIZE, }, {"ul", 0, 1, 1, 0, 1, 0, 0, }, }; static char *pnl = "

\n", *nlpnl = "\n

\n"; static Html_SourceInfo *source_info; /* * Implementation */ static char * Html_GetText(Widget src, XawTextPosition position) { char *result, *tempResult; XawTextPosition offset = 0; XawTextBlock text; tempResult = result = XtMalloc((unsigned)(position + 1)); while (offset < position) { offset = XawTextSourceRead(src, offset, &text, position - offset); if (!text.length) break; memcpy(tempResult, text.ptr, (unsigned)text.length); tempResult += text.length; } *tempResult = '\0'; return (result); } void Html_ModeStart(Widget src) { Html_Parser *parser = XtNew(Html_Parser); Html_Item *next, *item; XColor color, exact; Html_SourceInfo *info = XtNew(Html_SourceInfo); if (XAllocNamedColor(XtDisplay(toplevel), toplevel->core.colormap, "blue", &color, &exact)) parser->alink = color.pixel; else parser->alink = 0L; XtVaSetValues(src, XtNeditType, XawtextEdit, NULL); Html_ModeInit(); /* initialize parser state */ parser->source = src; parser->position = XawTextSourceRead(parser->source, 0, &parser->block, 4096); parser->replace.ptr = NULL; parser->replace.firstPos = 0; parser->replace.length = 0; parser->replace.format = FMT8BIT; parser->offset = -1; parser->quark = NULLQUARK; parser->i = 0; parser->i = parser->ch = parser->next = 0; parser->last = XawTextSourceScan(src, 0, XawstAll, XawsdRight, 1, 1); if (parser->block.length == 0) parser->ch = parser->next = EOF; else (void)Html_Get(parser); parser->pre = 0; parser->adnl = 1; parser->list = parser->desc = parser->column = 0; parser->spc = True; info->source = src; info->block.ptr = Html_GetText(src, parser->last); info->block.length = parser->last; info->block.format = FMT8BIT; info->block.firstPos = 0; info->next = NULL; if (source_info == NULL) source_info = info; else { Html_SourceInfo *tmp = source_info; while (tmp->next) tmp = tmp->next; tmp->next = info; } while (Html_Format1(parser) != EOF) ; XawTextSourceReplace(parser->source, 0, parser->last, &parser->replace); XtFree(parser->replace.ptr); /* re-initialize parser state */ parser->position = XawTextSourceRead(parser->source, 0, &parser->block, 4096); parser->offset = -1; parser->quark = NULLQUARK; parser->i = parser->ch = parser->next = 0; parser->last = XawTextSourceScan(src, 0, XawstAll, XawsdRight, 1, 1); info->last = parser->last; if (parser->block.length == 0) parser->ch = parser->next = EOF; else (void)Html_Get(parser); parser->adnl = 1; parser->list = parser->desc = parser->column = 0; parser->spc = True; parser->head = parser->item = NULL; parser->mask = XmuNewScanline(0, 0, 0); /* build html structure information */ while (Html_Parse1(parser) != EOF) ; /* create top level entity mask */ (void)XmuScanlineNot(parser->mask, 0, parser->last); item = parser->item; while (item) { next = item->next; Html_AddEntities(parser, item); if (item->combine) XtFree((XtPointer)item->combine); XtFree((XtPointer)item); item = next; } XmuDestroyScanline(parser->mask); XtVaSetValues(src, XtNeditType, XawtextRead, NULL); XtFree((XtPointer)parser); /* add callbacks for interactive changes */ XtAddCallback(src, XtNpropertyCallback, Html_ParseCallback, NULL); } void Html_ModeEnd(Widget src) { Html_SourceInfo *info, *pinfo; XtRemoveCallback(src, XtNpropertyCallback, Html_ParseCallback, NULL); for (pinfo = info = source_info; info; pinfo = info, info = info->next) if (info->source == src) break; if (info == NULL) return; XawTextSourceClearEntities(src, 0, info->last); XtVaSetValues(src, XtNeditType, XawtextEdit, NULL); XawTextSourceReplace(src, 0, info->last, &info->block); XtVaSetValues(src, XtNeditType, XawtextRead, NULL); if (info == source_info) source_info = source_info->next; else pinfo->next = info->next; XtFree(info->block.ptr); XtFree((XtPointer)info); } static void Html_ParseCallback(Widget w, XtPointer client_data, XtPointer call_data) { } static int bcmp_tag_info(_Xconst void *left, _Xconst void *right) { return (strcmp((char*)left, ((Html_TagInfo*)right)->name)); } static Html_TagInfo * Html_GetInfo(char *name) { return (bsearch(name, tag_info, sizeof(tag_info) / sizeof(tag_info[0]), sizeof(Html_TagInfo), bcmp_tag_info)); } static int Html_Get(Html_Parser *parser) { if (parser->ch == EOF) return (EOF); if (parser->i >= parser->block.length) { parser->i = 0; parser->position = XawTextSourceRead(parser->source, parser->position, &parser->block, 4096); } parser->ch = parser->next; if (parser->block.length == 0) parser->next = EOF; else parser->next = (unsigned char)parser->block.ptr[parser->i++]; parser->offset++; return (parser->ch); } static void Html_ModeInit(void) { static int initialized; int i; if (initialized) return; Qbr = XrmPermStringToQuark("br"); Qdd = XrmPermStringToQuark("dd"); Qdefault = XrmPermStringToQuark("default"); Qdl = XrmPermStringToQuark("dl"); Qdt = XrmPermStringToQuark("dt"); Qentity = XrmPermStringToQuark("entity"); Qetag = XrmPermStringToQuark("/tag"); Qhide = XrmPermStringToQuark("hide"); Qli = XrmPermStringToQuark("li"); Qol = XrmPermStringToQuark("ol"); Qp = XrmPermStringToQuark("p"); Qpre = XrmPermStringToQuark("pre"); Qspace = XrmPermStringToQuark("space"); Qtag = XrmPermStringToQuark("tag"); Qul = XrmPermStringToQuark("ul"); for (i = 0; i < sizeof(tag_info) / sizeof(tag_info[0]); i++) tag_info[i].ident = XrmPermStringToQuark(tag_info[i].name); initialized = True; } /************************************************************************/ /* PARSE */ /************************************************************************/ static void Html_AddEntities(Html_Parser *parser, Html_Item *item) { Html_Item *parent, *next, *child = item->child; XmuSegment segment, *ent; XmuScanline *mask = XmuNewScanline(0, 0, 0); XawTextProperty *tprop, *property = NULL; Widget sink; Bool changed = False; /* combine properties */ if (item->info && (item->info->entity || (item->parent && item->parent->ident != item->parent->info->ident))) { sink = XawTextGetSink(text); parent = item->parent; property = XawTextSinkCopyProperty(sink, item->ident); property->mask = item->info->mask; property->xlfd_mask = item->info->xlfd_mask; if (parent) { (void)XawTextSinkCombineProperty(sink, property, XawTextSinkGetProperty(sink, parent->ident), False); if (item->combine && parent->combine) (void)XawTextSinkCombineProperty(sink, item->combine, parent->combine, item->override); } if (item->combine) XawTextSinkCombineProperty(sink, property, item->combine, True); tprop = property; property = XawTextSinkAddProperty(sink, property); XtFree((XtPointer)tprop); if (property && item->ident != property->identifier) { item->ident = property->identifier; changed = True; } } if (item->end < 0) { if (item->next) item->end = item->next->start; else if (item->parent) item->end = item->parent->end; else item->end = parser->last; } while (child) { next = child->next; segment.x1 = child->start; segment.x2 = child->end; (void)XmuScanlineOrSegment(mask, &segment); Html_AddEntities(parser, child); if (child->combine) XtFree((XtPointer)child->combine); XtFree((XtPointer)child); child = next; } /* build entity mask */ (void)XmuScanlineNot(mask, item->start, item->end); (void)XmuScanlineAnd(mask, parser->mask); /* add entities */ if (item->info && changed) { for (ent = mask->segment; ent; ent = ent->next) (void)XawTextSourceAddEntity(parser->source, 0, 0, NULL, ent->x1, ent->x2 - ent->x1, item->ident); } else if (item->info == NULL) (void)XawTextSourceAddEntity(parser->source, 0, XAW_TENTF_READ | XAW_TENTF_REPLACE, item->replace, item->start, item->end - item->start, item->parent->ident); /* set mask for parent entities */ (void)XmuScanlineOr(parser->mask, mask); XmuDestroyScanline(mask); #if 0 if (item->info && item->info->para) { XawTextSourceSetParagraph(parser->source, item->start, item->end, 40, /* arbitrary value, for testing */ 0, 0); } #endif } static void Html_Commit(Html_Parser *parser) { XawTextPosition position; int length; position = parser->start; length = parser->end - parser->start; if (position < 0) { length += position; position = 0; } if (position + length > parser->last + 1) length -= (position + length) - parser->last + 1; if (parser->quark != Qdefault && parser->quark != NULLQUARK && length > 0) { XmuSegment segment; Html_Item *head = parser->head; XrmQuark quark = parser->quark; parser->quark = Qdefault; if (quark == Qli && head && (head->info->ident == Qol || head->info->ident == Qul)) { if (parser->head == NULL || head->info->ident != Qol) XawTextSourceAddEntity(parser->source, 0, /*XAW_TENT_BULLET,*/ XAW_TENTF_HIDE, NULL, position, length, Qli); else XawTextSourceAddEntity(parser->source, 0, /*XAW_TENT_LITEM,*/ XAW_TENTF_HIDE, (XtPointer)(long)head->li++, position, length, Qli); } else if (quark == Qhide) XawTextSourceAddEntity(parser->source, 0, XAW_TENTF_HIDE, NULL, position, length, quark); else if (quark == Qentity) { if (head && head->end == -1) { Html_Item *item, *it; item = XtNew(Html_Item); item->ident = Qentity; item->start = position; item->end = position + length; item->info = NULL; item->combine = NULL; item->override = False; item->replace = (XtPointer)parser->entity; item->child = item->next = NULL; it = head->child; item->parent = head; if (it == NULL) head->child = item; else { while (it->next) it = it->next; it->next = item; } return; } XawTextSourceAddEntity(parser->source, 0, XAW_TENTF_READ | XAW_TENTF_REPLACE, (XtPointer)parser->entity, position, length, Qentity); } segment.x1 = position; segment.x2 = position + length; (void)XmuScanlineOrSegment(parser->mask, &segment); } } static void Html_ParseTag(Html_Parser *parser) { int ch, sz; char buf[32]; Html_TagInfo *info; Html_Item *item = NULL; XawTextPosition offset = parser->offset - 1; switch (Html_Peek(parser)) { case '!': (void)Html_Get(parser); /* eat `!' */ if (Html_Peek(parser) == '-') { /* comment */ (void)Html_Get(parser); /* eat `-' */ if (Html_Peek(parser) == '-') { int count = 0; (void)Html_Get(parser); while ((ch = Html_Peek(parser)) != EOF) { if (ch == '>' && count >= 2) break; else if (ch == '-') ++count; else count = 0; (void)Html_Get(parser); } } } break; case '?': break; case '/': (void)Html_Get(parser); /* eat `/' */ sz = 0; while (isalnum(Html_Peek(parser)) && ((sz + 1) < sizeof(buf))) buf[sz++] = tolower(Html_Get(parser)); buf[sz] = '\0'; if ((info = Html_GetInfo(buf)) != NULL) { if (parser->head) { Html_Item *it = parser->head; while (it) { if (it->info == info) break; it = it->parent; } if (it) { if (it == parser->head) parser->head->end = offset; else { it->end = offset; do { parser->head->end = offset; parser->head = parser->head->parent; } while (parser->head != it); } if (parser->head->parent) parser->head = parser->head->parent; else parser->head = parser->item; } } } break; default: sz = 0; while (isalnum(Html_Peek(parser)) && ((sz + 1) < sizeof(buf))) buf[sz++] = tolower(Html_Get(parser)); buf[sz] = '\0'; if ((info = Html_GetInfo(buf)) != NULL) { if (info->end == False) { if (info->ident == Qli) parser->quark = Qli; if (!info->para) break; /* no more processing required */ } item = XtNew(Html_Item); item->info = info; item->ident = item->info->ident; item->combine = NULL; item->override = False; item->start = item->end = -1; if (info->ident == Qol) item->li = 1; else item->li = 0; item->parent = item->child = item->next = NULL; if (parser->item == NULL) parser->item = parser->head = item; else if (parser->head->end == -1) { if (parser->head->info != item->info || info->nest) { Html_Item *it = parser->head; /* first, see if we need to close a long list of tags */ if (info->ident == Qdd) { if (parser->head && parser->head->info->ident == Qdt) { parser->head->end = offset; parser->head = parser->head->parent; } } else if (info->ident == Qdt) { if (parser->head && parser->head->info->ident == Qdd) { parser->head->end = offset; parser->head = parser->head->parent; } } else if (!info->nest) { while (it) { if (it->info == info || it->info->end) break; it = it->parent; } if (it) { /* close the items */ while (parser->head != it) { if (parser->head->info->ident == Qpre) --parser->pre; parser->head->end = offset; parser->head = parser->head->parent; } } } /* add child item */ it = parser->head->child; item->parent = parser->head; if (it == NULL) parser->head->child = item; else { while (it->next) it = it->next; it->next = item; } parser->head = item; } else { /* close the `head' item and start a new one */ Html_Item *it; parser->head->end = offset; if (parser->head->parent) parser->head = parser->head->parent; else parser->head = parser->item; if ((it = parser->head->child) != NULL) { item->parent = parser->head; while (it->next) it = it->next; it->next = item; parser->head = item; } else { parser->head->child = item; parser->head = item; } } } else { /* this is not common, but handle it */ Html_Item *it = parser->item; while (it->next) it = it->next; it->next = item; parser->head = item; } if (info->parse_args) (info->parse_args)(parser, item); } break; } /* skip anything not processed */ while ((ch = Html_Peek(parser)) != '>' && ch != EOF) (void)Html_Get(parser); if (item && item->start == -1) item->start = parser->offset + 1; } /* tags */ static int Html_Parse2(Html_Parser *parser) { int ch; for (;;) { if ((ch = Html_Get(parser)) == '<') { parser->end = parser->offset - 1; Html_Commit(parser); parser->quark = Qhide; parser->start = parser->end; Html_ParseTag(parser); (void)Html_Get(parser); /* eat `>' */ parser->end = parser->offset; Html_Commit(parser); } else return (ch); } /*NOTREACHED*/ } /* entities */ static int Html_Parse1(Html_Parser *parser) { static XawTextBlock *entities[256]; static char chars[256]; int ch; for (;;) { if ((ch = Html_Parse2(parser)) == EOF) return (EOF); if (ch == '&') { unsigned char idx = '?'; char buf[32]; int sz = 0; /* the string comparisons need a big optmization! */ parser->end = parser->offset - 1; Html_Commit(parser); parser->start = parser->end; while ((ch = Html_Peek(parser)) != ';' && ch != EOF && !isspace(ch)) { ch = Html_Get(parser); if (sz + 1 < sizeof(buf)) buf[sz++] = ch; } buf[sz] = '\0'; if (ch == ';') (void)Html_Get(parser); if (sz == 0) idx = '&'; else if (strcasecmp(buf, "lt") == 0) idx = '<'; else if (strcasecmp(buf, "gt") == 0) idx = '>'; else if (strcasecmp(buf, "nbsp") == 0) idx = ' '; else if (strcasecmp(buf, "amp") == 0) idx = '&'; else if (strcasecmp(buf, "quot") == 0) idx = '"'; else if (*buf == '#') { if (sz == 1) idx = '#'; else { char *tmp; idx = strtol(buf + 1, &tmp, 10); if (*tmp) idx = '?'; } } else if (strcmp(buf + 1, "acute") == 0) { switch (*buf) { case 'a': idx = 0xe1; break; case 'e': idx = 0xe9; break; case 'i': idx = 0xed; break; case 'o': idx = 0xf3; break; case 'u': idx = 0xfa; break; case 'A': idx = 0xc1; break; case 'E': idx = 0xc9; break; case 'I': idx = 0xcd; break; case 'O': idx = 0xd3; break; case 'U': idx = 0xda; break; case 'y': idx = 0xfd; break; case 'Y': idx = 0xdd; break; } } else if (strcmp(buf + 1, "grave") == 0) { switch (*buf) { case 'a': idx = 0xe0; break; case 'e': idx = 0xe8; break; case 'i': idx = 0xec; break; case 'o': idx = 0xf2; break; case 'u': idx = 0xf9; break; case 'A': idx = 0xc0; break; case 'E': idx = 0xc8; break; case 'I': idx = 0xcc; break; case 'O': idx = 0xd2; break; case 'U': idx = 0xd9; break; } } else if (strcmp(buf + 1, "tilde") == 0) { switch (*buf) { case 'a': idx = 0xe3; break; case 'o': idx = 0xf5; break; case 'n': idx = 0xf1; break; case 'A': idx = 0xc3; break; case 'O': idx = 0xd5; break; case 'N': idx = 0xd1; break; } } else if (strcmp(buf + 1, "circ") == 0) { switch (*buf) { case 'a': idx = 0xe2; break; case 'e': idx = 0xea; break; case 'i': idx = 0xee; break; case 'o': idx = 0xf4; break; case 'u': idx = 0xfb; break; case 'A': idx = 0xc2; break; case 'E': idx = 0xca; break; case 'I': idx = 0xce; break; case 'O': idx = 0xd4; break; case 'U': idx = 0xdb; break; } } else if (strcmp(buf + 1, "uml") == 0) { switch (*buf) { case 'a': idx = 0xe4; break; case 'e': idx = 0xeb; break; case 'i': idx = 0xef; break; case 'o': idx = 0xf6; break; case 'u': idx = 0xfc; break; case 'A': idx = 0xc4; break; case 'E': idx = 0xcb; break; case 'I': idx = 0xfc; break; case 'O': idx = 0xd6; break; case 'U': idx = 0xdc; break; case 'y': idx = 0xff; break; } } else if (strcmp(buf + 1, "cedil") == 0) { switch (*buf) { case 'c': idx = 0xe7; break; case 'C': idx = 0xc7; break; } } else if (strcmp(buf + 1, "slash") == 0) { switch (*buf) { case 'o': idx = 0xf8; break; case 'O': idx = 0xd8; break; } } else if (strcmp(buf + 1, "ring") == 0) { switch (*buf) { case 'a': idx = 0xe5; break; case 'A': idx = 0xc5; break; } } else if (strcasecmp(buf, "iexcl") == 0) idx = 0xa1; else if (strcasecmp(buf, "cent") == 0) idx = 0xa2; else if (strcasecmp(buf, "pound") == 0) idx = 0xa3; else if (strcasecmp(buf, "curren") == 0) idx = 0xa4; else if (strcasecmp(buf, "yen") == 0) idx = 0xa5; else if (strcasecmp(buf, "brvbar") == 0) idx = 0xa6; else if (strcasecmp(buf, "sect") == 0) idx = 0xa7; else if (strcasecmp(buf, "uml") == 0) idx = 0xa8; else if (strcasecmp(buf, "copy") == 0) idx = 0xa9; else if (strcasecmp(buf, "ordf") == 0) idx = 0xaa; else if (strcasecmp(buf, "laquo") == 0) idx = 0xab; else if (strcasecmp(buf, "not") == 0) idx = 0xac; else if (strcasecmp(buf, "shy") == 0) idx = 0xad; else if (strcasecmp(buf, "reg") == 0) idx = 0xae; else if (strcasecmp(buf, "macr") == 0) idx = 0xaf; else if (strcasecmp(buf, "deg") == 0) idx = 0xb0; else if (strcasecmp(buf, "plusmn") == 0) idx = 0xb1; else if (strcasecmp(buf, "sup2") == 0) idx = 0xb2; else if (strcasecmp(buf, "sup3") == 0) idx = 0xb3; else if (strcasecmp(buf, "acute") == 0) idx = 0xb4; else if (strcasecmp(buf, "micro") == 0) idx = 0xb5; else if (strcasecmp(buf, "para") == 0) idx = 0xb6; else if (strcasecmp(buf, "middot") == 0) idx = 0xb7; else if (strcasecmp(buf, "cedil") == 0) idx = 0xb8; else if (strcasecmp(buf, "supl") == 0) idx = 0xb9; else if (strcasecmp(buf, "ordm") == 0) idx = 0xba; else if (strcasecmp(buf, "raquo") == 0) idx = 0xbb; else if (strcasecmp(buf, "frac14") == 0) idx = 0xbc; else if (strcasecmp(buf, "frac12") == 0) idx = 0xbd; else if (strcasecmp(buf, "frac34") == 0) idx = 0xbe; else if (strcasecmp(buf, "iquest") == 0) idx = 0xbf; else if (strcasecmp(buf, "AElig") == 0) idx = 0xc6; else if (strcasecmp(buf, "ETH") == 0) idx = 0xd0; else if (strcasecmp(buf, "THORN") == 0) idx = 0xde; else if (strcasecmp(buf, "szlig") == 0) idx = 0xdf; else if (strcasecmp(buf, "aelig") == 0) idx = 0xe6; else if (strcasecmp(buf, "eth") == 0) idx = 0xf0; else if (strcasecmp(buf, "thorn") == 0) idx = 0xfe; parser->quark = Qentity; if (entities[idx] == NULL) { entities[idx] = XtNew(XawTextBlock); entities[idx]->firstPos = 0; entities[idx]->length = 1; entities[idx]->ptr = chars + idx; entities[idx]->format = FMT8BIT; chars[idx] = idx; } parser->entity = entities[idx]; parser->end = parser->offset; Html_Commit(parser); parser->start = parser->end; } } /*NOTREACHED*/ } /************************************************************************/ /* FORMAT */ /************************************************************************/ static int Html_Put(Html_Parser *parser, int ch) { if (ch != '\r') { if (parser->replace.length % 4096 == 0) parser->replace.ptr = XtRealloc(parser->replace.ptr, parser->replace.length + 4096); parser->replace.ptr[parser->replace.length++] = ch; } return (ch); } static void Html_Puts(Html_Parser *parser, char *str) { int len = strlen(str); if (parser->replace.length % 4096 == 0 || parser->replace.length + len > parser->replace.length + (4096 - (parser->replace.length % 4096))) parser->replace.ptr = XtRealloc(parser->replace.ptr, parser->replace.length + 4096); memcpy(parser->replace.ptr + parser->replace.length, str, len); parser->replace.length += len; } static void Html_FormatTag(Html_Parser *parser) { int ch = 0, sz = 0; char buf[32]; Html_TagInfo *info = NULL; switch (Html_Peek(parser)) { case '!': Html_Put(parser, '<'); Html_Put(parser, Html_Get(parser)); /* eat `!' */ if (Html_Peek(parser) == '-') { /* comment */ Html_Put(parser, Html_Get(parser)); /* eat `-' */ if (Html_Peek(parser) == '-') { int count = 0; Html_Put(parser, Html_Get(parser)); while ((ch = Html_Peek(parser)) != EOF) { if (ch == '>' && count >= 2) break; else if (ch == '-') ++count; else count = 0; Html_Put(parser, Html_Get(parser)); } (void)Html_Get(parser); /* eat `>' */ Html_Put(parser, '>'); return; } } break; case '?': Html_Put(parser, '<'); break; case '/': (void)Html_Get(parser); /* eat `/' */ while (isalnum(Html_Peek(parser)) && ((sz + 1) < sizeof(buf))) buf[sz++] = ch = tolower(Html_Get(parser)); buf[sz] = '\0'; if ((info = Html_GetInfo(buf)) != NULL && info->adnl) { if (info->ident == Qpre && parser->pre) { if (--parser->pre == 0) parser->column = 0; } parser->quark = Qetag; parser->spc = True; if (info->ident == Qp) { while ((ch = Html_Peek(parser) != '>' && ch != EOF)) (void)Html_Get(parser); (void)Html_Get(parser); /* eat '>' */ return; } } else if (info) { if (info->ident == Qol || info->ident == Qul) { if (parser->list && --parser->list == 0 && parser->desc == 0) { parser->quark = Qetag; Html_Put(parser, '\n'); ++parser->adnl; parser->column = 0; } } else if (info->ident == Qdl) { if (parser->desc && --parser->desc == 0 && parser->list == 0) { parser->quark = Qetag; Html_Put(parser, '\n'); ++parser->adnl; parser->column = 0; } } } Html_Puts(parser, "adnl) { if (info->ident == Qpre) ++parser->pre; if (parser->quark != Qtag) { if (parser->adnl < 2) { Html_Puts(parser, parser->adnl ? pnl : nlpnl); parser->adnl = 2; parser->spc = True; parser->column = 0; } } parser->quark = Qtag; if (info->ident == Qp) { while ((ch = Html_Peek(parser) != '>' && ch != EOF)) (void)Html_Get(parser); (void)Html_Get(parser); /* eat '>' */ return; } } else if (info) { if (info->ident == Qol || info->ident == Qul) { if (++parser->list == 1 && !parser->desc) { if (parser->adnl < 2) { Html_Puts(parser, parser->adnl ? pnl : nlpnl); parser->adnl = 2; parser->column = 0; } } else if (parser->adnl == 0) { Html_Put(parser, '\n'); parser->adnl = 1; parser->column = 0; } parser->spc = True; } else if (info->ident == Qli) { if (parser->adnl == 0) { Html_Put(parser, '\n'); parser->adnl = 1; parser->column = 0; } } else if (info->ident == Qdl) { if (++parser->desc == 1 && !parser->list) { if (parser->adnl < 2) { Html_Puts(parser, parser->adnl ? pnl : nlpnl); parser->adnl = 2; parser->column = 0; } } else if (parser->adnl == 0) { Html_Put(parser, '\n'); parser->adnl = 1; parser->column = 0; } parser->spc = True; } else if (info->ident == Qdd) { if (parser->desc == 0) { if (parser->adnl < 2) { Html_Puts(parser, parser->adnl ? pnl : nlpnl); parser->adnl = 2; parser->column = 0; } } else if (parser->adnl == 0) { Html_Put(parser, '\n'); parser->adnl = 1; parser->column = 0; } parser->spc = True; } else if (info->ident == Qdt) { if (parser->adnl == 0) { Html_Put(parser, '\n'); parser->adnl = 1; parser->spc = True; parser->column = 0; } } } Html_Put(parser, '<'); Html_Puts(parser, buf); break; } sz = 0; while ((ch = Html_Peek(parser)) != '>' && ch != EOF) { if (isspace(ch)) { (void)Html_Get(parser); ++sz; continue; } else if (sz) { Html_Put(parser, ' '); sz = 0; } Html_Put(parser, Html_Get(parser)); } Html_Put(parser, Html_Get(parser)); /* eat `>' */ if (info && info->ident == Qbr) { ++parser->adnl; parser->spc = True; Html_Put(parser, '\n'); parser->quark = info->ident; parser->column = 0; } } /* tags */ static int Html_Format3(Html_Parser *parser) { int ch; for (;;) { if ((ch = Html_Get(parser)) == '<') { if (parser->quark == Qspace && parser->spc == False) { Html_Put(parser, ' '); parser->spc = True; } /* parser->quark = Qhide;*/ Html_FormatTag(parser); } else return (ch); } /*NOTREACHED*/ } /* entities */ static int Html_Format2(Html_Parser *parser) { int ch; for (ch = Html_Format3(parser); ch == '&'; ch = Html_Format3(parser)) { Html_Put(parser, '&'); while ((ch = Html_Peek(parser)) != ';') { if (isspace(ch) || ch == EOF) break; Html_Put(parser, Html_Get(parser)); } if (ch != EOF) Html_Put(parser, Html_Get(parser)); else break; if (parser->pre) ++parser->column; } return (ch); } /* spaces */ static int Html_Format1(Html_Parser *parser) { int ch; for (;;) { if ((ch = Html_Format2(parser)) == EOF) return (ch); if (parser->quark == Qetag) { if (parser->adnl < 2) { Html_Puts(parser, parser->adnl ? pnl : nlpnl); parser->adnl = 2; parser->spc = True; } } else if (parser->quark == Qspace && parser->spc == False) { Html_Put(parser, ' '); parser->spc = True; } if (!parser->pre && isspace(ch)) parser->quark = Qspace; else { if (parser->pre) { if (parser->spc) { /* did not yet see any non space character */ if (isspace(ch)) { if (ch == '\n') { parser->column = 0; parser->spc = False; parser->adnl = 1; } else if (ch == '\t') parser->column += 8 - (parser->column % 8); else ++parser->column; continue; } else { int column = parser->column; while (column-- > 0) Html_Put(parser, ' '); parser->spc = False; parser->adnl = 0; } } else if (ch == '\n') { ++parser->adnl; parser->column = 0; } else if (ch == '\t') { int column = parser->column + (8 - (parser->column % 8)); parser->adnl = 0; while (parser->column < column) { Html_Put(parser, ' '); ++parser->column; } continue; } else { parser->adnl = 0; ++parser->column; } } else parser->adnl = 0; Html_Put(parser, ch); parser->quark = Qdefault; parser->spc = False; } } } /************************************************************************/ /* ARGUMENTS */ /************************************************************************/ static void Html_AArgs(Html_Parser *parser, Html_Item *item) { int ch, sz; char buf[32]; /*CONSTCOND*/ while (True) { sz = 0; while ((ch = Html_Peek(parser)) != '>' && ch != EOF) { if (isalnum(ch)) break; else (void)Html_Get(parser); } if (ch == '>' || ch == EOF) return; buf[sz++] = tolower(Html_Get(parser)); while ((ch = Html_Peek(parser)) != '>' && ch != EOF) if (isalnum(ch)) buf[sz++] = tolower(Html_Get(parser)); else break; buf[sz] = '\0'; if (strcmp(buf, "href") == 0) { item->combine = XawTextSinkCopyProperty(XawTextGetSink(text), item->info->ident); item->override = True; item->combine->xlfd_mask = 0L; item->combine->mask = XAW_TPROP_UNDERLINE | XAW_TPROP_FOREGROUND; item->combine->foreground = parser->alink; return; } while ((ch = Html_Peek(parser)) != '>' && ch != EOF) { if (isspace(ch)) break; else (void)Html_Get(parser); } } } static void Html_FontArgs(Html_Parser *parser, Html_Item *item) { int ch, sz; char name[32], value[256], xlfd[128]; item->combine = XawTextSinkCopyProperty(XawTextGetSink(text), Qdefault); item->override = True; item->combine->mask = item->combine->xlfd_mask = 0L; /*CONSTCOND*/ while (True) { /* skip white spaces */ while ((ch = Html_Peek(parser)) != '>' && ch != EOF) { if (isalnum(ch)) break; else (void)Html_Get(parser); } if (ch == '>' || ch == EOF) return; /* read option name */ sz = 0; name[sz++] = tolower(Html_Get(parser)); while ((ch = Html_Peek(parser)) != '>' && ch != EOF) if (isalnum(ch) && (sz + 1 < sizeof(name))) name[sz++] = tolower(Html_Get(parser)); else break; name[sz] = '\0'; if (ch != '=') continue; (void)Html_Get(parser); /* skip `=' */ if (Html_Peek(parser) == '"') (void)Html_Get(parser); sz = 0; while ((ch = Html_Peek(parser)) != '>' && ch != EOF) { if (!isspace(ch) && (sz + 1 < sizeof(value))) value[sz++] = Html_Get(parser); else break; } value[sz] = '\0'; if (sz > 0 && value[sz - 1] == '"') value[--sz] = '\0'; if (strcmp(name, "color") == 0) { XColor color, exact; if (XAllocNamedColor(XtDisplay(toplevel), toplevel->core.colormap, value, &color, &exact)) { item->combine->mask |= XAW_TPROP_FOREGROUND; item->combine->foreground = color.pixel; } } else if (strcmp(name, "face") == 0) { int count = 0; char *ptr, *family, **font_list; ptr = value; do { family = ptr; ptr = strchr(ptr, ','); if (ptr) *ptr++ = '\0'; XmuSnprintf(xlfd, sizeof(xlfd), "-*-%s-*-*-*-*-*-*-*-*-*-*-*-*", family); font_list = XListFonts(XtDisplay(toplevel), xlfd, 1, &count); if (font_list) XFreeFontNames(font_list); if (count) break; } while (ptr); if (count) { item->combine->xlfd_mask |= XAW_TPROP_FAMILY; item->combine->family = XrmStringToQuark(family); } } else if (strcmp(name, "size") == 0) { int size, sign = 0; if (isalnum(*value)) { size = atoi(value); sign = 0; } else { char *str = XrmQuarkToString(item->combine->pixel_size); size = str ? atoi(str) : 12; if (*value == '+') { size += atoi(value + 1); sign = 1; } else if (*value == '-') { size -= atoi(value + 1); sign = -1; } } if (item->combine->xlfd != NULLQUARK) { int count, ucount, dcount, usize, dsize; char **current, **result, **up, **down; current = result = up = down = NULL; /* try to load an appropriate font */ XmuSnprintf(value, sizeof(value), "-*-%s-%s-%s-*--%%d-*-*-*-*-*-%s-%s", XrmQuarkToString(item->combine->family), XrmQuarkToString(item->combine->weight), XrmQuarkToString(item->combine->slant), XrmQuarkToString(item->combine->registry), XrmQuarkToString(item->combine->encoding)); XmuSnprintf(xlfd, sizeof(xlfd), value, atoi(XrmQuarkToString(item->combine->pixel_size))); current = XListFonts(XtDisplay(toplevel), xlfd, 1, &count); if (count) { ucount = dcount = usize = dsize = 0; XmuSnprintf(xlfd, sizeof(xlfd), value, size); result = XListFonts(XtDisplay(toplevel), xlfd, 1, &count); if (count == 0 || strstr(*result, "-0-")) { if (sign <= 0) { sz = dsize = size; while (dcount == 0 && --sz > size - 8 && sz > 1) { XmuSnprintf(xlfd, sizeof(xlfd), value, sz); down = XListFonts(XtDisplay(toplevel), xlfd, 1, &dcount); if (dcount && strstr(*down, "-0-") != NULL) { XFreeFontNames(down); down = NULL; dcount = 0; } } if (dcount) dsize = sz; } if (sign >= 0) { sz = usize = size; while (ucount == 0 && ++sz < size + 8) { XmuSnprintf(xlfd, sizeof(xlfd), value, sz); up = XListFonts(XtDisplay(toplevel), xlfd, 1, &ucount); if (ucount && strstr(*up, "-0-") != NULL) { XFreeFontNames(up); up = NULL; ucount = 0; } } if (ucount) usize = sz; } if (ucount && dcount) size = size - dsize < usize - size ? dsize : usize; else if (ucount) size = usize; else if (dcount) size = dsize; } if (current) XFreeFontNames(current); if (result) XFreeFontNames(result); if (up) XFreeFontNames(up); if (down) XFreeFontNames(down); } } XmuSnprintf(value, sizeof(value), "%d", size); item->combine->xlfd_mask |= XAW_TPROP_PIXELSIZE; item->combine->pixel_size = XrmStringToQuark(value); } while ((ch = Html_Peek(parser)) != '>' && ch != EOF) { if (isspace(ch)) break; else (void)Html_Get(parser); } } }