forked from Imagelibrary/rtems
2253 lines
56 KiB
C
2253 lines
56 KiB
C
//
|
|
// edit.c
|
|
//
|
|
// Text editor
|
|
//
|
|
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions
|
|
// are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// 2. Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// 3. Neither the name of the project nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software
|
|
// without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
// SUCH DAMAGE.
|
|
//
|
|
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef SANOS
|
|
#include <os.h>
|
|
#endif
|
|
|
|
#ifdef __rtems__
|
|
#include <assert.h>
|
|
#include <rtems.h>
|
|
#include <rtems/shell.h>
|
|
#endif
|
|
|
|
#if defined(__linux__) || defined(__rtems__)
|
|
#include <sys/ioctl.h>
|
|
#include <termios.h>
|
|
#define O_BINARY 0
|
|
static int linux_console;
|
|
#endif
|
|
|
|
#define MINEXTEND 32768
|
|
#define LINEBUF_EXTRA 32
|
|
|
|
#ifndef TABSIZE
|
|
#define TABSIZE 8
|
|
#endif
|
|
|
|
#ifndef INDENT
|
|
#define INDENT " "
|
|
#endif
|
|
|
|
#define CLRSCR "\033[0J\x1b[H\x1b[J"
|
|
#define CLREOL "\033[K"
|
|
#define GOTOXY "\033[%d;%dH"
|
|
#define RESET_COLOR "\033[0m"
|
|
|
|
#ifdef COLOR
|
|
#define TEXT_COLOR "\033[44m\033[37m\033[1m"
|
|
#define SELECT_COLOR "\033[47m\033[37m\033[1m"
|
|
#define STATUS_COLOR "\033[0m\033[47m\033[30m"
|
|
#else
|
|
#define TEXT_COLOR "\033[0m"
|
|
#define SELECT_COLOR "\033[7m\033[1m"
|
|
#define STATUS_COLOR "\033[1m\033[7m"
|
|
#endif
|
|
|
|
//
|
|
// Key codes
|
|
//
|
|
|
|
#define KEY_BACKSPACE 0x101
|
|
#define KEY_ESC 0x102
|
|
#define KEY_INS 0x103
|
|
#define KEY_DEL 0x104
|
|
#define KEY_LEFT 0x105
|
|
#define KEY_RIGHT 0x106
|
|
#define KEY_UP 0x107
|
|
#define KEY_DOWN 0x108
|
|
#define KEY_HOME 0x109
|
|
#define KEY_END 0x10A
|
|
#define KEY_ENTER 0x10B
|
|
#define KEY_TAB 0x10C
|
|
#define KEY_PGUP 0x10D
|
|
#define KEY_PGDN 0x10E
|
|
|
|
#define KEY_CTRL_LEFT 0x10F
|
|
#define KEY_CTRL_RIGHT 0x110
|
|
#define KEY_CTRL_UP 0x111
|
|
#define KEY_CTRL_DOWN 0x112
|
|
#define KEY_CTRL_HOME 0x113
|
|
#define KEY_CTRL_END 0x114
|
|
#define KEY_CTRL_TAB 0x115
|
|
|
|
#define KEY_SHIFT_LEFT 0x116
|
|
#define KEY_SHIFT_RIGHT 0x117
|
|
#define KEY_SHIFT_UP 0x118
|
|
#define KEY_SHIFT_DOWN 0x119
|
|
#define KEY_SHIFT_PGUP 0x11A
|
|
#define KEY_SHIFT_PGDN 0x11B
|
|
#define KEY_SHIFT_HOME 0x11C
|
|
#define KEY_SHIFT_END 0x11D
|
|
#define KEY_SHIFT_TAB 0x11E
|
|
|
|
#define KEY_SHIFT_CTRL_LEFT 0x11F
|
|
#define KEY_SHIFT_CTRL_RIGHT 0x120
|
|
#define KEY_SHIFT_CTRL_UP 0x121
|
|
#define KEY_SHIFT_CTRL_DOWN 0x122
|
|
#define KEY_SHIFT_CTRL_HOME 0x123
|
|
#define KEY_SHIFT_CTRL_END 0x124
|
|
|
|
#define KEY_F1 0x125
|
|
#define KEY_F2 0x126
|
|
#define KEY_F3 0x127
|
|
#define KEY_F4 0x128
|
|
#define KEY_F5 0x129
|
|
#define KEY_F6 0x12a
|
|
#define KEY_F7 0x12b
|
|
#define KEY_F8 0x12c
|
|
#define KEY_F9 0x12d
|
|
#define KEY_F10 0x12e
|
|
|
|
#define KEY_UNKNOWN 0xFFF
|
|
|
|
#define ctrl(c) ((c) - 0x60)
|
|
|
|
//
|
|
// Editor data block
|
|
//
|
|
// Structure of split buffer:
|
|
//
|
|
// +------------------+------------------+------------------+
|
|
// | text before gap | gap | text after gap |
|
|
// +------------------+------------------+------------------+
|
|
// ^ ^ ^ ^
|
|
// | | | |
|
|
// start gap rest end
|
|
//
|
|
|
|
struct env;
|
|
|
|
struct undo {
|
|
int pos; // Editor position
|
|
int erased; // Size of erased contents
|
|
int inserted; // Size of inserted contents
|
|
unsigned char *undobuf; // Erased contents for undo
|
|
unsigned char *redobuf; // Inserted contents for redo
|
|
struct undo *next; // Next undo buffer
|
|
struct undo *prev; // Previous undo buffer
|
|
};
|
|
|
|
struct editor {
|
|
unsigned char *start; // Start of text buffer
|
|
unsigned char *gap; // Start of gap
|
|
unsigned char *rest; // End of gap
|
|
unsigned char *end; // End of text buffer
|
|
|
|
int toppos; // Text position for current top screen line
|
|
int topline; // Line number for top of screen
|
|
int margin; // Position for leftmost column on screen
|
|
|
|
int linepos; // Text position for current line
|
|
int line; // Current document line
|
|
int col; // Current document column
|
|
int lastcol; // Remembered column from last horizontal navigation
|
|
int anchor; // Anchor position for selection
|
|
|
|
struct undo *undohead; // Start of undo buffer list
|
|
struct undo *undotail; // End of undo buffer list
|
|
struct undo *undo; // Undo/redo boundary
|
|
|
|
int refresh; // Flag to trigger screen redraw
|
|
int lineupdate; // Flag to trigger redraw of current line
|
|
int dirty; // Dirty flag is set when the editor buffer has been changed
|
|
|
|
int newfile; // File is a new file
|
|
int permissions; // File permissions
|
|
|
|
int selecting; // Selecting active in the edtor.
|
|
|
|
struct env *env; // Reference to global editor environment
|
|
struct editor *next; // Next editor
|
|
struct editor *prev; // Previous editor
|
|
|
|
char filename[FILENAME_MAX];
|
|
};
|
|
|
|
struct env {
|
|
struct editor *current; // Current editor
|
|
|
|
unsigned char *clipboard; // Clipboard
|
|
int clipsize; // Clipboard size
|
|
|
|
unsigned char *search; // Search text
|
|
unsigned char *linebuf; // Scratch buffer
|
|
|
|
int cols; // Console columns
|
|
int lines; // Console lines
|
|
|
|
int untitled; // Counter for untitled files
|
|
};
|
|
|
|
//
|
|
// Editor buffer functions
|
|
//
|
|
|
|
static void clear_undo(struct editor *ed) {
|
|
struct undo *undo = ed->undohead;
|
|
while (undo) {
|
|
struct undo *next = undo->next;
|
|
free(undo->undobuf);
|
|
free(undo->redobuf);
|
|
free(undo);
|
|
undo = next;
|
|
}
|
|
ed->undohead = ed->undotail = ed->undo = NULL;
|
|
}
|
|
|
|
static void reset_undo(struct editor *ed) {
|
|
while (ed->undotail != ed->undo) {
|
|
struct undo *undo = ed->undotail;
|
|
if (!undo) {
|
|
ed->undohead = NULL;
|
|
ed->undotail = NULL;
|
|
break;
|
|
}
|
|
ed->undotail = undo->prev;
|
|
if (undo->prev) undo->prev->next = NULL;
|
|
free(undo->undobuf);
|
|
free(undo->redobuf);
|
|
free(undo);
|
|
}
|
|
ed->undo = ed->undotail;
|
|
}
|
|
|
|
static struct editor *create_editor(struct env *env) {
|
|
struct editor *ed = (struct editor *) malloc(sizeof(struct editor));
|
|
memset(ed, 0, sizeof(struct editor));
|
|
if (env->current) {
|
|
ed->next = env->current->next;
|
|
ed->prev = env->current;
|
|
env->current->next->prev = ed;
|
|
env->current->next = ed;
|
|
} else {
|
|
ed->next = ed->prev = ed;
|
|
}
|
|
ed->env = env;
|
|
env->current = ed;
|
|
return ed;
|
|
}
|
|
|
|
static void delete_editor(struct editor *ed) {
|
|
if (ed->next == ed) {
|
|
ed->env->current = NULL;
|
|
} else {
|
|
ed->env->current = ed->prev;
|
|
}
|
|
ed->next->prev = ed->prev;
|
|
ed->prev->next = ed->next;
|
|
if (ed->start) free(ed->start);
|
|
clear_undo(ed);
|
|
free(ed);
|
|
}
|
|
|
|
static struct editor *find_editor(struct env *env, char *filename) {
|
|
char fn[FILENAME_MAX];
|
|
struct editor *ed = env->current;
|
|
struct editor *start = ed;
|
|
|
|
if (!realpath(filename, fn)) strcpy(fn, filename);
|
|
|
|
do {
|
|
if (strcmp(fn, ed->filename) == 0) return ed;
|
|
ed = ed->next;
|
|
} while (ed != start);
|
|
return NULL;
|
|
}
|
|
|
|
static int new_file(struct editor *ed, char *filename) {
|
|
if (*filename) {
|
|
strcpy(ed->filename, filename);
|
|
} else {
|
|
sprintf(ed->filename, "Untitled-%d", ++ed->env->untitled);
|
|
ed->newfile = 1;
|
|
}
|
|
ed->permissions = 0644;
|
|
|
|
ed->start = (unsigned char *) malloc(MINEXTEND);
|
|
if (!ed->start) return -1;
|
|
#ifdef DEBUG
|
|
memset(ed->start, 0, MINEXTEND);
|
|
#endif
|
|
|
|
ed->gap = ed->start;
|
|
ed->rest = ed->end = ed->gap + MINEXTEND;
|
|
ed->anchor = -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int load_file(struct editor *ed, char *filename) {
|
|
struct stat statbuf;
|
|
int length;
|
|
int f;
|
|
|
|
if (!realpath(filename, ed->filename)) return -1;
|
|
f = open(ed->filename, O_RDONLY | O_BINARY);
|
|
if (f < 0) return -1;
|
|
|
|
if (fstat(f, &statbuf) < 0) goto err;
|
|
length = statbuf.st_size;
|
|
ed->permissions = statbuf.st_mode & 0777;
|
|
|
|
ed->start = (unsigned char *) malloc(length + MINEXTEND);
|
|
if (!ed->start) goto err;
|
|
#ifdef DEBUG
|
|
memset(ed->start, 0, length + MINEXTEND);
|
|
#endif
|
|
if (read(f, ed->start, length) != length) goto err;
|
|
|
|
ed->gap = ed->start + length;
|
|
ed->rest = ed->end = ed->gap + MINEXTEND;
|
|
ed->anchor = -1;
|
|
|
|
close(f);
|
|
return 0;
|
|
|
|
err:
|
|
close(f);
|
|
if (ed->start) {
|
|
free(ed->start);
|
|
ed->start = NULL;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int save_file(struct editor *ed) {
|
|
int f;
|
|
|
|
f = open(ed->filename, O_CREAT | O_TRUNC | O_WRONLY, ed->permissions);
|
|
if (f < 0) return -1;
|
|
|
|
if (write(f, ed->start, ed->gap - ed->start) != ed->gap - ed->start) goto err;
|
|
if (write(f, ed->rest, ed->end - ed->rest) != ed->end - ed->rest) goto err;
|
|
|
|
close(f);
|
|
ed->dirty = 0;
|
|
clear_undo(ed);
|
|
return 0;
|
|
|
|
err:
|
|
close(f);
|
|
return -1;
|
|
}
|
|
|
|
static int text_length(struct editor *ed) {
|
|
return (ed->gap - ed->start) + (ed->end - ed->rest);
|
|
}
|
|
|
|
static unsigned char *text_ptr(struct editor *ed, int pos) {
|
|
unsigned char *p = ed->start + pos;
|
|
if (p >= ed->gap) p += (ed->rest - ed->gap);
|
|
return p;
|
|
}
|
|
|
|
static void move_gap(struct editor *ed, int pos, int minsize) {
|
|
int gapsize = ed->rest - ed->gap;
|
|
unsigned char *p = text_ptr(ed, pos);
|
|
if (minsize < 0) minsize = 0;
|
|
|
|
if (minsize <= gapsize) {
|
|
if (p != ed->rest) {
|
|
if (p < ed->gap) {
|
|
memmove(p + gapsize, p, ed->gap - p);
|
|
} else {
|
|
memmove(ed->gap, ed->rest, p - ed->rest);
|
|
}
|
|
ed->gap = ed->start + pos;
|
|
ed->rest = ed->gap + gapsize;
|
|
}
|
|
} else {
|
|
int newsize;
|
|
unsigned char *start;
|
|
unsigned char *gap;
|
|
unsigned char *rest;
|
|
unsigned char *end;
|
|
|
|
if (gapsize + MINEXTEND > minsize) minsize = gapsize + MINEXTEND;
|
|
newsize = (ed->end - ed->start) - gapsize + minsize;
|
|
start = (unsigned char *) malloc(newsize); // TODO check for out of memory
|
|
gap = start + pos;
|
|
rest = gap + minsize;
|
|
end = start + newsize;
|
|
|
|
if (p < ed->gap) {
|
|
memcpy(start, ed->start, pos);
|
|
memcpy(rest, p, ed->gap - p);
|
|
memcpy(end - (ed->end - ed->rest), ed->rest, ed->end - ed->rest);
|
|
} else {
|
|
memcpy(start, ed->start, ed->gap - ed->start);
|
|
memcpy(start + (ed->gap - ed->start), ed->rest, p - ed->rest);
|
|
memcpy(rest, p, ed->end - p);
|
|
}
|
|
|
|
free(ed->start);
|
|
ed->start = start;
|
|
ed->gap = gap;
|
|
ed->rest = rest;
|
|
ed->end = end;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
memset(ed->gap, 0, ed->rest - ed->gap);
|
|
#endif
|
|
}
|
|
|
|
static void close_gap(struct editor *ed) {
|
|
int len = text_length(ed);
|
|
move_gap(ed, len, 1);
|
|
ed->start[len] = 0;
|
|
}
|
|
|
|
static int get(struct editor *ed, int pos) {
|
|
unsigned char *p = text_ptr(ed, pos);
|
|
if (p >= ed->end) return -1;
|
|
return *p;
|
|
}
|
|
|
|
static int compare(struct editor *ed, unsigned char *buf, int pos, int len) {
|
|
unsigned char *bufptr = buf;
|
|
unsigned char *p = ed->start + pos;
|
|
if (p >= ed->gap) p += (ed->rest - ed->gap);
|
|
|
|
while (len > 0) {
|
|
if (p == ed->end) return 0;
|
|
if (*bufptr++ != *p) return 0;
|
|
len--;
|
|
if (++p == ed->gap) p = ed->rest;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int copy(struct editor *ed, unsigned char *buf, int pos, int len) {
|
|
unsigned char *bufptr = buf;
|
|
unsigned char *p = ed->start + pos;
|
|
if (p >= ed->gap) p += (ed->rest - ed->gap);
|
|
|
|
while (len > 0) {
|
|
if (p == ed->end) break;
|
|
*bufptr++ = *p;
|
|
len--;
|
|
if (++p == ed->gap) p = ed->rest;
|
|
}
|
|
|
|
return bufptr - buf;
|
|
}
|
|
|
|
static void replace(struct editor *ed, int pos, int len, unsigned char *buf, int bufsize, int doundo) {
|
|
unsigned char *p;
|
|
struct undo *undo;
|
|
|
|
// Store undo information
|
|
if (doundo) {
|
|
reset_undo(ed);
|
|
undo = ed->undotail;
|
|
if (undo && len == 0 && bufsize == 1 && undo->erased == 0 && pos == undo->pos + undo->inserted) {
|
|
// Insert character at end of current redo buffer
|
|
undo->redobuf = realloc(undo->redobuf, undo->inserted + 1);
|
|
undo->redobuf[undo->inserted] = *buf;
|
|
undo->inserted++;
|
|
} else if (undo && len == 1 && bufsize == 0 && undo->inserted == 0 && pos == undo->pos) {
|
|
// Erase character at end of current undo buffer
|
|
undo->undobuf = realloc(undo->undobuf, undo->erased + 1);
|
|
undo->undobuf[undo->erased] = get(ed, pos);
|
|
undo->erased++;
|
|
} else if (undo && len == 1 && bufsize == 0 && undo->inserted == 0 && pos == undo->pos - 1) {
|
|
// Erase character at beginning of current undo buffer
|
|
undo->pos--;
|
|
undo->undobuf = realloc(undo->undobuf, undo->erased + 1);
|
|
memmove(undo->undobuf + 1, undo->undobuf, undo->erased);
|
|
undo->undobuf[0] = get(ed, pos);
|
|
undo->erased++;
|
|
} else {
|
|
// Create new undo buffer
|
|
undo = (struct undo *) malloc(sizeof(struct undo));
|
|
if (ed->undotail) ed->undotail->next = undo;
|
|
undo->prev = ed->undotail;
|
|
undo->next = NULL;
|
|
ed->undotail = ed->undo = undo;
|
|
if (!ed->undohead) ed->undohead = undo;
|
|
|
|
undo->pos = pos;
|
|
undo->erased = len;
|
|
undo->inserted = bufsize;
|
|
undo->undobuf = undo->redobuf = NULL;
|
|
if (len > 0) {
|
|
undo->undobuf = malloc(len);
|
|
copy(ed, undo->undobuf, pos, len);
|
|
}
|
|
if (bufsize > 0) {
|
|
undo->redobuf = malloc(bufsize);
|
|
memcpy(undo->redobuf, buf, bufsize);
|
|
}
|
|
}
|
|
}
|
|
|
|
p = ed->start + pos;
|
|
if (bufsize == 0 && p <= ed->gap && p + len >= ed->gap) {
|
|
// Handle deletions at the edges of the gap
|
|
ed->rest += len - (ed->gap - p);
|
|
ed->gap = p;
|
|
} else {
|
|
// Move the gap
|
|
move_gap(ed, pos + len, bufsize - len);
|
|
|
|
// Replace contents
|
|
memcpy(ed->start + pos, buf, bufsize);
|
|
ed->gap = ed->start + pos + bufsize;
|
|
}
|
|
|
|
// Mark buffer as dirty
|
|
ed->dirty = 1;
|
|
}
|
|
|
|
static void insert(struct editor *ed, int pos, unsigned char *buf, int bufsize) {
|
|
replace(ed, pos, 0, buf, bufsize, 1);
|
|
}
|
|
|
|
static void erase(struct editor *ed, int pos, int len) {
|
|
replace(ed, pos, len, NULL, 0, 1);
|
|
}
|
|
|
|
//
|
|
// Navigation functions
|
|
//
|
|
|
|
static int line_length(struct editor *ed, int linepos) {
|
|
int pos = linepos;
|
|
while (1) {
|
|
int ch = get(ed, pos);
|
|
if (ch < 0 || ch == '\n' || ch == '\r') break;
|
|
pos++;
|
|
}
|
|
|
|
return pos - linepos;
|
|
}
|
|
|
|
static int line_start(struct editor *ed, int pos) {
|
|
while (1) {
|
|
if (pos == 0) break;
|
|
if (get(ed, pos - 1) == '\n') break;
|
|
pos--;
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
static int next_line(struct editor *ed, int pos) {
|
|
while (1) {
|
|
int ch = get(ed, pos);
|
|
if (ch < 0) return -1;
|
|
pos++;
|
|
if (ch == '\n') return pos;
|
|
}
|
|
}
|
|
|
|
static int prev_line(struct editor *ed, int pos) {
|
|
if (pos == 0) return -1;
|
|
|
|
while (pos > 0) {
|
|
int ch = get(ed, --pos);
|
|
if (ch == '\n') break;
|
|
}
|
|
|
|
while (pos > 0) {
|
|
int ch = get(ed, --pos);
|
|
if (ch == '\n') return pos + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int column(struct editor *ed, int linepos, int col) {
|
|
unsigned char *p = text_ptr(ed, linepos);
|
|
int c = 0;
|
|
while (col > 0) {
|
|
if (p == ed->end) break;
|
|
if (*p == '\t') {
|
|
int spaces = TABSIZE - c % TABSIZE;
|
|
c += spaces;
|
|
} else {
|
|
c++;
|
|
}
|
|
col--;
|
|
if (++p == ed->gap) p = ed->rest;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
static void moveto(struct editor *ed, int pos, int center) {
|
|
int scroll = 0;
|
|
for (;;) {
|
|
int cur = ed->linepos + ed->col;
|
|
if (pos < cur) {
|
|
if (pos >= ed->linepos) {
|
|
ed->col = pos - ed->linepos;
|
|
} else {
|
|
ed->col = 0;
|
|
ed->linepos = prev_line(ed, ed->linepos);
|
|
ed->line--;
|
|
|
|
if (ed->topline > ed->line) {
|
|
ed->toppos = ed->linepos;
|
|
ed->topline--;
|
|
ed->refresh = 1;
|
|
scroll = 1;
|
|
}
|
|
}
|
|
} else if (pos > cur) {
|
|
int next = next_line(ed, ed->linepos);
|
|
if (next == -1) {
|
|
ed->col = line_length(ed, ed->linepos);
|
|
break;
|
|
} else if (pos < next) {
|
|
ed->col = pos - ed->linepos;
|
|
} else {
|
|
ed->col = 0;
|
|
ed->linepos = next;
|
|
ed->line++;
|
|
|
|
if (ed->line >= ed->topline + ed->env->lines) {
|
|
ed->toppos = next_line(ed, ed->toppos);
|
|
ed->topline++;
|
|
ed->refresh = 1;
|
|
scroll = 1;
|
|
}
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (scroll && center) {
|
|
int tl = ed->line - ed->env->lines / 2;
|
|
if (tl < 0) tl = 0;
|
|
for (;;) {
|
|
if (ed->topline > tl) {
|
|
ed->toppos = prev_line(ed, ed->toppos);
|
|
ed->topline--;
|
|
} else if (ed->topline < tl) {
|
|
ed->toppos = next_line(ed, ed->toppos);
|
|
ed->topline++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Text selection
|
|
//
|
|
|
|
static int get_selection(struct editor *ed, int *start, int *end) {
|
|
if (ed->anchor == -1) {
|
|
*start = *end = -1;
|
|
return 0;
|
|
} else {
|
|
int pos = ed->linepos + ed->col;
|
|
if (pos == ed->anchor) {
|
|
*start = *end = -1;
|
|
return 0;
|
|
} else if (pos < ed->anchor) {
|
|
*start = pos;
|
|
*end = ed->anchor;
|
|
} else {
|
|
*start = ed->anchor;
|
|
*end = pos;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int get_selected_text(struct editor *ed, char *buffer, int size) {
|
|
int selstart, selend, len;
|
|
|
|
if (!get_selection(ed, &selstart, &selend)) return 0;
|
|
len = selend - selstart;
|
|
if (len >= size) return 0;
|
|
copy(ed, (unsigned char*) buffer, selstart, len);
|
|
buffer[len] = 0;
|
|
return len;
|
|
}
|
|
|
|
static void update_selection(struct editor *ed, int select) {
|
|
if (select) {
|
|
if (ed->anchor == -1) ed->anchor = ed->linepos + ed->col;
|
|
ed->refresh = 1;
|
|
} else {
|
|
if (ed->anchor != -1) ed->refresh = 1;
|
|
ed->anchor = -1;
|
|
}
|
|
}
|
|
|
|
static int erase_selection(struct editor *ed) {
|
|
int selstart, selend;
|
|
|
|
if (!get_selection(ed, &selstart, &selend)) return 0;
|
|
moveto(ed, selstart, 0);
|
|
erase(ed, selstart, selend - selstart);
|
|
ed->anchor = -1;
|
|
ed->refresh = 1;
|
|
return 1;
|
|
}
|
|
|
|
static void select_all(struct editor *ed) {
|
|
ed->anchor = 0;
|
|
ed->refresh = 1;
|
|
moveto(ed, text_length(ed), 0);
|
|
}
|
|
|
|
//
|
|
// Screen functions
|
|
//
|
|
|
|
static void get_console_size(struct env *env) {
|
|
#ifdef __linux__
|
|
struct winsize ws;
|
|
ioctl(0, TIOCGWINSZ, &ws);
|
|
env->cols = ws.ws_col;
|
|
env->lines = ws.ws_row - 1;
|
|
#elif defined(__rtems__)
|
|
env->cols = 80;
|
|
env->lines = 25;
|
|
#else
|
|
struct term *term = gettib()->proc->term;
|
|
env->cols = term->cols;
|
|
env->lines = term->lines - 1;
|
|
#endif
|
|
env->linebuf = realloc(env->linebuf, env->cols + LINEBUF_EXTRA);
|
|
}
|
|
|
|
static void outch(char c) {
|
|
putchar(c);
|
|
}
|
|
|
|
static void outbuf(unsigned char *buf, int len) {
|
|
fwrite(buf, 1, len, stdout);
|
|
}
|
|
|
|
static void outstr(char *str) {
|
|
fputs(str, stdout);
|
|
}
|
|
|
|
static void clear_screen(void) {
|
|
outstr(CLRSCR);
|
|
}
|
|
|
|
static void gotoxy(int col, int line) {
|
|
char buf[32];
|
|
|
|
sprintf(buf, GOTOXY, line + 1, col + 1);
|
|
outstr(buf);
|
|
}
|
|
|
|
//
|
|
// Keyboard functions
|
|
//
|
|
|
|
static void get_modifier_keys(int *shift, int *ctrl) {
|
|
*shift = *ctrl = 0;
|
|
#ifdef __linux__
|
|
if (linux_console) {
|
|
char modifiers = 6;
|
|
if (ioctl(0, TIOCLINUX, &modifiers) >= 0) {
|
|
if (modifiers & 1) *shift = 1;
|
|
if (modifiers & 4) *ctrl = 1;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int getachar(void)
|
|
{
|
|
int ch = getchar();
|
|
return ch;
|
|
}
|
|
|
|
static int getkey(void) {
|
|
int ch, shift, ctrl;
|
|
|
|
ch = getachar();
|
|
if (ch < 0) return ch;
|
|
|
|
switch (ch) {
|
|
case 0x08: return KEY_BACKSPACE;
|
|
case 0x09:
|
|
get_modifier_keys(&shift, &ctrl);
|
|
if (shift) return KEY_SHIFT_TAB;
|
|
if (ctrl) return KEY_CTRL_TAB;
|
|
return KEY_TAB;
|
|
#ifdef SANOS
|
|
case 0x0D: return gettib()->proc->term->type == TERM_CONSOLE ? KEY_ENTER : KEY_UNKNOWN;
|
|
case 0x0A: return gettib()->proc->term->type != TERM_CONSOLE ? KEY_ENTER : KEY_UNKNOWN;
|
|
#else
|
|
case 0x0D: return KEY_ENTER;
|
|
case 0x0A: return KEY_ENTER;
|
|
#endif
|
|
case 0x1B:
|
|
ch = getachar();
|
|
switch (ch) {
|
|
case 0x1B: return KEY_ESC;
|
|
case 0x4F:
|
|
ch = getachar();
|
|
switch (ch) {
|
|
case 0x46: return KEY_END;
|
|
case 0x48: return KEY_HOME;
|
|
case 0x50: return KEY_F1;
|
|
case 0x51: return KEY_F2;
|
|
case 0x52: return KEY_F3;
|
|
case 0x53: return KEY_F4;
|
|
case 0x54: return KEY_F5;
|
|
default: return KEY_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case 0x5B:
|
|
get_modifier_keys(&shift, &ctrl);
|
|
ch = getachar();
|
|
if (ch == 0x31) {
|
|
ch = getachar();
|
|
switch (ch) {
|
|
case 0x35:
|
|
return getachar() == 0x7E ? KEY_F5 : KEY_UNKNOWN;
|
|
case 0x37:
|
|
return getachar() == 0x7E ? KEY_F6 : KEY_UNKNOWN;
|
|
case 0x3B:
|
|
ch = getachar();
|
|
if (ch == 0x7E) return KEY_F7;
|
|
if (ch == 0x32) shift = 1;
|
|
if (ch == 0x35) ctrl = 1;
|
|
if (ch == 0x36) shift = ctrl = 1;
|
|
ch = getachar();
|
|
break;
|
|
case 0x39:
|
|
return getachar() == 0x7E ? KEY_F8 : KEY_UNKNOWN;
|
|
case 0x7E:
|
|
if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
|
|
if (shift) return KEY_SHIFT_HOME;
|
|
if (ctrl) return KEY_CTRL_HOME;
|
|
return KEY_HOME;
|
|
default:
|
|
return KEY_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
switch (ch) {
|
|
case 0x31:
|
|
ch = getachar();
|
|
if (ch != 0x7E) return KEY_UNKNOWN;
|
|
if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
|
|
if (shift) return KEY_SHIFT_HOME;
|
|
if (ctrl) return KEY_CTRL_HOME;
|
|
return KEY_HOME;
|
|
case 0x32:
|
|
ch = getachar();
|
|
switch (ch) {
|
|
case 0x30: ch = getachar(); return KEY_F9;
|
|
case 0x31: ch = getachar(); return KEY_F10;
|
|
case 0x7E: return KEY_INS;
|
|
default: break;
|
|
}
|
|
return KEY_UNKNOWN;
|
|
case 0x33: return getachar() == 0x7E ? KEY_DEL : KEY_UNKNOWN;
|
|
case 0x34:
|
|
if (getachar() != 0x7E) return KEY_UNKNOWN;
|
|
if (shift && ctrl) return KEY_SHIFT_CTRL_END;
|
|
if (shift) return KEY_SHIFT_END;
|
|
if (ctrl) return KEY_CTRL_END;
|
|
return KEY_END;
|
|
case 0x35:
|
|
if (getachar() != 0x7E) return KEY_UNKNOWN;
|
|
if (shift) return KEY_SHIFT_PGUP;
|
|
return KEY_PGUP;
|
|
case 0x36:
|
|
if (getachar() != 0x7E) return KEY_UNKNOWN;
|
|
if (shift) return KEY_SHIFT_PGDN;
|
|
return KEY_PGDN;
|
|
case 0x41:
|
|
if (shift && ctrl) return KEY_SHIFT_CTRL_UP;
|
|
if (shift) return KEY_SHIFT_UP;
|
|
if (ctrl) return KEY_CTRL_UP;
|
|
return KEY_UP;
|
|
case 0x42:
|
|
if (shift && ctrl) return KEY_SHIFT_CTRL_DOWN;
|
|
if (shift) return KEY_SHIFT_DOWN;
|
|
if (ctrl) return KEY_CTRL_DOWN;
|
|
return KEY_DOWN;
|
|
case 0x43:
|
|
if (shift && ctrl) return KEY_SHIFT_CTRL_RIGHT;
|
|
if (shift) return KEY_SHIFT_RIGHT;
|
|
if (ctrl) return KEY_CTRL_RIGHT;
|
|
return KEY_RIGHT;
|
|
case 0x44:
|
|
if (shift && ctrl) return KEY_SHIFT_CTRL_LEFT;
|
|
if (shift) return KEY_SHIFT_LEFT;
|
|
if (ctrl) return KEY_CTRL_LEFT;
|
|
return KEY_LEFT;
|
|
case 0x46:
|
|
if (shift && ctrl) return KEY_SHIFT_CTRL_END;
|
|
if (shift) return KEY_SHIFT_END;
|
|
if (ctrl) return KEY_CTRL_END;
|
|
return KEY_END;
|
|
case 0x48:
|
|
if (shift && ctrl) return KEY_SHIFT_CTRL_HOME;
|
|
if (shift) return KEY_SHIFT_HOME;
|
|
if (ctrl) return KEY_CTRL_HOME;
|
|
return KEY_HOME;
|
|
case 0x5A:
|
|
return KEY_SHIFT_TAB;
|
|
case 0x5B:
|
|
ch = getachar();
|
|
switch (ch) {
|
|
case 0x41: return KEY_F1;
|
|
case 0x43: return KEY_F3;
|
|
case 0x45: return KEY_F5;
|
|
}
|
|
return KEY_UNKNOWN;
|
|
|
|
default: return KEY_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
default: return KEY_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case 0x00:
|
|
case 0xE0:
|
|
ch = getachar();
|
|
switch (ch) {
|
|
case 0x0F: return KEY_SHIFT_TAB;
|
|
case 0x3B: return KEY_F1;
|
|
case 0x3D: return KEY_F3;
|
|
case 0x3F: return KEY_F5;
|
|
case 0x47: return KEY_HOME;
|
|
case 0x48: return KEY_UP;
|
|
case 0x49: return KEY_PGUP;
|
|
case 0x4B: return KEY_LEFT;
|
|
case 0x4D: return KEY_RIGHT;
|
|
case 0x4F: return KEY_END;
|
|
case 0x50: return KEY_DOWN;
|
|
case 0x51: return KEY_PGDN;
|
|
case 0x52: return KEY_INS;
|
|
case 0x53: return KEY_DEL;
|
|
case 0x73: return KEY_CTRL_LEFT;
|
|
case 0x74: return KEY_CTRL_RIGHT;
|
|
case 0x75: return KEY_CTRL_END;
|
|
case 0x77: return KEY_CTRL_HOME;
|
|
case 0x8D: return KEY_CTRL_UP;
|
|
case 0x91: return KEY_CTRL_DOWN;
|
|
case 0x94: return KEY_CTRL_TAB;
|
|
case 0xB8: return KEY_SHIFT_UP;
|
|
case 0xB7: return KEY_SHIFT_HOME;
|
|
case 0xBF: return KEY_SHIFT_END;
|
|
case 0xB9: return KEY_SHIFT_PGUP;
|
|
case 0xBB: return KEY_SHIFT_LEFT;
|
|
case 0xBD: return KEY_SHIFT_RIGHT;
|
|
case 0xC0: return KEY_SHIFT_DOWN;
|
|
case 0xC1: return KEY_SHIFT_PGDN;
|
|
case 0xDB: return KEY_SHIFT_CTRL_LEFT;
|
|
case 0xDD: return KEY_SHIFT_CTRL_RIGHT;
|
|
case 0xD8: return KEY_SHIFT_CTRL_UP;
|
|
case 0xE0: return KEY_SHIFT_CTRL_DOWN;
|
|
case 0xD7: return KEY_SHIFT_CTRL_HOME;
|
|
case 0xDF: return KEY_SHIFT_CTRL_END;
|
|
|
|
default: return KEY_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case 0x7F: return KEY_BACKSPACE;
|
|
|
|
default: return ch;
|
|
}
|
|
}
|
|
|
|
static int prompt(struct editor *ed, char *msg, int selection) {
|
|
int maxlen, len, ch;
|
|
char *buf = (char*) ed->env->linebuf;
|
|
|
|
gotoxy(0, ed->env->lines);
|
|
outstr(STATUS_COLOR);
|
|
outstr(msg);
|
|
outstr(CLREOL);
|
|
|
|
len = 0;
|
|
maxlen = ed->env->cols - strlen(msg) - 1;
|
|
if (selection) {
|
|
len = get_selected_text(ed, buf, maxlen);
|
|
outbuf((unsigned char*) buf, len);
|
|
}
|
|
|
|
for (;;) {
|
|
fflush(stdout);
|
|
ch = getkey();
|
|
if (ch == KEY_ESC) {
|
|
return 0;
|
|
} else if (ch == KEY_ENTER) {
|
|
buf[len] = 0;
|
|
return len > 0;
|
|
} else if (ch == KEY_BACKSPACE) {
|
|
if (len > 0) {
|
|
outstr("\b \b");
|
|
len--;
|
|
}
|
|
} else if (ch >= ' ' && ch < 0x100 && len < maxlen) {
|
|
outch(ch);
|
|
buf[len++] = ch;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int ask(void) {
|
|
int ch = getachar();
|
|
return ch == 'y' || ch == 'Y';
|
|
}
|
|
|
|
//
|
|
// Display functions
|
|
//
|
|
|
|
static void display_message(struct editor *ed, char *fmt, ...) {
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
gotoxy(0, ed->env->lines);
|
|
outstr(STATUS_COLOR);
|
|
vprintf(fmt, args);
|
|
outstr(CLREOL TEXT_COLOR);
|
|
fflush(stdout);
|
|
va_end(args);
|
|
}
|
|
|
|
static void draw_full_statusline(struct editor *ed) {
|
|
struct env *env = ed->env;
|
|
int namewidth = env->cols - 29;
|
|
gotoxy(0, env->lines);
|
|
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);
|
|
outstr((char*) env->linebuf);
|
|
}
|
|
|
|
static void draw_statusline(struct editor *ed) {
|
|
gotoxy(ed->env->cols - 20, ed->env->lines);
|
|
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);
|
|
outstr((char*) ed->env->linebuf);
|
|
}
|
|
|
|
static void display_line(struct editor *ed, int pos, int fullline) {
|
|
int hilite = 0;
|
|
int col = 0;
|
|
int margin = ed->margin;
|
|
int maxcol = ed->env->cols + margin;
|
|
unsigned char *bufptr = ed->env->linebuf;
|
|
unsigned char *p = text_ptr(ed, pos);
|
|
int selstart, selend, ch;
|
|
char *s;
|
|
|
|
get_selection(ed, &selstart, &selend);
|
|
while (col < maxcol) {
|
|
if (margin == 0) {
|
|
if (!hilite && pos >= selstart && pos < selend) {
|
|
for (s = SELECT_COLOR; *s; s++) *bufptr++ = *s;
|
|
hilite = 1;
|
|
} else if (hilite && pos >= selend) {
|
|
for (s = TEXT_COLOR; *s; s++) *bufptr++ = *s;
|
|
hilite = 0;
|
|
}
|
|
}
|
|
|
|
if (p == ed->end) break;
|
|
ch = *p;
|
|
if (ch == '\r' || ch == '\n') break;
|
|
|
|
if (ch == '\t') {
|
|
int spaces = TABSIZE - col % TABSIZE;
|
|
while (spaces > 0 && col < maxcol) {
|
|
if (margin > 0) {
|
|
margin--;
|
|
} else {
|
|
*bufptr++ = ' ';
|
|
}
|
|
col++;
|
|
spaces--;
|
|
}
|
|
} else {
|
|
if (margin > 0) {
|
|
margin--;
|
|
} else {
|
|
*bufptr++ = ch;
|
|
}
|
|
col++;
|
|
}
|
|
|
|
if (++p == ed->gap) p = ed->rest;
|
|
pos++;
|
|
}
|
|
|
|
#if defined(__linux__)
|
|
if (hilite) {
|
|
while (col < maxcol) {
|
|
*bufptr++ = ' ';
|
|
col++;
|
|
}
|
|
} else {
|
|
if (col == margin) *bufptr++ = ' ';
|
|
}
|
|
#endif
|
|
|
|
if (col < maxcol) {
|
|
for (s = CLREOL; *s; s++) *bufptr++ = *s;
|
|
if (fullline) {
|
|
memcpy(bufptr, "\r\n", 2);
|
|
bufptr += 2;
|
|
}
|
|
}
|
|
|
|
if (hilite) {
|
|
for (s = TEXT_COLOR; *s; s++) *bufptr++ = *s;
|
|
}
|
|
|
|
outbuf(ed->env->linebuf, bufptr - ed->env->linebuf);
|
|
}
|
|
|
|
static void update_line(struct editor *ed) {
|
|
gotoxy(0, ed->line - ed->topline);
|
|
display_line(ed, ed->linepos, 0);
|
|
}
|
|
|
|
static void draw_screen(struct editor *ed) {
|
|
int pos;
|
|
int i;
|
|
|
|
gotoxy(0, 0);
|
|
outstr(TEXT_COLOR);
|
|
pos = ed->toppos;
|
|
for (i = 0; i < ed->env->lines; i++) {
|
|
if (pos < 0) {
|
|
outstr(CLREOL "\r\n");
|
|
} else {
|
|
display_line(ed, pos, 1);
|
|
pos = next_line(ed, pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void position_cursor(struct editor *ed) {
|
|
int col = column(ed, ed->linepos, ed->col);
|
|
gotoxy(col - ed->margin, ed->line - ed->topline);
|
|
}
|
|
|
|
//
|
|
// Cursor movement
|
|
//
|
|
|
|
static void adjust(struct editor *ed) {
|
|
int col;
|
|
int ll = line_length(ed, ed->linepos);
|
|
ed->col = ed->lastcol;
|
|
if (ed->col > ll) ed->col = ll;
|
|
|
|
col = column(ed, ed->linepos, ed->col);
|
|
while (col < ed->margin) {
|
|
ed->margin -= 4;
|
|
if (ed->margin < 0) ed->margin = 0;
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
while (col - ed->margin >= ed->env->cols) {
|
|
ed->margin += 4;
|
|
ed->refresh = 1;
|
|
}
|
|
}
|
|
|
|
static void select_toggle(struct editor *ed) {
|
|
ed->selecting = ed->selecting ? 0 : 1;
|
|
update_selection(ed, ed->selecting);
|
|
adjust(ed);
|
|
}
|
|
|
|
static void up(struct editor *ed, int select) {
|
|
int newpos;
|
|
|
|
update_selection(ed, select);
|
|
|
|
newpos = prev_line(ed, ed->linepos);
|
|
if (newpos < 0) return;
|
|
|
|
ed->linepos = newpos;
|
|
ed->line--;
|
|
if (ed->line < ed->topline) {
|
|
ed->toppos = ed->linepos;
|
|
ed->topline = ed->line;
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
adjust(ed);
|
|
}
|
|
|
|
static void down(struct editor *ed, int select) {
|
|
int newpos;
|
|
|
|
update_selection(ed, select);
|
|
|
|
newpos = next_line(ed, ed->linepos);
|
|
if (newpos < 0) return;
|
|
|
|
ed->linepos = newpos;
|
|
ed->line++;
|
|
|
|
if (ed->line >= ed->topline + ed->env->lines) {
|
|
ed->toppos = next_line(ed, ed->toppos);
|
|
ed->topline++;
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
adjust(ed);
|
|
}
|
|
|
|
static void left(struct editor *ed, int select) {
|
|
update_selection(ed, select);
|
|
if (ed->col > 0) {
|
|
ed->col--;
|
|
} else {
|
|
int newpos = prev_line(ed, ed->linepos);
|
|
if (newpos < 0) return;
|
|
|
|
ed->col = line_length(ed, newpos);
|
|
ed->linepos = newpos;
|
|
ed->line--;
|
|
if (ed->line < ed->topline) {
|
|
ed->toppos = ed->linepos;
|
|
ed->topline = ed->line;
|
|
ed->refresh = 1;
|
|
}
|
|
}
|
|
|
|
ed->lastcol = ed->col;
|
|
adjust(ed);
|
|
}
|
|
|
|
static void right(struct editor *ed, int select) {
|
|
update_selection(ed, select);
|
|
if (ed->col < line_length(ed, ed->linepos)) {
|
|
ed->col++;
|
|
} else {
|
|
int newpos = next_line(ed, ed->linepos);
|
|
if (newpos < 0) return;
|
|
|
|
ed->col = 0;
|
|
ed->linepos = newpos;
|
|
ed->line++;
|
|
|
|
if (ed->line >= ed->topline + ed->env->lines) {
|
|
ed->toppos = next_line(ed, ed->toppos);
|
|
ed->topline++;
|
|
ed->refresh = 1;
|
|
}
|
|
}
|
|
|
|
ed->lastcol = ed->col;
|
|
adjust(ed);
|
|
}
|
|
|
|
static int wordchar(int ch) {
|
|
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');
|
|
}
|
|
|
|
static void wordleft(struct editor *ed, int select) {
|
|
int pos, phase;
|
|
|
|
update_selection(ed, select);
|
|
pos = ed->linepos + ed->col;
|
|
phase = 0;
|
|
while (pos > 0) {
|
|
int ch = get(ed, pos - 1);
|
|
if (phase == 0) {
|
|
if (wordchar(ch)) phase = 1;
|
|
} else {
|
|
if (!wordchar(ch)) break;
|
|
}
|
|
|
|
pos--;
|
|
if (pos < ed->linepos) {
|
|
ed->linepos = prev_line(ed, ed->linepos);
|
|
ed->line--;
|
|
ed->refresh = 1;
|
|
}
|
|
}
|
|
ed->col = pos - ed->linepos;
|
|
if (ed->line < ed->topline) {
|
|
ed->toppos = ed->linepos;
|
|
ed->topline = ed->line;
|
|
}
|
|
|
|
ed->lastcol = ed->col;
|
|
adjust(ed);
|
|
}
|
|
|
|
static void wordright(struct editor *ed, int select) {
|
|
int pos, end, phase, next;
|
|
|
|
update_selection(ed, select);
|
|
pos = ed->linepos + ed->col;
|
|
end = text_length(ed);
|
|
next = next_line(ed, ed->linepos);
|
|
phase = 0;
|
|
while (pos < end) {
|
|
int ch = get(ed, pos);
|
|
if (phase == 0) {
|
|
if (wordchar(ch)) phase = 1;
|
|
} else {
|
|
if (!wordchar(ch)) break;
|
|
}
|
|
|
|
pos++;
|
|
if (pos == next) {
|
|
ed->linepos = next;
|
|
next = next_line(ed, ed->linepos);
|
|
ed->line++;
|
|
ed->refresh = 1;
|
|
}
|
|
}
|
|
ed->col = pos - ed->linepos;
|
|
if (ed->line >= ed->topline + ed->env->lines) {
|
|
ed->toppos = next_line(ed, ed->toppos);
|
|
ed->topline++;
|
|
}
|
|
|
|
ed->lastcol = ed->col;
|
|
adjust(ed);
|
|
}
|
|
|
|
static void home(struct editor *ed, int select) {
|
|
update_selection(ed, select);
|
|
ed->col = ed->lastcol = 0;
|
|
adjust(ed);
|
|
}
|
|
|
|
static void end(struct editor *ed, int select) {
|
|
update_selection(ed, select);
|
|
ed->col = ed->lastcol = line_length(ed, ed->linepos);
|
|
adjust(ed);
|
|
}
|
|
|
|
static void top(struct editor *ed, int select) {
|
|
update_selection(ed, select);
|
|
ed->toppos = ed->topline = ed->margin = 0;
|
|
ed->linepos = ed->line = ed->col = ed->lastcol = 0;
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
static void bottom(struct editor *ed, int select) {
|
|
update_selection(ed, select);
|
|
for (;;) {
|
|
int newpos = next_line(ed, ed->linepos);
|
|
if (newpos < 0) break;
|
|
|
|
ed->linepos = newpos;
|
|
ed->line++;
|
|
|
|
if (ed->line >= ed->topline + ed->env->lines) {
|
|
ed->toppos = next_line(ed, ed->toppos);
|
|
ed->topline++;
|
|
ed->refresh = 1;
|
|
}
|
|
}
|
|
ed->col = ed->lastcol = line_length(ed, ed->linepos);
|
|
adjust(ed);
|
|
}
|
|
|
|
static void pageup(struct editor *ed, int select) {
|
|
int i;
|
|
|
|
update_selection(ed, select);
|
|
if (ed->line < ed->env->lines) {
|
|
ed->linepos = ed->toppos = 0;
|
|
ed->line = ed->topline = 0;
|
|
} else {
|
|
for (i = 0; i < ed->env->lines; i++) {
|
|
int newpos = prev_line(ed, ed->linepos);
|
|
if (newpos < 0) return;
|
|
|
|
ed->linepos = newpos;
|
|
ed->line--;
|
|
|
|
if (ed->topline > 0) {
|
|
ed->toppos = prev_line(ed, ed->toppos);
|
|
ed->topline--;
|
|
}
|
|
}
|
|
}
|
|
|
|
ed->refresh = 1;
|
|
adjust(ed);
|
|
}
|
|
|
|
static void pagedown(struct editor *ed, int select) {
|
|
int i;
|
|
|
|
update_selection(ed, select);
|
|
for (i = 0; i < ed->env->lines; i++) {
|
|
int newpos = next_line(ed, ed->linepos);
|
|
if (newpos < 0) break;
|
|
|
|
ed->linepos = newpos;
|
|
ed->line++;
|
|
|
|
ed->toppos = next_line(ed, ed->toppos);
|
|
ed->topline++;
|
|
}
|
|
|
|
ed->refresh = 1;
|
|
adjust(ed);
|
|
}
|
|
|
|
//
|
|
// Text editing
|
|
//
|
|
|
|
static void insert_char(struct editor *ed, unsigned char ch) {
|
|
erase_selection(ed);
|
|
insert(ed, ed->linepos + ed->col, &ch, 1);
|
|
ed->col++;
|
|
ed->lastcol = ed->col;
|
|
adjust(ed);
|
|
if (!ed->refresh) ed->lineupdate = 1;
|
|
}
|
|
|
|
static void newline(struct editor *ed) {
|
|
int p;
|
|
unsigned char ch;
|
|
|
|
erase_selection(ed);
|
|
#if defined(__linux__) || defined(__rtems__)
|
|
insert(ed, ed->linepos + ed->col, (unsigned char*) "\n", 1);
|
|
#else
|
|
insert(ed, ed->linepos + ed->col, "\r\n", 2);
|
|
#endif
|
|
ed->col = ed->lastcol = 0;
|
|
ed->line++;
|
|
p = ed->linepos;
|
|
ed->linepos = next_line(ed, ed->linepos);
|
|
for (;;) {
|
|
ch = get(ed, p++);
|
|
if (ch == ' ' || ch == '\t') {
|
|
insert(ed, ed->linepos + ed->col, &ch, 1);
|
|
ed->col++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
ed->lastcol = ed->col;
|
|
|
|
ed->refresh = 1;
|
|
|
|
if (ed->line >= ed->topline + ed->env->lines) {
|
|
ed->toppos = next_line(ed, ed->toppos);
|
|
ed->topline++;
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
adjust(ed);
|
|
}
|
|
|
|
static void backspace(struct editor *ed) {
|
|
if (erase_selection(ed)) return;
|
|
if (ed->linepos + ed->col == 0) return;
|
|
if (ed->col == 0) {
|
|
int pos = ed->linepos;
|
|
erase(ed, --pos, 1);
|
|
if (get(ed, pos - 1) == '\r') erase(ed, --pos, 1);
|
|
|
|
ed->line--;
|
|
ed->linepos = line_start(ed, pos);
|
|
ed->col = pos - ed->linepos;
|
|
ed->refresh = 1;
|
|
|
|
if (ed->line < ed->topline) {
|
|
ed->toppos = ed->linepos;
|
|
ed->topline = ed->line;
|
|
}
|
|
} else {
|
|
ed->col--;
|
|
erase(ed, ed->linepos + ed->col, 1);
|
|
ed->lineupdate = 1;
|
|
}
|
|
|
|
ed->lastcol = ed->col;
|
|
adjust(ed);
|
|
}
|
|
|
|
static void del(struct editor *ed) {
|
|
int pos, ch;
|
|
|
|
if (erase_selection(ed)) return;
|
|
pos = ed->linepos + ed->col;
|
|
ch = get(ed, pos);
|
|
if (ch < 0) return;
|
|
|
|
erase(ed, pos, 1);
|
|
if (ch == '\r') {
|
|
ch = get(ed, pos);
|
|
if (ch == '\n') erase(ed, pos, 1);
|
|
}
|
|
|
|
if (ch == '\n') {
|
|
ed->refresh = 1;
|
|
} else {
|
|
ed->lineupdate = 1;
|
|
}
|
|
}
|
|
|
|
static void indent(struct editor *ed, unsigned char *indentation) {
|
|
int start, end, i, lines, toplines, newline, ch;
|
|
unsigned char *buffer, *p;
|
|
int buflen;
|
|
int width = strlen((const char*) indentation);
|
|
int pos = ed->linepos + ed->col;
|
|
|
|
if (!get_selection(ed, &start, &end)) {
|
|
insert_char(ed, '\t');
|
|
return;
|
|
}
|
|
|
|
lines = 0;
|
|
toplines = 0;
|
|
newline = 1;
|
|
for (i = start; i < end; i++) {
|
|
if (i == ed->toppos) toplines = lines;
|
|
if (newline) {
|
|
lines++;
|
|
newline = 0;
|
|
}
|
|
if (get(ed, i) == '\n') newline = 1;
|
|
}
|
|
buflen = end - start + lines * width;
|
|
buffer = malloc(buflen);
|
|
if (!buffer) return;
|
|
|
|
newline = 1;
|
|
p = buffer;
|
|
for (i = start; i < end; i++) {
|
|
if (newline) {
|
|
memcpy(p, indentation, width);
|
|
p += width;
|
|
newline = 0;
|
|
}
|
|
ch = get(ed, i);
|
|
*p++ = ch;
|
|
if (ch == '\n') newline = 1;
|
|
}
|
|
|
|
replace(ed, start, end - start, buffer, buflen, 1);
|
|
free(buffer);
|
|
|
|
if (ed->anchor < pos) {
|
|
pos += width * lines;
|
|
} else {
|
|
ed->anchor += width * lines;
|
|
}
|
|
|
|
ed->toppos += width * toplines;
|
|
ed->linepos = line_start(ed, pos);
|
|
ed->col = ed->lastcol = pos - ed->linepos;
|
|
|
|
adjust(ed);
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
static void unindent(struct editor *ed, unsigned char *indentation) {
|
|
int start, end, i, newline, ch, shrinkage, topofs;
|
|
unsigned char *buffer, *p;
|
|
int width = strlen((const char*) indentation);
|
|
int pos = ed->linepos + ed->col;
|
|
|
|
if (!get_selection(ed, &start, &end)) return;
|
|
|
|
buffer = malloc(end - start);
|
|
if (!buffer) return;
|
|
|
|
newline = 1;
|
|
p = buffer;
|
|
i = start;
|
|
shrinkage = 0;
|
|
topofs = 0;
|
|
while (i < end) {
|
|
if (newline) {
|
|
newline = 0;
|
|
if (compare(ed, indentation, i, width)) {
|
|
i += width;
|
|
shrinkage += width;
|
|
if (i < ed->toppos) topofs -= width;
|
|
continue;
|
|
}
|
|
}
|
|
ch = get(ed, i++);
|
|
*p++ = ch;
|
|
if (ch == '\n') newline = 1;
|
|
}
|
|
|
|
if (!shrinkage) {
|
|
free(buffer);
|
|
return;
|
|
}
|
|
|
|
replace(ed, start, end - start, buffer, p - buffer, 1);
|
|
free(buffer);
|
|
|
|
if (ed->anchor < pos) {
|
|
pos -= shrinkage;
|
|
} else {
|
|
ed->anchor -= shrinkage;
|
|
}
|
|
|
|
ed->toppos += topofs;
|
|
ed->linepos = line_start(ed, pos);
|
|
ed->col = ed->lastcol = pos - ed->linepos;
|
|
|
|
ed->refresh = 1;
|
|
adjust(ed);
|
|
}
|
|
|
|
static void undo(struct editor *ed) {
|
|
if (!ed->undo) return;
|
|
moveto(ed, ed->undo->pos, 0);
|
|
replace(ed, ed->undo->pos, ed->undo->inserted, ed->undo->undobuf, ed->undo->erased, 0);
|
|
ed->undo = ed->undo->prev;
|
|
if (!ed->undo) ed->dirty = 0;
|
|
ed->anchor = -1;
|
|
ed->lastcol = ed->col;
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
static void redo(struct editor *ed) {
|
|
if (ed->undo) {
|
|
if (!ed->undo->next) return;
|
|
ed->undo = ed->undo->next;
|
|
} else {
|
|
if (!ed->undohead) return;
|
|
ed->undo = ed->undohead;
|
|
}
|
|
replace(ed, ed->undo->pos, ed->undo->erased, ed->undo->redobuf, ed->undo->inserted, 0);
|
|
moveto(ed, ed->undo->pos, 0);
|
|
ed->dirty = 1;
|
|
ed->anchor = -1;
|
|
ed->lastcol = ed->col;
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
//
|
|
// Clipboard
|
|
//
|
|
|
|
static void copy_selection(struct editor *ed) {
|
|
int selstart, selend;
|
|
|
|
if (!get_selection(ed, &selstart, &selend)) return;
|
|
ed->env->clipsize = selend - selstart;
|
|
ed->env->clipboard = (unsigned char *) realloc(ed->env->clipboard, ed->env->clipsize);
|
|
if (!ed->env->clipboard) return;
|
|
copy(ed, ed->env->clipboard, selstart, ed->env->clipsize);
|
|
select_toggle(ed);
|
|
}
|
|
|
|
static void cut_selection(struct editor *ed) {
|
|
copy_selection(ed);
|
|
erase_selection(ed);
|
|
select_toggle(ed);
|
|
}
|
|
|
|
static void paste_selection(struct editor *ed) {
|
|
erase_selection(ed);
|
|
insert(ed, ed->linepos + ed->col, ed->env->clipboard, ed->env->clipsize);
|
|
moveto(ed, ed->linepos + ed->col + ed->env->clipsize, 0);
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
//
|
|
// Editor Commands
|
|
//
|
|
|
|
static void open_editor(struct editor *ed) {
|
|
int rc;
|
|
char *filename;
|
|
struct env *env = ed->env;
|
|
|
|
if (!prompt(ed, "Open file: ", 1)) {
|
|
ed->refresh = 1;
|
|
return;
|
|
}
|
|
filename = (char*) ed->env->linebuf;
|
|
|
|
ed = find_editor(ed->env, filename);
|
|
if (ed) {
|
|
env->current = ed;
|
|
} else {
|
|
ed = create_editor(env);
|
|
rc = load_file(ed, filename);
|
|
if (rc < 0) {
|
|
display_message(ed, "Error %d opening %s (%s)", errno, filename, strerror(errno));
|
|
sleep(5);
|
|
delete_editor(ed);
|
|
ed = env->current;
|
|
}
|
|
}
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
static void new_editor(struct editor *ed) {
|
|
ed = create_editor(ed->env);
|
|
new_file(ed, "");
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
static void read_from_stdin(struct editor *ed) {
|
|
char buffer[512];
|
|
int n, pos;
|
|
|
|
pos = 0;
|
|
while ((n = fread(buffer, 1, sizeof(buffer), stdin)) > 0) {
|
|
insert(ed, pos, (unsigned char*) buffer, n);
|
|
pos += n;
|
|
}
|
|
strcpy(ed->filename, "<stdin>");
|
|
ed->newfile = 1;
|
|
ed->dirty = 0;
|
|
}
|
|
|
|
static void save_editor(struct editor *ed) {
|
|
int rc;
|
|
|
|
if (!ed->dirty && !ed->newfile) return;
|
|
|
|
if (ed->newfile) {
|
|
if (!prompt(ed, "Save as: ", 1)) {
|
|
ed->refresh = 1;
|
|
return;
|
|
}
|
|
|
|
if (access((const char*) ed->env->linebuf, F_OK) == 0) {
|
|
display_message(ed, "Overwrite %s (y/n)? ", ed->env->linebuf);
|
|
if (!ask()) {
|
|
ed->refresh = 1;
|
|
return;
|
|
}
|
|
}
|
|
strcpy(ed->filename, (const char*) ed->env->linebuf);
|
|
ed->newfile = 0;
|
|
}
|
|
|
|
rc = save_file(ed);
|
|
if (rc < 0) {
|
|
display_message(ed, "Error %d saving document (%s)", errno, strerror(errno));
|
|
sleep(5);
|
|
}
|
|
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
static void close_editor(struct editor *ed) {
|
|
struct env *env = ed->env;
|
|
|
|
if (ed->dirty) {
|
|
display_message(ed, "Close %s without saving changes (y/n)? ", ed->filename);
|
|
if (!ask()) {
|
|
ed->refresh = 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
delete_editor(ed);
|
|
|
|
ed = env->current;
|
|
if (!ed) {
|
|
ed = create_editor(env);
|
|
new_file(ed, "");
|
|
}
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
static void pipe_command(struct editor *ed) {
|
|
#ifdef __rtems__
|
|
display_message(ed, "Not supported");
|
|
sleep(3);
|
|
#else
|
|
FILE *f;
|
|
char buffer[512];
|
|
int n;
|
|
int pos;
|
|
|
|
if (!prompt(ed, "Command: ", 1)) {
|
|
ed->refresh = 1;
|
|
return;
|
|
}
|
|
|
|
#ifdef SANOS
|
|
f = popen(ed->env->linebuf, "r2");
|
|
#else
|
|
f = popen(ed->env->linebuf, "r");
|
|
#endif
|
|
if (!f) {
|
|
display_message(ed, "Error %d running command (%s)", errno, strerror(errno));
|
|
sleep(5);
|
|
} else {
|
|
erase_selection(ed);
|
|
pos = ed->linepos + ed->col;
|
|
while ((n = fread(buffer, 1, sizeof(buffer), f)) > 0) {
|
|
insert(ed, pos, buffer, n);
|
|
pos += n;
|
|
}
|
|
moveto(ed, pos, 0);
|
|
pclose(f);
|
|
}
|
|
ed->refresh = 1;
|
|
#endif
|
|
}
|
|
|
|
static void find_text(struct editor *ed, int next) {
|
|
int slen;
|
|
|
|
if (!next) {
|
|
if (!prompt(ed, "Find: ", 1)) {
|
|
ed->refresh = 1;
|
|
return;
|
|
}
|
|
if (ed->env->search) free(ed->env->search);
|
|
ed->env->search = (unsigned char*) strdup((const char*) ed->env->linebuf);
|
|
}
|
|
|
|
if (!ed->env->search) return;
|
|
slen = strlen((const char*) ed->env->search);
|
|
if (slen > 0) {
|
|
unsigned char *match;
|
|
|
|
close_gap(ed);
|
|
match = (unsigned char*) strstr((char*) ed->start + ed->linepos + ed->col, (char*) ed->env->search);
|
|
if (match != NULL) {
|
|
int pos = match - ed->start;
|
|
ed->anchor = pos;
|
|
moveto(ed, pos + slen, 1);
|
|
} else {
|
|
outch('\007');
|
|
}
|
|
}
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
static void goto_line(struct editor *ed) {
|
|
int lineno, l, pos;
|
|
|
|
ed->anchor = -1;
|
|
if (prompt(ed, "Goto line: ", 1)) {
|
|
lineno = atoi((char*) ed->env->linebuf);
|
|
if (lineno > 0) {
|
|
pos = 0;
|
|
for (l = 0; l < lineno - 1; l++) {
|
|
pos = next_line(ed, pos);
|
|
if (pos < 0) break;
|
|
}
|
|
} else {
|
|
pos = -1;
|
|
}
|
|
|
|
if (pos >= 0) {
|
|
moveto(ed, pos, 1);
|
|
} else {
|
|
outch('\007');
|
|
}
|
|
}
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
static struct editor *next_file(struct editor *ed) {
|
|
ed = ed->env->current = ed->next;
|
|
ed->refresh = 1;
|
|
return ed;
|
|
}
|
|
|
|
static void jump_to_editor(struct editor *ed) {
|
|
struct env *env = ed->env;
|
|
char filename[FILENAME_MAX];
|
|
int lineno = 0;
|
|
|
|
if (!get_selected_text(ed, filename, FILENAME_MAX)) {
|
|
int pos = ed->linepos + ed->col;
|
|
char *p = filename;
|
|
int left = FILENAME_MAX - 1;
|
|
while (left > 0) {
|
|
int ch = get(ed, pos);
|
|
if (ch < 0) break;
|
|
if (strchr("!@\"'#%&()[]{}*?+:;\r\n\t ", ch)) break;
|
|
*p++ = ch;
|
|
left--;
|
|
pos++;
|
|
}
|
|
*p = 0;
|
|
|
|
if (get(ed, pos) == ':') {
|
|
pos++;
|
|
for (;;) {
|
|
int ch = get(ed, pos);
|
|
if (ch < 0) break;
|
|
if (ch >= '0' && ch <= '9') {
|
|
lineno = lineno * 10 + (ch - '0');
|
|
} else {
|
|
break;
|
|
}
|
|
pos++;
|
|
}
|
|
}
|
|
}
|
|
if (!*filename) return;
|
|
|
|
ed = find_editor(env, filename);
|
|
if (ed) {
|
|
env->current = ed;
|
|
} else {
|
|
ed = create_editor(env);
|
|
if (load_file(ed, filename) < 0) {
|
|
outch('\007');
|
|
delete_editor(ed);
|
|
ed = env->current;
|
|
}
|
|
}
|
|
|
|
if (lineno > 0) {
|
|
int pos = 0;
|
|
while (--lineno > 0) {
|
|
pos = next_line(ed, pos);
|
|
if (pos < 0) break;
|
|
}
|
|
if (pos >= 0) moveto(ed, pos, 1);
|
|
}
|
|
|
|
ed->refresh = 1;
|
|
}
|
|
|
|
static void redraw_screen(struct editor *ed) {
|
|
get_console_size(ed->env);
|
|
draw_screen(ed);
|
|
}
|
|
|
|
static int quit(struct env *env) {
|
|
struct editor *ed = env->current;
|
|
struct editor *start = ed;
|
|
|
|
do {
|
|
if (ed->dirty) {
|
|
display_message(ed, "Close %s without saving changes (y/n)? ", ed->filename);
|
|
if (!ask()) return 0;
|
|
}
|
|
ed = ed->next;
|
|
} while (ed != start);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void help(struct editor *ed) {
|
|
gotoxy(0, 0);
|
|
clear_screen();
|
|
outstr("Editor Command Summary\r\n");
|
|
outstr("======================\r\n\r\n");
|
|
outstr("<up> Move one line up (*) Ctrl+N New editor\r\n");
|
|
outstr("<down> Move one line down (*) Ctrl+O Open file\r\n");
|
|
outstr("<left> Move one character left (*) Ctrl+S Save file\r\n");
|
|
outstr("<right> Move one character right (*) Ctrl+W Close file\r\n");
|
|
outstr("<pgup> Move one page up (*) Ctrl+Q Quit\r\n");
|
|
outstr("<pgdn> Move one page down (*) Ctrl+P Pipe command\r\n");
|
|
outstr("Ctrl+<left> Move to previous word (*) Ctrl+A Select all\r\n");
|
|
outstr("Ctrl+<right> Move to next word (*) Ctrl+C Copy selection to clipboard\r\n");
|
|
outstr("<home> Move to start of line (*) Ctrl+X Cut selection to clipboard\r\n");
|
|
outstr("<end> Move to end of line (*) Ctrl+V Paste from clipboard\r\n");
|
|
outstr("Ctrl+<home> Move to start of file (*) Ctrl+Z Undo\r\n");
|
|
outstr("Ctrl+<end> Move to end of file (*) Ctrl+R Redo\r\n");
|
|
outstr("<backspace> Delete previous character Ctrl+F Find text\r\n");
|
|
outstr("<delete> Delete current character Ctrl+G Find next\r\n");
|
|
outstr("Ctrl+<tab> Next editor Ctrl+L Goto line\r\n");
|
|
outstr("<tab> Indent selection F1 Help\r\n");
|
|
outstr("Shift+<tab> Unindent selection F2 Select toggle\r\n");
|
|
outstr(" (*) Extends selection, F2 toggles. F3 Navigate to file\r\n");
|
|
outstr(" F4 Copy selection to clipboard\r\n");
|
|
outstr(" Ctrl-Q/S may not work over F5 Redraw screen\r\n");
|
|
outstr(" serial links, use funcions keys F9 Save file\r\n");
|
|
outstr(" F10 Quit\r\n");
|
|
outstr("Press any key to continue...");
|
|
fflush(stdout);
|
|
|
|
getkey();
|
|
draw_screen(ed);
|
|
draw_full_statusline(ed);
|
|
}
|
|
|
|
//
|
|
// Editor
|
|
//
|
|
|
|
static void edit(struct editor *ed) {
|
|
int done = 0;
|
|
int key;
|
|
|
|
ed->refresh = 1;
|
|
while (!done) {
|
|
if (ed->refresh) {
|
|
draw_screen(ed);
|
|
draw_full_statusline(ed);
|
|
ed->refresh = 0;
|
|
ed->lineupdate = 0;
|
|
} else if (ed->lineupdate) {
|
|
update_line(ed);
|
|
ed->lineupdate = 0;
|
|
draw_statusline(ed);
|
|
} else {
|
|
draw_statusline(ed);
|
|
}
|
|
|
|
position_cursor(ed);
|
|
fflush(stdout);
|
|
key = getkey();
|
|
|
|
if (key >= ' ' && key <= 0x7F) {
|
|
#ifdef LESS
|
|
switch (key) {
|
|
case 'q': done = 1; break;
|
|
case '/': find_text(ed, 0); break;
|
|
}
|
|
#else
|
|
insert_char(ed, (unsigned char) key);
|
|
#endif
|
|
} else {
|
|
switch (key) {
|
|
case KEY_F1: help(ed); break;
|
|
case KEY_F2: select_toggle(ed); break;
|
|
case KEY_F3: jump_to_editor(ed); ed = ed->env->current; break;
|
|
case KEY_F4: copy_selection(ed); break;
|
|
case KEY_F5: redraw_screen(ed); break;
|
|
case KEY_F9: save_editor(ed); break;
|
|
case KEY_F10: done = 1; break;
|
|
|
|
#if defined(__linux__) || defined(__rtems__)
|
|
case ctrl('y'): help(ed); break;
|
|
case ctrl('t'): top(ed, 0); break;
|
|
case ctrl('b'): bottom(ed, 0); break;
|
|
#endif
|
|
|
|
case KEY_UP: up(ed, ed->selecting); break;
|
|
case KEY_DOWN: down(ed, ed->selecting); break;
|
|
case KEY_LEFT: left(ed, ed->selecting); break;
|
|
case KEY_RIGHT: right(ed, ed->selecting); break;
|
|
case KEY_HOME: home(ed, ed->selecting); break;
|
|
case KEY_END: end(ed, ed->selecting); break;
|
|
case KEY_PGUP: pageup(ed, ed->selecting); break;
|
|
case KEY_PGDN: pagedown(ed, ed->selecting); break;
|
|
|
|
case KEY_CTRL_RIGHT: wordright(ed, ed->selecting); break;
|
|
case KEY_CTRL_LEFT: wordleft(ed, ed->selecting); break;
|
|
case KEY_CTRL_HOME: top(ed, ed->selecting); break;
|
|
case KEY_CTRL_END: bottom(ed, ed->selecting); break;
|
|
|
|
#if SHIFT_SELECT
|
|
case KEY_SHIFT_UP: up(ed, 1); break;
|
|
case KEY_SHIFT_DOWN: down(ed, 1); break;
|
|
case KEY_SHIFT_LEFT: left(ed, 1); break;
|
|
case KEY_SHIFT_RIGHT: right(ed, 1); break;
|
|
case KEY_SHIFT_PGUP: pageup(ed, 1); break;
|
|
case KEY_SHIFT_PGDN: pagedown(ed, 1); break;
|
|
case KEY_SHIFT_HOME: home(ed, 1); break;
|
|
case KEY_SHIFT_END: end(ed, 1); break;
|
|
|
|
case KEY_SHIFT_CTRL_RIGHT: wordright(ed, 1); break;
|
|
case KEY_SHIFT_CTRL_LEFT: wordleft(ed, 1); break;
|
|
case KEY_SHIFT_CTRL_HOME: top(ed, 1); break;
|
|
case KEY_SHIFT_CTRL_END: bottom(ed, 1); break;
|
|
#endif
|
|
|
|
case KEY_CTRL_TAB: ed = next_file(ed); break;
|
|
|
|
case ctrl('e'): select_toggle(ed); break;
|
|
case ctrl('a'): select_all(ed); break;
|
|
case ctrl('c'): copy_selection(ed); break;
|
|
case ctrl('f'): find_text(ed, 0); break;
|
|
case ctrl('l'): goto_line(ed); break;
|
|
case ctrl('g'): find_text(ed, 1); break;
|
|
case ctrl('q'): done = 1; break;
|
|
#ifdef LESS
|
|
case KEY_ESC: done = 1; break;
|
|
#else
|
|
case KEY_TAB: indent(ed, (unsigned char*) INDENT); break;
|
|
case KEY_SHIFT_TAB: unindent(ed, (unsigned char*) INDENT); break;
|
|
|
|
case KEY_ENTER: newline(ed); break;
|
|
case KEY_BACKSPACE: backspace(ed); break;
|
|
case KEY_DEL: del(ed); break;
|
|
case ctrl('x'): cut_selection(ed); break;
|
|
case ctrl('z'): undo(ed); break;
|
|
case ctrl('r'): redo(ed); break;
|
|
case ctrl('v'): paste_selection(ed); break;
|
|
case ctrl('o'): open_editor(ed); ed = ed->env->current; break;
|
|
case ctrl('n'): new_editor(ed); ed = ed->env->current; break;
|
|
case ctrl('s'): save_editor(ed); break;
|
|
case ctrl('p'): pipe_command(ed); break;
|
|
#endif
|
|
#if defined(__rtems__)
|
|
/*
|
|
* Coverity spotted this as using ed after free() so changing
|
|
* the order of the statements.
|
|
*/
|
|
case ctrl('w'): ed = ed->env->current; close_editor(ed); break;
|
|
#else
|
|
case ctrl('w'): close_editor(ed); ed = ed->env->current; break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// main
|
|
//
|
|
static int rtems_shell_main_edit(int argc, char *argv[])
|
|
{
|
|
struct env env;
|
|
int rc;
|
|
int i;
|
|
sigset_t blocked_sigmask, orig_sigmask;
|
|
#if defined(__linux__)
|
|
struct termios tio;
|
|
#endif
|
|
#if defined(__linux__) || defined(__rtems__)
|
|
struct termios orig_tio;
|
|
#endif
|
|
#ifdef SANOS
|
|
struct term *term;
|
|
#endif
|
|
|
|
memset(&env, 0, sizeof(env));
|
|
for (i = 1; i < argc; i++) {
|
|
struct editor *ed = create_editor(&env);
|
|
rc = load_file(ed, argv[i]);
|
|
if (rc < 0 && errno == ENOENT) rc = new_file(ed, argv[i]);
|
|
if (rc < 0) {
|
|
perror(argv[i]);
|
|
return 0;
|
|
}
|
|
}
|
|
if (env.current == NULL) {
|
|
struct editor *ed = create_editor(&env);
|
|
if (isatty(fileno(stdin))) {
|
|
new_file(ed, "");
|
|
} else {
|
|
read_from_stdin(ed);
|
|
}
|
|
}
|
|
env.current = env.current->next;
|
|
|
|
#ifdef SANOS
|
|
term = gettib()->proc->term;
|
|
if (fdin != term->ttyin) dup2(term->ttyin, fdin);
|
|
if (fdout != term->ttyout) dup2(term->ttyout, fdout);
|
|
#elif !defined(__rtems__)
|
|
if (!isatty(fileno(stdin))) {
|
|
if (!freopen("/dev/tty", "r", stdin)) perror("/dev/tty");
|
|
}
|
|
#endif
|
|
|
|
setvbuf(stdout, NULL, 0, 8192);
|
|
|
|
#if defined(__linux__) || defined(__rtems__)
|
|
(void) tcgetattr(0, &orig_tio);
|
|
#if !defined(__rtems__)
|
|
cfmakeraw(&tio);
|
|
tcsetattr(0, TCSANOW, &tio);
|
|
#endif
|
|
if (getenv("TERM") && strcmp(getenv("TERM"), "linux") == 0) {
|
|
linux_console = 1;
|
|
} else {
|
|
outstr(CLRSCR);
|
|
outstr("\033[3 q"); // xterm
|
|
outstr("\033]50;CursorShape=2\a"); // KDE
|
|
}
|
|
#endif
|
|
|
|
get_console_size(&env);
|
|
|
|
sigemptyset(&blocked_sigmask);
|
|
sigaddset(&blocked_sigmask, SIGINT);
|
|
sigaddset(&blocked_sigmask, SIGTSTP);
|
|
sigaddset(&blocked_sigmask, SIGABRT);
|
|
sigprocmask(SIG_BLOCK, &blocked_sigmask, &orig_sigmask);
|
|
|
|
for (;;) {
|
|
if (!env.current) break;
|
|
edit(env.current);
|
|
if (quit(&env)) break;
|
|
}
|
|
|
|
gotoxy(0, env.lines + 1);
|
|
outstr(RESET_COLOR CLREOL);
|
|
#if defined(__linux__) || defined(__rtems__)
|
|
tcsetattr(0, TCSANOW, &orig_tio);
|
|
#endif
|
|
|
|
while (env.current) delete_editor(env.current);
|
|
|
|
if (env.clipboard) free(env.clipboard);
|
|
if (env.search) free(env.search);
|
|
if (env.linebuf) free(env.linebuf);
|
|
|
|
setbuf(stdout, NULL);
|
|
sigprocmask(SIG_SETMASK, &orig_sigmask, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
rtems_shell_cmd_t rtems_shell_EDIT_Command = {
|
|
"edit", /* name */
|
|
"edit [file ...]", /* usage */
|
|
"files", /* topic */
|
|
rtems_shell_main_edit, /* command */
|
|
NULL, /* alias */
|
|
NULL /* next */
|
|
};
|