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

5 5.3
Last change on this file since b7f1fa2f was b7f1fa2f, checked in by Chris Johns <chrisj@…>, on 01/30/23 at 03:11:59

libmisc/shell/edit: Return if no memory in move_gap

Closes #4565

  • Property mode set to 100644
File size: 55.4 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    if (start == NULL) {
411        return;
412    }
413    gap = start + pos;
414    rest = gap + minsize;
415    end = start + newsize;
416
417    if (p < ed->gap) {
418      memcpy(start, ed->start, pos);
419      memcpy(rest, p, ed->gap - p);
420      memcpy(end - (ed->end - ed->rest), ed->rest, ed->end - ed->rest);
421    } else {
422      memcpy(start, ed->start, ed->gap - ed->start);
423      memcpy(start + (ed->gap - ed->start), ed->rest, p - ed->rest);
424      memcpy(rest, p, ed->end - p);
425    }
426
427    free(ed->start);
428    ed->start = start;
429    ed->gap = gap;
430    ed->rest = rest;
431    ed->end = end;
432  }
433
434#ifdef DEBUG
435  memset(ed->gap, 0, ed->rest - ed->gap);
436#endif
437}
438
439static void close_gap(struct editor *ed) {
440  int len = text_length(ed);
441  move_gap(ed, len, 1);
442  ed->start[len] = 0;
443}
444
445static int get(struct editor *ed, int pos) {
446  unsigned char *p = text_ptr(ed, pos);
447  if (p >= ed->end) return -1;
448  return *p;
449}
450
451static int compare(struct editor *ed, unsigned char *buf, int pos, int len) {
452  unsigned char *bufptr = buf;
453  unsigned char *p = ed->start + pos;
454  if (p >= ed->gap) p += (ed->rest - ed->gap);
455
456  while (len > 0) {
457    if (p == ed->end) return 0;
458    if (*bufptr++ != *p) return 0;
459    len--;
460    if (++p == ed->gap) p = ed->rest;
461  }
462
463  return 1;
464}
465
466static int copy(struct editor *ed, unsigned char *buf, int pos, int len) {
467  unsigned char *bufptr = buf;
468  unsigned char *p = ed->start + pos;
469  if (p >= ed->gap) p += (ed->rest - ed->gap);
470
471  while (len > 0) {
472    if (p == ed->end) break;
473    *bufptr++ = *p;
474    len--;
475    if (++p == ed->gap) p = ed->rest;
476  }
477
478  return bufptr - buf;
479}
480
481static void replace(struct editor *ed, int pos, int len, unsigned char *buf, int bufsize, int doundo) {
482  unsigned char *p;
483  struct undo *undo;
484
485  // Store undo information
486  if (doundo) {
487    reset_undo(ed);
488    undo = ed->undotail;
489    if (undo && len == 0 && bufsize == 1 && undo->erased == 0 && pos == undo->pos + undo->inserted) {
490      // Insert character at end of current redo buffer
491      undo->redobuf = realloc(undo->redobuf, undo->inserted + 1);
492      undo->redobuf[undo->inserted] = *buf;
493      undo->inserted++;
494    } else if (undo && len == 1 && bufsize == 0 && undo->inserted == 0 && pos == undo->pos) {
495      // Erase character at end of current undo buffer
496      undo->undobuf = realloc(undo->undobuf, undo->erased + 1);
497      undo->undobuf[undo->erased] = get(ed, pos);
498      undo->erased++;
499    } else if (undo && len == 1 && bufsize == 0 && undo->inserted == 0 && pos == undo->pos - 1) {
500      // Erase character at beginning of current undo buffer
501      undo->pos--;
502      undo->undobuf = realloc(undo->undobuf, undo->erased + 1);
503      memmove(undo->undobuf + 1, undo->undobuf, undo->erased);
504      undo->undobuf[0] = get(ed, pos);
505      undo->erased++;
506    } else {
507      // Create new undo buffer
508      undo = (struct undo *) malloc(sizeof(struct undo));
509      if (ed->undotail) ed->undotail->next = undo;
510      undo->prev = ed->undotail;
511      undo->next = NULL;
512      ed->undotail = ed->undo = undo;
513      if (!ed->undohead) ed->undohead = undo;
514
515      undo->pos = pos;
516      undo->erased = len;
517      undo->inserted = bufsize;
518      undo->undobuf = undo->redobuf = NULL;
519      if (len > 0) {
520        undo->undobuf = malloc(len);
521        copy(ed, undo->undobuf, pos, len);
522      }
523      if (bufsize > 0) {
524        undo->redobuf = malloc(bufsize);
525        memcpy(undo->redobuf, buf, bufsize);
526      }
527    }
528  }
529
530  p = ed->start + pos;
531  if (bufsize == 0 && p <= ed->gap && p + len >= ed->gap) {
532    // Handle deletions at the edges of the gap
533    ed->rest += len - (ed->gap - p);
534    ed->gap = p;
535  } else {
536    // Move the gap
537    move_gap(ed, pos + len, bufsize - len);
538
539    // Replace contents
540    memcpy(ed->start + pos, buf, bufsize);
541    ed->gap = ed->start + pos + bufsize;
542  }
543
544  // Mark buffer as dirty
545  ed->dirty = 1;
546}
547
548static void insert(struct editor *ed, int pos, unsigned char *buf, int bufsize) {
549  replace(ed, pos, 0, buf, bufsize, 1);
550}
551
552static void erase(struct editor *ed, int pos, int len) {
553  replace(ed, pos, len, NULL, 0, 1);
554}
555
556//
557// Navigation functions
558//
559
560static int line_length(struct editor *ed, int linepos) {
561  int pos = linepos;
562  while (1) {
563    int ch = get(ed, pos);
564    if (ch < 0 || ch == '\n' || ch == '\r') break;
565    pos++;
566  }
567
568  return pos - linepos;
569}
570
571static int line_start(struct editor *ed, int pos) {
572  while (1) {
573    if (pos == 0) break;
574    if (get(ed, pos - 1) == '\n') break;
575    pos--;
576  }
577
578  return pos;
579}
580
581static int next_line(struct editor *ed, int pos) {
582  while (1) {
583    int ch = get(ed, pos);
584    if (ch < 0) return -1;
585    pos++;
586    if (ch == '\n') return pos;
587  }
588}
589
590static int prev_line(struct editor *ed, int pos) {
591  if (pos == 0) return -1;
592
593  while (pos > 0) {
594    int ch = get(ed, --pos);
595    if (ch == '\n') break;
596  }
597
598  while (pos > 0) {
599    int ch = get(ed, --pos);
600    if (ch == '\n') return pos + 1;
601  }
602
603  return 0;
604}
605
606static int column(struct editor *ed, int linepos, int col) {
607  unsigned char *p = text_ptr(ed, linepos);
608  int c = 0;
609  while (col > 0) {
610    if (p == ed->end) break;
611    if (*p == '\t') {
612      int spaces = TABSIZE - c % TABSIZE;
613      c += spaces;
614    } else {
615      c++;
616    }
617    col--;
618    if (++p == ed->gap) p = ed->rest;
619  }
620  return c;
621}
622
623static void moveto(struct editor *ed, int pos, int center) {
624  int scroll = 0;
625  for (;;) {
626    int cur = ed->linepos + ed->col;
627    if (pos < cur) {
628      if (pos >= ed->linepos) {
629        ed->col = pos - ed->linepos;
630      } else {
631        ed->col = 0;
632        ed->linepos = prev_line(ed, ed->linepos);
633        ed->line--;
634
635        if (ed->topline > ed->line) {
636          ed->toppos = ed->linepos;
637          ed->topline--;
638          ed->refresh = 1;
639          scroll = 1;
640        }
641      }
642    } else if (pos > cur) {
643      int next = next_line(ed, ed->linepos);
644      if (next == -1) {
645        ed->col = line_length(ed, ed->linepos);
646        break;
647      } else if (pos < next) {
648        ed->col = pos - ed->linepos;
649      } else {
650        ed->col = 0;
651        ed->linepos = next;
652        ed->line++;
653
654        if (ed->line >= ed->topline + ed->env->lines) {
655          ed->toppos = next_line(ed, ed->toppos);
656          ed->topline++;
657          ed->refresh = 1;
658          scroll = 1;
659        }
660      }
661    } else {
662      break;
663    }
664  }
665
666  if (scroll && center) {
667    int tl = ed->line - ed->env->lines / 2;
668    if (tl < 0) tl = 0;
669    for (;;) {
670      if (ed->topline > tl) {
671        ed->toppos = prev_line(ed, ed->toppos);
672        ed->topline--;
673      } else if (ed->topline < tl) {
674        ed->toppos = next_line(ed, ed->toppos);
675        ed->topline++;
676      } else {
677        break;
678      }
679    }
680  }
681}
682
683//
684// Text selection
685//
686
687static int get_selection(struct editor *ed, int *start, int *end) {
688  if (ed->anchor == -1) {
689    *start = *end = -1;
690    return 0;
691  } else {
692    int pos = ed->linepos + ed->col;
693    if (pos == ed->anchor) {
694      *start = *end = -1;
695      return 0;
696    } else if (pos < ed->anchor) {
697      *start = pos;
698      *end = ed->anchor;
699    } else {
700      *start = ed->anchor;
701      *end = pos;
702    }
703  }
704  return 1;
705}
706
707static int get_selected_text(struct editor *ed, char *buffer, int size) {
708  int selstart, selend, len;
709
710  if (!get_selection(ed, &selstart, &selend)) return 0;
711  len = selend - selstart;
712  if (len >= size) return 0;
713  copy(ed, (unsigned char*) buffer, selstart, len);
714  buffer[len] = 0;
715  return len;
716}
717
718static void update_selection(struct editor *ed, int select) {
719  if (select) {
720    if (ed->anchor == -1) ed->anchor = ed->linepos + ed->col;
721    ed->refresh = 1;
722  } else {
723    if (ed->anchor != -1) ed->refresh = 1;
724    ed->anchor = -1;
725  }
726}
727
728static int erase_selection(struct editor *ed) {
729  int selstart, selend;
730
731  if (!get_selection(ed, &selstart, &selend)) return 0;
732  moveto(ed, selstart, 0);
733  erase(ed, selstart, selend - selstart);
734  ed->anchor = -1;
735  ed->refresh = 1;
736  return 1;
737}
738
739static void select_all(struct editor *ed) {
740  ed->anchor = 0;
741  ed->refresh = 1;
742  moveto(ed, text_length(ed), 0);
743}
744
745//
746// Screen functions
747//
748
749static void get_console_size(struct env *env) {
750#ifdef __linux__
751  struct winsize ws;
752  ioctl(0, TIOCGWINSZ, &ws);
753  env->cols = ws.ws_col;
754  env->lines = ws.ws_row - 1;
755#elif defined(__rtems__)
756  env->cols = 80;
757  env->lines = 25;
758#else
759  struct term *term = gettib()->proc->term;
760  env->cols = term->cols;
761  env->lines = term->lines - 1;
762#endif
763  env->linebuf = realloc(env->linebuf, env->cols + LINEBUF_EXTRA);
764}
765
766static void outch(char c) {
767  putchar(c);
768}
769
770static void outbuf(unsigned char *buf, int len) {
771  fwrite(buf, 1, len, stdout);
772}
773
774static void outstr(char *str) {
775  fputs(str, stdout);
776}
777
778static void clear_screen(void) {
779  outstr(CLRSCR);
780}
781
782static void gotoxy(int col, int line) {
783  char buf[32];
784
785  sprintf(buf, GOTOXY, line + 1, col + 1);
786  outstr(buf);
787}
788
789//
790// Keyboard functions
791//
792
793static void get_modifier_keys(int *shift, int *ctrl) {
794  *shift = *ctrl = 0;
795#ifdef __linux__
796  if (linux_console) {
797    char modifiers = 6;
798    if (ioctl(0, TIOCLINUX, &modifiers) >= 0) {
799      if (modifiers & 1) *shift = 1;
800      if (modifiers & 4) *ctrl = 1;
801    }
802  }
803#endif
804}
805
806static int getachar(void)
807{
808  int ch = getchar();
809  return ch;
810}
811
812static int getkey(void) {
813  int ch, shift, ctrl;
814
815  ch = getachar();
816  if (ch < 0) return ch;
817
818  switch (ch) {
819    case 0x08: return KEY_BACKSPACE;
820    case 0x09:
821      get_modifier_keys(&shift, &ctrl);
822      if (shift) return KEY_SHIFT_TAB;
823      if (ctrl) return KEY_CTRL_TAB;
824      return KEY_TAB;
825#ifdef SANOS
826    case 0x0D: return gettib()->proc->term->type == TERM_CONSOLE ? KEY_ENTER : KEY_UNKNOWN;
827    case 0x0A: return gettib()->proc->term->type != TERM_CONSOLE ? KEY_ENTER : KEY_UNKNOWN;
828#else
829    case 0x0D: return KEY_ENTER;
830    case 0x0A: return KEY_ENTER;
831#endif
832    case 0x1B:
833      ch = getachar();
834      switch (ch) {
835        case 0x1B: return KEY_ESC;
836        case 0x4F:
837          ch = getachar();
838          switch (ch) {
839            case 0x46: return KEY_END;
840            case 0x48: return KEY_HOME;
841            case 0x50: return KEY_F1;
842            case 0x51: return KEY_F2;
843            case 0x52: return KEY_F3;
844            case 0x53: return KEY_F4;
845            case 0x54: return KEY_F5;
846            default: return KEY_UNKNOWN;
847          }
848          break;
849
850        case 0x5B:
851          get_modifier_keys(&shift, &ctrl);
852          ch = getachar();
853          if (ch == 0x31) {
854            ch = getachar();
855            switch (ch) {
856              case 0x35:
857                return getachar() == 0x7E ? KEY_F5 : KEY_UNKNOWN;
858              case 0x37:
859                return getachar() == 0x7E ? KEY_F6 : KEY_UNKNOWN;
860              case 0x3B:
861                ch = getachar();
862                if (ch == 0x7E) return KEY_F7;
863                if (ch == 0x32) shift = 1;
864                if (ch == 0x35) ctrl = 1;
865                if (ch == 0x36) shift = ctrl = 1;
866                ch = getachar();
867                break;
868              case 0x39:
869                return getachar() == 0x7E ? KEY_F8 : KEY_UNKNOWN;
870              case 0x7E:
871                if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
872                if (shift) return KEY_SHIFT_HOME;
873                if (ctrl) return KEY_CTRL_HOME;
874                return KEY_HOME;
875              default:
876                return KEY_UNKNOWN;
877            }
878          }
879
880          switch (ch) {
881            case 0x31:
882              ch = getachar();
883              if (ch != 0x7E) return KEY_UNKNOWN;
884              if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
885              if (shift) return KEY_SHIFT_HOME;
886              if (ctrl) return KEY_CTRL_HOME;
887              return KEY_HOME;
888            case 0x32:
889              ch = getachar();
890              switch (ch) {
891                case 0x30: ch = getachar(); return KEY_F9;
892                case 0x31: ch = getachar(); return KEY_F10;
893                case 0x7E: return KEY_INS;
894                default: break;
895              }
896              return KEY_UNKNOWN;
897            case 0x33: return getachar() == 0x7E ? KEY_DEL : KEY_UNKNOWN;
898            case 0x34:
899              if (getachar() != 0x7E) return KEY_UNKNOWN;
900              if (shift && ctrl) return KEY_SHIFT_CTRL_END;
901              if (shift) return KEY_SHIFT_END;
902              if (ctrl) return KEY_CTRL_END;
903              return KEY_END;
904            case 0x35:
905              if (getachar() != 0x7E) return KEY_UNKNOWN;
906              if (shift) return KEY_SHIFT_PGUP;
907              return KEY_PGUP;
908            case 0x36:
909              if (getachar() != 0x7E) return KEY_UNKNOWN;
910              if (shift) return KEY_SHIFT_PGDN;
911              return KEY_PGDN;
912            case 0x41:
913              if (shift && ctrl) return KEY_SHIFT_CTRL_UP;
914              if (shift) return KEY_SHIFT_UP;
915              if (ctrl) return KEY_CTRL_UP;
916              return KEY_UP;
917            case 0x42:
918              if (shift && ctrl) return KEY_SHIFT_CTRL_DOWN;
919              if (shift) return KEY_SHIFT_DOWN;
920              if (ctrl) return KEY_CTRL_DOWN;
921              return KEY_DOWN;
922            case 0x43:
923              if (shift && ctrl) return KEY_SHIFT_CTRL_RIGHT;
924              if (shift) return KEY_SHIFT_RIGHT;
925              if (ctrl) return KEY_CTRL_RIGHT;
926              return KEY_RIGHT;
927            case 0x44:
928              if (shift && ctrl) return KEY_SHIFT_CTRL_LEFT;
929              if (shift) return KEY_SHIFT_LEFT;
930              if (ctrl) return KEY_CTRL_LEFT;
931              return KEY_LEFT;
932            case 0x46:
933              if (shift && ctrl) return KEY_SHIFT_CTRL_END;
934              if (shift) return KEY_SHIFT_END;
935              if (ctrl) return KEY_CTRL_END;
936              return KEY_END;
937            case 0x48:
938              if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
939              if (shift) return KEY_SHIFT_HOME;
940              if (ctrl) return KEY_CTRL_HOME;
941              return KEY_HOME;
942            case 0x5A:
943              return KEY_SHIFT_TAB;
944            case 0x5B:
945              ch = getachar();
946              switch (ch) {
947                case 0x41: return KEY_F1;
948                case 0x43: return KEY_F3;
949                case 0x45: return KEY_F5;
950              }
951              return KEY_UNKNOWN;
952
953            default: return KEY_UNKNOWN;
954          }
955          break;
956
957        default: return KEY_UNKNOWN;
958      }
959      break;
960
961    case 0x00:
962    case 0xE0:
963      ch = getachar();
964      switch (ch) {
965        case 0x0F: return KEY_SHIFT_TAB;
966        case 0x3B: return KEY_F1;
967        case 0x3D: return KEY_F3;
968        case 0x3F: return KEY_F5;
969        case 0x47: return KEY_HOME;
970        case 0x48: return KEY_UP;
971        case 0x49: return KEY_PGUP;
972        case 0x4B: return KEY_LEFT;
973        case 0x4D: return KEY_RIGHT;
974        case 0x4F: return KEY_END;
975        case 0x50: return KEY_DOWN;
976        case 0x51: return KEY_PGDN;
977        case 0x52: return KEY_INS;
978        case 0x53: return KEY_DEL;
979        case 0x73: return KEY_CTRL_LEFT;
980        case 0x74: return KEY_CTRL_RIGHT;
981        case 0x75: return KEY_CTRL_END;
982        case 0x77: return KEY_CTRL_HOME;
983        case 0x8D: return KEY_CTRL_UP;
984        case 0x91: return KEY_CTRL_DOWN;
985        case 0x94: return KEY_CTRL_TAB;
986        case 0xB8: return KEY_SHIFT_UP;
987        case 0xB7: return KEY_SHIFT_HOME;
988        case 0xBF: return KEY_SHIFT_END;
989        case 0xB9: return KEY_SHIFT_PGUP;
990        case 0xBB: return KEY_SHIFT_LEFT;
991        case 0xBD: return KEY_SHIFT_RIGHT;
992        case 0xC0: return KEY_SHIFT_DOWN;
993        case 0xC1: return KEY_SHIFT_PGDN;
994        case 0xDB: return KEY_SHIFT_CTRL_LEFT;
995        case 0xDD: return KEY_SHIFT_CTRL_RIGHT;
996        case 0xD8: return KEY_SHIFT_CTRL_UP;
997        case 0xE0: return KEY_SHIFT_CTRL_DOWN;
998        case 0xD7: return KEY_SHIFT_CTRL_HOME;
999        case 0xDF: return KEY_SHIFT_CTRL_END;
1000
1001        default: return KEY_UNKNOWN;
1002      }
1003      break;
1004
1005    case 0x7F: return KEY_BACKSPACE;
1006
1007    default: return ch;
1008  }
1009}
1010
1011static int prompt(struct editor *ed, char *msg, int selection) {
1012  int maxlen, len, ch;
1013  char *buf = (char*) ed->env->linebuf;
1014
1015  gotoxy(0, ed->env->lines);
1016  outstr(STATUS_COLOR);
1017  outstr(msg);
1018  outstr(CLREOL);
1019
1020  len = 0;
1021  maxlen = ed->env->cols - strlen(msg) - 1;
1022  if (selection) {
1023    len = get_selected_text(ed, buf, maxlen);
1024    outbuf((unsigned char*) buf, len);
1025  }
1026
1027  for (;;) {
1028    fflush(stdout);
1029    ch = getkey();
1030    if (ch == KEY_ESC) {
1031      return 0;
1032    } else if (ch == KEY_ENTER) {
1033      buf[len] = 0;
1034      return len > 0;
1035    } else if (ch == KEY_BACKSPACE) {
1036      if (len > 0) {
1037        outstr("\b \b");
1038        len--;
1039      }
1040    } else if (ch >= ' ' && ch < 0x100 && len < maxlen) {
1041      outch(ch);
1042      buf[len++] = ch;
1043    }
1044  }
1045}
1046
1047static int ask(void) {
1048  int ch = getachar();
1049  return ch == 'y' || ch == 'Y';
1050}
1051
1052//
1053// Display functions
1054//
1055
1056static void display_message(struct editor *ed, char *fmt, ...) {
1057  va_list args;
1058
1059  va_start(args, fmt);
1060  gotoxy(0, ed->env->lines);
1061  outstr(STATUS_COLOR);
1062  vprintf(fmt, args);
1063  outstr(CLREOL TEXT_COLOR);
1064  fflush(stdout);
1065  va_end(args);
1066}
1067
1068static void draw_full_statusline(struct editor *ed) {
1069  struct env *env = ed->env;
1070  int namewidth = env->cols - 29;
1071  gotoxy(0, env->lines);
1072  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);
1073  outstr((char*) env->linebuf);
1074}
1075
1076static void draw_statusline(struct editor *ed) {
1077  gotoxy(ed->env->cols - 20, ed->env->lines);
1078  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);
1079  outstr((char*) ed->env->linebuf);
1080}
1081
1082static void display_line(struct editor *ed, int pos, int fullline) {
1083  int hilite = 0;
1084  int col = 0;
1085  int margin = ed->margin;
1086  int maxcol = ed->env->cols + margin;
1087  unsigned char *bufptr = ed->env->linebuf;
1088  unsigned char *p = text_ptr(ed, pos);
1089  int selstart, selend, ch;
1090  char *s;
1091
1092  get_selection(ed, &selstart, &selend);
1093  while (col < maxcol) {
1094    if (margin == 0) {
1095      if (!hilite && pos >= selstart && pos < selend) {
1096        for (s = SELECT_COLOR; *s; s++) *bufptr++ = *s;
1097        hilite = 1;
1098      } else if (hilite && pos >= selend) {
1099        for (s = TEXT_COLOR; *s; s++) *bufptr++ = *s;
1100        hilite = 0;
1101      }
1102    }
1103
1104    if (p == ed->end) break;
1105    ch = *p;
1106    if (ch == '\r' || ch == '\n') break;
1107
1108    if (ch == '\t') {
1109      int spaces = TABSIZE - col % TABSIZE;
1110      while (spaces > 0 && col < maxcol) {
1111        if (margin > 0) {
1112          margin--;
1113        } else {
1114          *bufptr++ = ' ';
1115        }
1116        col++;
1117        spaces--;
1118      }
1119    } else {
1120      if (margin > 0) {
1121        margin--;
1122      } else {
1123        *bufptr++ = ch;
1124      }
1125      col++;
1126    }
1127
1128    if (++p == ed->gap) p = ed->rest;
1129    pos++;
1130  }
1131
1132#if defined(__linux__)
1133  if (hilite) {
1134    while (col < maxcol) {
1135      *bufptr++ = ' ';
1136      col++;
1137    }
1138  } else {
1139    if (col == margin) *bufptr++ = ' ';
1140  }
1141#endif
1142
1143  if (col < maxcol) {
1144    for (s = CLREOL; *s; s++) *bufptr++ = *s;
1145    if (fullline) {
1146      memcpy(bufptr, "\r\n", 2);
1147      bufptr += 2;
1148    }
1149  }
1150
1151  if (hilite) {
1152    for (s = TEXT_COLOR; *s; s++) *bufptr++ = *s;
1153  }
1154
1155  outbuf(ed->env->linebuf, bufptr - ed->env->linebuf);
1156}
1157
1158static void update_line(struct editor *ed) {
1159  gotoxy(0, ed->line - ed->topline);
1160  display_line(ed, ed->linepos, 0);
1161}
1162
1163static void draw_screen(struct editor *ed) {
1164  int pos;
1165  int i;
1166
1167  gotoxy(0, 0);
1168  outstr(TEXT_COLOR);
1169  pos = ed->toppos;
1170  for (i = 0; i < ed->env->lines; i++) {
1171    if (pos < 0) {
1172      outstr(CLREOL "\r\n");
1173    } else {
1174      display_line(ed, pos, 1);
1175      pos = next_line(ed, pos);
1176    }
1177  }
1178}
1179
1180static void position_cursor(struct editor *ed) {
1181  int col = column(ed, ed->linepos, ed->col);
1182  gotoxy(col - ed->margin, ed->line - ed->topline);
1183}
1184
1185//
1186// Cursor movement
1187//
1188
1189static void adjust(struct editor *ed) {
1190  int col;
1191  int ll = line_length(ed, ed->linepos);
1192  ed->col = ed->lastcol;
1193  if (ed->col > ll) ed->col = ll;
1194
1195  col = column(ed, ed->linepos, ed->col);
1196  while (col < ed->margin) {
1197    ed->margin -= 4;
1198    if (ed->margin < 0) ed->margin = 0;
1199    ed->refresh = 1;
1200  }
1201
1202  while (col - ed->margin >= ed->env->cols) {
1203    ed->margin += 4;
1204    ed->refresh = 1;
1205  }
1206}
1207
1208static void select_toggle(struct editor *ed) {
1209  ed->selecting = ed->selecting ? 0 : 1;
1210  update_selection(ed, ed->selecting);
1211  adjust(ed);
1212}
1213
1214static void up(struct editor *ed, int select) {
1215  int newpos;
1216
1217  update_selection(ed, select);
1218
1219  newpos = prev_line(ed, ed->linepos);
1220  if (newpos < 0) return;
1221
1222  ed->linepos = newpos;
1223  ed->line--;
1224  if (ed->line < ed->topline) {
1225    ed->toppos = ed->linepos;
1226    ed->topline = ed->line;
1227    ed->refresh = 1;
1228  }
1229
1230  adjust(ed);
1231}
1232
1233static void down(struct editor *ed, int select) {
1234  int newpos;
1235
1236  update_selection(ed, select);
1237
1238  newpos = next_line(ed, ed->linepos);
1239  if (newpos < 0) return;
1240
1241  ed->linepos = newpos;
1242  ed->line++;
1243
1244  if (ed->line >= ed->topline + ed->env->lines) {
1245    ed->toppos = next_line(ed, ed->toppos);
1246    ed->topline++;
1247    ed->refresh = 1;
1248  }
1249
1250  adjust(ed);
1251}
1252
1253static void left(struct editor *ed, int select) {
1254  update_selection(ed, select);
1255  if (ed->col > 0) {
1256    ed->col--;
1257  } else {
1258    int newpos = prev_line(ed, ed->linepos);
1259    if (newpos < 0) return;
1260
1261    ed->col = line_length(ed, newpos);
1262    ed->linepos = newpos;
1263    ed->line--;
1264    if (ed->line < ed->topline) {
1265      ed->toppos = ed->linepos;
1266      ed->topline = ed->line;
1267      ed->refresh = 1;
1268    }
1269  }
1270
1271  ed->lastcol = ed->col;
1272  adjust(ed);
1273}
1274
1275static void right(struct editor *ed, int select) {
1276  update_selection(ed, select);
1277  if (ed->col < line_length(ed, ed->linepos)) {
1278    ed->col++;
1279  } else {
1280    int newpos = next_line(ed, ed->linepos);
1281    if (newpos < 0) return;
1282
1283    ed->col = 0;
1284    ed->linepos = newpos;
1285    ed->line++;
1286
1287    if (ed->line >= ed->topline + ed->env->lines) {
1288      ed->toppos = next_line(ed, ed->toppos);
1289      ed->topline++;
1290      ed->refresh = 1;
1291    }
1292  }
1293
1294  ed->lastcol = ed->col;
1295  adjust(ed);
1296}
1297
1298static int wordchar(int ch) {
1299  return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');
1300}
1301
1302static void wordleft(struct editor *ed, int select) {
1303  int pos, phase;
1304
1305  update_selection(ed, select);
1306  pos = ed->linepos + ed->col;
1307  phase = 0;
1308  while (pos > 0) {
1309    int ch = get(ed, pos - 1);
1310    if (phase == 0) {
1311      if (wordchar(ch)) phase = 1;
1312    } else {
1313      if (!wordchar(ch)) break;
1314    }
1315
1316    pos--;
1317    if (pos < ed->linepos) {
1318      ed->linepos = prev_line(ed, ed->linepos);
1319      ed->line--;
1320      ed->refresh = 1;
1321    }
1322  }
1323  ed->col = pos - ed->linepos;
1324  if (ed->line < ed->topline) {
1325    ed->toppos = ed->linepos;
1326    ed->topline = ed->line;
1327  }
1328
1329  ed->lastcol = ed->col;
1330  adjust(ed);
1331}
1332
1333static void wordright(struct editor *ed, int select) {
1334  int pos, end, phase, next;
1335
1336  update_selection(ed, select);
1337  pos = ed->linepos + ed->col;
1338  end = text_length(ed);
1339  next = next_line(ed, ed->linepos);
1340  phase = 0;
1341  while (pos < end) {
1342    int ch = get(ed, pos);
1343    if (phase == 0) {
1344      if (wordchar(ch)) phase = 1;
1345    } else {
1346      if (!wordchar(ch)) break;
1347    }
1348
1349    pos++;
1350    if (pos == next) {
1351      ed->linepos = next;
1352      next = next_line(ed, ed->linepos);
1353      ed->line++;
1354      ed->refresh = 1;
1355    }
1356  }
1357  ed->col = pos - ed->linepos;
1358  if (ed->line >= ed->topline + ed->env->lines) {
1359    ed->toppos = next_line(ed, ed->toppos);
1360    ed->topline++;
1361  }
1362
1363  ed->lastcol = ed->col;
1364  adjust(ed);
1365}
1366
1367static void home(struct editor *ed, int select) {
1368  update_selection(ed, select);
1369  ed->col = ed->lastcol = 0;
1370  adjust(ed);
1371}
1372
1373static void end(struct editor *ed, int select) {
1374  update_selection(ed, select);
1375  ed->col = ed->lastcol = line_length(ed, ed->linepos);
1376  adjust(ed);
1377}
1378
1379static void top(struct editor *ed, int select) {
1380  update_selection(ed, select);
1381  ed->toppos = ed->topline = ed->margin = 0;
1382  ed->linepos = ed->line = ed->col = ed->lastcol = 0;
1383  ed->refresh = 1;
1384}
1385
1386static void bottom(struct editor *ed, int select) {
1387  update_selection(ed, select);
1388  for (;;) {
1389    int newpos = next_line(ed, ed->linepos);
1390    if (newpos < 0) break;
1391
1392    ed->linepos = newpos;
1393    ed->line++;
1394
1395    if (ed->line >= ed->topline + ed->env->lines) {
1396      ed->toppos = next_line(ed, ed->toppos);
1397      ed->topline++;
1398      ed->refresh = 1;
1399    }
1400  }
1401  ed->col = ed->lastcol = line_length(ed, ed->linepos);
1402  adjust(ed);
1403}
1404
1405static void pageup(struct editor *ed, int select) {
1406  int i;
1407
1408  update_selection(ed, select);
1409  if (ed->line < ed->env->lines) {
1410    ed->linepos = ed->toppos = 0;
1411    ed->line = ed->topline = 0;
1412  } else {
1413    for (i = 0; i < ed->env->lines; i++) {
1414      int newpos = prev_line(ed, ed->linepos);
1415      if (newpos < 0) return;
1416
1417      ed->linepos = newpos;
1418      ed->line--;
1419
1420      if (ed->topline > 0) {
1421        ed->toppos = prev_line(ed, ed->toppos);
1422        ed->topline--;
1423      }
1424    }
1425  }
1426
1427  ed->refresh = 1;
1428  adjust(ed);
1429}
1430
1431static void pagedown(struct editor *ed, int select) {
1432  int i;
1433
1434  update_selection(ed, select);
1435  for (i = 0; i < ed->env->lines; i++) {
1436    int newpos = next_line(ed, ed->linepos);
1437    if (newpos < 0) break;
1438
1439    ed->linepos = newpos;
1440    ed->line++;
1441
1442    ed->toppos = next_line(ed, ed->toppos);
1443    ed->topline++;
1444  }
1445
1446  ed->refresh = 1;
1447  adjust(ed);
1448}
1449
1450//
1451// Text editing
1452//
1453
1454static void insert_char(struct editor *ed, unsigned char ch) {
1455  erase_selection(ed);
1456  insert(ed, ed->linepos + ed->col, &ch, 1);
1457  ed->col++;
1458  ed->lastcol = ed->col;
1459  adjust(ed);
1460  if (!ed->refresh) ed->lineupdate = 1;
1461}
1462
1463static void newline(struct editor *ed) {
1464  int p;
1465  unsigned char ch;
1466
1467  erase_selection(ed);
1468#if defined(__linux__) || defined(__rtems__)
1469  insert(ed, ed->linepos + ed->col, (unsigned char*) "\n", 1);
1470#else
1471  insert(ed, ed->linepos + ed->col, "\r\n", 2);
1472#endif
1473  ed->col = ed->lastcol = 0;
1474  ed->line++;
1475  p = ed->linepos;
1476  ed->linepos = next_line(ed, ed->linepos);
1477  for (;;) {
1478    ch = get(ed, p++);
1479    if (ch == ' ' || ch == '\t') {
1480      insert(ed, ed->linepos + ed->col, &ch, 1);
1481      ed->col++;
1482    } else {
1483      break;
1484    }
1485  }
1486  ed->lastcol = ed->col;
1487
1488  ed->refresh = 1;
1489
1490  if (ed->line >= ed->topline + ed->env->lines) {
1491    ed->toppos = next_line(ed, ed->toppos);
1492    ed->topline++;
1493    ed->refresh = 1;
1494  }
1495
1496  adjust(ed);
1497}
1498
1499static void backspace(struct editor *ed) {
1500  if (erase_selection(ed)) return;
1501  if (ed->linepos + ed->col == 0) return;
1502  if (ed->col == 0) {
1503    int pos = ed->linepos;
1504    erase(ed, --pos, 1);
1505    if (get(ed, pos - 1) == '\r') erase(ed, --pos, 1);
1506
1507    ed->line--;
1508    ed->linepos = line_start(ed, pos);
1509    ed->col = pos - ed->linepos;
1510    ed->refresh = 1;
1511
1512    if (ed->line < ed->topline) {
1513      ed->toppos = ed->linepos;
1514      ed->topline = ed->line;
1515    }
1516  } else {
1517    ed->col--;
1518    erase(ed, ed->linepos + ed->col, 1);
1519    ed->lineupdate = 1;
1520  }
1521
1522  ed->lastcol = ed->col;
1523  adjust(ed);
1524}
1525
1526static void del(struct editor *ed) {
1527  int pos, ch;
1528
1529  if (erase_selection(ed)) return;
1530  pos = ed->linepos + ed->col;
1531  ch = get(ed, pos);
1532  if (ch < 0) return;
1533
1534  erase(ed, pos, 1);
1535  if (ch == '\r') {
1536    ch = get(ed, pos);
1537    if (ch == '\n') erase(ed, pos, 1);
1538  }
1539
1540  if (ch == '\n') {
1541    ed->refresh = 1;
1542  } else {
1543    ed->lineupdate = 1;
1544  }
1545}
1546
1547static void indent(struct editor *ed, unsigned char *indentation) {
1548  int start, end, i, lines, toplines, newline, ch;
1549  unsigned char *buffer, *p;
1550  int buflen;
1551  int width = strlen((const char*) indentation);
1552  int pos = ed->linepos + ed->col;
1553
1554  if (!get_selection(ed, &start, &end)) {
1555    insert_char(ed, '\t');
1556    return;
1557  }
1558
1559  lines = 0;
1560  toplines = 0;
1561  newline = 1;
1562  for (i = start; i < end; i++) {
1563    if (i == ed->toppos) toplines = lines;
1564    if (newline) {
1565      lines++;
1566      newline = 0;
1567    }
1568    if (get(ed, i) == '\n') newline = 1;
1569  }
1570  buflen = end - start + lines * width;
1571  buffer = malloc(buflen);
1572  if (!buffer) return;
1573
1574  newline = 1;
1575  p = buffer;
1576  for (i = start; i < end; i++) {
1577    if (newline) {
1578      memcpy(p, indentation, width);
1579      p += width;
1580      newline = 0;
1581    }
1582    ch = get(ed, i);
1583    *p++ = ch;
1584    if (ch == '\n') newline = 1;
1585  }
1586
1587  replace(ed, start, end - start, buffer, buflen, 1);
1588  free(buffer);
1589
1590  if (ed->anchor < pos) {
1591    pos += width * lines;
1592  } else {
1593    ed->anchor += width * lines;
1594  }
1595
1596  ed->toppos += width * toplines;
1597  ed->linepos = line_start(ed, pos);
1598  ed->col = ed->lastcol = pos - ed->linepos;
1599
1600  adjust(ed);
1601  ed->refresh = 1;
1602}
1603
1604static void unindent(struct editor *ed, unsigned char *indentation) {
1605  int start, end, i, newline, ch, shrinkage, topofs;
1606  unsigned char *buffer, *p;
1607  int width = strlen((const char*) indentation);
1608  int pos = ed->linepos + ed->col;
1609
1610  if (!get_selection(ed, &start, &end)) return;
1611
1612  buffer = malloc(end - start);
1613  if (!buffer) return;
1614
1615  newline = 1;
1616  p = buffer;
1617  i = start;
1618  shrinkage = 0;
1619  topofs = 0;
1620  while (i < end) {
1621    if (newline) {
1622      newline = 0;
1623      if (compare(ed, indentation, i, width)) {
1624        i += width;
1625        shrinkage += width;
1626        if (i < ed->toppos) topofs -= width;
1627        continue;
1628      }
1629    }
1630    ch = get(ed, i++);
1631    *p++ = ch;
1632    if (ch == '\n') newline = 1;
1633  }
1634
1635  if (!shrinkage) {
1636    free(buffer);
1637    return;
1638  }
1639
1640  replace(ed, start, end - start, buffer, p - buffer, 1);
1641  free(buffer);
1642
1643  if (ed->anchor < pos) {
1644    pos -= shrinkage;
1645  } else {
1646    ed->anchor -= shrinkage;
1647  }
1648
1649  ed->toppos += topofs;
1650  ed->linepos = line_start(ed, pos);
1651  ed->col = ed->lastcol = pos - ed->linepos;
1652
1653  ed->refresh = 1;
1654  adjust(ed);
1655}
1656
1657static void undo(struct editor *ed) {
1658  if (!ed->undo) return;
1659  moveto(ed, ed->undo->pos, 0);
1660  replace(ed, ed->undo->pos, ed->undo->inserted, ed->undo->undobuf, ed->undo->erased, 0);
1661  ed->undo = ed->undo->prev;
1662  if (!ed->undo) ed->dirty = 0;
1663  ed->anchor = -1;
1664  ed->lastcol = ed->col;
1665  ed->refresh = 1;
1666}
1667
1668static void redo(struct editor *ed) {
1669  if (ed->undo) {
1670    if (!ed->undo->next) return;
1671    ed->undo = ed->undo->next;
1672  } else {
1673    if (!ed->undohead) return;
1674    ed->undo = ed->undohead;
1675  }
1676  replace(ed, ed->undo->pos, ed->undo->erased, ed->undo->redobuf, ed->undo->inserted, 0);
1677  moveto(ed, ed->undo->pos, 0);
1678  ed->dirty = 1;
1679  ed->anchor = -1;
1680  ed->lastcol = ed->col;
1681  ed->refresh = 1;
1682}
1683
1684//
1685// Clipboard
1686//
1687
1688static void copy_selection(struct editor *ed) {
1689  int selstart, selend;
1690
1691  if (!get_selection(ed, &selstart, &selend)) return;
1692  ed->env->clipsize = selend - selstart;
1693  ed->env->clipboard = (unsigned char *) realloc(ed->env->clipboard, ed->env->clipsize);
1694  if (!ed->env->clipboard) return;
1695  copy(ed, ed->env->clipboard, selstart, ed->env->clipsize);
1696  select_toggle(ed);
1697}
1698
1699static void cut_selection(struct editor *ed) {
1700  copy_selection(ed);
1701  erase_selection(ed);
1702  select_toggle(ed);
1703}
1704
1705static void paste_selection(struct editor *ed) {
1706  erase_selection(ed);
1707  insert(ed, ed->linepos + ed->col, ed->env->clipboard, ed->env->clipsize);
1708  moveto(ed, ed->linepos + ed->col + ed->env->clipsize, 0);
1709  ed->refresh = 1;
1710}
1711
1712//
1713// Editor Commands
1714//
1715
1716static void open_editor(struct editor *ed) {
1717  int rc;
1718  char *filename;
1719  struct env *env = ed->env;
1720
1721  if (!prompt(ed, "Open file: ", 1)) {
1722    ed->refresh = 1;
1723    return;
1724  }
1725  filename = (char*) ed->env->linebuf;
1726
1727  ed = find_editor(ed->env, filename);
1728  if (ed) {
1729    env->current = ed;
1730  } else {
1731    ed = create_editor(env);
1732    rc = load_file(ed, filename);
1733    if (rc < 0) {
1734      display_message(ed, "Error %d opening %s (%s)", errno, filename, strerror(errno));
1735      sleep(5);
1736      delete_editor(ed);
1737      ed = env->current;
1738    }
1739  }
1740  ed->refresh = 1;
1741}
1742
1743static void new_editor(struct editor *ed) {
1744  ed = create_editor(ed->env);
1745  new_file(ed, "");
1746  ed->refresh = 1;
1747}
1748
1749static void read_from_stdin(struct editor *ed) {
1750  char buffer[512];
1751  int n, pos;
1752
1753  pos = 0;
1754  while ((n = fread(buffer, 1, sizeof(buffer), stdin)) > 0) {
1755    insert(ed, pos, (unsigned char*) buffer, n);
1756    pos += n;
1757  }
1758  strncpy(ed->filename, "<stdin>", FILENAME_MAX);
1759  ed->newfile = 1;
1760  ed->dirty = 0;
1761}
1762
1763static void save_editor(struct editor *ed) {
1764  int rc;
1765
1766  if (!ed->dirty && !ed->newfile) return;
1767
1768  if (ed->newfile) {
1769    if (!prompt(ed, "Save as: ", 1)) {
1770      ed->refresh = 1;
1771      return;
1772    }
1773
1774    if (access((const char*) ed->env->linebuf, F_OK) == 0) {
1775      display_message(ed, "Overwrite %s (y/n)? ", ed->env->linebuf);
1776      if (!ask()) {
1777        ed->refresh = 1;
1778        return;
1779      }
1780    }
1781    strncpy(
1782      ed->filename, (const char*) ed->env->linebuf, FILENAME_MAX);
1783    ed->newfile = 0;
1784  }
1785
1786  rc = save_file(ed);
1787  if (rc < 0) {
1788    display_message(ed, "Error %d saving document (%s)", errno, strerror(errno));
1789    sleep(5);
1790  }
1791
1792  ed->refresh = 1;
1793}
1794
1795static struct editor* close_editor(struct editor *ed) {
1796  struct env *env = ed->env;
1797
1798  if (ed->dirty) {
1799    display_message(ed, "Close %s without saving changes (y/n)? ", ed->filename);
1800    if (!ask()) {
1801      ed->refresh = 1;
1802      return ed;
1803    }
1804  }
1805
1806  delete_editor(ed);
1807
1808  ed = env->current;
1809  if (!ed) {
1810    ed = create_editor(env);
1811    new_file(ed, "");
1812  }
1813  ed->refresh = 1;
1814  return ed;
1815}
1816
1817static void pipe_command(struct editor *ed) {
1818#ifdef __rtems__
1819    display_message(ed, "Not supported");
1820    sleep(3);
1821#else
1822  FILE *f;
1823  char buffer[512];
1824  int n;
1825  int pos;
1826
1827  if (!prompt(ed, "Command: ", 1)) {
1828    ed->refresh = 1;
1829    return;
1830  }
1831
1832#ifdef SANOS
1833  f = popen(ed->env->linebuf, "r2");
1834#else
1835  f = popen(ed->env->linebuf, "r");
1836#endif
1837  if (!f) {
1838    display_message(ed, "Error %d running command (%s)", errno, strerror(errno));
1839    sleep(5);
1840  } else {
1841    erase_selection(ed);
1842    pos = ed->linepos + ed->col;
1843    while ((n = fread(buffer, 1, sizeof(buffer), f)) > 0) {
1844      insert(ed, pos, buffer, n);
1845      pos += n;
1846    }
1847    moveto(ed, pos, 0);
1848    pclose(f);
1849  }
1850  ed->refresh = 1;
1851#endif
1852}
1853
1854static void find_text(struct editor *ed, int next) {
1855  int slen;
1856
1857  if (!next) {
1858    if (!prompt(ed, "Find: ", 1)) {
1859      ed->refresh = 1;
1860      return;
1861    }
1862    if (ed->env->search) free(ed->env->search);
1863    ed->env->search = (unsigned char*) strdup((const char*) ed->env->linebuf);
1864  }
1865
1866  if (!ed->env->search) return;
1867  slen = strlen((const char*) ed->env->search);
1868  if (slen > 0) {
1869    unsigned char *match;
1870
1871    close_gap(ed);
1872    match = (unsigned char*) strstr((char*) ed->start + ed->linepos + ed->col, (char*) ed->env->search);
1873    if (match != NULL) {
1874      int pos = match - ed->start;
1875      ed->anchor = pos;
1876      moveto(ed, pos + slen, 1);
1877    } else {
1878      outch('\007');
1879    }
1880  }
1881  ed->refresh = 1;
1882}
1883
1884static void goto_line(struct editor *ed) {
1885  int lineno, l, pos;
1886
1887  ed->anchor = -1;
1888  if (prompt(ed, "Goto line: ", 1)) {
1889    lineno = atoi((char*) ed->env->linebuf);
1890    if (lineno > 0) {
1891      pos = 0;
1892      for (l = 0; l < lineno - 1; l++) {
1893        pos = next_line(ed, pos);
1894        if (pos < 0) break;
1895      }
1896    } else {
1897      pos = -1;
1898    }
1899
1900    if (pos >= 0) {
1901      moveto(ed, pos, 1);
1902    } else {
1903      outch('\007');
1904    }
1905  }
1906  ed->refresh = 1;
1907}
1908
1909static struct editor *next_file(struct editor *ed) {
1910  ed = ed->env->current = ed->next;
1911  ed->refresh = 1;
1912  return ed;
1913}
1914
1915static void jump_to_editor(struct editor *ed) {
1916  struct env *env = ed->env;
1917  char filename[FILENAME_MAX];
1918  int lineno = 0;
1919
1920  if (!get_selected_text(ed, filename, FILENAME_MAX)) {
1921    int pos = ed->linepos + ed->col;
1922    char *p = filename;
1923    int left = FILENAME_MAX - 1;
1924    while (left > 0) {
1925      int ch = get(ed, pos);
1926      if (ch < 0) break;
1927      if (strchr("!@\"'#%&()[]{}*?+:;\r\n\t ", ch)) break;
1928      *p++ = ch;
1929      left--;
1930      pos++;
1931    }
1932    *p = 0;
1933
1934    if (get(ed, pos) == ':') {
1935      pos++;
1936      for (;;) {
1937        int ch = get(ed, pos);
1938        if (ch < 0) break;
1939        if (ch >= '0' && ch <= '9') {
1940          lineno = lineno * 10 + (ch - '0');
1941        } else {
1942          break;
1943        }
1944        pos++;
1945      }
1946    }
1947  }
1948  if (!*filename) return;
1949
1950  ed = find_editor(env, filename);
1951  if (ed) {
1952    env->current = ed;
1953  } else {
1954    ed = create_editor(env);
1955    if (load_file(ed, filename) < 0) {
1956      outch('\007');
1957      delete_editor(ed);
1958      ed = env->current;
1959    }
1960  }
1961
1962  if (lineno > 0) {
1963    int pos = 0;
1964    while (--lineno > 0) {
1965      pos = next_line(ed, pos);
1966      if (pos < 0) break;
1967    }
1968    if (pos >= 0) moveto(ed, pos, 1);
1969  }
1970
1971  ed->refresh = 1;
1972}
1973
1974static void redraw_screen(struct editor *ed) {
1975  get_console_size(ed->env);
1976  draw_screen(ed);
1977}
1978
1979static int quit(struct env *env) {
1980  struct editor *ed = env->current;
1981  struct editor *start = ed;
1982
1983  do {
1984    if (ed->dirty) {
1985      display_message(ed, "Close %s without saving changes (y/n)? ", ed->filename);
1986      if (!ask()) return 0;
1987    }
1988    ed = ed->next;
1989  } while (ed != start);
1990
1991  return 1;
1992}
1993
1994static void help(struct editor *ed) {
1995  gotoxy(0, 0);
1996  clear_screen();
1997  outstr("Editor Command Summary\r\n");
1998  outstr("======================\r\n\r\n");
1999  outstr("<up>         Move one line up (*)         Ctrl+N  New editor\r\n");
2000  outstr("<down>       Move one line down (*)       Ctrl+O  Open file\r\n");
2001  outstr("<left>       Move one character left (*)  Ctrl+S  Save file\r\n");
2002  outstr("<right>      Move one character right (*) Ctrl+W  Close file\r\n");
2003  outstr("<pgup>       Move one page up (*)         Ctrl+Q  Quit\r\n");
2004  outstr("<pgdn>       Move one page down (*)       Ctrl+P  Pipe command\r\n");
2005  outstr("Ctrl+<left>  Move to previous word (*)    Ctrl+A  Select all\r\n");
2006  outstr("Ctrl+<right> Move to next word (*)        Ctrl+C  Copy selection to clipboard\r\n");
2007  outstr("<home>       Move to start of line (*)    Ctrl+X  Cut selection to clipboard\r\n");
2008  outstr("<end>        Move to end of line (*)      Ctrl+V  Paste from clipboard\r\n");
2009  outstr("Ctrl+<home>  Move to start of file (*)    Ctrl+Z  Undo\r\n");
2010  outstr("Ctrl+<end>   Move to end of file (*)      Ctrl+R  Redo\r\n");
2011  outstr("<backspace>  Delete previous character    Ctrl+F  Find text\r\n");
2012  outstr("<delete>     Delete current character     Ctrl+G  Find next\r\n");
2013  outstr("Ctrl+<tab>   Next editor                  Ctrl+L  Goto line\r\n");
2014  outstr("<tab>        Indent selection             F1      Help\r\n");
2015  outstr("Shift+<tab>  Unindent selection           F2      Select toggle\r\n");
2016  outstr(" (*) Extends selection, F2 toggles.       F3      Navigate to file\r\n");
2017  outstr("                                          F4      Copy selection to clipboard\r\n");
2018  outstr("  Ctrl-Q/S may not work over              F5      Redraw screen\r\n");
2019  outstr("  serial links, use funcions keys         F9      Save file\r\n");
2020  outstr("                                          F10     Quit\r\n");
2021  outstr("Press any key to continue...");
2022  fflush(stdout);
2023
2024  getkey();
2025  draw_screen(ed);
2026  draw_full_statusline(ed);
2027}
2028
2029//
2030// Editor
2031//
2032
2033static void edit(struct editor *ed) {
2034  int done = 0;
2035  int key;
2036
2037  ed->refresh = 1;
2038  while (!done) {
2039    if (ed->refresh) {
2040      draw_screen(ed);
2041      draw_full_statusline(ed);
2042      ed->refresh = 0;
2043      ed->lineupdate = 0;
2044    } else if (ed->lineupdate) {
2045      update_line(ed);
2046      ed->lineupdate = 0;
2047      draw_statusline(ed);
2048    } else {
2049      draw_statusline(ed);
2050    }
2051
2052    position_cursor(ed);
2053    fflush(stdout);
2054    key = getkey();
2055
2056    if (key >= ' ' && key <= 0x7F) {
2057#ifdef LESS
2058      switch (key) {
2059        case 'q': done = 1; break;
2060        case '/': find_text(ed, 0); break;
2061      }
2062#else
2063      insert_char(ed, (unsigned char) key);
2064#endif
2065    } else {
2066      switch (key) {
2067        case KEY_F1: help(ed); break;
2068        case KEY_F2: select_toggle(ed); break;
2069        case KEY_F3: jump_to_editor(ed); ed = ed->env->current; break;
2070        case KEY_F4: copy_selection(ed); break;
2071        case KEY_F5: redraw_screen(ed); break;
2072        case KEY_F9: save_editor(ed); break;
2073        case KEY_F10: done = 1; break;
2074
2075#if defined(__linux__) || defined(__rtems__)
2076        case ctrl('y'): help(ed); break;
2077        case ctrl('t'): top(ed, 0); break;
2078        case ctrl('b'): bottom(ed, 0); break;
2079#endif
2080
2081        case KEY_UP: up(ed, ed->selecting); break;
2082        case KEY_DOWN: down(ed, ed->selecting); break;
2083        case KEY_LEFT: left(ed, ed->selecting); break;
2084        case KEY_RIGHT: right(ed, ed->selecting); break;
2085        case KEY_HOME: home(ed, ed->selecting); break;
2086        case KEY_END: end(ed, ed->selecting); break;
2087        case KEY_PGUP: pageup(ed, ed->selecting); break;
2088        case KEY_PGDN: pagedown(ed, ed->selecting); break;
2089
2090        case KEY_CTRL_RIGHT: wordright(ed, ed->selecting); break;
2091        case KEY_CTRL_LEFT: wordleft(ed, ed->selecting); break;
2092        case KEY_CTRL_HOME: top(ed, ed->selecting); break;
2093        case KEY_CTRL_END: bottom(ed, ed->selecting); break;
2094
2095#if SHIFT_SELECT
2096        case KEY_SHIFT_UP: up(ed, 1); break;
2097        case KEY_SHIFT_DOWN: down(ed, 1); break;
2098        case KEY_SHIFT_LEFT: left(ed, 1); break;
2099        case KEY_SHIFT_RIGHT: right(ed, 1); break;
2100        case KEY_SHIFT_PGUP: pageup(ed, 1); break;
2101        case KEY_SHIFT_PGDN: pagedown(ed, 1); break;
2102        case KEY_SHIFT_HOME: home(ed, 1); break;
2103        case KEY_SHIFT_END: end(ed, 1); break;
2104
2105        case KEY_SHIFT_CTRL_RIGHT: wordright(ed, 1); break;
2106        case KEY_SHIFT_CTRL_LEFT: wordleft(ed, 1); break;
2107        case KEY_SHIFT_CTRL_HOME: top(ed, 1); break;
2108        case KEY_SHIFT_CTRL_END: bottom(ed, 1); break;
2109#endif
2110
2111        case KEY_CTRL_TAB: ed = next_file(ed); break;
2112
2113        case ctrl('e'): select_toggle(ed); break;
2114        case ctrl('a'): select_all(ed); break;
2115        case ctrl('c'): copy_selection(ed); break;
2116        case ctrl('f'): find_text(ed, 0); break;
2117        case ctrl('l'): goto_line(ed); break;
2118        case ctrl('g'): find_text(ed, 1); break;
2119        case ctrl('q'): done = 1; break;
2120#ifdef LESS
2121        case KEY_ESC: done = 1; break;
2122#else
2123        case KEY_TAB: indent(ed, (unsigned char*) INDENT); break;
2124        case KEY_SHIFT_TAB: unindent(ed, (unsigned char*) INDENT); break;
2125
2126        case KEY_ENTER: newline(ed); break;
2127        case KEY_BACKSPACE: backspace(ed); break;
2128        case KEY_DEL: del(ed); break;
2129        case ctrl('x'): cut_selection(ed); break;
2130        case ctrl('z'): undo(ed); break;
2131        case ctrl('r'): redo(ed); break;
2132        case ctrl('v'): paste_selection(ed); break;
2133        case ctrl('o'): open_editor(ed); ed = ed->env->current; break;
2134        case ctrl('n'): new_editor(ed); ed = ed->env->current; break;
2135        case ctrl('s'): save_editor(ed); break;
2136        case ctrl('p'): pipe_command(ed); break;
2137#endif
2138        case ctrl('w'): ed = close_editor(ed); break;
2139      }
2140    }
2141  }
2142}
2143
2144//
2145// main
2146//
2147static int rtems_shell_main_edit(int argc, char *argv[])
2148{
2149  struct env env;
2150  int rc;
2151  int i;
2152  sigset_t blocked_sigmask, orig_sigmask;
2153#if defined(__linux__)
2154  struct termios tio;
2155#endif
2156#if defined(__linux__) || defined(__rtems__)
2157  struct termios orig_tio;
2158#endif
2159#ifdef SANOS
2160  struct term *term;
2161#endif
2162
2163  memset(&env, 0, sizeof(env));
2164  for (i = 1; i < argc; i++) {
2165    struct editor *ed = create_editor(&env);
2166    rc = load_file(ed, argv[i]);
2167    if (rc < 0 && errno == ENOENT) rc = new_file(ed, argv[i]);
2168    if (rc < 0) {
2169      perror(argv[i]);
2170      return 0;
2171    }
2172  }
2173  if (env.current == NULL) {
2174    struct editor *ed = create_editor(&env);
2175    if (isatty(fileno(stdin))) {
2176      new_file(ed, "");
2177    } else {
2178      read_from_stdin(ed);
2179    }
2180  }
2181  env.current = env.current->next;
2182
2183#ifdef SANOS
2184  term = gettib()->proc->term;
2185  if (fdin != term->ttyin) dup2(term->ttyin, fdin);
2186  if (fdout != term->ttyout) dup2(term->ttyout, fdout);
2187#elif !defined(__rtems__)
2188  if (!isatty(fileno(stdin))) {
2189    if (!freopen("/dev/tty", "r", stdin)) perror("/dev/tty");
2190  }
2191#endif
2192
2193  setvbuf(stdout, NULL, 0, 8192);
2194
2195#if defined(__linux__) || defined(__rtems__)
2196  (void) tcgetattr(0, &orig_tio);
2197#if !defined(__rtems__)
2198  cfmakeraw(&tio);
2199  tcsetattr(0, TCSANOW, &tio);
2200#endif
2201  if (getenv("TERM") && strcmp(getenv("TERM"), "linux") == 0) {
2202    linux_console = 1;
2203  } else {
2204    outstr(CLRSCR);
2205    outstr("\033[3 q");  // xterm
2206    outstr("\033]50;CursorShape=2\a");  // KDE
2207  }
2208#endif
2209
2210  get_console_size(&env);
2211
2212  sigemptyset(&blocked_sigmask);
2213  sigaddset(&blocked_sigmask, SIGINT);
2214  sigaddset(&blocked_sigmask, SIGTSTP);
2215  sigaddset(&blocked_sigmask, SIGABRT);
2216  sigprocmask(SIG_BLOCK, &blocked_sigmask, &orig_sigmask);
2217
2218  for (;;) {
2219    if (!env.current) break;
2220    edit(env.current);
2221    if (quit(&env)) break;
2222  }
2223
2224  gotoxy(0, env.lines + 1);
2225  outstr(RESET_COLOR CLREOL);
2226#if defined(__linux__) || defined(__rtems__)
2227  tcsetattr(0, TCSANOW, &orig_tio);
2228#endif
2229
2230  while (env.current) delete_editor(env.current);
2231
2232  if (env.clipboard) free(env.clipboard);
2233  if (env.search) free(env.search);
2234  if (env.linebuf) free(env.linebuf);
2235
2236  setbuf(stdout, NULL);
2237  sigprocmask(SIG_SETMASK, &orig_sigmask, NULL);
2238
2239  return 0;
2240}
2241
2242rtems_shell_cmd_t rtems_shell_EDIT_Command = {
2243  "edit",                /* name */
2244  "edit [file ...]",     /* usage */
2245  "files",               /* topic */
2246  rtems_shell_main_edit, /* command */
2247  NULL,                  /* alias */
2248  NULL                   /* next */
2249};
Note: See TracBrowser for help on using the repository browser.