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