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. |
6e182d98 |
118 | */ |
119 | |
11825bd4 |
120 | #include "tweak.h" |
121 | |
6e182d98 |
122 | #include <stdio.h> |
123 | #include <stdlib.h> |
124 | #include <string.h> |
125 | #include <ctype.h> |
126 | #include <assert.h> |
127 | |
128 | #if defined(unix) && !defined(GO32) |
129 | #include <signal.h> |
130 | #include <sys/ioctl.h> |
131 | #include <termios.h> |
132 | #elif defined(MSDOS) |
133 | #include <dos.h> |
134 | #include <process.h> |
135 | #endif |
136 | |
6e182d98 |
137 | static void init(void); |
138 | static void done(void); |
139 | static void load_file (char *); |
140 | |
141 | char toprint[256]; /* LUT: printable versions of chars */ |
142 | char hex[256][3]; /* LUT: binary to hex, 1 byte */ |
143 | |
144 | char message[80]; |
145 | |
11825bd4 |
146 | char decstatus[] = "%s TWEAK "VER": %-18.18s %s posn=%-10"OFF"d size=%-10"OFF"d"; |
147 | char hexstatus[] = "%s TWEAK "VER": %-18.18s %s posn=0x%-8"OFF"X size=0x%-8"OFF"X"; |
6e182d98 |
148 | char *statfmt = hexstatus; |
149 | |
150 | char last_char; |
151 | char *pname; |
152 | char *filename = NULL; |
153 | buffer *filedata, *cutbuffer = NULL; |
154 | int fix_mode = FALSE; |
155 | int look_mode = FALSE; |
156 | int eager_mode = FALSE; |
157 | int insert_mode = FALSE; |
158 | int edit_type = 1; /* 1,2 are hex digits, 0=ascii */ |
159 | int finished = FALSE; |
160 | int marking = FALSE; |
161 | int modified = FALSE; |
162 | int new_file = FALSE; /* shouldn't need initialisation - |
163 | * but let's not take chances :-) */ |
11825bd4 |
164 | fileoffset_t width = 16; |
165 | fileoffset_t realoffset = 0, offset = 16; |
6e182d98 |
166 | |
167 | int ascii_enabled = TRUE; |
168 | |
11825bd4 |
169 | fileoffset_t file_size = 0, top_pos = 0, cur_pos = 0, mark_point = 0; |
6e182d98 |
170 | |
171 | int scrlines; |
172 | |
173 | /* |
174 | * Main program |
175 | */ |
176 | int main(int argc, char **argv) { |
11825bd4 |
177 | fileoffset_t newoffset = -1, newwidth = -1; |
6e182d98 |
178 | |
179 | /* |
180 | * Parse command line arguments |
181 | */ |
182 | pname = *argv; /* program name */ |
183 | if (argc < 2) { |
184 | fprintf(stderr, |
185 | "usage: %s [-f] [-l] [-e] filename\n" |
186 | " or %s -D to write default tweak.rc to stdout\n", |
187 | pname, pname); |
188 | return 0; |
189 | } |
190 | |
191 | while (--argc > 0) { |
192 | char c, *p = *++argv, *value; |
193 | |
194 | if (*p == '-') { |
195 | p++; |
196 | while (*p) switch (c = *p++) { |
197 | case 'o': case 'O': |
198 | case 'w': case 'W': |
199 | /* |
200 | * these parameters require arguments |
201 | */ |
202 | if (*p) |
203 | value = p, p = ""; |
204 | else if (--argc) |
205 | value = *++argv; |
206 | else { |
207 | fprintf(stderr, "%s: option `-%c' requires an argument\n", |
208 | pname, c); |
209 | return 1; |
210 | } |
211 | switch (c) { |
212 | case 'o': case 'O': |
11825bd4 |
213 | newoffset = parse_num(value, NULL); |
6e182d98 |
214 | break; |
215 | case 'w': case 'W': |
11825bd4 |
216 | newwidth = parse_num(value, NULL); |
6e182d98 |
217 | break; |
218 | } |
219 | break; |
220 | case 'f': case 'F': |
221 | fix_mode = TRUE; |
222 | break; |
223 | case 'l': case 'L': |
224 | look_mode = TRUE; |
225 | break; |
226 | case 'e': case 'E': |
227 | eager_mode = TRUE; |
228 | break; |
229 | case 'D': |
230 | write_default_rc(); |
231 | return 0; |
232 | break; |
233 | } |
234 | } else { |
235 | if (filename) { |
236 | fprintf(stderr, "%s: multiple filenames specified\n", pname); |
237 | return 1; |
238 | } |
239 | filename = p; |
240 | } |
241 | } |
242 | |
243 | if (!filename) { |
244 | fprintf(stderr, "%s: no filename specified\n", pname); |
245 | return 1; |
246 | } |
247 | |
248 | read_rc(); |
249 | if (newoffset != -1) |
250 | realoffset = newoffset; |
251 | if (newwidth != -1) |
252 | width = newwidth; |
253 | load_file (filename); |
254 | init(); |
255 | fix_offset(); |
256 | do { |
257 | draw_scr (); |
258 | proc_key (); |
259 | } while (!finished); |
260 | done(); |
261 | |
262 | return 0; |
263 | } |
264 | |
265 | /* |
266 | * Fix up `offset' to match `realoffset'. Also, while we're here, |
267 | * enable or disable ASCII mode and sanity-check the width. |
268 | */ |
269 | void fix_offset(void) { |
270 | if (3*width+11 > display_cols) { |
271 | width = (display_cols-11) / 3; |
11825bd4 |
272 | sprintf (message, "Width reduced to %"OFF"d to fit on the screen", width); |
6e182d98 |
273 | } |
274 | if (4*width+14 > display_cols) { |
275 | ascii_enabled = FALSE; |
276 | if (edit_type == 0) |
277 | edit_type = 1; /* force to hex mode */ |
278 | } else |
279 | ascii_enabled = TRUE; |
280 | offset = realoffset % width; |
281 | if (!offset) |
282 | offset = width; |
283 | } |
284 | |
285 | /* |
286 | * Initialise stuff at the beginning of the program: mostly the |
287 | * display. |
288 | */ |
289 | static void init(void) { |
290 | int i; |
291 | |
292 | display_setup(); |
293 | |
ef7de295 |
294 | display_define_colour(COL_BUFFER, -1, -1, FALSE); |
295 | display_define_colour(COL_SELECT, 0, 7, TRUE); |
296 | display_define_colour(COL_STATUS, 11, 4, TRUE); |
297 | display_define_colour(COL_ESCAPE, 9, 0, FALSE); |
298 | display_define_colour(COL_INVALID, 11, 0, FALSE); |
6e182d98 |
299 | |
300 | for (i=0; i<256; i++) { |
301 | sprintf(hex[i], "%02X", i); |
302 | toprint[i] = (i>=32 && i<127 ? i : '.'); |
303 | } |
304 | } |
305 | |
306 | /* |
307 | * Clean up all the stuff that init() did. |
308 | */ |
309 | static void done(void) { |
310 | display_cleanup(); |
311 | } |
312 | |
313 | /* |
314 | * Load the file specified on the command line. |
315 | */ |
316 | static void load_file (char *fname) { |
317 | FILE *fp; |
318 | |
319 | file_size = 0; |
320 | if ( (fp = fopen (fname, "rb")) ) { |
321 | if (eager_mode) { |
11825bd4 |
322 | size_t len; |
6e182d98 |
323 | static char buffer[4096]; |
324 | |
325 | filedata = buf_new_empty(); |
326 | |
327 | file_size = 0; |
328 | |
329 | /* |
330 | * We've opened the file. Load it. |
331 | */ |
332 | while ( (len = fread (buffer, 1, sizeof(buffer), fp)) > 0 ) { |
333 | buf_insert_data (filedata, buffer, len, file_size); |
334 | file_size += len; |
335 | } |
336 | fclose (fp); |
337 | assert(file_size == buf_length(filedata)); |
11825bd4 |
338 | sprintf(message, "loaded %s (size %"OFF"d == 0x%"OFF"X).", |
6e182d98 |
339 | fname, file_size, file_size); |
340 | } else { |
341 | filedata = buf_new_from_file(fp); |
342 | file_size = buf_length(filedata); |
11825bd4 |
343 | sprintf(message, "opened %s (size %"OFF"d == 0x%"OFF"X).", |
6e182d98 |
344 | fname, file_size, file_size); |
345 | } |
346 | new_file = FALSE; |
347 | } else { |
348 | if (look_mode || fix_mode) { |
349 | fprintf(stderr, "%s: file %s not found, and %s mode active\n", |
350 | pname, fname, (look_mode ? "LOOK" : "FIX")); |
351 | exit (1); |
352 | } |
353 | filedata = buf_new_empty(); |
354 | sprintf(message, "New file %s.", fname); |
355 | new_file = TRUE; |
356 | } |
357 | } |
358 | |
359 | /* |
360 | * Save the file. Return TRUE on success, FALSE on error. |
361 | */ |
362 | int save_file (void) { |
363 | FILE *fp; |
11825bd4 |
364 | fileoffset_t pos = 0; |
6e182d98 |
365 | |
366 | if (look_mode) |
367 | return FALSE; /* do nothing! */ |
368 | |
369 | if ( (fp = fopen (filename, "wb")) ) { |
370 | static char buffer[SAVE_BLKSIZ]; |
371 | |
372 | while (pos < file_size) { |
11825bd4 |
373 | fileoffset_t size = file_size - pos; |
6e182d98 |
374 | if (size > SAVE_BLKSIZ) |
375 | size = SAVE_BLKSIZ; |
376 | |
377 | buf_fetch_data (filedata, buffer, size, pos); |
378 | if (size != fwrite (buffer, 1, size, fp)) { |
379 | fclose (fp); |
380 | return FALSE; |
381 | } |
382 | pos += size; |
383 | } |
384 | } else |
385 | return FALSE; |
386 | fclose (fp); |
387 | return TRUE; |
388 | } |
389 | |
390 | /* |
391 | * Make a backup of the file, if such has not already been done. |
392 | * Return TRUE on success, FALSE on error. |
393 | */ |
394 | int backup_file (void) { |
395 | char backup_name[FILENAME_MAX]; |
396 | |
397 | if (new_file) |
398 | return TRUE; /* unnecessary - pretend it's done */ |
399 | strcpy (backup_name, filename); |
400 | #if defined(unix) && !defined(GO32) |
401 | strcat (backup_name, ".bak"); |
402 | #elif defined(MSDOS) |
403 | { |
404 | char *p, *q; |
405 | |
406 | q = NULL; |
407 | for (p = backup_name; *p; p++) { |
408 | if (*p == '\\') |
409 | q = NULL; |
410 | else if (*p == '.') |
411 | q = p; |
412 | } |
413 | if (!q) |
414 | q = p; |
415 | strcpy (q, ".BAK"); |
416 | } |
417 | #endif |
418 | remove (backup_name); /* don't care if this fails */ |
419 | return !rename (filename, backup_name); |
420 | } |
421 | |
422 | static unsigned char *scrbuf = NULL; |
423 | static int scrbuflines = 0; |
424 | |
425 | /* |
426 | * Draw the screen, for normal usage. |
427 | */ |
428 | void draw_scr (void) { |
429 | int scrsize, scroff, llen, i, j; |
11825bd4 |
430 | fileoffset_t currpos; |
431 | fileoffset_t marktop, markbot; |
432 | int mark; |
6e182d98 |
433 | char *p; |
434 | unsigned char c, *q; |
435 | char *linebuf; |
436 | |
437 | scrlines = display_rows - 2; |
438 | if (scrlines > scrbuflines) { |
439 | scrbuf = (scrbuf ? |
440 | realloc(scrbuf, scrlines*width) : |
441 | malloc(scrlines*width)); |
442 | if (!scrbuf) { |
443 | done(); |
444 | fprintf(stderr, "%s: out of memory!\n", pname); |
445 | exit (2); |
446 | } |
447 | scrbuflines = scrlines; |
448 | } |
449 | |
450 | linebuf = malloc(width*4+20); |
451 | if (!linebuf) { |
452 | done(); |
453 | fprintf(stderr, "%s: out of memory!\n", pname); |
454 | exit (2); |
455 | } |
456 | memset (linebuf, ' ', width*4+13); |
457 | linebuf[width*4+13] = '\0'; |
458 | |
459 | if (top_pos == 0) |
460 | scroff = width - offset; |
461 | else |
462 | scroff = 0; |
463 | scrsize = scrlines * width - scroff; |
464 | if (scrsize > file_size - top_pos) |
465 | scrsize = file_size - top_pos; |
466 | |
467 | buf_fetch_data (filedata, scrbuf, scrsize, top_pos); |
468 | |
469 | scrsize += scroff; /* hack but it'll work */ |
470 | |
471 | mark = marking && (cur_pos != mark_point); |
472 | if (mark) { |
473 | if (cur_pos > mark_point) |
474 | marktop = mark_point, markbot = cur_pos; |
475 | else |
476 | marktop = cur_pos, markbot = mark_point; |
477 | } else |
478 | marktop = markbot = 0; /* placate gcc */ |
479 | |
480 | currpos = top_pos; |
481 | q = scrbuf; |
482 | |
483 | for (i=0; i<scrlines; i++) { |
484 | display_moveto (i, 0); |
485 | if (currpos<=cur_pos || currpos<file_size) { |
486 | p = hex[(currpos >> 24) & 0xFF]; |
487 | linebuf[0]=p[0]; |
488 | linebuf[1]=p[1]; |
489 | p = hex[(currpos >> 16) & 0xFF]; |
490 | linebuf[2]=p[0]; |
491 | linebuf[3]=p[1]; |
492 | p = hex[(currpos >> 8) & 0xFF]; |
493 | linebuf[4]=p[0]; |
494 | linebuf[5]=p[1]; |
495 | p = hex[currpos & 0xFF]; |
496 | linebuf[6]=p[0]; |
497 | linebuf[7]=p[1]; |
498 | for (j=0; j<width; j++) { |
499 | if (scrsize > 0) { |
500 | if (currpos == 0 && j < width-offset) |
501 | p = " ", c = ' '; |
502 | else |
503 | p = hex[*q], c = *q++; |
504 | scrsize--; |
505 | } else { |
506 | p = " ", c = ' '; |
507 | } |
508 | linebuf[11+3*j]=p[0]; |
509 | linebuf[12+3*j]=p[1]; |
510 | linebuf[13+3*width+j]=toprint[c]; |
511 | } |
512 | llen = (currpos ? width : offset); |
513 | if (mark && currpos<markbot && currpos+llen>marktop) { |
514 | /* |
515 | * Some of this line is marked. Maybe all. Whatever |
516 | * the precise details, there will be two regions |
517 | * requiring highlighting: a hex bit and an ascii |
518 | * bit. |
519 | */ |
11825bd4 |
520 | fileoffset_t localstart= (currpos<marktop ? marktop : |
521 | currpos) - currpos; |
522 | fileoffset_t localstop = (currpos+llen>markbot ? markbot : |
523 | currpos+llen) - currpos; |
6e182d98 |
524 | localstart += width-llen; |
525 | localstop += width-llen; |
526 | display_write_chars(linebuf, 11+3*localstart); |
527 | display_set_colour(COL_SELECT); |
528 | display_write_chars(linebuf+11+3*localstart, |
529 | 3*(localstop-localstart)-1); |
530 | display_set_colour(COL_BUFFER); |
531 | if (ascii_enabled) { |
532 | display_write_chars(linebuf+10+3*localstop, |
533 | 3+3*width+localstart-3*localstop); |
534 | display_set_colour(COL_SELECT); |
535 | display_write_chars(linebuf+13+3*width+localstart, |
536 | localstop-localstart); |
537 | display_set_colour(COL_BUFFER); |
538 | display_write_chars(linebuf+13+3*width+localstop, |
539 | width-localstop); |
540 | } else { |
541 | display_write_chars(linebuf+10+3*localstop, |
542 | 2+3*width-3*localstop); |
543 | } |
1610a3c6 |
544 | } else { |
545 | display_set_colour(COL_BUFFER); |
6e182d98 |
546 | display_write_chars(linebuf, |
547 | ascii_enabled ? 13+4*width : 10+3*width); |
1610a3c6 |
548 | } |
6e182d98 |
549 | } |
550 | currpos += (currpos ? width : offset); |
551 | display_clear_to_eol(); |
552 | } |
553 | |
554 | { |
555 | char status[80]; |
556 | int slen; |
557 | display_moveto (display_rows-2, 0); |
558 | display_set_colour(COL_STATUS); |
559 | sprintf(status, statfmt, |
560 | (modified ? "**" : " "), |
561 | filename, |
562 | (insert_mode ? "(Insert)" : |
563 | look_mode ? "(LOOK) " : |
564 | fix_mode ? "(FIX) " : "(Ovrwrt)"), |
565 | cur_pos, file_size); |
566 | slen = strlen(status); |
567 | if (slen > display_cols) |
568 | slen = display_cols; |
569 | display_write_chars(status, slen); |
570 | while (slen++ < display_cols) |
571 | display_write_str(" "); |
572 | display_set_colour(COL_BUFFER); |
573 | } |
574 | |
575 | display_moveto (display_rows-1, 0); |
576 | display_write_str (message); |
577 | display_clear_to_eol(); |
578 | message[0] = '\0'; |
579 | |
580 | i = cur_pos - top_pos; |
581 | if (top_pos == 0) |
582 | i += width - offset; |
583 | j = (edit_type ? (i%width)*3+10+edit_type : (i%width)+13+3*width); |
584 | if (j >= display_cols) |
585 | j = display_cols-1; |
586 | free (linebuf); |
587 | display_moveto (i/width, j); |
588 | display_refresh (); |
589 | } |
590 | |
ef7de295 |
591 | volatile int safe_update, update_required; |
592 | void update (void); |
593 | |
6e182d98 |
594 | /* |
595 | * Get a string, in the "minibuffer". Return TRUE on success, FALSE |
596 | * on break. Possibly syntax-highlight the entered string for |
597 | * backslash-escapes, depending on the "highlight" parameter. |
598 | */ |
599 | int get_str (char *prompt, char *buf, int highlight) { |
600 | int maxlen = 79 - strlen(prompt); /* limit to 80 - who cares? :) */ |
601 | int len = 0; |
602 | int c; |
603 | |
604 | for (EVER) { |
605 | display_moveto (display_rows-1, 0); |
606 | display_set_colour (COL_MINIBUF); |
607 | display_write_str (prompt); |
608 | if (highlight) { |
609 | char *q, *p = buf, *r = buf+len; |
610 | while (p<r) { |
611 | q = p; |
612 | if (*p == '\\') { |
613 | p++; |
614 | if (p<r && *p == '\\') |
615 | p++, display_set_colour(COL_ESCAPE); |
40d827aa |
616 | else if (p>=r || !isxdigit ((unsigned char)*p)) |
6e182d98 |
617 | display_set_colour(COL_INVALID); |
40d827aa |
618 | else if (p+1>=r || !isxdigit ((unsigned char)p[1])) |
6e182d98 |
619 | p++, display_set_colour(COL_INVALID); |
620 | else |
621 | p+=2, display_set_colour(COL_ESCAPE); |
622 | } else { |
623 | while (p<r && *p != '\\') |
624 | p++; |
625 | display_set_colour (COL_MINIBUF); |
626 | } |
627 | display_write_chars (q, p-q); |
628 | } |
629 | } else |
630 | display_write_chars (buf, len); |
631 | display_set_colour (COL_MINIBUF); |
632 | display_clear_to_eol(); |
633 | display_refresh(); |
634 | if (update_required) |
635 | update(); |
636 | safe_update = TRUE; |
637 | c = display_getkey(); |
638 | safe_update = FALSE; |
639 | if (c == 13 || c == 10) { |
640 | buf[len] = '\0'; |
641 | return TRUE; |
642 | } else if (c == 27 || c == 7) { |
643 | display_beep(); |
644 | display_post_error(); |
645 | strcpy (message, "User Break!"); |
646 | return FALSE; |
647 | } |
648 | |
649 | if (c >= 32 && c <= 126) { |
650 | if (len < maxlen) |
651 | buf[len++] = c; |
652 | else |
653 | display_beep(); |
654 | } |
655 | |
656 | if ((c == 127 || c == 8) && len > 0) |
657 | len--; |
658 | |
659 | if (c == 'U'-'@') /* ^U kill line */ |
660 | len = 0; |
661 | } |
662 | } |
663 | |
664 | /* |
665 | * Take a buffer containing possible backslash-escapes, and return |
666 | * a buffer containing a (binary!) string. Since the string is |
667 | * binary, it cannot be null terminated: hence the length is |
668 | * returned from the function. The string is processed in place. |
669 | * |
670 | * Escapes are simple: a backslash followed by two hex digits |
671 | * represents that character; a doubled backslash represents a |
672 | * backslash itself; a backslash followed by anything else is |
673 | * invalid. (-1 is returned if an invalid sequence is detected.) |
674 | */ |
675 | int parse_quoted (char *buffer) { |
676 | char *p, *q; |
677 | |
678 | p = q = buffer; |
679 | while (*p) { |
680 | while (*p && *p != '\\') |
681 | *q++ = *p++; |
682 | if (*p == '\\') { |
683 | p++; |
684 | if (*p == '\\') |
685 | *q++ = *p++; |
40d827aa |
686 | else if (p[1] && isxdigit((unsigned char)*p) && |
687 | isxdigit((unsigned char)p[1])) { |
6e182d98 |
688 | char buf[3]; |
689 | buf[0] = *p++; |
690 | buf[1] = *p++; |
691 | buf[2] = '\0'; |
692 | *q++ = strtol(buf, NULL, 16); |
693 | } else |
694 | return -1; |
695 | } |
696 | } |
697 | return q - buffer; |
698 | } |
699 | |
700 | /* |
701 | * Suspend program. (Or shell out, depending on OS, of course.) |
702 | */ |
703 | void suspend(void) { |
704 | #if defined(unix) && !defined(GO32) |
705 | done(); |
706 | raise (SIGTSTP); |
707 | init(); |
708 | #elif defined(MSDOS) |
709 | done(); |
710 | spawnl (P_WAIT, getenv("COMSPEC"), "", NULL); |
711 | init(); |
712 | #else |
713 | display_beep(); |
714 | strcpy(message, "Suspend function not yet implemented."); |
715 | #endif |
716 | } |
717 | |
6e182d98 |
718 | void update (void) { |
719 | display_recheck_size(); |
720 | fix_offset (); |
721 | draw_scr (); |
722 | } |
723 | |
724 | void schedule_update(void) { |
725 | if (safe_update) |
726 | update(); |
727 | else |
728 | update_required = TRUE; |
729 | } |
730 | |
11825bd4 |
731 | fileoffset_t parse_num (char *buffer, int *error) { |
6e182d98 |
732 | if (error) |
733 | *error = FALSE; |
734 | if (!buffer[strspn(buffer, "0123456789")]) { |
735 | /* interpret as decimal */ |
11825bd4 |
736 | return ATOOFF(buffer); |
6e182d98 |
737 | } else if (buffer[0]=='0' && (buffer[1]=='X' || buffer[1]=='x') && |
738 | !buffer[2+strspn(buffer+2,"0123456789ABCDEFabcdef")]) { |
11825bd4 |
739 | return STRTOOFF(buffer+2, NULL, 16); |
6e182d98 |
740 | } else if (buffer[0]=='$' && |
741 | !buffer[1+strspn(buffer+1,"0123456789ABCDEFabcdef")]) { |
11825bd4 |
742 | return STRTOOFF(buffer+1, NULL, 16); |
6e182d98 |
743 | } else { |
744 | return 0; |
745 | if (error) |
746 | *error = TRUE; |
747 | } |
748 | } |