Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:19

0001 //
0002 // edit.c
0003 //
0004 // Text editor
0005 //
0006 // Copyright (C) 2002 Michael Ringgaard. All rights reserved.
0007 //
0008 // Redistribution and use in source and binary forms, with or without
0009 // modification, are permitted provided that the following conditions
0010 // are met:
0011 //
0012 // 1. Redistributions of source code must retain the above copyright
0013 //    notice, this list of conditions and the following disclaimer.
0014 // 2. Redistributions in binary form must reproduce the above copyright
0015 //    notice, this list of conditions and the following disclaimer in the
0016 //    documentation and/or other materials provided with the distribution.
0017 // 3. Neither the name of the project nor the names of its contributors
0018 //    may be used to endorse or promote products derived from this software
0019 //    without specific prior written permission.
0020 //
0021 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
0022 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0023 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0024 // ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
0025 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0026 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0027 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0028 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0029 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0030 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0031 // SUCH DAMAGE.
0032 //
0033 
0034 #include <signal.h>
0035 #include <limits.h>
0036 #include <stdlib.h>
0037 #include <stdio.h>
0038 #include <stdarg.h>
0039 #include <string.h>
0040 #include <errno.h>
0041 #include <fcntl.h>
0042 #include <unistd.h>
0043 #include <sys/stat.h>
0044 
0045 #ifdef SANOS
0046 #include <os.h>
0047 #endif
0048 
0049 #ifdef __rtems__
0050 #include <assert.h>
0051 #include <rtems.h>
0052 #include <rtems/shell.h>
0053 #endif
0054 
0055 #if defined(__linux__) || defined(__rtems__)
0056 #include <sys/ioctl.h>
0057 #include <termios.h>
0058 #ifndef O_BINARY
0059 #define O_BINARY 0
0060 #endif
0061 static int linux_console;
0062 #endif
0063 
0064 #define MINEXTEND      32768
0065 #define LINEBUF_EXTRA  32
0066 
0067 #ifndef TABSIZE
0068 #define TABSIZE        8
0069 #endif
0070 
0071 #ifndef INDENT
0072 #define INDENT "  "
0073 #endif
0074 
0075 #define CLRSCR           "\033[0J\x1b[H\x1b[J"
0076 #define CLREOL           "\033[K"
0077 #define GOTOXY           "\033[%d;%dH"
0078 #define RESET_COLOR      "\033[0m"
0079 
0080 #ifdef COLOR
0081 #define TEXT_COLOR       "\033[44m\033[37m\033[1m"
0082 #define SELECT_COLOR     "\033[47m\033[37m\033[1m"
0083 #define STATUS_COLOR     "\033[0m\033[47m\033[30m"
0084 #else
0085 #define TEXT_COLOR       "\033[0m"
0086 #define SELECT_COLOR     "\033[7m\033[1m"
0087 #define STATUS_COLOR     "\033[1m\033[7m"
0088 #endif
0089 
0090 //
0091 // Key codes
0092 //
0093 
0094 #define KEY_BACKSPACE        0x101
0095 #define KEY_ESC              0x102
0096 #define KEY_INS              0x103
0097 #define KEY_DEL              0x104
0098 #define KEY_LEFT             0x105
0099 #define KEY_RIGHT            0x106
0100 #define KEY_UP               0x107
0101 #define KEY_DOWN             0x108
0102 #define KEY_HOME             0x109
0103 #define KEY_END              0x10A
0104 #define KEY_ENTER            0x10B
0105 #define KEY_TAB              0x10C
0106 #define KEY_PGUP             0x10D
0107 #define KEY_PGDN             0x10E
0108 
0109 #define KEY_CTRL_LEFT        0x10F
0110 #define KEY_CTRL_RIGHT       0x110
0111 #define KEY_CTRL_UP          0x111
0112 #define KEY_CTRL_DOWN        0x112
0113 #define KEY_CTRL_HOME        0x113
0114 #define KEY_CTRL_END         0x114
0115 #define KEY_CTRL_TAB         0x115
0116 
0117 #define KEY_SHIFT_LEFT       0x116
0118 #define KEY_SHIFT_RIGHT      0x117
0119 #define KEY_SHIFT_UP         0x118
0120 #define KEY_SHIFT_DOWN       0x119
0121 #define KEY_SHIFT_PGUP       0x11A
0122 #define KEY_SHIFT_PGDN       0x11B
0123 #define KEY_SHIFT_HOME       0x11C
0124 #define KEY_SHIFT_END        0x11D
0125 #define KEY_SHIFT_TAB        0x11E
0126 
0127 #define KEY_SHIFT_CTRL_LEFT  0x11F
0128 #define KEY_SHIFT_CTRL_RIGHT 0x120
0129 #define KEY_SHIFT_CTRL_UP    0x121
0130 #define KEY_SHIFT_CTRL_DOWN  0x122
0131 #define KEY_SHIFT_CTRL_HOME  0x123
0132 #define KEY_SHIFT_CTRL_END   0x124
0133 
0134 #define KEY_F1               0x125
0135 #define KEY_F2               0x126
0136 #define KEY_F3               0x127
0137 #define KEY_F4               0x128
0138 #define KEY_F5               0x129
0139 #define KEY_F6               0x12a
0140 #define KEY_F7               0x12b
0141 #define KEY_F8               0x12c
0142 #define KEY_F9               0x12d
0143 #define KEY_F10              0x12e
0144 
0145 #define KEY_UNKNOWN          0xFFF
0146 
0147 #define ctrl(c) ((c) - 0x60)
0148 
0149 //
0150 // Editor data block
0151 //
0152 // Structure of split buffer:
0153 //
0154 //    +------------------+------------------+------------------+
0155 //    | text before gap  |        gap       |  text after gap  |
0156 //    +------------------+------------------+------------------+
0157 //    ^                  ^                  ^                  ^
0158 //    |                  |                  |                  |
0159 //  start               gap                rest               end
0160 //
0161 
0162 struct env;
0163 
0164 struct undo {
0165   int pos;                 // Editor position
0166   int erased;              // Size of erased contents
0167   int inserted;            // Size of inserted contents
0168   unsigned char *undobuf;  // Erased contents for undo
0169   unsigned char *redobuf;  // Inserted contents for redo
0170   struct undo *next;       // Next undo buffer
0171   struct undo *prev;       // Previous undo buffer
0172 };
0173 
0174 struct editor {
0175   unsigned char *start;      // Start of text buffer
0176   unsigned char *gap;        // Start of gap
0177   unsigned char *rest;       // End of gap
0178   unsigned char *end;        // End of text buffer
0179 
0180   int toppos;                // Text position for current top screen line
0181   int topline;               // Line number for top of screen
0182   int margin;                // Position for leftmost column on screen
0183 
0184   int linepos;               // Text position for current line
0185   int line;                  // Current document line
0186   int col;                   // Current document column
0187   int lastcol;               // Remembered column from last horizontal navigation
0188   int anchor;                // Anchor position for selection
0189 
0190   struct undo *undohead;     // Start of undo buffer list
0191   struct undo *undotail;     // End of undo buffer list
0192   struct undo *undo;         // Undo/redo boundary
0193 
0194   int refresh;               // Flag to trigger screen redraw
0195   int lineupdate;            // Flag to trigger redraw of current line
0196   int dirty;                 // Dirty flag is set when the editor buffer has been changed
0197 
0198   int newfile;               // File is a new file
0199   int permissions;           // File permissions
0200 
0201   int selecting;             // Selecting active in the edtor.
0202 
0203   struct env *env;           // Reference to global editor environment
0204   struct editor *next;       // Next editor
0205   struct editor *prev;       // Previous editor
0206 
0207   char filename[FILENAME_MAX];
0208 };
0209 
0210 struct env {
0211   struct editor *current;   // Current editor
0212 
0213   unsigned char *clipboard; // Clipboard
0214   int clipsize;             // Clipboard size
0215 
0216   unsigned char *search;    // Search text
0217   unsigned char *linebuf;   // Scratch buffer
0218 
0219   int cols;                 // Console columns
0220   int lines;                // Console lines
0221 
0222   int untitled;             // Counter for untitled files
0223 };
0224 
0225 //
0226 // Editor buffer functions
0227 //
0228 
0229 static void clear_undo(struct editor *ed) {
0230   struct undo *undo = ed->undohead;
0231   while (undo) {
0232     struct undo *next = undo->next;
0233     free(undo->undobuf);
0234     free(undo->redobuf);
0235     free(undo);
0236     undo = next;
0237   }
0238   ed->undohead = ed->undotail = ed->undo = NULL;
0239 }
0240 
0241 static void reset_undo(struct editor *ed) {
0242   while (ed->undotail != ed->undo) {
0243     struct undo *undo = ed->undotail;
0244     if (!undo) {
0245       ed->undohead = NULL;
0246       ed->undotail = NULL;
0247       break;
0248     }
0249     ed->undotail = undo->prev;
0250     if (undo->prev) undo->prev->next = NULL;
0251     free(undo->undobuf);
0252     free(undo->redobuf);
0253     free(undo);
0254   }
0255   ed->undo = ed->undotail;
0256 }
0257 
0258 static struct editor *create_editor(struct env *env) {
0259   struct editor *ed = (struct editor *) malloc(sizeof(struct editor));
0260   memset(ed, 0, sizeof(struct editor));
0261   if (env->current) {
0262     ed->next = env->current->next;
0263     ed->prev = env->current;
0264     env->current->next->prev = ed;
0265     env->current->next = ed;
0266   } else {
0267     ed->next = ed->prev = ed;
0268   }
0269   ed->env = env;
0270   env->current = ed;
0271   return ed;
0272 }
0273 
0274 static void delete_editor(struct editor *ed) {
0275   if (ed->next == ed) {
0276     ed->env->current = NULL;
0277   } else {
0278     ed->env->current = ed->prev;
0279   }
0280   ed->next->prev = ed->prev;
0281   ed->prev->next = ed->next;
0282   if (ed->start) free(ed->start);
0283   clear_undo(ed);
0284   free(ed);
0285 }
0286 
0287 static struct editor *find_editor(struct env *env, char *filename) {
0288   char fnbuf[PATH_MAX];
0289   char *fn = fnbuf;
0290   struct editor *ed = env->current;
0291   struct editor *start = ed;
0292 
0293   /* Note: When realpath() == NULL, usually the file doesn't exist */
0294   if (!realpath(filename, fn)) { fn = filename; }
0295 
0296   do {
0297     if (strcmp(fn, ed->filename) == 0) return ed;
0298     ed = ed->next;
0299   } while (ed != start);
0300   return NULL;
0301 }
0302 
0303 static int new_file(struct editor *ed, char *filename) {
0304   if (*filename) {
0305     strlcpy(ed->filename, filename, sizeof(ed->filename));
0306   } else {
0307     sprintf(ed->filename, "Untitled-%d", ++ed->env->untitled);
0308     ed->newfile = 1;
0309   }
0310   ed->permissions = 0644;
0311 
0312   ed->start = (unsigned char *) malloc(MINEXTEND);
0313   if (!ed->start) return -1;
0314 #ifdef DEBUG
0315   memset(ed->start, 0, MINEXTEND);
0316 #endif
0317 
0318   ed->gap = ed->start;
0319   ed->rest = ed->end = ed->gap + MINEXTEND;
0320   ed->anchor = -1;
0321 
0322   return 0;
0323 }
0324 
0325 static int load_file(struct editor *ed, char *filename) {
0326   struct stat statbuf;
0327   int length;
0328   int f;
0329 
0330   if (!realpath(filename, ed->filename)) return -1;
0331   f = open(ed->filename, O_RDONLY | O_BINARY);
0332   if (f < 0) return -1;
0333 
0334   if (fstat(f, &statbuf) < 0) goto err;
0335   length = statbuf.st_size;
0336   ed->permissions = statbuf.st_mode & 0777;
0337 
0338   ed->start = (unsigned char *) malloc(length + MINEXTEND);
0339   if (!ed->start) goto err;
0340 #ifdef DEBUG
0341   memset(ed->start, 0, length + MINEXTEND);
0342 #endif
0343   if (read(f, ed->start, length) != length) goto err;
0344 
0345   ed->gap = ed->start + length;
0346   ed->rest = ed->end = ed->gap + MINEXTEND;
0347   ed->anchor = -1;
0348 
0349   close(f);
0350   return 0;
0351 
0352 err:
0353   close(f);
0354   if (ed->start) {
0355     free(ed->start);
0356     ed->start = NULL;
0357   }
0358   return -1;
0359 }
0360 
0361 static int save_file(struct editor *ed) {
0362   int f;
0363 
0364   f = open(ed->filename, O_CREAT | O_TRUNC | O_WRONLY, ed->permissions);
0365   if (f < 0) return -1;
0366 
0367   if (write(f, ed->start, ed->gap - ed->start) != ed->gap - ed->start) goto err;
0368   if (write(f, ed->rest, ed->end - ed->rest) != ed->end - ed->rest) goto err;
0369 
0370   close(f);
0371   ed->dirty = 0;
0372   clear_undo(ed);
0373   return 0;
0374 
0375 err:
0376   close(f);
0377   return -1;
0378 }
0379 
0380 static int text_length(struct editor *ed) {
0381   return (ed->gap - ed->start) + (ed->end - ed->rest);
0382 }
0383 
0384 static unsigned char *text_ptr(struct editor *ed, int pos) {
0385   unsigned char *p = ed->start + pos;
0386   if (p >= ed->gap) p += (ed->rest - ed->gap);
0387   return p;
0388 }
0389 
0390 static void move_gap(struct editor *ed, int pos, int minsize) {
0391   int gapsize = ed->rest - ed->gap;
0392   unsigned char *p = text_ptr(ed, pos);
0393   if (minsize < 0) minsize = 0;
0394 
0395   if (minsize <= gapsize) {
0396     if (p != ed->rest) {
0397       if (p < ed->gap) {
0398         memmove(p + gapsize, p, ed->gap - p);
0399       } else {
0400         memmove(ed->gap, ed->rest, p - ed->rest);
0401       }
0402       ed->gap = ed->start + pos;
0403       ed->rest = ed->gap + gapsize;
0404     }
0405   } else {
0406     int newsize;
0407     unsigned char *start;
0408     unsigned char *gap;
0409     unsigned char *rest;
0410     unsigned char *end;
0411 
0412     if (gapsize + MINEXTEND > minsize) minsize = gapsize + MINEXTEND;
0413     newsize = (ed->end - ed->start) - gapsize + minsize;
0414     start = (unsigned char *) malloc(newsize); // TODO check for out of memory
0415     if (start == NULL) {
0416         return;
0417     }
0418     gap = start + pos;
0419     rest = gap + minsize;
0420     end = start + newsize;
0421 
0422     if (p < ed->gap) {
0423       memcpy(start, ed->start, pos);
0424       memcpy(rest, p, ed->gap - p);
0425       memcpy(end - (ed->end - ed->rest), ed->rest, ed->end - ed->rest);
0426     } else {
0427       memcpy(start, ed->start, ed->gap - ed->start);
0428       memcpy(start + (ed->gap - ed->start), ed->rest, p - ed->rest);
0429       memcpy(rest, p, ed->end - p);
0430     }
0431 
0432     free(ed->start);
0433     ed->start = start;
0434     ed->gap = gap;
0435     ed->rest = rest;
0436     ed->end = end;
0437   }
0438 
0439 #ifdef DEBUG
0440   memset(ed->gap, 0, ed->rest - ed->gap);
0441 #endif
0442 }
0443 
0444 static void close_gap(struct editor *ed) {
0445   int len = text_length(ed);
0446   move_gap(ed, len, 1);
0447   ed->start[len] = 0;
0448 }
0449 
0450 static int get(struct editor *ed, int pos) {
0451   unsigned char *p = text_ptr(ed, pos);
0452   if (p >= ed->end) return -1;
0453   return *p;
0454 }
0455 
0456 static int compare(struct editor *ed, unsigned char *buf, int pos, int len) {
0457   unsigned char *bufptr = buf;
0458   unsigned char *p = ed->start + pos;
0459   if (p >= ed->gap) p += (ed->rest - ed->gap);
0460 
0461   while (len > 0) {
0462     if (p == ed->end) return 0;
0463     if (*bufptr++ != *p) return 0;
0464     len--;
0465     if (++p == ed->gap) p = ed->rest;
0466   }
0467 
0468   return 1;
0469 }
0470 
0471 static int copy(struct editor *ed, unsigned char *buf, int pos, int len) {
0472   unsigned char *bufptr = buf;
0473   unsigned char *p = ed->start + pos;
0474   if (p >= ed->gap) p += (ed->rest - ed->gap);
0475 
0476   while (len > 0) {
0477     if (p == ed->end) break;
0478     *bufptr++ = *p;
0479     len--;
0480     if (++p == ed->gap) p = ed->rest;
0481   }
0482 
0483   return bufptr - buf;
0484 }
0485 
0486 static void replace(struct editor *ed, int pos, int len, unsigned char *buf, int bufsize, int doundo) {
0487   unsigned char *p;
0488   struct undo *undo;
0489 
0490   // Store undo information
0491   if (doundo) {
0492     reset_undo(ed);
0493     undo = ed->undotail;
0494     if (undo && len == 0 && bufsize == 1 && undo->erased == 0 && pos == undo->pos + undo->inserted) {
0495       // Insert character at end of current redo buffer
0496       undo->redobuf = realloc(undo->redobuf, undo->inserted + 1);
0497       undo->redobuf[undo->inserted] = *buf;
0498       undo->inserted++;
0499     } else if (undo && len == 1 && bufsize == 0 && undo->inserted == 0 && pos == undo->pos) {
0500       // Erase character at end of current undo buffer
0501       undo->undobuf = realloc(undo->undobuf, undo->erased + 1);
0502       undo->undobuf[undo->erased] = get(ed, pos);
0503       undo->erased++;
0504     } else if (undo && len == 1 && bufsize == 0 && undo->inserted == 0 && pos == undo->pos - 1) {
0505       // Erase character at beginning of current undo buffer
0506       undo->pos--;
0507       undo->undobuf = realloc(undo->undobuf, undo->erased + 1);
0508       memmove(undo->undobuf + 1, undo->undobuf, undo->erased);
0509       undo->undobuf[0] = get(ed, pos);
0510       undo->erased++;
0511     } else {
0512       // Create new undo buffer
0513       undo = (struct undo *) malloc(sizeof(struct undo));
0514       if (ed->undotail) ed->undotail->next = undo;
0515       undo->prev = ed->undotail;
0516       undo->next = NULL;
0517       ed->undotail = ed->undo = undo;
0518       if (!ed->undohead) ed->undohead = undo;
0519 
0520       undo->pos = pos;
0521       undo->erased = len;
0522       undo->inserted = bufsize;
0523       undo->undobuf = undo->redobuf = NULL;
0524       if (len > 0) {
0525         undo->undobuf = malloc(len);
0526         copy(ed, undo->undobuf, pos, len);
0527       }
0528       if (bufsize > 0) {
0529         undo->redobuf = malloc(bufsize);
0530         memcpy(undo->redobuf, buf, bufsize);
0531       }
0532     }
0533   }
0534 
0535   p = ed->start + pos;
0536   if (bufsize == 0 && p <= ed->gap && p + len >= ed->gap) {
0537     // Handle deletions at the edges of the gap
0538     ed->rest += len - (ed->gap - p);
0539     ed->gap = p;
0540   } else {
0541     // Move the gap
0542     move_gap(ed, pos + len, bufsize - len);
0543 
0544     // Replace contents
0545     memcpy(ed->start + pos, buf, bufsize);
0546     ed->gap = ed->start + pos + bufsize;
0547   }
0548 
0549   // Mark buffer as dirty
0550   ed->dirty = 1;
0551 }
0552 
0553 static void insert(struct editor *ed, int pos, unsigned char *buf, int bufsize) {
0554   replace(ed, pos, 0, buf, bufsize, 1);
0555 }
0556 
0557 static void erase(struct editor *ed, int pos, int len) {
0558   replace(ed, pos, len, NULL, 0, 1);
0559 }
0560 
0561 //
0562 // Navigation functions
0563 //
0564 
0565 static int line_length(struct editor *ed, int linepos) {
0566   int pos = linepos;
0567   while (1) {
0568     int ch = get(ed, pos);
0569     if (ch < 0 || ch == '\n' || ch == '\r') break;
0570     pos++;
0571   }
0572 
0573   return pos - linepos;
0574 }
0575 
0576 static int line_start(struct editor *ed, int pos) {
0577   while (1) {
0578     if (pos == 0) break;
0579     if (get(ed, pos - 1) == '\n') break;
0580     pos--;
0581   }
0582 
0583   return pos;
0584 }
0585 
0586 static int next_line(struct editor *ed, int pos) {
0587   while (1) {
0588     int ch = get(ed, pos);
0589     if (ch < 0) return -1;
0590     pos++;
0591     if (ch == '\n') return pos;
0592   }
0593 }
0594 
0595 static int prev_line(struct editor *ed, int pos) {
0596   if (pos == 0) return -1;
0597 
0598   while (pos > 0) {
0599     int ch = get(ed, --pos);
0600     if (ch == '\n') break;
0601   }
0602 
0603   while (pos > 0) {
0604     int ch = get(ed, --pos);
0605     if (ch == '\n') return pos + 1;
0606   }
0607 
0608   return 0;
0609 }
0610 
0611 static int column(struct editor *ed, int linepos, int col) {
0612   unsigned char *p = text_ptr(ed, linepos);
0613   int c = 0;
0614   while (col > 0) {
0615     if (p == ed->end) break;
0616     if (*p == '\t') {
0617       int spaces = TABSIZE - c % TABSIZE;
0618       c += spaces;
0619     } else {
0620       c++;
0621     }
0622     col--;
0623     if (++p == ed->gap) p = ed->rest;
0624   }
0625   return c;
0626 }
0627 
0628 static void moveto(struct editor *ed, int pos, int center) {
0629   int scroll = 0;
0630   for (;;) {
0631     int cur = ed->linepos + ed->col;
0632     if (pos < cur) {
0633       if (pos >= ed->linepos) {
0634         ed->col = pos - ed->linepos;
0635       } else {
0636         ed->col = 0;
0637         ed->linepos = prev_line(ed, ed->linepos);
0638         ed->line--;
0639 
0640         if (ed->topline > ed->line) {
0641           ed->toppos = ed->linepos;
0642           ed->topline--;
0643           ed->refresh = 1;
0644           scroll = 1;
0645         }
0646       }
0647     } else if (pos > cur) {
0648       int next = next_line(ed, ed->linepos);
0649       if (next == -1) {
0650         ed->col = line_length(ed, ed->linepos);
0651         break;
0652       } else if (pos < next) {
0653         ed->col = pos - ed->linepos;
0654       } else {
0655         ed->col = 0;
0656         ed->linepos = next;
0657         ed->line++;
0658 
0659         if (ed->line >= ed->topline + ed->env->lines) {
0660           ed->toppos = next_line(ed, ed->toppos);
0661           ed->topline++;
0662           ed->refresh = 1;
0663           scroll = 1;
0664         }
0665       }
0666     } else {
0667       break;
0668     }
0669   }
0670 
0671   if (scroll && center) {
0672     int tl = ed->line - ed->env->lines / 2;
0673     if (tl < 0) tl = 0;
0674     for (;;) {
0675       if (ed->topline > tl) {
0676         ed->toppos = prev_line(ed, ed->toppos);
0677         ed->topline--;
0678       } else if (ed->topline < tl) {
0679         ed->toppos = next_line(ed, ed->toppos);
0680         ed->topline++;
0681       } else {
0682         break;
0683       }
0684     }
0685   }
0686 }
0687 
0688 //
0689 // Text selection
0690 //
0691 
0692 static int get_selection(struct editor *ed, size_t *start, size_t *end) {
0693   if (ed->anchor == -1) {
0694     *start = *end = -1;
0695     return 0;
0696   } else {
0697     int pos = ed->linepos + ed->col;
0698     if (pos == ed->anchor) {
0699       *start = *end = -1;
0700       return 0;
0701     } else if (pos < ed->anchor) {
0702       *start = pos;
0703       *end = ed->anchor;
0704     } else {
0705       *start = ed->anchor;
0706       *end = pos;
0707     }
0708   }
0709   return 1;
0710 }
0711 
0712 static int get_selected_text(struct editor *ed, char *buffer, int size) {
0713   size_t selstart, selend, len;
0714 
0715   if (!get_selection(ed, &selstart, &selend)) return 0;
0716   len = selend - selstart;
0717   if (len >= size) return 0;
0718   copy(ed, (unsigned char*) buffer, selstart, len);
0719   buffer[len] = 0;
0720   return len;
0721 }
0722 
0723 static void update_selection(struct editor *ed, int select) {
0724   if (select) {
0725     if (ed->anchor == -1) ed->anchor = ed->linepos + ed->col;
0726     ed->refresh = 1;
0727   } else {
0728     if (ed->anchor != -1) ed->refresh = 1;
0729     ed->anchor = -1;
0730   }
0731 }
0732 
0733 static int erase_selection(struct editor *ed) {
0734   size_t selstart, selend;
0735 
0736   if (!get_selection(ed, &selstart, &selend)) return 0;
0737   moveto(ed, selstart, 0);
0738   erase(ed, selstart, selend - selstart);
0739   ed->anchor = -1;
0740   ed->refresh = 1;
0741   return 1;
0742 }
0743 
0744 static void select_all(struct editor *ed) {
0745   ed->anchor = 0;
0746   ed->refresh = 1;
0747   moveto(ed, text_length(ed), 0);
0748 }
0749 
0750 //
0751 // Screen functions
0752 //
0753 
0754 static void get_console_size(struct env *env) {
0755 #ifdef __linux__
0756   struct winsize ws;
0757   ioctl(0, TIOCGWINSZ, &ws);
0758   env->cols = ws.ws_col;
0759   env->lines = ws.ws_row - 1;
0760 #elif defined(__rtems__)
0761   char* e;
0762   env->lines = 25;
0763   env->cols = 80;
0764   e = getenv("LINES");
0765   if (e != NULL) {
0766     int lines = strtol(e, 0, 10);
0767     if (lines > 0) {
0768       env->lines = lines - 1;
0769     }
0770   }
0771   e = getenv("COLUMNS");
0772   if (e != NULL) {
0773     int cols = strtol(e, 0, 10);
0774     if (cols > 0) {
0775       env->cols = cols;
0776     }
0777   }
0778 #else
0779   struct term *term = gettib()->proc->term;
0780   env->cols = term->cols;
0781   env->lines = term->lines - 1;
0782 #endif
0783   env->linebuf = realloc(env->linebuf, env->cols + LINEBUF_EXTRA);
0784 }
0785 
0786 static void outch(char c) {
0787   putchar(c);
0788 }
0789 
0790 static void outbuf(unsigned char *buf, int len) {
0791   fwrite(buf, 1, len, stdout);
0792 }
0793 
0794 static void outstr(char *str) {
0795   fputs(str, stdout);
0796 }
0797 
0798 static void clear_screen(void) {
0799   outstr(CLRSCR);
0800 }
0801 
0802 static void gotoxy(int col, int line) {
0803   char buf[32];
0804 
0805   sprintf(buf, GOTOXY, line + 1, col + 1);
0806   outstr(buf);
0807 }
0808 
0809 //
0810 // Keyboard functions
0811 //
0812 
0813 static void get_modifier_keys(int *shift, int *ctrl) {
0814   *shift = *ctrl = 0;
0815 #ifdef __linux__
0816   if (linux_console) {
0817     char modifiers = 6;
0818     if (ioctl(0, TIOCLINUX, &modifiers) >= 0) {
0819       if (modifiers & 1) *shift = 1;
0820       if (modifiers & 4) *ctrl = 1;
0821     }
0822   }
0823 #endif
0824 }
0825 
0826 static int getachar(void)
0827 {
0828   int ch = getchar();
0829   return ch;
0830 }
0831 
0832 static int getkey(void) {
0833   int ch, shift, ctrl;
0834 
0835   ch = getachar();
0836   if (ch < 0) return ch;
0837 
0838   switch (ch) {
0839     case 0x08: return KEY_BACKSPACE;
0840     case 0x09:
0841       get_modifier_keys(&shift, &ctrl);
0842       if (shift) return KEY_SHIFT_TAB;
0843       if (ctrl) return KEY_CTRL_TAB;
0844       return KEY_TAB;
0845 #ifdef SANOS
0846     case 0x0D: return gettib()->proc->term->type == TERM_CONSOLE ? KEY_ENTER : KEY_UNKNOWN;
0847     case 0x0A: return gettib()->proc->term->type != TERM_CONSOLE ? KEY_ENTER : KEY_UNKNOWN;
0848 #else
0849     case 0x0D: return KEY_ENTER;
0850     case 0x0A: return KEY_ENTER;
0851 #endif
0852     case 0x1B:
0853       ch = getachar();
0854       switch (ch) {
0855         case 0x1B: return KEY_ESC;
0856         case 0x4F:
0857           ch = getachar();
0858           switch (ch) {
0859             case 0x46: return KEY_END;
0860             case 0x48: return KEY_HOME;
0861             case 0x50: return KEY_F1;
0862             case 0x51: return KEY_F2;
0863             case 0x52: return KEY_F3;
0864             case 0x53: return KEY_F4;
0865             case 0x54: return KEY_F5;
0866             default: return KEY_UNKNOWN;
0867           }
0868           break;
0869 
0870         case 0x5B:
0871           get_modifier_keys(&shift, &ctrl);
0872           ch = getachar();
0873           if (ch == 0x31) {
0874             ch = getachar();
0875             switch (ch) {
0876               case 0x35:
0877                 return getachar() == 0x7E ? KEY_F5 : KEY_UNKNOWN;
0878               case 0x37:
0879                 return getachar() == 0x7E ? KEY_F6 : KEY_UNKNOWN;
0880               case 0x3B:
0881                 ch = getachar();
0882                 if (ch == 0x7E) return KEY_F7;
0883                 if (ch == 0x32) shift = 1;
0884                 if (ch == 0x35) ctrl = 1;
0885                 if (ch == 0x36) shift = ctrl = 1;
0886                 ch = getachar();
0887                 break;
0888               case 0x39:
0889                 return getachar() == 0x7E ? KEY_F8 : KEY_UNKNOWN;
0890               case 0x7E:
0891                 if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
0892                 if (shift) return KEY_SHIFT_HOME;
0893                 if (ctrl) return KEY_CTRL_HOME;
0894                 return KEY_HOME;
0895               default:
0896                 return KEY_UNKNOWN;
0897             }
0898           }
0899 
0900           switch (ch) {
0901             case 0x31:
0902               ch = getachar();
0903               if (ch != 0x7E) return KEY_UNKNOWN;
0904               if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
0905               if (shift) return KEY_SHIFT_HOME;
0906               if (ctrl) return KEY_CTRL_HOME;
0907               return KEY_HOME;
0908             case 0x32:
0909               ch = getachar();
0910               switch (ch) {
0911                 case 0x30: ch = getachar(); return KEY_F9;
0912                 case 0x31: ch = getachar(); return KEY_F10;
0913                 case 0x7E: return KEY_INS;
0914                 default: break;
0915               }
0916               return KEY_UNKNOWN;
0917             case 0x33: return getachar() == 0x7E ? KEY_DEL : KEY_UNKNOWN;
0918             case 0x34:
0919               if (getachar() != 0x7E) return KEY_UNKNOWN;
0920               if (shift && ctrl) return KEY_SHIFT_CTRL_END;
0921               if (shift) return KEY_SHIFT_END;
0922               if (ctrl) return KEY_CTRL_END;
0923               return KEY_END;
0924             case 0x35:
0925               if (getachar() != 0x7E) return KEY_UNKNOWN;
0926               if (shift) return KEY_SHIFT_PGUP;
0927               return KEY_PGUP;
0928             case 0x36:
0929               if (getachar() != 0x7E) return KEY_UNKNOWN;
0930               if (shift) return KEY_SHIFT_PGDN;
0931               return KEY_PGDN;
0932             case 0x41:
0933               if (shift && ctrl) return KEY_SHIFT_CTRL_UP;
0934               if (shift) return KEY_SHIFT_UP;
0935               if (ctrl) return KEY_CTRL_UP;
0936               return KEY_UP;
0937             case 0x42:
0938               if (shift && ctrl) return KEY_SHIFT_CTRL_DOWN;
0939               if (shift) return KEY_SHIFT_DOWN;
0940               if (ctrl) return KEY_CTRL_DOWN;
0941               return KEY_DOWN;
0942             case 0x43:
0943               if (shift && ctrl) return KEY_SHIFT_CTRL_RIGHT;
0944               if (shift) return KEY_SHIFT_RIGHT;
0945               if (ctrl) return KEY_CTRL_RIGHT;
0946               return KEY_RIGHT;
0947             case 0x44:
0948               if (shift && ctrl) return KEY_SHIFT_CTRL_LEFT;
0949               if (shift) return KEY_SHIFT_LEFT;
0950               if (ctrl) return KEY_CTRL_LEFT;
0951               return KEY_LEFT;
0952             case 0x46:
0953               if (shift && ctrl) return KEY_SHIFT_CTRL_END;
0954               if (shift) return KEY_SHIFT_END;
0955               if (ctrl) return KEY_CTRL_END;
0956               return KEY_END;
0957             case 0x48:
0958               if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
0959               if (shift) return KEY_SHIFT_HOME;
0960               if (ctrl) return KEY_CTRL_HOME;
0961               return KEY_HOME;
0962             case 0x5A:
0963               return KEY_SHIFT_TAB;
0964             case 0x5B:
0965               ch = getachar();
0966               switch (ch) {
0967                 case 0x41: return KEY_F1;
0968                 case 0x43: return KEY_F3;
0969                 case 0x45: return KEY_F5;
0970               }
0971               return KEY_UNKNOWN;
0972 
0973             default: return KEY_UNKNOWN;
0974           }
0975           break;
0976 
0977         default: return KEY_UNKNOWN;
0978       }
0979       break;
0980 
0981     case 0x00:
0982     case 0xE0:
0983       ch = getachar();
0984       switch (ch) {
0985         case 0x0F: return KEY_SHIFT_TAB;
0986         case 0x3B: return KEY_F1;
0987         case 0x3D: return KEY_F3;
0988         case 0x3F: return KEY_F5;
0989         case 0x47: return KEY_HOME;
0990         case 0x48: return KEY_UP;
0991         case 0x49: return KEY_PGUP;
0992         case 0x4B: return KEY_LEFT;
0993         case 0x4D: return KEY_RIGHT;
0994         case 0x4F: return KEY_END;
0995         case 0x50: return KEY_DOWN;
0996         case 0x51: return KEY_PGDN;
0997         case 0x52: return KEY_INS;
0998         case 0x53: return KEY_DEL;
0999         case 0x73: return KEY_CTRL_LEFT;
1000         case 0x74: return KEY_CTRL_RIGHT;
1001         case 0x75: return KEY_CTRL_END;
1002         case 0x77: return KEY_CTRL_HOME;
1003         case 0x8D: return KEY_CTRL_UP;
1004         case 0x91: return KEY_CTRL_DOWN;
1005         case 0x94: return KEY_CTRL_TAB;
1006         case 0xB8: return KEY_SHIFT_UP;
1007         case 0xB7: return KEY_SHIFT_HOME;
1008         case 0xBF: return KEY_SHIFT_END;
1009         case 0xB9: return KEY_SHIFT_PGUP;
1010         case 0xBB: return KEY_SHIFT_LEFT;
1011         case 0xBD: return KEY_SHIFT_RIGHT;
1012         case 0xC0: return KEY_SHIFT_DOWN;
1013         case 0xC1: return KEY_SHIFT_PGDN;
1014         case 0xDB: return KEY_SHIFT_CTRL_LEFT;
1015         case 0xDD: return KEY_SHIFT_CTRL_RIGHT;
1016         case 0xD8: return KEY_SHIFT_CTRL_UP;
1017         case 0xE0: return KEY_SHIFT_CTRL_DOWN;
1018         case 0xD7: return KEY_SHIFT_CTRL_HOME;
1019         case 0xDF: return KEY_SHIFT_CTRL_END;
1020 
1021         default: return KEY_UNKNOWN;
1022       }
1023       break;
1024 
1025     case 0x7F: return KEY_BACKSPACE;
1026 
1027     default: return ch;
1028   }
1029 }
1030 
1031 static int prompt(struct editor *ed, char *msg, int selection) {
1032   int maxlen, len, ch;
1033   char *buf = (char*) ed->env->linebuf;
1034 
1035   gotoxy(0, ed->env->lines);
1036   outstr(STATUS_COLOR);
1037   outstr(msg);
1038   outstr(CLREOL);
1039 
1040   len = 0;
1041   maxlen = ed->env->cols - strlen(msg) - 1;
1042   if (selection) {
1043     len = get_selected_text(ed, buf, maxlen);
1044     outbuf((unsigned char*) buf, len);
1045   }
1046 
1047   for (;;) {
1048     fflush(stdout);
1049     ch = getkey();
1050     if (ch == KEY_ESC) {
1051       return 0;
1052     } else if (ch == KEY_ENTER) {
1053       buf[len] = 0;
1054       return len > 0;
1055     } else if (ch == KEY_BACKSPACE) {
1056       if (len > 0) {
1057         outstr("\b \b");
1058         len--;
1059       }
1060     } else if (ch >= ' ' && ch < 0x100 && len < maxlen) {
1061       outch(ch);
1062       buf[len++] = ch;
1063     }
1064   }
1065 }
1066 
1067 static int ask(void) {
1068   int ch = getachar();
1069   return ch == 'y' || ch == 'Y';
1070 }
1071 
1072 //
1073 // Display functions
1074 //
1075 
1076 static void display_message(struct editor *ed, char *fmt, ...) {
1077   va_list args;
1078 
1079   va_start(args, fmt);
1080   gotoxy(0, ed->env->lines);
1081   outstr(STATUS_COLOR);
1082   vprintf(fmt, args);
1083   outstr(CLREOL TEXT_COLOR);
1084   fflush(stdout);
1085   va_end(args);
1086 }
1087 
1088 static void draw_full_statusline(struct editor *ed) {
1089   struct env *env = ed->env;
1090   int namewidth = env->cols - 29;
1091   gotoxy(0, env->lines);
1092   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);
1093   outstr((char*) env->linebuf);
1094 }
1095 
1096 static void draw_statusline(struct editor *ed) {
1097   gotoxy(ed->env->cols - 20, ed->env->lines);
1098   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);
1099   outstr((char*) ed->env->linebuf);
1100 }
1101 
1102 static void display_line(struct editor *ed, int pos, int fullline) {
1103   int hilite = 0;
1104   int col = 0;
1105   int margin = ed->margin;
1106   int maxcol = ed->env->cols + margin;
1107   unsigned char *bufptr = ed->env->linebuf;
1108   unsigned char *p = text_ptr(ed, pos);
1109   size_t selstart, selend, ch;
1110   char *s;
1111 
1112   (void) get_selection(ed, &selstart, &selend);
1113   while (col < maxcol) {
1114     if (margin == 0) {
1115       if (!hilite && pos >= selstart && pos < selend) {
1116         for (s = SELECT_COLOR; *s; s++) *bufptr++ = *s;
1117         hilite = 1;
1118       } else if (hilite && pos >= selend) {
1119         for (s = TEXT_COLOR; *s; s++) *bufptr++ = *s;
1120         hilite = 0;
1121       }
1122     }
1123 
1124     if (p == ed->end) break;
1125     ch = *p;
1126     if (ch == '\r' || ch == '\n') break;
1127 
1128     if (ch == '\t') {
1129       int spaces = TABSIZE - col % TABSIZE;
1130       while (spaces > 0 && col < maxcol) {
1131         if (margin > 0) {
1132           margin--;
1133         } else {
1134           *bufptr++ = ' ';
1135         }
1136         col++;
1137         spaces--;
1138       }
1139     } else {
1140       if (margin > 0) {
1141         margin--;
1142       } else {
1143         *bufptr++ = ch;
1144       }
1145       col++;
1146     }
1147 
1148     if (++p == ed->gap) p = ed->rest;
1149     pos++;
1150   }
1151 
1152 #if defined(__linux__)
1153   if (hilite) {
1154     while (col < maxcol) {
1155       *bufptr++ = ' ';
1156       col++;
1157     }
1158   } else {
1159     if (col == margin) *bufptr++ = ' ';
1160   }
1161 #endif
1162 
1163   if (col < maxcol) {
1164     for (s = CLREOL; *s; s++) *bufptr++ = *s;
1165     if (fullline) {
1166       memcpy(bufptr, "\r\n", 2);
1167       bufptr += 2;
1168     }
1169   }
1170 
1171   if (hilite) {
1172     for (s = TEXT_COLOR; *s; s++) *bufptr++ = *s;
1173   }
1174 
1175   outbuf(ed->env->linebuf, bufptr - ed->env->linebuf);
1176 }
1177 
1178 static void update_line(struct editor *ed) {
1179   gotoxy(0, ed->line - ed->topline);
1180   display_line(ed, ed->linepos, 0);
1181 }
1182 
1183 static void draw_screen(struct editor *ed) {
1184   int pos;
1185   int i;
1186 
1187   gotoxy(0, 0);
1188   outstr(TEXT_COLOR);
1189   pos = ed->toppos;
1190   for (i = 0; i < ed->env->lines; i++) {
1191     if (pos < 0) {
1192       outstr(CLREOL "\r\n");
1193     } else {
1194       display_line(ed, pos, 1);
1195       pos = next_line(ed, pos);
1196     }
1197   }
1198 }
1199 
1200 static void position_cursor(struct editor *ed) {
1201   int col = column(ed, ed->linepos, ed->col);
1202   gotoxy(col - ed->margin, ed->line - ed->topline);
1203 }
1204 
1205 //
1206 // Cursor movement
1207 //
1208 
1209 static void adjust(struct editor *ed) {
1210   int col;
1211   int ll = line_length(ed, ed->linepos);
1212   ed->col = ed->lastcol;
1213   if (ed->col > ll) ed->col = ll;
1214 
1215   col = column(ed, ed->linepos, ed->col);
1216   while (col < ed->margin) {
1217     ed->margin -= 4;
1218     if (ed->margin < 0) ed->margin = 0;
1219     ed->refresh = 1;
1220   }
1221 
1222   while (col - ed->margin >= ed->env->cols) {
1223     ed->margin += 4;
1224     ed->refresh = 1;
1225   }
1226 }
1227 
1228 static void select_toggle(struct editor *ed) {
1229   ed->selecting = ed->selecting ? 0 : 1;
1230   update_selection(ed, ed->selecting);
1231   adjust(ed);
1232 }
1233 
1234 static void up(struct editor *ed, int select) {
1235   int newpos;
1236 
1237   update_selection(ed, select);
1238 
1239   newpos = prev_line(ed, ed->linepos);
1240   if (newpos < 0) return;
1241 
1242   ed->linepos = newpos;
1243   ed->line--;
1244   if (ed->line < ed->topline) {
1245     ed->toppos = ed->linepos;
1246     ed->topline = ed->line;
1247     ed->refresh = 1;
1248   }
1249 
1250   adjust(ed);
1251 }
1252 
1253 static void down(struct editor *ed, int select) {
1254   int newpos;
1255 
1256   update_selection(ed, select);
1257 
1258   newpos = next_line(ed, ed->linepos);
1259   if (newpos < 0) return;
1260 
1261   ed->linepos = newpos;
1262   ed->line++;
1263 
1264   if (ed->line >= ed->topline + ed->env->lines) {
1265     ed->toppos = next_line(ed, ed->toppos);
1266     ed->topline++;
1267     ed->refresh = 1;
1268   }
1269 
1270   adjust(ed);
1271 }
1272 
1273 static void left(struct editor *ed, int select) {
1274   update_selection(ed, select);
1275   if (ed->col > 0) {
1276     ed->col--;
1277   } else {
1278     int newpos = prev_line(ed, ed->linepos);
1279     if (newpos < 0) return;
1280 
1281     ed->col = line_length(ed, newpos);
1282     ed->linepos = newpos;
1283     ed->line--;
1284     if (ed->line < ed->topline) {
1285       ed->toppos = ed->linepos;
1286       ed->topline = ed->line;
1287       ed->refresh = 1;
1288     }
1289   }
1290 
1291   ed->lastcol = ed->col;
1292   adjust(ed);
1293 }
1294 
1295 static void right(struct editor *ed, int select) {
1296   update_selection(ed, select);
1297   if (ed->col < line_length(ed, ed->linepos)) {
1298     ed->col++;
1299   } else {
1300     int newpos = next_line(ed, ed->linepos);
1301     if (newpos < 0) return;
1302 
1303     ed->col = 0;
1304     ed->linepos = newpos;
1305     ed->line++;
1306 
1307     if (ed->line >= ed->topline + ed->env->lines) {
1308       ed->toppos = next_line(ed, ed->toppos);
1309       ed->topline++;
1310       ed->refresh = 1;
1311     }
1312   }
1313 
1314   ed->lastcol = ed->col;
1315   adjust(ed);
1316 }
1317 
1318 static int wordchar(int ch) {
1319   return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');
1320 }
1321 
1322 static void wordleft(struct editor *ed, int select) {
1323   int pos, phase;
1324 
1325   update_selection(ed, select);
1326   pos = ed->linepos + ed->col;
1327   phase = 0;
1328   while (pos > 0) {
1329     int ch = get(ed, pos - 1);
1330     if (phase == 0) {
1331       if (wordchar(ch)) phase = 1;
1332     } else {
1333       if (!wordchar(ch)) break;
1334     }
1335 
1336     pos--;
1337     if (pos < ed->linepos) {
1338       ed->linepos = prev_line(ed, ed->linepos);
1339       ed->line--;
1340       ed->refresh = 1;
1341     }
1342   }
1343   ed->col = pos - ed->linepos;
1344   if (ed->line < ed->topline) {
1345     ed->toppos = ed->linepos;
1346     ed->topline = ed->line;
1347   }
1348 
1349   ed->lastcol = ed->col;
1350   adjust(ed);
1351 }
1352 
1353 static void wordright(struct editor *ed, int select) {
1354   int pos, end, phase, next;
1355 
1356   update_selection(ed, select);
1357   pos = ed->linepos + ed->col;
1358   end = text_length(ed);
1359   next = next_line(ed, ed->linepos);
1360   phase = 0;
1361   while (pos < end) {
1362     int ch = get(ed, pos);
1363     if (phase == 0) {
1364       if (wordchar(ch)) phase = 1;
1365     } else {
1366       if (!wordchar(ch)) break;
1367     }
1368 
1369     pos++;
1370     if (pos == next) {
1371       ed->linepos = next;
1372       next = next_line(ed, ed->linepos);
1373       ed->line++;
1374       ed->refresh = 1;
1375     }
1376   }
1377   ed->col = pos - ed->linepos;
1378   if (ed->line >= ed->topline + ed->env->lines) {
1379     ed->toppos = next_line(ed, ed->toppos);
1380     ed->topline++;
1381   }
1382 
1383   ed->lastcol = ed->col;
1384   adjust(ed);
1385 }
1386 
1387 static void home(struct editor *ed, int select) {
1388   update_selection(ed, select);
1389   ed->col = ed->lastcol = 0;
1390   adjust(ed);
1391 }
1392 
1393 static void end(struct editor *ed, int select) {
1394   update_selection(ed, select);
1395   ed->col = ed->lastcol = line_length(ed, ed->linepos);
1396   adjust(ed);
1397 }
1398 
1399 static void top(struct editor *ed, int select) {
1400   update_selection(ed, select);
1401   ed->toppos = ed->topline = ed->margin = 0;
1402   ed->linepos = ed->line = ed->col = ed->lastcol = 0;
1403   ed->refresh = 1;
1404 }
1405 
1406 static void bottom(struct editor *ed, int select) {
1407   update_selection(ed, select);
1408   for (;;) {
1409     int newpos = next_line(ed, ed->linepos);
1410     if (newpos < 0) break;
1411 
1412     ed->linepos = newpos;
1413     ed->line++;
1414 
1415     if (ed->line >= ed->topline + ed->env->lines) {
1416       ed->toppos = next_line(ed, ed->toppos);
1417       ed->topline++;
1418       ed->refresh = 1;
1419     }
1420   }
1421   ed->col = ed->lastcol = line_length(ed, ed->linepos);
1422   adjust(ed);
1423 }
1424 
1425 static void pageup(struct editor *ed, int select) {
1426   int i;
1427 
1428   update_selection(ed, select);
1429   if (ed->line < ed->env->lines) {
1430     ed->linepos = ed->toppos = 0;
1431     ed->line = ed->topline = 0;
1432   } else {
1433     for (i = 0; i < ed->env->lines; i++) {
1434       int newpos = prev_line(ed, ed->linepos);
1435       if (newpos < 0) return;
1436 
1437       ed->linepos = newpos;
1438       ed->line--;
1439 
1440       if (ed->topline > 0) {
1441         ed->toppos = prev_line(ed, ed->toppos);
1442         ed->topline--;
1443       }
1444     }
1445   }
1446 
1447   ed->refresh = 1;
1448   adjust(ed);
1449 }
1450 
1451 static void pagedown(struct editor *ed, int select) {
1452   int i;
1453 
1454   update_selection(ed, select);
1455   for (i = 0; i < ed->env->lines; i++) {
1456     int newpos = next_line(ed, ed->linepos);
1457     if (newpos < 0) break;
1458 
1459     ed->linepos = newpos;
1460     ed->line++;
1461 
1462     ed->toppos = next_line(ed, ed->toppos);
1463     ed->topline++;
1464   }
1465 
1466   ed->refresh = 1;
1467   adjust(ed);
1468 }
1469 
1470 //
1471 // Text editing
1472 //
1473 
1474 static void insert_char(struct editor *ed, unsigned char ch) {
1475   erase_selection(ed);
1476   insert(ed, ed->linepos + ed->col, &ch, 1);
1477   ed->col++;
1478   ed->lastcol = ed->col;
1479   adjust(ed);
1480   if (!ed->refresh) ed->lineupdate = 1;
1481 }
1482 
1483 static void newline(struct editor *ed) {
1484   int p;
1485   unsigned char ch;
1486 
1487   erase_selection(ed);
1488 #if defined(__linux__) || defined(__rtems__)
1489   insert(ed, ed->linepos + ed->col, (unsigned char*) "\n", 1);
1490 #else
1491   insert(ed, ed->linepos + ed->col, "\r\n", 2);
1492 #endif
1493   ed->col = ed->lastcol = 0;
1494   ed->line++;
1495   p = ed->linepos;
1496   ed->linepos = next_line(ed, ed->linepos);
1497   for (;;) {
1498     ch = get(ed, p++);
1499     if (ch == ' ' || ch == '\t') {
1500       insert(ed, ed->linepos + ed->col, &ch, 1);
1501       ed->col++;
1502     } else {
1503       break;
1504     }
1505   }
1506   ed->lastcol = ed->col;
1507 
1508   ed->refresh = 1;
1509 
1510   if (ed->line >= ed->topline + ed->env->lines) {
1511     ed->toppos = next_line(ed, ed->toppos);
1512     ed->topline++;
1513     ed->refresh = 1;
1514   }
1515 
1516   adjust(ed);
1517 }
1518 
1519 static void backspace(struct editor *ed) {
1520   if (erase_selection(ed)) return;
1521   if (ed->linepos + ed->col == 0) return;
1522   if (ed->col == 0) {
1523     int pos = ed->linepos;
1524     erase(ed, --pos, 1);
1525     if (get(ed, pos - 1) == '\r') erase(ed, --pos, 1);
1526 
1527     ed->line--;
1528     ed->linepos = line_start(ed, pos);
1529     ed->col = pos - ed->linepos;
1530     ed->refresh = 1;
1531 
1532     if (ed->line < ed->topline) {
1533       ed->toppos = ed->linepos;
1534       ed->topline = ed->line;
1535     }
1536   } else {
1537     ed->col--;
1538     erase(ed, ed->linepos + ed->col, 1);
1539     ed->lineupdate = 1;
1540   }
1541 
1542   ed->lastcol = ed->col;
1543   adjust(ed);
1544 }
1545 
1546 static void del(struct editor *ed) {
1547   int pos, ch;
1548 
1549   if (erase_selection(ed)) return;
1550   pos = ed->linepos + ed->col;
1551   ch = get(ed, pos);
1552   if (ch < 0) return;
1553 
1554   erase(ed, pos, 1);
1555   if (ch == '\r') {
1556     ch = get(ed, pos);
1557     if (ch == '\n') erase(ed, pos, 1);
1558   }
1559 
1560   if (ch == '\n') {
1561     ed->refresh = 1;
1562   } else {
1563     ed->lineupdate = 1;
1564   }
1565 }
1566 
1567 static void indent(struct editor *ed, unsigned char *indentation) {
1568   size_t start, end, i, lines, toplines, newline, ch;
1569   unsigned char *buffer, *p;
1570   size_t buflen;
1571   int width = strlen((const char*) indentation);
1572   int pos = ed->linepos + ed->col;
1573 
1574   if (!get_selection(ed, &start, &end)) {
1575     insert_char(ed, '\t');
1576     return;
1577   }
1578 
1579   lines = 0;
1580   toplines = 0;
1581   newline = 1;
1582   for (i = start; i < end; i++) {
1583     if (i == ed->toppos) toplines = lines;
1584     if (newline) {
1585       lines++;
1586       newline = 0;
1587     }
1588     if (get(ed, i) == '\n') newline = 1;
1589   }
1590   buflen = end - start + lines * width;
1591   buffer = malloc(buflen);
1592   if (!buffer) return;
1593 
1594   newline = 1;
1595   p = buffer;
1596   for (i = start; i < end; i++) {
1597     if (newline) {
1598       memcpy(p, indentation, width);
1599       p += width;
1600       newline = 0;
1601     }
1602     ch = get(ed, i);
1603     *p++ = ch;
1604     if (ch == '\n') newline = 1;
1605   }
1606 
1607   replace(ed, start, end - start, buffer, buflen, 1);
1608   free(buffer);
1609 
1610   if (ed->anchor < pos) {
1611     pos += width * lines;
1612   } else {
1613     ed->anchor += width * lines;
1614   }
1615 
1616   ed->toppos += width * toplines;
1617   ed->linepos = line_start(ed, pos);
1618   ed->col = ed->lastcol = pos - ed->linepos;
1619 
1620   adjust(ed);
1621   ed->refresh = 1;
1622 }
1623 
1624 static void unindent(struct editor *ed, unsigned char *indentation) {
1625   size_t start, end, i, newline, ch, shrinkage, topofs;
1626   unsigned char *buffer, *p;
1627   int width = strlen((const char*) indentation);
1628   int pos = ed->linepos + ed->col;
1629 
1630   if (!get_selection(ed, &start, &end)) return;
1631 
1632   buffer = malloc(end - start);
1633   if (!buffer) return;
1634 
1635   newline = 1;
1636   p = buffer;
1637   i = start;
1638   shrinkage = 0;
1639   topofs = 0;
1640   while (i < end) {
1641     if (newline) {
1642       newline = 0;
1643       if (compare(ed, indentation, i, width)) {
1644         i += width;
1645         shrinkage += width;
1646         if (i < ed->toppos) topofs -= width;
1647         continue;
1648       }
1649     }
1650     ch = get(ed, i++);
1651     *p++ = ch;
1652     if (ch == '\n') newline = 1;
1653   }
1654 
1655   if (!shrinkage) {
1656     free(buffer);
1657     return;
1658   }
1659 
1660   replace(ed, start, end - start, buffer, p - buffer, 1);
1661   free(buffer);
1662 
1663   if (ed->anchor < pos) {
1664     pos -= shrinkage;
1665   } else {
1666     ed->anchor -= shrinkage;
1667   }
1668 
1669   ed->toppos += topofs;
1670   ed->linepos = line_start(ed, pos);
1671   ed->col = ed->lastcol = pos - ed->linepos;
1672 
1673   ed->refresh = 1;
1674   adjust(ed);
1675 }
1676 
1677 static void undo(struct editor *ed) {
1678   if (!ed->undo) return;
1679   moveto(ed, ed->undo->pos, 0);
1680   replace(ed, ed->undo->pos, ed->undo->inserted, ed->undo->undobuf, ed->undo->erased, 0);
1681   ed->undo = ed->undo->prev;
1682   if (!ed->undo) ed->dirty = 0;
1683   ed->anchor = -1;
1684   ed->lastcol = ed->col;
1685   ed->refresh = 1;
1686 }
1687 
1688 static void redo(struct editor *ed) {
1689   if (ed->undo) {
1690     if (!ed->undo->next) return;
1691     ed->undo = ed->undo->next;
1692   } else {
1693     if (!ed->undohead) return;
1694     ed->undo = ed->undohead;
1695   }
1696   replace(ed, ed->undo->pos, ed->undo->erased, ed->undo->redobuf, ed->undo->inserted, 0);
1697   moveto(ed, ed->undo->pos, 0);
1698   ed->dirty = 1;
1699   ed->anchor = -1;
1700   ed->lastcol = ed->col;
1701   ed->refresh = 1;
1702 }
1703 
1704 //
1705 // Clipboard
1706 //
1707 
1708 static void copy_selection(struct editor *ed) {
1709   size_t selstart, selend;
1710 
1711   if (!get_selection(ed, &selstart, &selend)) return;
1712   ed->env->clipsize = selend - selstart;
1713   ed->env->clipboard = (unsigned char *) realloc(ed->env->clipboard, ed->env->clipsize);
1714   if (!ed->env->clipboard) return;
1715   copy(ed, ed->env->clipboard, selstart, ed->env->clipsize);
1716 }
1717 
1718 static void cut_selection(struct editor *ed) {
1719   copy_selection(ed);
1720   erase_selection(ed);
1721   select_toggle(ed);
1722 }
1723 
1724 static void paste_selection(struct editor *ed) {
1725   erase_selection(ed);
1726   insert(ed, ed->linepos + ed->col, ed->env->clipboard, ed->env->clipsize);
1727   moveto(ed, ed->linepos + ed->col + ed->env->clipsize, 0);
1728   ed->refresh = 1;
1729 }
1730 
1731 //
1732 // Editor Commands
1733 //
1734 
1735 static void open_editor(struct editor *ed) {
1736   int rc;
1737   char *filename;
1738   struct env *env = ed->env;
1739 
1740   if (!prompt(ed, "Open file: ", 1)) {
1741     ed->refresh = 1;
1742     return;
1743   }
1744   filename = (char*) ed->env->linebuf;
1745 
1746   ed = find_editor(ed->env, filename);
1747   if (ed) {
1748     env->current = ed;
1749   } else {
1750     ed = create_editor(env);
1751     rc = load_file(ed, filename);
1752     if (rc < 0) {
1753       display_message(ed, "Error %d opening %s (%s)", errno, filename, strerror(errno));
1754       sleep(5);
1755       delete_editor(ed);
1756       ed = env->current;
1757     }
1758   }
1759   ed->refresh = 1;
1760 }
1761 
1762 static void new_editor(struct editor *ed) {
1763   ed = create_editor(ed->env);
1764   new_file(ed, "");
1765   ed->refresh = 1;
1766 }
1767 
1768 static void read_from_stdin(struct editor *ed) {
1769   char buffer[512];
1770   int n, pos;
1771 
1772   pos = 0;
1773   while ((n = fread(buffer, 1, sizeof(buffer), stdin)) > 0) {
1774     insert(ed, pos, (unsigned char*) buffer, n);
1775     pos += n;
1776   }
1777   strncpy(ed->filename, "<stdin>", FILENAME_MAX);
1778   ed->newfile = 1;
1779   ed->dirty = 0;
1780 }
1781 
1782 static void save_editor(struct editor *ed) {
1783   int rc;
1784 
1785   if (!ed->dirty && !ed->newfile) return;
1786 
1787   if (ed->newfile) {
1788     if (!prompt(ed, "Save as: ", 1)) {
1789       ed->refresh = 1;
1790       return;
1791     }
1792 
1793     if (access((const char*) ed->env->linebuf, F_OK) == 0) {
1794       display_message(ed, "Overwrite %s (y/n)? ", ed->env->linebuf);
1795       if (!ask()) {
1796         ed->refresh = 1;
1797         return;
1798       }
1799     }
1800     strlcpy(
1801       ed->filename, (const char*) ed->env->linebuf, sizeof(ed->filename));
1802     ed->newfile = 0;
1803   }
1804 
1805   rc = save_file(ed);
1806   if (rc < 0) {
1807     display_message(ed, "Error %d saving document (%s)", errno, strerror(errno));
1808     sleep(5);
1809   }
1810 
1811   ed->refresh = 1;
1812 }
1813 
1814 static struct editor* close_editor(struct editor *ed) {
1815   struct env *env = ed->env;
1816 
1817   if (ed->dirty) {
1818     display_message(ed, "Close %s without saving changes (y/n)? ", ed->filename);
1819     if (!ask()) {
1820       ed->refresh = 1;
1821       return ed;
1822     }
1823   }
1824 
1825   delete_editor(ed);
1826 
1827   ed = env->current;
1828   if (!ed) {
1829     ed = create_editor(env);
1830     new_file(ed, "");
1831   }
1832   ed->refresh = 1;
1833   return ed;
1834 }
1835 
1836 static void pipe_command(struct editor *ed) {
1837 #ifdef __rtems__
1838     display_message(ed, "Not supported");
1839     sleep(3);
1840 #else
1841   FILE *f;
1842   char buffer[512];
1843   int n;
1844   int pos;
1845 
1846   if (!prompt(ed, "Command: ", 1)) {
1847     ed->refresh = 1;
1848     return;
1849   }
1850 
1851 #ifdef SANOS
1852   f = popen(ed->env->linebuf, "r2");
1853 #else
1854   f = popen(ed->env->linebuf, "r");
1855 #endif
1856   if (!f) {
1857     display_message(ed, "Error %d running command (%s)", errno, strerror(errno));
1858     sleep(5);
1859   } else {
1860     erase_selection(ed);
1861     pos = ed->linepos + ed->col;
1862     while ((n = fread(buffer, 1, sizeof(buffer), f)) > 0) {
1863       insert(ed, pos, buffer, n);
1864       pos += n;
1865     }
1866     moveto(ed, pos, 0);
1867     pclose(f);
1868   }
1869   ed->refresh = 1;
1870 #endif
1871 }
1872 
1873 static void find_text(struct editor *ed, int next) {
1874   int slen;
1875 
1876   if (!next) {
1877     if (!prompt(ed, "Find: ", 1)) {
1878       ed->refresh = 1;
1879       return;
1880     }
1881     if (ed->env->search) free(ed->env->search);
1882     ed->env->search = (unsigned char*) strdup((const char*) ed->env->linebuf);
1883   }
1884 
1885   if (!ed->env->search) return;
1886   slen = strlen((const char*) ed->env->search);
1887   if (slen > 0) {
1888     unsigned char *match;
1889 
1890     close_gap(ed);
1891     match = (unsigned char*) strstr((char*) ed->start + ed->linepos + ed->col, (char*) ed->env->search);
1892     if (match != NULL) {
1893       int pos = match - ed->start;
1894       ed->anchor = pos;
1895       moveto(ed, pos + slen, 1);
1896     } else {
1897       outch('\007');
1898     }
1899   }
1900   ed->refresh = 1;
1901 }
1902 
1903 static void goto_line(struct editor *ed) {
1904   int lineno, l, pos;
1905 
1906   ed->anchor = -1;
1907   if (prompt(ed, "Goto line: ", 1)) {
1908     lineno = atoi((char*) ed->env->linebuf);
1909     if (lineno > 0) {
1910       pos = 0;
1911       for (l = 0; l < lineno - 1; l++) {
1912         pos = next_line(ed, pos);
1913         if (pos < 0) break;
1914       }
1915     } else {
1916       pos = -1;
1917     }
1918 
1919     if (pos >= 0) {
1920       moveto(ed, pos, 1);
1921     } else {
1922       outch('\007');
1923     }
1924   }
1925   ed->refresh = 1;
1926 }
1927 
1928 static struct editor *next_file(struct editor *ed) {
1929   ed = ed->env->current = ed->next;
1930   ed->refresh = 1;
1931   return ed;
1932 }
1933 
1934 static void jump_to_editor(struct editor *ed) {
1935   struct env *env = ed->env;
1936   char filename[FILENAME_MAX];
1937   int lineno = 0;
1938 
1939   if (!get_selected_text(ed, filename, FILENAME_MAX)) {
1940     int pos = ed->linepos + ed->col;
1941     char *p = filename;
1942     int left = FILENAME_MAX - 1;
1943     while (left > 0) {
1944       int ch = get(ed, pos);
1945       if (ch < 0) break;
1946       if (strchr("!@\"'#%&()[]{}*?+:;\r\n\t ", ch)) break;
1947       *p++ = ch;
1948       left--;
1949       pos++;
1950     }
1951     *p = 0;
1952 
1953     if (get(ed, pos) == ':') {
1954       pos++;
1955       for (;;) {
1956         int ch = get(ed, pos);
1957         if (ch < 0) break;
1958         if (ch >= '0' && ch <= '9') {
1959           lineno = lineno * 10 + (ch - '0');
1960         } else {
1961           break;
1962         }
1963         pos++;
1964       }
1965     }
1966   }
1967   if (!*filename) return;
1968 
1969   ed = find_editor(env, filename);
1970   if (ed) {
1971     env->current = ed;
1972   } else {
1973     ed = create_editor(env);
1974     if (load_file(ed, filename) < 0) {
1975       outch('\007');
1976       delete_editor(ed);
1977       ed = env->current;
1978     }
1979   }
1980 
1981   if (lineno > 0) {
1982     int pos = 0;
1983     while (--lineno > 0) {
1984       pos = next_line(ed, pos);
1985       if (pos < 0) break;
1986     }
1987     if (pos >= 0) moveto(ed, pos, 1);
1988   }
1989 
1990   ed->refresh = 1;
1991 }
1992 
1993 static void redraw_screen(struct editor *ed) {
1994   get_console_size(ed->env);
1995   draw_screen(ed);
1996 }
1997 
1998 static int quit(struct env *env) {
1999   struct editor *ed = env->current;
2000   struct editor *start = ed;
2001 
2002   do {
2003     if (ed->dirty) {
2004       display_message(ed, "Close %s without saving changes (y/n)? ", ed->filename);
2005       if (!ask()) return 0;
2006     }
2007     ed = ed->next;
2008   } while (ed != start);
2009 
2010   return 1;
2011 }
2012 
2013 static void help(struct editor *ed) {
2014   gotoxy(0, 0);
2015   clear_screen();
2016   outstr("Editor Command Summary\r\n");
2017   outstr("======================\r\n\r\n");
2018   outstr("<up>         Move one line up (*)         Ctrl+N  New editor\r\n");
2019   outstr("<down>       Move one line down (*)       Ctrl+O  Open file\r\n");
2020   outstr("<left>       Move one character left (*)  Ctrl+S  Save file\r\n");
2021   outstr("<right>      Move one character right (*) Ctrl+W  Close file\r\n");
2022   outstr("<pgup>       Move one page up (*)         Ctrl+Q  Quit\r\n");
2023   outstr("<pgdn>       Move one page down (*)       Ctrl+P  Pipe command\r\n");
2024   outstr("Ctrl+<left>  Move to previous word (*)    Ctrl+A  Select all\r\n");
2025   outstr("Ctrl+<right> Move to next word (*)        Ctrl+C  Copy selection to clipboard\r\n");
2026   outstr("<home>       Move to start of line (*)    Ctrl+X  Cut selection to clipboard\r\n");
2027   outstr("<end>        Move to end of line (*)      Ctrl+V  Paste from clipboard\r\n");
2028   outstr("Ctrl+<home>  Move to start of file (*)    Ctrl+Z  Undo\r\n");
2029   outstr("Ctrl+<end>   Move to end of file (*)      Ctrl+R  Redo\r\n");
2030   outstr("<backspace>  Delete previous character    Ctrl+F  Find text\r\n");
2031   outstr("<delete>     Delete current character     Ctrl+G  Find next\r\n");
2032   outstr("Ctrl+<tab>   Next editor                  Ctrl+L  Goto line\r\n");
2033   outstr("<tab>        Indent selection             F1      Help\r\n");
2034   outstr("Shift+<tab>  Unindent selection           F2      Select toggle\r\n");
2035   outstr(" (*) Extends selection, F2 toggles.       F3      Navigate to file\r\n");
2036   outstr("                                          F4      Copy selection to clipboard\r\n");
2037   outstr("  Ctrl-Q/S may not work over              F5      Redraw screen\r\n");
2038   outstr("  serial links, use funcions keys         F9      Save file\r\n");
2039   outstr("                                          F10     Quit\r\n");
2040   outstr("Press any key to continue...");
2041   fflush(stdout);
2042 
2043   getkey();
2044   draw_screen(ed);
2045   draw_full_statusline(ed);
2046 }
2047 
2048 //
2049 // Editor
2050 //
2051 
2052 static void edit(struct editor *ed) {
2053   int done = 0;
2054   int key;
2055 
2056   ed->refresh = 1;
2057   while (!done) {
2058     if (ed->refresh) {
2059       draw_screen(ed);
2060       draw_full_statusline(ed);
2061       ed->refresh = 0;
2062       ed->lineupdate = 0;
2063     } else if (ed->lineupdate) {
2064       update_line(ed);
2065       ed->lineupdate = 0;
2066       draw_statusline(ed);
2067     } else {
2068       draw_statusline(ed);
2069     }
2070 
2071     position_cursor(ed);
2072     fflush(stdout);
2073     key = getkey();
2074 
2075     if (key >= ' ' && key <= 0x7F) {
2076 #ifdef LESS
2077       switch (key) {
2078         case 'q': done = 1; break;
2079         case '/': find_text(ed, 0); break;
2080       }
2081 #else
2082       insert_char(ed, (unsigned char) key);
2083 #endif
2084     } else {
2085       switch (key) {
2086         case KEY_F1: help(ed); break;
2087         case KEY_F2: select_toggle(ed); break;
2088         case KEY_F3: jump_to_editor(ed); ed = ed->env->current; break;
2089         case KEY_F4: copy_selection(ed); break;
2090         case KEY_F5: redraw_screen(ed); break;
2091         case KEY_F9: save_editor(ed); break;
2092         case KEY_F10: done = 1; break;
2093 
2094 #if defined(__linux__) || defined(__rtems__)
2095         case ctrl('y'): help(ed); break;
2096         case ctrl('t'): top(ed, 0); break;
2097         case ctrl('b'): bottom(ed, 0); break;
2098 #endif
2099 
2100         case KEY_UP: up(ed, ed->selecting); break;
2101         case KEY_DOWN: down(ed, ed->selecting); break;
2102         case KEY_LEFT: left(ed, ed->selecting); break;
2103         case KEY_RIGHT: right(ed, ed->selecting); break;
2104         case KEY_HOME: home(ed, ed->selecting); break;
2105         case KEY_END: end(ed, ed->selecting); break;
2106         case KEY_PGUP: pageup(ed, ed->selecting); break;
2107         case KEY_PGDN: pagedown(ed, ed->selecting); break;
2108 
2109         case KEY_CTRL_RIGHT: wordright(ed, ed->selecting); break;
2110         case KEY_CTRL_LEFT: wordleft(ed, ed->selecting); break;
2111         case KEY_CTRL_HOME: top(ed, ed->selecting); break;
2112         case KEY_CTRL_END: bottom(ed, ed->selecting); break;
2113 
2114 #if SHIFT_SELECT
2115         case KEY_SHIFT_UP: up(ed, 1); break;
2116         case KEY_SHIFT_DOWN: down(ed, 1); break;
2117         case KEY_SHIFT_LEFT: left(ed, 1); break;
2118         case KEY_SHIFT_RIGHT: right(ed, 1); break;
2119         case KEY_SHIFT_PGUP: pageup(ed, 1); break;
2120         case KEY_SHIFT_PGDN: pagedown(ed, 1); break;
2121         case KEY_SHIFT_HOME: home(ed, 1); break;
2122         case KEY_SHIFT_END: end(ed, 1); break;
2123 
2124         case KEY_SHIFT_CTRL_RIGHT: wordright(ed, 1); break;
2125         case KEY_SHIFT_CTRL_LEFT: wordleft(ed, 1); break;
2126         case KEY_SHIFT_CTRL_HOME: top(ed, 1); break;
2127         case KEY_SHIFT_CTRL_END: bottom(ed, 1); break;
2128 #endif
2129 
2130         case KEY_CTRL_TAB: ed = next_file(ed); break;
2131 
2132         case ctrl('e'): select_toggle(ed); break;
2133         case ctrl('a'): select_all(ed); break;
2134         case ctrl('c'): copy_selection(ed);select_toggle(ed); break;
2135         case ctrl('f'): find_text(ed, 0); break;
2136         case ctrl('l'): goto_line(ed); break;
2137         case ctrl('g'): find_text(ed, 1); break;
2138         case ctrl('q'): done = 1; break;
2139 #ifdef LESS
2140         case KEY_ESC: done = 1; break;
2141 #else
2142         case KEY_TAB: indent(ed, (unsigned char*) INDENT); break;
2143         case KEY_SHIFT_TAB: unindent(ed, (unsigned char*) INDENT); break;
2144 
2145         case KEY_ENTER: newline(ed); break;
2146         case KEY_BACKSPACE: backspace(ed); break;
2147         case KEY_DEL: del(ed); break;
2148         case ctrl('x'): cut_selection(ed); break;
2149         case ctrl('z'): undo(ed); break;
2150         case ctrl('r'): redo(ed); break;
2151         case ctrl('v'): paste_selection(ed); break;
2152         case ctrl('o'): open_editor(ed); ed = ed->env->current; break;
2153         case ctrl('n'): new_editor(ed); ed = ed->env->current; break;
2154         case ctrl('s'): save_editor(ed); break;
2155         case ctrl('p'): pipe_command(ed); break;
2156 #endif
2157         case ctrl('w'): ed = close_editor(ed); break;
2158       }
2159     }
2160   }
2161 }
2162 
2163 //
2164 // main
2165 //
2166 static int rtems_shell_main_edit(int argc, char *argv[])
2167 {
2168   struct env env;
2169   int rc;
2170   int i;
2171   sigset_t blocked_sigmask, orig_sigmask;
2172 #if defined(__linux__)
2173   struct termios tio;
2174 #endif
2175 #if defined(__linux__) || defined(__rtems__)
2176   struct termios orig_tio;
2177 #endif
2178 #ifdef SANOS
2179   struct term *term;
2180 #endif
2181 
2182   memset(&env, 0, sizeof(env));
2183   for (i = 1; i < argc; i++) {
2184     struct editor *ed = create_editor(&env);
2185     rc = load_file(ed, argv[i]);
2186     if (rc < 0 && errno == ENOENT) rc = new_file(ed, argv[i]);
2187     if (rc < 0) {
2188       perror(argv[i]);
2189       return 0;
2190     }
2191   }
2192   if (env.current == NULL) {
2193     struct editor *ed = create_editor(&env);
2194     if (isatty(fileno(stdin))) {
2195       new_file(ed, "");
2196     } else {
2197       read_from_stdin(ed);
2198     }
2199   }
2200   env.current = env.current->next;
2201 
2202 #ifdef SANOS
2203   term = gettib()->proc->term;
2204   if (fdin != term->ttyin) dup2(term->ttyin, fdin);
2205   if (fdout != term->ttyout) dup2(term->ttyout, fdout);
2206 #elif !defined(__rtems__)
2207   if (!isatty(fileno(stdin))) {
2208     if (!freopen("/dev/tty", "r", stdin)) perror("/dev/tty");
2209   }
2210 #endif
2211 
2212   setvbuf(stdout, NULL, 0, 8192);
2213 
2214 #if defined(__linux__) || defined(__rtems__)
2215   (void) tcgetattr(0, &orig_tio);
2216 #if !defined(__rtems__)
2217   cfmakeraw(&tio);
2218   tcsetattr(0, TCSANOW, &tio);
2219 #endif
2220   if (getenv("TERM") && strcmp(getenv("TERM"), "linux") == 0) {
2221     linux_console = 1;
2222   } else {
2223     outstr(CLRSCR);
2224     outstr("\033[3 q");  // xterm
2225     outstr("\033]50;CursorShape=2\a");  // KDE
2226   }
2227 #endif
2228 
2229   get_console_size(&env);
2230 
2231   sigemptyset(&blocked_sigmask);
2232   sigaddset(&blocked_sigmask, SIGINT);
2233   sigaddset(&blocked_sigmask, SIGTSTP);
2234   sigaddset(&blocked_sigmask, SIGABRT);
2235   sigprocmask(SIG_BLOCK, &blocked_sigmask, &orig_sigmask);
2236 
2237   for (;;) {
2238     if (!env.current) break;
2239     edit(env.current);
2240     if (quit(&env)) break;
2241   }
2242 
2243   gotoxy(0, env.lines + 1);
2244   outstr(RESET_COLOR CLREOL);
2245 #if defined(__linux__) || defined(__rtems__)
2246   tcsetattr(0, TCSANOW, &orig_tio);
2247 #endif
2248 
2249   while (env.current) delete_editor(env.current);
2250 
2251   if (env.clipboard) free(env.clipboard);
2252   if (env.search) free(env.search);
2253   if (env.linebuf) free(env.linebuf);
2254 
2255   setbuf(stdout, NULL);
2256   sigprocmask(SIG_SETMASK, &orig_sigmask, NULL);
2257 
2258   return 0;
2259 }
2260 
2261 rtems_shell_cmd_t rtems_shell_EDIT_Command = {
2262   "edit",                /* name */
2263   "edit [file ...]",     /* usage */
2264   "files",               /* topic */
2265   rtems_shell_main_edit, /* command */
2266   NULL,                  /* alias */
2267   NULL                   /* next */
2268 };