Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/word.c
- This file was added.
/* $OpenBSD: word.c,v 1.19 2015/12/30 20:51:51 lum Exp $ */ | |||||
/* This file is in the public domain. */ | |||||
/* | |||||
* Word mode commands. | |||||
* The routines in this file implement commands that work word at a time. | |||||
* There are all sorts of word mode commands. | |||||
*/ | |||||
#include <sys/queue.h> | |||||
#include <signal.h> | |||||
#include <errno.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include "def.h" | |||||
RSIZE countfword(void); | |||||
int grabword(char **); | |||||
/* | |||||
* Move the cursor backward by "n" words. All of the details of motion are | |||||
* performed by the "backchar" and "forwchar" routines. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
backword(int f, int n) | |||||
{ | |||||
if (n < 0) | |||||
return (forwword(f | FFRAND, -n)); | |||||
if (backchar(FFRAND, 1) == FALSE) | |||||
return (FALSE); | |||||
while (n--) { | |||||
while (inword() == FALSE) { | |||||
if (backchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
} | |||||
while (inword() != FALSE) { | |||||
if (backchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
} | |||||
} | |||||
return (forwchar(FFRAND, 1)); | |||||
} | |||||
/* | |||||
* Move the cursor forward by the specified number of words. All of the | |||||
* motion is done by "forwchar". | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
forwword(int f, int n) | |||||
{ | |||||
if (n < 0) | |||||
return (backword(f | FFRAND, -n)); | |||||
while (n--) { | |||||
while (inword() == FALSE) { | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
} | |||||
while (inword() != FALSE) { | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
} | |||||
} | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Transpose 2 words. | |||||
* The function below is artifically restricted to only a maximum of 1 iteration | |||||
* at the moment because the 'undo' functionality within mg needs amended for | |||||
* multiple movements of point, backwards and forwards. | |||||
*/ | |||||
int | |||||
transposeword(int f, int n) | |||||
{ | |||||
struct line *tmp1_w_dotp = NULL; | |||||
struct line *tmp2_w_dotp = NULL; | |||||
int tmp2_w_doto = 0; | |||||
int tmp1_w_dotline = 0; | |||||
int tmp2_w_dotline = 0; | |||||
int tmp1_w_doto; | |||||
int i; /* start-of-line space counter */ | |||||
int ret, s; | |||||
int newline; | |||||
int leave = 0; | |||||
int tmp_len; | |||||
char *word1 = NULL; | |||||
char *word2 = NULL; | |||||
char *chr; | |||||
if (n == 0) | |||||
return (TRUE); | |||||
n = 1; /* remove this line to allow muliple-iterations */ | |||||
if ((s = checkdirty(curbp)) != TRUE) | |||||
return (s); | |||||
if (curbp->b_flag & BFREADONLY) { | |||||
dobeep(); | |||||
ewprintf("Buffer is read-only"); | |||||
return (FALSE); | |||||
} | |||||
undo_boundary_enable(FFRAND, 0); | |||||
/* go backwards to find the start of a word to transpose. */ | |||||
(void)backword(FFRAND, 1); | |||||
ret = grabword(&word1); | |||||
if (ret == ABORT) { | |||||
ewprintf("No word to the left to tranpose."); | |||||
return (FALSE); | |||||
} | |||||
if (ret < 0) { | |||||
dobeep(); | |||||
ewprintf("Error copying word: %s", strerror(ret)); | |||||
free(word1); | |||||
return (FALSE); | |||||
} | |||||
while (n-- > 0) { | |||||
i = 0; | |||||
newline = 0; | |||||
tmp1_w_doto = curwp->w_doto; | |||||
tmp1_w_dotline = curwp->w_dotline; | |||||
tmp1_w_dotp = curwp->w_dotp; | |||||
/* go forward and find next word. */ | |||||
while (inword() == FALSE) { | |||||
if (forwchar(FFRAND, 1) == FALSE) { | |||||
leave = 1; | |||||
if (tmp1_w_dotline < curwp->w_dotline) | |||||
curwp->w_dotline--; | |||||
ewprintf("Don't have two things to transpose"); | |||||
break; | |||||
} | |||||
if (curwp->w_doto == 0) { | |||||
newline = 1; | |||||
i = 0; | |||||
} else if (newline) | |||||
i++; | |||||
} | |||||
if (leave) { | |||||
tmp2_w_doto = tmp1_w_doto; | |||||
tmp2_w_dotline = tmp1_w_dotline; | |||||
tmp2_w_dotp = tmp1_w_dotp; | |||||
break; | |||||
} | |||||
tmp2_w_doto = curwp->w_doto; | |||||
tmp2_w_dotline = curwp->w_dotline; | |||||
tmp2_w_dotp = curwp->w_dotp; | |||||
ret = grabword(&word2); | |||||
if (ret < 0 || ret == ABORT) { | |||||
dobeep(); | |||||
ewprintf("Error copying word: %s", strerror(ret)); | |||||
free(word1); | |||||
return (FALSE); | |||||
} | |||||
tmp_len = strlen(word2); | |||||
tmp2_w_doto += tmp_len; | |||||
curwp->w_doto = tmp1_w_doto; | |||||
curwp->w_dotline = tmp1_w_dotline; | |||||
curwp->w_dotp = tmp1_w_dotp; | |||||
/* insert shuffled along word */ | |||||
for (chr = word2; *chr != '\0'; ++chr) | |||||
linsert(1, *chr); | |||||
if (newline) | |||||
tmp2_w_doto = i; | |||||
curwp->w_doto = tmp2_w_doto; | |||||
curwp->w_dotline = tmp2_w_dotline; | |||||
curwp->w_dotp = tmp2_w_dotp; | |||||
word2 = NULL; | |||||
} | |||||
curwp->w_doto = tmp2_w_doto; | |||||
curwp->w_dotline = tmp2_w_dotline; | |||||
curwp->w_dotp = tmp2_w_dotp; | |||||
/* insert very first word in its new position */ | |||||
for (chr = word1; *chr != '\0'; ++chr) | |||||
linsert(1, *chr); | |||||
if (leave) | |||||
(void)backword(FFRAND, 1); | |||||
free(word1); | |||||
free(word2); | |||||
undo_boundary_enable(FFRAND, 1); | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* copy and delete word. | |||||
*/ | |||||
int | |||||
grabword(char **word) | |||||
{ | |||||
int c; | |||||
while (inword() == TRUE) { | |||||
c = lgetc(curwp->w_dotp, curwp->w_doto); | |||||
if (*word == NULL) { | |||||
if (asprintf(word, "%c", c) == -1) | |||||
return (errno); | |||||
} else { | |||||
if (asprintf(word, "%s%c", *word, c) == -1) | |||||
return (errno); | |||||
} | |||||
(void)forwdel(FFRAND, 1); | |||||
} | |||||
if (*word == NULL) | |||||
return (ABORT); | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Move the cursor forward by the specified number of words. As you move, | |||||
* convert any characters to upper case. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
upperword(int f, int n) | |||||
{ | |||||
int c, s; | |||||
RSIZE size; | |||||
if ((s = checkdirty(curbp)) != TRUE) | |||||
return (s); | |||||
if (curbp->b_flag & BFREADONLY) { | |||||
dobeep(); | |||||
ewprintf("Buffer is read-only"); | |||||
return (FALSE); | |||||
} | |||||
if (n < 0) | |||||
return (FALSE); | |||||
while (n--) { | |||||
while (inword() == FALSE) { | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
} | |||||
size = countfword(); | |||||
undo_add_change(curwp->w_dotp, curwp->w_doto, size); | |||||
while (inword() != FALSE) { | |||||
c = lgetc(curwp->w_dotp, curwp->w_doto); | |||||
if (ISLOWER(c) != FALSE) { | |||||
c = TOUPPER(c); | |||||
lputc(curwp->w_dotp, curwp->w_doto, c); | |||||
lchange(WFFULL); | |||||
} | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
} | |||||
} | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Move the cursor forward by the specified number of words. As you move | |||||
* convert characters to lower case. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
lowerword(int f, int n) | |||||
{ | |||||
int c, s; | |||||
RSIZE size; | |||||
if ((s = checkdirty(curbp)) != TRUE) | |||||
return (s); | |||||
if (curbp->b_flag & BFREADONLY) { | |||||
dobeep(); | |||||
ewprintf("Buffer is read-only"); | |||||
return (FALSE); | |||||
} | |||||
if (n < 0) | |||||
return (FALSE); | |||||
while (n--) { | |||||
while (inword() == FALSE) { | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
} | |||||
size = countfword(); | |||||
undo_add_change(curwp->w_dotp, curwp->w_doto, size); | |||||
while (inword() != FALSE) { | |||||
c = lgetc(curwp->w_dotp, curwp->w_doto); | |||||
if (ISUPPER(c) != FALSE) { | |||||
c = TOLOWER(c); | |||||
lputc(curwp->w_dotp, curwp->w_doto, c); | |||||
lchange(WFFULL); | |||||
} | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
} | |||||
} | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Move the cursor forward by the specified number of words. As you move | |||||
* convert the first character of the word to upper case, and subsequent | |||||
* characters to lower case. Error if you try to move past the end of the | |||||
* buffer. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
capword(int f, int n) | |||||
{ | |||||
int c, s; | |||||
RSIZE size; | |||||
if ((s = checkdirty(curbp)) != TRUE) | |||||
return (s); | |||||
if (curbp->b_flag & BFREADONLY) { | |||||
dobeep(); | |||||
ewprintf("Buffer is read-only"); | |||||
return (FALSE); | |||||
} | |||||
if (n < 0) | |||||
return (FALSE); | |||||
while (n--) { | |||||
while (inword() == FALSE) { | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
} | |||||
size = countfword(); | |||||
undo_add_change(curwp->w_dotp, curwp->w_doto, size); | |||||
if (inword() != FALSE) { | |||||
c = lgetc(curwp->w_dotp, curwp->w_doto); | |||||
if (ISLOWER(c) != FALSE) { | |||||
c = TOUPPER(c); | |||||
lputc(curwp->w_dotp, curwp->w_doto, c); | |||||
lchange(WFFULL); | |||||
} | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
while (inword() != FALSE) { | |||||
c = lgetc(curwp->w_dotp, curwp->w_doto); | |||||
if (ISUPPER(c) != FALSE) { | |||||
c = TOLOWER(c); | |||||
lputc(curwp->w_dotp, curwp->w_doto, c); | |||||
lchange(WFFULL); | |||||
} | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
return (TRUE); | |||||
} | |||||
} | |||||
} | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Count characters in word, from current position | |||||
*/ | |||||
RSIZE | |||||
countfword() | |||||
{ | |||||
RSIZE size; | |||||
struct line *dotp; | |||||
int doto; | |||||
dotp = curwp->w_dotp; | |||||
doto = curwp->w_doto; | |||||
size = 0; | |||||
while (inword() != FALSE) { | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
/* hit the end of the buffer */ | |||||
goto out; | |||||
++size; | |||||
} | |||||
out: | |||||
curwp->w_dotp = dotp; | |||||
curwp->w_doto = doto; | |||||
return (size); | |||||
} | |||||
/* | |||||
* Kill forward by "n" words. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
delfword(int f, int n) | |||||
{ | |||||
RSIZE size; | |||||
struct line *dotp; | |||||
int doto; | |||||
int s; | |||||
if ((s = checkdirty(curbp)) != TRUE) | |||||
return (s); | |||||
if (curbp->b_flag & BFREADONLY) { | |||||
dobeep(); | |||||
ewprintf("Buffer is read-only"); | |||||
return (FALSE); | |||||
} | |||||
if (n < 0) | |||||
return (FALSE); | |||||
/* purge kill buffer */ | |||||
if ((lastflag & CFKILL) == 0) | |||||
kdelete(); | |||||
thisflag |= CFKILL; | |||||
dotp = curwp->w_dotp; | |||||
doto = curwp->w_doto; | |||||
size = 0; | |||||
while (n--) { | |||||
while (inword() == FALSE) { | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
/* hit the end of the buffer */ | |||||
goto out; | |||||
++size; | |||||
} | |||||
while (inword() != FALSE) { | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
/* hit the end of the buffer */ | |||||
goto out; | |||||
++size; | |||||
} | |||||
} | |||||
out: | |||||
curwp->w_dotp = dotp; | |||||
curwp->w_doto = doto; | |||||
return (ldelete(size, KFORW)); | |||||
} | |||||
/* | |||||
* Kill backwards by "n" words. The rules for success and failure are now | |||||
* different, to prevent strange behavior at the start of the buffer. The | |||||
* command only fails if something goes wrong with the actual delete of the | |||||
* characters. It is successful even if no characters are deleted, or if you | |||||
* say delete 5 words, and there are only 4 words left. I considered making | |||||
* the first call to "backchar" special, but decided that that would just be | |||||
* weird. Normally this is bound to "M-Rubout" and to "M-Backspace". | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
delbword(int f, int n) | |||||
{ | |||||
RSIZE size; | |||||
int s; | |||||
if ((s = checkdirty(curbp)) != TRUE) | |||||
return (s); | |||||
if (curbp->b_flag & BFREADONLY) { | |||||
dobeep(); | |||||
ewprintf("Buffer is read-only"); | |||||
return (FALSE); | |||||
} | |||||
if (n < 0) | |||||
return (FALSE); | |||||
/* purge kill buffer */ | |||||
if ((lastflag & CFKILL) == 0) | |||||
kdelete(); | |||||
thisflag |= CFKILL; | |||||
if (backchar(FFRAND, 1) == FALSE) | |||||
/* hit buffer start */ | |||||
return (TRUE); | |||||
/* one deleted */ | |||||
size = 1; | |||||
while (n--) { | |||||
while (inword() == FALSE) { | |||||
if (backchar(FFRAND, 1) == FALSE) | |||||
/* hit buffer start */ | |||||
goto out; | |||||
++size; | |||||
} | |||||
while (inword() != FALSE) { | |||||
if (backchar(FFRAND, 1) == FALSE) | |||||
/* hit buffer start */ | |||||
goto out; | |||||
++size; | |||||
} | |||||
} | |||||
if (forwchar(FFRAND, 1) == FALSE) | |||||
return (FALSE); | |||||
/* undo assumed delete */ | |||||
--size; | |||||
out: | |||||
return (ldelete(size, KBACK)); | |||||
} | |||||
/* | |||||
* Return TRUE if the character at dot is a character that is considered to be | |||||
* part of a word. The word character list is hard coded. Should be settable. | |||||
*/ | |||||
int | |||||
inword(void) | |||||
{ | |||||
/* can't use lgetc in ISWORD due to bug in OSK cpp */ | |||||
return (curwp->w_doto != llength(curwp->w_dotp) && | |||||
ISWORD(curwp->w_dotp->l_text[curwp->w_doto])); | |||||
} |