6e182d98 |
1 | /* |
6e182d98 |
2 | * TODO possibly after that: |
3 | * |
4 | * - Need to handle >2Gb files! Up the `filesize' type to long |
d28a4799 |
5 | * long, and use it everywhere (not just in buffer.c). |
6e182d98 |
6 | * |
7 | * - Multiple buffers, multiple on-screen windows. |
8 | * + ^X^F to open new file |
9 | * + ^X^R to open new file RO |
10 | * + ^X b to switch buffers in a window |
11 | * + ^X o to switch windows |
12 | * + ^X 2 to split a window |
13 | * + ^X 1 to destroy all windows but this |
14 | * + ^X 0 to destroy this window |
15 | * + ^X ^ to enlarge this window by one line |
16 | * + width settings vary per buffer (aha, _that's_ why I wanted |
17 | * a buffer structure surrounding the raw B-tree) |
18 | * + hex-editor-style minibuffer for entering search terms, |
19 | * rather than the current rather crap one; in particular |
20 | * this enables pasting into the search string. |
21 | * + er, how exactly do we deal with the problem of saving over |
d28a4799 |
22 | * a file which we're maintaining references to in another |
23 | * buffer? The _current_ buffer can at least be sorted out by |
24 | * replacing it with a fresh tree containing a single |
25 | * file-data block, but other buffers are in trouble. |
26 | * * if we can rely on Unix fd semantics, this isn't too |
27 | * bad; we can just keep the fd open on the original file, |
28 | * and then the data stays around even after we rename(2) |
29 | * our new version over the top. Disk space usage gets |
30 | * silly after a few iterations, but it's better than |
31 | * nothing. |
6e182d98 |
32 | * |
33 | * - Undo! |
34 | * + this actually doesn't seem _too_ horrid. For a start, one |
35 | * simple approach would be to clone the entire buffer B-tree |
36 | * every time we perform an operation! That's actually not |
37 | * _too_ expensive, if we maintain a limit on the number of |
38 | * operations we may undo. |
39 | * + I had also thought of cloning the tree we insert for each |
40 | * buf_insert_data and cloning the one removed for each |
41 | * buf_delete_data (both must be cloned for an overwrite), |
42 | * but I'm not convinced that simply cloning the entire thing |
43 | * isn't a superior option. |
d28a4799 |
44 | * + this really starts to show up the distinction between a |
45 | * `buffer' and a bare tree. A buffer is something which has |
46 | * an undo chain attached; so, in particular, the cut buffer |
47 | * shouldn't be one. Sort that out. |
6e182d98 |
48 | * |
6e182d98 |
49 | * - In-place editing. |
50 | * + this is an extra option when running in Fix mode. It |
51 | * causes a change of semantics when saving: instead of |
52 | * constructing a new backup file and writing it over the old |
53 | * one, we simply seek within the original file and write out |
54 | * all the pieces that have changed. |
55 | * + Primarily useful for editing disk devices directly |
d28a4799 |
56 | * (yikes!), or other situations where you actually cannot |
57 | * create a fresh copy of the file and rename(2) it into |
58 | * place. |
6e182d98 |
59 | * + I had intended to suggest that in Fix mode this would be |
60 | * nice and easy, since every element of the buffer tree is |
61 | * either a literal block (needs writing) or a from-file |
62 | * block denoting the same file _in the same position_. |
63 | * However, this is not in fact the case because you can cut |
64 | * and paste, so it's not that easy. |
65 | * + So I'm forced to the conclusion that when operating in |
66 | * this mode, it becomes illegal to cut and paste from-file |
67 | * blocks: they must be loaded in full at some point. |
68 | * * Thinking ahead about multiple-buffer operation: it |
69 | * would be a bad idea to keep a from-file block |
70 | * referencing /dev/hda and paste it into another ordinary |
71 | * buffer. But _also_ it would be a bad idea to paste a |
72 | * from-file block referencing a file stored _on_ /dev/hda |
73 | * into the in-place buffer dealing with /dev/hda itself. |
74 | * * So I'm forced to another odd conclusion, which is that |
75 | * from-file blocks must be eliminated in _two_ places: |
76 | * when copying a cut buffer _from_ an in-place buffer, |
77 | * _and_ when pasting a cut buffer _into_ one. |
78 | */ |
79 | |
80 | #include <stdio.h> |
81 | #include <stdlib.h> |
82 | #include <string.h> |
83 | #include <ctype.h> |
84 | #include <assert.h> |
85 | |
86 | #if defined(unix) && !defined(GO32) |
87 | #include <signal.h> |
88 | #include <sys/ioctl.h> |
89 | #include <termios.h> |
90 | #elif defined(MSDOS) |
91 | #include <dos.h> |
92 | #include <process.h> |
93 | #endif |
94 | |
95 | #include "tweak.h" |
96 | |
97 | static void init(void); |
98 | static void done(void); |
99 | static void load_file (char *); |
100 | |
101 | char toprint[256]; /* LUT: printable versions of chars */ |
102 | char hex[256][3]; /* LUT: binary to hex, 1 byte */ |
103 | |
104 | char message[80]; |
105 | |
106 | char decstatus[] = "%s TWEAK "VER": %-18.18s %s posn=%-10d size=%-10d"; |
107 | char hexstatus[] = "%s TWEAK "VER": %-18.18s %s posn=0x%-8X size=0x%-8X"; |
108 | char *statfmt = hexstatus; |
109 | |
110 | char last_char; |
111 | char *pname; |
112 | char *filename = NULL; |
113 | buffer *filedata, *cutbuffer = NULL; |
114 | int fix_mode = FALSE; |
115 | int look_mode = FALSE; |
116 | int eager_mode = FALSE; |
117 | int insert_mode = FALSE; |
118 | int edit_type = 1; /* 1,2 are hex digits, 0=ascii */ |
119 | int finished = FALSE; |
120 | int marking = FALSE; |
121 | int modified = FALSE; |
122 | int new_file = FALSE; /* shouldn't need initialisation - |
123 | * but let's not take chances :-) */ |
124 | int width = 16; |
125 | int realoffset = 0, offset = 16; |
126 | |
127 | int ascii_enabled = TRUE; |
128 | |
129 | long file_size = 0, top_pos = 0, cur_pos = 0, mark_point = 0; |
130 | |
131 | int scrlines; |
132 | |
133 | /* |
134 | * Main program |
135 | */ |
136 | int main(int argc, char **argv) { |
137 | int newoffset = -1, newwidth = -1; |
138 | |
139 | /* |
140 | * Parse command line arguments |
141 | */ |
142 | pname = *argv; /* program name */ |
143 | if (argc < 2) { |
144 | fprintf(stderr, |
145 | "usage: %s [-f] [-l] [-e] filename\n" |
146 | " or %s -D to write default tweak.rc to stdout\n", |
147 | pname, pname); |
148 | return 0; |
149 | } |
150 | |
151 | while (--argc > 0) { |
152 | char c, *p = *++argv, *value; |
153 | |
154 | if (*p == '-') { |
155 | p++; |
156 | while (*p) switch (c = *p++) { |
157 | case 'o': case 'O': |
158 | case 'w': case 'W': |
159 | /* |
160 | * these parameters require arguments |
161 | */ |
162 | if (*p) |
163 | value = p, p = ""; |
164 | else if (--argc) |
165 | value = *++argv; |
166 | else { |
167 | fprintf(stderr, "%s: option `-%c' requires an argument\n", |
168 | pname, c); |
169 | return 1; |
170 | } |
171 | switch (c) { |
172 | case 'o': case 'O': |
173 | newoffset = strtol(value, NULL, 0); /* allow `0xXX' */ |
174 | break; |
175 | case 'w': case 'W': |
176 | newwidth = strtol(value, NULL, 0); |
177 | break; |
178 | } |
179 | break; |
180 | case 'f': case 'F': |
181 | fix_mode = TRUE; |
182 | break; |
183 | case 'l': case 'L': |
184 | look_mode = TRUE; |
185 | break; |
186 | case 'e': case 'E': |
187 | eager_mode = TRUE; |
188 | break; |
189 | case 'D': |
190 | write_default_rc(); |
191 | return 0; |
192 | break; |
193 | } |
194 | } else { |
195 | if (filename) { |
196 | fprintf(stderr, "%s: multiple filenames specified\n", pname); |
197 | return 1; |
198 | } |
199 | filename = p; |
200 | } |
201 | } |
202 | |
203 | if (!filename) { |
204 | fprintf(stderr, "%s: no filename specified\n", pname); |
205 | return 1; |
206 | } |
207 | |
208 | read_rc(); |
209 | if (newoffset != -1) |
210 | realoffset = newoffset; |
211 | if (newwidth != -1) |
212 | width = newwidth; |
213 | load_file (filename); |
214 | init(); |
215 | fix_offset(); |
216 | do { |
217 | draw_scr (); |
218 | proc_key (); |
219 | } while (!finished); |
220 | done(); |
221 | |
222 | return 0; |
223 | } |
224 | |
225 | /* |
226 | * Fix up `offset' to match `realoffset'. Also, while we're here, |
227 | * enable or disable ASCII mode and sanity-check the width. |
228 | */ |
229 | void fix_offset(void) { |
230 | if (3*width+11 > display_cols) { |
231 | width = (display_cols-11) / 3; |
232 | sprintf (message, "Width reduced to %d to fit on the screen", width); |
233 | } |
234 | if (4*width+14 > display_cols) { |
235 | ascii_enabled = FALSE; |
236 | if (edit_type == 0) |
237 | edit_type = 1; /* force to hex mode */ |
238 | } else |
239 | ascii_enabled = TRUE; |
240 | offset = realoffset % width; |
241 | if (!offset) |
242 | offset = width; |
243 | } |
244 | |
245 | /* |
246 | * Initialise stuff at the beginning of the program: mostly the |
247 | * display. |
248 | */ |
249 | static void init(void) { |
250 | int i; |
251 | |
252 | display_setup(); |
253 | |
ef7de295 |
254 | display_define_colour(COL_BUFFER, -1, -1, FALSE); |
255 | display_define_colour(COL_SELECT, 0, 7, TRUE); |
256 | display_define_colour(COL_STATUS, 11, 4, TRUE); |
257 | display_define_colour(COL_ESCAPE, 9, 0, FALSE); |
258 | display_define_colour(COL_INVALID, 11, 0, FALSE); |
6e182d98 |
259 | |
260 | for (i=0; i<256; i++) { |
261 | sprintf(hex[i], "%02X", i); |
262 | toprint[i] = (i>=32 && i<127 ? i : '.'); |
263 | } |
264 | } |
265 | |
266 | /* |
267 | * Clean up all the stuff that init() did. |
268 | */ |
269 | static void done(void) { |
270 | display_cleanup(); |
271 | } |
272 | |
273 | /* |
274 | * Load the file specified on the command line. |
275 | */ |
276 | static void load_file (char *fname) { |
277 | FILE *fp; |
278 | |
279 | file_size = 0; |
280 | if ( (fp = fopen (fname, "rb")) ) { |
281 | if (eager_mode) { |
282 | long len; |
283 | static char buffer[4096]; |
284 | |
285 | filedata = buf_new_empty(); |
286 | |
287 | file_size = 0; |
288 | |
289 | /* |
290 | * We've opened the file. Load it. |
291 | */ |
292 | while ( (len = fread (buffer, 1, sizeof(buffer), fp)) > 0 ) { |
293 | buf_insert_data (filedata, buffer, len, file_size); |
294 | file_size += len; |
295 | } |
296 | fclose (fp); |
297 | assert(file_size == buf_length(filedata)); |
298 | sprintf(message, "loaded %s (size %ld == 0x%lX).", |
299 | fname, file_size, file_size); |
300 | } else { |
301 | filedata = buf_new_from_file(fp); |
302 | file_size = buf_length(filedata); |
303 | sprintf(message, "opened %s (size %ld == 0x%lX).", |
304 | fname, file_size, file_size); |
305 | } |
306 | new_file = FALSE; |
307 | } else { |
308 | if (look_mode || fix_mode) { |
309 | fprintf(stderr, "%s: file %s not found, and %s mode active\n", |
310 | pname, fname, (look_mode ? "LOOK" : "FIX")); |
311 | exit (1); |
312 | } |
313 | filedata = buf_new_empty(); |
314 | sprintf(message, "New file %s.", fname); |
315 | new_file = TRUE; |
316 | } |
317 | } |
318 | |
319 | /* |
320 | * Save the file. Return TRUE on success, FALSE on error. |
321 | */ |
322 | int save_file (void) { |
323 | FILE *fp; |
324 | long pos = 0; |
325 | |
326 | if (look_mode) |
327 | return FALSE; /* do nothing! */ |
328 | |
329 | if ( (fp = fopen (filename, "wb")) ) { |
330 | static char buffer[SAVE_BLKSIZ]; |
331 | |
332 | while (pos < file_size) { |
333 | long size = file_size - pos; |
334 | if (size > SAVE_BLKSIZ) |
335 | size = SAVE_BLKSIZ; |
336 | |
337 | buf_fetch_data (filedata, buffer, size, pos); |
338 | if (size != fwrite (buffer, 1, size, fp)) { |
339 | fclose (fp); |
340 | return FALSE; |
341 | } |
342 | pos += size; |
343 | } |
344 | } else |
345 | return FALSE; |
346 | fclose (fp); |
347 | return TRUE; |
348 | } |
349 | |
350 | /* |
351 | * Make a backup of the file, if such has not already been done. |
352 | * Return TRUE on success, FALSE on error. |
353 | */ |
354 | int backup_file (void) { |
355 | char backup_name[FILENAME_MAX]; |
356 | |
357 | if (new_file) |
358 | return TRUE; /* unnecessary - pretend it's done */ |
359 | strcpy (backup_name, filename); |
360 | #if defined(unix) && !defined(GO32) |
361 | strcat (backup_name, ".bak"); |
362 | #elif defined(MSDOS) |
363 | { |
364 | char *p, *q; |
365 | |
366 | q = NULL; |
367 | for (p = backup_name; *p; p++) { |
368 | if (*p == '\\') |
369 | q = NULL; |
370 | else if (*p == '.') |
371 | q = p; |
372 | } |
373 | if (!q) |
374 | q = p; |
375 | strcpy (q, ".BAK"); |
376 | } |
377 | #endif |
378 | remove (backup_name); /* don't care if this fails */ |
379 | return !rename (filename, backup_name); |
380 | } |
381 | |
382 | static unsigned char *scrbuf = NULL; |
383 | static int scrbuflines = 0; |
384 | |
385 | /* |
386 | * Draw the screen, for normal usage. |
387 | */ |
388 | void draw_scr (void) { |
389 | int scrsize, scroff, llen, i, j; |
390 | long currpos; |
391 | int marktop, markbot, mark; |
392 | char *p; |
393 | unsigned char c, *q; |
394 | char *linebuf; |
395 | |
396 | scrlines = display_rows - 2; |
397 | if (scrlines > scrbuflines) { |
398 | scrbuf = (scrbuf ? |
399 | realloc(scrbuf, scrlines*width) : |
400 | malloc(scrlines*width)); |
401 | if (!scrbuf) { |
402 | done(); |
403 | fprintf(stderr, "%s: out of memory!\n", pname); |
404 | exit (2); |
405 | } |
406 | scrbuflines = scrlines; |
407 | } |
408 | |
409 | linebuf = malloc(width*4+20); |
410 | if (!linebuf) { |
411 | done(); |
412 | fprintf(stderr, "%s: out of memory!\n", pname); |
413 | exit (2); |
414 | } |
415 | memset (linebuf, ' ', width*4+13); |
416 | linebuf[width*4+13] = '\0'; |
417 | |
418 | if (top_pos == 0) |
419 | scroff = width - offset; |
420 | else |
421 | scroff = 0; |
422 | scrsize = scrlines * width - scroff; |
423 | if (scrsize > file_size - top_pos) |
424 | scrsize = file_size - top_pos; |
425 | |
426 | buf_fetch_data (filedata, scrbuf, scrsize, top_pos); |
427 | |
428 | scrsize += scroff; /* hack but it'll work */ |
429 | |
430 | mark = marking && (cur_pos != mark_point); |
431 | if (mark) { |
432 | if (cur_pos > mark_point) |
433 | marktop = mark_point, markbot = cur_pos; |
434 | else |
435 | marktop = cur_pos, markbot = mark_point; |
436 | } else |
437 | marktop = markbot = 0; /* placate gcc */ |
438 | |
439 | currpos = top_pos; |
440 | q = scrbuf; |
441 | |
442 | for (i=0; i<scrlines; i++) { |
443 | display_moveto (i, 0); |
444 | if (currpos<=cur_pos || currpos<file_size) { |
445 | p = hex[(currpos >> 24) & 0xFF]; |
446 | linebuf[0]=p[0]; |
447 | linebuf[1]=p[1]; |
448 | p = hex[(currpos >> 16) & 0xFF]; |
449 | linebuf[2]=p[0]; |
450 | linebuf[3]=p[1]; |
451 | p = hex[(currpos >> 8) & 0xFF]; |
452 | linebuf[4]=p[0]; |
453 | linebuf[5]=p[1]; |
454 | p = hex[currpos & 0xFF]; |
455 | linebuf[6]=p[0]; |
456 | linebuf[7]=p[1]; |
457 | for (j=0; j<width; j++) { |
458 | if (scrsize > 0) { |
459 | if (currpos == 0 && j < width-offset) |
460 | p = " ", c = ' '; |
461 | else |
462 | p = hex[*q], c = *q++; |
463 | scrsize--; |
464 | } else { |
465 | p = " ", c = ' '; |
466 | } |
467 | linebuf[11+3*j]=p[0]; |
468 | linebuf[12+3*j]=p[1]; |
469 | linebuf[13+3*width+j]=toprint[c]; |
470 | } |
471 | llen = (currpos ? width : offset); |
472 | if (mark && currpos<markbot && currpos+llen>marktop) { |
473 | /* |
474 | * Some of this line is marked. Maybe all. Whatever |
475 | * the precise details, there will be two regions |
476 | * requiring highlighting: a hex bit and an ascii |
477 | * bit. |
478 | */ |
479 | int localstart= (currpos<marktop?marktop:currpos) - currpos; |
480 | int localstop = (currpos+llen>markbot ? markbot : |
481 | currpos+llen) - currpos; |
482 | localstart += width-llen; |
483 | localstop += width-llen; |
484 | display_write_chars(linebuf, 11+3*localstart); |
485 | display_set_colour(COL_SELECT); |
486 | display_write_chars(linebuf+11+3*localstart, |
487 | 3*(localstop-localstart)-1); |
488 | display_set_colour(COL_BUFFER); |
489 | if (ascii_enabled) { |
490 | display_write_chars(linebuf+10+3*localstop, |
491 | 3+3*width+localstart-3*localstop); |
492 | display_set_colour(COL_SELECT); |
493 | display_write_chars(linebuf+13+3*width+localstart, |
494 | localstop-localstart); |
495 | display_set_colour(COL_BUFFER); |
496 | display_write_chars(linebuf+13+3*width+localstop, |
497 | width-localstop); |
498 | } else { |
499 | display_write_chars(linebuf+10+3*localstop, |
500 | 2+3*width-3*localstop); |
501 | } |
1610a3c6 |
502 | } else { |
503 | display_set_colour(COL_BUFFER); |
6e182d98 |
504 | display_write_chars(linebuf, |
505 | ascii_enabled ? 13+4*width : 10+3*width); |
1610a3c6 |
506 | } |
6e182d98 |
507 | } |
508 | currpos += (currpos ? width : offset); |
509 | display_clear_to_eol(); |
510 | } |
511 | |
512 | { |
513 | char status[80]; |
514 | int slen; |
515 | display_moveto (display_rows-2, 0); |
516 | display_set_colour(COL_STATUS); |
517 | sprintf(status, statfmt, |
518 | (modified ? "**" : " "), |
519 | filename, |
520 | (insert_mode ? "(Insert)" : |
521 | look_mode ? "(LOOK) " : |
522 | fix_mode ? "(FIX) " : "(Ovrwrt)"), |
523 | cur_pos, file_size); |
524 | slen = strlen(status); |
525 | if (slen > display_cols) |
526 | slen = display_cols; |
527 | display_write_chars(status, slen); |
528 | while (slen++ < display_cols) |
529 | display_write_str(" "); |
530 | display_set_colour(COL_BUFFER); |
531 | } |
532 | |
533 | display_moveto (display_rows-1, 0); |
534 | display_write_str (message); |
535 | display_clear_to_eol(); |
536 | message[0] = '\0'; |
537 | |
538 | i = cur_pos - top_pos; |
539 | if (top_pos == 0) |
540 | i += width - offset; |
541 | j = (edit_type ? (i%width)*3+10+edit_type : (i%width)+13+3*width); |
542 | if (j >= display_cols) |
543 | j = display_cols-1; |
544 | free (linebuf); |
545 | display_moveto (i/width, j); |
546 | display_refresh (); |
547 | } |
548 | |
ef7de295 |
549 | volatile int safe_update, update_required; |
550 | void update (void); |
551 | |
6e182d98 |
552 | /* |
553 | * Get a string, in the "minibuffer". Return TRUE on success, FALSE |
554 | * on break. Possibly syntax-highlight the entered string for |
555 | * backslash-escapes, depending on the "highlight" parameter. |
556 | */ |
557 | int get_str (char *prompt, char *buf, int highlight) { |
558 | int maxlen = 79 - strlen(prompt); /* limit to 80 - who cares? :) */ |
559 | int len = 0; |
560 | int c; |
561 | |
562 | for (EVER) { |
563 | display_moveto (display_rows-1, 0); |
564 | display_set_colour (COL_MINIBUF); |
565 | display_write_str (prompt); |
566 | if (highlight) { |
567 | char *q, *p = buf, *r = buf+len; |
568 | while (p<r) { |
569 | q = p; |
570 | if (*p == '\\') { |
571 | p++; |
572 | if (p<r && *p == '\\') |
573 | p++, display_set_colour(COL_ESCAPE); |
40d827aa |
574 | else if (p>=r || !isxdigit ((unsigned char)*p)) |
6e182d98 |
575 | display_set_colour(COL_INVALID); |
40d827aa |
576 | else if (p+1>=r || !isxdigit ((unsigned char)p[1])) |
6e182d98 |
577 | p++, display_set_colour(COL_INVALID); |
578 | else |
579 | p+=2, display_set_colour(COL_ESCAPE); |
580 | } else { |
581 | while (p<r && *p != '\\') |
582 | p++; |
583 | display_set_colour (COL_MINIBUF); |
584 | } |
585 | display_write_chars (q, p-q); |
586 | } |
587 | } else |
588 | display_write_chars (buf, len); |
589 | display_set_colour (COL_MINIBUF); |
590 | display_clear_to_eol(); |
591 | display_refresh(); |
592 | if (update_required) |
593 | update(); |
594 | safe_update = TRUE; |
595 | c = display_getkey(); |
596 | safe_update = FALSE; |
597 | if (c == 13 || c == 10) { |
598 | buf[len] = '\0'; |
599 | return TRUE; |
600 | } else if (c == 27 || c == 7) { |
601 | display_beep(); |
602 | display_post_error(); |
603 | strcpy (message, "User Break!"); |
604 | return FALSE; |
605 | } |
606 | |
607 | if (c >= 32 && c <= 126) { |
608 | if (len < maxlen) |
609 | buf[len++] = c; |
610 | else |
611 | display_beep(); |
612 | } |
613 | |
614 | if ((c == 127 || c == 8) && len > 0) |
615 | len--; |
616 | |
617 | if (c == 'U'-'@') /* ^U kill line */ |
618 | len = 0; |
619 | } |
620 | } |
621 | |
622 | /* |
623 | * Take a buffer containing possible backslash-escapes, and return |
624 | * a buffer containing a (binary!) string. Since the string is |
625 | * binary, it cannot be null terminated: hence the length is |
626 | * returned from the function. The string is processed in place. |
627 | * |
628 | * Escapes are simple: a backslash followed by two hex digits |
629 | * represents that character; a doubled backslash represents a |
630 | * backslash itself; a backslash followed by anything else is |
631 | * invalid. (-1 is returned if an invalid sequence is detected.) |
632 | */ |
633 | int parse_quoted (char *buffer) { |
634 | char *p, *q; |
635 | |
636 | p = q = buffer; |
637 | while (*p) { |
638 | while (*p && *p != '\\') |
639 | *q++ = *p++; |
640 | if (*p == '\\') { |
641 | p++; |
642 | if (*p == '\\') |
643 | *q++ = *p++; |
40d827aa |
644 | else if (p[1] && isxdigit((unsigned char)*p) && |
645 | isxdigit((unsigned char)p[1])) { |
6e182d98 |
646 | char buf[3]; |
647 | buf[0] = *p++; |
648 | buf[1] = *p++; |
649 | buf[2] = '\0'; |
650 | *q++ = strtol(buf, NULL, 16); |
651 | } else |
652 | return -1; |
653 | } |
654 | } |
655 | return q - buffer; |
656 | } |
657 | |
658 | /* |
659 | * Suspend program. (Or shell out, depending on OS, of course.) |
660 | */ |
661 | void suspend(void) { |
662 | #if defined(unix) && !defined(GO32) |
663 | done(); |
664 | raise (SIGTSTP); |
665 | init(); |
666 | #elif defined(MSDOS) |
667 | done(); |
668 | spawnl (P_WAIT, getenv("COMSPEC"), "", NULL); |
669 | init(); |
670 | #else |
671 | display_beep(); |
672 | strcpy(message, "Suspend function not yet implemented."); |
673 | #endif |
674 | } |
675 | |
6e182d98 |
676 | void update (void) { |
677 | display_recheck_size(); |
678 | fix_offset (); |
679 | draw_scr (); |
680 | } |
681 | |
682 | void schedule_update(void) { |
683 | if (safe_update) |
684 | update(); |
685 | else |
686 | update_required = TRUE; |
687 | } |
688 | |
689 | long parse_num (char *buffer, int *error) { |
690 | if (error) |
691 | *error = FALSE; |
692 | if (!buffer[strspn(buffer, "0123456789")]) { |
693 | /* interpret as decimal */ |
694 | return atoi(buffer); |
695 | } else if (buffer[0]=='0' && (buffer[1]=='X' || buffer[1]=='x') && |
696 | !buffer[2+strspn(buffer+2,"0123456789ABCDEFabcdef")]) { |
697 | return strtol(buffer+2, NULL, 16); |
698 | } else if (buffer[0]=='$' && |
699 | !buffer[1+strspn(buffer+1,"0123456789ABCDEFabcdef")]) { |
700 | return strtol(buffer+1, NULL, 16); |
701 | } else { |
702 | return 0; |
703 | if (error) |
704 | *error = TRUE; |
705 | } |
706 | } |