source: rtems/cpukit/libmisc/shell/main_edit.c @ c319945

5
Last change on this file since c319945 was c319945, checked in by Joel Sherrill <joel@…>, on 03/13/19 at 15:07:23

main_edit.c: Use strncpy() to eliminate potential buffer overflow.

  • Property mode set to 100644
File size: 55.6 KB
Line 
1//
2// edit.c
3//
4// Text editor
5//
6// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions
10// are met:
11//
12// 1. Redistributions of source code must retain the above copyright
13//    notice, this list of conditions and the following disclaimer.
14// 2. Redistributions in binary form must reproduce the above copyright
15//    notice, this list of conditions and the following disclaimer in the
16//    documentation and/or other materials provided with the distribution.
17// 3. Neither the name of the project nor the names of its contributors
18//    may be used to endorse or promote products derived from this software
19//    without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
25// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31// SUCH DAMAGE.
32//
33
34#include <signal.h>
35#include <stdlib.h>
36#include <stdio.h>
37#include <stdarg.h>
38#include <string.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <unistd.h>
42#include <sys/stat.h>
43
44#ifdef SANOS
45#include <os.h>
46#endif
47
48#ifdef __rtems__
49#include <assert.h>
50#include <rtems.h>
51#include <rtems/shell.h>
52#endif
53
54#if defined(__linux__) || defined(__rtems__)
55#include <sys/ioctl.h>
56#include <termios.h>
57#define O_BINARY 0
58static int linux_console;
59#endif
60
61#define MINEXTEND      32768
62#define LINEBUF_EXTRA  32
63
64#ifndef TABSIZE
65#define TABSIZE        8
66#endif
67
68#ifndef INDENT
69#define INDENT "  "
70#endif
71
72#define CLRSCR           "\033[0J\x1b[H\x1b[J"
73#define CLREOL           "\033[K"
74#define GOTOXY           "\033[%d;%dH"
75#define RESET_COLOR      "\033[0m"
76
77#ifdef COLOR
78#define TEXT_COLOR       "\033[44m\033[37m\033[1m"
79#define SELECT_COLOR     "\033[47m\033[37m\033[1m"
80#define STATUS_COLOR     "\033[0m\033[47m\033[30m"
81#else
82#define TEXT_COLOR       "\033[0m"
83#define SELECT_COLOR     "\033[7m\033[1m"
84#define STATUS_COLOR     "\033[1m\033[7m"
85#endif
86
87//
88// Key codes
89//
90
91#define KEY_BACKSPACE        0x101
92#define KEY_ESC              0x102
93#define KEY_INS              0x103
94#define KEY_DEL              0x104
95#define KEY_LEFT             0x105
96#define KEY_RIGHT            0x106
97#define KEY_UP               0x107
98#define KEY_DOWN             0x108
99#define KEY_HOME             0x109
100#define KEY_END              0x10A
101#define KEY_ENTER            0x10B
102#define KEY_TAB              0x10C
103#define KEY_PGUP             0x10D
104#define KEY_PGDN             0x10E
105
106#define KEY_CTRL_LEFT        0x10F
107#define KEY_CTRL_RIGHT       0x110
108#define KEY_CTRL_UP          0x111
109#define KEY_CTRL_DOWN        0x112
110#define KEY_CTRL_HOME        0x113
111#define KEY_CTRL_END         0x114
112#define KEY_CTRL_TAB         0x115
113
114#define KEY_SHIFT_LEFT       0x116
115#define KEY_SHIFT_RIGHT      0x117
116#define KEY_SHIFT_UP         0x118
117#define KEY_SHIFT_DOWN       0x119
118#define KEY_SHIFT_PGUP       0x11A
119#define KEY_SHIFT_PGDN       0x11B
120#define KEY_SHIFT_HOME       0x11C
121#define KEY_SHIFT_END        0x11D
122#define KEY_SHIFT_TAB        0x11E
123
124#define KEY_SHIFT_CTRL_LEFT  0x11F
125#define KEY_SHIFT_CTRL_RIGHT 0x120
126#define KEY_SHIFT_CTRL_UP    0x121
127#define KEY_SHIFT_CTRL_DOWN  0x122
128#define KEY_SHIFT_CTRL_HOME  0x123
129#define KEY_SHIFT_CTRL_END   0x124
130
131#define KEY_F1               0x125
132#define KEY_F2               0x126
133#define KEY_F3               0x127
134#define KEY_F4               0x128
135#define KEY_F5               0x129
136#define KEY_F6               0x12a
137#define KEY_F7               0x12b
138#define KEY_F8               0x12c
139#define KEY_F9               0x12d
140#define KEY_F10              0x12e
141
142#define KEY_UNKNOWN          0xFFF
143
144#define ctrl(c) ((c) - 0x60)
145
146//
147// Editor data block
148//
149// Structure of split buffer:
150//
151//    +------------------+------------------+------------------+
152//    | text before gap  |        gap       |  text after gap  |
153//    +------------------+------------------+------------------+
154//    ^                  ^                  ^                  ^
155//    |                  |                  |                  |
156//  start               gap                rest               end
157//
158
159struct env;
160
161struct undo {
162  int pos;                 // Editor position
163  int erased;              // Size of erased contents
164  int inserted;            // Size of inserted contents
165  unsigned char *undobuf;  // Erased contents for undo
166  unsigned char *redobuf;  // Inserted contents for redo
167  struct undo *next;       // Next undo buffer
168  struct undo *prev;       // Previous undo buffer
169};
170
171struct editor {
172  unsigned char *start;      // Start of text buffer
173  unsigned char *gap;        // Start of gap
174  unsigned char *rest;       // End of gap
175  unsigned char *end;        // End of text buffer
176
177  int toppos;                // Text position for current top screen line
178  int topline;               // Line number for top of screen
179  int margin;                // Position for leftmost column on screen
180
181  int linepos;               // Text position for current line
182  int line;                  // Current document line
183  int col;                   // Current document column
184  int lastcol;               // Remembered column from last horizontal navigation
185  int anchor;                // Anchor position for selection
186
187  struct undo *undohead;     // Start of undo buffer list
188  struct undo *undotail;     // End of undo buffer list
189  struct undo *undo;         // Undo/redo boundary
190
191  int refresh;               // Flag to trigger screen redraw
192  int lineupdate;            // Flag to trigger redraw of current line
193  int dirty;                 // Dirty flag is set when the editor buffer has been changed
194
195  int newfile;               // File is a new file
196  int permissions;           // File permissions
197
198  int selecting;             // Selecting active in the edtor.
199
200  struct env *env;           // Reference to global editor environment
201  struct editor *next;       // Next editor
202  struct editor *prev;       // Previous editor
203
204  char filename[FILENAME_MAX];
205};
206
207struct env {
208  struct editor *current;   // Current editor
209
210  unsigned char *clipboard; // Clipboard
211  int clipsize;             // Clipboard size
212
213  unsigned char *search;    // Search text
214  unsigned char *linebuf;   // Scratch buffer
215
216  int cols;                 // Console columns
217  int lines;                // Console lines
218
219  int untitled;             // Counter for untitled files
220};
221
222//
223// Editor buffer functions
224//
225
226static void clear_undo(struct editor *ed) {
227  struct undo *undo = ed->undohead;
228  while (undo) {
229    struct undo *next = undo->next;
230    free(undo->undobuf);
231    free(undo->redobuf);
232    free(undo);
233    undo = next;
234  }
235  ed->undohead = ed->undotail = ed->undo = NULL;
236}
237
238static void reset_undo(struct editor *ed) {
239  while (ed->undotail != ed->undo) {
240    struct undo *undo = ed->undotail;
241    if (!undo) {
242      ed->undohead = NULL;
243      ed->undotail = NULL;
244      break;
245    }
246    ed->undotail = undo->prev;
247    if (undo->prev) undo->prev->next = NULL;
248    free(undo->undobuf);
249    free(undo->redobuf);
250    free(undo);
251  }
252  ed->undo = ed->undotail;
253}
254
255static struct editor *create_editor(struct env *env) {
256  struct editor *ed = (struct editor *) malloc(sizeof(struct editor));
257  memset(ed, 0, sizeof(struct editor));
258  if (env->current) {
259    ed->next = env->current->next;
260    ed->prev = env->current;
261    env->current->next->prev = ed;
262    env->current->next = ed;
263  } else {
264    ed->next = ed->prev = ed;
265  }
266  ed->env = env;
267  env->current = ed;
268  return ed;
269}
270
271static void delete_editor(struct editor *ed) {
272  if (ed->next == ed) {
273    ed->env->current = NULL;
274  } else {
275    ed->env->current = ed->prev;
276  }
277  ed->next->prev = ed->prev;
278  ed->prev->next = ed->next;
279  if (ed->start) free(ed->start);
280  clear_undo(ed);
281  free(ed);
282}
283
284static struct editor *find_editor(struct env *env, char *filename) {
285  char fn[FILENAME_MAX];
286  struct editor *ed = env->current;
287  struct editor *start = ed;
288
289  if (!realpath(filename, fn)) strncpy(fn, filename, FILENAME_MAX);
290
291  do {
292    if (strcmp(fn, ed->filename) == 0) return ed;
293    ed = ed->next;
294  } while (ed != start);
295  return NULL;
296}
297
298static int new_file(struct editor *ed, char *filename) {
299  if (*filename) {
300    strncpy(ed->filename, filename, FILENAME_MAX);
301  } else {
302    sprintf(ed->filename, "Untitled-%d", ++ed->env->untitled);
303    ed->newfile = 1;
304  }
305  ed->permissions = 0644;
306
307  ed->start = (unsigned char *) malloc(MINEXTEND);
308  if (!ed->start) return -1;
309#ifdef DEBUG
310  memset(ed->start, 0, MINEXTEND);
311#endif
312
313  ed->gap = ed->start;
314  ed->rest = ed->end = ed->gap + MINEXTEND;
315  ed->anchor = -1;
316
317  return 0;
318}
319
320static int load_file(struct editor *ed, char *filename) {
321  struct stat statbuf;
322  int length;
323  int f;
324
325  if (!realpath(filename, ed->filename)) return -1;
326  f = open(ed->filename, O_RDONLY | O_BINARY);
327  if (f < 0) return -1;
328
329  if (fstat(f, &statbuf) < 0) goto err;
330  length = statbuf.st_size;
331  ed->permissions = statbuf.st_mode & 0777;
332
333  ed->start = (unsigned char *) malloc(length + MINEXTEND);
334  if (!ed->start) goto err;
335#ifdef DEBUG
336  memset(ed->start, 0, length + MINEXTEND);
337#endif
338  if (read(f, ed->start, length) != length) goto err;
339
340  ed->gap = ed->start + length;
341  ed->rest = ed->end = ed->gap + MINEXTEND;
342  ed->anchor = -1;
343
344  close(f);
345  return 0;
346
347err:
348  close(f);
349  if (ed->start) {
350    free(ed->start);
351    ed->start = NULL;
352  }
353  return -1;
354}
355
356static int save_file(struct editor *ed) {
357  int f;
358
359  f = open(ed->filename, O_CREAT | O_TRUNC | O_WRONLY, ed->permissions);
360  if (f < 0) return -1;
361
362  if (write(f, ed->start, ed->gap - ed->start) != ed->gap - ed->start) goto err;
363  if (write(f, ed->rest, ed->end - ed->rest) != ed->end - ed->rest) goto err;
364
365  close(f);
366  ed->dirty = 0;
367  clear_undo(ed);
368  return 0;
369
370err:
371  close(f);
372  return -1;
373}
374
375static int text_length(struct editor *ed) {
376  return (ed->gap - ed->start) + (ed->end - ed->rest);
377}
378
379static unsigned char *text_ptr(struct editor *ed, int pos) {
380  unsigned char *p = ed->start + pos;
381  if (p >= ed->gap) p += (ed->rest - ed->gap);
382  return p;
383}
384
385static void move_gap(struct editor *ed, int pos, int minsize) {
386  int gapsize = ed->rest - ed->gap;
387  unsigned char *p = text_ptr(ed, pos);
388  if (minsize < 0) minsize = 0;
389
390  if (minsize <= gapsize) {
391    if (p != ed->rest) {
392      if (p < ed->gap) {
393        memmove(p + gapsize, p, ed->gap - p);
394      } else {
395        memmove(ed->gap, ed->rest, p - ed->rest);
396      }
397      ed->gap = ed->start + pos;
398      ed->rest = ed->gap + gapsize;
399    }
400  } else {
401    int newsize;
402    unsigned char *start;
403    unsigned char *gap;
404    unsigned char *rest;
405    unsigned char *end;
406
407    if (gapsize + MINEXTEND > minsize) minsize = gapsize + MINEXTEND;
408    newsize = (ed->end - ed->start) - gapsize + minsize;
409    start = (unsigned char *) malloc(newsize); // TODO check for out of memory
410    gap = start + pos;
411    rest = gap + minsize;
412    end = start + newsize;
413
414    if (p < ed->gap) {
415      memcpy(start, ed->start, pos);
416      memcpy(rest, p, ed->gap - p);
417      memcpy(end - (ed->end - ed->rest), ed->rest, ed->end - ed->rest);
418    } else {
419      memcpy(start, ed->start, ed->gap - ed->start);
420      memcpy(start + (ed->gap - ed->start), ed->rest, p - ed->rest);
421      memcpy(rest, p, ed->end - p);
422    }
423
424    free(ed->start);
425    ed->start = start;
426    ed->gap = gap;
427    ed->rest = rest;
428    ed->end = end;
429  }
430
431#ifdef DEBUG
432  memset(ed->gap, 0, ed->rest - ed->gap);
433#endif
434}
435
436static void close_gap(struct editor *ed) {
437  int len = text_length(ed);
438  move_gap(ed, len, 1);
439  ed->start[len] = 0;
440}
441
442static int get(struct editor *ed, int pos) {
443  unsigned char *p = text_ptr(ed, pos);
444  if (p >= ed->end) return -1;
445  return *p;
446}
447
448static int compare(struct editor *ed, unsigned char *buf, int pos, int len) {
449  unsigned char *bufptr = buf;
450  unsigned char *p = ed->start + pos;
451  if (p >= ed->gap) p += (ed->rest - ed->gap);
452
453  while (len > 0) {
454    if (p == ed->end) return 0;
455    if (*bufptr++ != *p) return 0;
456    len--;
457    if (++p == ed->gap) p = ed->rest;
458  }
459
460  return 1;
461}
462
463static int copy(struct editor *ed, unsigned char *buf, int pos, int len) {
464  unsigned char *bufptr = buf;
465  unsigned char *p = ed->start + pos;
466  if (p >= ed->gap) p += (ed->rest - ed->gap);
467
468  while (len > 0) {
469    if (p == ed->end) break;
470    *bufptr++ = *p;
471    len--;
472    if (++p == ed->gap) p = ed->rest;
473  }
474
475  return bufptr - buf;
476}
477
478static void replace(struct editor *ed, int pos, int len, unsigned char *buf, int bufsize, int doundo) {
479  unsigned char *p;
480  struct undo *undo;
481
482  // Store undo information
483  if (doundo) {
484    reset_undo(ed);
485    undo = ed->undotail;
486    if (undo && len == 0 && bufsize == 1 && undo->erased == 0 && pos == undo->pos + undo->inserted) {
487      // Insert character at end of current redo buffer
488      undo->redobuf = realloc(undo->redobuf, undo->inserted + 1);
489      undo->redobuf[undo->inserted] = *buf;
490      undo->inserted++;
491    } else if (undo && len == 1 && bufsize == 0 && undo->inserted == 0 && pos == undo->pos) {
492      // Erase character at end of current undo buffer
493      undo->undobuf = realloc(undo->undobuf, undo->erased + 1);
494      undo->undobuf[undo->erased] = get(ed, pos);
495      undo->erased++;
496    } else if (undo && len == 1 && bufsize == 0 && undo->inserted == 0 && pos == undo->pos - 1) {
497      // Erase character at beginning of current undo buffer
498      undo->pos--;
499      undo->undobuf = realloc(undo->undobuf, undo->erased + 1);
500      memmove(undo->undobuf + 1, undo->undobuf, undo->erased);
501      undo->undobuf[0] = get(ed, pos);
502      undo->erased++;
503    } else {
504      // Create new undo buffer
505      undo = (struct undo *) malloc(sizeof(struct undo));
506      if (ed->undotail) ed->undotail->next = undo;
507      undo->prev = ed->undotail;
508      undo->next = NULL;
509      ed->undotail = ed->undo = undo;
510      if (!ed->undohead) ed->undohead = undo;
511
512      undo->pos = pos;
513      undo->erased = len;
514      undo->inserted = bufsize;
515      undo->undobuf = undo->redobuf = NULL;
516      if (len > 0) {
517        undo->undobuf = malloc(len);
518        copy(ed, undo->undobuf, pos, len);
519      }
520      if (bufsize > 0) {
521        undo->redobuf = malloc(bufsize);
522        memcpy(undo->redobuf, buf, bufsize);
523      }
524    }
525  }
526
527  p = ed->start + pos;
528  if (bufsize == 0 && p <= ed->gap && p + len >= ed->gap) {
529    // Handle deletions at the edges of the gap
530    ed->rest += len - (ed->gap - p);
531    ed->gap = p;
532  } else {
533    // Move the gap
534    move_gap(ed, pos + len, bufsize - len);
535
536    // Replace contents
537    memcpy(ed->start + pos, buf, bufsize);
538    ed->gap = ed->start + pos + bufsize;
539  }
540
541  // Mark buffer as dirty
542  ed->dirty = 1;
543}
544
545static void insert(struct editor *ed, int pos, unsigned char *buf, int bufsize) {
546  replace(ed, pos, 0, buf, bufsize, 1);
547}
548
549static void erase(struct editor *ed, int pos, int len) {
550  replace(ed, pos, len, NULL, 0, 1);
551}
552
553//
554// Navigation functions
555//
556
557static int line_length(struct editor *ed, int linepos) {
558  int pos = linepos;
559  while (1) {
560    int ch = get(ed, pos);
561    if (ch < 0 || ch == '\n' || ch == '\r') break;
562    pos++;
563  }
564
565  return pos - linepos;
566}
567
568static int line_start(struct editor *ed, int pos) {
569  while (1) {
570    if (pos == 0) break;
571    if (get(ed, pos - 1) == '\n') break;
572    pos--;
573  }
574
575  return pos;
576}
577
578static int next_line(struct editor *ed, int pos) {
579  while (1) {
580    int ch = get(ed, pos);
581    if (ch < 0) return -1;
582    pos++;
583    if (ch == '\n') return pos;
584  }
585}
586
587static int prev_line(struct editor *ed, int pos) {
588  if (pos == 0) return -1;
589
590  while (pos > 0) {
591    int ch = get(ed, --pos);
592    if (ch == '\n') break;
593  }
594
595  while (pos > 0) {
596    int ch = get(ed, --pos);
597    if (ch == '\n') return pos + 1;
598  }
599
600  return 0;
601}
602
603static int column(struct editor *ed, int linepos, int col) {
604  unsigned char *p = text_ptr(ed, linepos);
605  int c = 0;
606  while (col > 0) {
607    if (p == ed->end) break;
608    if (*p == '\t') {
609      int spaces = TABSIZE - c % TABSIZE;
610      c += spaces;
611    } else {
612      c++;
613    }
614    col--;
615    if (++p == ed->gap) p = ed->rest;
616  }
617  return c;
618}
619
620static void moveto(struct editor *ed, int pos, int center) {
621  int scroll = 0;
622  for (;;) {
623    int cur = ed->linepos + ed->col;
624    if (pos < cur) {
625      if (pos >= ed->linepos) {
626        ed->col = pos - ed->linepos;
627      } else {
628        ed->col = 0;
629        ed->linepos = prev_line(ed, ed->linepos);
630        ed->line--;
631
632        if (ed->topline > ed->line) {
633          ed->toppos = ed->linepos;
634          ed->topline--;
635          ed->refresh = 1;
636          scroll = 1;
637        }
638      }
639    } else if (pos > cur) {
640      int next = next_line(ed, ed->linepos);
641      if (next == -1) {
642        ed->col = line_length(ed, ed->linepos);
643        break;
644      } else if (pos < next) {
645        ed->col = pos - ed->linepos;
646      } else {
647        ed->col = 0;
648        ed->linepos = next;
649        ed->line++;
650
651        if (ed->line >= ed->topline + ed->env->lines) {
652          ed->toppos = next_line(ed, ed->toppos);
653          ed->topline++;
654          ed->refresh = 1;
655          scroll = 1;
656        }
657      }
658    } else {
659      break;
660    }
661  }
662
663  if (scroll && center) {
664    int tl = ed->line - ed->env->lines / 2;
665    if (tl < 0) tl = 0;
666    for (;;) {
667      if (ed->topline > tl) {
668        ed->toppos = prev_line(ed, ed->toppos);
669        ed->topline--;
670      } else if (ed->topline < tl) {
671        ed->toppos = next_line(ed, ed->toppos);
672        ed->topline++;
673      } else {
674        break;
675      }
676    }
677  }
678}
679
680//
681// Text selection
682//
683
684static int get_selection(struct editor *ed, int *start, int *end) {
685  if (ed->anchor == -1) {
686    *start = *end = -1;
687    return 0;
688  } else {
689    int pos = ed->linepos + ed->col;
690    if (pos == ed->anchor) {
691      *start = *end = -1;
692      return 0;
693    } else if (pos < ed->anchor) {
694      *start = pos;
695      *end = ed->anchor;
696    } else {
697      *start = ed->anchor;
698      *end = pos;
699    }
700  }
701  return 1;
702}
703
704static int get_selected_text(struct editor *ed, char *buffer, int size) {
705  int selstart, selend, len;
706
707  if (!get_selection(ed, &selstart, &selend)) return 0;
708  len = selend - selstart;
709  if (len >= size) return 0;
710  copy(ed, (unsigned char*) buffer, selstart, len);
711  buffer[len] = 0;
712  return len;
713}
714
715static void update_selection(struct editor *ed, int select) {
716  if (select) {
717    if (ed->anchor == -1) ed->anchor = ed->linepos + ed->col;
718    ed->refresh = 1;
719  } else {
720    if (ed->anchor != -1) ed->refresh = 1;
721    ed->anchor = -1;
722  }
723}
724
725static int erase_selection(struct editor *ed) {
726  int selstart, selend;
727
728  if (!get_selection(ed, &selstart, &selend)) return 0;
729  moveto(ed, selstart, 0);
730  erase(ed, selstart, selend - selstart);
731  ed->anchor = -1;
732  ed->refresh = 1;
733  return 1;
734}
735
736static void select_all(struct editor *ed) {
737  ed->anchor = 0;
738  ed->refresh = 1;
739  moveto(ed, text_length(ed), 0);
740}
741
742//
743// Screen functions
744//
745
746static void get_console_size(struct env *env) {
747#ifdef __linux__
748  struct winsize ws;
749  ioctl(0, TIOCGWINSZ, &ws);
750  env->cols = ws.ws_col;
751  env->lines = ws.ws_row - 1;
752#elif defined(__rtems__)
753  env->cols = 80;
754  env->lines = 25;
755#else
756  struct term *term = gettib()->proc->term;
757  env->cols = term->cols;
758  env->lines = term->lines - 1;
759#endif
760  env->linebuf = realloc(env->linebuf, env->cols + LINEBUF_EXTRA);
761}
762
763static void outch(char c) {
764  putchar(c);
765}
766
767static void outbuf(unsigned char *buf, int len) {
768  fwrite(buf, 1, len, stdout);
769}
770
771static void outstr(char *str) {
772  fputs(str, stdout);
773}
774
775static void clear_screen(void) {
776  outstr(CLRSCR);
777}
778
779static void gotoxy(int col, int line) {
780  char buf[32];
781
782  sprintf(buf, GOTOXY, line + 1, col + 1);
783  outstr(buf);
784}
785
786//
787// Keyboard functions
788//
789
790static void get_modifier_keys(int *shift, int *ctrl) {
791  *shift = *ctrl = 0;
792#ifdef __linux__
793  if (linux_console) {
794    char modifiers = 6;
795    if (ioctl(0, TIOCLINUX, &modifiers) >= 0) {
796      if (modifiers & 1) *shift = 1;
797      if (modifiers & 4) *ctrl = 1;
798    }
799  }
800#endif
801}
802
803static int getachar(void)
804{
805  int ch = getchar();
806  return ch;
807}
808
809static int getkey(void) {
810  int ch, shift, ctrl;
811
812  ch = getachar();
813  if (ch < 0) return ch;
814
815  switch (ch) {
816    case 0x08: return KEY_BACKSPACE;
817    case 0x09:
818      get_modifier_keys(&shift, &ctrl);
819      if (shift) return KEY_SHIFT_TAB;
820      if (ctrl) return KEY_CTRL_TAB;
821      return KEY_TAB;
822#ifdef SANOS
823    case 0x0D: return gettib()->proc->term->type == TERM_CONSOLE ? KEY_ENTER : KEY_UNKNOWN;
824    case 0x0A: return gettib()->proc->term->type != TERM_CONSOLE ? KEY_ENTER : KEY_UNKNOWN;
825#else
826    case 0x0D: return KEY_ENTER;
827    case 0x0A: return KEY_ENTER;
828#endif
829    case 0x1B:
830      ch = getachar();
831      switch (ch) {
832        case 0x1B: return KEY_ESC;
833        case 0x4F:
834          ch = getachar();
835          switch (ch) {
836            case 0x46: return KEY_END;
837            case 0x48: return KEY_HOME;
838            case 0x50: return KEY_F1;
839            case 0x51: return KEY_F2;
840            case 0x52: return KEY_F3;
841            case 0x53: return KEY_F4;
842            case 0x54: return KEY_F5;
843            default: return KEY_UNKNOWN;
844          }
845          break;
846
847        case 0x5B:
848          get_modifier_keys(&shift, &ctrl);
849          ch = getachar();
850          if (ch == 0x31) {
851            ch = getachar();
852            switch (ch) {
853              case 0x35:
854                return getachar() == 0x7E ? KEY_F5 : KEY_UNKNOWN;
855              case 0x37:
856                return getachar() == 0x7E ? KEY_F6 : KEY_UNKNOWN;
857              case 0x3B:
858                ch = getachar();
859                if (ch == 0x7E) return KEY_F7;
860                if (ch == 0x32) shift = 1;
861                if (ch == 0x35) ctrl = 1;
862                if (ch == 0x36) shift = ctrl = 1;
863                ch = getachar();
864                break;
865              case 0x39:
866                return getachar() == 0x7E ? KEY_F8 : KEY_UNKNOWN;
867              case 0x7E:
868                if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
869                if (shift) return KEY_SHIFT_HOME;
870                if (ctrl) return KEY_CTRL_HOME;
871                return KEY_HOME;
872              default:
873                return KEY_UNKNOWN;
874            }
875          }
876
877          switch (ch) {
878            case 0x31:
879              ch = getachar();
880              if (ch != 0x7E) return KEY_UNKNOWN;
881              if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
882              if (shift) return KEY_SHIFT_HOME;
883              if (ctrl) return KEY_CTRL_HOME;
884              return KEY_HOME;
885            case 0x32:
886              ch = getachar();
887              switch (ch) {
888                case 0x30: ch = getachar(); return KEY_F9;
889                case 0x31: ch = getachar(); return KEY_F10;
890                case 0x7E: return KEY_INS;
891                default: break;
892              }
893              return KEY_UNKNOWN;
894            case 0x33: return getachar() == 0x7E ? KEY_DEL : KEY_UNKNOWN;
895            case 0x34:
896              if (getachar() != 0x7E) return KEY_UNKNOWN;
897              if (shift && ctrl) return KEY_SHIFT_CTRL_END;
898              if (shift) return KEY_SHIFT_END;
899              if (ctrl) return KEY_CTRL_END;
900              return KEY_END;
901            case 0x35:
902              if (getachar() != 0x7E) return KEY_UNKNOWN;
903              if (shift) return KEY_SHIFT_PGUP;
904              return KEY_PGUP;
905            case 0x36:
906              if (getachar() != 0x7E) return KEY_UNKNOWN;
907              if (shift) return KEY_SHIFT_PGDN;
908              return KEY_PGDN;
909            case 0x41:
910              if (shift && ctrl) return KEY_SHIFT_CTRL_UP;
911              if (shift) return KEY_SHIFT_UP;
912              if (ctrl) return KEY_CTRL_UP;
913              return KEY_UP;
914            case 0x42:
915              if (shift && ctrl) return KEY_SHIFT_CTRL_DOWN;
916              if (shift) return KEY_SHIFT_DOWN;
917              if (ctrl) return KEY_CTRL_DOWN;
918              return KEY_DOWN;
919            case 0x43:
920              if (shift && ctrl) return KEY_SHIFT_CTRL_RIGHT;
921              if (shift) return KEY_SHIFT_RIGHT;
922              if (ctrl) return KEY_CTRL_RIGHT;
923              return KEY_RIGHT;
924            case 0x44:
925              if (shift && ctrl) return KEY_SHIFT_CTRL_LEFT;
926              if (shift) return KEY_SHIFT_LEFT;
927              if (ctrl) return KEY_CTRL_LEFT;
928              return KEY_LEFT;
929            case 0x46:
930              if (shift && ctrl) return KEY_SHIFT_CTRL_END;
931              if (shift) return KEY_SHIFT_END;
932              if (ctrl) return KEY_CTRL_END;
933              return KEY_END;
934            case 0x48:
935              if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
936              if (shift) return KEY_SHIFT_HOME;
937              if (ctrl) return KEY_CTRL_HOME;
938              return KEY_HOME;
939            case 0x5A:
940              return KEY_SHIFT_TAB;
941            case 0x5B:
942              ch = getachar();
943              switch (ch) {
944                case 0x41: return KEY_F1;
945                case 0x43: return KEY_F3;
946                case 0x45: return KEY_F5;
947              }
948              return KEY_UNKNOWN;
949
950            default: return KEY_UNKNOWN;
951          }
952          break;
953
954        default: return KEY_UNKNOWN;
955      }
956      break;
957
958    case 0x00:
959    case 0xE0:
960      ch = getachar();
961      switch (ch) {
962        case 0x0F: return KEY_SHIFT_TAB;
963        case 0x3B: return KEY_F1;
964        case 0x3D: return KEY_F3;
965        case 0x3F: return KEY_F5;
966        case 0x47: return KEY_HOME;
967        case 0x48: return KEY_UP;
968        case 0x49: return KEY_PGUP;
969        case 0x4B: return KEY_LEFT;
970        case 0x4D: return KEY_RIGHT;
971        case 0x4F: return KEY_END;
972        case 0x50: return KEY_DOWN;
973        case 0x51: return KEY_PGDN;
974        case 0x52: return KEY_INS;
975        case 0x53: return KEY_DEL;
976        case 0x73: return KEY_CTRL_LEFT;
977        case 0x74: return KEY_CTRL_RIGHT;
978        case 0x75: return KEY_CTRL_END;
979        case 0x77: return KEY_CTRL_HOME;
980        case 0x8D: return KEY_CTRL_UP;
981        case 0x91: return KEY_CTRL_DOWN;
982        case 0x94: return KEY_CTRL_TAB;
983        case 0xB8: return KEY_SHIFT_UP;
984        case 0xB7: return KEY_SHIFT_HOME;
985        case 0xBF: return KEY_SHIFT_END;
986        case 0xB9: return KEY_SHIFT_PGUP;
987        case 0xBB: return KEY_SHIFT_LEFT;
988        case 0xBD: return KEY_SHIFT_RIGHT;
989        case 0xC0: return KEY_SHIFT_DOWN;
990        case 0xC1: return KEY_SHIFT_PGDN;
991        case 0xDB: return KEY_SHIFT_CTRL_LEFT;
992        case 0xDD: return KEY_SHIFT_CTRL_RIGHT;
993        case 0xD8: return KEY_SHIFT_CTRL_UP;
994        case 0xE0: return KEY_SHIFT_CTRL_DOWN;
995        case 0xD7: return KEY_SHIFT_CTRL_HOME;
996        case 0xDF: return KEY_SHIFT_CTRL_END;
997
998        default: return KEY_UNKNOWN;
999      }
1000      break;
1001
1002    case 0x7F: return KEY_BACKSPACE;
1003
1004    default: return ch;
1005  }
1006}
1007
1008static int prompt(struct editor *ed, char *msg, int selection) {
1009  int maxlen, len, ch;
1010  char *buf = (char*) ed->env->linebuf;
1011
1012  gotoxy(0, ed->env->lines);
1013  outstr(STATUS_COLOR);
1014  outstr(msg);
1015  outstr(CLREOL);
1016
1017  len = 0;
1018  maxlen = ed->env->cols - strlen(msg) - 1;
1019  if (selection) {
1020    len = get_selected_text(ed, buf, maxlen);
1021    outbuf((unsigned char*) buf, len);
1022  }
1023
1024  for (;;) {
1025    fflush(stdout);
1026    ch = getkey();
1027    if (ch == KEY_ESC) {
1028      return 0;
1029    } else if (ch == KEY_ENTER) {
1030      buf[len] = 0;
1031      return len > 0;
1032    } else if (ch == KEY_BACKSPACE) {
1033      if (len > 0) {
1034        outstr("\b \b");
1035        len--;
1036      }
1037    } else if (ch >= ' ' && ch < 0x100 && len < maxlen) {
1038      outch(ch);
1039      buf[len++] = ch;
1040    }
1041  }
1042}
1043
1044static int ask(void) {
1045  int ch = getachar();
1046  return ch == 'y' || ch == 'Y';
1047}
1048
1049//
1050// Display functions
1051//
1052
1053static void display_message(struct editor *ed, char *fmt, ...) {
1054  va_list args;
1055
1056  va_start(args, fmt);
1057  gotoxy(0, ed->env->lines);
1058  outstr(STATUS_COLOR);
1059  vprintf(fmt, args);
1060  outstr(CLREOL TEXT_COLOR);
1061  fflush(stdout);
1062  va_end(args);
1063}
1064
1065static void draw_full_statusline(struct editor *ed) {
1066  struct env *env = ed->env;
1067  int namewidth = env->cols - 29;
1068  gotoxy(0, env->lines);
1069  sprintf((char*) env->linebuf, STATUS_COLOR "%*.*sF1=Help %c%c Ln %-6dCol %-4d" CLREOL TEXT_COLOR, -namewidth, namewidth, ed->filename, ed->selecting ? '+' : ' ', ed->dirty ? '*' : ' ', ed->line + 1, column(ed, ed->linepos, ed->col) + 1);
1070  outstr((char*) env->linebuf);
1071}
1072
1073static void draw_statusline(struct editor *ed) {
1074  gotoxy(ed->env->cols - 20, ed->env->lines);
1075  sprintf((char*) ed->env->linebuf, STATUS_COLOR "%c Ln %-6dCol %-4d" CLREOL TEXT_COLOR, ed->dirty ? '*' : ' ', ed->line + 1, column(ed, ed->linepos, ed->col) + 1);
1076  outstr((char*) ed->env->linebuf);
1077}
1078
1079static void display_line(struct editor *ed, int pos, int fullline) {
1080  int hilite = 0;
1081  int col = 0;
1082  int margin = ed->margin;
1083  int maxcol = ed->env->cols + margin;
1084  unsigned char *bufptr = ed->env->linebuf;
1085  unsigned char *p = text_ptr(ed, pos);
1086  int selstart, selend, ch;
1087  char *s;
1088
1089  get_selection(ed, &selstart, &selend);
1090  while (col < maxcol) {
1091    if (margin == 0) {
1092      if (!hilite && pos >= selstart && pos < selend) {
1093        for (s = SELECT_COLOR; *s; s++) *bufptr++ = *s;
1094        hilite = 1;
1095      } else if (hilite && pos >= selend) {
1096        for (s = TEXT_COLOR; *s; s++) *bufptr++ = *s;
1097        hilite = 0;
1098      }
1099    }
1100
1101    if (p == ed->end) break;
1102    ch = *p;
1103    if (ch == '\r' || ch == '\n') break;
1104
1105    if (ch == '\t') {
1106      int spaces = TABSIZE - col % TABSIZE;
1107      while (spaces > 0 && col < maxcol) {
1108        if (margin > 0) {
1109          margin--;
1110        } else {
1111          *bufptr++ = ' ';
1112        }
1113        col++;
1114        spaces--;
1115      }
1116    } else {
1117      if (margin > 0) {
1118        margin--;
1119      } else {
1120        *bufptr++ = ch;
1121      }
1122      col++;
1123    }
1124
1125    if (++p == ed->gap) p = ed->rest;
1126    pos++;
1127  }
1128
1129#if defined(__linux__)
1130  if (hilite) {
1131    while (col < maxcol) {
1132      *bufptr++ = ' ';
1133      col++;
1134    }
1135  } else {
1136    if (col == margin) *bufptr++ = ' ';
1137  }
1138#endif
1139
1140  if (col < maxcol) {
1141    for (s = CLREOL; *s; s++) *bufptr++ = *s;
1142    if (fullline) {
1143      memcpy(bufptr, "\r\n", 2);
1144      bufptr += 2;
1145    }
1146  }
1147
1148  if (hilite) {
1149    for (s = TEXT_COLOR; *s; s++) *bufptr++ = *s;
1150  }
1151
1152  outbuf(ed->env->linebuf, bufptr - ed->env->linebuf);
1153}
1154
1155static void update_line(struct editor *ed) {
1156  gotoxy(0, ed->line - ed->topline);
1157  display_line(ed, ed->linepos, 0);
1158}
1159
1160static void draw_screen(struct editor *ed) {
1161  int pos;
1162  int i;
1163
1164  gotoxy(0, 0);
1165  outstr(TEXT_COLOR);
1166  pos = ed->toppos;
1167  for (i = 0; i < ed->env->lines; i++) {
1168    if (pos < 0) {
1169      outstr(CLREOL "\r\n");
1170    } else {
1171      display_line(ed, pos, 1);
1172      pos = next_line(ed, pos);
1173    }
1174  }
1175}
1176
1177static void position_cursor(struct editor *ed) {
1178  int col = column(ed, ed->linepos, ed->col);
1179  gotoxy(col - ed->margin, ed->line - ed->topline);
1180}
1181
1182//
1183// Cursor movement
1184//
1185
1186static void adjust(struct editor *ed) {
1187  int col;
1188  int ll = line_length(ed, ed->linepos);
1189  ed->col = ed->lastcol;
1190  if (ed->col > ll) ed->col = ll;
1191
1192  col = column(ed, ed->linepos, ed->col);
1193  while (col < ed->margin) {
1194    ed->margin -= 4;
1195    if (ed->margin < 0) ed->margin = 0;
1196    ed->refresh = 1;
1197  }
1198
1199  while (col - ed->margin >= ed->env->cols) {
1200    ed->margin += 4;
1201    ed->refresh = 1;
1202  }
1203}
1204
1205static void select_toggle(struct editor *ed) {
1206  ed->selecting = ed->selecting ? 0 : 1;
1207  update_selection(ed, ed->selecting);
1208  adjust(ed);
1209}
1210
1211static void up(struct editor *ed, int select) {
1212  int newpos;
1213
1214  update_selection(ed, select);
1215
1216  newpos = prev_line(ed, ed->linepos);
1217  if (newpos < 0) return;
1218
1219  ed->linepos = newpos;
1220  ed->line--;
1221  if (ed->line < ed->topline) {
1222    ed->toppos = ed->linepos;
1223    ed->topline = ed->line;
1224    ed->refresh = 1;
1225  }
1226
1227  adjust(ed);
1228}
1229
1230static void down(struct editor *ed, int select) {
1231  int newpos;
1232
1233  update_selection(ed, select);
1234
1235  newpos = next_line(ed, ed->linepos);
1236  if (newpos < 0) return;
1237
1238  ed->linepos = newpos;
1239  ed->line++;
1240
1241  if (ed->line >= ed->topline + ed->env->lines) {
1242    ed->toppos = next_line(ed, ed->toppos);
1243    ed->topline++;
1244    ed->refresh = 1;
1245  }
1246
1247  adjust(ed);
1248}
1249
1250static void left(struct editor *ed, int select) {
1251  update_selection(ed, select);
1252  if (ed->col > 0) {
1253    ed->col--;
1254  } else {
1255    int newpos = prev_line(ed, ed->linepos);
1256    if (newpos < 0) return;
1257
1258    ed->col = line_length(ed, newpos);
1259    ed->linepos = newpos;
1260    ed->line--;
1261    if (ed->line < ed->topline) {
1262      ed->toppos = ed->linepos;
1263      ed->topline = ed->line;
1264      ed->refresh = 1;
1265    }
1266  }
1267
1268  ed->lastcol = ed->col;
1269  adjust(ed);
1270}
1271
1272static void right(struct editor *ed, int select) {
1273  update_selection(ed, select);
1274  if (ed->col < line_length(ed, ed->linepos)) {
1275    ed->col++;
1276  } else {
1277    int newpos = next_line(ed, ed->linepos);
1278    if (newpos < 0) return;
1279
1280    ed->col = 0;
1281    ed->linepos = newpos;
1282    ed->line++;
1283
1284    if (ed->line >= ed->topline + ed->env->lines) {
1285      ed->toppos = next_line(ed, ed->toppos);
1286      ed->topline++;
1287      ed->refresh = 1;
1288    }
1289  }
1290
1291  ed->lastcol = ed->col;
1292  adjust(ed);
1293}
1294
1295static int wordchar(int ch) {
1296  return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');
1297}
1298
1299static void wordleft(struct editor *ed, int select) {
1300  int pos, phase;
1301
1302  update_selection(ed, select);
1303  pos = ed->linepos + ed->col;
1304  phase = 0;
1305  while (pos > 0) {
1306    int ch = get(ed, pos - 1);
1307    if (phase == 0) {
1308      if (wordchar(ch)) phase = 1;
1309    } else {
1310      if (!wordchar(ch)) break;
1311    }
1312
1313    pos--;
1314    if (pos < ed->linepos) {
1315      ed->linepos = prev_line(ed, ed->linepos);
1316      ed->line--;
1317      ed->refresh = 1;
1318    }
1319  }
1320  ed->col = pos - ed->linepos;
1321  if (ed->line < ed->topline) {
1322    ed->toppos = ed->linepos;
1323    ed->topline = ed->line;
1324  }
1325
1326  ed->lastcol = ed->col;
1327  adjust(ed);
1328}
1329
1330static void wordright(struct editor *ed, int select) {
1331  int pos, end, phase, next;
1332
1333  update_selection(ed, select);
1334  pos = ed->linepos + ed->col;
1335  end = text_length(ed);
1336  next = next_line(ed, ed->linepos);
1337  phase = 0;
1338  while (pos < end) {
1339    int ch = get(ed, pos);
1340    if (phase == 0) {
1341      if (wordchar(ch)) phase = 1;
1342    } else {
1343      if (!wordchar(ch)) break;
1344    }
1345
1346    pos++;
1347    if (pos == next) {
1348      ed->linepos = next;
1349      next = next_line(ed, ed->linepos);
1350      ed->line++;
1351      ed->refresh = 1;
1352    }
1353  }
1354  ed->col = pos - ed->linepos;
1355  if (ed->line >= ed->topline + ed->env->lines) {
1356    ed->toppos = next_line(ed, ed->toppos);
1357    ed->topline++;
1358  }
1359
1360  ed->lastcol = ed->col;
1361  adjust(ed);
1362}
1363
1364static void home(struct editor *ed, int select) {
1365  update_selection(ed, select);
1366  ed->col = ed->lastcol = 0;
1367  adjust(ed);
1368}
1369
1370static void end(struct editor *ed, int select) {
1371  update_selection(ed, select);
1372  ed->col = ed->lastcol = line_length(ed, ed->linepos);
1373  adjust(ed);
1374}
1375
1376static void top(struct editor *ed, int select) {
1377  update_selection(ed, select);
1378  ed->toppos = ed->topline = ed->margin = 0;
1379  ed->linepos = ed->line = ed->col = ed->lastcol = 0;
1380  ed->refresh = 1;
1381}
1382
1383static void bottom(struct editor *ed, int select) {
1384  update_selection(ed, select);
1385  for (;;) {
1386    int newpos = next_line(ed, ed->linepos);
1387    if (newpos < 0) break;
1388
1389    ed->linepos = newpos;
1390    ed->line++;
1391
1392    if (ed->line >= ed->topline + ed->env->lines) {
1393      ed->toppos = next_line(ed, ed->toppos);
1394      ed->topline++;
1395      ed->refresh = 1;
1396    }
1397  }
1398  ed->col = ed->lastcol = line_length(ed, ed->linepos);
1399  adjust(ed);
1400}
1401
1402static void pageup(struct editor *ed, int select) {
1403  int i;
1404
1405  update_selection(ed, select);
1406  if (ed->line < ed->env->lines) {
1407    ed->linepos = ed->toppos = 0;
1408    ed->line = ed->topline = 0;
1409  } else {
1410    for (i = 0; i < ed->env->lines; i++) {
1411      int newpos = prev_line(ed, ed->linepos);
1412      if (newpos < 0) return;
1413
1414      ed->linepos = newpos;
1415      ed->line--;
1416
1417      if (ed->topline > 0) {
1418        ed->toppos = prev_line(ed, ed->toppos);
1419        ed->topline--;
1420      }
1421    }
1422  }
1423
1424  ed->refresh = 1;
1425  adjust(ed);
1426}
1427
1428static void pagedown(struct editor *ed, int select) {
1429  int i;
1430
1431  update_selection(ed, select);
1432  for (i = 0; i < ed->env->lines; i++) {
1433    int newpos = next_line(ed, ed->linepos);
1434    if (newpos < 0) break;
1435
1436    ed->linepos = newpos;
1437    ed->line++;
1438
1439    ed->toppos = next_line(ed, ed->toppos);
1440    ed->topline++;
1441  }
1442
1443  ed->refresh = 1;
1444  adjust(ed);
1445}
1446
1447//
1448// Text editing
1449//
1450
1451static void insert_char(struct editor *ed, unsigned char ch) {
1452  erase_selection(ed);
1453  insert(ed, ed->linepos + ed->col, &ch, 1);
1454  ed->col++;
1455  ed->lastcol = ed->col;
1456  adjust(ed);
1457  if (!ed->refresh) ed->lineupdate = 1;
1458}
1459
1460static void newline(struct editor *ed) {
1461  int p;
1462  unsigned char ch;
1463
1464  erase_selection(ed);
1465#if defined(__linux__) || defined(__rtems__)
1466  insert(ed, ed->linepos + ed->col, (unsigned char*) "\n", 1);
1467#else
1468  insert(ed, ed->linepos + ed->col, "\r\n", 2);
1469#endif
1470  ed->col = ed->lastcol = 0;
1471  ed->line++;
1472  p = ed->linepos;
1473  ed->linepos = next_line(ed, ed->linepos);
1474  for (;;) {
1475    ch = get(ed, p++);
1476    if (ch == ' ' || ch == '\t') {
1477      insert(ed, ed->linepos + ed->col, &ch, 1);
1478      ed->col++;
1479    } else {
1480      break;
1481    }
1482  }
1483  ed->lastcol = ed->col;
1484
1485  ed->refresh = 1;
1486
1487  if (ed->line >= ed->topline + ed->env->lines) {
1488    ed->toppos = next_line(ed, ed->toppos);
1489    ed->topline++;
1490    ed->refresh = 1;
1491  }
1492
1493  adjust(ed);
1494}
1495
1496static void backspace(struct editor *ed) {
1497  if (erase_selection(ed)) return;
1498  if (ed->linepos + ed->col == 0) return;
1499  if (ed->col == 0) {
1500    int pos = ed->linepos;
1501    erase(ed, --pos, 1);
1502    if (get(ed, pos - 1) == '\r') erase(ed, --pos, 1);
1503
1504    ed->line--;
1505    ed->linepos = line_start(ed, pos);
1506    ed->col = pos - ed->linepos;
1507    ed->refresh = 1;
1508
1509    if (ed->line < ed->topline) {
1510      ed->toppos = ed->linepos;
1511      ed->topline = ed->line;
1512    }
1513  } else {
1514    ed->col--;
1515    erase(ed, ed->linepos + ed->col, 1);
1516    ed->lineupdate = 1;
1517  }
1518
1519  ed->lastcol = ed->col;
1520  adjust(ed);
1521}
1522
1523static void del(struct editor *ed) {
1524  int pos, ch;
1525
1526  if (erase_selection(ed)) return;
1527  pos = ed->linepos + ed->col;
1528  ch = get(ed, pos);
1529  if (ch < 0) return;
1530
1531  erase(ed, pos, 1);
1532  if (ch == '\r') {
1533    ch = get(ed, pos);
1534    if (ch == '\n') erase(ed, pos, 1);
1535  }
1536
1537  if (ch == '\n') {
1538    ed->refresh = 1;
1539  } else {
1540    ed->lineupdate = 1;
1541  }
1542}
1543
1544static void indent(struct editor *ed, unsigned char *indentation) {
1545  int start, end, i, lines, toplines, newline, ch;
1546  unsigned char *buffer, *p;
1547  int buflen;
1548  int width = strlen((const char*) indentation);
1549  int pos = ed->linepos + ed->col;
1550
1551  if (!get_selection(ed, &start, &end)) {
1552    insert_char(ed, '\t');
1553    return;
1554  }
1555
1556  lines = 0;
1557  toplines = 0;
1558  newline = 1;
1559  for (i = start; i < end; i++) {
1560    if (i == ed->toppos) toplines = lines;
1561    if (newline) {
1562      lines++;
1563      newline = 0;
1564    }
1565    if (get(ed, i) == '\n') newline = 1;
1566  }
1567  buflen = end - start + lines * width;
1568  buffer = malloc(buflen);
1569  if (!buffer) return;
1570
1571  newline = 1;
1572  p = buffer;
1573  for (i = start; i < end; i++) {
1574    if (newline) {
1575      memcpy(p, indentation, width);
1576      p += width;
1577      newline = 0;
1578    }
1579    ch = get(ed, i);
1580    *p++ = ch;
1581    if (ch == '\n') newline = 1;
1582  }
1583
1584  replace(ed, start, end - start, buffer, buflen, 1);
1585  free(buffer);
1586
1587  if (ed->anchor < pos) {
1588    pos += width * lines;
1589  } else {
1590    ed->anchor += width * lines;
1591  }
1592
1593  ed->toppos += width * toplines;
1594  ed->linepos = line_start(ed, pos);
1595  ed->col = ed->lastcol = pos - ed->linepos;
1596
1597  adjust(ed);
1598  ed->refresh = 1;
1599}
1600
1601static void unindent(struct editor *ed, unsigned char *indentation) {
1602  int start, end, i, newline, ch, shrinkage, topofs;
1603  unsigned char *buffer, *p;
1604  int width = strlen((const char*) indentation);
1605  int pos = ed->linepos + ed->col;
1606
1607  if (!get_selection(ed, &start, &end)) return;
1608
1609  buffer = malloc(end - start);
1610  if (!buffer) return;
1611
1612  newline = 1;
1613  p = buffer;
1614  i = start;
1615  shrinkage = 0;
1616  topofs = 0;
1617  while (i < end) {
1618    if (newline) {
1619      newline = 0;
1620      if (compare(ed, indentation, i, width)) {
1621        i += width;
1622        shrinkage += width;
1623        if (i < ed->toppos) topofs -= width;
1624        continue;
1625      }
1626    }
1627    ch = get(ed, i++);
1628    *p++ = ch;
1629    if (ch == '\n') newline = 1;
1630  }
1631
1632  if (!shrinkage) {
1633    free(buffer);
1634    return;
1635  }
1636
1637  replace(ed, start, end - start, buffer, p - buffer, 1);
1638  free(buffer);
1639
1640  if (ed->anchor < pos) {
1641    pos -= shrinkage;
1642  } else {
1643    ed->anchor -= shrinkage;
1644  }
1645
1646  ed->toppos += topofs;
1647  ed->linepos = line_start(ed, pos);
1648  ed->col = ed->lastcol = pos - ed->linepos;
1649
1650  ed->refresh = 1;
1651  adjust(ed);
1652}
1653
1654static void undo(struct editor *ed) {
1655  if (!ed->undo) return;
1656  moveto(ed, ed->undo->pos, 0);
1657  replace(ed, ed->undo->pos, ed->undo->inserted, ed->undo->undobuf, ed->undo->erased, 0);
1658  ed->undo = ed->undo->prev;
1659  if (!ed->undo) ed->dirty = 0;
1660  ed->anchor = -1;
1661  ed->lastcol = ed->col;
1662  ed->refresh = 1;
1663}
1664
1665static void redo(struct editor *ed) {
1666  if (ed->undo) {
1667    if (!ed->undo->next) return;
1668    ed->undo = ed->undo->next;
1669  } else {
1670    if (!ed->undohead) return;
1671    ed->undo = ed->undohead;
1672  }
1673  replace(ed, ed->undo->pos, ed->undo->erased, ed->undo->redobuf, ed->undo->inserted, 0);
1674  moveto(ed, ed->undo->pos, 0);
1675  ed->dirty = 1;
1676  ed->anchor = -1;
1677  ed->lastcol = ed->col;
1678  ed->refresh = 1;
1679}
1680
1681//
1682// Clipboard
1683//
1684
1685static void copy_selection(struct editor *ed) {
1686  int selstart, selend;
1687
1688  if (!get_selection(ed, &selstart, &selend)) return;
1689  ed->env->clipsize = selend - selstart;
1690  ed->env->clipboard = (unsigned char *) realloc(ed->env->clipboard, ed->env->clipsize);
1691  if (!ed->env->clipboard) return;
1692  copy(ed, ed->env->clipboard, selstart, ed->env->clipsize);
1693  select_toggle(ed);
1694}
1695
1696static void cut_selection(struct editor *ed) {
1697  copy_selection(ed);
1698  erase_selection(ed);
1699  select_toggle(ed);
1700}
1701
1702static void paste_selection(struct editor *ed) {
1703  erase_selection(ed);
1704  insert(ed, ed->linepos + ed->col, ed->env->clipboard, ed->env->clipsize);
1705  moveto(ed, ed->linepos + ed->col + ed->env->clipsize, 0);
1706  ed->refresh = 1;
1707}
1708
1709//
1710// Editor Commands
1711//
1712
1713static void open_editor(struct editor *ed) {
1714  int rc;
1715  char *filename;
1716  struct env *env = ed->env;
1717
1718  if (!prompt(ed, "Open file: ", 1)) {
1719    ed->refresh = 1;
1720    return;
1721  }
1722  filename = (char*) ed->env->linebuf;
1723
1724  ed = find_editor(ed->env, filename);
1725  if (ed) {
1726    env->current = ed;
1727  } else {
1728    ed = create_editor(env);
1729    rc = load_file(ed, filename);
1730    if (rc < 0) {
1731      display_message(ed, "Error %d opening %s (%s)", errno, filename, strerror(errno));
1732      sleep(5);
1733      delete_editor(ed);
1734      ed = env->current;
1735    }
1736  }
1737  ed->refresh = 1;
1738}
1739
1740static void new_editor(struct editor *ed) {
1741  ed = create_editor(ed->env);
1742  new_file(ed, "");
1743  ed->refresh = 1;
1744}
1745
1746static void read_from_stdin(struct editor *ed) {
1747  char buffer[512];
1748  int n, pos;
1749
1750  pos = 0;
1751  while ((n = fread(buffer, 1, sizeof(buffer), stdin)) > 0) {
1752    insert(ed, pos, (unsigned char*) buffer, n);
1753    pos += n;
1754  }
1755  strncpy(ed->filename, "<stdin>", FILENAME_MAX);
1756  ed->newfile = 1;
1757  ed->dirty = 0;
1758}
1759
1760static void save_editor(struct editor *ed) {
1761  int rc;
1762
1763  if (!ed->dirty && !ed->newfile) return;
1764
1765  if (ed->newfile) {
1766    if (!prompt(ed, "Save as: ", 1)) {
1767      ed->refresh = 1;
1768      return;
1769    }
1770
1771    if (access((const char*) ed->env->linebuf, F_OK) == 0) {
1772      display_message(ed, "Overwrite %s (y/n)? ", ed->env->linebuf);
1773      if (!ask()) {
1774        ed->refresh = 1;
1775        return;
1776      }
1777    }
1778    strncpy(
1779      ed->filename, (const char*) ed->env->linebuf, FILENAME_MAX);
1780    ed->newfile = 0;
1781  }
1782
1783  rc = save_file(ed);
1784  if (rc < 0) {
1785    display_message(ed, "Error %d saving document (%s)", errno, strerror(errno));
1786    sleep(5);
1787  }
1788
1789  ed->refresh = 1;
1790}
1791
1792static void close_editor(struct editor *ed) {
1793  struct env *env = ed->env;
1794
1795  if (ed->dirty) {
1796    display_message(ed, "Close %s without saving changes (y/n)? ", ed->filename);
1797    if (!ask()) {
1798      ed->refresh = 1;
1799      return;
1800    }
1801  }
1802
1803  delete_editor(ed);
1804
1805  ed = env->current;
1806  if (!ed) {
1807    ed = create_editor(env);
1808    new_file(ed, "");
1809  }
1810  ed->refresh = 1;
1811}
1812
1813static void pipe_command(struct editor *ed) {
1814#ifdef __rtems__
1815    display_message(ed, "Not supported");
1816    sleep(3);
1817#else
1818  FILE *f;
1819  char buffer[512];
1820  int n;
1821  int pos;
1822
1823  if (!prompt(ed, "Command: ", 1)) {
1824    ed->refresh = 1;
1825    return;
1826  }
1827
1828#ifdef SANOS
1829  f = popen(ed->env->linebuf, "r2");
1830#else
1831  f = popen(ed->env->linebuf, "r");
1832#endif
1833  if (!f) {
1834    display_message(ed, "Error %d running command (%s)", errno, strerror(errno));
1835    sleep(5);
1836  } else {
1837    erase_selection(ed);
1838    pos = ed->linepos + ed->col;
1839    while ((n = fread(buffer, 1, sizeof(buffer), f)) > 0) {
1840      insert(ed, pos, buffer, n);
1841      pos += n;
1842    }
1843    moveto(ed, pos, 0);
1844    pclose(f);
1845  }
1846  ed->refresh = 1;
1847#endif
1848}
1849
1850static void find_text(struct editor *ed, int next) {
1851  int slen;
1852
1853  if (!next) {
1854    if (!prompt(ed, "Find: ", 1)) {
1855      ed->refresh = 1;
1856      return;
1857    }
1858    if (ed->env->search) free(ed->env->search);
1859    ed->env->search = (unsigned char*) strdup((const char*) ed->env->linebuf);
1860  }
1861
1862  if (!ed->env->search) return;
1863  slen = strlen((const char*) ed->env->search);
1864  if (slen > 0) {
1865    unsigned char *match;
1866
1867    close_gap(ed);
1868    match = (unsigned char*) strstr((char*) ed->start + ed->linepos + ed->col, (char*) ed->env->search);
1869    if (match != NULL) {
1870      int pos = match - ed->start;
1871      ed->anchor = pos;
1872      moveto(ed, pos + slen, 1);
1873    } else {
1874      outch('\007');
1875    }
1876  }
1877  ed->refresh = 1;
1878}
1879
1880static void goto_line(struct editor *ed) {
1881  int lineno, l, pos;
1882
1883  ed->anchor = -1;
1884  if (prompt(ed, "Goto line: ", 1)) {
1885    lineno = atoi((char*) ed->env->linebuf);
1886    if (lineno > 0) {
1887      pos = 0;
1888      for (l = 0; l < lineno - 1; l++) {
1889        pos = next_line(ed, pos);
1890        if (pos < 0) break;
1891      }
1892    } else {
1893      pos = -1;
1894    }
1895
1896    if (pos >= 0) {
1897      moveto(ed, pos, 1);
1898    } else {
1899      outch('\007');
1900    }
1901  }
1902  ed->refresh = 1;
1903}
1904
1905static struct editor *next_file(struct editor *ed) {
1906  ed = ed->env->current = ed->next;
1907  ed->refresh = 1;
1908  return ed;
1909}
1910
1911static void jump_to_editor(struct editor *ed) {
1912  struct env *env = ed->env;
1913  char filename[FILENAME_MAX];
1914  int lineno = 0;
1915
1916  if (!get_selected_text(ed, filename, FILENAME_MAX)) {
1917    int pos = ed->linepos + ed->col;
1918    char *p = filename;
1919    int left = FILENAME_MAX - 1;
1920    while (left > 0) {
1921      int ch = get(ed, pos);
1922      if (ch < 0) break;
1923      if (strchr("!@\"'#%&()[]{}*?+:;\r\n\t ", ch)) break;
1924      *p++ = ch;
1925      left--;
1926      pos++;
1927    }
1928    *p = 0;
1929
1930    if (get(ed, pos) == ':') {
1931      pos++;
1932      for (;;) {
1933        int ch = get(ed, pos);
1934        if (ch < 0) break;
1935        if (ch >= '0' && ch <= '9') {
1936          lineno = lineno * 10 + (ch - '0');
1937        } else {
1938          break;
1939        }
1940        pos++;
1941      }
1942    }
1943  }
1944  if (!*filename) return;
1945
1946  ed = find_editor(env, filename);
1947  if (ed) {
1948    env->current = ed;
1949  } else {
1950    ed = create_editor(env);
1951    if (load_file(ed, filename) < 0) {
1952      outch('\007');
1953      delete_editor(ed);
1954      ed = env->current;
1955    }
1956  }
1957
1958  if (lineno > 0) {
1959    int pos = 0;
1960    while (--lineno > 0) {
1961      pos = next_line(ed, pos);
1962      if (pos < 0) break;
1963    }
1964    if (pos >= 0) moveto(ed, pos, 1);
1965  }
1966
1967  ed->refresh = 1;
1968}
1969
1970static void redraw_screen(struct editor *ed) {
1971  get_console_size(ed->env);
1972  draw_screen(ed);
1973}
1974
1975static int quit(struct env *env) {
1976  struct editor *ed = env->current;
1977  struct editor *start = ed;
1978
1979  do {
1980    if (ed->dirty) {
1981      display_message(ed, "Close %s without saving changes (y/n)? ", ed->filename);
1982      if (!ask()) return 0;
1983    }
1984    ed = ed->next;
1985  } while (ed != start);
1986
1987  return 1;
1988}
1989
1990static void help(struct editor *ed) {
1991  gotoxy(0, 0);
1992  clear_screen();
1993  outstr("Editor Command Summary\r\n");
1994  outstr("======================\r\n\r\n");
1995  outstr("<up>         Move one line up (*)         Ctrl+N  New editor\r\n");
1996  outstr("<down>       Move one line down (*)       Ctrl+O  Open file\r\n");
1997  outstr("<left>       Move one character left (*)  Ctrl+S  Save file\r\n");
1998  outstr("<right>      Move one character right (*) Ctrl+W  Close file\r\n");
1999  outstr("<pgup>       Move one page up (*)         Ctrl+Q  Quit\r\n");
2000  outstr("<pgdn>       Move one page down (*)       Ctrl+P  Pipe command\r\n");
2001  outstr("Ctrl+<left>  Move to previous word (*)    Ctrl+A  Select all\r\n");
2002  outstr("Ctrl+<right> Move to next word (*)        Ctrl+C  Copy selection to clipboard\r\n");
2003  outstr("<home>       Move to start of line (*)    Ctrl+X  Cut selection to clipboard\r\n");
2004  outstr("<end>        Move to end of line (*)      Ctrl+V  Paste from clipboard\r\n");
2005  outstr("Ctrl+<home>  Move to start of file (*)    Ctrl+Z  Undo\r\n");
2006  outstr("Ctrl+<end>   Move to end of file (*)      Ctrl+R  Redo\r\n");
2007  outstr("<backspace>  Delete previous character    Ctrl+F  Find text\r\n");
2008  outstr("<delete>     Delete current character     Ctrl+G  Find next\r\n");
2009  outstr("Ctrl+<tab>   Next editor                  Ctrl+L  Goto line\r\n");
2010  outstr("<tab>        Indent selection             F1      Help\r\n");
2011  outstr("Shift+<tab>  Unindent selection           F2      Select toggle\r\n");
2012  outstr(" (*) Extends selection, F2 toggles.       F3      Navigate to file\r\n");
2013  outstr("                                          F4      Copy selection to clipboard\r\n");
2014  outstr("  Ctrl-Q/S may not work over              F5      Redraw screen\r\n");
2015  outstr("  serial links, use funcions keys         F9      Save file\r\n");
2016  outstr("                                          F10     Quit\r\n");
2017  outstr("Press any key to continue...");
2018  fflush(stdout);
2019
2020  getkey();
2021  draw_screen(ed);
2022  draw_full_statusline(ed);
2023}
2024
2025//
2026// Editor
2027//
2028
2029static void edit(struct editor *ed) {
2030  int done = 0;
2031  int key;
2032
2033  ed->refresh = 1;
2034  while (!done) {
2035    if (ed->refresh) {
2036      draw_screen(ed);
2037      draw_full_statusline(ed);
2038      ed->refresh = 0;
2039      ed->lineupdate = 0;
2040    } else if (ed->lineupdate) {
2041      update_line(ed);
2042      ed->lineupdate = 0;
2043      draw_statusline(ed);
2044    } else {
2045      draw_statusline(ed);
2046    }
2047
2048    position_cursor(ed);
2049    fflush(stdout);
2050    key = getkey();
2051
2052    if (key >= ' ' && key <= 0x7F) {
2053#ifdef LESS
2054      switch (key) {
2055        case 'q': done = 1; break;
2056        case '/': find_text(ed, 0); break;
2057      }
2058#else
2059      insert_char(ed, (unsigned char) key);
2060#endif
2061    } else {
2062      switch (key) {
2063        case KEY_F1: help(ed); break;
2064        case KEY_F2: select_toggle(ed); break;
2065        case KEY_F3: jump_to_editor(ed); ed = ed->env->current; break;
2066        case KEY_F4: copy_selection(ed); break;
2067        case KEY_F5: redraw_screen(ed); break;
2068        case KEY_F9: save_editor(ed); break;
2069        case KEY_F10: done = 1; break;
2070
2071#if defined(__linux__) || defined(__rtems__)
2072        case ctrl('y'): help(ed); break;
2073        case ctrl('t'): top(ed, 0); break;
2074        case ctrl('b'): bottom(ed, 0); break;
2075#endif
2076
2077        case KEY_UP: up(ed, ed->selecting); break;
2078        case KEY_DOWN: down(ed, ed->selecting); break;
2079        case KEY_LEFT: left(ed, ed->selecting); break;
2080        case KEY_RIGHT: right(ed, ed->selecting); break;
2081        case KEY_HOME: home(ed, ed->selecting); break;
2082        case KEY_END: end(ed, ed->selecting); break;
2083        case KEY_PGUP: pageup(ed, ed->selecting); break;
2084        case KEY_PGDN: pagedown(ed, ed->selecting); break;
2085
2086        case KEY_CTRL_RIGHT: wordright(ed, ed->selecting); break;
2087        case KEY_CTRL_LEFT: wordleft(ed, ed->selecting); break;
2088        case KEY_CTRL_HOME: top(ed, ed->selecting); break;
2089        case KEY_CTRL_END: bottom(ed, ed->selecting); break;
2090
2091#if SHIFT_SELECT
2092        case KEY_SHIFT_UP: up(ed, 1); break;
2093        case KEY_SHIFT_DOWN: down(ed, 1); break;
2094        case KEY_SHIFT_LEFT: left(ed, 1); break;
2095        case KEY_SHIFT_RIGHT: right(ed, 1); break;
2096        case KEY_SHIFT_PGUP: pageup(ed, 1); break;
2097        case KEY_SHIFT_PGDN: pagedown(ed, 1); break;
2098        case KEY_SHIFT_HOME: home(ed, 1); break;
2099        case KEY_SHIFT_END: end(ed, 1); break;
2100
2101        case KEY_SHIFT_CTRL_RIGHT: wordright(ed, 1); break;
2102        case KEY_SHIFT_CTRL_LEFT: wordleft(ed, 1); break;
2103        case KEY_SHIFT_CTRL_HOME: top(ed, 1); break;
2104        case KEY_SHIFT_CTRL_END: bottom(ed, 1); break;
2105#endif
2106
2107        case KEY_CTRL_TAB: ed = next_file(ed); break;
2108
2109        case ctrl('e'): select_toggle(ed); break;
2110        case ctrl('a'): select_all(ed); break;
2111        case ctrl('c'): copy_selection(ed); break;
2112        case ctrl('f'): find_text(ed, 0); break;
2113        case ctrl('l'): goto_line(ed); break;
2114        case ctrl('g'): find_text(ed, 1); break;
2115        case ctrl('q'): done = 1; break;
2116#ifdef LESS
2117        case KEY_ESC: done = 1; break;
2118#else
2119        case KEY_TAB: indent(ed, (unsigned char*) INDENT); break;
2120        case KEY_SHIFT_TAB: unindent(ed, (unsigned char*) INDENT); break;
2121
2122        case KEY_ENTER: newline(ed); break;
2123        case KEY_BACKSPACE: backspace(ed); break;
2124        case KEY_DEL: del(ed); break;
2125        case ctrl('x'): cut_selection(ed); break;
2126        case ctrl('z'): undo(ed); break;
2127        case ctrl('r'): redo(ed); break;
2128        case ctrl('v'): paste_selection(ed); break;
2129        case ctrl('o'): open_editor(ed); ed = ed->env->current; break;
2130        case ctrl('n'): new_editor(ed); ed = ed->env->current; break;
2131        case ctrl('s'): save_editor(ed); break;
2132        case ctrl('p'): pipe_command(ed); break;
2133#endif
2134#if defined(__rtems__)
2135        /*
2136         * Coverity spotted this as using ed after free() so changing
2137         * the order of the statements.
2138         */
2139        case ctrl('w'): ed = ed->env->current; close_editor(ed); break;
2140#else
2141        case ctrl('w'): close_editor(ed); ed = ed->env->current; break;
2142#endif
2143      }
2144    }
2145  }
2146}
2147
2148//
2149// main
2150//
2151static int rtems_shell_main_edit(int argc, char *argv[])
2152{
2153  struct env env;
2154  int rc;
2155  int i;
2156  sigset_t blocked_sigmask, orig_sigmask;
2157#if defined(__linux__)
2158  struct termios tio;
2159#endif
2160#if defined(__linux__) || defined(__rtems__)
2161  struct termios orig_tio;
2162#endif
2163#ifdef SANOS
2164  struct term *term;
2165#endif
2166
2167  memset(&env, 0, sizeof(env));
2168  for (i = 1; i < argc; i++) {
2169    struct editor *ed = create_editor(&env);
2170    rc = load_file(ed, argv[i]);
2171    if (rc < 0 && errno == ENOENT) rc = new_file(ed, argv[i]);
2172    if (rc < 0) {
2173      perror(argv[i]);
2174      return 0;
2175    }
2176  }
2177  if (env.current == NULL) {
2178    struct editor *ed = create_editor(&env);
2179    if (isatty(fileno(stdin))) {
2180      new_file(ed, "");
2181    } else {
2182      read_from_stdin(ed);
2183    }
2184  }
2185  env.current = env.current->next;
2186
2187#ifdef SANOS
2188  term = gettib()->proc->term;
2189  if (fdin != term->ttyin) dup2(term->ttyin, fdin);
2190  if (fdout != term->ttyout) dup2(term->ttyout, fdout);
2191#elif !defined(__rtems__)
2192  if (!isatty(fileno(stdin))) {
2193    if (!freopen("/dev/tty", "r", stdin)) perror("/dev/tty");
2194  }
2195#endif
2196
2197  setvbuf(stdout, NULL, 0, 8192);
2198
2199#if defined(__linux__) || defined(__rtems__)
2200  (void) tcgetattr(0, &orig_tio);
2201#if !defined(__rtems__)
2202  cfmakeraw(&tio);
2203  tcsetattr(0, TCSANOW, &tio);
2204#endif
2205  if (getenv("TERM") && strcmp(getenv("TERM"), "linux") == 0) {
2206    linux_console = 1;
2207  } else {
2208    outstr(CLRSCR);
2209    outstr("\033[3 q");  // xterm
2210    outstr("\033]50;CursorShape=2\a");  // KDE
2211  }
2212#endif
2213
2214  get_console_size(&env);
2215
2216  sigemptyset(&blocked_sigmask);
2217  sigaddset(&blocked_sigmask, SIGINT);
2218  sigaddset(&blocked_sigmask, SIGTSTP);
2219  sigaddset(&blocked_sigmask, SIGABRT);
2220  sigprocmask(SIG_BLOCK, &blocked_sigmask, &orig_sigmask);
2221
2222  for (;;) {
2223    if (!env.current) break;
2224    edit(env.current);
2225    if (quit(&env)) break;
2226  }
2227
2228  gotoxy(0, env.lines + 1);
2229  outstr(RESET_COLOR CLREOL);
2230#if defined(__linux__) || defined(__rtems__)
2231  tcsetattr(0, TCSANOW, &orig_tio);
2232#endif
2233
2234  while (env.current) delete_editor(env.current);
2235
2236  if (env.clipboard) free(env.clipboard);
2237  if (env.search) free(env.search);
2238  if (env.linebuf) free(env.linebuf);
2239
2240  setbuf(stdout, NULL);
2241  sigprocmask(SIG_SETMASK, &orig_sigmask, NULL);
2242
2243  return 0;
2244}
2245
2246rtems_shell_cmd_t rtems_shell_EDIT_Command = {
2247  "edit",                /* name */
2248  "edit [file ...]",     /* usage */
2249  "files",               /* topic */
2250  rtems_shell_main_edit, /* command */
2251  NULL,                  /* alias */
2252  NULL                   /* next */
2253};
Note: See TracBrowser for help on using the repository browser.