another update from CVS HEAD, for QA
[alioth/jupp.git] / uformat.c
1 /*
2  *      User text formatting functions
3  *      Copyright
4  *              (C) 1992 Joseph H. Allen
5  *
6  *      This file is part of JOE (Joe's Own Editor)
7  */
8 #include "config.h"
9 #include "types.h"
10
11 __RCSID("$MirOS: contrib/code/jupp/uformat.c,v 1.9 2017/12/08 02:28:07 tg Exp $");
12
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "b.h"
17 #include "ublock.h"
18 #include "uformat.h"
19 #include "charmap.h"
20 #include "utils.h"
21
22 /* Center line cursor is on and move cursor to beginning of next line */
23
24 int ucenter(BW *bw)
25 {
26         P *p = bw->cursor, *q;
27         long endcol, begcol, x;
28         int c;
29
30         p_goto_eol(p);
31         while (joe_isblank(bw->b->o.charmap, (c = prgetc(p))))
32                 /* do nothing */;
33         if (c == '\n') {
34                 pgetc(p);
35                 goto done;
36         }
37         if (c == NO_MORE_DATA)
38                 goto done;
39         pgetc(p);
40         endcol = piscol(p);
41
42         p_goto_bol(p);
43         while (joe_isblank(bw->b->o.charmap, (c = pgetc(p))))
44                 /* do nothing */;
45         if (c == '\n') {
46                 prgetc(p);
47                 goto done;
48         }
49         if (c == NO_MORE_DATA)
50                 goto done;
51         prgetc(p);
52         begcol = piscol(p);
53
54         if (endcol - begcol > bw->o.rmargin + bw->o.lmargin)
55                 goto done;
56
57         q = pdup(p);
58         p_goto_bol(q);
59         bdel(q, p);
60         prm(q);
61
62         for (x = 0; x != (bw->o.lmargin + bw->o.rmargin) / 2 - (endcol - begcol) / 2; ++x)
63                 binsc(p, ' ');
64
65  done:
66         if (!pnextl(p)) {
67                 binsc(p, '\n');
68                 pgetc(p);
69                 return -1;
70         } else
71                 return 0;
72 }
73
74 /* Return true if c is a character which can indent a paragraph */
75
76 static int cpara(int c)
77 {
78         if (c == ' ' || c == '\t' || c == '\\' ||
79             c == '>' || c == '|' || c == ':' || c == '*' || c == '/' ||
80             c == ',' || c == '.' || c == '?' || c == ';' || c == ']' ||
81             c == '}' || c == '=' || c == '+' || c == '-' || c == '_' ||
82             c == ')' || c == '&' || c == '^' || c == '%' || c == '$' ||
83             c == '#' || c == '@' || c == '!' || c == '~')
84                 return 1;
85         else
86                 return 0;
87 }
88
89 /* Return true if line is definitly not a paragraph line.
90  * Lines which arn't paragraph lines:
91  *  1) Blank lines
92  *  2) Lines which begin with '.'
93  */
94
95 static int pisnpara(P *p)
96 {
97         P *q;
98         int c;
99
100         q = pdup(p);
101         p_goto_bol(q);
102         while (cpara(c = pgetc(q)))
103                 /* do nothing */;
104         prm(q);
105         if (c == '.' || c == '\r' || c == '\n')
106                 return 1;
107         else
108                 return 0;
109 }
110
111 /* Determine amount of indentation on current line */
112
113 static long nindent(P *p)
114 {
115         P *q = pdup(p);
116         long col;
117
118         p_goto_bol(q);
119         do {
120                 col = q->col;
121         } while (cpara(pgetc(q)));
122         prm(q);
123         return col;
124 }
125
126 /* Get indentation prefix column */
127
128 static long prefix(P *p)
129 {
130         long len;
131         P *q = pdup(p);
132
133         p_goto_bol(q);
134         while (cpara(brch(q)))
135                 pgetc(q);
136         while (!pisbol(q))
137                 if (!joe_isblank(p->b->o.charmap, prgetc(q))) {
138                         pgetc(q);
139                         break;
140                 }
141         len = q->col;
142         prm(q);
143         return len;
144 }
145
146 /* Move pointer to beginning of paragraph
147  *
148  * This function simply moves backwards until it sees:
149  *  0) The beginning of the file
150  *  1) A blank line
151  *  2) A line with a different indentation prefix
152  *  3) A line with indentation greater than that of the line we started with
153  *  4) A line with indentation less than that of the starting line, but with
154  *     a blank line (or beginning of file) preceeding it.
155  */
156
157 int within = 0;
158
159 P *pbop(P *p)
160 {
161         long indent;
162         long prelen;
163
164         p_goto_bol(p);
165         indent = nindent(p);
166         prelen = prefix(p);
167         while (!pisbof(p) && (!within || !markb || p->byte > markb->byte)) {
168                 long ind;
169                 long len;
170
171                 pprevl(p);
172                 p_goto_bol(p);
173                 ind = nindent(p);
174                 len = prefix(p);
175                 if (pisnpara(p) || len != prelen) {
176                         pnextl(p);
177                         break;
178                 }
179                 if (ind > indent)
180                         break;
181                 if (ind < indent) {
182                         if (pisbof(p))
183                                 break;
184                         pprevl(p);
185                         p_goto_bol(p);
186                         if (pisnpara(p)) {
187                                 pnextl(p);
188                                 break;
189                         } else {
190                                 pnextl(p);
191                                 pnextl(p);
192                                 break;
193                         }
194                 }
195         }
196         return p;
197 }
198
199 /* Move pointer to end of paragraph.  Pointer must already be on first
200  * line of paragraph for this to work correctly.
201  *
202  * This function moves forwards until it sees:
203  *  0) The end of the file.
204  *  1) A blank line
205  *  2) A line with indentation different from the second line of the paragraph
206  *  3) A line with prefix column different from first line
207  */
208
209 P *peop(P *p)
210 {
211         long indent;
212         long prelen;
213
214         if (!pnextl(p) || pisnpara(p) || (within && markk && p->byte >= markk->byte))
215                 return p;
216         indent = nindent(p);
217         prelen = prefix(p);
218         while (pnextl(p) && (!within || !markk || p->byte < markk->byte)) {
219                 long ind = nindent(p);
220                 long len = prefix(p);
221
222                 if (ind != indent || len != prelen || pisnpara(p))
223                         break;
224         }
225         return p;
226 }
227
228 /* Motion commands */
229
230 int ubop(BW *bw)
231 {
232         P *q = pdup(bw->cursor);
233
234  up:
235         while (pisnpara(q) && !pisbof(q) && (!within || !markb || q->byte > markb->byte))
236                 pprevl(q);
237         pbop(q);
238         if (q->byte != bw->cursor->byte) {
239                 pset(bw->cursor, q);
240                 prm(q);
241                 return 0;
242         } else if (!pisbof(q)) {
243                 prgetc(q);
244                 goto up;
245         } else {
246                 prm(q);
247                 return -1;
248         }
249 }
250
251 int ueop(BW *bw)
252 {
253         P *q = pdup(bw->cursor);
254
255  up:
256         while (pisnpara(q) && !piseof(q))
257                 pnextl(q);
258         pbop(q);
259         peop(q);
260         if (q->byte != bw->cursor->byte) {
261                 pset(bw->cursor, q);
262                 prm(q);
263                 return 0;
264         } else if (!piseof(q)) {
265                 pnextl(q);
266                 goto up;
267         } else {
268                 prm(q);
269                 return -1;
270         }
271 }
272
273 /* Wrap word.  If 'french' is set, only one space will be placed
274  * after . ? or !
275  */
276
277 void wrapword(P *p, long int indent, int french, unsigned char *indents)
278 {
279         P *q;
280         int rmf = 0;
281         int c;
282         long to = p->byte;
283
284         /* Get indentation prefix from beginning of line */
285 /*
286         if(!indents) {
287                 int f = 0;
288                 P *r = pdup(p);
289
290                 p_goto_bol(r);
291                 q = pdup(r);
292                 while(cpara(c = brc(q))) {
293                         if(!joe_isblank(c))
294                                 f = 1;
295                         pgetc(q);
296                 }
297                 if(f) {
298                         indents = brs(r, q->byte-r->byte);
299                         rmf = 1;
300                         if(indents[0] == '/' && indents[1] == '*')
301                                 indents[0] = ' ';
302                 }
303                 prm(r);
304                 prm(q);
305         }
306 */
307
308         /* Get to beginning of word */
309         while (!pisbol(p) && piscol(p) > indent && !joe_isblank(p->b->o.charmap, prgetc(p)))
310                 /* do nothing */;
311
312         /* If we found the beginning of a word... */
313         if (!pisbol(p) && piscol(p) > indent) {
314                 /* Move q to two (or one if 'french' is set) spaces after end of previous
315                    word */
316                 q = pdup(p);
317                 while (!pisbol(q))
318                         if (!joe_isblank(p->b->o.charmap, (c = prgetc(q)))) {
319                                 pgetc(q);
320                                 if ((c == '.' || c == '?' || c == '!')
321                                     && q->byte != p->byte && !french)
322                                         pgetc(q);
323                                 break;
324                         }
325                 pgetc(p);
326
327                 /* Delete space between start of word and end of previous word */
328                 to -= p->byte - q->byte;
329                 bdel(q, p);
330                 prm(q);
331
332                 /* Move word to beginning of next line */
333                 binsc(p, '\n');
334                 ++to;
335                 if (p->b->o.crlf)
336                         ++to;
337                 pgetc(p);
338
339                 /* Indent to left margin */
340                 if (indents) {
341                         binss(p, indents);
342                         to += strlen((char *)indents);
343                 } else
344                         while (indent--) {
345                                 binsc(p, ' ');
346                                 ++to;
347                         }
348
349                 if (rmf)
350                         free(indents);
351         }
352
353         /* Move cursor back to original position */
354         pfwrd(p, to - p->byte);
355 }
356
357 /* Reformat paragraph */
358
359 int uformat(BW *bw)
360 {
361         long indent;
362         unsigned char *indents;
363         B *buf;
364         P *b;
365         long curoff;
366         int c;
367         P *p, *q;
368
369         p = pdup(bw->cursor);
370         p_goto_bol(p);
371
372         /* Do nothing if we're not on a paragraph line */
373         if (pisnpara(p)) {
374                 prm(p);
375                 return 0;
376         }
377
378         /* Move p to beginning of paragraph, bw->cursor to end of paragraph and
379          * set curoff to original cursor offset within the paragraph */
380         pbop(p);
381         curoff = bw->cursor->byte - p->byte;
382         pset(bw->cursor, p);
383         peop(bw->cursor);
384
385         /* Ensure that paragraph ends on a beginning of a line */
386         if (!pisbol(bw->cursor))
387                 binsc(bw->cursor, '\n'), pgetc(bw->cursor);
388
389         /* Record indentation of second line of paragraph, of first line if there
390          * is only one line */
391         q = pdup(p);
392         pnextl(q);
393         if (q->line != bw->cursor->line) {
394                 P *r = pdup(q);
395
396                 indent = nindent(q);
397                 pcol(r, indent);
398                 indents = brs(q, r->byte - q->byte);
399                 prm(r);
400         } else {
401                 P *r = pdup(p);
402
403                 indent = nindent(p);
404                 pcol(r, indent);
405                 indents = brs(p, r->byte - p->byte);
406                 prm(r);
407         }
408         prm(q);
409
410         /* But if the left margin is greater, we use that instead */
411         if (bw->o.lmargin > indent)
412                 indent = bw->o.lmargin;
413
414         /* Cut paragraph into new buffer */
415
416         /* New buffer needs to inherit UTF-8 and CR-LF options */
417         buf = bcpy(p, bw->cursor);
418         buf->o.crlf = p->b->o.crlf;
419         buf->o.charmap = p->b->o.charmap;
420         bdel(p, bw->cursor);
421
422         /* text is in buffer.  insert it at cursor */
423
424         /* Do first line */
425         b = pdup(buf->bof);
426
427         while (!piseof(b)) {
428                 /* Set cursor position if we're at original offset */
429                 if (b->byte == curoff)
430                         pset(bw->cursor, p);
431
432                 /* Get character from buffer */
433                 c = pgetc(b);
434
435                 /* Stop if we found end of line */
436                 if (c == '\n') {
437                         prgetc(b);
438                         break;
439                 }
440
441                 /* Stop if we found white-space followed by end of line */
442                 if (joe_isblank(b->b->o.charmap, c) && piseolblank(b))
443                         break;
444
445                 /* Insert character, advance pointer */
446                 binsc(p, c);
447                 pgetc(p);
448
449                 /* Do word wrap if we reach right margin */
450                 if (piscol(p) > bw->o.rmargin && !joe_isblank(p->b->o.charmap,c)) {
451                         wrapword(p, indent, bw->o.french, indents);
452                         break;
453                 }
454         }
455
456         /* Do rest */
457
458         while (!piseof(b)) {
459                 c = brch(b);
460                 if (joe_isblank(b->b->o.charmap,c) || c == '\n') {
461                         int f = 0;
462                         P *d;
463                         int g;
464
465                         /* Set f if there are two spaces after . ? or ! instead of one */
466                         /* (What is c was '\n'?) */
467                         d=pdup(b);
468                         g=prgetc(d);
469                         if (g=='.' || g=='?' || g=='!') {
470                                 pset(d,b);
471                                 pgetc(d);
472                                 if (joe_isspace(bw->b->o.charmap,brch(d)))
473                                         f = 1;
474                         }
475                         prm(d);
476
477                         /* Skip past the whitespace.  Skip over indentations */
478  loop:
479                         c = brch(b);
480                         if (c == '\n') {
481                                 if (b->byte == curoff)
482                                         pset(bw->cursor, p);
483
484                                 pgetc(b);
485                                 while (cpara(c=brch(b))) {
486                                         if (b->byte == curoff)
487                                                 pset(bw->cursor, p);
488                                         pgetc(b);
489                                 }
490                         }
491
492                         if (joe_isblank(b->b->o.charmap,c)) {
493                                 if(b->byte == curoff)
494                                         pset(bw->cursor, p);
495                                 pgetc(b);
496                                 goto loop;
497                         }
498
499                         /* Insert proper amount of whitespace */
500                         if (!piseof(b)) {
501                                 if (f && !bw->o.french)
502                                         binsc(p, ' '), pgetc(p);
503                                 binsc(p, ' ');
504                                 pgetc(p);
505                         }
506                 } else {
507                         /* Insert characters of word and wrap if necessary */
508                         if (b->byte == curoff)
509                                 pset(bw->cursor, p);
510
511                         binsc(p, pgetc(b));
512                         pgetc(p);
513                         if (piscol(p) > bw->o.rmargin)
514                                 wrapword(p, indent, bw->o.french, indents);
515                 }
516         }
517
518         binsc(p, '\n');
519         prm(p);
520         brm(buf);
521         free(indents);
522         return 0;
523 }
524
525 /* Format entire block */
526
527 int ufmtblk(BW *bw)
528 {
529         if (markv(1) && bw->cursor->byte >= markb->byte && bw->cursor->byte <= markk->byte) {
530                 markk->end = 1;
531                 utomarkk(bw);
532                 within = 1;
533                 do {
534                         ubop(bw), uformat(bw);
535                 } while (bw->cursor->byte > markb->byte);
536                 within = 0;
537                 markk->end = 0;
538                 if (lightoff)
539                         unmark(bw);
540                 return 0;
541         } else
542                 return uformat(bw);
543 }