Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

blockop.cc

Go to the documentation of this file.
00001 /*
00002  *      HT Editor
00003  *      blockop.cc
00004  *
00005  *      Copyright (C) 1999-2002 Stefan Weyergraf (stefan@weyergraf.de)
00006  *
00007  *      This program is free software; you can redistribute it and/or modify
00008  *      it under the terms of the GNU General Public License version 2 as
00009  *      published by the Free Software Foundation.
00010  *
00011  *      This program is distributed in the hope that it will be useful,
00012  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *      GNU General Public License for more details.
00015  *
00016  *      You should have received a copy of the GNU General Public License
00017  *      along with this program; if not, write to the Free Software
00018  *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019  */
00020 
00021 #include <stdlib.h>
00022 #include <string.h>
00023 
00024 #include "blockop.h"
00025 #include "cmds.h"
00026 #include "htatom.h"
00027 #include "htctrl.h"
00028 #include "htendian.h"
00029 #include "hteval.h"
00030 #include "htexcept.h"
00031 #include "hthist.h"
00032 #include "htiobox.h"
00033 #include "htkeyb.h"
00034 #include "htstring.h"
00035 #include "process.h"
00036 #include "snprintf.h"
00037 
00038 #include "evalx.h"
00039 
00040 /*
00041  *      CLASS ht_blockop_dialog
00042  */
00043 
00044 void ht_blockop_dialog::init(bounds *b, FILEOFS pstart, FILEOFS pend, ht_list *history)
00045 {
00046         ht_dialog::init(b, "operate on block", FS_TITLE | FS_KILLER | FS_MOVE);
00047         bounds c;
00048 
00049         bool prerange=(pend>pstart);
00050 
00051         ht_statictext *text;
00052 
00053         ht_label *s;
00054         
00055         ht_list *addrhist=(ht_list*)find_atom(HISTATOM_GOTO);
00056 /* start */
00057         c=*b;
00058         c.h=1;
00059         c.w=13;
00060         c.x=7;
00061         c.y=1;
00062         start=new ht_strinputfield();
00063         start->init(&c, 64, addrhist);
00064         insert(start);
00065         if (prerange) {
00066                 char t[16];
00067                 ht_snprintf(t, sizeof t, "0x%x", pstart);
00068                 ht_inputfield_data d;
00069                 d.textlen=strlen(t);
00070                 d.text=(byte*)t;
00071                 start->databuf_set(&d, sizeof d);
00072         }
00073 
00074 /* start_desc */
00075         c.x=1;
00076         c.w=6;
00077         s=new ht_label();
00078         s->init(&c, "~start", start);
00079         insert(s);
00080         
00081 /* end */
00082         c=*b;
00083         c.h=1;
00084         c.w=13;
00085         c.x=27;
00086         c.y=1;
00087         end=new ht_strinputfield();
00088         end->init(&c, 64, addrhist);
00089         insert(end);
00090         if (prerange) {
00091                 char t[16];
00092                 ht_snprintf(t, sizeof t, "0x%x", pend);
00093                 ht_inputfield_data d;
00094                 d.textlen=strlen(t);
00095                 d.text=(byte*)t;
00096                 end->databuf_set(&d, sizeof d);
00097         }
00098 
00099 
00100 /* end_desc */
00101         c.x=23;
00102         c.w=3;
00103         s=new ht_label();
00104         s->init(&c, "~end", end);
00105         insert(s);
00106 
00107 /* mode */
00108         c=*b;
00109         c.h=1;
00110         c.w=16;
00111         c.x=7;
00112         c.y=3;
00113         mode=new ht_listpopup();
00114         mode->init(&c);
00115         mode->insertstring("byte (8-bit)");
00116         mode->insertstring("word (16-bit)");
00117         mode->insertstring("dword (32-bit)");
00118         mode->insertstring("string");
00119         insert(mode);
00120 
00121 /* mode_desc */
00122         c.x=1;
00123         c.w=12;
00124         c.y=3;
00125         s=new ht_label();
00126         s->init(&c, "~mode", mode);
00127         insert(s);
00128 
00129 /* action_expl */
00130         c=*b;
00131         c.x=1;
00132         c.y=5;
00133         c.w-=3;
00134         c.h=1;
00135         text=new ht_statictext();
00136         text->init(&c, "set each element to", align_left);
00137         insert(text);
00138         
00139 /* action */
00140         ht_list *ehist=(ht_list*)find_atom(HISTATOM_EVAL_EXPR);
00141 
00142         c=*b;
00143         c.h=1;
00144         c.w=40;
00145         c.x=7;
00146         c.y=6;
00147         action=new ht_strinputfield();
00148         action->init(&c, 4096, ehist);
00149         insert(action);
00150 
00151 /* action_desc */
00152         c.x=1;
00153         c.w=27;
00154         c.y=6;
00155         s=new ht_label();
00156         s->init(&c, "e~xpr", action);
00157         insert(s);
00158 
00159 /* help */
00160 /*      c=*b;
00161         c.x=1;
00162         c.y=8;
00163         c.w-=c.x+2;
00164         c.h-=c.y+2;
00165         text=new ht_statictext();
00166         text->init(&c,
00167                 "special vars:          special funcs:\n"
00168                 "o - file offset        readbyte(ofs)\n"
00169                 "i - iteration index    readstring(ofs, n)", align_left);
00170         insert(text);*/
00171 /* functions */
00172         ht_button *bhelp = new ht_button();
00173         c = *b;
00174         c.x = 1;
00175         c.y = 8;
00176         c.w = 11;
00177         c.h = 1;
00178         bhelp->init(&c, "~Functions", 100);
00179         insert(bhelp);
00180 }
00181 
00182 void ht_blockop_dialog::done()
00183 {
00184         ht_dialog::done();
00185 }
00186 
00187 struct ht_blockop_dialog_data {
00188         ht_inputfield_data start;
00189         ht_inputfield_data end;
00190         ht_listpopup_data mode;
00191         ht_inputfield_data action;
00192 };
00193 
00194 /*
00195  *   blockop_dialog
00196  */
00197 
00198 static dword blockop_i;
00199 static dword blockop_o;
00200 static bool blockop_expr_is_const;
00201 
00202 static int blockop_symbol_eval(eval_scalar *r, char *symbol)
00203 {
00204         if (strcmp(symbol, "i")==0) {
00205                 r->type=SCALAR_INT;
00206                 r->scalar.integer.value=to_qword(blockop_i);
00207                 r->scalar.integer.type=TYPE_UNKNOWN;
00208                 blockop_expr_is_const=false;
00209                 return 1;
00210         } else if (strcmp(symbol, "o")==0) {
00211                 r->type=SCALAR_INT;
00212                 r->scalar.integer.value=to_qword(blockop_o);
00213                 r->scalar.integer.type=TYPE_UNKNOWN;
00214                 blockop_expr_is_const=false;
00215                 return 1;
00216         }
00217         return 0;
00218 }
00219 
00220 static int func_readint(eval_scalar *result, eval_int *offset, int size, endianess e)
00221 {
00222         ht_streamfile *f=(ht_streamfile*)eval_get_context();
00223         byte buf[4];
00224         int read = 0;
00225         if ((f->seek(QWORD_GET_INT(offset->value))!=0) ||
00226         ((read = f->read(buf, size)) != size)) {
00227                 set_eval_error("i/o error (requested %d, read %d from ofs %08x)", size, read, offset->value);
00228                 return 0;
00229         }
00230         scalar_create_int_c(result, create_host_int(buf, size, e));
00231         return 1;
00232 }
00233 
00234 static int func_readbyte(eval_scalar *result, eval_int *offset)
00235 {
00236         return func_readint(result, offset, 1, little_endian);
00237 }
00238 
00239 static int func_read16le(eval_scalar *result, eval_int *offset)
00240 {
00241         return func_readint(result, offset, 2, little_endian);
00242 }
00243 
00244 static int func_read32le(eval_scalar *result, eval_int *offset)
00245 {
00246         return func_readint(result, offset, 4, little_endian);
00247 }
00248 
00249 static int func_read16be(eval_scalar *result, eval_int *offset)
00250 {
00251         return func_readint(result, offset, 2, big_endian);
00252 }
00253 
00254 static int func_read32be(eval_scalar *result, eval_int *offset)
00255 {
00256         return func_readint(result, offset, 4, big_endian);
00257 }
00258 
00259 static int func_readstring(eval_scalar *result, eval_int *offset, eval_int *len)
00260 {
00261         ht_streamfile *f=(ht_streamfile*)eval_get_context();
00262 
00263         UINT l=QWORD_GET_INT(len->value);
00264         void *buf=malloc(l);    /* FIXME: may be too slow... */
00265 
00266         if (buf) {
00267                 eval_str s;
00268                 UINT c = 0;
00269                 if ((f->seek(QWORD_GET_INT(offset->value))!=0) || ( (c=f->read(buf, l)) !=l)) {
00270                         free(buf);
00271                         set_eval_error("i/o error (requested %d, read %d from ofs %08x)", l, c, offset->value);
00272                         return 0;
00273                 }
00274                 s.value=(char*)buf;
00275                 s.len=l;
00276                 scalar_create_str(result, &s);
00277                 free(buf);
00278                 return 1;
00279         }
00280         set_eval_error("out of memory");
00281         return 0;
00282 }
00283 
00284 static int blockop_func_eval(eval_scalar *result, char *name, eval_scalarlist *params)
00285 {
00286 /* FIXME: non-constant funcs (e.g. rand()) should
00287    set blockop_expr_is_const to false */
00288         eval_func myfuncs[] = {
00289                 {"readbyte", (void*)&func_readbyte, {SCALAR_INT}},
00290                 {"read16le", (void*)&func_read16le, {SCALAR_INT}},
00291                 {"read32le", (void*)&func_read32le, {SCALAR_INT}},
00292                 {"read16be", (void*)&func_read16be, {SCALAR_INT}},
00293                 {"read32be", (void*)&func_read32be, {SCALAR_INT}},
00294                 {"readstring", (void*)&func_readstring, {SCALAR_INT, SCALAR_INT}},
00295                 {NULL}
00296         };
00297         
00298         blockop_expr_is_const=false;
00299                 
00300         return std_eval_func_handler(result, name, params, myfuncs);
00301 }
00302 
00303 /*
00304  *      BLOCKOP STRING
00305  */
00306 
00307 class ht_blockop_str_context: public Object {
00308 public:
00309         ht_streamfile *file;
00310 
00311         FILEOFS ofs;
00312         UINT len;
00313 
00314         UINT size;
00315         bool netendian;
00316 
00317         char *action;
00318 
00319         UINT i;
00320         FILEOFS o;
00321 
00322         bool expr_const;
00323         eval_str v;
00324 
00325         ~ht_blockop_str_context()
00326         {
00327                 free(action);
00328                 if (expr_const) string_destroy(&v);
00329         }
00330 };
00331 
00332 Object *create_blockop_str_context(ht_streamfile *file, FILEOFS ofs, UINT len, UINT size, bool netendian, char *action)
00333 {
00334         ht_blockop_str_context *ctx = new ht_blockop_str_context();
00335         ctx->file = file;
00336         ctx->ofs = ofs;
00337         ctx->len = len;
00338         ctx->size = size;
00339         ctx->netendian = netendian;
00340         ctx->action = strdup(action);
00341 
00342         ctx->i = 0;
00343         ctx->o = ofs;
00344 
00345         blockop_expr_is_const = true;
00346 
00347         // test if first eval works
00348         blockop_i = ctx->i;
00349         blockop_o = ctx->o;
00350         eval_scalar r;
00351         if (!eval(&r, action, blockop_func_eval, blockop_symbol_eval, file)) {
00352                 char *s;
00353                 int p;
00354                 get_eval_error(&s, &p);
00355                 throw new ht_io_exception("error evaluating '%s': %s at %d", action, s, p);
00356         }
00357 
00358         ctx->expr_const = blockop_expr_is_const;
00359 
00360         if (ctx->expr_const) {
00361                 scalar_context_str(&r, &ctx->v);
00362         }
00363         scalar_destroy(&r);
00364         return ctx;
00365 }
00366 
00367 #define BLOCKOP_STR_MAX_ITERATIONS 1024
00368 bool blockop_str_process(Object *context, ht_text *progress_indicator)
00369 {
00370         char status[64];
00371         ht_blockop_str_context *ctx = (ht_blockop_str_context*)context;
00372         if (ctx->expr_const) {
00373                 ht_snprintf(status, sizeof status, "operating (constant string)... %d%% complete", (int)(((double)(ctx->o-ctx->ofs)) * 100 / ctx->len));
00374                 progress_indicator->settext(status);
00375                 for (UINT i=0; i < BLOCKOP_STR_MAX_ITERATIONS; i++)
00376                 if (ctx->o < ctx->ofs + ctx->len) {
00377                         UINT s = ctx->v.len;
00378                         if (ctx->o + s > ctx->ofs + ctx->len) s = ctx->ofs + ctx->len - ctx->o;
00379                         
00380                         ctx->file->seek(ctx->o);
00381                         if (ctx->file->write(ctx->v.value, s)!=s) {
00382                                 throw new ht_io_exception("blockop_str(): write error at pos %08x, size %08x", ctx->o, s);
00383                         }
00384                         ctx->o += s;
00385                 } else {
00386                         return false;
00387                 }
00388         } else {
00389                 ht_snprintf(status, sizeof status, "operating (variable string)... %d%% complete", (int)(((double)(ctx->o-ctx->ofs)) * 100 / ctx->len));
00390                 progress_indicator->settext(status);
00391                 eval_scalar r;
00392                 eval_str sr;
00393                 for (UINT i=0; i < BLOCKOP_STR_MAX_ITERATIONS; i++)
00394                 if (ctx->o < ctx->ofs + ctx->len) {
00395                         blockop_i = ctx->i;
00396                         blockop_o = ctx->o;
00397                         if (!eval(&r, ctx->action, blockop_func_eval, blockop_symbol_eval, ctx->file)) {
00398                                 char *s;
00399                                 int p;
00400                                 get_eval_error(&s, &p);
00401                                 throw new ht_io_exception("error evaluating '%s': %s at %d", ctx->action, s, p);
00402                         }
00403                         scalar_context_str(&r, &sr);
00404                         scalar_destroy(&r);
00405 
00406                         UINT s = sr.len;
00407                         if (ctx->o+s > ctx->ofs+ctx->len) s = ctx->ofs+ctx->len-ctx->o;
00408 
00409                         ctx->file->seek(ctx->o);
00410                         if (ctx->file->write(sr.value, s)!=s) {
00411                                 throw new ht_io_exception("blockop_str(): write error at pos %08x, size %08x", ctx->o, s);
00412                         }
00413                         string_destroy(&sr);
00414                         ctx->o += s;
00415                         ctx->i++;
00416                 } else {
00417                         return false;
00418                 }
00419         }
00420         return true;
00421 }
00422 
00423 /*
00424  *      BLOCKOP INTEGER
00425  */
00426 
00427 class ht_blockop_int_context: public Object {
00428 public:
00429         ht_streamfile *file;
00430 
00431         FILEOFS ofs;
00432         UINT len;
00433 
00434         UINT size;
00435         endianess endian;
00436 
00437         char *action;
00438 
00439         UINT i;
00440         FILEOFS o;
00441 
00442         bool expr_const;
00443         UINT v;
00444 
00445         ~ht_blockop_int_context()
00446         {
00447                 free(action);
00448         }
00449 };
00450 
00451 Object *create_blockop_int_context(ht_streamfile *file, FILEOFS ofs, UINT len, UINT size, endianess endian, char *action)
00452 {
00453         ht_blockop_int_context *ctx = new ht_blockop_int_context();
00454         ctx->file = file;
00455         ctx->ofs = ofs;
00456         ctx->len = len;
00457         ctx->size = size;
00458         ctx->endian = endian;
00459         ctx->action = ht_strdup(action);
00460 
00461         ctx->i = 0;
00462         ctx->o = ofs;
00463 
00464         blockop_expr_is_const = true;
00465 
00466         // test if first eval works
00467         blockop_i = ctx->i;
00468         blockop_o = ctx->o;
00469         eval_scalar r;
00470         eval_int ir;
00471         if (!eval(&r, action, blockop_func_eval, blockop_symbol_eval, file)) {
00472                 char *s;
00473                 int p;
00474                 get_eval_error(&s, &p);
00475                 throw new ht_io_exception("error evaluating '%s': %s at %d", action, s, p);
00476         }
00477 
00478         ctx->expr_const = blockop_expr_is_const;
00479         
00480         if (ctx->expr_const) {
00481                 scalar_context_int(&r, &ir);
00482                 ctx->v = QWORD_GET_INT(ir.value);
00483         }
00484         scalar_destroy(&r);
00485         return ctx;
00486 }
00487 
00488 #define BLOCKOP_INT_MAX_ITERATIONS      1024
00489 bool blockop_int_process(Object *context, ht_text *progress_indicator)
00490 {
00491         ht_blockop_int_context *ctx = (ht_blockop_int_context*)context;
00492         char status[64];
00493         if (ctx->expr_const) {          
00494                 ht_snprintf(status, sizeof status, "operating (constant integer)... %d%% complete", (int)(((double)(ctx->o-ctx->ofs)) * 100 / ctx->len));
00495                 progress_indicator->settext(status);
00496                 byte ibuf[4];
00497                 create_foreign_int(ibuf, ctx->v, ctx->size, ctx->endian);
00498                 ctx->file->seek(ctx->o);
00499                 for (UINT i=0; i < BLOCKOP_INT_MAX_ITERATIONS; i++)
00500                 if (ctx->o < ctx->ofs + ctx->len) {
00501                         UINT s = ctx->size;
00502                         if (ctx->o + s > ctx->ofs + ctx->len) s = ctx->ofs + ctx->len - ctx->o;
00503                         if (ctx->file->write(ibuf, s)!=s) {
00504                                 throw new ht_io_exception("blockop_int(): write error at pos %08x, size %08x", ctx->o, s);
00505                         }
00506                         ctx->o += s;
00507                 } else {
00508                         return false;
00509                 }
00510         } else {
00511                 ht_snprintf(status, sizeof status, "operating (variable integer)... %d%% complete", (int)(((double)(ctx->o-ctx->ofs)) * 100 / ctx->len));
00512                 progress_indicator->settext(status);
00513                 eval_scalar r;
00514                 eval_int ir;
00515                 for (UINT i=0; i < BLOCKOP_INT_MAX_ITERATIONS; i++)
00516                 if (ctx->o < ctx->ofs + ctx->len) {
00517                         blockop_o = ctx->o;
00518                         blockop_i = ctx->i;
00519                         if (!eval(&r, ctx->action, blockop_func_eval, blockop_symbol_eval, ctx->file)) {
00520                                 char *s;
00521                                 int p;
00522                                 get_eval_error(&s, &p);
00523                                 throw new ht_io_exception("error evaluating '%s': %s at %d", ctx->action, s, p);
00524                         }
00525                         scalar_context_int(&r, &ir);
00526                         scalar_destroy(&r);
00527                         ctx->v=QWORD_GET_INT(ir.value);
00528 
00529                         UINT s = ctx->size;
00530                         if (ctx->o+s > ctx->ofs+ctx->len) s = ctx->ofs+ctx->len-ctx->o;
00531 
00532                         byte ibuf[4];
00533                         create_foreign_int(ibuf, ctx->v, ctx->size, ctx->endian);
00534                         if ((ctx->file->seek(ctx->o) != 0) || (ctx->file->write(ibuf, s)!=s)) {
00535                                 throw new ht_io_exception("blockop_int(): write error at pos %08x, size %08x", ctx->o, s);
00536                         }
00537                         ctx->o += s;
00538                         ctx->i++;
00539                 } else {
00540                         return false;
00541                 }
00542         }
00543         return true;
00544 }
00545 
00546 /*
00547  *
00548  */
00549 
00550 bool format_string_to_offset_if_avail(ht_format_viewer *format, byte *string, int stringlen, const char *string_desc, FILEOFS *ofs)
00551 {
00552         if (string && *string && stringlen<64) {
00553                 char str[64];
00554                 memmove(str, string, stringlen);
00555                 str[stringlen]=0;
00556                 if (!format->string_to_offset(str, ofs)) {
00557                         errorbox("%s: '%s' doesn't seem to be a valid offset", string_desc, &str);
00558                         return false;
00559                 }
00560                 return true;
00561         }
00562         return false;
00563 }
00564                 
00565                 
00566 void blockop_dialog(ht_format_viewer *format, FILEOFS pstart, FILEOFS pend)
00567 {
00568         bounds b;
00569         b.w=65;
00570         b.h=15;
00571         b.x=(screen->size.w-b.w)/2;
00572         b.y=(screen->size.h-b.h)/2;
00573         
00574         ht_blockop_dialog *d=new ht_blockop_dialog();
00575         d->init(&b, pstart, pend, 0);
00576         bool run = true;
00577         int r;
00578         while (run && ((r = d->run(false)) != button_cancel)) {
00579                 switch (r) {
00580                         case 100:
00581                                 dialog_eval_help(blockop_func_eval, blockop_symbol_eval, NULL);
00582                                 break;
00583                         default: 
00584                 {
00585                 ht_blockop_dialog_data t;
00586                 d->databuf_get(&t, sizeof t);
00587                 
00588                 ht_streamfile *file=format->get_file();
00589 
00590                 baseview->sendmsg(cmd_edit_mode_i, file, NULL);
00591                 
00592                 if (file->get_access_mode() & FAM_WRITE) {
00593                         FILEOFS start=pstart, end=pend;
00594 
00595                         if (format_string_to_offset_if_avail(format, t.start.text, t.start.textlen, "start", &start) &&
00596                         format_string_to_offset_if_avail(format, t.end.text, t.end.textlen, "end", &end)) {
00597                                 if (end > start) {
00598                                         int esize=0;
00599                                         int esizes[3]={4, 2, 1};
00600                                         switch (t.mode.cursor_pos) {
00601                                                 /* element type: byte */
00602                                                 case 0: esize++;
00603                                                 /* element type: word */
00604                                                 case 1: esize++;
00605                                                 /* element type: dword */
00606                                                 case 2: {
00607                                                         char a[4096];
00608                                                         bin2str(a, t.action.text, MIN(sizeof a, t.action.textlen));
00609                                                         insert_history_entry((ht_list*)find_atom(HISTATOM_EVAL_EXPR), a, NULL);
00610                                                 
00611                                                         char addr[128];
00612                                                         bin2str(addr, t.start.text, MIN(sizeof addr, t.start.textlen));
00613                                                         insert_history_entry((ht_list*)find_atom(HISTATOM_GOTO), addr, NULL);
00614                                                         bin2str(addr, t.end.text, MIN(sizeof addr, t.end.textlen));
00615                                                         insert_history_entry((ht_list*)find_atom(HISTATOM_GOTO), addr, NULL);
00616                                                 
00617                                                         esize = esizes[esize];
00618                                                         Object *ctx = NULL;
00619                                                         try {
00620                                                                 ctx = create_blockop_int_context(file, start, end-start, esize, little_endian, a);
00621                                                                 if (ctx) {
00622                                                                         /*bool b = */execute_process(blockop_int_process, ctx);
00623                                                                 }
00624                                                         } catch (ht_exception *e) {
00625                                                                 errorbox("error: %s", e->what());
00626                                                         }
00627                                                         if (ctx) delete ctx;
00628                                                         break;
00629                                                 }
00630                                                 /* element type: string */
00631                                                 case 3: {
00632                                                         char a[256];
00633                                                         bin2str(a, t.action.text, MIN(sizeof a, t.action.textlen));
00634                                                         insert_history_entry((ht_list*)find_atom(HISTATOM_EVAL_EXPR), a, NULL);
00635 
00636                                                         char addr[128];
00637                                                         bin2str(addr, t.start.text, MIN(sizeof addr, t.start.textlen));
00638                                                         insert_history_entry((ht_list*)find_atom(HISTATOM_GOTO), addr, NULL);
00639                                                         bin2str(addr, t.end.text, MIN(sizeof addr, t.end.textlen));
00640                                                         insert_history_entry((ht_list*)find_atom(HISTATOM_GOTO), addr, NULL);
00641 
00642                                                         Object *ctx = NULL;
00643                                                         try {
00644                                                                 ctx = create_blockop_str_context(file, start, end-start, esize, little_endian, a);
00645                                                                 if (ctx) {
00646                                                                         /*bool b = */execute_process(blockop_str_process, ctx);
00647                                                                 }
00648                                                         } catch (ht_exception *e) {
00649                                                                 errorbox("error: %s", e->what());
00650                                                         }
00651                                                         if (ctx) delete ctx;
00652                                                         break;
00653                                                 }
00654                                                 default:
00655                                                         errorbox("mode %d not supported", t.mode.cursor_pos);
00656                                         }                                                       
00657                                 } else {
00658                                         errorbox("end offset must be greater than start offset");
00659                                 }
00660                         }
00661                 }
00662                 run = false;
00663                 }
00664                 }
00665         }
00666 
00667         d->done();
00668         delete d;
00669 }
00670 

Generated on Fri May 7 21:15:29 2004 by doxygen 1.3.5