AmendHub

Download:

jcs

/

wikipedia

/

amendments

/

13

browser: Add live search drop-down list

Move request buffers into the http_request struct so each request
automatically gets them.

jcs made amendment 13 about 1 year ago
--- browser.c Wed Aug 24 20:50:52 2022 +++ browser.c Thu Aug 25 14:14:13 2022 @@ -35,6 +35,8 @@ void browser_mouse_down(struct focusable *focusable, E bool browser_handle_menu(struct focusable *focusable, short menu, short item); void browser_atexit(struct focusable *focusable); +void browser_live_search(struct browser *browser); +void browser_hide_search_results(struct browser *browser); Pattern fill_pattern; @@ -44,57 +46,41 @@ browser_idle(struct focusable *focusable, EventRecord struct browser *browser = (struct browser *)focusable->cookie; size_t len; - TEIdle(browser->input_te); - - if (browser->wpr) - wikipedia_request_process(browser->wpr); - -#if 0 switch (browser->state) { case BROWSER_STATE_IDLE: - if (browser->need_refresh) { - browser->need_refresh = false; - browser->state = BROWSER_STATE_UPDATE_AMENDMENT_LIST; + TEIdle(browser->input_te); + + if (browser->last_input_for_search != 0 && + (Ticks - browser->last_input_for_search > 30)) { + browser->last_input_for_search = 0; + browser_live_search(browser); } break; - case BROWSER_STATE_ADD_FILE: - if (repo_add_file(browser->repo) == NULL) + case BROWSER_STATE_DO_SEARCH: { + TERec *te; + char *input; + + HLock(browser->input_te); + te = *(browser->input_te); + HLock(te->hText); + (*(te->hText))[te->teLength] = '\0'; + input = xstrdup(*(te->hText), "browser te input"); + HUnlock(te->hText); + HUnlock(browser->input_te); + browser->wpr = wikipedia_fetch_article(browser, input); + xfree(&input); + browser->state = BROWSER_STATE_PROCESS_SEARCH; + break; + } + case BROWSER_STATE_PROCESS_SEARCH: + if (browser->wpr == NULL) { browser->state = BROWSER_STATE_IDLE; - else - browser->state = BROWSER_STATE_UPDATE_FILE_LIST; + break; + } + + wikipedia_request_process(browser->wpr); break; - case BROWSER_STATE_UPDATE_FILE_LIST: - browser_add_files(browser); - browser->state = BROWSER_STATE_IDLE; - break; - case BROWSER_STATE_UPDATE_AMENDMENT_LIST: - browser_filter_amendments(browser); - browser->state = BROWSER_STATE_IDLE; - break; - case BROWSER_STATE_OPEN_COMMITTER: - committer_init(browser); - browser->state = BROWSER_STATE_WAITING_FOR_COMMITTER; - break; - case BROWSER_STATE_WAITING_FOR_COMMITTER: - break; - case BROWSER_STATE_DISCARD_CHANGES: - browser_discard_changes(browser); - browser->state = BROWSER_STATE_IDLE; - break; - case BROWSER_STATE_EXPORT_PATCH: - browser_export_patch(browser); - browser->state = BROWSER_STATE_IDLE; - break; - case BROWSER_STATE_APPLY_PATCH: - browser_apply_patch(browser); - browser->state = BROWSER_STATE_IDLE; - break; - case BROWSER_STATE_EDIT_AMENDMENT: - browser_edit_amendment(browser); - browser->state = BROWSER_STATE_IDLE; - break; } -#endif } struct browser * @@ -107,7 +93,7 @@ browser_init(void) Rect data_bounds = { 0, 0, 0, 1 }; /* tlbr */ Point cell_size = { 0, 0 }; Cell cell = { 0 }; - short n; + short n, width, height; browser = xmalloczero(sizeof(struct browser), "browser"); browser->state = BROWSER_STATE_IDLE; @@ -115,11 +101,14 @@ browser_init(void) GetIndPattern(&fill_pattern, sysPatListID, 22); /* main window */ - bounds.left = (PADDING / 2); - bounds.top = screenBits.bounds.top + (GetMBarHeight() * 2) - 1 + - (PADDING / 2); - bounds.right = screenBits.bounds.right - 1 - (PADDING / 2); - bounds.bottom = screenBits.bounds.bottom - 1 - (PADDING / 2); + width = screenBits.bounds.right - screenBits.bounds.left - PADDING; + if (width > 620) + width = 620; + height = screenBits.bounds.bottom - screenBits.bounds.top - + PADDING - (GetMBarHeight() * 2); + if (height > 340) + height = 340; + center_in_screen(width, height, true, &bounds); snprintf(title, sizeof(title), "%s", PROGRAM_NAME); CtoPstr(title); @@ -129,6 +118,7 @@ browser_init(void) err(1, "Can't create window"); SetPort(browser->win); + /* search input TE */ bounds.top = PADDING; bounds.left = PADDING; bounds.right = browser->win->portRect.right - PADDING; @@ -141,7 +131,8 @@ browser_init(void) TEAutoView(true, browser->input_te); TEActivate(browser->input_te); - bounds.top = bounds.bottom + PADDING; + /* main article TE */ + bounds.top = (*(browser->input_te))->viewRect.bottom + PADDING; bounds.left = PADDING; bounds.right = browser->win->portRect.right - SCROLLBAR_WIDTH - PADDING; bounds.bottom = browser->win->portRect.bottom - PADDING; @@ -191,7 +182,7 @@ browser_close(struct focusable *focusable) TEDispose(browser->te); DisposeWindow(browser->win); - free(browser); + xfree(&browser); return true; } @@ -262,6 +253,14 @@ browser_update(struct focusable *focusable, EventRecor InsetRect(&r, -1, -1); FrameRect(&r); + if (browser->search_results != NULL) { + r = (*(browser->search_results))->rView; + InsetRect(&r, -1, -1); + FillRect(&r, white); + FrameRect(&r); + LUpdate(browser->win->visRgn, browser->search_results); + } + UpdtControl(browser->win, browser->win->visRgn); browser_update_menu(browser); @@ -273,13 +272,13 @@ browser_update(struct focusable *focusable, EventRecor void browser_mouse_down(struct focusable *focusable, EventRecord *event) { - Cell selected = { 0 }, now = { 0 }; + struct browser *browser = (struct browser *)focusable->cookie; + char str[255]; + Cell selected = { 0 }; Point p; ControlHandle control; Rect r; - struct browser *browser = (struct browser *)focusable->cookie; - short val, adj, page, was_selected, part, i, data_len; - char *path; + short val, adj, page, i, len, part; p = event->where; GlobalToLocal(&p); @@ -291,6 +290,25 @@ browser_mouse_down(struct focusable *focusable, EventR return; } + if (browser->search_results != NULL) { + r = (*(browser->search_results))->rView; + r.right += SCROLLBAR_WIDTH; + if (PtInRect(p, &r)) { + LClick(p, event->modifiers, browser->search_results); + selected.v = 0; + if (LGetSelect(true, &selected, browser->search_results)) { + len = sizeof(str); + LGetCell(&str, &len, selected, browser->search_results); + TESetText(str, len, browser->input_te); + InvalRect(&(*(browser->input_te))->viewRect); + browser_hide_search_results(browser); + browser->state = BROWSER_STATE_DO_SEARCH; + } + + return; + } + } + r = (*(browser->te))->viewRect; if (PtInRect(p, &r)) { TEClick(p, ((event->modifiers & shiftKey) != 0), browser->te); @@ -331,26 +349,96 @@ void browser_key_down(struct focusable *focusable, EventRecord *event) { struct browser *browser = (struct browser *)(focusable->cookie); - TERec *te; - char *input, k; - + char k; + k = (event->message & charCodeMask); if (k == '\r') { - HLock(browser->input_te); - te = *(browser->input_te); - HLock(te->hText); - (*(te->hText))[te->teLength] = '\0'; - input = xstrdup(*(te->hText), "browser te input"); - HUnlock(te->hText); - HUnlock(browser->input_te); - browser->wpr = wikipedia_fetch_article(browser, input); - free(input); + browser->state = BROWSER_STATE_DO_SEARCH; } else { TEKey(k, browser->input_te); TESelView(browser->input_te); + browser->last_input_for_search = Ticks; } } +void +browser_live_search(struct browser *browser) +{ + TERec *te; + Rect bounds = { 0 }; + Rect data_bounds = { 0, 0, 0, 1 }; /* tlbr */ + char *input, **results, k; + size_t nresults, n; + Point cell_size = { 0, 0 }; + Cell cell = { 0, 0 }; + Rect r; + + HLock(browser->input_te); + te = *(browser->input_te); + + if (te->teLength == 0) { + HUnlock(browser->input_te); + browser_hide_search_results(browser); + return; + } + + SetCursor(*(GetCursor(watchCursor))); + + HLock(te->hText); + (*(te->hText))[te->teLength] = '\0'; + input = xstrdup(*(te->hText), "browser te input"); + HUnlock(te->hText); + HUnlock(browser->input_te); + nresults = wikipedia_fetch_search_results(browser, input, &results); + xfree(&input); + + bounds.top = (*(browser->input_te))->viewRect.bottom + 1; + bounds.left = PADDING; + bounds.bottom = bounds.top + 70; + bounds.right = bounds.left + 200; + + browser->search_results = LNew(&bounds, &data_bounds, cell_size, 0, + browser->win, false, false, false, true); + if (!browser->search_results) + panic("LNew failed"); + (*(browser->search_results))->selFlags = lOnlyOne; + LAddColumn(1, 0, browser->search_results); + + for (n = 0; n < nresults; n++) { + LAddRow(1, cell.v, browser->search_results); + LSetCell(results[n], strlen(results[n]), cell, + browser->search_results); + cell.v++; + xfree(&results[n]); + } + + r = (*(browser->search_results))->rView; + FillRect(&r, white); + InsetRect(&r, -1, -1); + FrameRect(&r); + LDoDraw(true, browser->search_results); + LUpdate(browser->win->visRgn, browser->search_results); + + SetCursor(&arrow); +} + +void +browser_hide_search_results(struct browser *browser) +{ + Rect r; + + if (browser->search_results == NULL) + return; + + r = (*(browser->search_results))->rView; + r.right += SCROLLBAR_WIDTH; + InsetRect(&r, -1, -1); + InvalRect(&r); + + LDispose(browser->search_results); + browser->search_results = NULL; +} + bool browser_handle_menu(struct focusable *focusable, short menu, short item) { @@ -483,9 +571,9 @@ no_overflow: progress(NULL); TEStylInsert(str, len, scrp_rec_h, browser->te); - TEPinScroll(0, -INT_MAX, browser->te); - SetCtlValue(browser->te_scroller, GetCtlMax(browser->te_scroller)); + SetCtlValue(browser->te_scroller, GetCtlMin(browser->te_scroller)); UpdateScrollbarForTE(browser->te_scroller, browser->te, false); + UpdtControl(browser->win, browser->win->visRgn); HUnlock(browser->te); last_style = style; --- browser.h Wed Aug 24 17:30:16 2022 +++ browser.h Thu Aug 25 12:19:52 2022 @@ -21,7 +21,9 @@ #include "http.h" enum { - BROWSER_STATE_IDLE + BROWSER_STATE_IDLE, + BROWSER_STATE_DO_SEARCH, + BROWSER_STATE_PROCESS_SEARCH }; #define STYLE_BOLD (1 << 0) @@ -37,8 +39,10 @@ struct browser { short state; WindowPtr win; TEHandle input_te; + unsigned long last_input_for_search; TEHandle te; ControlHandle te_scroller; + ListHandle search_results; struct wikipedia_request *wpr; }; --- http.c Wed Aug 24 13:19:56 2022 +++ http.c Thu Aug 25 11:06:19 2022 @@ -91,6 +91,42 @@ cleanup: return url; } +char * +url_encode(char *str) +{ + char *ret = NULL; + size_t len, n; + bool encode = false; + char a, b; + +encode: + for (n = 0, len = 0; str[n] != '\0'; n++) { + if ((str[n] >= 'A' && str[n] <= 'Z') || + (str[n] >= 'a' && str[n] <= 'z') || + (str[n] >= '0' && str[n] <= '9') || + (str[n] == '-' || str[n] == '_' || str[n] == '.' || + str[n] == '~')) { + if (ret) + ret[len] = str[n]; + len++; + } else { + if (ret) { + sprintf(ret + len, "%%%02X", str[n]); + } + len += 3; + } + } + + if (ret) { + ret[len] = '\0'; + return ret; + } + + ret = xmalloc(len + 1, "url_encode"); + len = 0; + goto encode; +} + struct http_request * http_get(const char *surl) { @@ -201,6 +237,75 @@ http_req_read(struct http_request *req, char *data, si return rlen; } +bool +http_req_skip_header(struct http_request *req) +{ + size_t len, n; + + for (;;) { + if (req->chunk_len > 3) { + /* + * Leave last 3 bytes of previous read in case \r\n\r\n happens + * across reads. + */ + memmove(req->chunk, req->chunk + req->chunk_len - 3, + req->chunk_len - 3); + req->chunk_len = 3; + } + len = http_req_read(req, req->chunk + req->chunk_len, + sizeof(req->chunk) - req->chunk_len); + if (len < 0) + return false; + if (len == 0) + continue; + req->chunk_len += len; + + for (n = 3; n < req->chunk_len; n++) { + if (req->chunk[n - 3] != '\r' || req->chunk[n - 2] != '\n' || + req->chunk[n - 1] != '\r' || req->chunk[n] != '\n') + continue; + + req->chunk_len -= n + 1; + memmove(req->chunk, req->chunk + n + 1, req->chunk_len); + req->chunk_off = 0; + return true; + } + } + + return false; +} + +short +http_req_chunk_peek(void *cookie) +{ + struct http_request *req = (struct http_request *)cookie; + + if (req->chunk_len == 0 || (req->chunk_off + 1 > req->chunk_len)) { + req->chunk_len = http_req_read(req, req->chunk, sizeof(req->chunk)); + req->chunk_off = 0; + } + + if (req->chunk_len == 0 || (req->chunk_off + 1 > req->chunk_len)) + return EOF; + + return req->chunk[req->chunk_off]; +} + +short +http_req_chunk_read(void *cookie) +{ + struct http_request *req = (struct http_request *)cookie; + short c; + + c = http_req_chunk_peek(req); + if (c == EOF) + return c; + + req->chunk_off++; + + return c; +} + void http_req_free(void *reqptr) { @@ -213,9 +318,9 @@ http_req_free(void *reqptr) _TCPRelease(&req->tcp_iopb, req->tcp_stream, nil, nil, false); - if (req->message != NULL) - xfree(&req->message); - xfree(&req->tcp_buf); - xfree(&req->url); - xfree(reqptr); +// if (req->message != NULL) +// xfree(&req->message); +// xfree(&req->tcp_buf); +// xfree(&req->url); +// xfree(reqptr); } --- http.h Fri Aug 19 14:08:56 2022 +++ http.h Wed Aug 24 22:17:37 2022 @@ -39,12 +39,20 @@ struct http_request { TCPStatusPB tcp_status_pb; char *message; + + char chunk[1024]; + size_t chunk_len; + size_t chunk_off; }; struct url * url_parse(const char *str); +char * url_encode(char *str); struct http_request * http_get(const char *url); ssize_t http_req_read(struct http_request *req, char *data, size_t len); +bool http_req_skip_header(struct http_request *req); +short http_req_chunk_peek(void *req); +short http_req_chunk_read(void *req); void http_req_free(void *reqptr); #endif --- wikipedia.c Wed Aug 24 17:56:32 2022 +++ wikipedia.c Thu Aug 25 14:13:25 2022 @@ -26,7 +26,10 @@ /* en.wikipedia.org doesn't support non-TLS :( */ #define WIKIPEDIA_HOST "wikipedia.jcs.org" +/* {"query":{"normalized":[{"to": */ #define NORMALIZED_TITLE_CONTEXT "{\"query\":{\"normalized\":[{\"to\":" + +/* {"query":{"pages":[{"revisions":[{"slots":{"main":{"content": */ #define ARTICLE_TEXT_CONTEXT "{\"query\":{\"pages\":[{\"revisions\":[{\"slots\":{\"main\":{\"content\":" short wikipedia_json_peek(void *cookie); @@ -37,11 +40,10 @@ wikipedia_fetch_article(struct browser *browser, char { static char url[256]; struct wikipedia_request *wpr; - struct http_request *req; short state; char *c; - /* XXX */ + /* "Macintosh Plus" -> "Macintosh_Plus" */ for (c = name; *c != '\0'; c++) { if (*c == ' ') *c = '_'; @@ -54,15 +56,71 @@ wikipedia_fetch_article(struct browser *browser, char progress("Contacting Wikipedia..."); snprintf(url, sizeof(url), "http://%s/w/api.php?action=query&" - "prop=revisions&rvslots=*&rvprop=content&formatversion=2&" - "format=json&titles=%s", WIKIPEDIA_HOST, name); + "prop=revisions&rvslots=*&rvprop=content&" + "format=json&formatversion=2&titles=%s", WIKIPEDIA_HOST, name); wpr->http_request = http_get(url); - wpr->state = WP_STATE_FIND_BODY; + http_req_skip_header(wpr->http_request); + wpr->state = WP_STATE_PARSE_JSON; wpr->normalized_title = xstrdup(name, "normalized_title"); return wpr; } +size_t +wikipedia_fetch_search_results(struct browser *browser, char *query, + char ***results) +{ + static char url[256]; + json_stream json; + struct http_request *req; + char *qencoded; + enum json_type type; + short strings = 0; + char **rets = NULL; + size_t nrets = 0; + + qencoded = url_encode(query); + + snprintf(url, sizeof(url), "http://%s/w/api.php?action=opensearch&" + "format=json&formatversion=2&namespace=0&limit=10&" + "search=%s", WIKIPEDIA_HOST, qencoded); + xfree(&qencoded); + req = http_get(url); + http_req_skip_header(req); + + json_open_user(&json, http_req_chunk_read, http_req_chunk_peek, req); + + for (;;) { + type = json_next(&json); + + if (type == JSON_ERROR || type == JSON_DONE || + type == JSON_ARRAY_END) + break; + + if (type == JSON_STRING) { + strings++; + + /* skip first, it'll be our query */ + if (strings == 1) + continue; + + nrets++; + rets = xreallocarray(rets, sizeof(Ptr), nrets); + rets[nrets - 1] = xstrdup(json_get_string(&json, NULL), + "search result"); + } else if (type == JSON_ARRAY_END) { + break; + } + } + + json_close(&json); + http_req_free(&req); + + *results = rets; + + return nrets; +} + struct wikipedia_request * wikipedia_read_cached_article(struct browser *browser, char *name) { @@ -103,7 +161,7 @@ wikipedia_read_cached_article(struct browser *browser, FSClose(frefnum); - wpr->state = WP_STATE_FIND_BODY; + wpr->state = WP_STATE_PARSE_JSON; wpr->normalized_title = xstrdup(name, "normalized_title"); return wpr; @@ -113,172 +171,138 @@ short wikipedia_json_peek(void *cookie) { struct wikipedia_request *wpr = (struct wikipedia_request *)cookie; + struct http_request *req = wpr->http_request; - if (wpr->buf_len == 0 || (wpr->buf_off + 1 > wpr->buf_len)) { - wpr->buf_len = http_req_read(wpr->http_request, wpr->buf, - sizeof(wpr->buf)); - wpr->buf_off = 0; - } - - if (wpr->buf_len == 0 || (wpr->buf_off + 1 > wpr->buf_len)) - return EOF; - - return wpr->buf[wpr->buf_off]; + return http_req_chunk_peek(req); } short wikipedia_json_get(void *cookie) { struct wikipedia_request *wpr = (struct wikipedia_request *)cookie; - short c; + struct http_request *req = wpr->http_request; - c = wikipedia_json_peek(cookie); - if (c == EOF) - return c; - - wpr->buf_off++; - - return c; + return http_req_chunk_read(req); } void wikipedia_request_process(struct wikipedia_request *wpr) { + struct http_request *req = wpr->http_request; size_t len, n; switch (wpr->state) { - case WP_STATE_FIND_BODY: - if (wpr->buf_len > 3) { - /* - * Leave last 3 bytes of previous read in case \r\n\r\n happens - * across reads. - */ - memmove(wpr->buf, wpr->buf + wpr->buf_len - 3, - wpr->buf_len - 3); - wpr->buf_len = 3; - } - len = http_req_read(wpr->http_request, wpr->buf + wpr->buf_len, - sizeof(wpr->buf) - wpr->buf_len); - wpr->buf_len += len; - if (!len) - break; - - for (n = 3; n < wpr->buf_len; n++) { - if (wpr->buf[n - 3] != '\r' || wpr->buf[n - 2] != '\n' || - wpr->buf[n - 1] != '\r' || wpr->buf[n] != '\n') - continue; - - wpr->buf_off = n + 1; - wpr->state = WP_STATE_PARSE_JSON; - wpr->json_context_depth = 0; - - progress("Parsing JSON response..."); - json_open_user(&wpr->json, wikipedia_json_get, - wikipedia_json_peek, wpr); - break; - } - - break; case WP_STATE_PARSE_JSON: { - static char context_str[PDJSON_STACK_MAX * 32]; + char context_str[PDJSON_STACK_MAX * 32]; const char *str; enum json_type type, context_type; size_t tmp_depth; - type = json_next(&wpr->json); + wpr->json_context_depth = 0; + + progress("Parsing JSON response..."); + json_open_user(&wpr->json, wikipedia_json_get, wikipedia_json_peek, + wpr); + + for (;;) { + type = json_next(&wpr->json); - if (type == JSON_ERROR || type == JSON_DONE) { - if (type == JSON_ERROR) { - char err[100]; - size_t len; - len = snprintf(err, sizeof(err), "%s at line %ld pos %ld", - json_get_error(&wpr->json), json_get_lineno(&wpr->json), - json_get_position(&wpr->json)); - browser_print(wpr->browser, err, len, 0); + if (type == JSON_ERROR || type == JSON_DONE) { + if (type == JSON_ERROR) { + char err[100]; + size_t len; + len = snprintf(err, sizeof(err), + "%s at line %ld pos %ld", json_get_error(&wpr->json), + json_get_lineno(&wpr->json), + json_get_position(&wpr->json)); + browser_print(wpr->browser, err, len, 0); + } + json_close(&wpr->json); + if (wpr->http_request != NULL) + http_req_free(&wpr->http_request); + if (type == JSON_ERROR) + wpr->state = WP_STATE_DONE; + else { + wpr->state = WP_STATE_PARSE_WIKITEXT; + progress("Formatting article..."); + } + break; } - json_close(&wpr->json); - if (wpr->http_request != NULL) - http_req_free(&wpr->http_request); - if (type == JSON_ERROR) - wpr->state = WP_STATE_DONE; - else { - wpr->state = WP_STATE_PARSE_WIKITEXT; - progress("Formatting article..."); - } - break; - } - - context_type = json_get_context(&wpr->json, &tmp_depth); + context_type = json_get_context(&wpr->json, &tmp_depth); + #define wprjcd wpr->json_context[wpr->json_context_depth] - switch (type) { - case JSON_OBJECT: - snprintf(wprjcd, sizeof(wprjcd), "{"); - wpr->json_context_depth++; - break; - case JSON_OBJECT_END: - snprintf(wprjcd, sizeof(wprjcd), "}"); - wpr->json_context_depth--; - break; - case JSON_ARRAY: - snprintf(wprjcd, sizeof(wprjcd), "["); - wpr->json_context_depth++; - break; - case JSON_ARRAY_END: - snprintf(wprjcd, sizeof(wprjcd), "]"); - wpr->json_context_depth--; - break; - case JSON_STRING: - snprintf(wprjcd, sizeof(wprjcd), "\"%s\"", - json_get_string(&wpr->json, NULL)); - break; - case JSON_NUMBER: - snprintf(wprjcd, sizeof(wprjcd), "%s", - json_get_string(&wpr->json, NULL)); - break; - case JSON_TRUE: - snprintf(wprjcd, sizeof(wprjcd), "true"); - break; - case JSON_FALSE: - snprintf(wprjcd, sizeof(wprjcd), "false"); - break; - case JSON_NULL: - snprintf(wprjcd, sizeof(wprjcd), "null"); - break; - } - - if (tmp_depth > 0 && (tmp_depth % 2) != 0) - strlcat(wprjcd, ":", sizeof(wprjcd)); - - if (type != JSON_STRING) - goto next_context; + switch (type) { + case JSON_OBJECT: + snprintf(wprjcd, sizeof(wprjcd), "{"); + wpr->json_context_depth++; + break; + case JSON_OBJECT_END: + snprintf(wprjcd, sizeof(wprjcd), "}"); + wpr->json_context_depth--; + break; + case JSON_ARRAY: + snprintf(wprjcd, sizeof(wprjcd), "["); + wpr->json_context_depth++; + break; + case JSON_ARRAY_END: + snprintf(wprjcd, sizeof(wprjcd), "]"); + wpr->json_context_depth--; + break; + case JSON_STRING: + snprintf(wprjcd, sizeof(wprjcd), "\"%s\"", + json_get_string(&wpr->json, NULL)); + break; + case JSON_NUMBER: + snprintf(wprjcd, sizeof(wprjcd), "%s", + json_get_string(&wpr->json, NULL)); + break; + case JSON_TRUE: + snprintf(wprjcd, sizeof(wprjcd), "true"); + break; + case JSON_FALSE: + snprintf(wprjcd, sizeof(wprjcd), "false"); + break; + case JSON_NULL: + snprintf(wprjcd, sizeof(wprjcd), "null"); + break; + } - context_str[0] = '\0'; - for (n = 0; n < wpr->json_context_depth; n++) - strlcat(context_str, wpr->json_context[n], sizeof(context_str)); + if (tmp_depth > 0 && (tmp_depth % 2) != 0) + strlcat(wprjcd, ":", sizeof(wprjcd)); + + if (type != JSON_STRING) + goto next_context; + + context_str[0] = '\0'; + for (n = 0; n < wpr->json_context_depth; n++) + strlcat(context_str, wpr->json_context[n], + sizeof(context_str)); - if (strcmp(context_str, NORMALIZED_TITLE_CONTEXT) == 0) { - xfree(&wpr->normalized_title); - wpr->normalized_title = - xstrdup(json_get_string(&wpr->json, NULL), "normalized_title"); - } else if (strcmp(context_str, ARTICLE_TEXT_CONTEXT) == 0) { - str = json_get_string(&wpr->json, &wpr->article_len); - wpr->article = xmalloc(wpr->article_len, "article"); - for (n = 0; n < wpr->article_len; n++) { - if (str[n] == '\n') - wpr->article[n] = '\r'; - else - wpr->article[n] = str[n]; + if (strcmp(context_str, NORMALIZED_TITLE_CONTEXT) == 0) { + xfree(&wpr->normalized_title); + wpr->normalized_title = + xstrdup(json_get_string(&wpr->json, NULL), + "normalized_title"); + } else if (strcmp(context_str, ARTICLE_TEXT_CONTEXT) == 0) { + str = json_get_string(&wpr->json, &wpr->article_len); + wpr->article = xmalloc(wpr->article_len, "article"); + for (n = 0; n < wpr->article_len; n++) { + if (str[n] == '\n') + wpr->article[n] = '\r'; + else + wpr->article[n] = str[n]; + } } - } next_context: - if (context_type == JSON_OBJECT && tmp_depth > 0) { - if (tmp_depth % 2 == 0) - wpr->json_context_depth--; - else - wpr->json_context_depth++; + if (context_type == JSON_OBJECT && tmp_depth > 0) { + if (tmp_depth % 2 == 0) + wpr->json_context_depth--; + else + wpr->json_context_depth++; + } } break; } --- wikipedia.h Wed Aug 24 17:20:19 2022 +++ wikipedia.h Thu Aug 25 08:50:29 2022 @@ -42,7 +42,6 @@ extern MenuHandle file_menu, edit_menu; void menu_defaults(void); enum { - WP_STATE_FIND_BODY, WP_STATE_PARSE_JSON, WP_STATE_PARSE_WIKITEXT, WP_STATE_DONE @@ -53,9 +52,6 @@ struct wikipedia_request { struct browser *browser; struct http_request *http_request; char *normalized_title; - char buf[1024]; - size_t buf_len; - size_t buf_off; json_stream json; char *article; size_t article_len; @@ -65,6 +61,8 @@ struct wikipedia_request { struct wikipedia_request * wikipedia_fetch_article(struct browser *, char *); +size_t wikipedia_fetch_search_results(struct browser *browser, char *query, + char ***results); struct wikipedia_request * wikipedia_read_cached_article(struct browser *browser, char *name); void wikipedia_request_process(struct wikipedia_request *wpr);