00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <stdlib.h>
00022 #include <string.h>
00023
00024 #include "htdoc.h"
00025 #include "htiobox.h"
00026 #include "htpal.h"
00027 #include "htstring.h"
00028 #include "htsys.h"
00029 #include "infoview.h"
00030 #include "tools.h"
00031
00032 class info_pos: public Object {
00033 public:
00034 UINT line;
00035 UINT ofs;
00036
00037 info_pos(UINT l, UINT o) {
00038 line = l;
00039 ofs = o;
00040 }
00041 };
00042
00043 int compare_keys_info_pos_delinear(Object *data_a, Object *data_b)
00044 {
00045 info_pos *a = (info_pos*)data_a;
00046 info_pos *b = (info_pos*)data_b;
00047 dword da = delinearize(a->line);
00048 dword db = delinearize(b->line);
00049 if (da == db) {
00050 return delinearize(a->ofs) - delinearize(b->ofs);
00051 }
00052 return da - db;
00053 }
00054
00055 int compare_keys_info_pos(Object *data_a, Object *data_b)
00056 {
00057 info_pos *a = (info_pos*)data_a;
00058 info_pos *b = (info_pos*)data_b;
00059 if (a->line == b->line) {
00060 return a->ofs - b->ofs;
00061 }
00062 return a->line - b->line;
00063 }
00064
00065 class info_xref: public Object {
00066 public:
00067 char *target;
00068 UINT len;
00069
00070 info_xref(char *t, UINT l) {
00071 target = strdup(t);
00072 len = l;
00073 }
00074
00075 ~info_xref() {
00076 free(target);
00077 }
00078 };
00079
00080
00081 char *memndup(const char *s, int n)
00082 {
00083 char *q = (char*)malloc(n+1);
00084 memcpy(q, s, n);
00085 q[n] = 0;
00086 return q;
00087 }
00088
00089
00090
00091
00092
00093 bool parse_xref_body(ht_streamfile *f, ht_tree *t, char **n, UINT *o, UINT *line, bool note)
00094 {
00095 whitespaces(n);
00096 char *l = strchr(*n, ':');
00097 if (!l) return false;
00098 char *e = l;
00099 while ((e>*n) && ((unsigned char)*(e-1)<=32)) e--;
00100 char *name = NULL;
00101 char *target = NULL;
00102 char *end = l;
00103 bool extrabreak=false;
00104 l++;
00105 whitespaces(&l);
00106 if (*(end+1) == ':') {
00107 name = memndup(*n, e-*n);
00108 end+=2;
00109 } else if ((note && (l-1 > end)) || (!note && (*(end+1) == ' '))){
00110 if (*(end+1) == '\n') extrabreak = true;
00111 char *v = l;
00112 if (*l == '(') {
00113 v = strchr(l, ')');
00114 if (!v) return false;
00115 }
00116 char *q = v;
00117 while (*q && (*q != '.') && (*q != ',')) q++;
00118 if (!*q) return false;
00119 char *p = q;
00120
00121 while ((q>l) && ((unsigned char)*(q-1)<=32)) q--;
00122 name = memndup(*n, e-*n);
00123 target = memndup(l, q-l);
00124 end = p+1;
00125 } else return false;
00126 f->write(name, strlen(name));
00127
00128
00129
00130 char *thetarget = strdup(target ? target : name);
00131 char *ttt = thetarget;
00132 while (*ttt) {
00133 if (*ttt == '\n') *ttt = ' ';
00134 ttt++;
00135 }
00136
00137 char *p = name;
00138 info_xref *x = new info_xref(thetarget, *o);
00139 t->insert(new info_pos(*line, *o), x);
00140 while (*p) {
00141 if (*p=='\n') {
00142 x->len = *o - x->len;
00143 *o = 0;
00144 (*line)++;
00145 x = new info_xref(thetarget, *o);
00146 t->insert(new info_pos(*line, *o), x);
00147 } else {
00148 (*o)++;
00149 }
00150 p++;
00151 }
00152 x->len = *o - x->len;
00153 if (extrabreak) {
00154 char cr = '\n';
00155 f->write(&cr, 1);
00156 *o = 0;
00157 (*line)++;
00158 }
00159 free(thetarget);
00160 if (name) free(name);
00161 if (target) free(target);
00162
00163 *n = end;
00164 return true;
00165 }
00166
00167 ht_tree *parse_info_node(ht_streamfile *fl, char *infotext)
00168 {
00169 char *n = infotext;
00170 bool linestart = true;
00171 FILEOFS f = 0;
00172 UINT o = 0;
00173 UINT l = 0;
00174 ht_stree *t = new ht_stree();
00175 t->init(compare_keys_info_pos_delinear);
00176
00177 while (*n && (*n != 0x1f)) {
00178 char *on = n;
00179 UINT oo = o;
00180 UINT ol = l;
00181 FILEOFS of = f;
00182 char *k = (*n == '*') ? n : strchr(n, '*');
00183 if ((k == n) && (ht_strnicmp(n, "*note", 5) == 0)) {
00184 n += 5;
00185 if (!parse_xref_body(fl, t, &n, &o, &l, true)) {
00186 n = on;
00187 o = oo;
00188 l = ol;
00189 f = of;
00190 fl->seek(f);
00191 goto fallback;
00192 }
00193 f = fl->tell();
00194 } else if (linestart && (k == n) && (n[1] == ' ')) {
00195 n++;
00196 if (!parse_xref_body(fl, t, &n, &o, &l, false)) {
00197 n = on;
00198 o = oo;
00199 l = ol;
00200 f = of;
00201 fl->seek(f);
00202 goto fallback;
00203 }
00204 f = fl->tell();
00205 } else {
00206 fallback:
00207 if (k && (k>n)) {
00208 char *cr = strchr(n, '\n');
00209 if (cr && (cr<k)) k = cr;
00210 if (k-n == 0) goto fallback2;
00211 fl->write(n, k-n);
00212 linestart = false;
00213 o+= k-n;
00214 f+= k-n;
00215 n+= k-n;
00216 } else {
00217 fallback2:
00218 fl->write(n, 1);
00219 if (*n == '\n') {
00220 linestart = true;
00221 o=0;
00222 l++;
00223 } else {
00224 linestart = false;
00225 o++;
00226 }
00227 n++;
00228 f++;
00229 }
00230 }
00231 }
00232 t->set_compare_keys(compare_keys_info_pos);
00233 return t;
00234 }
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251 void ht_info_lexer::init(ht_view *pf)
00252 {
00253 ht_syntax_lexer::init();
00254 xrefs = NULL;
00255 cx = 0;
00256 cy = 0;
00257 pal_from = pf;
00258 }
00259
00260 lexer_state ht_info_lexer::getinitstate()
00261 {
00262 return 1;
00263 }
00264
00265 vcp ht_info_lexer::getcolor_syntax(UINT pal_index)
00266 {
00267 return VCP(VC_WHITE, VC_BLACK);
00268 }
00269
00270 lexer_token ht_info_lexer::geterrortoken()
00271 {
00272 return 42;
00273 }
00274
00275 char *ht_info_lexer::getname()
00276 {
00277 return "infoview";
00278 }
00279
00280 #define ILT_TEXT 1
00281 #define ILT_LINK 2
00282 #define ILT_LINK_SEL 3
00283
00284 lexer_token ht_info_lexer::gettoken(void *b, UINT buflen, text_pos p, bool start_of_line, lexer_state *ret_state, UINT *ret_len)
00285 {
00286 if (buflen) {
00287 if (xrefs) {
00288 info_pos q(p.line, p.pofs);
00289 info_xref *x = (info_xref*)xrefs->get(&q);
00290 if (x) {
00291 *ret_len = MIN(x->len, buflen);
00292 return ((cy == p.line) && (cx >= p.pofs) &&
00293 (cx < p.pofs + x->len)) ? ILT_LINK_SEL : ILT_LINK;
00294 }
00295 }
00296 *ret_len = 1;
00297 return ILT_TEXT;
00298 } else {
00299 *ret_len = 0;
00300 return 0;
00301 }
00302 }
00303
00304 vcp ht_info_lexer::gettoken_color(lexer_token t)
00305 {
00306 switch (t) {
00307 case ILT_TEXT:
00308 return pal_from->getcolor(palidx_generic_text_focused);
00309 case ILT_LINK:
00310 return pal_from->getcolor(palidx_generic_text_shortcut);
00311 case ILT_LINK_SEL:
00312 return pal_from->getcolor(palidx_generic_text_shortcut_selected);
00313 }
00314 return VCP(VC_WHITE, VC_RED);
00315 }
00316
00317 void ht_info_lexer::set_cursor(UINT x, UINT y)
00318 {
00319 cx = x;
00320 cy = y;
00321 }
00322
00323 void ht_info_lexer::set_xrefs(ht_tree *x)
00324 {
00325 xrefs = x;
00326 }
00327
00328
00329
00330
00331
00332 class info_history_entry: public Object {
00333 public:
00334 char *cwd;
00335 char *file;
00336 char *node;
00337 UINT cursorx;
00338 UINT cursory;
00339 UINT xofs;
00340 UINT top_line;
00341
00342 info_history_entry(char *c, char *f, char *n, UINT x, UINT y, UINT xo, UINT yo)
00343 {
00344 cwd = ht_strdup(c);
00345 file = ht_strdup(f);
00346 node = ht_strdup(n);
00347 cursorx = x;
00348 cursory = y;
00349 xofs = xo;
00350 top_line = yo;
00351 }
00352
00353 ~info_history_entry()
00354 {
00355 if (cwd) free(cwd);
00356 if (file) free(file);
00357 if (node) free(node);
00358 }
00359 };
00360
00361 void ht_info_textfile::init(ht_streamfile *s, bool own_s, ht_syntax_lexer *l)
00362 {
00363 start = 0;
00364 end = 0;
00365 ht_ltextfile::init(s, own_s, l);
00366 }
00367
00368 void ht_info_textfile::done()
00369 {
00370 ht_ltextfile::done();
00371 }
00372
00373 ht_ltextfile_line *ht_info_textfile::fetch_line(UINT line)
00374 {
00375 if (line < linecount())
00376 return ht_ltextfile::fetch_line(start+line);
00377 return NULL;
00378 }
00379
00380 UINT ht_info_textfile::linecount()
00381 {
00382 return end-start;
00383 }
00384
00385 void ht_info_textfile::set_node(UINT ofs, UINT len)
00386 {
00387 UINT s, e, t;
00388 start = 0;
00389 end = ht_ltextfile::linecount();
00390 convert_ofs2line(ofs, &s, &t);
00391 convert_ofs2line(ofs+len, &e, &t);
00392 start = s;
00393 end = e;
00394 }
00395
00396
00397
00398
00399
00400 void ht_info_viewer::init(bounds *b)
00401 {
00402 ht_mem_file *f = new ht_mem_file();
00403 f->init();
00404
00405 ht_info_textfile *s = new ht_info_textfile();
00406 s->init(f, true, NULL);
00407
00408 ht_text_viewer::init(b, true, s, NULL);
00409 cwd = NULL;
00410 file = NULL;
00411 node = NULL;
00412 xrefs = NULL;
00413 history = new ht_clist();
00414 ((ht_clist*)history)->init();
00415 }
00416
00417 void ht_info_viewer::done()
00418 {
00419 if (history) {
00420 history->destroy();
00421 delete history;
00422 }
00423 if (xrefs) {
00424 xrefs->destroy();
00425 delete xrefs;
00426 }
00427 if (cwd) free(cwd);
00428 if (node) free(node);
00429 if (file) free(file);
00430 ht_text_viewer::done();
00431 }
00432
00433 char *ht_info_viewer::defaultpalette()
00434 {
00435 return palkey_generic_help_default;
00436 }
00437
00438 void ht_info_viewer::draw()
00439 {
00440 ((ht_info_lexer*)lexer)->set_cursor(physical_cursorx(), cursory+top_line);
00441 ht_text_viewer::draw();
00442 }
00443
00444 int ht_info_viewer::find_node(char *infotext, char *node)
00445 {
00446 char *tags[] = {"File", "Node", "Prev", "Next", "Up"};
00447 #define NUM_NODE_TAGS (sizeof (tags) / sizeof (tags[0]))
00448 char *s = infotext;
00449 char *firstnode = NULL;
00450 while ((s=strchr(s, 0x1f))) {
00451 s++;
00452 while ((*s>0) && (*s<32)) s++;
00453 char *cr = strchr(s, '\n');
00454 if (cr) {
00455 while (*s && (s<cr)) {
00456 whitespaces(&s);
00457 char *os = s;
00458 for (UINT i=0; i<NUM_NODE_TAGS; i++) {
00459 UINT l = strlen(tags[i]);
00460 if ((strncmp(s, tags[i], l) == 0) && (s[l] == ':')) {
00461 s += l+1;
00462 whitespaces(&s);
00463 char *e = strchr(s, ',');
00464 if (!e || (e>cr)) e = cr;
00465 if (!firstnode && (strcmp(tags[i], "Node") == 0)) {
00466 firstnode = cr+1;
00467 }
00468 if ((strcmp(tags[i], "Node") == 0) &&
00469 ((size_t)(e-s) == strlen(node)) &&
00470 (strncmp(s, node, e-s)==0)) {
00471 return cr+1-infotext;
00472 }
00473 s = e+1;
00474 }
00475 }
00476 if (os == s) break;
00477 }
00478 }
00479 }
00480
00481 return -1;
00482 }
00483
00484 ht_tree *ht_info_viewer::get_xrefs()
00485 {
00486 return xrefs;
00487 }
00488
00489 bool ht_info_viewer::gotonode(char *f, char *n)
00490 {
00491 return igotonode(f, n, true);
00492 }
00493
00494 UINT ht_info_viewer::readfile(char *fn, char **text)
00495 {
00496 FILE *f = fopen(fn, "r");
00497 if (!f) return 0;
00498
00499 fseek(f, 0, SEEK_END);
00500 int size = ftell(f);
00501 fseek(f, 0, SEEK_SET);
00502
00503 char *x = (char*)malloc(size+1);
00504 UINT len = fread(x, 1, size, f);
00505 x[len] = 0;
00506
00507 fclose(f);
00508
00509 *text = x;
00510 return len;
00511 }
00512
00513 bool ht_info_viewer::igotonode(char *f, char *n, bool add2hist)
00514 {
00515 char *infotext = NULL;
00516 char nfile[HT_NAME_MAX];
00517 nfile[0] = 0;
00518 char ncwd[HT_NAME_MAX];
00519 ncwd[0] = 0;
00520 bool newnode = !node || (node && (strcmp(node, n) != 0));
00521 int fl = strlen(f)-strlen(MAGIC_HT_HELP);
00522 if ((fl>=0) && (strcmp(f+fl, MAGIC_HT_HELP) == 0)) {
00523 infotext = strdup(htinfo);
00524 strcpy(ncwd, "");
00525 strcpy(nfile, MAGIC_HT_HELP);
00526 } else {
00527 char ff[HT_NAME_MAX], cff[HT_NAME_MAX];
00528 cff[0] = 0;
00529 if (file && sys_common_canonicalize(cff, file, cwd, sys_is_path_delim) != 0) return false;
00530 if (sys_common_canonicalize(ff, f, cwd, sys_is_path_delim) != 0) return false;
00531 if ((strcmp(ff, cff) != 0) || newnode) {
00532 if (!readfile(ff, &infotext)) return false;
00533 char c[HT_NAME_MAX];
00534 if (sys_dirname(c, ff) == 0) strcpy(ncwd, c);
00535 if (sys_basename(c, ff) == 0) strcpy(nfile, c);
00536 }
00537 }
00538 if (infotext) {
00539 int o = find_node(infotext, n);
00540 if (o == -1) {
00541 free(infotext);
00542 return false;
00543 }
00544
00545 ht_mem_file *m = new ht_mem_file();
00546 m->init();
00547
00548 ht_tree *x = parse_info_node(m, infotext+o);
00549 if (x == NULL) {
00550 m->done();
00551 delete m;
00552 free(infotext);
00553 return false;
00554 }
00555 free(infotext);
00556
00557 if (add2hist && cwd && file && node) {
00558
00559 history->insert(new info_history_entry(
00560 cwd, file, node, cursorx, cursory,
00561 xofs, top_line));
00562 }
00563
00564 if (xrefs) {
00565 xrefs->destroy();
00566 delete xrefs;
00567 }
00568 xrefs = x;
00569
00570 ht_info_textfile *infofile=new ht_info_textfile();
00571 infofile->init(m, true, NULL);
00572
00573 ht_info_lexer *infolexer = new ht_info_lexer();
00574 infolexer->init(this);
00575
00576 infolexer->set_xrefs(xrefs);
00577 infofile->set_node(0, m->get_size());
00578 set_textfile(infofile, true);
00579 set_lexer(infolexer, true);
00580
00581 cursorx = 0;
00582 cursory = 0;
00583 xofs = 0;
00584 top_line = 0;
00585
00586 if (file) free(file);
00587 file = strdup(nfile);
00588
00589 if (cwd) free(cwd);
00590 cwd = strdup(ncwd);
00591
00592 if (node) free(node);
00593 node = strdup(n);
00594
00595
00596
00597 select_clear();
00598 return true;
00599 }
00600 return false;
00601 }
00602
00603 void ht_info_viewer::get_pindicator_str(char *buf)
00604 {
00605 buf += sprintf(buf, " %d:%d ", top_line+cursory+1, xofs+cursorx+1);
00606 sprintf(buf, "(%s) %s ", file, node);
00607 }
00608
00609 void ht_info_viewer::handlemsg(htmsg *msg)
00610 {
00611 if (msg->msg == msg_keypressed) {
00612 switch (msg->data1.integer) {
00613 case K_Space:
00614 case K_Return:
00615 if (get_xrefs()) {
00616 info_pos p(top_line + cursory, xofs + physical_cursorx());
00617 info_xref *x = (info_xref*)get_xrefs()->get(&p);
00618 if (!x) {
00619 UINT cx = physical_cursorx();
00620 info_pos *q = (info_pos*)get_xrefs()->enum_prev((Object**)&x, &p);
00621 if ((q) && ((q->line != top_line+cursory) ||
00622 (cx < q->ofs) || (cx >= q->ofs + x->len))) {
00623 x = NULL;
00624 }
00625 }
00626 if (x) {
00627 char *p = NULL, *q = NULL;
00628 char *a = x->target;
00629 if (*a == '(') {
00630 char *b = strchr(a, ')');
00631 if (b) {
00632 p = memndup(a+1, b-a-1);
00633 q = ht_strdup(b+1);
00634 }
00635 }
00636 if (!p) p = ht_strdup(file);
00637 if (!q) q = ht_strdup(x->target);
00638 if (!igotonode(p, q, true))
00639 errorbox("help topic '(%s)%s' not found", p, q);
00640 if (p) free(p);
00641 if (q) free(q);
00642 }
00643 clearmsg(msg);
00644 dirtyview();
00645 return;
00646 }
00647 break;
00648 case K_Alt_Backspace:
00649 case K_Backspace: {
00650 int c;
00651 if ((c = history->count())) {
00652 info_history_entry *e = (info_history_entry*)history->get(c-1);
00653
00654 if (e->node) {
00655 if (igotonode(e->file, e->node, false)) {
00656 cursorx = e->cursorx;
00657 cursory = e->cursory;
00658 xofs = e->xofs;
00659 top_line = e->top_line;
00660 } else {
00661 errorbox("help topic '(%s)%s' not found", e->file, e->node);
00662 }
00663 history->del(c-1);
00664 clearmsg(msg);
00665 dirtyview();
00666 return;
00667 }
00668 }
00669 break;
00670 }
00671 case K_Tab: {
00672 if (get_xrefs()) {
00673 info_pos p(top_line + cursory, xofs + physical_cursorx());
00674 info_xref *r;
00675 info_pos *q = (info_pos*)get_xrefs()->enum_next((Object**)&r, &p);
00676 if (q) {
00677 goto_line(q->line);
00678 cursor_pput(q->ofs);
00679 }
00680 }
00681 clearmsg(msg);
00682 dirtyview();
00683 return;
00684 }
00685 case K_BackTab: {
00686 if (get_xrefs()) {
00687 info_pos p(top_line + cursory, xofs + physical_cursorx());
00688 info_xref *r;
00689 info_pos *q = (info_pos*)get_xrefs()->enum_prev((Object**)&r, &p);
00690 if (q) {
00691 goto_line(q->line);
00692 cursor_pput(q->ofs);
00693 }
00694 }
00695 clearmsg(msg);
00696 dirtyview();
00697 return;
00698 }
00699 }
00700 }
00701 ht_text_viewer::handlemsg(msg);
00702 }