1 /*    e_block.cpp
2  *
3  *    Copyright (c) 1994-1996, Marko Macek
4  *
5  *    You may distribute under the terms of either the GNU General Public
6  *    License or the Artistic License, as specified in the README file.
7  *
8  */
9 
10 #include "c_config.h"
11 #include "e_undo.h"
12 #include "i_modelview.h"
13 #include "i_view.h"
14 #include "o_buflist.h"
15 #include "s_files.h"
16 #include "s_util.h"
17 
18 #include <stdio.h>
19 
20 ///////////////////////////////////////////////////////////////////////////////
21 // Block Commands                                                            //
22 ///////////////////////////////////////////////////////////////////////////////
23 
SetBB(const EPoint & M)24 int EBuffer::SetBB(const EPoint& M) {
25     EPoint OldBB = BB;
26     int MinL, MaxL;
27 
28     if (BB.Row == M.Row && BB.Col == M.Col) return 1;
29 #ifdef CONFIG_UNDOREDO
30     if (PushBlockData() == 0) return 0;
31 #endif
32     BB = M;
33     if (OldBB.Row == -1) OldBB = BE;
34     if ((OldBB.Col != BB.Col) && (BlockMode == bmColumn)) BlockRedraw();
35     MinL = Min(OldBB.Row, BB.Row);
36     MaxL = Max(OldBB.Row, BB.Row);
37     if (MinL != -1)
38         if (MinL <= MaxL) Draw(MinL, MaxL);
39     return 1;
40 }
41 
SetBE(const EPoint & M)42 int EBuffer::SetBE(const EPoint& M) {
43     EPoint OldBE = BE;
44     int MinL, MaxL;
45 
46     if (BE.Row == M.Row && BE.Col == M.Col) return 1;
47 #ifdef CONFIG_UNDOREDO
48     if (PushBlockData() == 0) return 0;
49 #endif
50     BE = M;
51     if (OldBE.Row == -1) OldBE = BB;
52     if ((OldBE.Col != BE.Col) && (BlockMode == bmColumn)) BlockRedraw();
53     MinL = Min(OldBE.Row, BE.Row);
54     MaxL = Max(OldBE.Row, BE.Row);
55     if (MinL != -1)
56         if (MinL <= MaxL) Draw(MinL, MaxL);
57     return 1;
58 }
59 
60 // check if there is active or valid selection
CheckBlock()61 int EBuffer::CheckBlock() {
62     if (BB.Row == -1 && BE.Row == 1) {
63         BB.Col = -1;
64         BE.Col = -1;
65         return 0;
66     }
67     if (BB.Row == -1 || BE.Row == -1) return 0;
68     if (BB.Row >= RCount) BB.Row = RCount - 1;
69     if (BE.Row >= RCount) BE.Row = RCount - 1;
70     switch(BlockMode) {
71     case bmLine:
72         BB.Col = 0;
73         BE.Col = 0;
74         if (BB.Row >= BE.Row) return 0;
75         break;
76     case bmColumn:
77         if (BB.Col >= BE.Col) return 0;
78         if (BB.Row >= BE.Row) return 0;
79         break;
80     case bmStream:
81         if (BB.Row > BE.Row) return 0;
82         if (BB.Row == BE.Row && BB.Col >= BE.Col) return 0;
83         break;
84     }
85     return 1;
86 }
87 
BlockRedraw()88 int EBuffer::BlockRedraw() {
89     if (BB.Row == -1 || BE.Row == -1) return 0;
90     Draw(BB.Row, BE.Row);
91     return 1;
92 }
93 
94 
BlockBegin()95 int EBuffer::BlockBegin() {
96     EPoint X;
97 
98     X.Row = VToR(CP.Row);
99     X.Col = CP.Col;
100     CheckBlock();
101     SetBB(X);
102     return 1;
103 }
104 
BlockEnd()105 int EBuffer::BlockEnd() {
106     EPoint X;
107 
108     X.Row = VToR(CP.Row);
109     X.Col = CP.Col;
110     CheckBlock();
111     SetBE(X);
112     return 1;
113 }
114 
BlockUnmark()115 int EBuffer::BlockUnmark() {
116     EPoint Null(-1,-1);
117 
118     SetBB(BE);
119     SetBE(Null);
120     SetBB(Null);
121     AutoExtend = 0;
122     return 1;
123 }
124 
BlockCut(int Append)125 int EBuffer::BlockCut(int Append) {
126     if (BlockCopy(Append) && BlockKill()) return 1;
127     return 0;
128 }
129 
BlockCopy(int Append,int clipboard)130 int EBuffer::BlockCopy(int Append, int clipboard) {
131     EPoint B, E;
132     int L;
133     int SL, OldCount;
134 
135     AutoExtend = 0;
136     if (CheckBlock() == 0) return 0;
137     if (RCount == 0) return 0;
138     if (SSBuffer == 0) return 0;
139     if (Append) {
140         if (SystemClipboard)
141             GetPMClip(clipboard);
142     } else
143         SSBuffer->Clear();
144     SSBuffer->BlockMode = BlockMode;
145     BFI(SSBuffer, BFI_TabSize) = BFI(this, BFI_TabSize);
146     BFI(SSBuffer, BFI_ExpandTabs) = BFI(this, BFI_ExpandTabs);
147     BFI(SSBuffer, BFI_Undo) = 0;
148     B = BB;
149     E = BE;
150     OldCount = SL = SSBuffer->RCount;
151     switch (BlockMode) {
152     case bmLine:
153         for (L = B.Row; L < E.Row; L++) {
154             if (SSBuffer->InsLine(SL, 0) == 0) return 0;
155             if (SSBuffer->InsLineText(SL, 0, -1, 0, RLine(L)) == 0) return 0;
156             SL++;
157         }
158         break;
159 
160     case bmColumn:
161         for (L = B.Row; L < E.Row; L++) {
162             if (SSBuffer->InsLine(SL, 0) == 0) return 0;
163             if (SSBuffer->InsLineText(SL, 0, E.Col - B.Col, B.Col, RLine(L)) == 0) return 0;
164             if (SSBuffer->PadLine(SL, E.Col - B.Col) == 0) return 0;
165             SL++;
166         }
167         break;
168 
169     case bmStream:
170         if (B.Row == E.Row) {
171             if (SSBuffer->InsLine(SL, 0) == 0) return 0;
172             if (SSBuffer->InsLineText(SL, 0, E.Col - B.Col, B.Col, RLine(B.Row)) == 0) return 0;
173         } else {
174             if (SSBuffer->InsLine(SL, 0) == 0) return 0;
175             if (SSBuffer->InsLineText(SL, 0, -1, B.Col, RLine(B.Row)) == 0) return 0;
176             SL++;
177             for (L = B.Row + 1; L < E.Row; L++) {
178                 if (SSBuffer->InsLine(SL, 0) == 0) return 0;
179                 if (SSBuffer->InsLineText(SL, 0, -1, 0, RLine(L)) == 0) return 0;
180                 SL++;
181             }
182             if (SSBuffer->InsLine(SL, 0) == 0) return 0;
183             if (SSBuffer->InsLineText(SL, 0, E.Col, 0, RLine(E.Row)) == 0) return 0;
184         }
185         if (Append && OldCount > 0)
186             if (SSBuffer->JoinLine(OldCount - 1, 0) == 0)
187                 return 0;
188         break;
189     }
190     if (SystemClipboard)
191         PutPMClip(clipboard);
192     return 1;
193 }
194 
BlockPasteStream(int clipboard)195 int EBuffer::BlockPasteStream(int clipboard) {
196     BlockMode = bmStream;
197     return BlockPaste(clipboard);
198 }
199 
BlockPasteLine(int clipboard)200 int EBuffer::BlockPasteLine(int clipboard) {
201     BlockMode = bmLine;
202     return BlockPaste(clipboard);
203 }
204 
BlockPasteColumn(int clipboard)205 int EBuffer::BlockPasteColumn(int clipboard) {
206     BlockMode = bmColumn;
207     return BlockPaste(clipboard);
208 }
209 
BlockPaste(int clipboard)210 int EBuffer::BlockPaste(int clipboard) {
211     EPoint B, E;
212     int L, BL;
213 
214     if (SystemClipboard)
215         GetPMClip(clipboard);
216 
217     if (SSBuffer == 0) return 0;
218     if (SSBuffer->RCount == 0) return 0;
219     AutoExtend = 0;
220     BFI(SSBuffer, BFI_TabSize) = BFI(this, BFI_TabSize);
221     BFI(SSBuffer, BFI_ExpandTabs) = BFI(this, BFI_ExpandTabs);
222     BFI(SSBuffer, BFI_Undo) = 0;
223     BlockUnmark();
224     B.Row = VToR(CP.Row);
225     B.Col = CP.Col;
226     BL = B.Row;
227     switch(BlockMode) {
228     case bmLine:
229         B.Col = 0;
230         for (L = 0; L < SSBuffer->RCount; L++) {
231             if (InsLine(BL, 0) == 0) return 0;
232             if (InsLineText(BL, 0, SSBuffer->LineLen(L), 0, SSBuffer->RLine(L)) == 0) return 0;
233             BL++;
234         }
235         E.Row = BL;
236         E.Col = 0;
237         SetBB(B);
238         SetBE(E);
239         break;
240 
241     case bmColumn:
242         for (L = 0; L < SSBuffer->RCount; L++) {
243             if (AssertLine(BL) == 0) return 0;
244             if (InsLineText(BL, B.Col, SSBuffer->LineLen(L), 0, SSBuffer->RLine(L)) == 0) return 0;
245             if (TrimLine(BL) == 0) return 0;
246             BL++;
247         }
248         if (AssertLine(BL) == 0) return 0;
249         E.Row = BL;
250         E.Col = B.Col + SSBuffer->LineLen(0);
251         SetBB(B);
252         SetBE(E);
253         break;
254 
255     case bmStream:
256         if (SSBuffer->RCount > 1)
257             if (SplitLine(B.Row, B.Col) == 0) return 0;
258         if (InsLineText(B.Row, B.Col, SSBuffer->LineLen(0), 0, SSBuffer->RLine(0)) == 0) return 0;
259         E = B;
260         E.Col += SSBuffer->LineLen(0);
261         BL++;
262         if (SSBuffer->RCount > 1) {
263             for (L = 1; L < SSBuffer->RCount - 1; L++) {
264                 if (InsLine(BL, 0) == 0) return 0;
265                 if (InsLineText(BL, 0, SSBuffer->LineLen(L), 0, SSBuffer->RLine(L)) == 0) return 0;
266                 BL++;
267             }
268             L = SSBuffer->RCount - 1;
269             if (InsLineText(BL, 0, SSBuffer->LineLen(L), 0, SSBuffer->RLine(L)) == 0) return 0;
270             E.Col = SSBuffer->LineLen(L);
271             E.Row = BL;
272         }
273         SetBB(B);
274         SetBE(E);
275         break;
276     }
277     return 1;
278 }
279 
BlockKill()280 int EBuffer::BlockKill() {
281     EPoint B, E;
282     int L;
283     int Y = -1;
284 
285     AutoExtend = 0;
286     if (CheckBlock() == 0) return 0;
287     if (RCount <= 0) return 0;
288     B = BB;
289     E = BE;
290     Draw(B.Row, -1);
291     //    if (MoveToPos(B.Col, B.Row) == 0) return 0;
292 
293 #ifdef CONFIG_UNDOREDO
294     if (BFI(this, BFI_Undo) == 1) {
295         if (PushULong(CP.Col) == 0) return 0;
296         if (PushULong(CP.Row) == 0) return 0;
297         if (PushUChar(ucPosition) == 0) return 0;
298     }
299 #endif
300 
301     switch (BlockMode) {
302     case bmLine:
303         Y = VToR(CP.Row);
304         if (Y >= B.Row) {
305             if (Y >= E.Row) {
306                 if (SetPosR(CP.Col, Y - (E.Row - B.Row)) == 0) return 0;
307             } else {
308                 if (SetPosR(CP.Col, B.Row) == 0) return 0;
309             }
310         }
311         for (L = B.Row; L < E.Row; L++)
312             if (DelLine(B.Row) == 0) return 0;
313         break;
314 
315     case bmColumn:
316         Y = VToR(CP.Row);
317         if (Y >= B.Row && Y < E.Row) {
318             if (CP.Col >= B.Col) {
319                 if (CP.Col >= E.Col) {
320                     if (SetPos(CP.Col - (E.Col - B.Col), CP.Row) == 0) return 0;
321                 } else {
322                     if (SetPos(B.Col, CP.Row) == 0) return 0;
323                 }
324             }
325         }
326         for (L = B.Row; L < E.Row; L++)
327             if (DelText(L, B.Col, E.Col - B.Col) == 0) return 0;
328         break;
329 
330     case bmStream:
331         Y = VToR(CP.Row);
332 
333         if (B.Row == E.Row) {
334             if (Y == B.Row) {
335                 if (CP.Col >= B.Col) {
336                     if (CP.Col >= E.Col) {
337                         if (SetPos(CP.Col - (E.Col - B.Col), CP.Row) == 0) return 0;
338                     } else {
339                         if (SetPos(B.Col, CP.Row) == 0) return 0;
340                     }
341                 }
342             }
343             if (DelText(B.Row, B.Col, E.Col - B.Col) == 0) return 0;
344         } else {
345             if (Y >= B.Row) {
346                 if (Y > E.Row || (Y == E.Row && E.Col == 0)) {
347                     if (SetPosR(CP.Col, Y - (E.Row - B.Row)) == 0) return 0;
348                 } else if (Y == E.Row) {
349                     if (CP.Col >= E.Col) {
350                         if (SetPosR(CP.Col - E.Col + B.Col, B.Row) == 0) return 0;
351                     } else {
352                         if (SetPosR(B.Col, B.Row) == 0) return 0;
353                     }
354                 } else {
355                     if (SetPosR(B.Col, B.Row) == 0) return 0;
356                 }
357             }
358             if (DelText(E.Row, 0, E.Col) == 0) return 0;
359             for (L = B.Row + 1; L < E.Row; L++)
360                 if (DelLine(B.Row + 1) == 0) return 0;
361             if (DelText(B.Row, B.Col, -1) == 0) return 0;
362             if (JoinLine(B.Row, B.Col) == 0) return 0;
363         }
364         break;
365     }
366     return BlockUnmark();
367 }
368 
369 // remove selected text and paste information from clipboard to replace it
BlockPasteOver(int clipboard)370 int EBuffer::BlockPasteOver(int clipboard) {
371     // if there is existing selection, remove it's contents
372     if (CheckBlock())
373     {
374         BlockKill();
375     }
376 
377     // paste text from clipboard
378     if (BlockPaste(clipboard))
379     {
380         // go to end of selection
381         SetPos(BE.Col, BE.Row);
382 
383         // remove selection
384         return BlockUnmark();
385     }
386 
387     return 0;
388 }
389 
390 // XXX clipboard ???
ClipClear(int clipboard)391 int EBuffer::ClipClear(int clipboard) {
392     if (SSBuffer == 0)
393         return 0;
394     SSBuffer->Clear();
395     if (SystemClipboard)
396         PutPMClip(clipboard);
397     return 1;
398 }
399 
BlockIndent()400 int EBuffer::BlockIndent() {
401     EPoint B, E;
402     int L;
403 
404     AutoExtend = 0;
405     if (CheckBlock() == 0) return 0;
406     if (RCount <= 0) return 0;
407     B = BB;
408     E = BE;
409     Draw(B.Row, E.Row);
410     if (SetPosR(B.Col, B.Row) == 0) return 0;
411     for (L = B.Row; L <= E.Row; L++) {
412         switch (BlockMode) {
413         case bmStream:
414         case bmLine:
415             if (L < E.Row || E.Col != 0) {
416                 int I = LineIndented(L) + 1;
417                 IndentLine(L, I);
418             }
419             break;
420         case bmColumn:
421             if (L < E.Row) {
422                 if (InsText(L, B.Col, 1, 0) == 0) return 0;
423                 if (DelText(L, E.Col, 1) == 0) return 0;
424             }
425             break;
426         }
427     }
428     if (SetPosR(B.Col, B.Row) == 0) return 0;
429     return 1;
430 }
431 
BlockUnindent()432 int EBuffer::BlockUnindent() {
433     EPoint B, E;
434     int L;
435 
436     AutoExtend = 0;
437     if (CheckBlock() == 0) return 0;
438     if (RCount <= 0) return 0;
439     B = BB;
440     E = BE;
441     Draw(B.Row, E.Row);
442     if (SetPosR(B.Col, B.Row) == 0) return 0;
443     for (L = B.Row; L <= E.Row; L++) {
444         switch (BlockMode) {
445         case bmStream:
446         case bmLine:
447             if (L < E.Row || E.Col != 0) {
448                 int I = LineIndented(L) - 1;
449                 if (I >= 0)
450                     IndentLine(L, I);
451             }
452             break;
453         case bmColumn:
454             if (L < E.Row) {
455                 if (InsText(L, E.Col, 1, 0) == 0) return 0;
456                 if (DelText(L, B.Col, 1) == 0) return 0;
457             }
458             break;
459         }
460     }
461     if (SetPosR(B.Col, B.Row) == 0) return 0;
462     return 1;
463 }
464 
BlockClear()465 int EBuffer::BlockClear() {
466     return 0;
467 }
468 
BlockMarkStream()469 int EBuffer::BlockMarkStream() {
470     if (BlockMode != bmStream) BlockUnmark();
471     BlockMode= bmStream;
472     if (AutoExtend) AutoExtend = 0;
473     else {
474         BlockUnmark();
475         AutoExtend = 1;
476     }
477     return 1;
478 }
479 
BlockMarkLine()480 int EBuffer::BlockMarkLine() {
481     if (BlockMode != bmLine) BlockUnmark();
482     BlockMode= bmLine;
483     if (AutoExtend) AutoExtend = 0;
484     else {
485         BlockUnmark();
486         AutoExtend = 1;
487     }
488     return 1;
489 }
490 
BlockMarkColumn()491 int EBuffer::BlockMarkColumn() {
492     if (BlockMode != bmColumn) BlockUnmark();
493     BlockMode= bmColumn;
494     if (AutoExtend) AutoExtend = 0;
495     else {
496         BlockUnmark();
497         AutoExtend = 1;
498     }
499     return 1;
500 }
501 
BlockExtendBegin()502 int EBuffer::BlockExtendBegin() {
503     CheckBlock();
504     ExtendGrab = 0;
505     AutoExtend = 0;
506     int Y = VToR(CP.Row);
507 
508     switch (BlockMode) {
509     case bmStream:
510         if ((Y == BB.Row) && (CP.Col == BB.Col)) ExtendGrab |= 1;
511         if ((Y == BE.Row) && (CP.Col == BE.Col)) ExtendGrab |= 2;
512         break;
513     case bmLine:
514         if (Y == BB.Row) ExtendGrab |= 1;
515         if (Y == BE.Row) ExtendGrab |= 2;
516         break;
517     case bmColumn:
518         if (Y == BB.Row) ExtendGrab |= 1;
519         if (Y == BE.Row) ExtendGrab |= 2;
520         if (CP.Col == BB.Col) ExtendGrab |= 4;
521         if (CP.Col == BE.Col) ExtendGrab |= 8;
522         break;
523     }
524 
525     if (ExtendGrab == 0) {
526         BlockBegin();
527         BlockEnd();
528         if (BlockMode == bmColumn)
529             ExtendGrab = 1 | 2 | 4 | 8;
530         else
531             ExtendGrab = 1 | 2;
532     }
533     return 1;
534 }
535 
BlockExtendEnd()536 int EBuffer::BlockExtendEnd() {
537     EPoint T, B, E;
538 
539     CheckBlock();
540     B = BB;
541     E = BE;
542     switch (BlockMode) {
543     case bmLine:
544         if (ExtendGrab & 1) { B.Row = VToR(CP.Row); B.Col = 0; }
545         else if (ExtendGrab & 2) { E.Row = VToR(CP.Row); E.Col = 0; }
546         if (B.Row > E.Row) {
547             T = B;
548             B = E;
549             E = T;
550         }
551         break;
552     case bmStream:
553         if (ExtendGrab & 1) { B.Col = CP.Col; B.Row = VToR(CP.Row); }
554         else if (ExtendGrab & 2) { E.Col = CP.Col; E.Row = VToR(CP.Row); }
555         if ((B.Row > E.Row) ||
556             ((B.Row == E.Row) && (B.Col > E.Col))) {
557             T = B;
558             B = E;
559             E = T;
560         }
561         break;
562     case bmColumn:
563         if (ExtendGrab & 1) B.Row = VToR(CP.Row);
564         else if (ExtendGrab & 2) E.Row = VToR(CP.Row);
565         if (ExtendGrab & 4) B.Col = CP.Col;
566         else if (ExtendGrab & 8) E.Col = CP.Col;
567         if (B.Row > E.Row) {
568             int T;
569 
570             T = B.Row;
571             B.Row = E.Row;
572             E.Row = T;
573         }
574         if (B.Col > E.Col) {
575             int T;
576 
577             T = B.Col;
578             B.Col = E.Col;
579             E.Col = T;
580         }
581         break;
582     }
583     SetBB(B);
584     SetBE(E);
585     ExtendGrab = 0;
586     AutoExtend = 0;
587     return 1;
588 }
589 
BlockIsMarked()590 int EBuffer::BlockIsMarked() {
591     if ((BB.Row != -1) && (BE.Row != -1) && (BB.Col != -1) && (BE.Col != -1)) return 1;
592     return 0;
593 }
594 
BlockReIndent()595 int EBuffer::BlockReIndent() {
596     EPoint P = CP;
597     EPoint B, E;
598 
599     AutoExtend = 0;
600     if (CheckBlock() == 0) return 0;
601     if (RCount <= 0) return 0;
602     B = BB;
603     E = BE;
604     Draw(B.Row, E.Row);
605     for (int i = B.Row; i < E.Row; i++) {
606         if (SetPosR(0, i) == 0) return 0;
607         if (LineIndent() == 0) return 0;
608     }
609     return SetPos(P.Col, P.Row);
610 }
611 
BlockSelectWord()612 int EBuffer::BlockSelectWord() {
613     int Y = VToR(CP.Row);
614     PELine L = RLine(Y);
615     int P;
616     int C;
617 
618     if (BlockUnmark() == 0) return 0;
619     BlockMode = bmStream;
620 
621     P = CharOffset(L, CP.Col);
622 
623     if (P >= L->Count) return 0;
624     C = ChClassK(L->Chars[P]);
625 
626     while ((P > 0) && (C == ChClassK(L->Chars[P - 1]))) P--;
627     if (SetBB(EPoint(Y, ScreenPos(L, P))) == 0) return 0;
628     while ((P < L->Count) && (C == ChClassK(L->Chars[P]))) P++;
629     if (SetBE(EPoint(Y, ScreenPos(L, P))) == 0) return 0;
630     return 1;
631 }
632 
BlockSelectLine()633 int EBuffer::BlockSelectLine() {
634     int Y = VToR(CP.Row);
635     if (BlockUnmark() == 0) return 0;
636     BlockMode = bmStream;
637 
638     if (SetBB(EPoint(Y, 0)) == 0) return 0;
639     if (Y == RCount - 1) {
640         if (SetBE(EPoint(Y, LineLen(Y))) == 0) return 0;
641     } else {
642         if (SetBE(EPoint(Y + 1, 0)) == 0) return 0;
643     }
644     return 1;
645 }
646 
BlockSelectPara()647 int EBuffer::BlockSelectPara() {
648     return 1;
649 }
650 
BlockWriteTo(const char * AFileName,int Append)651 int EBuffer::BlockWriteTo(const char *AFileName, int Append) {
652     //int error = 0;
653     EPoint B, E;
654     int L;
655     PELine LL;
656     int A, Z;
657     FILE *fp;
658     int bc = 0, lc = 0, oldc = 0;
659 
660     AutoExtend = 0;
661     if (CheckBlock() == 0) return 0;
662     if (RCount == 0) return 0;
663     B = BB;
664     E = BE;
665     Msg(S_INFO, "Writing %s...", AFileName);
666     fp = fopen(AFileName, Append ? "ab" : "wb");
667     if (fp == NULL) goto error;
668     setvbuf(fp, FileBuffer, _IOFBF, sizeof(FileBuffer));
669     for (L = B.Row; L <= E.Row; L++) {
670         A = -1;
671         Z = -1;
672         LL = RLine(L);
673         switch (BlockMode) {
674         case bmLine:
675             if (L < E.Row) {
676                 A = 0;
677                 Z = LL->Count;
678             }
679             break;
680         case bmColumn:
681             if (L < E.Row) {
682                 A = CharOffset(LL, B.Col);
683                 Z = CharOffset(LL, E.Col);
684             }
685             break;
686         case bmStream:
687             if (B.Row == E.Row) {
688                 A = CharOffset(LL, B.Col);
689                 Z = CharOffset(LL, E.Col);
690             } else if (L == B.Row) {
691                 A = CharOffset(LL, B.Col);
692                 Z = LL->Count;
693             } else if (L < E.Row) {
694                 A = 0;
695                 Z  = LL->Count;
696             } else if (L == E.Row) {
697                 A = 0;
698                 Z = CharOffset(LL, E.Col);
699             }
700             break;
701         }
702         if (A != -1 && Z != -1) {
703             if (A < LL->Count) {
704                 if (Z > LL->Count)
705                     Z = LL->Count;
706                 if (Z > A) {
707                     if ((int)fwrite(LL->Chars + A, 1, Z - A, fp) != Z - A) {
708                         goto error;
709                     } else
710                         bc += Z - A;
711                 }
712             }
713             if (BFI(this, BFI_AddCR) == 1) {
714                 if (fputc(13, fp) < 0) goto error;
715                 else
716                     bc++;
717             }
718             if (BFI(this, BFI_AddLF) == 1) {
719                 if (fputc(10, fp) < 0)
720                     goto error;
721                 else {
722                     bc++;
723                     lc++;
724                 }
725             }
726             if (bc > 65536 + oldc) {
727                 Msg(S_INFO, "Writing %s, %d lines, %d bytes.", AFileName, lc, bc);
728                 oldc = bc;
729             }
730         }
731     }
732     fclose(fp);
733     Msg(S_INFO, "Wrote %s, %d lines, %d bytes.", AFileName, lc, bc);
734     return 1;
735 error:
736     if(fp != NULL)
737     {
738         fclose(fp);
739         unlink(AFileName);
740     }
741     View->MView->Win->Choice(GPC_ERROR, "Error", 1, "O&K", "Failed to write block to %s", AFileName);
742     return 0;
743 }
744 
BlockReadFrom(const char * AFileName,int blockMode)745 int EBuffer::BlockReadFrom(const char *AFileName, int blockMode) {
746     EBuffer *B;
747     int savesys;
748     int rc;
749 
750     if (FileExists(AFileName) == 0) {
751         View->MView->Win->Choice(GPC_ERROR, "Error", 1, "O&K", "File not found: %s", AFileName);
752         return 0;
753     }
754 
755     B = new EBuffer(0, (EModel **)&SSBuffer, AFileName);
756     if (B == 0) return 0;
757     B->SetFileName(AFileName, 0);
758     if (B->Load() == 0) {
759         delete B;
760         return 0;
761     }
762 
763     savesys = SystemClipboard;
764     SystemClipboard = 0;
765 
766     switch (blockMode) {
767     case bmColumn: rc = BlockPasteColumn(); break;
768     case bmLine:   rc = BlockPasteLine(); break;
769     default:
770     case bmStream: rc = BlockPasteStream(); break;
771     }
772 
773     SystemClipboard = savesys;
774 
775     if (rc == 0)
776         return 0;
777     delete B;
778     return 1;
779 }
780 
781 static EBuffer *SortBuffer;
782 static int SortReverse;
783 static int *SortRows = 0;
784 static int SortMinRow;
785 static int SortMaxRow;
786 static int SortMinCol;
787 static int SortMaxCol;
788 
SortProc(const void * A,const void * B)789 static int _LNK_CONV SortProc(const void *A, const void *B) {
790     int *AA = (int *)A;
791     int *BB = (int *)B;
792     ELine *LA = SortBuffer->RLine(*AA);
793     ELine *LB = SortBuffer->RLine(*BB);
794     int rc;
795 
796     if (SortMinCol == -1) {
797         int lA = LA->Count;
798         int lB = LB->Count;
799 
800         if (BFI(SortBuffer, BFI_MatchCase) == 1)
801             rc = memcmp(LA->Chars, LB->Chars, (lA < lB) ? lA : lB);
802         else
803             rc = memicmp(LA->Chars, LB->Chars, (lA < lB) ? lA : lB);
804         if (rc == 0) {
805             if (lA > lB)
806                 rc = 1;
807             else
808                 rc = -1;
809         }
810     } else {
811         int lA = LA->Count;
812         int lB = LB->Count;
813         int PA = SortBuffer->CharOffset(LA, SortMinCol);
814         int PB = SortBuffer->CharOffset(LB, SortMinCol);
815 
816         lA -= PA;
817         lB -= PB;
818         if (lA < 0 && lB < 0)
819             rc = 0;
820         else if (lA < 0 && lB > 0)
821             rc = -1;
822         else if (lA > 0 && lB < 0)
823             rc = 1;
824         else {
825             if (SortMaxCol != -1) {
826                 if (lA > SortMaxCol - SortMinCol)
827                     lA = SortMaxCol - SortMinCol;
828                 if (lB > SortMaxCol - SortMinCol)
829                     lB = SortMaxCol - SortMinCol;
830             }
831             if (BFI(SortBuffer, BFI_MatchCase) == 1)
832                 rc = memcmp(LA->Chars+ PA, LB->Chars + PB, (lA < lB) ? lA : lB);
833             else
834                 rc = memicmp(LA->Chars + PA, LB->Chars + PB, (lA < lB) ? lA : lB);
835             if (rc == 0) {
836                 if (lA > lB)
837                     rc = 1;
838                 else
839                     rc = -1;
840             }
841         }
842     }
843 
844     if (SortReverse)
845         return -rc;
846     return rc;
847 }
848 
BlockSort(int Reverse)849 int EBuffer::BlockSort(int Reverse) {
850     int rq;
851     ELine *oldL;
852 
853     if (CheckBlock() == 0) return 0;
854     if (RCount == 0) return 0;
855 
856     SortMinRow = BB.Row;
857     SortMaxRow = BE.Row;
858     if (BlockMode != bmStream || BE.Col == 0)
859         SortMaxRow--;
860 
861     if (SortMinRow >= SortMaxRow)
862         return 1;
863 
864     SortBuffer = this;
865     SortReverse = Reverse;
866     switch (BlockMode) {
867     case bmLine:
868     case bmStream:
869         SortMinCol = -1;
870         SortMaxCol = -1;
871         break;
872 
873     case bmColumn:
874         SortMinCol = BB.Col;
875         SortMaxCol = BE.Col;
876         break;
877     }
878 
879     SortRows = (int *)malloc((SortMaxRow - SortMinRow + 1) * sizeof(int));
880     if (SortRows == 0) {
881         free(SortRows);
882         return 0;
883     }
884     for (rq = 0; rq <= SortMaxRow - SortMinRow; rq++)
885         SortRows[rq] = rq + SortMinRow;
886 
887     qsort(SortRows, SortMaxRow - SortMinRow + 1, sizeof(int), SortProc);
888 
889     // now change the order of lines according to new order in Rows array.
890 
891     for (rq = 0; rq <= SortMaxRow - SortMinRow; rq++) {
892         oldL = RLine(SortRows[rq]);
893         if (InsLine(1 + rq + SortMaxRow, 0) == 0)
894             return 0;
895         if (InsChars(1 + rq + SortMaxRow, 0, oldL->Count, oldL->Chars) == 0)
896             return 0;
897     }
898 
899     for (rq = 0; rq <= SortMaxRow - SortMinRow; rq++)
900         if (DelLine(SortMinRow) == 0)
901             return 0;
902 
903     free(SortRows);
904     return 1;
905 }
906 
BlockUnTab()907 int EBuffer::BlockUnTab() {
908     EPoint B, E;
909     ELine *L;
910     int O, C;
911 
912     AutoExtend = 0;
913     if (CheckBlock() == 0) return 0;
914     if (RCount <= 0) return 0;
915     B = BB;
916     E = BE;
917     Draw(B.Row, E.Row);
918     for (int i = B.Row; i < E.Row; i++) {
919         L = RLine(i);
920         O = 0;
921         C = 0;
922         while (O < L->Count) {
923             if (L->Chars[O] == '\t') {
924                 C = NextTab(C, BFI(this, BFI_TabSize));
925 
926                 if (DelChars(i, O, 1) != 1)
927                     return 0;
928                 if (InsChars(i, O, C - O, 0) != 1)
929                     return 0;
930                 O = C;
931             } else {
932                 O++;
933                 C++;
934             }
935         }
936     }
937     return 1;
938 }
939 
BlockEnTab()940 int EBuffer::BlockEnTab() {
941     EPoint B, E;
942     ELine *L;
943     int O, C, O1, C1;
944     char tab = '\t';
945 
946     AutoExtend = 0;
947     if (CheckBlock() == 0) return 0;
948     if (RCount <= 0) return 0;
949     B = BB;
950     E = BE;
951     Draw(B.Row, E.Row);
952     for (int i = B.Row; i < E.Row; i++) {
953         L = RLine(i);
954         O = C = 0;
955         O1 = C1 = 0;
956         while (O < L->Count) {
957             if (L->Chars[O] == '\t') { // see if there are spaces to remove
958                 int C2 = NextTab(C, BFI(this, BFI_TabSize));
959                 int N = BFI(this, BFI_TabSize) - (C2 - C);
960                 if (O - O1 < N)
961                     N = O - O1;
962                 if (N > 0) {
963                     if (DelChars(i, O - N, N) != 1)
964                         return 0;
965                     O -= N;
966                     C = C2;
967                     O++;
968                     C1 = C;
969                     O1 = O;
970                 } else {
971                     O++;
972                     C = C2;
973                     O1 = O;
974                     C1 = C;
975                 }
976             } else if (L->Chars[O] != ' ') { // nope, cannot put tab here
977                 O++;
978                 C++;
979                 C1 = C;
980                 O1 = O;
981             } else if (((C % BFI(this, BFI_TabSize)) == (BFI(this, BFI_TabSize) - 1)) &&
982                        (C - C1 > 0))
983             { // reached a tab and can put one
984                 int N = BFI(this, BFI_TabSize);
985                 if (O - O1 + 1 < N) {
986                     N = O - O1 + 1;
987                 } else if (O - O1 + 1 > N) {
988                     O1 = O - N + 1;
989                 }
990                 if (DelChars(i, O1, N) != 1)
991                     return 0;
992                 if (InsChars(i, O1, 1, &tab) != 1)
993                     return 0;
994                 O1++;
995                 O = O1;
996                 C++;
997                 C1 = C;
998             } else {
999                 O++;
1000                 C++;
1001             }
1002         }
1003     }
1004     return 1;
1005 }
1006 
1007 // FindFunction -- search for line matching 'RoutineRegexp'
1008 // starting from current line + 'delta'. 'way' should be +1 or -1.
FindFunction(int delta,int way)1009 int EBuffer::FindFunction(int delta, int way) {
1010     RxNode     *regx;
1011     int         line;
1012     PELine      L;
1013     RxMatchRes  res;
1014 
1015     if (BFS(this, BFS_RoutineRegexp) == 0) {
1016         View->MView->Win->Choice(GPC_ERROR, "Error", 1,
1017                                  "O&K", "No routine regexp.");
1018         return -1;
1019     }
1020     regx = RxCompile(BFS(this, BFS_RoutineRegexp));
1021     if (regx == 0) {
1022         View->MView->Win->Choice(GPC_ERROR, "Error", 1,
1023                                  "O&K", "Failed to compile regexp '%s'",
1024                                  BFS(this, BFS_RoutineRegexp));
1025         return -1;
1026     }
1027 
1028     //** Scan backwards from the current cursor position,
1029     Msg(S_BUSY, "Matching %s", BFS(this, BFS_RoutineRegexp));
1030     line = VToR(CP.Row) + delta;
1031     while (line >= 0 && line < RCount) {
1032         L = RLine(line);
1033         if (RxExec(regx, L->Chars, L->Count, L->Chars, &res) == 1)
1034             break;
1035         line += way;
1036     }
1037     if (line < 0)
1038         line = 0;
1039     if (line >= RCount)
1040         line = RCount - 1;
1041     RxFree(regx);
1042     return line;
1043 }
1044 
1045 // Selects the current function.
BlockMarkFunction()1046 int EBuffer::BlockMarkFunction() {
1047     int by, ey;
1048 
1049     if (BlockUnmark() == 0)
1050         return 0;
1051 
1052     if ((by = FindFunction( 0, -1)) == -1)
1053         return 0;
1054     if ((ey = FindFunction(+1, +1)) == -1)
1055         return 0;
1056 
1057     //** Start and end are known. Set the block;
1058     BlockMode = bmStream;
1059     if (SetBB(EPoint(by, 0)) == 0)
1060         return 0;
1061     if (SetBE(EPoint(ey, 0)) == 0)
1062         return 0;
1063 
1064     return 1;
1065 }
1066 
IndentFunction()1067 int EBuffer::IndentFunction() {
1068     EPoint P = CP;
1069     int by, ey;
1070 
1071     if ((by = FindFunction( 0, -1)) == -1)
1072         return 0;
1073     if ((ey = FindFunction(+1, +1)) == -1)
1074         return 0;
1075 
1076     //Draw(by, ey); ?
1077     for (int i = by; i < ey; i++) {
1078         if (SetPosR(0, i) == 0)
1079             return 0;
1080         if (LineIndent() == 0)
1081             return 0;
1082     }
1083     return SetPos(P.Col, P.Row);
1084 }
1085 
MoveFunctionPrev()1086 int EBuffer::MoveFunctionPrev() {
1087     int line = FindFunction(-1, -1);
1088 
1089     if (line == -1)
1090         return 0;
1091 
1092     return CenterPosR(0, line);
1093 }
1094 
MoveFunctionNext()1095 int EBuffer::MoveFunctionNext() {
1096     int line = FindFunction(+1, +1);
1097 
1098     if (line == -1)
1099         return 0;
1100 
1101     return CenterPosR(0, line);
1102 }
1103