1
#if !defined(lint) && !defined(DOS)
2
static char rcsid[] = "$Id: word.c 334 2006-12-19 01:26:30Z hubert@u.washington.edu $";
6
* ========================================================================
7
* Copyright 2006 University of Washington
9
* Licensed under the Apache License, Version 2.0 (the "License");
10
* you may not use this file except in compliance with the License.
11
* You may obtain a copy of the License at
13
* http://www.apache.org/licenses/LICENSE-2.0
15
* ========================================================================
19
* Program: Word at a time routines
21
* The routines in this file implement commands that work word at a time.
22
* There are all sorts of word mode commands. If I do any sentence and/or
23
* paragraph mode commands, they are likely to be put in this file.
29
int fpnewline(UCS *quote);
32
/* Word wrap on n-spaces. Back-over whatever precedes the point on the current
33
* line and stop on the first word-break or the beginning of the line. If we
34
* reach the beginning of the line, jump back to the end of the word and start
35
* a new line. Otherwise, break the line at the word-break, eat it, and jump
36
* back to the end of the word.
37
* Returns TRUE on success, FALSE on errors.
42
register int cnt; /* size of word wrapped to next line */
43
register int bp; /* index to wrap on */
44
register int first = -1;
47
if(curwp->w_doto <= 0) /* no line to wrap? */
51
for(bp = cnt = 0; cnt < llength(curwp->w_dotp) && !bp; cnt++){
52
if(ucs4_isspace(lgetc(curwp->w_dotp, cnt).c)){
54
if(lgetc(curwp->w_dotp, cnt).c == TAB){
63
ww = wcellwidth((UCS) lgetc(curwp->w_dotp, cnt).c);
64
wid += (ww >= 0 ? ww : 1);
69
if(first > 0 && wid > fillcol)
76
/* bp now points to the first character of the next line */
77
cnt = curwp->w_doto - bp;
80
if(!lnewline()) /* break the line */
84
* if there's a line below, it doesn't start with whitespace
85
* and there's room for this line...
87
if(!(curbp->b_flag & BFWRAPOPEN)
88
&& lforw(curwp->w_dotp) != curbp->b_linep
89
&& llength(lforw(curwp->w_dotp))
90
&& !ucs4_isspace(lgetc(lforw(curwp->w_dotp), 0).c)
91
&& (llength(curwp->w_dotp) + llength(lforw(curwp->w_dotp)) < fillcol)){
92
gotoeol(0, 1); /* then pull text up from below */
93
if(lgetc(curwp->w_dotp, curwp->w_doto - 1).c != ' ')
100
curbp->b_flag &= ~BFWRAPOPEN; /* don't open new line next wrap */
101
/* restore dot (account for NL) */
102
if(cnt && !forwchar(0, cnt < 0 ? cnt-1 : cnt))
110
* Move the cursor backward by "n" words. All of the details of motion are
111
* performed by the "backchar" and "forwchar" routines. Error if you try to
112
* move beyond the buffers.
115
backword(int f, int n)
118
return (forwword(f, -n));
119
if (backchar(FALSE, 1) == FALSE)
122
while (inword() == FALSE) {
123
if (backchar(FALSE, 1) == FALSE)
126
while (inword() != FALSE) {
127
if (backchar(FALSE, 1) == FALSE)
131
return (forwchar(FALSE, 1));
135
* Move the cursor forward by the specified number of words. All of the motion
136
* is done by "forwchar". Error if you try and move beyond the buffer's end.
139
forwword(int f, int n)
142
return (backword(f, -n));
145
while (inword() != FALSE) {
146
if (forwchar(FALSE, 1) == FALSE)
150
while (inword() == FALSE) {
151
if (forwchar(FALSE, 1) == FALSE)
155
while (inword() != FALSE) {
156
if (forwchar(FALSE, 1) == FALSE)
167
return((c && c <= 0x7f && isalnum((unsigned char) c))
168
|| (c >= 0xA0 && !SPECIAL_SPACE(c)));
174
return((c && c <= 0x7f && isalpha((unsigned char) c))
175
|| (c >= 0xA0 && !SPECIAL_SPACE(c)));
181
return(c < 0xff && isspace((unsigned char) c) || SPECIAL_SPACE(c));
187
return !ucs4_isalnum(c) && !ucs4_isspace(c);
192
* Move the cursor forward by the specified number of words. As you move,
193
* convert any characters to upper case. Error if you try and move beyond the
194
* end of the buffer. Bound to "M-U".
197
upperword(int f, int n)
203
if (curbp->b_mode&MDVIEW) /* don't allow this command if */
204
return(rdonly()); /* we are in read only mode */
208
while (inword() == FALSE) {
209
if (forwchar(FALSE, 1) == FALSE)
212
while (inword() != FALSE) {
213
c = lgetc(curwp->w_dotp, curwp->w_doto).c;
214
if (c>='a' && c<='z') {
215
ac.c = (c -= 'a'-'A');
216
lputc(curwp->w_dotp, curwp->w_doto, ac);
219
if (forwchar(FALSE, 1) == FALSE)
227
* Move the cursor forward by the specified number of words. As you move
228
* convert characters to lower case. Error if you try and move over the end of
229
* the buffer. Bound to "M-L".
232
lowerword(int f, int n)
238
if (curbp->b_mode&MDVIEW) /* don't allow this command if */
239
return(rdonly()); /* we are in read only mode */
243
while (inword() == FALSE) {
244
if (forwchar(FALSE, 1) == FALSE)
247
while (inword() != FALSE) {
248
c = lgetc(curwp->w_dotp, curwp->w_doto).c;
249
if (c>='A' && c<='Z') {
251
lputc(curwp->w_dotp, curwp->w_doto, ac);
254
if (forwchar(FALSE, 1) == FALSE)
262
* Move the cursor forward by the specified number of words. As you move
263
* convert the first character of the word to upper case, and subsequent
264
* characters to lower case. Error if you try and move past the end of the
265
* buffer. Bound to "M-C".
268
capword(int f, int n)
274
if (curbp->b_mode&MDVIEW) /* don't allow this command if */
275
return(rdonly()); /* we are in read only mode */
279
while (inword() == FALSE) {
280
if (forwchar(FALSE, 1) == FALSE)
283
if (inword() != FALSE) {
284
c = lgetc(curwp->w_dotp, curwp->w_doto).c;
285
if (c>='a' && c<='z') {
286
ac.c = (c -= 'a'-'A');
287
lputc(curwp->w_dotp, curwp->w_doto, ac);
290
if (forwchar(FALSE, 1) == FALSE)
292
while (inword() != FALSE) {
293
c = lgetc(curwp->w_dotp, curwp->w_doto).c;
294
if (c>='A' && c<='Z') {
295
ac.c = (c += 'a'-'A');
296
lputc(curwp->w_dotp, curwp->w_doto, ac);
299
if (forwchar(FALSE, 1) == FALSE)
308
* Kill forward by "n" words. Remember the location of dot. Move forward by
309
* the right number of words. Put dot back where it was and issue the kill
310
* command for the right number of characters. Bound to "M-D".
313
delfword(int f, int n)
319
if (curbp->b_mode&MDVIEW) /* don't allow this command if */
320
return(rdonly()); /* we are in read only mode */
323
dotp = curwp->w_dotp;
324
doto = curwp->w_doto;
328
while (inword() != FALSE) {
329
if (forwchar(FALSE,1) == FALSE)
334
while (inword() == FALSE) {
335
if (forwchar(FALSE, 1) == FALSE)
340
while (inword() != FALSE) {
341
if (forwchar(FALSE, 1) == FALSE)
347
curwp->w_dotp = dotp;
348
curwp->w_doto = doto;
349
return (ldelete(size, kinsert));
353
* Kill backwards by "n" words. Move backwards by the desired number of words,
354
* counting the characters. When dot is finally moved to its resting place,
355
* fire off the kill command. Bound to "M-Rubout" and to "M-Backspace".
358
delbword(int f, int n)
362
if (curbp->b_mode&MDVIEW) /* don't allow this command if */
363
return(rdonly()); /* we are in read only mode */
366
if (backchar(FALSE, 1) == FALSE)
370
while (inword() == FALSE) {
371
if (backchar(FALSE, 1) == FALSE)
375
while (inword() != FALSE) {
376
if (backchar(FALSE, 1) == FALSE)
381
if (forwchar(FALSE, 1) == FALSE)
383
return (ldelete(size, kinsert));
385
#endif /* MAYBELATER */
388
* Return TRUE if the character at dot is a character that is considered to be
394
if(curwp->w_doto < llength(curwp->w_dotp))
396
if(ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto).c))
400
else if(ucs4_ispunct(lgetc(curwp->w_dotp, curwp->w_doto).c))
402
if((curwp->w_doto > 1) &&
403
ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto - 1).c) &&
404
(curwp->w_doto + 1 < llength(curwp->w_dotp)) &&
405
ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto + 1).c))
417
* Return number of quotes if whatever starts the line matches the quote string
420
quote_match(UCS *q, LINE *l, UCS *buf, int buflen)
422
register int i, n, j, qb;
428
qb = (ucs4_strlen(q) > 1 && q[ucs4_strlen(q)-1] == ' ') ? 1 : 0;
429
for(n = 0, j = 0; ;){
430
for(i = 0; j <= llength(l) && qb ? q[i+1] : q[i]; i++, j++)
431
if(q[i] != lgetc(l, j).c)
435
if((!qb && q[i] == '\0') || (qb && q[i+1] == '\0')){
436
if(ucs4_strlen(buf) + ucs4_strlen(q) + 1 < buflen){
437
ucs4_strncat(buf, q, buflen-ucs4_strlen(q));
438
buf[buflen-1] = '\0';
439
if(qb && (j > llength(l) || lgetc(l, j).c != ' '))
440
buf[ucs4_strlen(buf)-1] = '\0';
445
else if(qb && lgetc(l, j).c == ' ')
448
return(n); /* never reached */
452
/* Justify the entire buffer instead of just a paragraph */
454
fillbuf(int f, int n)
460
if(curbp->b_mode&MDVIEW){ /* don't allow this command if */
461
return(rdonly()); /* we are in read only mode */
463
else if (fillcol == 0) { /* no fill column set */
464
mlwrite_utf8("No fill column set", NULL);
468
if((lastflag & CFFILL) && (lastflag & CFFLBF)){
469
/* no use doing a full justify twice */
470
thisflag |= (CFFLBF | CFFILL);
474
/* record the pointer of the last line */
475
if(gotoeob(FALSE, 1) == FALSE)
478
eobline = curwp->w_dotp; /* last line of buffer */
479
if(!llength(eobline))
480
eobline = lback(eobline);
482
/* and back to the beginning of the buffer */
485
thisflag |= CFFLBF; /* CFFILL also gets set in fillpara */
490
curwp->w_flag |= WFMODE;
493
* clear the kill buffer, that's where we'll store undo
494
* information, we can't do the fill buffer because
495
* fillpara relies on its contents
499
getregion(®ion, eobline, llength(eobline));
501
/* Put full message in the kill buffer for undo */
502
if(!ldelete(region.r_size, kinsert))
505
/* before yank'ing, clear lastflag so we don't just unjustify */
506
lastflag &= ~(CFFLBF | CFFILL);
508
/* Now in kill buffer, bring back text to use in fillpara */
513
/* call fillpara until we're at the end of the buffer */
514
while(curwp->w_dotp != curbp->b_linep)
515
if(!(fillpara(FALSE, 1)))
523
* Fill the current paragraph according to the current fill column
526
fillpara(int f, int n)
528
int i, j, c, qlen, word[NSTRING], same_word,
529
spaces, word_len, word_ind, line_len, qn, ww;
531
UCS *qstr, qstr2[NSTRING];
535
if(curbp->b_mode&MDVIEW){ /* don't allow this command if */
536
return(rdonly()); /* we are in read only mode */
538
else if (fillcol == 0) { /* no fill column set */
539
mlwrite_utf8("No fill column set", NULL);
542
else if(curwp->w_dotp == curbp->b_linep) /* don't wrap! */
545
/* record the pointer to the line just past the EOP */
546
if(gotoeop(FALSE, 1) == FALSE)
549
eopline = curwp->w_dotp; /* first line of para */
551
/* and back to the beginning of the paragraph */
554
/* determine if we're justifying quoted text or not */
555
qstr = (glo_quote_str
556
&& quote_match(glo_quote_str,
557
curwp->w_dotp, qstr2, NSTRING)
558
&& *qstr2) ? qstr2 : NULL;
559
qlen = qstr ? ucs4_strlen(qstr) : 0;
561
/* let yank() know that it may be restoring a paragraph */
567
curwp->w_flag |= WFMODE;
569
/* cut the paragraph into our fill buffer */
572
getregion(®ion, eopline, llength(eopline));
573
if(!ldelete(region.r_size, finsert))
576
/* Now insert it back wrapped */
577
spaces = word_len = word_ind = line_len = same_word = 0;
579
/* Beginning with leading quoting... */
583
ww = wcellwidth(qstr[i]);
584
line_len += (ww >= 0 ? ww : 1);
585
linsert(1, qstr[i++]);
588
line_last = ' '; /* no word-flush space! */
591
/* ...and leading white space */
592
for(i = qlen; (c = fremove(i)) == ' ' || c == TAB; i++){
593
linsert(1, line_last = c);
594
line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1);
597
/* then digest the rest... */
598
while((c = fremove(i++)) > 0){
601
i += qlen; /* skip next quote string */
614
if(spaces){ /* flush word? */
615
if((line_len - qlen > 0)
616
&& line_len + word_len + 1 > fillcol
617
&& ((ucs4_isspace(line_last))
618
|| (linsert(1, ' ')))
619
&& (line_len = fpnewline(qstr)))
620
line_last = ' '; /* no word-flush space! */
622
if(word_len){ /* word to write? */
623
if(line_len && !ucs4_isspace(line_last)){
624
linsert(1, ' '); /* need padding? */
628
line_len += word_len;
629
for(j = 0; j < word_ind; j++)
630
linsert(1, line_last = word[j]);
632
if(spaces > 1 && strchr(".?!:;\")", line_last)){
633
linsert(2, line_last = ' ');
637
word_len = word_ind = 0;
643
if(word_ind + 1 >= NSTRING){
644
/* Magic! Fake that we output a wrapped word */
645
if((line_len - qlen > 0) && !same_word++){
646
if(!ucs4_isspace(line_last))
648
line_len = fpnewline(qstr);
651
line_len += word_len;
652
for(j = 0; j < word_ind; j++)
655
word_len = word_ind = 0;
659
word[word_ind++] = c;
661
word_len += (ww >= 0 ? ww : 1);
668
if((line_len - qlen > 0) && (line_len + word_len + 1 > fillcol)){
669
if(!ucs4_isspace(line_last))
671
(void) fpnewline(qstr);
673
else if(line_len && !ucs4_isspace(line_last))
676
for(j = 0; j < word_ind; j++)
680
/* Leave cursor on first char of first line after paragraph */
681
curwp->w_dotp = lforw(curwp->w_dotp);
689
* fpnewline - output a fill paragraph newline mindful of quote string
692
fpnewline(UCS *quote)
697
for(len = 0; quote && *quote; quote++){
700
ww = wcellwidth(*quote);
701
len += (ww >= 0 ? ww : 1);