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