source: rtems/cpukit/libmisc/shell/main_edit.c @ 1dbd1079

Last change on this file since 1dbd1079 was 1dbd1079, checked in by Frank Kühndel <frank.kuehndel@…>, on 10/12/20 at 16:50:24

shell/main_edit.c: Fix string truncation warning

Using strlcpy() instead of strncpy():

1) Prevents the compiler warnings
2) Ensures, the string is NUL terminated.
3) Avoids that strncpy() unnecessary fills the unused part of the buffer with

0 bytes.

(Note that realpath() also returns NULL if the file does not exist - that
happens always if someone creates a new file with the editor of the shell.)

  • Property mode set to 100644
File size: 55.7 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 <limits.h>
36#include <stdlib.h>
37#include <stdio.h>
38#include <stdarg.h>
39#include <string.h>
40#include <errno.h>
41#include <fcntl.h>
42#include <unistd.h>
43#include <sys/stat.h>
44
45#ifdef SANOS
46#include <os.h>
47#endif
48
49#ifdef __rtems__
50#include <assert.h>
51#include <rtems.h>
52#include <rtems/shell.h>
53#endif
54
55#if defined(__linux__) || defined(__rtems__)
56#include <sys/ioctl.h>
57#include <termios.h>
58#define O_BINARY 0
59static int linux_console;
60#endif
61
62#define MINEXTEND      32768
63#define LINEBUF_EXTRA  32
64
65#ifndef TABSIZE
66#define TABSIZE        8
67#endif
68
69#ifndef INDENT
70#define INDENT "  "
71#endif
72
73#define CLRSCR           "\033[0J\x1b[H\x1b[J"
74#define CLREOL           "\033[K"
75#define GOTOXY           "\033[%d;%dH"
76#define RESET_COLOR      "\033[0m"
77
78#ifdef COLOR
79#define TEXT_COLOR       "\033[44m\033[37m\033[1m"
80#define SELECT_COLOR     "\033[47m\033[37m\033[1m"
81#define STATUS_COLOR     "\033[0m\033[47m\033[30m"
82#else
83#define TEXT_COLOR       "\033[0m"
84#define SELECT_COLOR     "\033[7m\033[1m"
85#define STATUS_COLOR     "\033[1m\033[7m"
86#endif
87
88//
89// Key codes
90//
91
92#define KEY_BACKSPACE        0x101
93#define KEY_ESC              0x102
94#define KEY_INS              0x103
95#define KEY_DEL              0x104
96#define KEY_LEFT             0x105
97#define KEY_RIGHT            0x106
98#define KEY_UP               0x107
99#define KEY_DOWN             0x108
100#define KEY_HOME             0x109
101#define KEY_END              0x10A
102#define KEY_ENTER            0x10B
103#define KEY_TAB              0x10C
104#define KEY_PGUP             0x10D
105#define KEY_PGDN             0x10E
106
107#define KEY_CTRL_LEFT        0x10F
108#define KEY_CTRL_RIGHT       0x110
109#define KEY_CTRL_UP          0x111
110#define KEY_CTRL_DOWN        0x112
111#define KEY_CTRL_HOME        0x113
112#define KEY_CTRL_END         0x114
113#define KEY_CTRL_TAB         0x115
114
115#define KEY_SHIFT_LEFT       0x116
116#define KEY_SHIFT_RIGHT      0x117
117#define KEY_SHIFT_UP         0x118
118#define KEY_SHIFT_DOWN       0x119
119#define KEY_SHIFT_PGUP       0x11A
120#define KEY_SHIFT_PGDN       0x11B
121#define KEY_SHIFT_HOME       0x11C
122#define KEY_SHIFT_END        0x11D
123#define KEY_SHIFT_TAB        0x11E
124
125#define KEY_SHIFT_CTRL_LEFT  0x11F
126#define KEY_SHIFT_CTRL_RIGHT 0x120
127#define KEY_SHIFT_CTRL_UP    0x121
128#define KEY_SHIFT_CTRL_DOWN  0x122
129#define KEY_SHIFT_CTRL_HOME  0x123
130#define KEY_SHIFT_CTRL_END   0x124
131
132#define KEY_F1               0x125
133#define KEY_F2               0x126
134#define KEY_F3               0x127
135#define KEY_F4               0x128
136#define KEY_F5               0x129
137#define KEY_F6               0x12a
138#define KEY_F7               0x12b
139#define KEY_F8               0x12c
140#define KEY_F9               0x12d
141#define KEY_F10              0x12e
142
143#define KEY_UNKNOWN          0xFFF
144
145#define ctrl(c) ((c) - 0x60)
146
147//
148// Editor data block
149//
150// Structure of split buffer:
151//
152//    +------------------+------------------+------------------+
153//    | text before gap  |        gap       |  text after gap  |
154//    +------------------+------------------+------------------+
155//    ^                  ^                  ^                  ^
156//    |                  |                  |                  |
157//  start               gap                rest               end
158//
159
160struct env;
161
162struct undo {
163  int pos;                 // Editor position
164  int erased;              // Size of erased contents
165  int inserted;            // Size of inserted contents
166  unsigned char *undobuf;  // Erased contents for undo
167  unsigned char *redobuf;  // Inserted contents for redo
168  struct undo *next;       // Next undo buffer
169  struct undo *prev;       // Previous undo buffer
170};
171
172struct editor {
173  unsigned char *start;      // Start of text buffer
174  unsigned char *gap;        // Start of gap
175  unsigned char *rest;       // End of gap
176  unsigned char *end;        // End of text buffer
177
178  int toppos;                // Text position for current top screen line
179  int topline;               // Line number for top of screen
180  int margin;                // Position for leftmost column on screen
181
182  int linepos;               // Text position for current line
183  int line;                  // Current document line
184  int col;                   // Current document column
185  int lastcol;               // Remembered column from last horizontal navigation
186  int anchor;                // Anchor position for selection
187
188  struct undo *undohead;     // Start of undo buffer list
189  struct undo *undotail;     // End of undo buffer list
190  struct undo *undo;         // Undo/redo boundary
191
192  int refresh;               // Flag to trigger screen redraw
193  int lineupdate;            // Flag to trigger redraw of current line
194  int dirty;                 // Dirty flag is set when the editor buffer has been changed
195
196  int newfile;               // File is a new file
197  int permissions;           // File permissions
198
199  int selecting;             // Selecting active in the edtor.
200
201  struct env *env;           // Reference to global editor environment
202  struct editor *next;       // Next editor
203  struct editor *prev;       // Previous editor
204
205  char filename[FILENAME_MAX];
206};
207
208struct env {
209  struct editor *current;   // Current editor
210
211  unsigned char *clipboard; // Clipboard
212  int clipsize;             // Clipboard size
213
214  unsigned char *search;    // Search text
215  unsigned char *linebuf;   // Scratch buffer
216
217  int cols;                 // Console columns
218  int lines;                // Console lines
219
220  int untitled;             // Counter for untitled files
221};
222
223//
224// Editor buffer functions
225//
226
227static void clear_undo(struct editor *ed) {
228  struct undo *undo = ed->undohead;
229  while (undo) {
230    struct undo *next = undo->next;
231    free(undo->undobuf);
232    free(undo->redobuf);
233    free(undo);
234    undo = next;
235  }
236  ed->undohead = ed->undotail = ed->undo = NULL;
237}
238
239static void reset_undo(struct editor *ed) {
240  while (ed->undotail != ed->undo) {
241    struct undo *undo = ed->undotail;
242    if (!undo) {
243      ed->undohead = NULL;
244      ed->undotail = NULL;
245      break;
246    }
247    ed->undotail = undo->prev;
248    if (undo->prev) undo->prev->next = NULL;
249    free(undo->undobuf);
250    free(undo->redobuf);
251    free(undo);
252  }
253  ed->undo = ed->undotail;
254}
255
256static struct editor *create_editor(struct env *env) {
257  struct editor *ed = (struct editor *) malloc(sizeof(struct editor));
258  memset(ed, 0, sizeof(struct editor));
259  if (env->current) {
260    ed->next = env->current->next;
261    ed->prev = env->current;
262    env->current->next->prev = ed;
263    env->current->next = ed;
264  } else {
265    ed->next = ed->prev = ed;
266  }
267  ed->env = env;
268  env->current = ed;
269  return ed;
270}
271
272static void delete_editor(struct editor *ed) {
273  if (ed->next == ed) {
274    ed->env->current = NULL;
275  } else {
276    ed->env->current = ed->prev;
277  }
278  ed->next->prev = ed->prev;
279  ed->prev->next = ed->next;
280  if (ed->start) free(ed->start);
281  clear_undo(ed);
282  free(ed);
283}
284
285static struct editor *find_editor(struct env *env, char *filename) {
286  char fnbuf[PATH_MAX];
287  char *fn = fnbuf;
288  struct editor *ed = env->current;
289  struct editor *start = ed;
290
291  /* Note: When realpath() == NULL, usually the file doesn't exist */
292  if (!realpath(filename, fn)) { fn = filename; }
293
294  do {
295    if (strcmp(fn, ed->filename) == 0) return ed;
296    ed = ed->next;
297  } while (ed != start);
298  return NULL;
299}
300
301static int new_file(struct editor *ed, char *filename) {
302  if (*filename) {
303    strlcpy(ed->filename, filename, sizeof(ed->filename));
304  } else {
305    sprintf(ed->filename, "Untitled-%d", ++ed->env->untitled);
306    ed->newfile = 1;
307  }
308  ed->permissions = 0644;
309
310  ed->start = (unsigned char *) malloc(MINEXTEND);
311  if (!ed->start) return -1;
312#ifdef DEBUG
313  memset(ed->start, 0, MINEXTEND);
314#endif
315
316  ed->gap = ed->start;
317  ed->rest = ed->end = ed->gap + MINEXTEND;
318  ed->anchor = -1;
319
320  return 0;
321}
322
323static int load_file(struct editor *ed, char *filename) {
324  struct stat statbuf;
325  int length;
326  int f;
327
328  if (!realpath(filename, ed->filename)) return -1;
329  f = open(ed->filename, O_RDONLY | O_BINARY);
330  if (f < 0) return -1;
331
332  if (fstat(f, &statbuf) < 0) goto err;
333  length = statbuf.st_size;
334  ed->permissions = statbuf.st_mode & 0777;
335
336  ed->start = (unsigned char *) malloc(length + MINEXTEND);
337  if (!ed->start) goto err;
338#ifdef DEBUG
339  memset(ed->start, 0, length + MINEXTEND);
340#endif
341  if (read(f, ed->start, length) != length) goto err;
342
343  ed->gap = ed->start + length;
344  ed->rest = ed->end = ed->gap + MINEXTEND;
345  ed->anchor = -1;
346
347  close(f);
348  return 0;
349
350err:
351  close(f);
352  if (ed->start) {
353    free(ed->start);
354    ed->start = NULL;
355  }
356  return -1;
357}
358
359static int save_file(struct editor *ed) {
360  int f;
361
362  f = open(ed->filename, O_CREAT | O_TRUNC | O_WRONLY, ed->permissions);
363  if (f < 0) return -1;
364
365  if (write(f, ed->start, ed->gap - ed->start) != ed->gap - ed->start) goto err;
366  if (write(f, ed->rest, ed->end - ed->rest) != ed->end - ed->rest) goto err;
367
368  close(f);
369  ed->dirty = 0;
370  clear_undo(ed);
371  return 0;
372
373err:
374  close(f);
375  return -1;
376}
377
378static int text_length(struct editor *ed) {
379  return (ed->gap - ed->start) + (ed->end - ed->rest);
380}
381
382static unsigned char *text_ptr(struct editor *ed, int pos) {
383  unsigned char *p = ed->start + pos;
384  if (p >= ed->gap) p += (ed->rest - ed->gap);
385  return p;
386}
387
388static void move_gap(struct editor *ed, int pos, int minsize) {
389  int gapsize = ed->rest - ed->gap;
390  unsigned char *p = text_ptr(ed, pos);
391  if (minsize < 0) minsize = 0;
392
393  if (minsize <= gapsize) {
394    if (p != ed->rest) {
395      if (p < ed->gap) {
396        memmove(p + gapsize, p, ed->gap - p);
397      } else {
398        memmove(ed->gap, ed->rest, p - ed->rest);
399      }
400      ed->gap = ed->start + pos;
401      ed->rest = ed->gap + gapsize;
402    }
403  } else {
404    int newsize;
405    unsigned char *start;
406    unsigned char *gap;
407    unsigned char *rest;
408    unsigned char *end;
409
410    if (gapsize + MINEXTEND > minsize) minsize = gapsize + MINEXTEND;
411    newsize = (ed->end - ed->start) - gapsize + minsize;
412    start = (unsigned char *) malloc(newsize); // TODO check for out of memory
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    strlcpy(
1782      ed->filename, (const char*) ed->env->linebuf, sizeof(ed->filename));
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 void 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;
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}
1815
1816static void pipe_command(struct editor *ed) {
1817#ifdef __rtems__
1818    display_message(ed, "Not supported");
1819    sleep(3);
1820#else
1821  FILE *f;
1822  char buffer[512];
1823  int n;
1824  int pos;
1825
1826  if (!prompt(ed, "Command: ", 1)) {
1827    ed->refresh = 1;
1828    return;
1829  }
1830
1831#ifdef SANOS
1832  f = popen(ed->env->linebuf, "r2");
1833#else
1834  f = popen(ed->env->linebuf, "r");
1835#endif
1836  if (!f) {
1837    display_message(ed, "Error %d running command (%s)", errno, strerror(errno));
1838    sleep(5);
1839  } else {
1840    erase_selection(ed);
1841    pos = ed->linepos + ed->col;
1842    while ((n = fread(buffer, 1, sizeof(buffer), f)) > 0) {
1843      insert(ed, pos, buffer, n);
1844      pos += n;
1845    }
1846    moveto(ed, pos, 0);
1847    pclose(f);
1848  }
1849  ed->refresh = 1;
1850#endif
1851}
1852
1853static void find_text(struct editor *ed, int next) {
1854  int slen;
1855
1856  if (!next) {
1857    if (!prompt(ed, "Find: ", 1)) {
1858      ed->refresh = 1;
1859      return;
1860    }
1861    if (ed->env->search) free(ed->env->search);
1862    ed->env->search = (unsigned char*) strdup((const char*) ed->env->linebuf);
1863  }
1864
1865  if (!ed->env->search) return;
1866  slen = strlen((const char*) ed->env->search);
1867  if (slen > 0) {
1868    unsigned char *match;
1869
1870    close_gap(ed);
1871    match = (unsigned char*) strstr((char*) ed->start + ed->linepos + ed->col, (char*) ed->env->search);
1872    if (match != NULL) {
1873      int pos = match - ed->start;
1874      ed->anchor = pos;
1875      moveto(ed, pos + slen, 1);
1876    } else {
1877      outch('\007');
1878    }
1879  }
1880  ed->refresh = 1;
1881}
1882
1883static void goto_line(struct editor *ed) {
1884  int lineno, l, pos;
1885
1886  ed->anchor = -1;
1887  if (prompt(ed, "Goto line: ", 1)) {
1888    lineno = atoi((char*) ed->env->linebuf);
1889    if (lineno > 0) {
1890      pos = 0;
1891      for (l = 0; l < lineno - 1; l++) {
1892        pos = next_line(ed, pos);
1893        if (pos < 0) break;
1894      }
1895    } else {
1896      pos = -1;
1897    }
1898
1899    if (pos >= 0) {
1900      moveto(ed, pos, 1);
1901    } else {
1902      outch('\007');
1903    }
1904  }
1905  ed->refresh = 1;
1906}
1907
1908static struct editor *next_file(struct editor *ed) {
1909  ed = ed->env->current = ed->next;
1910  ed->refresh = 1;
1911  return ed;
1912}
1913
1914static void jump_to_editor(struct editor *ed) {
1915  struct env *env = ed->env;
1916  char filename[FILENAME_MAX];
1917  int lineno = 0;
1918
1919  if (!get_selected_text(ed, filename, FILENAME_MAX)) {
1920    int pos = ed->linepos + ed->col;
1921    char *p = filename;
1922    int left = FILENAME_MAX - 1;
1923    while (left > 0) {
1924      int ch = get(ed, pos);
1925      if (ch < 0) break;
1926      if (strchr("!@\"'#%&()[]{}*?+:;\r\n\t ", ch)) break;
1927      *p++ = ch;
1928      left--;
1929      pos++;
1930    }
1931    *p = 0;
1932
1933    if (get(ed, pos) == ':') {
1934      pos++;
1935      for (;;) {
1936        int ch = get(ed, pos);
1937        if (ch < 0) break;
1938        if (ch >= '0' && ch <= '9') {
1939          lineno = lineno * 10 + (ch - '0');
1940        } else {
1941          break;
1942        }
1943        pos++;
1944      }
1945    }
1946  }
1947  if (!*filename) return;
1948
1949  ed = find_editor(env, filename);
1950  if (ed) {
1951    env->current = ed;
1952  } else {
1953    ed = create_editor(env);
1954    if (load_file(ed, filename) < 0) {
1955      outch('\007');
1956      delete_editor(ed);
1957      ed = env->current;
1958    }
1959  }
1960
1961  if (lineno > 0) {
1962    int pos = 0;
1963    while (--lineno > 0) {
1964      pos = next_line(ed, pos);
1965      if (pos < 0) break;
1966    }
1967    if (pos >= 0) moveto(ed, pos, 1);
1968  }
1969
1970  ed->refresh = 1;
1971}
1972
1973static void redraw_screen(struct editor *ed) {
1974  get_console_size(ed->env);
1975  draw_screen(ed);
1976}
1977
1978static int quit(struct env *env) {
1979  struct editor *ed = env->current;
1980  struct editor *start = ed;
1981
1982  do {
1983    if (ed->dirty) {
1984      display_message(ed, "Close %s without saving changes (y/n)? ", ed->filename);
1985      if (!ask()) return 0;
1986    }
1987    ed = ed->next;
1988  } while (ed != start);
1989
1990  return 1;
1991}
1992
1993static void help(struct editor *ed) {
1994  gotoxy(0, 0);
1995  clear_screen();
1996  outstr("Editor Command Summary\r\n");
1997  outstr("======================\r\n\r\n");
1998  outstr("<up>         Move one line up (*)         Ctrl+N  New editor\r\n");
1999  outstr("<down>       Move one line down (*)       Ctrl+O  Open file\r\n");
2000  outstr("<left>       Move one character left (*)  Ctrl+S  Save file\r\n");
2001  outstr("<right>      Move one character right (*) Ctrl+W  Close file\r\n");
2002  outstr("<pgup>       Move one page up (*)         Ctrl+Q  Quit\r\n");
2003  outstr("<pgdn>       Move one page down (*)       Ctrl+P  Pipe command\r\n");
2004  outstr("Ctrl+<left>  Move to previous word (*)    Ctrl+A  Select all\r\n");
2005  outstr("Ctrl+<right> Move to next word (*)        Ctrl+C  Copy selection to clipboard\r\n");
2006  outstr("<home>       Move to start of line (*)    Ctrl+X  Cut selection to clipboard\r\n");
2007  outstr("<end>        Move to end of line (*)      Ctrl+V  Paste from clipboard\r\n");
2008  outstr("Ctrl+<home>  Move to start of file (*)    Ctrl+Z  Undo\r\n");
2009  outstr("Ctrl+<end>   Move to end of file (*)      Ctrl+R  Redo\r\n");
2010  outstr("<backspace>  Delete previous character    Ctrl+F  Find text\r\n");
2011  outstr("<delete>     Delete current character     Ctrl+G  Find next\r\n");
2012  outstr("Ctrl+<tab>   Next editor                  Ctrl+L  Goto line\r\n");
2013  outstr("<tab>        Indent selection             F1      Help\r\n");
2014  outstr("Shift+<tab>  Unindent selection           F2      Select toggle\r\n");
2015  outstr(" (*) Extends selection, F2 toggles.       F3      Navigate to file\r\n");
2016  outstr("                                          F4      Copy selection to clipboard\r\n");
2017  outstr("  Ctrl-Q/S may not work over              F5      Redraw screen\r\n");
2018  outstr("  serial links, use funcions keys         F9      Save file\r\n");
2019  outstr("                                          F10     Quit\r\n");
2020  outstr("Press any key to continue...");
2021  fflush(stdout);
2022
2023  getkey();
2024  draw_screen(ed);
2025  draw_full_statusline(ed);
2026}
2027
2028//
2029// Editor
2030//
2031
2032static void edit(struct editor *ed) {
2033  int done = 0;
2034  int key;
2035
2036  ed->refresh = 1;
2037  while (!done) {
2038    if (ed->refresh) {
2039      draw_screen(ed);
2040      draw_full_statusline(ed);
2041      ed->refresh = 0;
2042      ed->lineupdate = 0;
2043    } else if (ed->lineupdate) {
2044      update_line(ed);
2045      ed->lineupdate = 0;
2046      draw_statusline(ed);
2047    } else {
2048      draw_statusline(ed);
2049    }
2050
2051    position_cursor(ed);
2052    fflush(stdout);
2053    key = getkey();
2054
2055    if (key >= ' ' && key <= 0x7F) {
2056#ifdef LESS
2057      switch (key) {
2058        case 'q': done = 1; break;
2059        case '/': find_text(ed, 0); break;
2060      }
2061#else
2062      insert_char(ed, (unsigned char) key);
2063#endif
2064    } else {
2065      switch (key) {
2066        case KEY_F1: help(ed); break;
2067        case KEY_F2: select_toggle(ed); break;
2068        case KEY_F3: jump_to_editor(ed); ed = ed->env->current; break;
2069        case KEY_F4: copy_selection(ed); break;
2070        case KEY_F5: redraw_screen(ed); break;
2071        case KEY_F9: save_editor(ed); break;
2072        case KEY_F10: done = 1; break;
2073
2074#if defined(__linux__) || defined(__rtems__)
2075        case ctrl('y'): help(ed); break;
2076        case ctrl('t'): top(ed, 0); break;
2077        case ctrl('b'): bottom(ed, 0); break;
2078#endif
2079
2080        case KEY_UP: up(ed, ed->selecting); break;
2081        case KEY_DOWN: down(ed, ed->selecting); break;
2082        case KEY_LEFT: left(ed, ed->selecting); break;
2083        case KEY_RIGHT: right(ed, ed->selecting); break;
2084        case KEY_HOME: home(ed, ed->selecting); break;
2085        case KEY_END: end(ed, ed->selecting); break;
2086        case KEY_PGUP: pageup(ed, ed->selecting); break;
2087        case KEY_PGDN: pagedown(ed, ed->selecting); break;
2088
2089        case KEY_CTRL_RIGHT: wordright(ed, ed->selecting); break;
2090        case KEY_CTRL_LEFT: wordleft(ed, ed->selecting); break;
2091        case KEY_CTRL_HOME: top(ed, ed->selecting); break;
2092        case KEY_CTRL_END: bottom(ed, ed->selecting); break;
2093
2094#if SHIFT_SELECT
2095        case KEY_SHIFT_UP: up(ed, 1); break;
2096        case KEY_SHIFT_DOWN: down(ed, 1); break;
2097        case KEY_SHIFT_LEFT: left(ed, 1); break;
2098        case KEY_SHIFT_RIGHT: right(ed, 1); break;
2099        case KEY_SHIFT_PGUP: pageup(ed, 1); break;
2100        case KEY_SHIFT_PGDN: pagedown(ed, 1); break;
2101        case KEY_SHIFT_HOME: home(ed, 1); break;
2102        case KEY_SHIFT_END: end(ed, 1); break;
2103
2104        case KEY_SHIFT_CTRL_RIGHT: wordright(ed, 1); break;
2105        case KEY_SHIFT_CTRL_LEFT: wordleft(ed, 1); break;
2106        case KEY_SHIFT_CTRL_HOME: top(ed, 1); break;
2107        case KEY_SHIFT_CTRL_END: bottom(ed, 1); break;
2108#endif
2109
2110        case KEY_CTRL_TAB: ed = next_file(ed); break;
2111
2112        case ctrl('e'): select_toggle(ed); break;
2113        case ctrl('a'): select_all(ed); break;
2114        case ctrl('c'): copy_selection(ed); break;
2115        case ctrl('f'): find_text(ed, 0); break;
2116        case ctrl('l'): goto_line(ed); break;
2117        case ctrl('g'): find_text(ed, 1); break;
2118        case ctrl('q'): done = 1; break;
2119#ifdef LESS
2120        case KEY_ESC: done = 1; break;
2121#else
2122        case KEY_TAB: indent(ed, (unsigned char*) INDENT); break;
2123        case KEY_SHIFT_TAB: unindent(ed, (unsigned char*) INDENT); break;
2124
2125        case KEY_ENTER: newline(ed); break;
2126        case KEY_BACKSPACE: backspace(ed); break;
2127        case KEY_DEL: del(ed); break;
2128        case ctrl('x'): cut_selection(ed); break;
2129        case ctrl('z'): undo(ed); break;
2130        case ctrl('r'): redo(ed); break;
2131        case ctrl('v'): paste_selection(ed); break;
2132        case ctrl('o'): open_editor(ed); ed = ed->env->current; break;
2133        case ctrl('n'): new_editor(ed); ed = ed->env->current; break;
2134        case ctrl('s'): save_editor(ed); break;
2135        case ctrl('p'): pipe_command(ed); break;
2136#endif
2137#if defined(__rtems__)
2138        /*
2139         * Coverity spotted this as using ed after free() so changing
2140         * the order of the statements.
2141         */
2142        case ctrl('w'): ed = ed->env->current; close_editor(ed); break;
2143#else
2144        case ctrl('w'): close_editor(ed); ed = ed->env->current; break;
2145#endif
2146      }
2147    }
2148  }
2149}
2150
2151//
2152// main
2153//
2154static int rtems_shell_main_edit(int argc, char *argv[])
2155{
2156  struct env env;
2157  int rc;
2158  int i;
2159  sigset_t blocked_sigmask, orig_sigmask;
2160#if defined(__linux__)
2161  struct termios tio;
2162#endif
2163#if defined(__linux__) || defined(__rtems__)
2164  struct termios orig_tio;
2165#endif
2166#ifdef SANOS
2167  struct term *term;
2168#endif
2169
2170  memset(&env, 0, sizeof(env));
2171  for (i = 1; i < argc; i++) {
2172    struct editor *ed = create_editor(&env);
2173    rc = load_file(ed, argv[i]);
2174    if (rc < 0 && errno == ENOENT) rc = new_file(ed, argv[i]);
2175    if (rc < 0) {
2176      perror(argv[i]);
2177      return 0;
2178    }
2179  }
2180  if (env.current == NULL) {
2181    struct editor *ed = create_editor(&env);
2182    if (isatty(fileno(stdin))) {
2183      new_file(ed, "");
2184    } else {
2185      read_from_stdin(ed);
2186    }
2187  }
2188  env.current = env.current->next;
2189
2190#ifdef SANOS
2191  term = gettib()->proc->term;
2192  if (fdin != term->ttyin) dup2(term->ttyin, fdin);
2193  if (fdout != term->ttyout) dup2(term->ttyout, fdout);
2194#elif !defined(__rtems__)
2195  if (!isatty(fileno(stdin))) {
2196    if (!freopen("/dev/tty", "r", stdin)) perror("/dev/tty");
2197  }
2198#endif
2199
2200  setvbuf(stdout, NULL, 0, 8192);
2201
2202#if defined(__linux__) || defined(__rtems__)
2203  (void) tcgetattr(0, &orig_tio);
2204#if !defined(__rtems__)
2205  cfmakeraw(&tio);
2206  tcsetattr(0, TCSANOW, &tio);
2207#endif
2208  if (getenv("TERM") && strcmp(getenv("TERM"), "linux") == 0) {
2209    linux_console = 1;
2210  } else {
2211    outstr(CLRSCR);
2212    outstr("\033[3 q");  // xterm
2213    outstr("\033]50;CursorShape=2\a");  // KDE
2214  }
2215#endif
2216
2217  get_console_size(&env);
2218
2219  sigemptyset(&blocked_sigmask);
2220  sigaddset(&blocked_sigmask, SIGINT);
2221  sigaddset(&blocked_sigmask, SIGTSTP);
2222  sigaddset(&blocked_sigmask, SIGABRT);
2223  sigprocmask(SIG_BLOCK, &blocked_sigmask, &orig_sigmask);
2224
2225  for (;;) {
2226    if (!env.current) break;
2227    edit(env.current);
2228    if (quit(&env)) break;
2229  }
2230
2231  gotoxy(0, env.lines + 1);
2232  outstr(RESET_COLOR CLREOL);
2233#if defined(__linux__) || defined(__rtems__)
2234  tcsetattr(0, TCSANOW, &orig_tio);
2235#endif
2236
2237  while (env.current) delete_editor(env.current);
2238
2239  if (env.clipboard) free(env.clipboard);
2240  if (env.search) free(env.search);
2241  if (env.linebuf) free(env.linebuf);
2242
2243  setbuf(stdout, NULL);
2244  sigprocmask(SIG_SETMASK, &orig_sigmask, NULL);
2245
2246  return 0;
2247}
2248
2249rtems_shell_cmd_t rtems_shell_EDIT_Command = {
2250  "edit",                /* name */
2251  "edit [file ...]",     /* usage */
2252  "files",               /* topic */
2253  rtems_shell_main_edit, /* command */
2254  NULL,                  /* alias */
2255  NULL                   /* next */
2256};
Note: See TracBrowser for help on using the repository browser.