/* Copyright (C) 1991-2002, The Numerical Algorithms Group Ltd. All rights reserved. Copyright (C) 2007-2008, Gabriel Dos Reis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of The Numerical Algorithms Group Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _PARSE_C #include "debug.h" #include "sockio.h" #include "parse.h" #include "parse-paste.h" #include "parse-types.h" #include "lex.h" #include "hyper.h" #include "extent.h" #include "event.h" #include "display.h" #include "group.h" #include "scrollbar.h" #include "titlebar.h" #include "all_hyper_proto.H1" TextNode *curr_node; /* current node being parsed. It is to be the * next one filled */ HashTable *gLinkHashTable; /* the hash table of active link windows */ TextNode *cur_spadcom; /* The current AXIOM command */ short int gParserMode; /* Parser mode flag */ short int gParserRegion; /* Parser Region flag scrolling etc */ short int gStringValueOk; /* is a string or box value ok */ boolean gEndedPage; extern int example_number; /* sequence example number */ int ret_val; /* The return value from get_token */ HyperDocPage *cur_page; char *replace_page; /* true if dynamic page is link to static one */ /* * These routines are used for storing an restoring the parser mode. When * I start to parse from string, or from a macro, I need to restore the * parser mode and region once done. These routines do that * */ typedef struct mr_stack { /** The structure for storing parser mode and region **/ short int fParserMode; short int fParserRegion; struct mr_stack *fNext; } MR_Stack; MR_Stack *top_mr_stack = NULL; /** Declaration for the stack **/ static void Push_MR(void) { MR_Stack *newStackItem = (MR_Stack *) halloc(sizeof(MR_Stack), "Mode Region Stack"); newStackItem->fParserMode = gParserMode; newStackItem->fParserRegion = gParserRegion; newStackItem->fNext = top_mr_stack; top_mr_stack = newStackItem; } static void Pop_MR(void) { MR_Stack *old = top_mr_stack; if (old == NULL) { fprintf(stderr, "(HyperDoc) Parser Error: Tried to pop empty MR Stack\n"); exit(-1); } else { gParserMode = old->fParserMode; gParserRegion = old->fParserRegion; top_mr_stack = old->fNext; free(old); } } void load_page(HyperDocPage *page) { if (page->type == UnloadedPageType) { HyperDocPage *new_page; init_scanner(); new_page = format_page((UnloadedPage *)page); gWindow->page = new_page; /* free(page); */ page = new_page; } } HyperDocPage *formatpage; /* Display a HyperDoc page with the given name, parsing it if needed */ void display_page(HyperDocPage *page) { HyperDocPage *new_page; XUnmapSubwindows(gXDisplay, gWindow->fMainWindow); XUnmapSubwindows(gXDisplay, gWindow->fScrollWindow); XFlush(gXDisplay); if (setjmp(jmpbuf)) { /* * since I did not finish formatting the page, let me get rid of what * I had */ free_page(formatpage); /* Replace the buggy page with what I started with */ hash_replace(gWindow->fPageHashTable, (char *)page, formatpage->name); if (!strcmp(formatpage->name, "ErrorPage")) { fprintf(stderr, "(HyperDoc) Oops the error page is buggy\n"); exit(-1); } gWindow->page = page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "ErrorPage"); if (page == NULL) { fprintf(stderr, "(HyperDoc) No error page found, exiting\n"); exit(-1); } reset_connection(); } if (page->type == UnloadedPageType || page->type == ErrorPage) { /* Gack! (page should be a union!) */ init_scanner(); new_page = format_page((UnloadedPage *)page); gWindow->page = new_page; /* free(page); */ page = new_page; } show_page(page); } /* Parse a given HyperDoc Page, from the top */ static HyperDocPage * format_page(UnloadedPage *ulpage) { /*int ret_val;*/ HyperDocPage *page = alloc_page(ulpage->name); /* * In case of an error I will have to get at this page so I can free the * waisted memory */ formatpage = page; page->type = Normal; hash_replace(gWindow->fPageHashTable, (char *)page, ulpage->name); cfile = find_fp(ulpage->fpos); page->filename = alloc_string(ulpage->fpos.name); parse_page(page); return page; } /* parse the HyperDoc statements in the given string */ void parse_from_string(char *str) { save_scanner_state(); last_ch = NoChar; last_token = 0; input_string = str; input_type = FromString; parse_HyperDoc(); restore_scanner_state(); } static void parse_title(HyperDocPage *page) { TextNode *node; Push_MR(); gParserRegion = Title; get_expected_token(Lbrace); node = alloc_node(); page->title = node; node->type = Titlenode; node->next = alloc_node(); node = node->next; node->type = Center; node->next = alloc_node(); curr_node = node->next; parse_HyperDoc(); curr_node->type = Endcenter; curr_node->next = alloc_node(); curr_node = curr_node->next; curr_node->type = Endtitle; curr_node->next = NULL; if (gNeedIconName) { char *title = print_to_string(page->title); XSetIconName(gXDisplay, gWindow->fMainWindow, title); gNeedIconName = 0; } if (token.type != Rbrace) { fprintf(stderr, "(HyperDoc) Parse title was expecting a closing brace\n"); print_page_and_filename(); jump(); } linkTitleBarWindows(); Pop_MR(); } static void parse_header(HyperDocPage *page) { TextNode *node; Push_MR(); gParserRegion = Header; node = alloc_node(); page->header = node; node->type = Headernode; node->next = alloc_node(); curr_node = node->next; parse_HyperDoc(); } /* * parse a page from the top level */ static void init_parse_page(HyperDocPage *page) { gEndedPage = gInDesc = gStringValueOk = gInIf = gInButton = gInOptional = gInVerbatim = gInPaste = gInItems = gInSpadsrc = FALSE; example_number = 1; cur_page = page; gParserMode = AllMode; /* Now I should set the input list to be null */ free_input_list(page->input_list); page->input_list = page->current_item = NULL; init_top_group(); clear_be_stack(); cur_spadcom = NULL; gLinkHashTable = page->fLinkHashTable; hash_init( gLinkHashTable, LinkHashSize, (EqualFunction) window_equal, (HashcodeFunction) window_code); gPageBeingParsed = page; } void init_parse_patch(HyperDocPage *page) { gEndedPage = gInDesc = gStringValueOk = gInIf = gInButton = gInOptional = gInVerbatim = gInPaste = gInItems = gInSpadsrc = FALSE; gParserMode = AllMode; gParserRegion = Scrolling; init_top_group(); clear_be_stack(); cur_spadcom = NULL; gLinkHashTable = page->fLinkHashTable; gPageBeingParsed = page; } #define end_page(t) ((t == Page || t == NewCommand ||t == Endpage)?1:0) static void parse_page(HyperDocPage *page) { init_parse_page(page); /* Get the name of the page */ get_expected_token(Page); get_expected_token(Lbrace); get_expected_token(Word); if (page->name == NULL) page->name = alloc_string(token.id); get_expected_token(Rbrace); /* parse the title */ gWindow->fDisplayedWindow = gWindow->fMainWindow; parse_title(page); /* * Now start parsing the header region */ parse_header(page); } char *ExpectedBeginScroll = "Parser Error: Unexpected new page, expecting a begin scroll\n", *ExpectedEndScroll = "Parser Error: Unexpected new page, expected an end scroll\n"; /* * The general HyperDoc parsing function. expects to see anything. This * function will parse until it sees either: 1) A new page starting 2) An end * of file 3) a closing bracket "}" */ void parse_HyperDoc(void) { TextNode *node = NULL /*, *save_node = NULL, *arg_node = NULL*/ ; for(;;) { ret_val = get_token(); if (ret_val == EOF) return; switch (token.type) { case Spadsrc: parse_spadsrc(curr_node); break; case Helppage: parse_help(); break; case Endpatch: case Endpaste: case Rbrace: return; case Paste: parse_paste(); break; case Pastebutton: parse_pastebutton(); break; case Endpage: case NewCommand: case Page: end_a_page(); return; case EndScroll: token.type = Endscroll; case Endscroll: start_footer(); break; case Beginscroll: start_scrolling(); break; case Thispage: /* it really is just a word */ curr_node->type = Word; curr_node->data.text = alloc_string(gPageBeingParsed->name); break; case Icorrection: node->type = Noop; break; case Newcond: parse_newcond(); break; case Setcond: parse_setcond(); break; case Dollar: parse_verbatim(Math); break; case Verbatim: parse_verbatim(Verbatim); break; case Ifcond: parse_ifcond(); break; case Fi: if (gInIf) return; else { curr_node->type = Noop; /* Oops I had a problem parsing this puppy */ fprintf(stderr, "(HyperDoc) \\fi found without macthing if?\n"); longjmp(jmpbuf, 1); fprintf(stderr, "(HyperDoc) Longjmp failed -- Exiting \n"); exit(-1); } case Else: if (gInIf) return; else { /* Oops I had a problem parsing this puppy */ curr_node->type = Noop; fprintf(stderr, "(HyperDoc) \\else found without macthing if?\n"); longjmp(jmpbuf, 1); fprintf(stderr, "(HyperDoc) Longjmp failed -- Exiting \n"); exit(-1); } case Macro: parse_macro(); break; case Env: /** In this case, get the environment value, and make it a word **/ parse_env(curr_node); break; case WindowId: curr_node->type = WindowId; curr_node->space = token.id[-1]; curr_node->data.text = window_id(gWindow->fMainWindow); break; case Punctuation: case Word: case Lsquarebrace: case Dash: curr_node->type = token.type; curr_node->space = token.id[-1]; curr_node->data.text = alloc_string(token.id); break; case Pagename: { char *str; curr_node->type = Word; curr_node->space = 0; str = halloc(strlen(cur_page->name) + 1, "parse"); sprintf(str, "%s", cur_page->name); curr_node->data.text = alloc_string(str); break; } case Examplenumber: { char *str; curr_node->type = Word; curr_node->space = 0; str = halloc(5, "parse"); sprintf(str, "%d", example_number); curr_node->data.text = alloc_string(str); break; } case Rsquarebrace: if (gInOptional) return; else { curr_node->type = token.type; curr_node->space = token.id[-1]; curr_node->data.text = alloc_string(token.id); } break; case EndTitems: token.type = Endtitems; case Endtitems: if (gParserMode != AllMode) { curr_node->type = Noop; fprintf(stderr, "(HyperDoc) Found a bad token %s\n", token_table[token.type]); longjmp(jmpbuf, 1); } else { curr_node->type = token.type; break; } case EndItems: token.type = Enditems; case Enditems: gInItems--; case Horizontalline: case Par: case Newline: case Titem: if (gParserMode != AllMode) { curr_node->type = Noop; fprintf(stderr, "(HyperDoc) Found a bad token %s\n", token_table[token.type]); longjmp(jmpbuf, 1); } else { curr_node->type = token.type; break; } case Begintitems: case Beginitems: if (gParserMode != AllMode) { curr_node->type = Noop; fprintf(stderr, "(HyperDoc) Found a bad token %s\n", token_table[token.type]); longjmp(jmpbuf, 1); } else { parse_begin_items(); break; } case Item: parse_item(); break; case Mitem: parse_mitem(); break; case VSpace: case Tab: case HSpace: case Indent: case Indentrel: parse_value1(); break; case Space: parse_value2(); break; case Lbrace: curr_node->type = Group; curr_node->space = token.id[-1]; push_group_stack(); node = alloc_node(); curr_node->next = node; curr_node = curr_node->next; parse_HyperDoc(); curr_node->type = Endgroup; pop_group_stack(); break; case Upbutton: case Returnbutton: case Link: case Downlink: case Memolink: case Windowlink: parse_button(); break; case Unixlink: case LispMemoLink: case LispDownLink: case Lisplink: case Lispcommand: case Lispcommandquit: case Spadlink: case Spaddownlink: case Spadmemolink: case Unixcommand: case Spadcall: case Spadcallquit: case Qspadcall: case Qspadcallquit: case Lispwindowlink: parse_command(); break; case Controlbitmap: case Inputbitmap: case Inputpixmap: case Inputimage: parse_input_pix(); break; case Box: parse_box(); break; case Mbox: parse_mbox(); break; case Free: parse_free(); break; case Center: parse_centerline(); break; case Bound: add_dependencies(); break; case Spadcommand: case Spadgraph: parse_spadcommand(curr_node); break; case Table: parse_table(); break; case Beep: case Emphasize: case BoldFace: case Rm: case It: case Tt: case Sl: curr_node->type = token.type; curr_node->space = token.id[-1]; break; case Inputstring: parse_inputstring(); break; case SimpleBox: parse_simplebox(); break; case BoxValue: case StringValue: if (!gStringValueOk) { strcpy(ebuffer,"(HyperDoc): Unexpected Value Command:"); strcat(ebuffer, token.id); parser_error(ebuffer); curr_node->type = Noop; longjmp(jmpbuf, 1); } curr_node->type = token.type; curr_node->space = token.id[-1]; get_expected_token(Lbrace); get_expected_token(Word); curr_node->data.text = alloc_string(token.id); get_expected_token(Rbrace); break; case NoLines: gPageBeingParsed->page_flags |= NOLINES; break; case Pound: curr_node->type = Pound; curr_node->space = token.id[-1]; curr_node->next = alloc_node(); curr_node = curr_node->next; parse_parameters(); break; case Radiobox: parse_radiobox(); break; case Radioboxes: parse_radioboxes(); break; case Replacepage: parse_replacepage(); break; default: fprintf(stderr, "(HyperDoc) Keyword not currently supported: %s\n", token.id); print_page_and_filename(); curr_node->type = Noop; break; } if (gEndedPage) return; if (curr_node->type != Noop) { node = alloc_node(); curr_node->next = node; curr_node = node; } } } /* parse a page from a socket source */ HyperDocPage * parse_page_from_socket(void) { HyperDocPage *page = alloc_page((char *) NULL); HyperDocPage *hpage; init_scanner(); input_type = FromSpadSocket; input_string = ""; cur_spadcom = NULL; gLinkHashTable = page->fLinkHashTable; hash_init( gLinkHashTable, LinkHashSize, (EqualFunction) window_equal, (HashcodeFunction) window_code); gPageBeingParsed = page; replace_page = NULL; if (setjmp(jmpbuf)) { /* Ooops, somewhere I had an error */ free_page(page); page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "ErrorPage"); reset_connection(); } else { parse_page(page); page->type = SpadGen; page->filename = NULL; /* just for kicks, let me add this thing to the hash file */ hpage = (HyperDocPage *) hash_find(gWindow->fPageHashTable, page->name); if (hpage) hash_replace(gWindow->fPageHashTable, (char *)page, page->name); else { hash_insert(gWindow->fPageHashTable, (char *)page, page->name); } } if (replace_page != NULL) { free_page(page); page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, replace_page); if (page == NULL) fprintf(stderr, "(HyperDoc) Unknown page: %s\n", replace_page); } return page; } HyperDocPage * parse_page_from_unixfd(void) { HyperDocPage *page = alloc_page((char *) NULL); init_scanner(); input_type = FromUnixFD; cur_spadcom = NULL; gLinkHashTable = page->fLinkHashTable; hash_init( gLinkHashTable, LinkHashSize, (EqualFunction) window_equal, (HashcodeFunction) window_code); gPageBeingParsed = page; if (setjmp(jmpbuf)) { /* Ooops, somewhere I had an error */ free_page(page); page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "ErrorPage"); reset_connection(); } else { parse_page(page); page->type = Unixfd; page->filename = NULL; } return page; } static void start_scrolling(void) { /* * if I am here than I had a begin scroll. This means I should end the * header, and then start parsing the footer */ if (gParserRegion != Header) { curr_node->type = Noop; fprintf(stderr, "(HyperDoc) Parser Error: Unexpected BeginScrollFound\n"); longjmp(jmpbuf, 1); fprintf(stderr, "(HyperDoc) Longjump failed exiting\n"); } curr_node->type = Endheader; curr_node->next = NULL; Pop_MR(); Push_MR(); gParserRegion = Scrolling; gWindow->fDisplayedWindow = gWindow->fScrollWindow; curr_node = alloc_node(); gPageBeingParsed->scrolling = curr_node; curr_node->type = Scrollingnode; } static void start_footer(void) { /* * This ends the parsing of the scrolling region, and then starts to * parse the footer */ if (gParserRegion != Scrolling) { curr_node->type = Noop; fprintf(stderr, "(HyperDoc) Parser Error: Unexpected Endscroll Found\n"); print_page_and_filename(); longjmp(jmpbuf, 1); fprintf(stderr, "(HyperDoc) Longjump failed exiting\n"); } curr_node->type = Endscrolling; curr_node->next = NULL; Pop_MR(); linkScrollBars(); Push_MR(); gParserRegion = Footer; curr_node = alloc_node(); curr_node->type = Footernode; gPageBeingParsed->footer = curr_node; gWindow->fDisplayedWindow = gWindow->fMainWindow; } static void end_a_page(void) { if (gParserRegion == Scrolling) { fprintf(stderr, "%s\n", "(HyperDoc) end_a_page: Unexpected End of Page occurred \ inside a \beginscroll"); print_page_and_filename(); jump(); } gEndedPage = TRUE; if (gParserRegion == Footer) { /* the person had all the regions, I basically just have to leave */ curr_node->type = Endscrolling; curr_node->next = NULL; Pop_MR(); } else if (gParserRegion == Header) { /* person had a header. So just end it and return */ curr_node->type = Endheader; curr_node->next = NULL; Pop_MR(); gPageBeingParsed->scrolling = NULL; gPageBeingParsed->footer = NULL; } } static void parse_replacepage(void) { get_expected_token(Lbrace); get_token(); replace_page = alloc_string(token.id); get_expected_token(Rbrace); }