/*
* WRAP.C
*
* Written on 30-Jul-90 by jim nutt. Changes on 10-Jul-94 by John Dennis.
* Released to the public domain.
*
* Editor for Msged.
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <smapi/compiler.h>
#include "addr.h"
#include "nedit.h"
#include "msged.h"
#include "editmail.h"
#include "memextra.h"
#include "winsys.h"
#include "nshow.h"
#include "unused.h"
#include "readmail.h"
#include "textfile.h"
#include "menu.h"
#include "dialogs.h"
#include "main.h"
#include "specch.h"
#include "screen.h"
#include "version.h"
#include "makemsgn.h"
#include "keys.h"
#include "misc.h"
#include "help.h"
#include "dosmisc.h"
#include "wrap.h"
#include "mctype.h"
static LINE *current; /* current line */
static LINE *pagetop; /* top line of page */
static LINE *msgtop; /* top line of file */
static LINE *udel; /* deleted line buffer */
static LINE *clip; /* for blocks of text */
static char line_buf[255]; /* buffer for the current line */
static int x = 1; /* x coordinate, 1 based */
static int y = 1; /* y coordinate, 1 based */
static int currline = 1; /* current line of msg, 1 based */
static int ed_miny = 1; /* logical min y value */
static int ed_maxy = 1; /* logical max y value */
static int edmaxy; /* real max y value */
static int edminy; /* real min y value */
static int quote_len = 14; /* length to look for quote */
static int done; /* finished editing? */
static int insert = 1; /* insert = on ? */
static int blocking; /* block on? */
static msg *messg; /* message being edited */
/* This function sets the current right and quote margins, taking
into account both the actual window size and the settings that the
user has desired. */
void adapt_margins(void)
{
SW->rm = maxx - 1;
if (SW->rm > SW->orgrm)
{
SW->rm = SW->orgrm;
}
SW->qm = SW->rm - strlen(ST->quotestr);
if (SW->qm > SW->orgqm)
{
SW->qm = SW->orgqm;
}
if (SW->rm < 1)
{
SW->rm = 1;
}
if (SW->qm < 1)
{
SW->qm = 1;
}
}
static void zap_quotes(void)
{
while (current->next != NULL && (current->quote || current->text[0] == 10))
{
delete_line();
}
}
static void rotate(void)
{
rot13 = (rot13 + 1) % 3;
}
static void nada(void)
{
}
char *e_getbind(unsigned int key)
{
unsigned int i = 0;
void (*action) (void);
if (key & 0xff)
{
action = editckeys[key & 0xff];
}
else
{
action = editakeys[(key >> 8) & 0xff];
}
while ((editcmds[i].label != NULL) && (action != editcmds[i].action))
{
i++;
}
return editcmds[i].label;
}
char *e_getlabels(int i)
{
return editcmds[i].label;
}
void e_assignkey(unsigned int key, char *label)
{
unsigned int i = 0;
while ((editcmds[i].label != NULL) && (strncmp(editcmds[i].label, label, strlen(editcmds[i].label)) != 0))
{
i++;
}
if (editcmds[i].label != NULL)
{
if (key & 0xff)
{
editckeys[key & 0xff] = editcmds[i].action;
}
else
{
editakeys[(key >> 8) & 0xff] = editcmds[i].action;
}
}
}
LINE *lineins(LINE * cl)
{
LINE *nl;
nl = xcalloc(1, sizeof *nl);
if (cl == NULL)
{
return nl;
}
nl->next = cl;
nl->prev = cl->prev;
cl->prev = nl;
if (nl->prev != NULL)
{
nl->prev->next = nl;
}
return nl;
}
/*
* Functions that are local to this file (excepting wrap and editmsg).
*/
/*
* EdPutLine(); Displays a line, translating the display type.
*/
static void EdPutLine(LINE * line, int y)
{
line->quote = isquote(line->text);
/* check line coords! */
if (y <= ed_maxy)
{
PutLine(line, y + edminy);
}
else
{
ed_error("EdPutLine", "illegal coordinates - y = %d!", y);
}
}
/*
* ScrollDown(); Scrolls down one line.
*/
void ScrollDown(int y1, int y2)
{
if (y1 <= ed_maxy && y2 <= ed_maxy)
{
WndScroll(0, y1 + edminy, maxx - 1, y2 + edminy, 0);
}
else
{
ed_error("ScrollDown", "illegal coordinates - y1 = %d!, y2 = %d", y1, y2);
}
}
/*
* ScrollUp(); Scrolls up one line.
*/
void ScrollUp(int y1, int y2)
{
if (y1 <= ed_maxy && y2 <= ed_maxy)
{
WndScroll(0, y1 + edminy, maxx - 1, y2 + edminy, 1);
}
else
{
ed_error("ScrollUp", "illegal coordinates - y1 = %d!, y2 = %d", y1, y2);
}
}
/*
* GotoXY(); Goes to a position on the screen.
*/
static void GotoXY(int x, int y)
{
if (x >= 1 && x <= maxx && y <= ed_maxy && y >= ed_miny)
{
WndGotoXY(x - 1, y + edminy);
}
}
/*
* RedrawPage(); Redraws the page, starting from the line passed and
* the y coord.
*/
void RedrawPage(LINE * start, int wherey)
{
LINE *cur = start;
static LINE blank = {"", 0, 0, 0, 0, 0, NULL, NULL};
int cury = wherey;
if (start != NULL && wherey <= ed_maxy)
{
TTBeginOutput();
while (cur != NULL && cury <= ed_maxy)
{
EdPutLine(cur, cury);
cur = cur->next;
cury++;
}
/*
* If we have lines left on the screen, clear them - simple to
* just write over them with blank lines.
*/
if (cury <= ed_maxy)
{
while (cury <= ed_maxy)
{
EdPutLine(&blank, cury);
cury++;
}
}
TTEndOutput();
}
}
/*
* InsertLine(); Inserts a line AFTER the current line.
*/
LINE *InsertLine(LINE * current)
{
LINE *nl;
nl = xcalloc(1, sizeof *nl);
nl->next = current->next;
nl->prev = current;
current->next = nl;
if (nl->next)
{
nl->next->prev = nl;
}
return nl;
}
/*
* UnmarkLineBuf(); Copies the line_buf to the current line structure,
*/
void UnmarkLineBuf(void)
{
release(current->text);
current->text = xstrdup(line_buf);
}
/*
* SetLineBuf(); Copies the current line text to the line_buf.
*/
void SetLineBuf(void)
{
memset(line_buf, 0, sizeof line_buf);
if (current->text)
{
strncpy(line_buf, current->text, sizeof line_buf);
line_buf[(sizeof line_buf) - 1] = '\0';
}
}
/*
* iswhspace(); Determines if this is a white space.
*/
int iswhspace(char c)
{
if (c == ' ' || c == '\t')
{
return 1;
}
return 0;
}
/*
* trailspace(); Determines if the eol has a trailing space.
*/
int trailspace(char *text)
{
if (text == NULL || strlen(text) == 0)
{
return 1;
}
if (*(text + strlen(text) - 1) == ' ' || *(text + strlen(text) - 1) == '\n')
{
return 1;
}
return 0;
}
/*
* isquote(); Determines if the line is a quote.
*/
int isquote(char *text)
{
char *s;
if (text == NULL || strlen(text) == 0)
{
return 0;
}
s = text;
while (*s && (s - text) < quote_len)
{
if (*s == '>')
{
return 1;
}
if (*s == '<' || *s == '-' || *s == '=')
{
return 0;
}
s++;
}
return 0;
}
/*
* FindQuoteEnd(); Finds and returns the end of the quote_string on
* the past line of text.
*/
char *FindQuoteEnd(char *txt)
{
char *s, *c;
if (txt == NULL || strlen(txt) == 0)
{
return txt;
}
if (strlen(txt) <= quote_len)
{
s = txt + strlen(txt) - 1;
}
else
{
s = txt + quote_len;
}
if ((c = strchr(txt, '<')) != NULL)
{
/*
* Check for the special case of '<sigh>' or some such similar
* text stuffing up the quoting process.
*/
/* mods by PE 1995-04-26 */
if (c < s)
{
if (c > txt)
{
s = c - 1;
}
else if (c == txt)
{
return (txt);
}
}
}
while (s > txt && *s != '>')
{
s--;
}
if (s == txt && *s != '>')
{
return txt;
}
if (*s == '>' && *(s + 1))
{
/* go past the '>' character */
/*
* We only want to increment two characters - this saves trouble
* when we encounter indents in the quotes. We could strip
* spaces when finding indents, but why bother?
*/
s++;
if (*s == ' ' && *(s + 1))
{
s++;
}
}
return s;
}
/*
* GetWrapPoint(); Finds the best point to break a line.
*/
char *GetWrapPoint(char *text, int rm)
{
char *s, *sp;
int slen;
if (text == NULL || strlen(text) == 0)
{
return NULL;
}
/* find the point to look for a wrap and save the spot */
slen = strlen(text);
if (slen > rm)
{
sp = text + (rm - 1);
}
else
{
sp = text + (slen - 1);
}
s = sp;
if (*s == '\0' || *s == '\n' || *s == '\r')
{
return NULL;
}
if (!iswhspace(*s))
{
/* search backward for beginning of word */
while (*s && !iswhspace(*s))
{
if (s - text < 2)
{
/* Can't wrap any further, so split at EOL. */
if (sp > text)
{
s = sp - 1;
}
else
{
s = sp;
}
break;
}
s--;
}
s++;
}
else
{
/* search forward for the beginning of next word */
while (*s && iswhspace(*s))
{
if (s - text < rm / 2)
{
break;
}
if (s - text < rm) /* prevent a line with too much whitespaces */
{
s++;
}
else
{
break;
}
}
if (*s == '\0' || *s == '\n' || *s == '\r')
{
return NULL;
}
}
return s;
}
/*
* wrap(); this is a very big procedure; wraps a line and following ones.
*/
int wrap(LINE * cl, int x, int y, int rm)
{
LINE *l, *nl;
char *s, *t, *tl, ch;
int wrapped_line = 0;
int slen, space;
unused(x);
unused(y);
l = cl;
/* stop when no more lines to process */
while (l != NULL)
{
/* get the next line for later use */
nl = l->next;
if (l->text == NULL || strlen(l->text) < rm)
{
/* we may want to copy stuff from the next line to this
one */
if (nl == NULL || nl->text == NULL)
{
/* nothing we can do */
return wrapped_line;
}
/*
* A '\n' terminates a para, so if we have one on this
* line, we want to stop wrapping and return.
*/
if (l->text != NULL && strchr(l->text, '\n') != NULL)
{
return wrapped_line;
}
/* Get the length of the current line. */
s = FindQuoteEnd(nl->text);
if (l->text != NULL)
{
slen = strlen(l->text);
}
else
{
slen = 0;
}
/*
* If the next line can fit on this line, then we may
* as well simply copy the entire line and delete the
* next line, otherwise we copy only what we have space
* for.
*/
space = rm - slen;
if (space > strlen(s) ||
(space == strlen(s) &&
((l->text != NULL && trailspace(l->text) != 0) ||
iswhspace(*s))))
{
/* then we move the entire line up */
tl = xcalloc(1, strlen(s) + slen + 2);
/* Copy the text to the new memory. */
if (l->text)
{
strcpy(tl, l->text);
}
else
{
strcpy(tl, "");
}
if (trailspace(tl) == 0 && !iswhspace(*s))
{
if (*s != '\0' && *s != '\n')
{
strcat(tl, " ");
}
}
/* If the line that will be deleted had the cursor,
mark the line that it has been merged with as
having the cursor. */
if (nl->cursor_position)
{
if (nl->cursor_position > (s - nl->text))
{
l->cursor_position = strlen(tl) +
nl->cursor_position - (s - nl->text);
}
else
{
l->cursor_position = strlen(tl) + 1;
}
}
strcat(tl, s);
/* Delete the old line. */
l->next = nl->next;
if (l->next)
{
l->next->prev = l;
}
release(nl->text);
release(nl);
release(l->text);
l->text = tl;
wrapped_line = 1;
}
else
{
if (space == 0)
{
return wrapped_line;
}
/* We want to copy some words up to this line; */
t = s + space - 1;
tl = t;
/*
* We start the wrap at the amount of space left on the
* previous line. If that spot is on a word, then we
* move to the beginning of that word and wrap anything
* before that. If the word goes to the beginning of the
* line, then it is too big to wrap (we only wrap whole
* words).
*/
if (!iswhspace(*t))
{
while (t > s && !iswhspace(*t))
{
t--;
}
if (t == s)
{
/* word too big */
return wrapped_line;
}
else
{
t++;
}
}
else
{
while (*t && iswhspace(*t))
{
t++;
}
if (*t == '\0' || *t == '\n')
{
t = tl;
}
}
/* Save current position. */
ch = *t;
*t = '\0';
tl = xcalloc(1, strlen(s) + slen + 2);
/* Copy stuff to be wrapped to the new memory. */
if (l->text)
{
strcpy(tl, l->text);
}
else
{
strcpy(tl, "");
}
if (trailspace(tl) == 0 && !iswhspace(*s))
{
strcat(tl, " ");
}
/* Adapt the cursor position */
if (nl->cursor_position)
{
if (nl->cursor_position > (s - nl->text))
{
if (nl->cursor_position - (s - nl->text) <=
strlen(s))
{
l->cursor_position = strlen(tl) +
nl->cursor_position - (s - nl->text);
nl->cursor_position = 0;
}
else
{
nl->cursor_position -= strlen(s);
}
}
else
{
l->cursor_position = strlen(tl) + 1;
nl->cursor_position = 0;
}
}
strcat(tl, s);
/*
* Assign new memory and move what is left on next line
* to beginning of that line (we don't bother to
* reallocate it).
*/
release(l->text);
l->text = tl;
wrapped_line = 1;
*t = ch;
memmove(s, t, strlen(t) + 1);
}
l = l->next;
}
else
{
/*
* We want to wrap stuff on current line to the next line
* because current line is overflowing.
*/
t = GetWrapPoint(l->text, rm);
if (t == NULL)
{
return wrapped_line;
}
if (nl != NULL && nl->text != NULL && strchr(l->text, '\n') == NULL && l->quote == nl->quote)
{
s = xcalloc(1, strlen(t) + strlen(nl->text) + 2);
tl = FindQuoteEnd(nl->text);
if (t == NULL)
{
ed_error("wrap()", "Logic error wrapping to next line!");
}
ch = *tl;
*tl = '\0';
strcpy(s, nl->text);
strcat(s, t);
/* adapt the cursor position */
if (l->cursor_position)
{
if (l->cursor_position > (t - l->text))
{
nl->cursor_position = l->cursor_position -
(t-l->text) + strlen(nl->text);
l->cursor_position = 0;
}
}
else if (nl->cursor_position)
{
if (nl->cursor_position > strlen(nl->text))
{
nl->cursor_position += strlen(t);
}
}
if (trailspace(s) == 0)
{
strcat(s, " ");
}
*tl = ch;
strcat(s, tl);
release(nl->text);
nl->text = s;
*t = '\0';
wrapped_line = 1;
}
else
{
nl = InsertLine(l);
if (cl->quote)
{
nl->quote = 1;
s = FindQuoteEnd(l->text);
ch = *s;
*s = '\0';
tl = xcalloc(1, strlen(t) + strlen(l->text) + 2);
strcpy(tl, l->text);
strcat(tl, t);
*s = ch;
nl->text = tl;
}
else
{
nl->text = xstrdup(t);
s = l->text;
}
/* adapt the cursor position */
if (l->cursor_position)
{
if (l->cursor_position > (t - l->text))
{
nl->cursor_position = l->cursor_position -
(t-l->text) + (s - l->text);
l->cursor_position = 0;
}
}
*t = '\0';
wrapped_line = 1;
}
l = l->next;
}
}
return wrapped_line;
}
/*
* toggle_quote(); Toggles the quote status of the current line.
*/
static void toggle_quote(void)
{
if (current == NULL)
{
return;
}
if (current->quote)
{
current->quote = 0;
}
else
{
current->quote = 1;
}
EdPutLine(current, y);
}
/*
* CheckXvalid(); Checks to see that the current X position is still
* on the line (ie. X is not past EOL).
*/
void CheckXvalid(void)
{
SetLineBuf();
if (current->text == NULL || strlen(current->text) < x)
{
go_eol();
}
}
/*
* WordStart(); Finds the start of the current word at the current X
* position, and then returns the difference between that position and
* the current X position.
*/
int WordStart(void)
{
char *s, *b;
if (*line_buf == 0)
{
return 0;
}
s = line_buf + x - 1;
b = s;
if (iswhspace(*s))
{
return 0;
}
while (s > line_buf && !iswhspace(*s))
{
s--;
}
return (int) (b - s);
}
/*
* insert_char(); Inserts a char at the current position.
*/
static void insert_char(char ch)
{
int slen, wlen;
/* entering these characters would cause problems on Unix */
#if defined(UNIX) && !defined(USE_CURSES)
if ((unsigned char)ch < 32 ||
((unsigned char)ch >= 128 && (unsigned char)ch < (128 + 32)))
{
return;
}
#endif
TTBeginOutput();
/* softcrxlat functionality moved to readmail.c, because it should take
place in the transport charset layer, not in the local charset
layer */
if (insert == 0 && line_buf[x - 1] != '\n')
{
line_buf[x - 1] = ch;
}
else
{
memmove(line_buf + x, line_buf + x - 1, strlen(line_buf + x - 1) + 1);
line_buf[x - 1] = ch;
}
UnmarkLineBuf();
current->templt = 0;
wlen = WordStart();
slen = strlen(current->text);
if (wrap(current, 0, 0, SW->rm) == 1)
{
SetLineBuf();
RedrawPage(current, y);
if (strlen(current->text) < x)
{
if (wlen)
{
x = wlen;
if (current->quote)
{
char *s;
s = FindQuoteEnd(current->text);
if (s && s > current->text)
{
x += (int)(s - current->text);
}
}
if (x > strlen(current->next->text))
{
x = strlen(current->next->text) - 1;
}
}
else
{
x = slen - strlen(current->text) - 1;
}
go_down();
}
}
else
{
EdPutLine(current, y);
}
x++;
SetLineBuf();
TTEndOutput();
}
/*
* delete_character(); Deletes the character at the current X position.
*/
static void delete_character(void)
{
LINE *nl;
current->templt = 0;
TTBeginOutput();
if (*line_buf == 0 || line_buf[0] == '\n')
{
/* Current line is blank, so we delete it. */
if (current->next == NULL)
{
return;
}
current->next->prev = current->prev;
if (current->prev)
{
current->prev->next = current->next;
}
if (msgtop == current)
{
msgtop = current->next;
}
if (pagetop == current)
{
msgtop = current->next;
}
nl = current;
current = nl->next;
release(nl->text);
release(nl);
RedrawPage(current, y);
}
else
{
/* Else we just want to kill the char at x. */
memmove(line_buf + x - 1, line_buf + x, strlen(line_buf + x) + 1);
UnmarkLineBuf();
if (wrap(current, 0, 0, SW->rm) == 1)
{
RedrawPage(current, y);
}
else
{
EdPutLine(current, y);
}
}
SetLineBuf();
TTEndOutput();
}
/*
* backspace; Deletes the char behind the current X pos and moves
* the cursor back one char.
*/
static void backspace(void)
{
TTBeginOutput();
if (x == 1)
{
if (current->prev == NULL)
{
return;
}
UnmarkLineBuf();
go_up();
go_eol();
delete_character();
}
else
{
x--;
delete_character();
}
EdPutLine(current, y);
TTEndOutput();
}
static void delword(void)
{
char *s;
s = line_buf + x - 1;
while (*s && !m_isspace(*s))
{
s++;
}
while (*s && m_isspace(*s))
{
s++;
}
strcpy(line_buf + x - 1, s);
UnmarkLineBuf();
wrap(current, x, y, SW->rm);
RedrawPage(current, y);
SetLineBuf();
}
static void go_left(void)
{
if (x == 1)
{
if (current->prev)
{
go_up();
go_eol();
}
}
else
{
x--;
}
}
static void go_right(void)
{
if (line_buf[x - 1] == '\0' || line_buf[x - 1] == '\n')
{
if (current->next)
{
go_down();
go_bol();
}
}
else
{
x++;
}
}
static void go_up(void)
{
UnmarkLineBuf();
if (current->prev)
{
current = current->prev;
currline--;
if (y == ed_miny)
{
TTBeginOutput();
pagetop = current;
ScrollDown(1, ed_maxy);
EdPutLine(current, y);
TTEndOutput();
}
else
{
y--;
}
}
CheckXvalid();
}
static void go_down(void)
{
UnmarkLineBuf();
if (current->next)
{
current = current->next;
currline++;
if (y == ed_maxy)
{
TTBeginOutput();
ScrollUp(1, ed_maxy);
EdPutLine(current, y);
TTEndOutput();
}
else
{
y++;
}
}
CheckXvalid();
}
static void go_bol(void)
{
x = 1;
}
static void go_eol(void)
{
x = strlen(line_buf);
if (x > 0)
{
if (line_buf[x - 1] != '\n')
{
x++;
}
}
x = min(max(1, x), SW->rm);
}
static void go_word_right(void)
{
int fsm = 0, c;
if (x >= strlen(line_buf))
{
if (current->next != NULL)
{
go_down();
go_bol();
}
else
{
fsm = 3;
}
}
while (fsm < 3)
{
c = m_isspace(*(line_buf + x - 1));
switch (fsm)
{
case 0:
if (!c)
{
fsm = 1;
}
else
{
fsm = 2;
}
break;
case 1:
if (c)
{
fsm = 2;
}
break;
case 2:
if (!c)
{
fsm = 3;
}
break;
default:
break;
}
if (fsm != 3)
{
if (x == strlen(line_buf))
{
if (current->next != NULL)
{
go_down();
go_bol();
}
else
{
fsm = 3;
}
}
else
{
x++;
}
}
}
}
static void go_word_left(void)
{
int fsm = 0, c;
if (x == 1)
{
if (current->prev != NULL)
{
go_up();
go_eol();
}
else
{
fsm = 4;
}
}
while (fsm < 4)
{
c = m_isspace(*(line_buf + x - 1));
switch (fsm)
{
case 0:
if (!c)
{
fsm = 1;
}
else
{
fsm = 3;
}
break;
case 1:
if (!c)
{
fsm = 2;
}
else
{
fsm = 3;
}
break;
case 2:
if (c)
{
fsm = 4;
x++;
}
break;
case 3:
if (!c)
{
fsm = 2;
}
break;
default:
break;
}
if (fsm != 4)
{
if (x == 1)
{
if (current->prev != NULL)
{
go_up();
go_eol();
}
else
{
fsm = 4;
}
}
else
{
x--;
}
}
else if (x > strlen(line_buf))
{
if (current->next != NULL)
{
go_down();
go_bol();
}
else
{
go_eol();
}
}
}
}
static void go_pgup(void)
{
LINE *l = current;
int count = 1;
UnmarkLineBuf();
while (count < ed_maxy && current->prev)
{
count++;
current = current->prev;
currline--;
}
/* If we actually moved, redraw the page. */
if (l != current)
{
y = 1;
RedrawPage(current, 1);
}
CheckXvalid();
}
static void go_pgdown(void)
{
LINE *l = current;
int count = 1;
UnmarkLineBuf();
while (count < ed_maxy && current->next)
{
count++;
current = current->next;
currline++;
}
/* If we actually moved, redraw the page. */
if (l != current)
{
y = 1;
RedrawPage(current, 1);
}
CheckXvalid();
}
static void newline(void)
{
LINE *nl;
nl = InsertLine(current);
if (nl == NULL)
{
return;
}
/*
* If the current line is a quote, then the break shouldn't cause a
* wrap from the previous line. This is prevented by a hard CR
* (which the user can then kill if wanted).
*/
if (current->quote && !strchr(line_buf, '\n'))
{
strcat(line_buf, "\n");
}
nl->text = xstrdup(line_buf + x - 1);
line_buf[x - 1] = '\0';
strcat(line_buf, "\n");
if (current->block)
{
nl->block = 1;
}
if (current->templt)
{
if (x == 1)
{
nl->templt = 1;
current->templt = 0;
}
else
{
current->templt = 0;
}
}
TTBeginOutput();
go_down();
go_bol();
wrap(current, 0, 0, SW->rm);
RedrawPage(current->prev, y - 1);
SetLineBuf();
TTEndOutput();
}
/*
* udel_add_q(); Adds l to the deleted line queue.
*/
static void udel_add_q(LINE * l)
{
LINE *nl = udel;
int num = 0;
if (udel == NULL)
{
udel = l;
l->next = NULL;
l->prev = NULL;
return;
}
/* find the last line, keeping count of lines * in the process. */
while (nl->next != NULL)
{
nl = nl->next;
num++;
}
/*
* If there are more than max num of lines, then we delete the
* oldest one (on the end of queue).
*/
if (num >= 50)
{
nl->prev->next = NULL;
release(nl->text);
release(nl);
}
/* Add the latest deleted line to the beginning of the queue. */
udel->prev = l;
l->next = udel;
l->prev = NULL;
udel = l;
}
static void delete_line(void)
{
LINE *nl;
if (current->next == NULL)
{
if (current->prev != NULL)
{
if (!SW->carthy)
{
current->prev->next = NULL;
nl = current;
current = current->prev;
if (y > 1)
{
y--;
}
}
else
{
nl = current;
current = xmalloc(sizeof(LINE));
memcpy(current, nl, sizeof(LINE));
current->text = xstrdup("\n");
current->prev->next = current;
}
}
else
{
nl = current;
current = xmalloc(sizeof(LINE));
memcpy(current, nl, sizeof(LINE));
current->text = xstrdup("\n");
}
GotoXY(x, y);
}
else
{
current->next->prev = current->prev;
if (current->prev)
{
current->prev->next = current->next;
}
nl = current;
current = (nl->next != NULL) ? nl->next : nl->prev;
}
if (msgtop == nl)
{
msgtop = current;
}
/* Save the deleted line in a buffer. */
udel_add_q(nl);
/* Redraw the screen to reflect missing line. */
RedrawPage(current, y);
CheckXvalid();
}
/*
* udel_delete_q(); Removes and returns the first line in the
* undelete buffer.
*/
static LINE *udel_delete_q(void)
{
LINE *nl = udel;
if (udel == NULL)
{
return NULL;
}
if (udel->next)
{
udel->next->prev = NULL;
udel = udel->next;
}
else
{
udel = NULL;
}
return nl;
}
/*
* undelete(); If there is a line to undelete, undeletes the last line
* deleted and inserts it before the current line.
*/
static void undelete(void)
{
LINE *nl;
/* Get last line deleted. */
nl = udel_delete_q();
if (nl == NULL)
{
return;
}
/* Make sure we don't split it (the current block) in half :-) */
nl->templt = 0;
if (blocking && current->block)
{
nl->block = 1;
}
else
{
if (current->block == 0)
{
nl->block = 0;
}
}
/* Insert it ABOVE current line. */
nl->prev = current->prev;
current->prev = nl;
nl->next = current;
if (nl->prev)
{
nl->prev->next = nl;
}
if (msgtop == current)
{
msgtop = nl;
}
/* Make it the current line && redraw page to reflect new line. */
UnmarkLineBuf();
current = nl;
RedrawPage(current, y);
CheckXvalid();
}
/*
* ClearBlocks(); Clears all blocked lines in the message.
*/
static void ClearBlocks(void)
{
LINE *nl = msgtop;
while (nl != NULL)
{
if (nl->block)
{
nl->block = 0;
}
nl = nl->next;
}
blocking = 0;
}
/*
* CalculateBlocks(); Provides QEdit style blocking. Called everytime
* anchor is called (a new anchor has been laid).
*/
static void CalculateBlocks(void)
{
LINE *nl = msgtop;
int bl = 0; /* hit a block yet? */
int cp = 0; /* gone past current line? */
while (nl != NULL)
{
/*
* If we've passed the current line and hit the block and come
* out the other side, we want to stop.
*/
if (cp && bl && !nl->block)
{
break;
}
/* If bl = FALSE and we get blocked line, turn ON. */
if (!bl && nl->block)
{
bl = 1;
}
/* If we hit current line. */
if (nl == current)
{
/*
* If anchor is hit in the middle of a block, then we want
* to recreate the block on that line.
*/
if (nl->block)
{
ClearBlocks();
bl = 0;
}
nl->block = 1;
if (bl)
{
/*
* If we're already blocking, then we want to stop at
* the current line.
*/
break;
}
else
{
if (blocking == 0)
{
/*
* If we aren't blocking and global blocking hasn't
* been set, then we want to stop, else start
* blocking.
*/
break;
}
}
cp = 1;
}
/*
* If we've passed the current line and haven't hit a block yet,
* then we want to block all lines between.
*/
if (cp && !bl)
{
nl->block = 1;
}
/*
* If we haven't hit the current line, but have hit the block,
* we want to mark stuff in between.
*/
if (!cp && bl)
{
nl->block = 1;
}
nl = nl->next;
}
blocking = 1;
}
/*
* unblock(); Kills the current block.
*/
static void unblock(void)
{
LINE *nl = current;
int y2 = y;
ClearBlocks();
/* Find the top of the page and redraw it. */
while (y2 != ed_miny)
{
nl = nl->prev;
y2--;
}
RedrawPage(nl, ed_miny);
}
/*
* anchor(); Lays a block anchor at the current position.
*/
static void anchor(void)
{
LINE *nl = current;
int y2 = y;
/* Calculate the new block configuration. */
CalculateBlocks();
/* Find the top of the page and redraw it. */
while (y2 != ed_miny)
{
nl = nl->prev;
y2--;
}
RedrawPage(nl, ed_miny);
}
/*
* cut(); Cuts the current block from the message and saves it.
*/
static void cut(void)
{
LINE *nl, *begin, *end;
int y1;
if (!blocking)
{
return;
}
UnmarkLineBuf();
if (clip != NULL)
{
/* Discard whatever was in there before. */
clip = clearbuffer(clip);
}
nl = msgtop;
begin = NULL;
end = NULL;
/* Find the begin and end of the block. */
while (nl)
{
/* If we haven't hit a block yet, then this must be it. */
if (nl->block && !begin)
{
begin = nl;
}
/* If we've hit a block & come out the other side, break. */
if (begin && !nl->block && !end)
{
end = nl;
break;
}
nl = nl->next;
}
if (!begin->prev)
{
if (!end)
{
/*
* Whole message has been selected - create a line to put the
* cursor on.
*/
nl = xcalloc(1, sizeof *nl);
nl->text = xstrdup("\n");
msgtop = nl;
current = nl;
}
else
{
/* There is some message left. */
msgtop = end;
current = end;
if (end->prev)
{
end->prev->next = NULL;
}
end->prev = NULL;
}
}
else
{
/* Join up the gap where the cut will be. */
if (end)
{
begin->prev->next = end;
end->prev->next = NULL;
end->prev = begin->prev;
}
else
{
begin->prev->next = NULL;
}
current = begin->prev;
begin->prev = NULL;
}
/* Save the cut text and redraw the page. */
blocking = 0;
clip = begin;
/* Find beginning of screen & corresponding line. */
y1 = y;
nl = current;
while (y1 > ed_miny && nl->prev != NULL)
{
nl = nl->prev;
y1--;
}
/* Handle case where lines have to be moved up on screen. */
if (y1 != ed_miny)
{
y -= (y1 - ed_miny);
}
RedrawPage(nl, 1);
CheckXvalid();
}
/*
* paste(); Pastes the block on the clipboard into the page at the
* current cursor position.
*/
static void paste(void)
{
LINE *nl = current;
LINE *t = clip;
LINE *t1;
if (t == NULL)
{
return;
}
/* If a block was there, kill it. */
ClearBlocks();
/* Copy from the clipboard to AFTER the current position. */
while (t != NULL)
{
t1 = InsertLine(nl);
t1->quote = t->quote;
t1->text = xstrdup(t->text);
nl = t1;
t = t->next;
}
/* Redraw the page. */
RedrawPage(current, y);
}
static void tabit(void)
{
TTBeginOutput();
if (!(x % SW->tabsize))
{
insert_char(' ');
}
while (x % SW->tabsize)
{
insert_char(' ');
}
insert_char(' ');
TTEndOutput();
}
static void go_tos(void)
{
UnmarkLineBuf();
while (y > ed_miny && current->prev != NULL)
{
current = current->prev;
currline--;
y--;
}
CheckXvalid();
}
static void go_bos(void)
{
UnmarkLineBuf();
while (y < ed_maxy && current->next != NULL)
{
current = current->next;
currline++;
y++;
}
CheckXvalid();
}
static void go_tom()
{
if (current == msgtop)
{
return;
}
UnmarkLineBuf();
current = msgtop;
currline = 1;
y = 1;
CheckXvalid();
RedrawPage(current, y);
}
static void go_bom(void)
{
if (current->next == NULL)
{
return;
}
UnmarkLineBuf();
while (current->next)
{
current = current->next;
currline++;
}
y = 1;
CheckXvalid();
RedrawPage(current, y);
}
static void emacskill(void)
{
if (x == 1 && line_buf[x] == 0)
{
delete_line();
}
else
{
killeol();
}
}
static void killeol(void)
{
memset(line_buf + x - 1, 0, strlen(line_buf + x - 1));
line_buf[x - 1] = '\n';
line_buf[x] = '\0';
UnmarkLineBuf();
EdPutLine(current, y);
}
static void imptxt(void)
{
UnmarkLineBuf();
import(current);
RedrawPage(current, y);
SetLineBuf();
}
static void ExportBlock(void)
{
LINE *l, *nl;
if (!blocking)
{
return;
}
l = msgtop;
while (l->next && !l->block)
{
l = l->next;
}
if (!l->block)
{
return;
}
nl = l;
while (nl->next && nl->block)
{
nl = nl->next;
}
if (!nl->block)
{
if (nl->prev)
{
nl->prev->next = NULL;
}
}
export(l);
if (!nl->block)
{
if (nl->prev)
{
nl->prev->next = nl;
}
}
}
static void outtext(void)
{
static char title[] = " Export ";
static char msgtxt[] = "Export what?";
int res;
if (clip && blocking == 0)
{
res = ChoiceBox(title, msgtxt, "Clipboard", "Message", NULL);
cursor(1);
switch (res)
{
case ID_ONE:
export(clip);
break;
case ID_TWO:
export_text(messg, NULL);
break;
default:
break;
}
return;
}
else if (clip == NULL && blocking)
{
res = ChoiceBox(title, msgtxt, "Block", "Message", NULL);
cursor(1);
switch (res)
{
case ID_ONE:
ExportBlock();
break;
case ID_TWO:
export_text(messg, NULL);
break;
default:
break;
}
return;
}
else if (clip && blocking)
{
res = ChoiceBox(title, msgtxt, "Clipboard", "Block", "Message");
cursor(1);
switch (res)
{
case ID_ONE:
export(clip);
break;
case ID_TWO:
ExportBlock();
break;
case ID_THREE:
export_text(messg, NULL);
break;
default:
break;
}
return;
}
export_text(messg, NULL);
cursor(1);
}
static void quit(void)
{
release(current->text);
current->text = strdup(line_buf);
if (current->text == NULL)
{
WndWriteStr(0, 0, cm[CM_WTXT], "WARNING: Memory allocation failure - attempting to save...");
}
done = SAVE;
}
static void die(void)
{
if (confirm("Cancel?"))
{
done = ABORT;
}
cursor(1);
}
void count_bytes(LINE *l, long *bytes, long *quotes)
{
long count = 0;
long quote_count = 0;
size_t len;
while (l != NULL)
{
if (l->text)
{
len = strlen(l->text);
count += len;
if (isquote(l->text))
{
quote_count += len;
}
}
l = l->next;
}
if (bytes != NULL)
{
*bytes = count;
}
if (quotes != NULL)
{
*quotes = quote_count;
}
}
static void bytecount(void)
{
long count = 0;
long quote_count = 0;
char text[80];
count_bytes(msgtop, &count, "e_count);
sprintf(text, "Message size is %ld characters (quote ratio: %ld%%)",
count, (quote_count * 100) / count);
ChoiceBox(" Message Size ", text, " Ok ", NULL, NULL);
cursor(1);
}
static void toggle_ins(void)
{
insert = !insert;
if (insert)
{
WndWriteStr(maxx - 5, 5, cm[CM_DTXT], "ins");
}
else
{
unsigned char buf[4];
buf[0]=SC8; buf[1]=SC8; buf[2]=SC8; buf[3]='\0';
WndWriteStr(maxx - 5, 5, cm[CM_DTXT] | F_ALTERNATE, (char *)buf);
}
}
static void close_screen(void) /* used by shellos, and on WND_WM_RESIZE) */
{
WndClose(hMnScr);
KillHotSpots();
TTgotoxy(term.NRow - 1, 0);
TTclose();
cursor(1);
}
static void reopen_screen(void) /* used by shellos, and on WND_WM_RESIZE */
{
LINE *curr;
int y2;
int oldmaxx = maxx;
/* Redraw the screen. */
cursor(0);
InitScreen();
BuildHotSpots();
DrawHeader();
ShowNewArea();
ShowMsgHeader(messg);
edminy = 5;
edmaxy = maxy - ((SW->statbar) ? 1 : 0);
ed_miny = 1;
ed_maxy = edmaxy - edminy - 1;
if (oldmaxx != maxx) /* terminal size has changed - rewrap! */
{
adapt_margins();
/* mark the cursor position */
for (curr = msgtop; curr != NULL; curr = curr->next)
{
if (curr == current)
{
curr->cursor_position = x;
}
else
{
curr->cursor_position = 0;
}
}
/* rewrap the message */
for (curr = msgtop; curr != NULL; curr = curr->next)
{
wrap (curr, 0, 0, SW->rm);
}
/* search the cursor position */
current = NULL;
for (curr = msgtop, currline = 1; curr != NULL; curr =
curr->next, currline++)
{
if (curr->cursor_position)
{
current = curr;
x = curr->cursor_position;
break;
}
}
if (current == NULL)
{
ed_error("reopen_screen", "lost track of cursor!");
}
SetLineBuf();
}
/* redraw the screen */
if (y > ed_maxy)
{
y = ed_maxy;
}
y2 = y;
curr = current;
while (y2 > ed_miny && curr->prev)
{
curr = curr->prev;
y2--;
}
y -= (y2 - ed_miny);
RedrawPage(curr, ed_miny);
cursor(1);
GotoXY(x,y);
}
static void shellos(void)
{
static char tmp[PATHLEN];
mygetcwd(tmp, PATHLEN);
setcwd(ST->home);
close_screen();
fputs("\nEnter the command \"EXIT\" to return to " PROG ".\n", stderr);
shell_to_dos();
reopen_screen();
setcwd(tmp);
}
static void doscmd(void)
{
WND *hCurr, *hWnd;
static char curdir[PATHLEN];
char cmd[64], tmp[40];
int ret;
mygetcwd(curdir, PATHLEN);
memset(cmd, 0, sizeof cmd);
#if defined(MSDOS)
if (!GetString(" System Command ", "Enter DOS command to execute:", cmd, 64))
{
return;
}
#elif defined(OS2)
if (!GetString(" System Command ", "Enter OS/2 command to execute:", cmd, 64))
{
return;
}
#else
if (!GetString(" System Command ", "Enter system command to execute:", cmd, 64))
{
return;
}
#endif
hCurr = WndTop();
hWnd = WndOpen(0, 0, maxx - 1, maxy - 1, NBDR, 0, cm[CM_NTXT]);
cursor(1);
ret = system(cmd);
cursor(0);
#if defined(MSDOS)
sprintf(tmp, "DOS command returned %d", ret);
#elif defined(OS2)
sprintf(tmp, "OS/2 command returned %d", ret);
#else
sprintf(tmp, "System command returned %d", ret);
#endif
ChoiceBox(" Info ", tmp, " Ok ", NULL, NULL);
WndClose(hWnd);
WndCurr(hCurr);
setcwd(curdir);
cursor(1);
}
/*
* editheader(); Lets the user edit the header.
*/
static void editheader(void)
{
msg *save;
int q;
q = 0;
save = duplicatemsg(messg);
while (!q)
{
if (EditHeader(save) == Key_Esc)
{
if (confirm("Cancel?..."))
{
dispose(save);
cursor(1);
return;
}
}
else
{
q = 1;
}
}
/*
* We want to save the changes: release all allocated memory and
* make *messg = *save.
*/
release(messg->isfrom);
release(messg->isto);
release(messg->subj);
release(messg->msgid);
release(messg->reply);
release(messg->to.domain);
release(messg->from.domain);
*messg = *save;
cursor(1);
}
/*
* setup(); Calls the setup dialog box and allows user to play with
* switches.
*/
static void setup(void)
{
LINE *nl = current;
int y2 = y;
set_switch();
while (y2 != ed_miny && nl->prev != NULL)
{
nl = nl->prev;
y2--;
}
RedrawPage(nl, ed_miny);
cursor(1);
}
static void do_help(void)
{
if (ST->helpfile)
{
DoHelp(1);
}
}
static void UpdateXY(void)
{
static char line[50];
if (SW->statbar)
{
sprintf(line, "%c X: %03d Y: %03d", SC7, x, currline);
#if defined(MSDOS)
WndPutsn(maxx - (17 + 16), maxy - 1, 1, cm[CM_ITXT] | F_ALTERNATE, line);
WndPutsn(maxx - (16 + 16), maxy - 1, 15, cm[CM_ITXT], line + 1);
#else
WndPutsn(maxx - 17, maxy - 1, 1, cm[CM_ITXT] | F_ALTERNATE, line);
WndPutsn(maxx - 16, maxy - 1, 15, cm[CM_ITXT], line + 1);
#endif
}
}
static void UpdateMem(void)
{
if (SW->statbar)
{
#if defined(MSDOS) && !defined(__FLAT__)
long mem, oldmem = 0;
char line[15];
mem = corerem();
if (mem != oldmem)
{
oldmem = mem;
sprintf(line, "%c %3ldK ", SC7, (long)(corerem() / 1024));
WndPutsn(maxx - 7, maxy - 1, 1, cm[CM_ITXT] | F_ALTERNATE, line);
WndPutsn(maxx - 6, maxy - 1, 6, cm[CM_ITXT], line + 1);
}
#endif
}
}
int editmsg(msg * m, int quote)
{
EVT event;
int editcrstate = 0;
unsigned int ch;
x = 1;
y = 1;
currline = 1;
edminy = 5;
edmaxy = maxy - ((SW->statbar) ? 1 : 0);
ed_miny = 1;
ed_maxy = edmaxy - edminy - 1;
messg = m;
current = messg->text;
msgtop = messg->text;
if (insert)
{
WndWriteStr(maxx - 5, 5, cm[CM_DTXT], "ins");
}
if (msgtop == NULL)
{
current = calloc(1, sizeof *current);
if (current == NULL)
{
WndWriteStr(0, 0, cm[CM_WTXT], "WARNING: Memory allocation error - aborting!");
return ABORT;
}
msgtop = current;
msgtop->text = xstrdup("\n");
msgtop->prev = NULL;
}
if (SW->editcronly)
{
editcrstate = SW->showcr;
SW->showcr = 1;
}
SetLineBuf();
TTBeginOutput();
RedrawPage(current, y);
done = FALSE;
cursor(1);
GotoXY(x, y);
TTEndOutput();
while (!done)
{
if (window_resized)
{
close_screen();
reopen_screen();
window_resized = 0; /* ack! */
}
UpdateMem();
UpdateXY();
GotoXY(x, y);
ch = MnuGetMsg(&event, hMnScr->wid);
switch (event.msgtype)
{
case WND_WM_CHAR:
if (ch & 0xff)
{
if (editckeys[(ch & 0xff)] == NULL)
{
insert_char((char)DOROT13((char)(ch & 0xff)));
}
else
{
(*editckeys[(ch & 0xff)]) ();
}
}
else if (editakeys[(ch >> 8)] != NULL)
{
(*editakeys[(ch >> 8)]) ();
}
break;
default:
break;
}
}
messg->text = msgtop;
if (SW->chopquote && quote)
{
LINE *ff, *lowest;
ff = lowest = msgtop;
/* Find the lowest line of text that isn't a template line. */
while (ff->next)
{
if (!ff->quote && strlen(ff->text) != 0 && *(ff->text) != '\n' && !ff->templt)
{
lowest = ff;
}
ff = ff->next;
}
if (lowest && lowest != msgtop && lowest->next)
{
ff = lowest;
/*
* We got a lowest line, now we find the template line that
* is next underneath it.
*/
while (ff)
{
if (ff->templt)
{
break;
}
ff = ff->next;
}
if (ff == NULL)
{
/* didn't find one */
lowest->next = clearbuffer(lowest->next);
}
else
{
LINE *d;
/*
* We've found one, so delete all lines of text between
* the lowest and the template line.
*/
ff = lowest->next;
while (!ff->templt)
{
d = ff;
ff = ff->next;
if (d->prev)
{
d->prev->next = ff;
}
if (d->next)
{
d->next->prev = d->prev;
}
release(d->text);
xfree(d);
}
}
}
}
/* Clean up everything. */
if (insert)
{
unsigned char buf[4];
buf[0]=SC8; buf[1]=SC8; buf[2]=SC8; buf[3]='\0';
WndWriteStr(maxx - 5, 5, cm[CM_DTXT] | F_ALTERNATE, (char *)buf);
}
if (SW->editcronly)
{
SW->showcr = editcrstate;
}
blocking = 0;
cursor(0);
return done;
}
int ed_error(char *fc, char *fmt,...)
{
va_list params;
static char line[255];
va_start(params, fmt);
vsprintf(line, fmt, params);
fprintf(stderr, "\nError! function %s: %s", fc, line);
exit(-1);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1