Increase the size of the 'message' buffer, which is currently
[sgt/tweak] / main.c
CommitLineData
6e182d98 1/*
32c65619 2 * Potential future TODO items. Points marked ISSUE need to be
3 * resolved one way or another, with good justification for the
4 * decision made, before implementation begins.
6e182d98 5 *
6e182d98 6 * - Multiple buffers, multiple on-screen windows.
7 * + ^X^F to open new file
8 * + ^X^R to open new file RO
9 * + ^X b to switch buffers in a window
10 * + ^X o to switch windows
11 * + ^X 2 to split a window
12 * + ^X 1 to destroy all windows but this
13 * + ^X 0 to destroy this window
14 * + ^X ^ to enlarge this window by one line
15 * + width settings vary per buffer (aha, _that's_ why I wanted
16 * a buffer structure surrounding the raw B-tree)
17 * + hex-editor-style minibuffer for entering search terms,
18 * rather than the current rather crap one; in particular
19 * this enables pasting into the search string.
32c65619 20 * + ISSUE: how exactly do we deal with the problem of saving
21 * over a file which we're maintaining references to in
22 * another buffer? The _current_ buffer can at least be
23 * sorted out by replacing it with a fresh tree containing a
24 * single file-data block, but other buffers are in trouble.
25 * * if we can rely on Unix fd semantics, one option is just
26 * to keep the fd open on the original file, and then the
27 * data stays around even after we rename(2) our new
28 * version over the top. Disk space usage gets silly after
29 * a few iterations, but it's better than nothing.
6e182d98 30 *
31 * - Undo!
32 * + this actually doesn't seem _too_ horrid. For a start, one
33 * simple approach would be to clone the entire buffer B-tree
34 * every time we perform an operation! That's actually not
35 * _too_ expensive, if we maintain a limit on the number of
36 * operations we may undo.
37 * + I had also thought of cloning the tree we insert for each
38 * buf_insert_data and cloning the one removed for each
39 * buf_delete_data (both must be cloned for an overwrite),
40 * but I'm not convinced that simply cloning the entire thing
41 * isn't a superior option.
d28a4799 42 * + this really starts to show up the distinction between a
43 * `buffer' and a bare tree. A buffer is something which has
44 * an undo chain attached; so, in particular, the cut buffer
45 * shouldn't be one. Sort that out.
6e182d98 46 *
6e182d98 47 * - In-place editing.
32c65619 48 * + this is an extra option useful for editing disk devices
49 * directly (!), or other situation in which it's impossible
50 * or impractical to rename(2) your new file over the old
51 * one. It causes a change of semantics when saving: instead
52 * of constructing a new backup file and writing it over the
53 * old one, we simply seek within the original file and write
54 * out all the pieces that have changed.
55 * + Saving the file involves identifying the bits of the file
56 * that need to change, and changing them. A piece of file
57 * can be discarded as `no change required' if it's
58 * represented in the buffer by a from-file block whose file
59 * offset is equal to its offset in the buffer.
60 * * Once we have identified all the bits that do need to
61 * change, we have to draw up a dependency graph to
62 * indicate which bits want to be copied from which other
63 * bits. (You don't want to overwrite a piece of file if
64 * you still have from-file blocks pointing at that
65 * piece.) This is a directed graph with nodes
66 * corresponding to intervals of the file, and edges
67 * indicating that the source node's interval is intended
68 * to end up containing the data from the target node's
69 * interval in the original file. Another node type is
70 * `literal data', which can be the target of an edge but
71 * never the source.
72 * - note that this means any two nodes connected by an
73 * edge must represent intervals of the same length.
74 * Sometimes this means that an interval must be split
75 * into pieces even though it is represented in the
76 * buffer by a single large from-file block (if
77 * from-file blocks copying _from_ it don't cover the
78 * whole of it). I suspect the simplest approach here
79 * is just to start by making a B-tree of division
80 * points in the file: every from-file block adds four
81 * division points (for start and end of both source
82 * and dest interval), and once the tree is complete,
83 * each graph node represents the interval between two
84 * adjacent division points.
85 * - ISSUE: actually, that strategy is inadequate:
86 * consider a large from-file block displaced by only
87 * one byte from its source location. The above
88 * strategy gives division points at x, x+1, x+y,
89 * x+y+1, but the interval [x,x+1] actually wants to
90 * point to [x+1,x+2] and we don't have a division
91 * point for that. Worse still, finding a way to add
92 * the remaining division points is also undesirable
93 * because there'd be so many of them. Needs design
94 * changes.
95 * * Then, any node which is not the target of any edge
96 * represents a piece of file which it's safe to write
97 * over, so we do so and throw away the node.
98 * * If we run out of such nodes and the graph is still
99 * non-empty, it's because all remaining nodes are part of
100 * loops. A loop must represent a set of disjoint
101 * intervals in the file, all the same length, which need
102 * to be permuted cyclically. So we deal with such a loop
103 * by reading a chunk of data from the start of one of the
104 * intervals and holding it, then copying from the next
105 * interval to that one, and so on until we've gone round
106 * the loop.
107 * + the intervals in the loop might be far too big to
108 * hold an entire interval's worth of real data in
109 * memory, so we might have to do it piecewise.
110 * + ISSUE: I wonder if a warning of some sort might be in
111 * order for if you accidentally request most of the file be
112 * moved about. This sort of trickery is really intended for
113 * small changes to a large file; if you (say) enable insert
114 * mode while editing a hard disk and accidentally leave
115 * everything one byte further up, you _really_ don't want to
116 * hit Save. The semantics of the warning are difficult,
117 * though.
fabf09e9 118 *
119 * - Custom display and/or input formats?
120 * + for example, Zap on RISC OS is able to display a binary
121 * file at 4 bytes per line and show the ARM disassembly of
122 * each word. For added credit, ability to type an ARM
123 * instruction back _in_ and have it reassembled into binary
124 * would be even better.
125 * + a simpler example is that sometimes you want to view a
126 * file as a sequence of little-endian 32-bit words rather
127 * than single bytes.
128 * + this would have to involve some sort of scripting or
129 * internal API. I'd really rather the interface was nailed
130 * down very early on and people were then free to develop
131 * custom formats without my involvement; I might be
132 * persuaded to keep a library of them or a list of
133 * hyperlinks or something, but actually _maintaining_ them
134 * is more effort than I want.
135 * + ARM assembler is all very well, but what about x86, with
136 * its variable instruction length? You can start
137 * disassembling from any byte position and work forwards
138 * unambiguously, but going backwards or jumping to an
139 * arbitrary byte position is much harder. You might have to
140 * shift your current file view back or forward by one byte
141 * to resynchronise, and the semantics of insert mode become
142 * generally confused, and even trying to _predict_ what a
143 * sensible synchronisation point would be when jumping to a
144 * bit of the file you've never seen before ... yuck.
145 * * The key thing that makes this horrid is that the custom
146 * display mode looks at the file _contents_, not merely
147 * its length, when deciding how many bytes per line to
148 * display. File-position-dependent number of bytes per
149 * line is fine, but _data_ dependency is doom.
150 * * So I think that in the interests of not causing tension
151 * between random things people would like in _some_ hex
152 * editor and what makes Tweak Tweak, I am going to put my
153 * foot down and say that I will not implement any
154 * mechanism which permits a data-dependent number of
155 * bytes per line. Anything short of that, fine, send me a
156 * patch or a detailed and well thought out design and
157 * I'll consider it on its merits.
158 * * I don't, OTOH, see any reason why a custom display
159 * function couldn't be permitted to see data before or
160 * after the current lineful if it wanted to. So x86
161 * disassembly could be done in a one-byte-per-line sort
162 * of fashion in which each line shows the machine
163 * instruction which the CPU would see if it started
164 * executing at that byte, and also gave its length. Then
165 * you could pick out the sequence of instructions you
166 * were interested in from the various out-of-sync ones.
6e182d98 167 */
168
11825bd4 169#include "tweak.h"
170
6e182d98 171#include <stdio.h>
172#include <stdlib.h>
173#include <string.h>
174#include <ctype.h>
175#include <assert.h>
176
177#if defined(unix) && !defined(GO32)
178#include <signal.h>
179#include <sys/ioctl.h>
180#include <termios.h>
181#elif defined(MSDOS)
182#include <dos.h>
183#include <process.h>
184#endif
185
6e182d98 186static void init(void);
187static void done(void);
188static void load_file (char *);
189
190char toprint[256]; /* LUT: printable versions of chars */
191char hex[256][3]; /* LUT: binary to hex, 1 byte */
192
0c7480ac 193char message[512];
6e182d98 194
11825bd4 195char decstatus[] = "%s TWEAK "VER": %-18.18s %s posn=%-10"OFF"d size=%-10"OFF"d";
196char hexstatus[] = "%s TWEAK "VER": %-18.18s %s posn=0x%-8"OFF"X size=0x%-8"OFF"X";
6e182d98 197char *statfmt = hexstatus;
198
199char last_char;
200char *pname;
201char *filename = NULL;
202buffer *filedata, *cutbuffer = NULL;
203int fix_mode = FALSE;
204int look_mode = FALSE;
205int eager_mode = FALSE;
206int insert_mode = FALSE;
207int edit_type = 1; /* 1,2 are hex digits, 0=ascii */
208int finished = FALSE;
209int marking = FALSE;
210int modified = FALSE;
211int new_file = FALSE; /* shouldn't need initialisation -
212 * but let's not take chances :-) */
11825bd4 213fileoffset_t width = 16;
214fileoffset_t realoffset = 0, offset = 16;
6e182d98 215
216int ascii_enabled = TRUE;
217
11825bd4 218fileoffset_t file_size = 0, top_pos = 0, cur_pos = 0, mark_point = 0;
6e182d98 219
220int scrlines;
221
222/*
223 * Main program
224 */
225int main(int argc, char **argv) {
11825bd4 226 fileoffset_t newoffset = -1, newwidth = -1;
6e182d98 227
228 /*
229 * Parse command line arguments
230 */
231 pname = *argv; /* program name */
232 if (argc < 2) {
233 fprintf(stderr,
234 "usage: %s [-f] [-l] [-e] filename\n"
235 " or %s -D to write default tweak.rc to stdout\n",
236 pname, pname);
237 return 0;
238 }
239
240 while (--argc > 0) {
241 char c, *p = *++argv, *value;
242
243 if (*p == '-') {
244 p++;
245 while (*p) switch (c = *p++) {
246 case 'o': case 'O':
247 case 'w': case 'W':
248 /*
249 * these parameters require arguments
250 */
251 if (*p)
252 value = p, p = "";
253 else if (--argc)
254 value = *++argv;
255 else {
256 fprintf(stderr, "%s: option `-%c' requires an argument\n",
257 pname, c);
258 return 1;
259 }
260 switch (c) {
261 case 'o': case 'O':
11825bd4 262 newoffset = parse_num(value, NULL);
6e182d98 263 break;
264 case 'w': case 'W':
11825bd4 265 newwidth = parse_num(value, NULL);
6e182d98 266 break;
267 }
268 break;
269 case 'f': case 'F':
270 fix_mode = TRUE;
271 break;
272 case 'l': case 'L':
273 look_mode = TRUE;
274 break;
275 case 'e': case 'E':
276 eager_mode = TRUE;
277 break;
278 case 'D':
279 write_default_rc();
280 return 0;
281 break;
282 }
283 } else {
284 if (filename) {
285 fprintf(stderr, "%s: multiple filenames specified\n", pname);
286 return 1;
287 }
288 filename = p;
289 }
290 }
291
292 if (!filename) {
293 fprintf(stderr, "%s: no filename specified\n", pname);
294 return 1;
295 }
296
297 read_rc();
298 if (newoffset != -1)
299 realoffset = newoffset;
300 if (newwidth != -1)
301 width = newwidth;
302 load_file (filename);
303 init();
304 fix_offset();
305 do {
306 draw_scr ();
307 proc_key ();
308 } while (!finished);
309 done();
310
311 return 0;
312}
313
314/*
315 * Fix up `offset' to match `realoffset'. Also, while we're here,
316 * enable or disable ASCII mode and sanity-check the width.
317 */
318void fix_offset(void) {
319 if (3*width+11 > display_cols) {
320 width = (display_cols-11) / 3;
11825bd4 321 sprintf (message, "Width reduced to %"OFF"d to fit on the screen", width);
6e182d98 322 }
323 if (4*width+14 > display_cols) {
324 ascii_enabled = FALSE;
325 if (edit_type == 0)
326 edit_type = 1; /* force to hex mode */
327 } else
328 ascii_enabled = TRUE;
329 offset = realoffset % width;
330 if (!offset)
331 offset = width;
332}
333
334/*
335 * Initialise stuff at the beginning of the program: mostly the
336 * display.
337 */
338static void init(void) {
339 int i;
340
341 display_setup();
342
ef7de295 343 display_define_colour(COL_BUFFER, -1, -1, FALSE);
344 display_define_colour(COL_SELECT, 0, 7, TRUE);
345 display_define_colour(COL_STATUS, 11, 4, TRUE);
346 display_define_colour(COL_ESCAPE, 9, 0, FALSE);
347 display_define_colour(COL_INVALID, 11, 0, FALSE);
6e182d98 348
349 for (i=0; i<256; i++) {
350 sprintf(hex[i], "%02X", i);
351 toprint[i] = (i>=32 && i<127 ? i : '.');
352 }
353}
354
355/*
356 * Clean up all the stuff that init() did.
357 */
358static void done(void) {
359 display_cleanup();
360}
361
362/*
363 * Load the file specified on the command line.
364 */
365static void load_file (char *fname) {
366 FILE *fp;
367
368 file_size = 0;
369 if ( (fp = fopen (fname, "rb")) ) {
370 if (eager_mode) {
11825bd4 371 size_t len;
6e182d98 372 static char buffer[4096];
373
374 filedata = buf_new_empty();
375
376 file_size = 0;
377
378 /*
379 * We've opened the file. Load it.
380 */
381 while ( (len = fread (buffer, 1, sizeof(buffer), fp)) > 0 ) {
382 buf_insert_data (filedata, buffer, len, file_size);
383 file_size += len;
384 }
385 fclose (fp);
386 assert(file_size == buf_length(filedata));
11825bd4 387 sprintf(message, "loaded %s (size %"OFF"d == 0x%"OFF"X).",
6e182d98 388 fname, file_size, file_size);
389 } else {
390 filedata = buf_new_from_file(fp);
391 file_size = buf_length(filedata);
11825bd4 392 sprintf(message, "opened %s (size %"OFF"d == 0x%"OFF"X).",
6e182d98 393 fname, file_size, file_size);
394 }
395 new_file = FALSE;
396 } else {
397 if (look_mode || fix_mode) {
398 fprintf(stderr, "%s: file %s not found, and %s mode active\n",
399 pname, fname, (look_mode ? "LOOK" : "FIX"));
400 exit (1);
401 }
402 filedata = buf_new_empty();
403 sprintf(message, "New file %s.", fname);
404 new_file = TRUE;
405 }
406}
407
408/*
409 * Save the file. Return TRUE on success, FALSE on error.
410 */
411int save_file (void) {
412 FILE *fp;
11825bd4 413 fileoffset_t pos = 0;
6e182d98 414
415 if (look_mode)
416 return FALSE; /* do nothing! */
417
418 if ( (fp = fopen (filename, "wb")) ) {
419 static char buffer[SAVE_BLKSIZ];
420
421 while (pos < file_size) {
11825bd4 422 fileoffset_t size = file_size - pos;
6e182d98 423 if (size > SAVE_BLKSIZ)
424 size = SAVE_BLKSIZ;
425
426 buf_fetch_data (filedata, buffer, size, pos);
427 if (size != fwrite (buffer, 1, size, fp)) {
428 fclose (fp);
429 return FALSE;
430 }
431 pos += size;
432 }
433 } else
434 return FALSE;
435 fclose (fp);
436 return TRUE;
437}
438
439/*
440 * Make a backup of the file, if such has not already been done.
441 * Return TRUE on success, FALSE on error.
442 */
443int backup_file (void) {
444 char backup_name[FILENAME_MAX];
445
446 if (new_file)
447 return TRUE; /* unnecessary - pretend it's done */
448 strcpy (backup_name, filename);
449#if defined(unix) && !defined(GO32)
450 strcat (backup_name, ".bak");
451#elif defined(MSDOS)
452 {
453 char *p, *q;
454
455 q = NULL;
456 for (p = backup_name; *p; p++) {
457 if (*p == '\\')
458 q = NULL;
459 else if (*p == '.')
460 q = p;
461 }
462 if (!q)
463 q = p;
464 strcpy (q, ".BAK");
465 }
466#endif
467 remove (backup_name); /* don't care if this fails */
468 return !rename (filename, backup_name);
469}
470
471static unsigned char *scrbuf = NULL;
c72c185f 472static int scrbufsize = 0;
6e182d98 473
474/*
475 * Draw the screen, for normal usage.
476 */
477void draw_scr (void) {
478 int scrsize, scroff, llen, i, j;
11825bd4 479 fileoffset_t currpos;
480 fileoffset_t marktop, markbot;
481 int mark;
6e182d98 482 char *p;
483 unsigned char c, *q;
484 char *linebuf;
485
486 scrlines = display_rows - 2;
c72c185f 487 scrsize = scrlines * width;
488 if (scrsize > scrbufsize) {
489 scrbuf = (scrbuf ? realloc(scrbuf, scrsize) : malloc(scrsize));
6e182d98 490 if (!scrbuf) {
491 done();
492 fprintf(stderr, "%s: out of memory!\n", pname);
493 exit (2);
494 }
c72c185f 495 scrbufsize = scrsize;
6e182d98 496 }
497
498 linebuf = malloc(width*4+20);
499 if (!linebuf) {
500 done();
501 fprintf(stderr, "%s: out of memory!\n", pname);
502 exit (2);
503 }
504 memset (linebuf, ' ', width*4+13);
505 linebuf[width*4+13] = '\0';
506
507 if (top_pos == 0)
508 scroff = width - offset;
509 else
510 scroff = 0;
c72c185f 511
512 scrsize -= scroff;
6e182d98 513 if (scrsize > file_size - top_pos)
514 scrsize = file_size - top_pos;
515
516 buf_fetch_data (filedata, scrbuf, scrsize, top_pos);
517
518 scrsize += scroff; /* hack but it'll work */
519
520 mark = marking && (cur_pos != mark_point);
521 if (mark) {
522 if (cur_pos > mark_point)
523 marktop = mark_point, markbot = cur_pos;
524 else
525 marktop = cur_pos, markbot = mark_point;
526 } else
527 marktop = markbot = 0; /* placate gcc */
528
529 currpos = top_pos;
530 q = scrbuf;
531
532 for (i=0; i<scrlines; i++) {
533 display_moveto (i, 0);
534 if (currpos<=cur_pos || currpos<file_size) {
535 p = hex[(currpos >> 24) & 0xFF];
536 linebuf[0]=p[0];
537 linebuf[1]=p[1];
538 p = hex[(currpos >> 16) & 0xFF];
539 linebuf[2]=p[0];
540 linebuf[3]=p[1];
541 p = hex[(currpos >> 8) & 0xFF];
542 linebuf[4]=p[0];
543 linebuf[5]=p[1];
544 p = hex[currpos & 0xFF];
545 linebuf[6]=p[0];
546 linebuf[7]=p[1];
547 for (j=0; j<width; j++) {
548 if (scrsize > 0) {
549 if (currpos == 0 && j < width-offset)
550 p = " ", c = ' ';
551 else
552 p = hex[*q], c = *q++;
553 scrsize--;
554 } else {
555 p = " ", c = ' ';
556 }
557 linebuf[11+3*j]=p[0];
558 linebuf[12+3*j]=p[1];
559 linebuf[13+3*width+j]=toprint[c];
560 }
561 llen = (currpos ? width : offset);
562 if (mark && currpos<markbot && currpos+llen>marktop) {
563 /*
564 * Some of this line is marked. Maybe all. Whatever
565 * the precise details, there will be two regions
566 * requiring highlighting: a hex bit and an ascii
567 * bit.
568 */
11825bd4 569 fileoffset_t localstart= (currpos<marktop ? marktop :
570 currpos) - currpos;
571 fileoffset_t localstop = (currpos+llen>markbot ? markbot :
572 currpos+llen) - currpos;
6e182d98 573 localstart += width-llen;
574 localstop += width-llen;
575 display_write_chars(linebuf, 11+3*localstart);
576 display_set_colour(COL_SELECT);
577 display_write_chars(linebuf+11+3*localstart,
578 3*(localstop-localstart)-1);
579 display_set_colour(COL_BUFFER);
580 if (ascii_enabled) {
581 display_write_chars(linebuf+10+3*localstop,
582 3+3*width+localstart-3*localstop);
583 display_set_colour(COL_SELECT);
584 display_write_chars(linebuf+13+3*width+localstart,
585 localstop-localstart);
586 display_set_colour(COL_BUFFER);
587 display_write_chars(linebuf+13+3*width+localstop,
588 width-localstop);
589 } else {
590 display_write_chars(linebuf+10+3*localstop,
591 2+3*width-3*localstop);
592 }
1610a3c6 593 } else {
594 display_set_colour(COL_BUFFER);
6e182d98 595 display_write_chars(linebuf,
596 ascii_enabled ? 13+4*width : 10+3*width);
1610a3c6 597 }
6e182d98 598 }
599 currpos += (currpos ? width : offset);
600 display_clear_to_eol();
601 }
602
603 {
604 char status[80];
605 int slen;
606 display_moveto (display_rows-2, 0);
607 display_set_colour(COL_STATUS);
608 sprintf(status, statfmt,
609 (modified ? "**" : " "),
610 filename,
611 (insert_mode ? "(Insert)" :
612 look_mode ? "(LOOK) " :
613 fix_mode ? "(FIX) " : "(Ovrwrt)"),
614 cur_pos, file_size);
615 slen = strlen(status);
616 if (slen > display_cols)
617 slen = display_cols;
618 display_write_chars(status, slen);
619 while (slen++ < display_cols)
620 display_write_str(" ");
621 display_set_colour(COL_BUFFER);
622 }
623
624 display_moveto (display_rows-1, 0);
625 display_write_str (message);
626 display_clear_to_eol();
627 message[0] = '\0';
628
629 i = cur_pos - top_pos;
630 if (top_pos == 0)
631 i += width - offset;
632 j = (edit_type ? (i%width)*3+10+edit_type : (i%width)+13+3*width);
633 if (j >= display_cols)
634 j = display_cols-1;
635 free (linebuf);
636 display_moveto (i/width, j);
637 display_refresh ();
638}
639
ef7de295 640volatile int safe_update, update_required;
641void update (void);
642
6e182d98 643/*
644 * Get a string, in the "minibuffer". Return TRUE on success, FALSE
645 * on break. Possibly syntax-highlight the entered string for
646 * backslash-escapes, depending on the "highlight" parameter.
647 */
648int get_str (char *prompt, char *buf, int highlight) {
649 int maxlen = 79 - strlen(prompt); /* limit to 80 - who cares? :) */
650 int len = 0;
651 int c;
652
653 for (EVER) {
654 display_moveto (display_rows-1, 0);
655 display_set_colour (COL_MINIBUF);
656 display_write_str (prompt);
657 if (highlight) {
658 char *q, *p = buf, *r = buf+len;
659 while (p<r) {
660 q = p;
661 if (*p == '\\') {
662 p++;
663 if (p<r && *p == '\\')
664 p++, display_set_colour(COL_ESCAPE);
40d827aa 665 else if (p>=r || !isxdigit ((unsigned char)*p))
6e182d98 666 display_set_colour(COL_INVALID);
40d827aa 667 else if (p+1>=r || !isxdigit ((unsigned char)p[1]))
6e182d98 668 p++, display_set_colour(COL_INVALID);
669 else
670 p+=2, display_set_colour(COL_ESCAPE);
671 } else {
672 while (p<r && *p != '\\')
673 p++;
674 display_set_colour (COL_MINIBUF);
675 }
676 display_write_chars (q, p-q);
677 }
678 } else
679 display_write_chars (buf, len);
680 display_set_colour (COL_MINIBUF);
681 display_clear_to_eol();
682 display_refresh();
683 if (update_required)
684 update();
685 safe_update = TRUE;
686 c = display_getkey();
687 safe_update = FALSE;
688 if (c == 13 || c == 10) {
689 buf[len] = '\0';
690 return TRUE;
691 } else if (c == 27 || c == 7) {
692 display_beep();
693 display_post_error();
694 strcpy (message, "User Break!");
695 return FALSE;
696 }
697
698 if (c >= 32 && c <= 126) {
699 if (len < maxlen)
700 buf[len++] = c;
701 else
702 display_beep();
703 }
704
705 if ((c == 127 || c == 8) && len > 0)
706 len--;
707
708 if (c == 'U'-'@') /* ^U kill line */
709 len = 0;
710 }
711}
712
713/*
714 * Take a buffer containing possible backslash-escapes, and return
715 * a buffer containing a (binary!) string. Since the string is
716 * binary, it cannot be null terminated: hence the length is
717 * returned from the function. The string is processed in place.
718 *
719 * Escapes are simple: a backslash followed by two hex digits
720 * represents that character; a doubled backslash represents a
721 * backslash itself; a backslash followed by anything else is
722 * invalid. (-1 is returned if an invalid sequence is detected.)
723 */
724int parse_quoted (char *buffer) {
725 char *p, *q;
726
727 p = q = buffer;
728 while (*p) {
729 while (*p && *p != '\\')
730 *q++ = *p++;
731 if (*p == '\\') {
732 p++;
733 if (*p == '\\')
734 *q++ = *p++;
40d827aa 735 else if (p[1] && isxdigit((unsigned char)*p) &&
736 isxdigit((unsigned char)p[1])) {
6e182d98 737 char buf[3];
738 buf[0] = *p++;
739 buf[1] = *p++;
740 buf[2] = '\0';
741 *q++ = strtol(buf, NULL, 16);
742 } else
743 return -1;
744 }
745 }
746 return q - buffer;
747}
748
749/*
750 * Suspend program. (Or shell out, depending on OS, of course.)
751 */
752void suspend(void) {
753#if defined(unix) && !defined(GO32)
754 done();
755 raise (SIGTSTP);
756 init();
757#elif defined(MSDOS)
758 done();
759 spawnl (P_WAIT, getenv("COMSPEC"), "", NULL);
760 init();
761#else
762 display_beep();
763 strcpy(message, "Suspend function not yet implemented.");
764#endif
765}
766
6e182d98 767void update (void) {
768 display_recheck_size();
769 fix_offset ();
770 draw_scr ();
771}
772
773void schedule_update(void) {
774 if (safe_update)
775 update();
776 else
777 update_required = TRUE;
778}
779
11825bd4 780fileoffset_t parse_num (char *buffer, int *error) {
6e182d98 781 if (error)
782 *error = FALSE;
783 if (!buffer[strspn(buffer, "0123456789")]) {
784 /* interpret as decimal */
11825bd4 785 return ATOOFF(buffer);
6e182d98 786 } else if (buffer[0]=='0' && (buffer[1]=='X' || buffer[1]=='x') &&
787 !buffer[2+strspn(buffer+2,"0123456789ABCDEFabcdef")]) {
11825bd4 788 return STRTOOFF(buffer+2, NULL, 16);
6e182d98 789 } else if (buffer[0]=='$' &&
790 !buffer[1+strspn(buffer+1,"0123456789ABCDEFabcdef")]) {
11825bd4 791 return STRTOOFF(buffer+1, NULL, 16);
6e182d98 792 } else {
793 return 0;
794 if (error)
795 *error = TRUE;
796 }
797}