00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "analy.h"
00022 #include "analy_names.h"
00023 #include "global.h"
00024 #include "log.h"
00025 #include "htanaly.h"
00026 #include "htctrl.h"
00027 #include "htdialog.h"
00028 #include "htdisasm.h"
00029 #include "hthist.h"
00030 #include "htidle.h"
00031 #include "htiobox.h"
00032 #include "htkeyb.h"
00033 #include "htmenu.h"
00034 #include "htsearch.h"
00035 #include "htstring.h"
00036 #include "httag.h"
00037 #include "httree.h"
00038 #include "language.h"
00039 #include "textedit.h"
00040 #include "textfile.h"
00041 #include "tools.h"
00042 #include "snprintf.h"
00043 #include "syntax.h"
00044 #include "out.h"
00045 #include "out_ht.h"
00046 #include "out_txt.h"
00047 #include "store.h"
00048
00049 extern "C" {
00050 #include "evalx.h"
00051 }
00052
00053 #include <stdlib.h>
00054 #include <string.h>
00055
00056
00057 #include "srt.h"
00058 #include "out_html.h"
00059 #include "out_sym.h"
00060
00061
00062
00063
00064 void AnalyserInformation::init(bounds *b, ht_aviewer *a)
00065 {
00066 analy = a;
00067 assert(a);
00068 ht_statictext::init(b, 0, align_left);
00069 register_idle_object(this);
00070 idle();
00071 }
00072
00073 void AnalyserInformation::done()
00074 {
00075 unregister_idle_object(this);
00076 ht_statictext::done();
00077 }
00078
00079 char *AnalyserInformation::gettext()
00080 {
00081 ht_snprintf(buf, sizeof buf,
00082 "Analyser statistics:\n"
00083 "====================\n\n"
00084 "Type: %s\nFile: %s\n"
00085 "Using disassembler: %s\n\n"
00086 "Known locations: %d\n"
00087 "Known symbols: %d\n\n",
00088 atype, aname,
00089 adis,
00090 addrs, labels);
00091 return buf;
00092 }
00093
00094 bool AnalyserInformation::idle()
00095 {
00096 if (analy && analy->analy) {
00097 addrs = analy->analy->getLocationCount();
00098 labels = analy->analy->getSymbolCount();
00099 atype = analy->analy->getType();
00100 aname = analy->analy->getName();
00101 if (analy->analy->disasm) {
00102 adis = analy->analy->disasm->getName();
00103 } else {
00104 adis = "?";
00105 }
00106 dirtyview();
00107 }
00108 return false;
00109 }
00110
00111
00112
00113
00114
00115 void SymbolBox::init(bounds *b, Analyser *Analy)
00116 {
00117 analy = Analy;
00118 ht_listbox::init(b);
00119 str = (char *)smalloc(1024);
00120 symbols = analy->getSymbolCount();
00121 idle_count = 1;
00122 }
00123
00124 void SymbolBox::done()
00125 {
00126 free(str);
00127 ht_listbox::done();
00128 }
00129
00130 int SymbolBox::calcCount()
00131 {
00132 return analy->getSymbolCount();
00133 }
00134
00135 int SymbolBox::cursorAdjust()
00136 {
00137 Symbol *l = ((Symbol *)getFirst());
00138 if (!l) return 0;
00139 return l->location->addr->stringSize()+10;
00140 }
00141
00142 int SymbolBox::estimateEntryPos(void *entry)
00143 {
00144 return 0;
00145 }
00146
00147 void *SymbolBox::getFirst()
00148 {
00149 return analy->enumSymbols(NULL);
00150 }
00151
00152 void *SymbolBox::getLast()
00153 {
00154 return analy->enumSymbolsReverse(NULL);
00155 }
00156
00157 void *SymbolBox::getNext(void *entry)
00158 {
00159 if (!entry) return NULL;
00160 return analy->enumSymbols((Symbol *)entry);
00161 }
00162
00163 void *SymbolBox::getPrev(void *entry)
00164 {
00165 if (!entry) return NULL;
00166 return analy->enumSymbolsReverse((Symbol *)entry);
00167 }
00168
00169 char *SymbolBox::getStr(int col, void *entry)
00170 {
00171 if (!entry) return NULL;
00172 Symbol *l = ((Symbol *)entry);
00173 switch (col) {
00174 case 0:
00175 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_WHITESPACE;
00176 ht_snprintf(str, 1024, "%y", l->location->addr);
00177 break;
00178 case 1:
00179 ht_snprintf(str, 1024, "%s", label_type_short(l->type));
00180 break;
00181 case 2:
00182 ht_snprintf(str, 1024, "%s", l->name);
00183 break;
00184 }
00185 return str;
00186 }
00187
00188 bool SymbolBox::idle()
00189 {
00190 if ((idle_count % 500)==0) {
00191 update();
00192 redraw();
00193 symbols = analy->getSymbolCount();
00194 idle_count = 1;
00195 return 1;
00196 }
00197 idle_count++;
00198 return 0;
00199 }
00200
00201 int SymbolBox::numColumns()
00202 {
00203 return 3;
00204 }
00205
00206 void *SymbolBox::quickfind(char *s)
00207 {
00208 Symbol *tmp = analy->getSymbolByName(s);
00209 if (tmp) return tmp;
00210 tmp = analy->enumSymbolsByName(s);
00211 if (tmp) {
00212 int slen = strlen(s);
00213 int tlen = strlen(tmp->name);
00214 if (slen > tlen) return NULL;
00215 if (strncmp(tmp->name, s, slen)==0) return tmp;
00216 return NULL;
00217 } else {
00218 return NULL;
00219 }
00220 }
00221
00222
00223 char *SymbolBox::quickfindCompletition(char *s)
00224 {
00225 if (analy->getSymbolByName(s)) {
00226 return ht_strdup(s);
00227 }
00228 Symbol *tmp = analy->enumSymbolsByName(s);
00229 if (!tmp) {
00230 return ht_strdup(s);
00231 }
00232 Symbol *tmp2 = analy->enumSymbols(tmp);
00233 if (!tmp2) {
00234 return ht_strdup(tmp->name);
00235 }
00236 int slen = strlen(s);
00237 if (!ht_strncmp(tmp->name, tmp2->name, slen)==0) {
00238 return ht_strdup(tmp->name);
00239 }
00240 char *res = (char *)smalloc(1024);
00241 strcpy(res, tmp->name);
00242 while (tmp2 && (ht_strncmp(tmp2->name, s, slen)==0)) {
00243
00244 int a = strccomm(res, tmp2->name);
00245 res[a] = 0;
00246 tmp2 = analy->enumSymbols(tmp2);
00247 }
00248 return res;
00249 }
00250
00251
00252
00253
00254
00255 void CallChain::init(bounds *b, Analyser *Analy, Address *a, char *desc)
00256 {
00257 ht_treeview::init(b, desc);
00258 VIEW_DEBUG_NAME("CallChain");
00259 analy = Analy;
00260 root = createNode(a);
00261 }
00262
00263 void CallChain_done(CallChainNode *n)
00264 {
00265 while (n) {
00266 CallChain_done(n->child);
00267 CallChainNode *temp = n->next;
00268 free(n);
00269 n = temp;
00270 }
00271 }
00272
00273 void CallChain::done()
00274 {
00275 CallChain_done(root);
00276 ht_treeview::done();
00277 }
00278
00279 void CallChain::adjust(void *node, bool expand)
00280 {
00281 ((CallChainNode*)node)->expanded = expand;
00282 }
00283
00284 CallChainNode *CallChain::createNode(Address *A)
00285 {
00286 CallChainNode *n = (CallChainNode *)smalloc(sizeof(CallChainNode));
00287 n->next = NULL;
00288 n->prev = NULL;
00289 n->child = NULL;
00290 n->examined = false;
00291 n->xa = (Address *)A->duplicate();
00292 n->faddr = analy->getFunctionByAddress(A);
00293 assert(n->faddr);
00294 n->fa = n->faddr->addr;
00295 n->expanded = false;
00296 return n;
00297 }
00298
00299 void CallChain::examineNode(CallChainNode *n)
00300 {
00301 n->examined = true;
00302 if (has_children(n)) {
00303 ht_tree *x_tree = n->faddr->xrefs;
00304 assert(x_tree);
00305 AddrXRef *x;
00306 Address *a = (Address *)x_tree->enum_next((Object**)&x, NULL);
00307 assert(a);
00308 CallChainNode *nn = n->child = createNode(a);
00309 while ((a = (Address *)x_tree->enum_next((Object**)&x, a))) {
00310 nn->next = createNode(a);
00311 nn = nn->next;
00312 }
00313 }
00314 }
00315
00316 void *CallChain::get_child(void *node, int i)
00317 {
00318 CallChainNode *p;
00319 if (node) {
00320 if (!((CallChainNode *)node)->examined) examineNode((CallChainNode*)node);
00321 p = ((CallChainNode *)node)->child;
00322 } else {
00323 p = root;
00324 }
00325 while (p && (--i)) p = p->next;
00326 return p;
00327 }
00328
00329 void *CallChain::get_next_node(void *node)
00330 {
00331 return ((CallChainNode*)node)->next;
00332 }
00333
00334 void *CallChain::get_prev_node(void *node)
00335 {
00336 return ((CallChainNode*)node)->prev;
00337 }
00338
00339 void *CallChain::get_root()
00340 {
00341 return root;
00342 }
00343
00344 char *CallChain::get_text(void *node)
00345 {
00346 static char stupid[1024];
00347 char *s = stupid;
00348 CallChainNode *n=(CallChainNode*)node;
00349 int d = 0;
00350 n->xa->difference(d, n->faddr->addr);
00351 char sign = '+';
00352 if (d<0) {
00353 d = -d;
00354 sign = '-';
00355 }
00356 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_COMPACT | ADDRESS_STRING_FORMAT_ADD_0X;
00357 s+=ht_snprintf(stupid, sizeof stupid, "%s%c%x (%y)", n->faddr->label?n->faddr->label->name:"unknown", sign, d, n->xa);
00358 return stupid;
00359 }
00360
00361 bool CallChain::has_children(void *node)
00362 {
00363 return ((CallChainNode*)node)->faddr->xrefs != NULL;
00364 }
00365
00366 bool CallChain::is_expanded(void *node)
00367 {
00368 return ((CallChainNode*)node)->expanded;
00369 }
00370
00371 void CallChain::select_node(void *node)
00372 {
00373 }
00374
00375
00377
00378
00379
00380
00381 #define ANALYINFOLINE_DISPLAYFORMAT_LENGTH 1024
00382 void AnalyInfoline::init(bounds *b, ht_aviewer *A, char *Format)
00383 {
00384 ht_statictext::init(b, 0, align_left);
00385 VIEW_DEBUG_NAME("AnalyInfoline");
00386 analy = A;
00387 displayformat = ht_strdup(Format);
00388 s = (char *)smalloc(ANALYINFOLINE_DISPLAYFORMAT_LENGTH);
00389 addr = new InvalidAddress();
00390 fofs = INVALID_FILE_OFS;
00391 }
00392
00393 void AnalyInfoline::done()
00394 {
00395 free(displayformat);
00396 free(s);
00397 delete addr;
00398 ht_statictext::done();
00399 }
00400
00401 char *AnalyInfoline::gettext()
00402 {
00403 if (valid()) {
00404 const char *sec = analy->analy->getSegmentNameByAddress(addr);
00405 if (fofs != INVALID_FILE_OFS) {
00406 Location *a = analy->analy->getFunctionByAddress(addr);
00407 const char *func = analy->analy->getSymbolNameByLocation(a);
00408
00409 char *d = displayformat;
00410 char *ss = s;
00411 while (*d) {
00412 if (*d=='%') {
00413 d++;
00414 switch (*d) {
00415 case ANALY_STATUS_ARG_SECTION:
00416 if (sec) ss+=ht_snprintf(ss, ANALYINFOLINE_DISPLAYFORMAT_LENGTH-(ss-s), "%s", sec);
00417 break;
00418 case ANALY_STATUS_ARG_FILEOFFSET:
00419 ss+=ht_snprintf(ss, ANALYINFOLINE_DISPLAYFORMAT_LENGTH-(ss-s), "%08x", fofs);
00420 break;
00421 case ANALY_STATUS_ARG_RAW_UNASM: {
00422 int length;
00423 ss+=ht_snprintf(ss, ANALYINFOLINE_DISPLAYFORMAT_LENGTH-(ss-s), "%s", analy->analy->getDisasmStr(addr, length));
00424 break;
00425 }
00426 case ANALY_STATUS_ARG_FUNCTION: {
00427 if (func) {
00428 int d = 0;
00429 addr->difference(d, a->addr);
00430 char sign='+';
00431 if (d<0) {
00432 d=-d;
00433 sign = '-';
00434 }
00435 ss+=ht_snprintf(ss, ANALYINFOLINE_DISPLAYFORMAT_LENGTH-(ss-s), "%s%c%x", func, sign, d);
00436 }
00437 break;
00438 }
00439 case ANALY_STATUS_ARG_OFFSET:
00440 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
00441 ss+=ht_snprintf(ss, ANALYINFOLINE_DISPLAYFORMAT_LENGTH-(ss-s), "%y", addr);
00442 break;
00443 case '%':
00444 if (ANALYINFOLINE_DISPLAYFORMAT_LENGTH-(ss-s)) {
00445 *ss++ = '%';
00446 }
00447 break;
00448 default:
00449 ss += ht_snprintf(ss, ANALYINFOLINE_DISPLAYFORMAT_LENGTH-(ss-s), " error in format ");
00450 }
00451 } else {
00452 if (ANALYINFOLINE_DISPLAYFORMAT_LENGTH-(ss-s)>1) {
00453
00454 *ss++ = *d;
00455 }
00456 }
00457 d++;
00458 }
00459 *ss = 0;
00460 } else {
00461 if (!sec) {
00462 ht_snprintf(s, ANALYINFOLINE_DISPLAYFORMAT_LENGTH, "[not in file]");
00463 } else {
00464 ht_snprintf(s, ANALYINFOLINE_DISPLAYFORMAT_LENGTH, "<%s> [not in file]", sec);
00465 }
00466 }
00467 return s;
00468 } else {
00469 return "<no analyser>";
00470 }
00471 }
00472
00473 void AnalyInfoline::update(Address *cursor_addr, FILEOFS ecursor_addr)
00474 {
00475 delete addr;
00476 if (valid()) {
00477 fofs = ecursor_addr;
00478 addr = (Address *)cursor_addr->duplicate();
00479 } else {
00480 fofs = INVALID_FILE_OFS;
00481 addr = new InvalidAddress();
00482 }
00483 dirtyview();
00484 redraw();
00485 }
00486
00487 bool AnalyInfoline::valid()
00488 {
00489 return ((analy)&&(analy->analy));
00490 }
00491
00492
00493
00494
00495 void ht_aviewer::init(bounds *b, char *desc, int caps, ht_streamfile *file, ht_format_group *format_group, Analyser *Analy)
00496 {
00497 analy = Analy;
00498 if (Analy) {
00499 analy->setDisplayMode(ANALY_SHOW_ADDRESS|ANALY_SHOW_COMMENTS|ANALY_SHOW_LABELS|ANALY_SHOW_XREFS|ANALY_TRANSLATE_SYMBOLS|ANALY_COLLAPSE_XREFS, ANALY_EDIT_BYTES);
00500 }
00501 analy_sub = NULL;
00502 ht_uformat_viewer::init(b, desc, caps, file, format_group);
00503 search_caps |= SEARCHMODE_BIN | SEARCHMODE_EVALSTR | SEARCHMODE_EXPR;
00504 infoline = NULL;
00505 idle_count = 0;
00506 last_active = true;
00507 pause = false;
00508 register_idle_object(this);
00509 one_load_hack = false;
00510 }
00511
00512 void ht_aviewer::done()
00513 {
00514 unregister_idle_object(this);
00515 ht_uformat_viewer::done();
00516 if (analy) {
00517 analy->done();
00518 delete analy;
00519 }
00520 }
00521
00522 void ht_aviewer::attachInfoline(AnalyInfoline *V)
00523 {
00524 infoline = V;
00525 }
00526
00527 bool ht_aviewer::pos_to_offset(viewer_pos p, FILEOFS *ofs)
00528 {
00529 if (analy) {
00530 Address *addr;
00531 if (!convertViewerPosToAddress(p, &addr)) return false;
00532 FILEOFS o=analy->addressToFileofs(addr);
00533 delete addr;
00534 if (o!=INVALID_FILE_OFS) {
00535 *ofs=o;
00536 return true;
00537 }
00538 }
00539 return false;
00540 }
00541
00542 bool ht_aviewer::pos_to_string(viewer_pos p, char *result, int maxlen)
00543 {
00544 if (!analy) return false;
00545 Address *a;
00546 if (!convertViewerPosToAddress(p, &a)) return false;
00547 Location *addr = analy->getLocationByAddress(a);
00548 if (addr && addr->label) {
00549
00550 ht_snprintf(result, maxlen, "%s", addr->label->name);
00551 return true;
00552 }
00553 addr = analy->getFunctionByAddress(a);
00554 if (addr && addr->label) {
00555 int d = 0;
00556 a->difference(d, addr->addr);
00557 ht_snprintf(result, maxlen, "%s+0%xh", addr->label->name, d);
00558 return true;
00559 }
00560 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS | ADDRESS_STRING_FORMAT_ADD_H;
00561 ht_snprintf(result, maxlen, "%y", a);
00562 return true;
00563 }
00564
00565 bool ht_aviewer::convertViewerPosToAddress(const viewer_pos &p, Address **a)
00566 {
00567 *a = analy->createAddress();
00568 (*a)->getFromArray((byte*)&(p.u.line_id.id2));
00569 return true;
00570 }
00571
00572 bool ht_aviewer::convertAddressToViewerPos(Address *a, viewer_pos *p)
00573 {
00574 if (a && a->isValid()) {
00575 clear_viewer_pos(p);
00576 p->u.sub = analy_sub;
00577 a->putIntoArray((byte*)&(p->u.line_id.id2));
00578 p->u.line_id.id1 = 0;
00579 return true;
00580 } else {
00581 return false;
00582 }
00583 }
00584
00585 char *ht_aviewer::func(UINT i, bool execute)
00586 {
00587 switch (i) {
00588 case 8: {
00589 if (execute) {
00590 sendmsg(cmd_analyser_symbols);
00591 }
00592 return "symbols";
00593 }
00594 default:
00595 return ht_uformat_viewer::func(i, execute);
00596 }
00597 return 0;
00598 }
00599
00600 static int aviewer_func_addr(eval_scalar *result, eval_str *str)
00601 {
00602 ht_aviewer *aviewer = (ht_aviewer*)eval_get_context();
00603
00604 Address *addr = aviewer->analy->createAddress();
00605 int l = addr->parseString(str->value, str->len, aviewer->analy);
00606 if (l) {
00607
00608 qword q = to_qword(0);
00609 addr->putIntoArray((byte*)&q);
00610 scalar_create_int_q(result, q);
00611 return 1;
00612 } else {
00613 char buffer[1024];
00614 bin2str(buffer, str->value, MIN((UINT)str->len, sizeof buffer));
00615 set_eval_error("invalid address '%s'", buffer);
00616 return 0;
00617 }
00618 }
00619
00620 static int aviewer_func_address_of(eval_scalar *result, eval_str *str)
00621 {
00622 ht_aviewer *aviewer = (ht_aviewer*)eval_get_context();
00623 char buffer[1024];
00624 bin2str(buffer, str->value, MIN((UINT)str->len, sizeof buffer));
00625 Symbol *l;
00626 if ((l = aviewer->analy->getSymbolByName(buffer))) {
00627
00628 qword q = to_qword(0);
00629 l->location->addr->putIntoArray((byte*)&q);
00630 scalar_create_int_q(result, q);
00631 return 1;
00632 } else {
00633 set_eval_error("invalid label '%s'", buffer);
00634 return 0;
00635 }
00636 }
00637
00638 static int aviewer_func_fileofs(eval_scalar *result, eval_int *i)
00639 {
00640
00641 ht_aviewer *aviewer = (ht_aviewer*)eval_get_context();
00642 viewer_pos p;
00643 if (aviewer->offset_to_pos(QWORD_GET_INT(i->value), &p)) {
00644 Address *a;
00645 qword q = to_qword(0);
00646 aviewer->convertViewerPosToAddress(p, &a);
00647 a->putIntoArray((byte*)&q);
00648 delete a;
00649 scalar_create_int_q(result, q);
00650 return 1;
00651 } else {
00652 set_eval_error("invalid file offset or no corresponding address for '0%xh'", i->value);
00653 return 0;
00654 }
00655 return 0;
00656 }
00657
00658
00659
00660
00661 static int ht_aviewer_symbol_to_addr(void *Aviewer, char **s, dword *v)
00662 {
00663
00664 ht_aviewer *aviewer = (ht_aviewer*)Aviewer;
00665 Address *a;
00666 if (**s == '@') {
00667 (*s)++;
00668 if (bnstr(s, v, 10)) {
00669 viewer_pos vp;
00670 if (!aviewer->offset_to_pos(*v, &vp)) {
00671 set_eval_error("invalid offset: %08x", v);
00672 return false;
00673 }
00674 aviewer->convertViewerPosToAddress(vp, &a);
00675 a->putIntoArray((byte*)v);
00676 delete a;
00677 return true;
00678 }
00679
00680 return false;
00681 } else {
00682 char *k=ht_strdup(*s);
00683 char *t=k;
00684 while (!strchr("+/-* \t[]", *t) && *t) t++;
00685 char temp=*t;
00686 *t=0;
00687 if ((*k == '$') && (k[1] == 0)) {
00688 *t=temp;
00689 *s+= t-k;
00690 free(k);
00691 if (aviewer->getCurrentAddress(&a)) {
00692 a->putIntoArray((byte*)v);
00693 delete a;
00694 return true;
00695 } else {
00696 delete a;
00697 return false;
00698 }
00699 }
00700 Symbol *l = aviewer->analy->getSymbolByName(k);
00701 *t=temp;
00702 if (l) {
00703
00704 *s+=t-k;
00705 a=l->location->addr;
00706 a->putIntoArray((byte*)v);
00707 free(k);
00708 return true;
00709 }
00710 free(k);
00711 }
00712 return false;
00713 }
00714
00715 static void setdatastr(ht_view *v, char *str)
00716 {
00717 ht_inputfield_data id;
00718 id.textlen = strlen(str);
00719 id.text = (byte*)str;
00720 v->databuf_set(&id, sizeof id);
00721 }
00722
00723 static void getdatastr(ht_inputfield_data *id, char *result)
00724 {
00725 memcpy(result, id->text, id->textlen);
00726 result[id->textlen]=0;
00727 }
00728
00729 struct output_dialog_data {
00730 ht_inputfield_data id1;
00731 ht_listpopup_data lp;
00732 ht_inputfield_data id2;
00733 ht_inputfield_data id3;
00734 };
00735
00736 void ht_aviewer::generateOutputDialog()
00737 {
00738 if (!analy) return;
00739 if (analy->active) {
00740 infobox("Please wait until analyser has finished before generating output file!");
00741 if (analy->active) return;
00742 }
00743
00744 bounds b;
00745 b.w=50;
00746 b.h=15;
00747 center_bounds(&b);
00748 ht_dialog *dialog;
00749 NEW_OBJECT(dialog, ht_dialog, &b, "generate analyser output", FS_KILLER | FS_TITLE | FS_MOVE);
00750 ht_view *v1, *v2;
00751 BOUNDS_ASSIGN(b, 2, 2, 25, 1);
00752 NEW_OBJECT(v1, ht_strinputfield, &b, 260);
00753 dialog->insert(v1);
00754 BOUNDS_ASSIGN(b, 2, 1, 25, 1);
00755 NEW_OBJECT(v2, ht_label, &b, "output ~filename:", v1);
00756 dialog->insert(v2);
00757
00758 char pname[HT_NAME_MAX+16];
00759 const char *filename = file->get_filename();
00760 if (filename) {
00761 pname[HT_NAME_MAX-1] = 0;
00762 strncpy(pname, filename, HT_NAME_MAX-1);
00763 char *suffix = sys_filename_suffix(pname);
00764 if (suffix) {
00765 strcpy(suffix, "out");
00766 setdatastr(v1, pname);
00767 }
00768 }
00769
00770 BOUNDS_ASSIGN(b, 29, 2, 15, 1);
00771 NEW_OBJECT(v1, ht_listpopup, &b);
00772 ((ht_listpopup*)v1)->insertstring("HTML");
00773 ((ht_listpopup*)v1)->insertstring("plain text");
00774 dialog->insert(v1);
00775 BOUNDS_ASSIGN(b, 29, 1, 15, 1);
00776 NEW_OBJECT(v2, ht_label, &b, "~output format:", v1);
00777 dialog->insert(v2);
00778 BOUNDS_ASSIGN(b, 2, 5, 35, 1);
00779 NEW_OBJECT(v1, ht_strinputfield, &b, 260);
00780 dialog->insert(v1);
00781 BOUNDS_ASSIGN(b, 2, 4, 35, 1);
00782 NEW_OBJECT(v2, ht_label, &b, "~start address:", v1);
00783 viewer_pos cur;
00784 if (get_current_pos(&cur)) {
00785 char str[1024];
00786 pos_to_string(cur, str, sizeof str);
00787 setdatastr(v1, str);
00788 }
00789 dialog->insert(v2);
00790 BOUNDS_ASSIGN(b, 2, 8, 35, 1);
00791 NEW_OBJECT(v1, ht_strinputfield, &b, 260);
00792 dialog->insert(v1);
00793 BOUNDS_ASSIGN(b, 2, 7, 35, 1);
00794 NEW_OBJECT(v2, ht_label, &b, "~end address (or #numberoflines):", v1);
00795 dialog->insert(v2);
00796
00797 if (get_current_pos(&cur)) {
00798 char str[1024];
00799 pos_to_string(cur, str, sizeof str);
00800 strcat(str, "+20");
00801 setdatastr(v1, str);
00802 }
00803 BOUNDS_ASSIGN(b, 13, 11, 9, 2);
00804 NEW_OBJECT(v1, ht_button, &b, "O~k", button_ok);
00805 dialog->insert(v1);
00806 BOUNDS_ASSIGN(b, 27, 11, 9, 2);
00807 NEW_OBJECT(v1, ht_button, &b, "~Cancel", button_cancel);
00808 dialog->insert(v1);
00809 while (dialog->run(false)==button_ok) {
00810 char filename[260];
00811 char start_str[1024], end_str[1024];
00812 viewer_pos start, end;
00813 bool by_lines;
00814 output_dialog_data odd;
00815 dialog->databuf_get(&odd, sizeof odd);
00816 getdatastr(&odd.id1, filename);
00817 getdatastr(&odd.id2, start_str);
00818 getdatastr(&odd.id3, end_str);
00819 if (!string_to_pos(start_str, &start)) {
00820 errorbox(globalerror);
00821 continue;
00822 }
00823 UINT end_by_lines;
00824 if ((by_lines = end_str[0]=='#')) {
00825 char *pend = &end_str[1];
00826 bnstr(&pend, &end_by_lines, 10);
00827 } else {
00828 if (!string_to_pos(end_str, &end)) {
00829 errorbox(globalerror);
00830 continue;
00831 }
00832 }
00833 Address *start_addr, *end_addr;
00834 if (!convertViewerPosToAddress(start, &start_addr) || !convertViewerPosToAddress(end, &end_addr)) {
00835 errorbox("invalid address");
00836 continue;
00837 }
00838
00839 ht_file *s = new ht_file();
00840 s->init(filename, FAM_WRITE, FOM_CREATE);
00841 if (s->get_error()) {
00842 infobox("couldnt create file '%s'.", filename);
00843 continue;
00844 } else {
00845 AnalyserOutput *out;
00846 switch (odd.lp.cursor_pos) {
00847 case 0:
00848 out = new AnalyserTxtOutput();
00849 ((AnalyserTxtOutput*)out)->init(analy, s);
00850 break;
00851 case 1:
00852 out = new AnalyserTxtOutput();
00853 ((AnalyserTxtOutput*)out)->init(analy, s);
00854 break;
00855 }
00856 out->generateFile(start_addr, end_addr);
00857 out->done();
00858 delete out;
00859 }
00860 s->done();
00861 delete s;
00862 break;
00863 }
00864 dialog->done();
00865 delete dialog;
00866 }
00867
00868 bool ht_aviewer::canCreateAddress(Address *addr, bool error_msg)
00869 {
00870 Location *ctx = analy->getLocationContextByAddress(addr);
00871 if (ctx && ctx->addr->compareTo(addr)!=0) {
00872 if (error_msg) {
00873 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
00874 errorbox("Can't create new symbol: Address %y belongs to %y (%s)", addr, ctx->addr, ctx->label ? ctx->label->name : "unnamed");
00875 }
00876 return false;
00877 }
00878 return true;
00879 }
00880
00881 void ht_aviewer::dataStringDialog()
00882 {
00883 if (!analy) return;
00884 Address *current_address;
00885 if (!getCurrentAddress(¤t_address)) return;
00886 if (!canCreateAddress(current_address, true)) {
00887 delete current_address;
00888 return;
00889 }
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903 if (analy->validAddress(current_address, scinitialized)) {
00904 byte buffer[1024];
00905 Location *a = analy->enumLocations(current_address);
00906 int d = sizeof buffer;
00907 if (a) a->addr->difference(d, current_address);
00908 UINT bz = analy->bufPtr(current_address, buffer, MIN(sizeof buffer, (UINT)d));
00909 if (bz > 2) {
00910 analy_string *str = string_test(buffer, bz);
00911 if (str) {
00912 char string1[256], string2[31];
00913 str->render_string(string2, sizeof string2);
00914 ht_snprintf(string1, sizeof string1, "%s_%s", str->name(), string2);
00915 make_valid_name(string2, string1);
00916 if (analy->addAddressSymbol(current_address, string2, label_data)) {
00917 analy->addComment(current_address, 0, "");
00918 }
00919 analy->data->setArrayAddressType(current_address, dst_string, str->length());
00920 str->done();
00921 delete str;
00922 }
00923 }
00924 }
00925 delete current_address;
00926 }
00927
00928 struct export_dialog_data {
00929 ht_inputfield_data id1;
00930 ht_listpopup_data lp;
00931 };
00932
00933 void ht_aviewer::exportFileDialog()
00934 {
00935 if (!analy) return;
00936 if (analy->active) {
00937 infobox("Please wait until analyser has finished before exporting file!");
00938 if (analy->active) return;
00939 }
00940
00941 bounds b;
00942 b.w=50;
00943 b.h=12;
00944 center_bounds(&b);
00945 ht_dialog *dialog;
00946 NEW_OBJECT(dialog, ht_dialog, &b, "export analyser information", FS_KILLER | FS_TITLE | FS_MOVE);
00947 ht_view *v1, *v2;
00948 BOUNDS_ASSIGN(b, 2, 2, 35, 1);
00949 NEW_OBJECT(v1, ht_strinputfield, &b, 260);
00950 dialog->insert(v1);
00951 BOUNDS_ASSIGN(b, 2, 1, 35, 1);
00952 NEW_OBJECT(v2, ht_label, &b, "output ~filename:", v1);
00953 dialog->insert(v2);
00954
00955 char pname[HT_NAME_MAX+16];
00956 const char *filename = file->get_filename();
00957 if (filename) {
00958 pname[HT_NAME_MAX-1] = 0;
00959 strncpy(pname, filename, HT_NAME_MAX-1);
00960 char *suffix = sys_filename_suffix(pname);
00961 if (suffix) {
00962 strcpy(suffix, "exp");
00963 setdatastr(v1, pname);
00964 }
00965 }
00966
00967 BOUNDS_ASSIGN(b, 2, 5, 25, 1);
00968 NEW_OBJECT(v1, ht_listpopup, &b);
00969 ((ht_listpopup*)v1)->insertstring(".sym symbol file");
00970 dialog->insert(v1);
00971 BOUNDS_ASSIGN(b, 2, 4, 25, 1);
00972 NEW_OBJECT(v2, ht_label, &b, "~export format:", v1);
00973 dialog->insert(v2);
00974
00975 BOUNDS_ASSIGN(b, 13, 8, 9, 2);
00976 NEW_OBJECT(v1, ht_button, &b, "O~k", button_ok);
00977 dialog->insert(v1);
00978 BOUNDS_ASSIGN(b, 27, 8, 9, 2);
00979 NEW_OBJECT(v1, ht_button, &b, "~Cancel", button_cancel);
00980 dialog->insert(v1);
00981 while (dialog->run(false)==button_ok) {
00982 char filename[260];
00983 export_dialog_data edd;
00984 dialog->databuf_get(&edd, sizeof edd);
00985 getdatastr(&edd.id1, filename);
00986
00987 ht_file *s = new ht_file();
00988 s->init(filename, FAM_WRITE, FOM_CREATE);
00989 if (s->get_error()) {
00990 infobox("couldnt create file '%s'.", filename);
00991 continue;
00992 } else {
00993 switch (edd.lp.cursor_pos) {
00994 case 0:
00995 export_to_sym(analy, s);
00996 break;
00997 }
00998 }
00999 s->done();
01000 delete s;
01001 break;
01002 }
01003 dialog->done();
01004 delete dialog;
01005 }
01006
01007 bool ht_aviewer::getCurrentAddress(Address **a)
01008 {
01009 viewer_pos vp;
01010 if (!get_current_pos(&vp)) return false;
01011 return convertViewerPosToAddress(vp, a);
01012 }
01013
01014 bool ht_aviewer::get_current_offset(FILEOFS *ofs)
01015 {
01016 if (ht_uformat_viewer::get_current_offset(ofs)) {
01017 return true;
01018 } else {
01019 Address *a;
01020 if (!getCurrentAddress(&a)) return false;
01021 *ofs = analy->addressToFileofs(a);
01022 delete a;
01023 return *ofs != INVALID_FILE_OFS;
01024 }
01025 }
01026
01027 bool ht_aviewer::get_hscrollbar_pos(int *pstart, int *psize)
01028 {
01029 if (analy_sub) {
01030
01031
01032
01033
01034
01035 }
01036 return false;
01037 }
01038
01039 void ht_aviewer::get_pindicator_str(char *buf)
01040 {
01041 Address *addr;
01042 if (analy && getCurrentAddress(&addr)) {
01043 FILEOFS o;
01044 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_COMPACT;
01045 if (get_current_offset(&o)) {
01046 ht_snprintf(buf, 1024, " %y/@%08x%s ", addr, o, (analy->isDirty())?" dirty":"");
01047 } else {
01048 ht_snprintf(buf, 1024, " %y%s ", addr, (analy->isDirty())?" dirty":"");
01049 }
01050 delete addr;
01051 } else {
01052 strcpy(buf, "?");
01053 }
01054 }
01055
01056 bool ht_aviewer::gotoAddress(Address *a, ht_view *source_object)
01057 {
01058 viewer_pos p;
01059 if (analy) {
01060 if (!analy->validAddress(a, scvalid)) return false;
01061 }
01062
01063 if (convertAddressToViewerPos(a, &p)) {
01064 return goto_pos(p, source_object);
01065 }
01066 return false;
01067 }
01068
01069 void ht_aviewer::handlemsg(htmsg *msg)
01070 {
01071 char str[1024];
01072 switch (msg->msg) {
01073 case msg_contextmenuquery: {
01074 ht_static_context_menu *m=new ht_static_context_menu();
01075 m->init("~Analyser");
01076 m->insert_entry("~Assemble...", "Ctrl-A", cmd_analyser_call_assembler, K_Control_A, 1);
01077 m->insert_separator();
01078 ht_static_context_menu *sub=new ht_static_context_menu();
01079 sub->init("~Control");
01080 sub->insert_entry("~Information...", 0, cmd_analyser_info, 0, 1);
01081 sub->insert_separator();
01082 sub->insert_entry("~Save state", 0, cmd_analyser_save, 0, 1);
01083 sub->insert_entry("~Export to file...", NULL, cmd_analyser_export_file, 0, 1);
01084 sub->insert_separator();
01085 sub->insert_entry("~Continue at address", "c", cmd_analyser_continue, 0, 1);
01086 sub->insert_entry("~Pause / resume", "p", cmd_analyser_pause_resume, 0, 1);
01087 m->insert_submenu(sub);
01088 sub=new ht_static_context_menu();
01089 sub->init("~Jump");
01090 sub->insert_entry("~Symbol list", "F8", cmd_analyser_symbols, 0, 1);
01091 sub->insert_entry("~Function start", "Ctrl-F", cmd_analyser_this_function, K_Control_F, 1);
01092 sub->insert_entry("Prev ~label", "Ctrl-L", cmd_analyser_previous_label, K_Control_L, 1);
01093 sub->insert_entry("Follow ~ptr", "f", cmd_analyser_follow, 0, 1);
01094 m->insert_submenu(sub);
01095 sub=new ht_static_context_menu();
01096 sub->init("~Location");
01097 sub->insert_entry("~Name address (label)...", "n", cmd_analyser_name_addr, 0, 1);
01098 sub->insert_entry("Edit co~mments", "#", cmd_analyser_comments, 0, 1);
01099 sub->insert_entry("Show ~xrefs", "x", cmd_analyser_xrefs, 0, 1);
01100 sub->insert_entry("~Delete location", "Del", cmd_analyser_del_addr_bindings, 0, 1);
01101 sub->insert_entry("~Callchain", "Ctrl-T", cmd_analyser_call_chain, K_Control_T, 1);
01102 m->insert_submenu(sub);
01103 sub=new ht_static_context_menu();
01104 sub->init("~Data");
01105 sub->insert_entry("Data ~string", "s", cmd_analyser_data_string, 0, 1);
01106 m->insert_submenu(sub);
01107 m->insert_separator();
01108 m->insert_entry("Symbol reg trace (exp!)", "Alt-Q", cmd_analyser_srt, K_Alt_Q, 1);
01109
01110 msg->msg = msg_retval;
01111 msg->data1.ptr = m;
01112 return;
01113 }
01114 case msg_keypressed:
01115 switch (msg->data1.integer) {
01116 case K_Control_L: {
01117 sendmsg(cmd_analyser_previous_label);
01118 clearmsg(msg);
01119 return;
01120 }
01121 case K_Control_F: {
01122 sendmsg(cmd_analyser_this_function);
01123 clearmsg(msg);
01124 return;
01125 }
01126 case K_Control_O: {
01127 sendmsg(cmd_analyser_generate_output);
01128 clearmsg(msg);
01129 return;
01130 }
01131 case 'c':
01132 if (cursor_tag_class == tag_class_sel) {
01133 sendmsg(cmd_analyser_continue);
01134 clearmsg(msg);
01135 return;
01136 }
01137 break;
01138 case '#':
01139 sendmsg(cmd_analyser_comments);
01140 clearmsg(msg);
01141 return;
01142 case 'F':
01143 if (cursor_tag_class == tag_class_sel) {
01144 sendmsg(cmd_analyser_follow_ex);
01145 clearmsg(msg);
01146 return;
01147 }
01148 break;
01149 case 'f':
01150 if (cursor_tag_class == tag_class_sel) {
01151 sendmsg(cmd_analyser_follow);
01152 clearmsg(msg);
01153 return;
01154 }
01155 break;
01156 case 'n':
01157 sendmsg(cmd_analyser_name_addr);
01158 clearmsg(msg);
01159 return;
01160 case 'p':
01161 sendmsg(cmd_analyser_pause_resume);
01162 clearmsg(msg);
01163 return;
01164 case 's':
01165 sendmsg(cmd_analyser_data_string);
01166 clearmsg(msg);
01167 return;
01168 case 'x':
01169 sendmsg(cmd_analyser_xrefs);
01170 clearmsg(msg);
01171 return;
01172 case K_Control_D:
01173 case K_Delete:
01174 sendmsg(cmd_analyser_del_addr_bindings);
01175 clearmsg(msg);
01176 return;
01177 }
01178 break;
01179 case cmd_analyser_call_assembler: {
01180 if (!analy) break;
01181 Assembler *a = analy->createAssembler();
01182 if (!a) {
01183
01184 infobox("no assembler available.");
01185 clearmsg(msg);
01186 return;
01187 }
01188 viewer_pos current_pos;
01189 Address *current_address;
01190 if (get_current_pos(¤t_pos) && getCurrentAddress(¤t_address)) {
01191 a->set_imm_eval_proc((int(*)(void *context, char **s, dword *v))ht_aviewer_symbol_to_addr, (void*)this);
01192 int want_length;
01193 analy->getDisasmStr(current_address, want_length);
01194 dialog_assemble(this, current_pos, analy->mapAddr(current_address), a, analy->disasm, analy->getDisasmStrFormatted(current_address), want_length);
01195 delete current_address;
01196 }
01197 a->done();
01198 delete a;
01199 clearmsg(msg);
01200 return;
01201 }
01202 case cmd_analyser_this_function: {
01203 if (!analy) break;
01204 Address *c;
01205 if (!getCurrentAddress(&c)) break;
01206 Location *a = analy->getFunctionByAddress(c);
01207 if (a) {
01208 gotoAddress(a->addr, this);
01209 } else {
01210 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01211 errorbox("Address %y doesn't belong to a function.", c);
01212 }
01213 delete c;
01214 clearmsg(msg);
01215 return;
01216 }
01217 case cmd_analyser_previous_label: {
01218 if (!analy) break;
01219 Address *c;
01220 if (!getCurrentAddress(&c)) break;
01221 Location *a = analy->getPreviousSymbolByAddress(c);
01222 if (a) {
01223 gotoAddress(a->addr, this);
01224 } else {
01225 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01226 errorbox("Address %y doesn't belong to a symbol.", c);
01227 }
01228 delete c;
01229 clearmsg(msg);
01230 return;
01231 }
01232 case cmd_analyser_continue: {
01233 if (!analy) break;
01234 Address *a;
01235 if (!getCurrentAddress(&a)) break;
01236 analy->continueAnalysisAt(a);
01237 delete a;
01238 analy->makeDirty();
01239 analy_sub->output->invalidateCache();
01240 clearmsg(msg);
01241 return;
01242 }
01243 case cmd_analyser_comments: {
01244 if (!analy) break;
01245 Address *current_address;
01246 if (getCurrentAddress(¤t_address)) {
01247 showComments(current_address);
01248 delete current_address;
01249 }
01250 clearmsg(msg);
01251 return;
01252 }
01253 case cmd_analyser_name_addr: {
01254 if (!analy) break;
01255 Address *addr;
01256 if (!getCurrentAddress(&addr) || !canCreateAddress(addr, true)) {
01257 delete addr;
01258 clearmsg(msg);
01259 return;
01260 }
01261 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01262 if (analy->validAddress(addr, scvalid)) {
01263 char n[255];
01264 Symbol *l = analy->getSymbolByAddress(addr);
01265
01266 if (l) ht_snprintf(n, sizeof n, "%s", l->name); else n[0] = 0;
01267 ht_snprintf(str, sizeof str, "name for address %y", addr);
01268 while (inputbox(str, "~label name:", n, 255, HISTATOM_NAME_ADDR)) {
01269 if (n[0]) {
01270 if (valid_name(n)) {
01271 char *n2 = ht_strdup(n);
01272 if (!analy->assignSymbol(addr, n2, (l)?l->type:label_unknown)) {
01273 l = analy->getSymbolByName(n);
01274 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01275 errorbox("Label '%s' already exists at address %y!", n, l->location->addr);
01276 free(n2);
01277 } else {
01278 analy->makeDirty();
01279 analy_sub->output->invalidateCache();
01280 break;
01281 }
01282 } else {
01283 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01284 if (confirmbox("'%s' is an invalid label name.\nMake valid?", n)==button_yes) {
01285 char n2[255];
01286 make_valid_name(n2, n);
01287 strcpy(n, n2);
01288 }
01289 }
01290 } else {
01291 if (l) {
01292
01293 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01294 if (confirmbox("Really delete label '%s' at address %y?", l->name, addr)==button_yes) {
01295 analy->deleteSymbol(addr);
01296 analy->makeDirty();
01297 analy_sub->output->invalidateCache();
01298 }
01299 }
01300 break;
01301 }
01302 }
01303 } else {
01304 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01305 errorbox("Address %y is invalid!", addr);
01306 }
01307 delete addr;
01308 clearmsg(msg);
01309 return;
01310 }
01311 case cmd_analyser_xrefs: {
01312 if (!analy) break;
01313 Address *a;
01314 if (!getCurrentAddress(&a)) break;
01315 showXRefs(a);
01316 delete a;
01317 clearmsg(msg);
01318 return;
01319 }
01320 case cmd_analyser_follow: {
01321 if (!analy) break;
01322 Address *c, *b;
01323 if (!getCurrentAddress(&c)) break;
01324 b = analy->createAddress();
01325 UINT bz = b->byteSize();
01326 byte *buf = (byte*)smalloc(bz);
01327 if (analy->bufPtr(c, buf, bz) != bz) break;
01328 b->getFromArray(buf);
01329 if (analy->validAddress(b, scvalid)) {
01330 gotoAddress(b, this);
01331 } else {
01332 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01333 errorbox("Follow: address %y is invalid!", b);
01334 }
01335 clearmsg(msg);
01336 return;
01337 }
01338 case cmd_analyser_follow_ex:
01339 if (!analy) break;
01340 clearmsg(msg);
01341 return;
01342 case cmd_analyser_pause_resume:
01343 if (!analy) break;
01344 pause = !pause;
01345 clearmsg(msg);
01346 return;
01347 case cmd_analyser_del_addr_bindings: {
01348 if (!analy) break;
01349 Address *addr;
01350 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01351 if (getCurrentAddress(&addr)) {
01352 if (confirmbox("Forget about address' %y bindings?\n(name, comments, xrefs, etc.)", addr)==button_yes) {
01353 analy->deleteLocation(addr);
01354 analy->makeDirty();
01355 analy_sub->output->invalidateCache();
01356 }
01357 delete addr;
01358 }
01359 clearmsg(msg);
01360 return;
01361 }
01362 case cmd_analyser_generate_output:
01363 if (!analy) break;
01364 generateOutputDialog();
01365 clearmsg(msg);
01366 return;
01367 case cmd_analyser_export_file:
01368 if (!analy) break;
01369 exportFileDialog();
01370 clearmsg(msg);
01371 return;
01372 case cmd_analyser_data_string:
01373 if (!analy) break;
01374 dataStringDialog();
01375 analy->makeDirty();
01376 analy_sub->output->invalidateCache();
01377 dirtyview();
01378 clearmsg(msg);
01379 return;
01380 case cmd_analyser_info:
01381 if (!analy) break;
01382 Address *current_address;
01383 if (!getCurrentAddress(¤t_address)) {
01384 showInfo(0);
01385 } else {
01386 showInfo(current_address);
01387 delete current_address;
01388 }
01389 dirtyview();
01390 clearmsg(msg);
01391 return;
01392 case cmd_analyser_symbols: {
01393 Address *current_address;
01394 if (!getCurrentAddress(¤t_address)) return;
01395 showSymbols(current_address);
01396 delete current_address;
01397 clearmsg(msg);
01398 return;
01399 }
01400 case cmd_edit_mode:
01401 case cmd_view_mode:
01402 if (analy) {
01403 if (edit()) {
01404 analy->setDisplayMode(ANALY_EDIT_BYTES, 0);
01405 } else {
01406 analy->setDisplayMode(0, ANALY_EDIT_BYTES);
01407 }
01408 }
01409 analy_sub->output->invalidateCache();
01410 break;
01411 case msg_file_changed: {
01412 analy_sub->output->invalidateCache();
01413 break;
01414 }
01415 case cmd_analyser_call_chain: {
01416 if (!analy) break;
01417 Address *addr;
01418 if (getCurrentAddress(&addr)) {
01419 showCallChain(addr);
01420 delete addr;
01421 }
01422 return;
01423 }
01424
01425 case cmd_analyser_srt: {
01426 Address *current_addr;
01427 if (getCurrentAddress(¤t_addr)) {
01428 test_srt(analy, current_addr);
01429 delete current_addr;
01430 }
01431 clearmsg(msg);
01432 return;
01433 }
01434 case msg_get_analyser: {
01435 msg->msg=msg_retval;
01436 msg->data1.ptr=analy;
01437 return;
01438 }
01439 case msg_set_analyser: {
01440 Analyser *a=(Analyser*)msg->data1.ptr;
01441 if (analy) {
01442 analy->done();
01443 delete analy;
01444 }
01445 a->setDisplayMode(0, ANALY_EDIT_BYTES);
01446 setAnalyser(a);
01447 analy_sub->output->invalidateCache();
01448 clearmsg(msg);
01449 one_load_hack = true;
01450 return;
01451 }
01452 case msg_postinit: {
01453 if (analy && !one_load_hack) {
01454 analy->beginAnalysis();
01455 analy_sub->output->invalidateCache();
01456 }
01457 return;
01458 }
01459 }
01460 ht_uformat_viewer::handlemsg(msg);
01461 switch (msg->msg) {
01462 case msg_draw:
01463 if (infoline) {
01464 FILEOFS a;
01465 Address *addr;
01466 if (!getCurrentAddress(&addr)) {
01467 addr = new InvalidAddress();
01468 }
01469 infoline->update(addr, (get_current_real_offset(&a)) ? a : INVALID_FILE_OFS);
01470 delete addr;
01471 }
01472 break;
01473 }
01474 }
01475
01476 bool ht_aviewer::idle()
01477 {
01478 if (!analy) return false;
01479 last_active=analy->active;
01480 if (!pause) analy->continueAnalysis();
01481 if (last_active && !analy->active) {
01482 LOG("%s: analyser finished after %d ops.", analy->getName(), analy->ops_parsed);
01483 dirtyview();
01484 app->sendmsg(msg_draw, 0);
01485 }
01486 idle_count++;
01487
01488
01489
01490
01491
01492 if (analy->active) {
01493 if (idle_count%53==0) {
01494 analy_sub->output->invalidateCache();
01495 dirtyview();
01496 app->sendmsg(msg_draw, 0);
01497 }
01498 }
01499 return last_active && !pause;
01500 }
01501
01502 bool ht_aviewer::offset_to_pos(FILEOFS ofs, viewer_pos *p)
01503 {
01504 if (!analy) return false;
01505 Address *a = analy->fileofsToAddress(ofs);
01506 bool res = convertAddressToViewerPos(a, p);
01507 delete a;
01508 return res;
01509 }
01510
01511 int ht_aviewer::ref_sel(LINE_ID *id)
01512 {
01513 if ((id->id1 | id->id2 | id->id3 |id->id4) == 0) return 0;
01514 switch (id->id4) {
01515 case 0:
01516 if (analy) {
01517 Address *a = analy->createAddress();
01518 a->getFromArray((byte*)&id->id1);
01519 bool res = gotoAddress(a, this);
01520 delete a;
01521 return res;
01522 } else {
01523 return 0;
01524 }
01525 case 1:
01526 if (analy) {
01527 Address *a = analy->createAddress();
01528 a->getFromArray((byte*)&id->id1);
01529 showXRefs(a);
01530 delete a;
01531 }
01532 return 0;
01533 }
01534 return 0;
01535 }
01536
01537 void ht_aviewer::reloadpalette()
01538 {
01539 ht_uformat_viewer::reloadpalette();
01540 if (analy_sub) analy_sub->output->changeConfig();
01541 }
01542
01543 void ht_aviewer::searchForXRefs(Address *Addr)
01544 {
01545
01546 char str[100];
01547 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_COMPACT;
01548 ht_snprintf(str, sizeof str, "%y", Addr);
01549 ht_regex_search_request *q=new ht_regex_search_request(SC_VISUAL, SF_REGEX_CASEINSENSITIVE, str);
01550 viewer_pos vp_start, vp_end;
01551 convertAddressToViewerPos(analy_sub->lowestaddress, &vp_start);
01552 convertAddressToViewerPos(analy_sub->highestaddress, &vp_end);
01553 int oldmode = analy->getDisplayMode();
01554 analy->setDisplayMode(0, -1);
01555 analy_sub->output->invalidateCache();
01556 ht_visual_search_result *r = (ht_visual_search_result *)vsearch(q, vp_start, vp_end);
01557 while (r) {
01558 Address *to;
01559 convertViewerPosToAddress(r->pos, &to);
01560 analy->addXRef(Addr, to, xrefoffset);
01561 viewer_pos na;
01562 next_logical_pos(r->pos, &na);
01563 delete to;
01564 delete r;
01565 r = (ht_visual_search_result *)vsearch(q, na, vp_end);
01566 }
01567 analy->setDisplayMode(oldmode, -1);
01568 analy->makeDirty();
01569 analy_sub->output->invalidateCache();
01570 delete q;
01571 }
01572
01573 void ht_aviewer::showCallChain(Address *Addr)
01574 {
01575 Location *a = analy->getFunctionByAddress(Addr);
01576 if (!a) return;
01577 bounds b;
01578 b.w = 60;
01579 b.h = 16;
01580 center_bounds(&b);
01581 char str[256];
01582 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01583 ht_snprintf(str, sizeof str, "call chain of address %y", Addr);
01584 ht_dialog *dialog = new ht_dialog();
01585 dialog->init(&b, str, FS_KILLER | FS_TITLE | FS_MOVE);
01586 BOUNDS_ASSIGN(b, 1, 0, 56, 10);
01587 ht_statictext *text = new ht_statictext();
01588 if (a->label) {
01589 ht_snprintf(str, sizeof str, "function %s %s", a->label->name, "is referenced by ..");
01590 } else {
01591 ht_snprintf(str, sizeof str, "address %y %s", a->addr, "is referenced by ..");
01592 }
01593 text->init(&b, str, align_left);
01594 dialog->insert(text);
01595 BOUNDS_ASSIGN(b, 1, 1, 56, 10);
01596 CallChain *cc;
01597 NEW_OBJECT(cc, CallChain, &b, analy, Addr, NULL);
01598 cc->adjust(cc->get_root(), true);
01599 dialog->insert(cc);
01600 BOUNDS_ASSIGN(b, 15, 12, 9, 2);
01601 ht_button *bt;
01602 NEW_OBJECT(bt, ht_button, &b, "O~k", button_ok);
01603 dialog->insert(bt);
01604 BOUNDS_ASSIGN(b, 35, 12, 9, 2);
01605 NEW_OBJECT(bt, ht_button, &b, "~Cancel", button_cancel);
01606 dialog->insert(bt);
01607 int r = dialog->run(false);
01608 if (r == button_ok) {
01609 ht_treeview_data tvd;
01610 dialog->databuf_get(&tvd, sizeof tvd);
01611 gotoAddress(((CallChainNode*)tvd.selected)->xa, this);
01612 }
01613 dialog->done();
01614 delete dialog;
01615 }
01616
01617 void ht_aviewer::showComments(Address *Addr)
01618 {
01619 if (!analy) return;
01620 if (!canCreateAddress(Addr, true)) {
01621 return;
01622 }
01623 CommentList *comment = analy->getComments(Addr);
01624
01625
01626 ht_mem_file *mem_file = new ht_mem_file();
01627 mem_file->init();
01628 if (comment) {
01629 int c1 = comment->count();
01630 for (int i=0; i < c1; i++) {
01631 const char *c = comment->getName(i);
01632 int len = strlen(c);
01633 if (len) mem_file->write(c, len);
01634 if (i+1<c1) mem_file->write((void*)"\n", 1);
01635 }
01636 }
01637
01638
01639
01640
01641
01642 ht_ltextfile *text_file = new ht_ltextfile();
01643 text_file->init(mem_file, true, NULL);
01644
01645
01646 bounds b;
01647 b.w = 60;
01648 b.h = 16;
01649 center_bounds(&b);
01650 ht_dialog *dialog = new ht_dialog();
01651 dialog->init(&b, "edit comments", FS_KILLER | FS_TITLE | FS_MOVE);
01652
01653 BOUNDS_ASSIGN(b, 1, 1, 55, 10);
01654 ht_text_editor *text_editor = new ht_text_editor();
01655 text_editor->init(&b, false, text_file, NULL, TEXTEDITOPT_UNDO);
01656 dialog->insert(text_editor);
01657
01658
01659
01660
01661
01662 ht_button *b1;
01663 BOUNDS_ASSIGN(b, 18, 12, 9, 2);
01664 NEW_OBJECT(b1, ht_button, &b, "O~k", button_ok);
01665 dialog->insert(b1);
01666 BOUNDS_ASSIGN(b, 32, 12, 9, 2);
01667 NEW_OBJECT(b1, ht_button, &b, "~Cancel", button_cancel);
01668 dialog->insert(b1);
01669
01670 if (dialog->run(false)==button_ok) {
01671 Location *a=analy->getLocationByAddress(Addr);
01672 if (a) analy->freeComments(a);
01673 UINT c=text_file->linecount();
01674 char buf[1024];
01675 bool empty=false;
01676 if (c==1) {
01677 UINT l = 0;
01678 text_file->getline(0, 0, buf, 1024, &l, NULL);
01679 empty=(l==0);
01680 }
01681 if (!empty) {
01682 for (UINT i=0; i<c; i++) {
01683 UINT l;
01684 if (text_file->getline(i, 0, buf, 1024, &l, NULL)) {
01685 buf[l]=0;
01686 analy->addComment(Addr, 0, buf);
01687 }
01688 }
01689 }
01690 analy->makeDirty();
01691 analy_sub->output->invalidateCache();
01692 }
01693
01694 dialog->done();
01695 delete dialog;
01696 text_file->done();
01697 delete text_file;
01698 }
01699
01700 void ht_aviewer::showInfo(Address *Addr)
01701 {
01702 bounds c, b;
01703 app->getbounds(&c);
01704 b.w=c.w*5/6;
01705 b.h=c.h*5/6;
01706 center_bounds(&b);
01707 char str[100];
01708 strcpy(str, "Analyser information");
01709 ht_dialog *dialog = new ht_dialog();
01710 dialog->init(&b, str, FS_KILLER | FS_TITLE | FS_MOVE);
01711 BOUNDS_ASSIGN(b, 1, 0, b.w-4, 10);
01712 AnalyserInformation *text = new AnalyserInformation();
01713 text->init(&b, this);
01714 dialog->insert(text);
01715
01716 dialog->run(false);
01717
01718 dialog->done();
01719 delete dialog;
01720 }
01721
01722 void ht_aviewer::showSymbols(Address *addr)
01723 {
01724 if (!analy) return;
01725
01726 Location *loc = analy->getPreviousSymbolByAddress(addr);
01727
01728 bounds b;
01729 b.w = 60;
01730 b.h = 15;
01731 center_bounds(&b);
01732 ht_dialog *dialog = new ht_dialog();
01733 dialog->init(&b, "symbols", FS_KILLER | FS_TITLE | FS_MOVE | FS_RESIZE);
01734
01735 BOUNDS_ASSIGN(b, 30, 0, 20, 1);
01736
01737
01738
01739
01740
01741
01742
01743
01744
01745 BOUNDS_ASSIGN(b, 1, 0, 56, 1);
01746 ht_listbox_title *text = new ht_listbox_title();
01747 text->init(&b);
01748 text->setText(3, "Address", "Type", "Name");
01749
01750 BOUNDS_ASSIGN(b, 1, 1, 56, 12);
01751 SymbolBox *sym = new SymbolBox();
01752 sym->init(&b, analy);
01753 if (loc && loc->label) {
01754 sym->gotoItemByEntry(sym->quickfind(loc->label->name));
01755 }
01756 dialog->insert(sym);
01757 dialog->insert(text);
01758 sym->attachTitle(text);
01759 register_idle_object(sym);
01760 int r = dialog->run(false);
01761 unregister_idle_object(sym);
01762 if (r == button_ok) {
01763
01764 ht_listbox_data d;
01765 sym->databuf_get(&d, sizeof d);
01766 if (d.cursor_ptr) gotoAddress(((Symbol *)d.cursor_ptr)->location->addr, this);
01767 }
01768 dialog->done();
01769 delete dialog;
01770 }
01771
01772 void ht_aviewer::showXRefs(Address *Addr)
01773 {
01774 ht_tree *x_tree = analy->getXRefs(Addr);
01775 if (x_tree) {
01776 restart2:
01777 bounds c, b;
01778 app->getbounds(&c);
01779 b.w = c.w*5/6;
01780 b.h = c.h*5/6;
01781 center_bounds(&b);
01782 restart:
01783 UINT bw = b.w;
01784 UINT bh = b.h;
01785 char str[256];
01786 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01787 ht_snprintf(str, sizeof str, "xrefs of address %y", Addr);
01788 ht_dialog *dialog = new ht_dialog();
01789 dialog->init(&b, str, FS_KILLER | FS_TITLE | FS_MOVE | FS_RESIZE);
01790 BOUNDS_ASSIGN(b, 1, 0, bw-4, 1);
01791 ht_listbox_title *text = new ht_listbox_title();
01792 text->init(&b);
01793 text->setText(3, "xref to", "type", "from function");
01794 b.y = 1;
01795 b.h = bh-6;
01796 ht_text_listbox *list;
01797 NEW_OBJECT(list, ht_text_listbox, &b, 3, 2);
01798 BOUNDS_ASSIGN(b, 2, bh-4, 26, 2);
01799 ht_button *search_for_xrefs;
01800 NEW_OBJECT(search_for_xrefs, ht_button, &b, "~Search for more XRefs", 666);
01801 search_for_xrefs->growmode = MK_GM(GMH_LEFT, GMV_BOTTOM);
01802 BOUNDS_ASSIGN(b, 29, bh-4, 11, 2);
01803 ht_button *delete_xref;
01804 NEW_OBJECT(delete_xref, ht_button, &b, "~Delete", 667);
01805 delete_xref->growmode = MK_GM(GMH_LEFT, GMV_BOTTOM);
01806
01807 BOUNDS_ASSIGN(b, 41, bh-4, 10, 2);
01808 ht_button *new_xref;
01809 NEW_OBJECT(new_xref, ht_button, &b, "~Add", 668);
01810 new_xref->growmode = MK_GM(GMH_LEFT, GMV_BOTTOM);
01811 char str2[1024];
01812 AddrXRef *x;
01813 Address *xa = (Address*)x_tree->enum_next((Object**)&x, NULL);
01814 int xcount=0;
01815 while (xa) {
01816 xcount++;
01817 ht_snprintf(str, sizeof str, "%y", xa);
01818 Location *a = analy->getFunctionByAddress(xa);
01819 const char *func = analy->getSymbolNameByLocation(a);
01820 if (func) {
01821 int d=0;
01822 xa->difference(d, a->addr);
01823 char sign='+';
01824 if (d<0) {
01825 d=-d;
01826 sign='-';
01827 }
01828 ht_snprintf(str2, sizeof str2, "%s%c%x", func, sign, d);
01829 } else {
01830 strcpy(str2, "?");
01831 }
01832 list->insert_str((int)xa, str, xref_type(x->type), str2);
01833 xa = (Address*)x_tree->enum_next((Object**)&x, xa);
01834 }
01835 list->attachTitle(text);
01836 list->update();
01837 dialog->insert(text);
01838 dialog->insert(list);
01839 dialog->insert(search_for_xrefs);
01840 dialog->insert(delete_xref);
01841 dialog->insert(new_xref);
01842 int r = dialog->run(false);
01843 ht_listbox_data data;
01844 list->databuf_get(&data, sizeof data);
01845 switch (r) {
01846 case 666:
01847 searchForXRefs(Addr);
01848 break;
01849 case 667:
01850 if (xcount) {
01851 analy->deleteXRef(Addr, (Address*)list->getID(data.cursor_ptr));
01852 analy->makeDirty();
01853 analy_sub->output->invalidateCache();
01854 }
01855 break;
01856 case 668: {
01857 char result[256];
01858 ht_snprintf(str, sizeof str, "add xref from %y", Addr);
01859 result[0] = 0;
01860 while (inputbox(str, "to ~address: ", result, 255, HISTATOM_GOTO)) {
01861 viewer_pos res_pos;
01862 if (!string_to_pos(result, &res_pos)) {
01863 errorbox(globalerror);
01864 continue;
01865 }
01866 Address *a;
01867 if (!convertViewerPosToAddress(res_pos, &a)) {
01868 errorbox("invalid address");
01869 delete a;
01870 continue;
01871 }
01872 if (!analy->addXRef(Addr, a, xrefoffset)) {
01873
01874 }
01875 delete a;
01876 analy->makeDirty();
01877 analy_sub->output->invalidateCache();
01878 break;
01879 }
01880 break;
01881 }
01882 case button_ok:
01883 if (xcount) gotoAddress((Address*)list->getID(data.cursor_ptr), this);
01884 break;
01885 }
01886 dialog->getbounds(&b);
01887 dialog->done();
01888 delete dialog;
01889 if ((r >= 666) && (r <= 668)) goto restart;
01890 } else {
01891 if (confirmbox("No xrefs for address %y!\nSearch for xrefs?", Addr)==button_yes) {
01892 searchForXRefs(Addr);
01893 x_tree = analy->getXRefs(Addr);
01894 if (x_tree) goto restart2;
01895 }
01896 }
01897 }
01898
01899 int ht_aviewer::func_handler(eval_scalar *result, char *name, eval_scalarlist *params)
01900 {
01901 eval_func myfuncs[] = {
01902 {"addressOf", (void*)&aviewer_func_address_of, {SCALAR_STR}},
01903 {"fileofs", (void*)&aviewer_func_fileofs, {SCALAR_INT}},
01904 {"addr", (void*)&aviewer_func_addr, {SCALAR_STR}},
01905 {NULL}
01906 };
01907 if (std_eval_func_handler(result, name, params, myfuncs)) return 1;
01908 return ht_uformat_viewer::func_handler(result, name, params);
01909 }
01910
01911 int ht_aviewer::symbol_handler(eval_scalar *result, char *name)
01912 {
01913 qword v;
01914 viewer_pos vp;
01915 Address *w;
01916 if (*name == '@') {
01917 name++;
01918 if (bnstr(&name, &v, 10)) {
01919 if (*name) return 0;
01920
01921 if (!offset_to_pos(QWORD_GET_LO(v), &vp)) {
01922 set_eval_error("invalid offset: %08x", v);
01923 return 0;
01924 }
01925 convertViewerPosToAddress(vp, &w);
01926 qword b = to_qword(0);
01927 w->putIntoArray((byte*)&b);
01928 delete w;
01929 scalar_create_int_q(result, b);
01930 return 1;
01931 }
01932
01933 } else {
01934 if (strcmp(name, "$")==0) {
01935 if (getCurrentAddress(&w)) {
01936 qword b = to_qword(0);
01937 w->putIntoArray((byte*)&b);
01938 scalar_create_int_q(result, b);
01939 delete w;
01940 return 1;
01941 } else {
01942 delete w;
01943 return 0;
01944 }
01945 }
01946 Symbol *l = analy->getSymbolByName(name);
01947 if (l) {
01948 w=l->location->addr;
01949 qword b;
01950 w->putIntoArray((byte*)&b);
01951 scalar_create_int_q(result, b);
01952 return 1;
01953 }
01954 }
01955 return ht_uformat_viewer::symbol_handler(result, name);
01956 }
01957
01958 bool ht_aviewer::qword_to_pos(qword q, viewer_pos *pos)
01959 {
01960 if (!analy) return false;
01961 Address *a=analy->createAddress();
01962
01963 if (a->byteSize()==8) {
01964 a->getFromArray((byte*)&q);
01965 } else {
01966 dword ii = QWORD_GET_INT(q);
01967 a->getFromArray((byte*)&ii);
01968 }
01969 if (analy->validAddress(a, scvalid)) {
01970 bool res = convertAddressToViewerPos(a, pos);
01971 delete a;
01972 return res;
01973 } else {
01974 global_analyser_address_string_format = ADDRESS_STRING_FORMAT_LEADING_ZEROS;
01975 ht_snprintf(globalerror, GLOBAL_ERROR_SIZE, "address %y is invalid", a);
01976 }
01977 delete a;
01978 return false;
01979 }
01980
01981
01982
01983
01984
01985 void ht_analy_sub::init(ht_streamfile *file, ht_aviewer *A, Analyser *analyser, Address *Lowestaddress, Address *Highestaddress)
01986 {
01987 ht_sub::init(file);
01988 aviewer = A;
01989 analy = analyser;
01990 output = new AnalyserHTOutput();
01991 ((AnalyserHTOutput*)output)->init(analy);
01992 lowestaddress = (Address *)Lowestaddress->duplicate();
01993 highestaddress = (Address *)Highestaddress->duplicate();
01994 }
01995
01996 void ht_analy_sub::done()
01997 {
01998 delete lowestaddress;
01999 delete highestaddress;
02000 output->done();
02001 delete output;
02002 ht_sub::done();
02003 }
02004
02005 bool ht_analy_sub::closest_line_id(LINE_ID *line_id)
02006 {
02007 if (!prev_line_id(line_id, 1)) {
02008 if (!next_line_id(line_id, 1)) {
02009 first_line_id(line_id);
02010 }
02011 }
02012 return true;
02013 }
02014
02015 bool ht_analy_sub::convert_ofs_to_id(const FILEOFS offset, LINE_ID *line_id)
02016 {
02017 viewer_pos a;
02018 if (!uformat_viewer->offset_to_pos(offset, &a)) return false;
02019 *line_id = a.u.line_id;
02020 return true;
02021 }
02022
02023 void ht_analy_sub::first_line_id(LINE_ID *line_id)
02024 {
02025 clear_line_id(line_id);
02026 lowestaddress->putIntoArray((byte*)&(line_id->id2));
02027 }
02028
02029 bool ht_analy_sub::getline(char *line, const LINE_ID line_id)
02030 {
02031 if (!analy) return false;
02032 Address *a = analy->createAddress();
02033 a->getFromArray((byte*)&(line_id.id2));
02034 bool res = output->getLineString(line, 1024, a, (int)line_id.id1);
02035 delete a;
02036 return res;
02037 }
02038
02039 void ht_analy_sub::last_line_id(LINE_ID *line_id)
02040 {
02041 clear_line_id(line_id);
02042 highestaddress->putIntoArray((byte*)&(line_id->id2));
02043 }
02044
02045 int ht_analy_sub::next_line_id(LINE_ID *line_id, int n)
02046 {
02047 if (!analy) return false;
02048 Address *a = analy->createAddress();
02049 a->getFromArray((byte*)&(line_id->id2));
02050 int line = line_id->id1;
02051 int res = output->nextLine(a, line, n, highestaddress);
02052 if (res) {
02053 line_id->id1 = line;
02054 a->putIntoArray((byte*)&(line_id->id2));
02055 }
02056 delete a;
02057 return res;
02058 }
02059
02060 int ht_analy_sub::prev_line_id(LINE_ID *line_id, int n)
02061 {
02062 if (!analy) return false;
02063 Address *a = analy->createAddress();
02064 a->getFromArray((byte*)&(line_id->id2));
02065 int line = line_id->id1;
02066 int res = output->prevLine(a, line, n, lowestaddress);
02067 if (res) {
02068 line_id->id1 = line;
02069 a->putIntoArray((byte*)&(line_id->id2));
02070 }
02071 delete a;
02072 return res;
02073 }
02074
02075 ht_search_result *ht_analy_sub::search(ht_search_request *search, FILEOFS start, FILEOFS end)
02076 {
02077
02078 Address *st = NULL;
02079 ht_search_result *r = NULL;
02080 while (!r) {
02081 st = (Address *)analy->initialized->findNext(st);
02082 if (!st) break;
02083 area_s *s = analy->initialized->getArea(st);
02084 if (!s) break;
02085 st = (Address *)s->end;
02086 FILEOFS fstart, fend;
02087 dword fsize;
02088 viewer_pos vp_start, vp_end;
02089 aviewer->convertAddressToViewerPos((Address *)s->start, &vp_start);
02090 if (!aviewer->pos_to_offset(vp_start, &fstart)) assert(0);
02091 Address *send = (Address *)s->end->duplicate();
02092 send->add(-1);
02093 aviewer->convertAddressToViewerPos(send, &vp_end);
02094 delete send;
02095 if (!aviewer->pos_to_offset(vp_end, &fend)) assert(0);
02096 fsize = fend-fstart;
02097 if ((search->search_class==SC_PHYSICAL) && (search->type==ST_EXPR)) {
02098 r = linear_expr_search(search, start, end, this, uformat_viewer, fstart, fsize);
02099 } else if ((search->search_class==SC_PHYSICAL) && (search->type==ST_FXBIN)) {
02100 r = linear_bin_search(search, start, end, file, fstart, fsize);
02101 }
02102 }
02103 return r;
02104 }
02105
02106 void ht_analy_sub::setAnalyser(Analyser *Analy)
02107 {
02108 analy = Analy;
02109 output->analy = Analy;
02110 output->invalidateCache();
02111 }
02112
02113