📄 edit.c
字号:
/* editor low level data handling and cursor fundamentals.
Copyright (C) 1996, 1997 the Free Software Foundation
Authors: 1996, 1997 Paul Sheer
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#define _EDIT_C THIS_IS
#include <config.h>
#if defined(OS2_NT)
# include <io.h>
# include <fcntl.h>
# define CR_LF_TRANSLATION
#endif
#include "edit.h"
#ifdef SCO_FLAVOR
# include <sys/timeb.h>
#endif /* SCO_FLAVOR */
#include <time.h> /* for ctime() */
/*
*
* here's a quick sketch of the layout: (don't run this through indent.)
*
* (b1 is buffers1 and b2 is buffers2)
*
* |
* \0\0\0\0\0m e _ f i l e . \nf i n . \n|T h i s _ i s _ s o\0\0\0\0\0\0\0\0\0
* ______________________________________|______________________________________
* |
* ... | b2[2] | b2[1] | b2[0] | b1[0] | b1[1] | b1[2] | ...
* |-> |-> |-> |-> |-> |-> |
* |
* _<------------------------->|<----------------->_
* WEdit->curs2 | WEdit->curs1
* ^ | ^
* | ^|^ |
* cursor ||| cursor
* |||
* file end|||file beginning
* |
* |
*
* _
* This_is_some_file
* fin.
*
*
*/
/*
returns a byte from any location in the file.
Returns '\n' if out of bounds.
*/
int edit_get_byte (WEdit * edit, long byte_index)
{
unsigned long p;
if (byte_index >= (edit->curs1 + edit->curs2) || byte_index < 0)
return '\n';
if (byte_index >= edit->curs1) {
p = edit->curs1 + edit->curs2 - byte_index - 1;
return edit->buffers2[p >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - (p & M_EDIT_BUF_SIZE) - 1];
} else {
return edit->buffers1[byte_index >> S_EDIT_BUF_SIZE][byte_index & M_EDIT_BUF_SIZE];
}
}
char *edit_get_buffer_as_text (WEdit * e)
{
int l, i;
char *t;
l = e->curs1 + e->curs2;
t = CMalloc (l + 1);
for (i = 0; i < l; i++)
t[i] = edit_get_byte (e, i);
t[l] = 0;
return t;
}
/* Initialisation routines */
/* returns 1 on error */
/* loads file OR text into buffers. Only one must be none-NULL. */
/* cursor set to start of file */
int init_dynamic_edit_buffers (WEdit * edit, const char *filename, const char *text)
{
#if defined CR_LF_TRANSLATION
/* Variables needed for safe handling of Translation from Microsoft CR/LF EOL to
Unix Style LF EOL - Franco */
long bytes_wanted,bytes_read,bytes_missing;
char *p;
#endif
long buf;
int j, file = 0, buf2;
for (j = 0; j <= MAXBUFF; j++) {
edit->buffers1[j] = NULL;
edit->buffers2[j] = NULL;
}
if (filename)
if ((file = open ((char *) filename, O_RDONLY | MY_O_TEXT)) == -1) {
/* The file-name is printed after the ':' */
edit_error_dialog (_(" Error "), get_sys_error (catstrs (_(" Fail trying to open file for reading: "), filename, " ", 0)));
return 1;
}
edit->curs2 = edit->last_byte;
buf2 = edit->curs2 >> S_EDIT_BUF_SIZE;
edit->buffers2[buf2] = CMalloc (EDIT_BUF_SIZE);
/*
_read returns the number of bytes read,
which may be less than count if there are fewer than count bytes left in the file
or if the file was opened in text mode,
in which case each carriage return杔inefeed (CR-LF) pair is replaced
with a single linefeed character. Only the single linefeed character is counted
in the return value. The replacement does not affect the file pointer.
_eof returns 1 if the current position is end of file, or 0 if it is not.
A return value of -1 indicates an error; in this case, errno is set to EBADF,
which indicates an invalid file handle.
*/
if (filename){
#if defined CR_LF_TRANSLATION
bytes_wanted=edit->curs2 & M_EDIT_BUF_SIZE;
p = (char *) edit->buffers2[buf2] + EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE);
bytes_read = read (file, p , edit->curs2 & M_EDIT_BUF_SIZE);
bytes_missing = bytes_wanted - bytes_read ;
while(bytes_missing ){
p += bytes_read;
bytes_read = read(file,p,bytes_missing);
if(bytes_read <= 0) break;
bytes_missing -= bytes_read ;
}
#else
read (file, (char *) edit->buffers2[buf2] + EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE), edit->curs2 & M_EDIT_BUF_SIZE);
#endif
}
else {
memcpy (edit->buffers2[buf2] + EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE), text, edit->curs2 & M_EDIT_BUF_SIZE);
text += edit->curs2 & M_EDIT_BUF_SIZE;
}
for (buf = buf2 - 1; buf >= 0; buf--) {
edit->buffers2[buf] = CMalloc (EDIT_BUF_SIZE);
if (filename){
#if defined CR_LF_TRANSLATION
bytes_wanted = EDIT_BUF_SIZE;
p = (char *) edit->buffers2[buf];
bytes_read = read (file, p, EDIT_BUF_SIZE);
bytes_missing = bytes_wanted - bytes_read ;
while(bytes_missing ){
p += bytes_read;
bytes_read = read(file,p,bytes_missing);
if(bytes_read <= 0) break;
bytes_missing -= bytes_read ;
}
#else
read (file, (char *) edit->buffers2[buf], EDIT_BUF_SIZE);
#endif
}
else {
memcpy (edit->buffers2[buf], text, EDIT_BUF_SIZE);
text += EDIT_BUF_SIZE;
}
}
edit->curs1 = 0;
if (filename)
close (file);
return 0;
}
/* returns 1 on error */
int edit_load_file (WEdit * edit, const char *filename, const char *text, unsigned long text_size)
{
struct stat s;
int file;
/* VARS for Lastbyte calculation in TEXT mode FRANCO */
#if defined CR_LF_TRANSLATION
char tmp_buf[1024];
long real_size,bytes_read;
#endif
if (text) {
edit->last_byte = text_size;
filename = NULL;
} else {
#if defined(MIDNIGHT) || defined(GTK)
if ((file = open ((char *) filename, O_RDONLY | MY_O_TEXT )) < 0)
{
close(creat((char *) filename, 0666));
if ((file = open ((char *) filename, O_RDONLY | MY_O_TEXT )) < 0) {
edit_error_dialog (_(" Error "), get_sys_error (catstrs (" Fail trying to open the file, ", filename, ", for reading ", 0)));
return 1;
}
edit->delete_file = 1;
}
#else
if ((file = open ((char *) filename, O_RDONLY)) < 0) {
edit_error_dialog (_(" Error "), get_sys_error (catstrs (_(" Fail trying to open file for reading: "), filename, " ", 0)));
return 1;
}
#endif
if (stat ((char *) filename, &s) < 0) {
close (file);
/* The file-name is printed after the ':' */
edit_error_dialog (_(" Error "), get_sys_error (catstrs (_(" Cannot get size/permissions info on file: "), filename, " ", 0)));
return 1;
}
if (S_ISDIR (s.st_mode) || S_ISSOCK (s.st_mode)
|| S_ISFIFO (s.st_mode)) {
close (file);
/* The file-name is printed after the ':' */
edit_error_dialog (_(" Error "), catstrs (_(" Not an ordinary file: "), filename, " ", 0));
return 1;
}
if (s.st_size >= SIZE_LIMIT) {
close (file);
/* The file-name is printed after the ':' */
edit_error_dialog (_(" Error "), catstrs (_(" File is too large: "), \
filename, _(" \n Increase edit.h:MAXBUF and recompile the editor. "), 0));
return 1;
}
/* Lastbyte calculation in TEXT mode FRANCO */
#if defined CR_LF_TRANSLATION
if(file && (!text)){
real_size=0;
tmp_buf[1024]=0;
while((bytes_read = read(file,tmp_buf,1024)) > 0){
real_size += bytes_read;
}
s.st_size = real_size;
}
#endif
close (file);
edit->last_byte = s.st_size;
edit->stat = s;
}
return init_dynamic_edit_buffers (edit, filename, text);
}
#ifdef MIDNIGHT
#define space_width 1
#else
int space_width;
extern int option_long_whitespace;
extern unsigned char per_char[256];
void edit_set_space_width (int s)
{
space_width = s;
}
#endif
/* fills in the edit struct. returns 0 on fail. Pass edit as NULL for this function to do an malloc for you */
WEdit *edit_init (WEdit * edit, int lines, int columns, const char *filename, const char *text, const char *dir, unsigned long text_size)
{
char *f;
#ifndef MIDNIGHT
if (option_long_whitespace)
edit_set_space_width (per_char[' '] * 2);
else
edit_set_space_width (per_char[' ']);
#endif
if (!edit)
edit = malloc (sizeof (WEdit));
if (!edit) {
edit_error_dialog (_(" Error "), _(" Error allocating memory "));
return 0;
}
memset (&(edit->from_here), 0, (unsigned long) &(edit->to_here) - (unsigned long) &(edit->from_here));
#ifndef MIDNIGHT
edit->max_column = columns * FONT_MEAN_WIDTH;
#endif
edit->num_widget_lines = lines;
edit->num_widget_columns = columns;
edit->stat.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
edit->stat.st_uid = getuid ();
edit->stat.st_gid = getgid ();
edit->bracket = -1;
if (!dir)
dir = "";
f = (char *) filename;
if (filename)
f = catstrs (dir, filename, 0);
if (edit_load_file (edit, f, text, text_size)) {
/* edit_load_file already gives an error message */
free (edit);
return 0;
}
edit->force |= REDRAW_PAGE;
if (filename) {
filename = catstrs (dir, filename, 0);
edit_split_filename (edit, (char *) filename);
} else {
edit->filename = strdup ("");
edit->dir = strdup(dir);
}
edit->stack_size = START_STACK_SIZE;
edit->stack_size_mask = START_STACK_SIZE - 1;
edit->undo_stack = malloc ((edit->stack_size + 10) * sizeof (long));
if (!edit->undo_stack) {
edit_error_dialog (_(" Error "), _(" Error allocating memory "));
free (edit);
return 0;
}
edit->total_lines = edit_count_lines (edit, 0, edit->last_byte);
edit_load_syntax (edit, 0, 0);
{
int fg, bg;
edit_get_syntax_color (edit, -1, &fg, &bg);
}
return edit;
}
/* clear the edit struct, freeing everything in it. returns 1 on success */
int edit_clean (WEdit * edit)
{
if (edit) {
int j = 0;
edit_free_syntax_rules (edit);
for (; j <= MAXBUFF; j++) {
if (edit->buffers1[j] != NULL)
free (edit->buffers1[j]);
if (edit->buffers2[j] != NULL)
free (edit->buffers2[j]);
}
if (edit->undo_stack)
free (edit->undo_stack);
if (edit->filename)
free (edit->filename);
if (edit->dir)
free (edit->dir);
/* we don't want to clear the widget */
memset (&(edit->from_here), 0, (unsigned long) &(edit->to_here) - (unsigned long) &(edit->from_here));
return 1;
}
return 0;
}
/* returns 1 on success */
int edit_renew (WEdit * edit)
{
int lines = edit->num_widget_lines;
int columns = edit->num_widget_columns;
char *dir;
if (edit->dir)
dir = strdup (edit->dir);
else
dir = 0;
edit_clean (edit);
if (!edit_init (edit, lines, columns, 0, "", dir, 0))
return 0;
return 1;
}
/* returns 1 on success */
int edit_reload (WEdit * edit, const char *filename, const char *text, const char *dir, unsigned long text_size)
{
int lines = edit->num_widget_lines;
int columns = edit->num_widget_columns;
edit_clean (edit);
if (!edit_init (edit, lines, columns, filename, text, dir, text_size)) {
return 0;
}
return 1;
}
/*
Recording stack for undo:
The following is an implementation of a compressed stack. Identical
pushes are recorded by a negative prefix indicating the number of times the
same char was pushed. This saves space for repeated curs-left or curs-right
delete etc.
eg:
pushed: stored:
a
b a
b -3
b b
c --> -4
c c
c d
c
d
If the stack long int is 0-255 it represents a normal insert (from a backspace),
256-512 is an insert ahead (from a delete), If it is betwen 600 and 700 it is one
of the cursor functions #define'd in edit.h. 1000 through 700'000'000 is to
set edit->mark1 position. 700'000'000 through 1400'000'000 is to set edit->mark2
position.
The only way the curser moves or the buffer is changed is through the routines:
insert, backspace, insert_ahead, delete, and cursor_move.
These record the reverse undo movements onto the stack each time they are
called.
Each key press results in a set of actions (insert; delete ...). So each time
a key is pressed the current position of start_display is pushed as
KEY_PRESS + start_display. Then for undoing, we pop until we get to a number
over KEY_PRESS. We then assign this number less KEY_PRESS to start_display. So undo
tracks scrolling and key actions exactly. (KEY_PRESS is about (2^31) * (2/3) = 1400'000'000)
*/
static int push_action_disabled = 0;
void edit_push_action (WEdit * edit, long c,...)
{
unsigned long sp = edit->stack_pointer;
unsigned long spm1;
long *t;
/* first enlarge the stack if necessary */
if (sp > edit->stack_size - 10) { /* say */
if (option_max_undo < 256)
option_max_undo = 256;
if (edit->stack_size < option_max_undo) {
t = malloc ((edit->stack_size * 2 + 10) * sizeof (long));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -