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
36 /* NOTE: Some of the code here is taken from GNU emacs */
37 /* Hence this file falls under the GNU License! */
39 // Invariants for edit_streambuf:
40 // An edit_streambuf is associated with a specific edit_string,
41 // which again is a sub-string of a specific edit_buffer.
42 // An edit_streambuf is always in either get mode or put mode, never both.
43 // In get mode, gptr() is the current position,
44 // and pbase(), pptr(), and epptr() are all NULL.
45 // In put mode, pptr() is the current position,
46 // and eback(), gptr(), and egptr() are all NULL.
47 // Any edit_streambuf that is actively doing insertion (as opposed to
48 // replacing) // must have its pptr() pointing to the start of the gap.
49 // Only one edit_streambuf can be actively inserting into a specific
50 // edit_buffer; the edit_buffer's _writer field points to that edit_streambuf.
51 // That edit_streambuf "owns" the gap, and the actual start of the
52 // gap is the pptr() of the edit_streambuf; the edit_buffer::_gap_start pointer
53 // will only be updated on an edit_streambuf::overflow().
55 int edit_streambuf::truncate()
57 str
->buffer
->delete_range(str
->buffer
->tell((buf_char
*)pptr()),
58 str
->buffer
->tell(str
->end
));
63 inline void disconnect_gap_from_file(edit_buffer
* buffer
, FILE* fp
)
65 if (buffer
->gap_start_ptr
!= &fp
->__bufp
)
67 buffer
->gap_start_normal
= fp
->__bufp
;
68 buffer
->gap_start_ptr
= &buffer
->gap_start_normal
;
72 void edit_streambuf::flush_to_buffer(edit_buffer
* buffer
)
74 if (pptr() > buffer
->_gap_start
&& pptr() < buffer
->gap_end())
75 buffer
->_gap_start
= pptr();
78 void edit_streambuf::disconnect_gap_from_file(edit_buffer
* buffer
)
80 if (buffer
->_writer
!= this) return;
81 flush_to_buffer(buffer
);
83 buffer
->_writer
= NULL
;
86 buf_index
edit_buffer::tell(buf_char
*ptr
)
88 if (ptr
<= gap_start())
91 return ptr
- gap_end() + size1();
95 buf_index
buf_cookie::tell()
97 return str
->buffer
->tell(file
->__bufp
);
101 buf_index
edit_buffer::tell(edit_mark
*mark
)
103 return tell(data
+ mark
->index_in_buffer(this));
106 // adjust the position of the gap
108 void edit_buffer::move_gap(buf_offset pos
)
112 else if (pos
> size1())
116 void edit_buffer::gap_left (int pos
)
118 register buf_char
*to
, *from
;
124 to
= from
+ gap_size();
127 /* Now copy the characters. To move the gap down,
128 copy characters up. */
132 /* I gets number of characters left to copy. */
137 /* If a quit is requested, stop copying now.
138 Change POS to be where we have actually moved the gap to. */
145 /* Move at most 32000 chars before checking again for a quit. */
153 /* Adjust markers, and buffer data structure, to put the gap at POS.
154 POS is where the loop above stopped, which may be what was specified
155 or may be where a quit was detected. */
156 adjust_markers (pos
<< 1, size1() << 1, gap_size(), data
);
158 _gap_start
= data
+ pos
;
160 if (gap_start_ptr
== &gap_start_normal
)
161 gap_start_normal
= data
+ pos
;
163 __gap_end_pos
= to
- data
;
167 void edit_buffer::gap_right (int pos
)
169 register buf_char
*to
, *from
;
175 from
= i
+ gap_end();
178 /* Now copy the characters. To move the gap up,
179 copy characters down. */
183 /* I gets number of characters left to copy. */
188 /* If a quit is requested, stop copying now.
189 Change POS to be where we have actually moved the gap to. */
196 /* Move at most 32000 chars before checking again for a quit. */
204 adjust_markers ((size1() + gap_size()) << 1, (pos
+ gap_size()) << 1,
207 _gap_start
= data
+pos
;
209 if (gap_start_ptr
== &gap_start_normal
)
210 gap_start_normal
= data
+ pos
;
212 __gap_end_pos
= from
- data
;
216 /* make sure that the gap in the current buffer is at least k
219 void edit_buffer::make_gap(buf_offset k
)
221 register buf_char
*p1
, *p2
, *lim
;
222 buf_char
*old_data
= data
;
228 /* Get more than just enough */
229 if (buf_size
> 1000) k
+= 2000;
230 else k
+= /*200;*/ 20; // for testing!
232 p1
= (buf_char
*) realloc (data
, s1
+ size2() + k
);
234 abort(); /*memory_full ();*/
236 k
-= gap_size(); /* Amount of increase. */
238 /* Record new location of text */
241 /* Transfer the new free space from the end to the gap
242 by shifting the second segment upward */
243 p2
= data
+ buf_size
;
249 /* Finish updating text location data */
253 _gap_start
= data
+ s1
;
255 if (gap_start_ptr
== &gap_start_normal
)
256 gap_start_normal
= data
+ s1
;
260 adjust_markers (s1
<< 1, (buf_size
<< 1) + 1, k
, old_data
);
264 /* Add `amount' to the position of every marker in the current buffer
265 whose current position is between `from' (exclusive) and `to' (inclusive).
266 Also, any markers past the outside of that interval, in the direction
267 of adjustment, are first moved back to the near end of the interval
268 and then adjusted by `amount'. */
270 void edit_buffer::adjust_markers(register mark_pointer low
,
271 register mark_pointer high
,
272 int amount
, buf_char
*old_data
)
274 register struct edit_mark
*m
;
275 register mark_pointer mpos
;
276 /* convert to mark_pointer */
280 _writer
->disconnect_gap_from_file(this);
282 for (m
= mark_list(); m
!= NULL
; m
= m
->chain
)
287 if (mpos
> high
&& mpos
< high
+ amount
)
288 mpos
= high
+ amount
;
292 if (mpos
> low
+ amount
&& mpos
<= low
)
295 if (mpos
> low
&& mpos
<= high
)
301 edit_streambuf
*file
;
303 for (file
= files
; file
!= NULL
; file
= file
->next
) {
304 mpos
= file
->current() - old_data
;
307 if (mpos
> high
&& mpos
< high
+ amount
)
308 mpos
= high
+ amount
;
312 if (mpos
> low
+ amount
&& mpos
<= low
)
315 if (mpos
> low
&& mpos
<= high
)
317 char* new_pos
= data
+ mpos
;
318 file
->set_current(new_pos
, file
->is_reading());
324 __off
== index at start of
buffer (need only be valid after seek
? )
327 if read
/read_delete
/overwrite mode
:
328 __endp
<= min(*gap_start_ptr
, edit_string
->end
->ptr(buffer
))
331 must have
*gap_start_ptr
== __bufp
&& *gap_start_ptr
+gap
== __endp
332 file
->edit_string
->end
->ptr(buffer
) == *gap_start_ptr
+end
337 int edit_streambuf::underflow()
339 if (!(_mode
& ios::in
))
341 struct edit_buffer
*buffer
= str
->buffer
;
342 if (!is_reading()) { // Must switch from put to get mode.
343 disconnect_gap_from_file(buffer
);
344 set_current(pptr(), 1);
346 buf_char
*str_end
= str
->end
->ptr(buffer
);
348 if (gptr() < egptr()) {
351 if ((buf_char
*)gptr() == str_end
)
353 if (str_end
<= buffer
->gap_start()) {
354 setg(eback(), gptr(), str_end
);
357 if (gptr() < buffer
->gap_start()) {
358 setg(eback(), gptr(), buffer
->gap_start());
361 if (gptr() == buffer
->gap_start()) {
362 disconnect_gap_from_file(buffer
);
363 // fp->__offset += fp->__bufp - fp->__buffer;
364 setg(buffer
->gap_end(), buffer
->gap_end(), str_end
);
367 setg(eback(), gptr(), str_end
);
371 int edit_streambuf::overflow(int ch
)
373 if (_mode
== ios::in
)
375 struct edit_buffer
*buffer
= str
->buffer
;
376 flush_to_buffer(buffer
);
379 if (is_reading()) { // Must switch from get to put mode.
380 set_current(gptr(), 0);
382 buf_char
*str_end
= str
->end
->ptr(buffer
);
384 if (pptr() < epptr()) {
387 return (unsigned char)ch
;
389 if ((buf_char
*)pptr() == str_end
|| inserting()) {
392 buffer
->_writer
->flush_to_buffer(); // Redundant?
393 buffer
->_writer
= NULL
;
394 if (pptr() >= buffer
->gap_end())
395 buffer
->move_gap(pptr() - buffer
->gap_size());
397 buffer
->move_gap(pptr());
399 setp(buffer
->gap_start(), buffer
->gap_end());
400 buffer
->_writer
= this;
403 return (unsigned char)ch
;
405 if (str_end
<= buffer
->gap_start()) {
406 // Entire string is left of gap.
407 setp(pptr(), str_end
);
409 else if (pptr() < buffer
->gap_start()) {
410 // Current pos is left of gap.
411 setp(pptr(), buffer
->gap_start());
414 else if (pptr() == buffer
->gap_start()) {
415 // Current pos is at start of gap; move to end of gap.
416 // disconnect_gap_from_file(buffer);
417 setp(buffer
->gap_end(), str_end
);
418 // __offset += __bufp - __buffer;
421 // Otherwise, current pos is right of gap.
422 setp(pptr(), str_end
);
427 void edit_streambuf::set_current(char *new_pos
, int reading
)
430 setg(new_pos
, new_pos
, new_pos
);
434 setg(NULL
, NULL
, NULL
);
435 setp(new_pos
, new_pos
);
439 // Called by fseek(fp, pos, whence) if fp is bound to a edit_buffer.
441 streampos
edit_streambuf::seekoff(streamoff offset
, _seek_dir dir
,
442 int /* =ios::in|ios::out*/)
444 struct edit_buffer
*buffer
= str
->buffer
;
445 disconnect_gap_from_file(buffer
);
446 buf_index cur_pos
= buffer
->tell((buf_char
*)current());;
447 buf_index start_pos
= buffer
->tell(str
->start
);
448 buf_index end_pos
= buffer
->tell(str
->end
);
460 if (offset
< start_pos
|| offset
> end_pos
)
462 buf_char
*new_pos
= buffer
->data
+ offset
;
463 buf_char
* gap_start
= buffer
->gap_start();
464 if (new_pos
> gap_start
) {
465 buf_char
* gap_end
= buffer
->gap_end();
466 new_pos
+= gap_end
- gap_start
;
467 if (new_pos
>= buffer
->data
+ buffer
->buf_size
) abort(); // Paranoia.
469 set_current(new_pos
, is_reading());
474 int buf_seek(void *arg_cookie
, fpos_t * pos
, int whence
)
476 struct buf_cookie
*cookie
= arg_cookie
;
477 FILE *file
= cookie
->file
;
478 struct edit_buffer
*buffer
= cookie
->str
->buffer
;
479 buf_char
*str_start
= cookie
->str
->start
->ptr(buffer
);
480 disconnect_gap_from_file(buffer
, cookie
->file
);
481 fpos_t cur_pos
, new_pos
;
482 if (file
->__bufp
<= *buffer
->gap_start_ptr
483 || str_start
>= buffer
->__gap_end
)
484 cur_pos
= str_start
- file
->__bufp
;
487 (*buffer
->gap_start_ptr
- str_start
) + (file
->__bufp
- __gap_end
);
494 new_pos
= cur_pos
+ *pos
;
497 new_pos
= end_pos
+ *pos
;
500 if (new_pos
> end_pos
) {
502 insert_nulls(new_pos
- end_pos
);
505 if (str_start
+ new_pos
<= *gap_start_ptr
&* *gap_start_ptr
< end
) {
506 __buffer
= str_start
;
508 __bufp
= str_start
+ new_pos
;
510 *buffer
->gap_start_ptr
; /* what if gap_start_ptr == &bufp ??? */
518 /* Delete characters from `from' up to (but not incl) `to' */
520 void edit_buffer::delete_range (buf_index from
, buf_index to
)
524 if ((numdel
= to
- from
) <= 0)
527 /* Make sure the gap is somewhere in or next to what we are deleting */
533 /* Relocate all markers pointing into the new, larger gap
534 to point at the end of the text before the gap. */
535 adjust_markers ((to
+ gap_size()) << 1, (to
+ gap_size()) << 1,
536 - numdel
- gap_size(), data
);
538 __gap_end_pos
= to
+ gap_size();
539 _gap_start
= data
+ from
;
542 void edit_buffer::delete_range(struct edit_mark
*start
, struct edit_mark
*end
)
544 delete_range(tell(start
), tell(end
));
547 void buf_delete_chars(struct edit_buffer
*, struct edit_mark
*, size_t)
552 edit_streambuf::edit_streambuf(edit_string
* bstr
, int mode
)
556 edit_buffer
* buffer
= bstr
->buffer
;
557 next
= buffer
->files
;
558 buffer
->files
= this;
559 char* buf_ptr
= bstr
->start
->ptr(buffer
);
561 // setb(buf_ptr, buf_ptr, 0);
562 set_current(buf_ptr
, !(mode
& ios::out
+ios::trunc
+ios::app
));
563 if (_mode
& ios::trunc
)
565 if (_mode
& ios::ate
)
566 seekoff(0, ios::end
);
569 // Called by fclose(fp) if fp is bound to a edit_buffer.
572 static int buf_close(void *arg
)
574 register struct buf_cookie
*cookie
= arg
;
575 struct edit_buffer
*buffer
= cookie
->str
->buffer
;
576 struct buf_cookie
**ptr
;
577 for (ptr
= &buffer
->files
; *ptr
!= cookie
; ptr
= &(*ptr
)->next
) ;
579 disconnect_gap_from_file(buffer
, cookie
->file
);
585 edit_streambuf::~edit_streambuf()
587 if (_mode
== ios::out
)
589 // Unlink this from list of files associated with bstr->buffer.
590 edit_streambuf
**ptr
= &str
->buffer
->files
;
591 for (; *ptr
!= this; ptr
= &(*ptr
)->next
) { }
594 disconnect_gap_from_file(str
->buffer
);
597 edit_buffer::edit_buffer()
599 buf_size
= /*200;*/ 15; /* for testing! */
600 data
= (buf_char
*)malloc(buf_size
);
606 gap_start_normal
= data
;
607 gap_start_ptr
= &gap_start_normal
;
609 __gap_end_pos
= buf_size
;
610 start_mark
.chain
= &end_mark
;
612 end_mark
.chain
= NULL
;
613 end_mark
._pos
= 2 * buf_size
+ 1;
616 // Allocate a new mark, which is adjusted by 'delta' bytes from 'this'.
617 // Restrict new mark to lie within 'str'.
619 edit_mark::edit_mark(struct edit_string
*str
, long delta
)
621 struct edit_buffer
*buf
= str
->buffer
;
622 chain
= buf
->start_mark
.chain
;
623 buf
->start_mark
.chain
= this;
624 mark_pointer size1
= buf
->size1() << 1;
625 int gap_size
= buf
->gap_size() << 1;
628 // check if new and old marks are opposite sides of gap
629 if (_pos
<= size1
&& _pos
+ delta
> size1
)
631 else if (_pos
>= size1
+ gap_size
&& _pos
+ delta
< size1
+ gap_size
)
635 if (_pos
< str
->start
->_pos
& ~1)
636 _pos
= (str
->start
->_pos
& ~ 1) + (_pos
& 1);
637 else if (_pos
>= str
->end
->_pos
)
638 _pos
= (str
->end
->_pos
& ~ 1) + (_pos
& 1);
641 // A (slow) way to find the buffer a mark belongs to.
643 edit_buffer
* edit_mark::buffer()
645 struct edit_mark
*mark
;
646 for (mark
= this; mark
->chain
!= NULL
; mark
= mark
->chain
) ;
647 // Assume that the last mark on the chain is the end_mark.
648 return (edit_buffer
*)((char*)mark
- offsetof(edit_buffer
, end_mark
));
651 edit_mark::~edit_mark()
653 // Must unlink mark from chain of owning buffer
654 struct edit_buffer
*buf
= buffer();
655 if (this == &buf
->start_mark
|| this == &buf
->end_mark
) abort();
657 for (ptr
= &buf
->start_mark
.chain
; *ptr
!= this; ptr
= &(*ptr
)->chain
) ;
661 int edit_string::length() const
663 ptrdiff_t delta
= end
->ptr(buffer
) - start
->ptr(buffer
);
664 if (end
->ptr(buffer
) <= buffer
->gap_start() ||
665 start
->ptr(buffer
) >= buffer
->gap_end())
667 return delta
- buffer
->gap_size();
670 buf_char
* edit_string::copy_bytes(int *lenp
) const
674 buf_char
*start1
, *start2
;
675 start1
= start
->ptr(buffer
);
676 if (end
->ptr(buffer
) <= buffer
->gap_start()
677 || start
->ptr(buffer
) >= buffer
->gap_end()) {
678 len1
= end
->ptr(buffer
) - start1
;
680 start2
= NULL
; // To avoid a warning from g++.
683 len1
= buffer
->gap_start() - start1
;
684 start2
= buffer
->gap_end();
685 len2
= end
->ptr(buffer
) - start2
;
687 new_str
= (char*)malloc(len1
+ len2
+ 1);
688 memcpy(new_str
, start1
, len1
);
689 if (len2
> 0) memcpy(new_str
+ len1
, start2
, len2
);
690 new_str
[len1
+len2
] = '\0';
695 // Replace the buf_chars in 'this' with ones from 'src'.
696 // Equivalent to deleting this, then inserting src, except tries
697 // to leave marks in place: Marks whose offset from the start
698 // of 'this' is less than 'src->length()' will still have the
699 // same offset in 'this' when done.
701 void edit_string::assign(struct edit_string
*src
)
703 edit_streambuf
dst_file(this, ios::out
);
704 if (buffer
== src
->buffer
/*&& ???*/) { /* overly conservative */
707 new_str
= src
->copy_bytes(&src_len
);
708 dst_file
.sputn(new_str
, src_len
);
711 edit_streambuf
src_file(src
, ios::in
);
713 int ch
= src_file
.sbumpc();
714 if (ch
== EOF
) break;