1 /* This is part of libio/iostream, providing -*- C++ -*- input/output.
2 Copyright (C) 1993 Free Software Foundation
4 This file is part of the GNU IO Library. This library is free
5 software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option)
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this library; see the file COPYING. If not, write to the Free
17 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 As a special exception, if you link this library with files
20 compiled with a GNU compiler to produce an executable, this does not cause
21 the resulting executable to be covered by the GNU General Public License.
22 This exception does not however invalidate any other reasons why
23 the executable file might be covered by the GNU General Public License.
25 Written by Per Bothner (bothner@cygnus.com). */
28 #pragma implementation
35 /* NOTE: Some of the code here is taken from GNU emacs */
36 /* Hence this file falls under the GNU License! */
38 // Invariants for edit_streambuf:
39 // An edit_streambuf is associated with a specific edit_string,
40 // which again is a sub-string of a specific edit_buffer.
41 // An edit_streambuf is always in either get mode or put mode, never both.
42 // In get mode, gptr() is the current position,
43 // and pbase(), pptr(), and epptr() are all NULL.
44 // In put mode, pptr() is the current position,
45 // and eback(), gptr(), and egptr() are all NULL.
46 // Any edit_streambuf that is actively doing insertion (as opposed to
47 // replacing) // must have its pptr() pointing to the start of the gap.
48 // Only one edit_streambuf can be actively inserting into a specific
49 // edit_buffer; the edit_buffer's _writer field points to that edit_streambuf.
50 // That edit_streambuf "owns" the gap, and the actual start of the
51 // gap is the pptr() of the edit_streambuf; the edit_buffer::_gap_start pointer
52 // will only be updated on an edit_streambuf::overflow().
54 int edit_streambuf::truncate()
56 str
->buffer
->delete_range(str
->buffer
->tell((buf_char
*)pptr()),
57 str
->buffer
->tell(str
->end
));
62 inline void disconnect_gap_from_file(edit_buffer
* buffer
, FILE* fp
)
64 if (buffer
->gap_start_ptr
!= &fp
->__bufp
)
66 buffer
->gap_start_normal
= fp
->__bufp
;
67 buffer
->gap_start_ptr
= &buffer
->gap_start_normal
;
71 void edit_streambuf::flush_to_buffer(edit_buffer
* buffer
)
73 if (pptr() > buffer
->_gap_start
&& pptr() < buffer
->gap_end())
74 buffer
->_gap_start
= pptr();
77 void edit_streambuf::disconnect_gap_from_file(edit_buffer
* buffer
)
79 if (buffer
->_writer
!= this) return;
80 flush_to_buffer(buffer
);
82 buffer
->_writer
= NULL
;
85 buf_index
edit_buffer::tell(buf_char
*ptr
)
87 if (ptr
<= gap_start())
90 return ptr
- gap_end() + size1();
94 buf_index
buf_cookie::tell()
96 return str
->buffer
->tell(file
->__bufp
);
100 buf_index
edit_buffer::tell(edit_mark
*mark
)
102 return tell(data
+ mark
->index_in_buffer(this));
105 // adjust the position of the gap
107 void edit_buffer::move_gap(buf_offset pos
)
111 else if (pos
> size1())
115 void edit_buffer::gap_left (int pos
)
117 register buf_char
*to
, *from
;
123 to
= from
+ gap_size();
126 /* Now copy the characters. To move the gap down,
127 copy characters up. */
131 /* I gets number of characters left to copy. */
136 /* If a quit is requested, stop copying now.
137 Change POS to be where we have actually moved the gap to. */
144 /* Move at most 32000 chars before checking again for a quit. */
152 /* Adjust markers, and buffer data structure, to put the gap at POS.
153 POS is where the loop above stopped, which may be what was specified
154 or may be where a quit was detected. */
155 adjust_markers (pos
<< 1, size1() << 1, gap_size(), data
);
157 _gap_start
= data
+ pos
;
159 if (gap_start_ptr
== &gap_start_normal
)
160 gap_start_normal
= data
+ pos
;
162 __gap_end_pos
= to
- data
;
166 void edit_buffer::gap_right (int pos
)
168 register buf_char
*to
, *from
;
174 from
= i
+ gap_end();
177 /* Now copy the characters. To move the gap up,
178 copy characters down. */
182 /* I gets number of characters left to copy. */
187 /* If a quit is requested, stop copying now.
188 Change POS to be where we have actually moved the gap to. */
195 /* Move at most 32000 chars before checking again for a quit. */
203 adjust_markers ((size1() + gap_size()) << 1, (pos
+ gap_size()) << 1,
206 _gap_start
= data
+pos
;
208 if (gap_start_ptr
== &gap_start_normal
)
209 gap_start_normal
= data
+ pos
;
211 __gap_end_pos
= from
- data
;
215 /* make sure that the gap in the current buffer is at least k
218 void edit_buffer::make_gap(buf_offset k
)
220 register buf_char
*p1
, *p2
, *lim
;
221 buf_char
*old_data
= data
;
227 /* Get more than just enough */
228 if (buf_size
> 1000) k
+= 2000;
229 else k
+= /*200;*/ 20; // for testing!
231 p1
= (buf_char
*) realloc (data
, s1
+ size2() + k
);
233 abort(); /*memory_full ();*/
235 k
-= gap_size(); /* Amount of increase. */
237 /* Record new location of text */
240 /* Transfer the new free space from the end to the gap
241 by shifting the second segment upward */
242 p2
= data
+ buf_size
;
248 /* Finish updating text location data */
252 _gap_start
= data
+ s1
;
254 if (gap_start_ptr
== &gap_start_normal
)
255 gap_start_normal
= data
+ s1
;
259 adjust_markers (s1
<< 1, (buf_size
<< 1) + 1, k
, old_data
);
263 /* Add `amount' to the position of every marker in the current buffer
264 whose current position is between `from' (exclusive) and `to' (inclusive).
265 Also, any markers past the outside of that interval, in the direction
266 of adjustment, are first moved back to the near end of the interval
267 and then adjusted by `amount'. */
269 void edit_buffer::adjust_markers(register mark_pointer low
,
270 register mark_pointer high
,
271 int amount
, buf_char
*old_data
)
273 register struct edit_mark
*m
;
274 register mark_pointer mpos
;
275 /* convert to mark_pointer */
279 _writer
->disconnect_gap_from_file(this);
281 for (m
= mark_list(); m
!= NULL
; m
= m
->chain
)
286 if (mpos
> high
&& mpos
< high
+ amount
)
287 mpos
= high
+ amount
;
291 if (mpos
> low
+ amount
&& mpos
<= low
)
294 if (mpos
> low
&& mpos
<= high
)
300 edit_streambuf
*file
;
302 for (file
= files
; file
!= NULL
; file
= file
->next
) {
303 mpos
= file
->current() - old_data
;
306 if (mpos
> high
&& mpos
< high
+ amount
)
307 mpos
= high
+ amount
;
311 if (mpos
> low
+ amount
&& mpos
<= low
)
314 if (mpos
> low
&& mpos
<= high
)
316 char* new_pos
= data
+ mpos
;
317 file
->set_current(new_pos
, file
->is_reading());
323 __off
== index at start of
buffer (need only be valid after seek
? )
326 if read
/read_delete
/overwrite mode
:
327 __endp
<= min(*gap_start_ptr
, edit_string
->end
->ptr(buffer
))
330 must have
*gap_start_ptr
== __bufp
&& *gap_start_ptr
+gap
== __endp
331 file
->edit_string
->end
->ptr(buffer
) == *gap_start_ptr
+end
336 int edit_streambuf::underflow()
338 if (!(_mode
& ios::in
))
340 struct edit_buffer
*buffer
= str
->buffer
;
341 if (!is_reading()) { // Must switch from put to get mode.
342 disconnect_gap_from_file(buffer
);
343 set_current(pptr(), 1);
345 buf_char
*str_end
= str
->end
->ptr(buffer
);
347 if (gptr() < egptr()) {
350 if ((buf_char
*)gptr() == str_end
)
352 if (str_end
<= buffer
->gap_start()) {
353 setg(eback(), gptr(), str_end
);
356 if (gptr() < buffer
->gap_start()) {
357 setg(eback(), gptr(), buffer
->gap_start());
360 if (gptr() == buffer
->gap_start()) {
361 disconnect_gap_from_file(buffer
);
362 // fp->__offset += fp->__bufp - fp->__buffer;
363 setg(buffer
->gap_end(), buffer
->gap_end(), str_end
);
366 setg(eback(), gptr(), str_end
);
370 int edit_streambuf::overflow(int ch
)
372 if (_mode
== ios::in
)
374 struct edit_buffer
*buffer
= str
->buffer
;
375 flush_to_buffer(buffer
);
378 if (is_reading()) { // Must switch from get to put mode.
379 set_current(gptr(), 0);
381 buf_char
*str_end
= str
->end
->ptr(buffer
);
383 if (pptr() < epptr()) {
386 return (unsigned char)ch
;
388 if ((buf_char
*)pptr() == str_end
|| inserting()) {
391 buffer
->_writer
->flush_to_buffer(); // Redundant?
392 buffer
->_writer
= NULL
;
393 if (pptr() >= buffer
->gap_end())
394 buffer
->move_gap(pptr() - buffer
->gap_size());
396 buffer
->move_gap(pptr());
398 setp(buffer
->gap_start(), buffer
->gap_end());
399 buffer
->_writer
= this;
402 return (unsigned char)ch
;
404 if (str_end
<= buffer
->gap_start()) {
405 // Entire string is left of gap.
406 setp(pptr(), str_end
);
408 else if (pptr() < buffer
->gap_start()) {
409 // Current pos is left of gap.
410 setp(pptr(), buffer
->gap_start());
413 else if (pptr() == buffer
->gap_start()) {
414 // Current pos is at start of gap; move to end of gap.
415 // disconnect_gap_from_file(buffer);
416 setp(buffer
->gap_end(), str_end
);
417 // __offset += __bufp - __buffer;
420 // Otherwise, current pos is right of gap.
421 setp(pptr(), str_end
);
426 void edit_streambuf::set_current(char *new_pos
, int reading
)
429 setg(new_pos
, new_pos
, new_pos
);
433 setg(NULL
, NULL
, NULL
);
434 setp(new_pos
, new_pos
);
438 // Called by fseek(fp, pos, whence) if fp is bound to a edit_buffer.
440 streampos
edit_streambuf::seekoff(streamoff offset
, _seek_dir dir
,
441 int /* =ios::in|ios::out*/)
443 struct edit_buffer
*buffer
= str
->buffer
;
444 disconnect_gap_from_file(buffer
);
445 buf_index cur_pos
= buffer
->tell((buf_char
*)current());;
446 buf_index start_pos
= buffer
->tell(str
->start
);
447 buf_index end_pos
= buffer
->tell(str
->end
);
459 if (offset
< start_pos
|| offset
> end_pos
)
461 buf_char
*new_pos
= buffer
->data
+ offset
;
462 buf_char
* gap_start
= buffer
->gap_start();
463 if (new_pos
> gap_start
) {
464 buf_char
* gap_end
= buffer
->gap_end();
465 new_pos
+= gap_end
- gap_start
;
466 if (new_pos
>= buffer
->data
+ buffer
->buf_size
) abort(); // Paranoia.
468 set_current(new_pos
, is_reading());
473 int buf_seek(void *arg_cookie
, fpos_t * pos
, int whence
)
475 struct buf_cookie
*cookie
= arg_cookie
;
476 FILE *file
= cookie
->file
;
477 struct edit_buffer
*buffer
= cookie
->str
->buffer
;
478 buf_char
*str_start
= cookie
->str
->start
->ptr(buffer
);
479 disconnect_gap_from_file(buffer
, cookie
->file
);
480 fpos_t cur_pos
, new_pos
;
481 if (file
->__bufp
<= *buffer
->gap_start_ptr
482 || str_start
>= buffer
->__gap_end
)
483 cur_pos
= str_start
- file
->__bufp
;
486 (*buffer
->gap_start_ptr
- str_start
) + (file
->__bufp
- __gap_end
);
493 new_pos
= cur_pos
+ *pos
;
496 new_pos
= end_pos
+ *pos
;
499 if (new_pos
> end_pos
) {
501 insert_nulls(new_pos
- end_pos
);
504 if (str_start
+ new_pos
<= *gap_start_ptr
&* *gap_start_ptr
< end
) {
505 __buffer
= str_start
;
507 __bufp
= str_start
+ new_pos
;
509 *buffer
->gap_start_ptr
; /* what if gap_start_ptr == &bufp ??? */
517 /* Delete characters from `from' up to (but not incl) `to' */
519 void edit_buffer::delete_range (buf_index from
, buf_index to
)
523 if ((numdel
= to
- from
) <= 0)
526 /* Make sure the gap is somewhere in or next to what we are deleting */
532 /* Relocate all markers pointing into the new, larger gap
533 to point at the end of the text before the gap. */
534 adjust_markers ((to
+ gap_size()) << 1, (to
+ gap_size()) << 1,
535 - numdel
- gap_size(), data
);
537 __gap_end_pos
= to
+ gap_size();
538 _gap_start
= data
+ from
;
541 void edit_buffer::delete_range(struct edit_mark
*start
, struct edit_mark
*end
)
543 delete_range(tell(start
), tell(end
));
546 void buf_delete_chars(struct edit_buffer
*, struct edit_mark
*, size_t)
551 edit_streambuf::edit_streambuf(edit_string
* bstr
, int mode
)
555 edit_buffer
* buffer
= bstr
->buffer
;
556 next
= buffer
->files
;
557 buffer
->files
= this;
558 char* buf_ptr
= bstr
->start
->ptr(buffer
);
560 // setb(buf_ptr, buf_ptr, 0);
561 set_current(buf_ptr
, !(mode
& ios::out
+ios::trunc
+ios::app
));
562 if (_mode
& ios::trunc
)
564 if (_mode
& ios::ate
)
565 seekoff(0, ios::end
);
568 // Called by fclose(fp) if fp is bound to a edit_buffer.
571 static int buf_close(void *arg
)
573 register struct buf_cookie
*cookie
= arg
;
574 struct edit_buffer
*buffer
= cookie
->str
->buffer
;
575 struct buf_cookie
**ptr
;
576 for (ptr
= &buffer
->files
; *ptr
!= cookie
; ptr
= &(*ptr
)->next
) ;
578 disconnect_gap_from_file(buffer
, cookie
->file
);
584 edit_streambuf::~edit_streambuf()
586 if (_mode
== ios::out
)
588 // Unlink this from list of files associated with bstr->buffer.
589 edit_streambuf
**ptr
= &str
->buffer
->files
;
590 for (; *ptr
!= this; ptr
= &(*ptr
)->next
) { }
593 disconnect_gap_from_file(str
->buffer
);
596 edit_buffer::edit_buffer()
598 buf_size
= /*200;*/ 15; /* for testing! */
599 data
= (buf_char
*)malloc(buf_size
);
605 gap_start_normal
= data
;
606 gap_start_ptr
= &gap_start_normal
;
608 __gap_end_pos
= buf_size
;
609 start_mark
.chain
= &end_mark
;
611 end_mark
.chain
= NULL
;
612 end_mark
._pos
= 2 * buf_size
+ 1;
615 // Allocate a new mark, which is adjusted by 'delta' bytes from 'this'.
616 // Restrict new mark to lie within 'str'.
618 edit_mark::edit_mark(struct edit_string
*str
, long delta
)
620 struct edit_buffer
*buf
= str
->buffer
;
621 chain
= buf
->start_mark
.chain
;
622 buf
->start_mark
.chain
= this;
623 mark_pointer size1
= buf
->size1() << 1;
624 int gap_size
= buf
->gap_size() << 1;
627 // check if new and old marks are opposite sides of gap
628 if (_pos
<= size1
&& _pos
+ delta
> size1
)
630 else if (_pos
>= size1
+ gap_size
&& _pos
+ delta
< size1
+ gap_size
)
634 if (_pos
< str
->start
->_pos
& ~1)
635 _pos
= (str
->start
->_pos
& ~ 1) + (_pos
& 1);
636 else if (_pos
>= str
->end
->_pos
)
637 _pos
= (str
->end
->_pos
& ~ 1) + (_pos
& 1);
640 // A (slow) way to find the buffer a mark belongs to.
642 edit_buffer
* edit_mark::buffer()
644 struct edit_mark
*mark
;
645 for (mark
= this; mark
->chain
!= NULL
; mark
= mark
->chain
) ;
646 // Assume that the last mark on the chain is the end_mark.
647 return (edit_buffer
*)((char*)mark
- offsetof(edit_buffer
, end_mark
));
650 edit_mark::~edit_mark()
652 // Must unlink mark from chain of owning buffer
653 struct edit_buffer
*buf
= buffer();
654 if (this == &buf
->start_mark
|| this == &buf
->end_mark
) abort();
656 for (ptr
= &buf
->start_mark
.chain
; *ptr
!= this; ptr
= &(*ptr
)->chain
) ;
660 int edit_string::length() const
662 ptrdiff_t delta
= end
->ptr(buffer
) - start
->ptr(buffer
);
663 if (end
->ptr(buffer
) <= buffer
->gap_start() ||
664 start
->ptr(buffer
) >= buffer
->gap_end())
666 return delta
- buffer
->gap_size();
669 buf_char
* edit_string::copy_bytes(int *lenp
) const
673 buf_char
*start1
, *start2
;
674 start1
= start
->ptr(buffer
);
675 if (end
->ptr(buffer
) <= buffer
->gap_start()
676 || start
->ptr(buffer
) >= buffer
->gap_end()) {
677 len1
= end
->ptr(buffer
) - start1
;
679 start2
= NULL
; // To avoid a warning from g++.
682 len1
= buffer
->gap_start() - start1
;
683 start2
= buffer
->gap_end();
684 len2
= end
->ptr(buffer
) - start2
;
686 new_str
= (char*)malloc(len1
+ len2
+ 1);
687 memcpy(new_str
, start1
, len1
);
688 if (len2
> 0) memcpy(new_str
+ len1
, start2
, len2
);
689 new_str
[len1
+len2
] = '\0';
694 // Replace the buf_chars in 'this' with ones from 'src'.
695 // Equivalent to deleting this, then inserting src, except tries
696 // to leave marks in place: Marks whose offset from the start
697 // of 'this' is less than 'src->length()' will still have the
698 // same offset in 'this' when done.
700 void edit_string::assign(struct edit_string
*src
)
702 edit_streambuf
dst_file(this, ios::out
);
703 if (buffer
== src
->buffer
/*&& ???*/) { /* overly conservative */
706 new_str
= src
->copy_bytes(&src_len
);
707 dst_file
.sputn(new_str
, src_len
);
710 edit_streambuf
src_file(src
, ios::in
);
712 int ch
= src_file
.sbumpc();
713 if (ch
== EOF
) break;