Declare malloc, free, and atexit if inhibit_libc is defined.
[official-gcc.git] / libio / editbuf.cc
blobe2df8c4641cd8e883e8398299b11b270c3565d34
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)
8 any later version.
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). */
27 #ifdef __GNUG__
28 #pragma implementation
29 #endif
30 #include "libioP.h"
31 #include "editbuf.h"
32 #include <stddef.h>
33 #include <stdlib.h>
34 #include <string.h>
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));
59 return 0;
62 #ifdef OLD_STDIO
63 inline void disconnect_gap_from_file(edit_buffer* buffer, FILE* fp)
65 if (buffer->gap_start_ptr != &fp->__bufp)
66 return;
67 buffer->gap_start_normal = fp->__bufp;
68 buffer->gap_start_ptr = &buffer->gap_start_normal;
70 #endif
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);
82 setp(pptr(),pptr());
83 buffer->_writer = NULL;
86 buf_index edit_buffer::tell(buf_char *ptr)
88 if (ptr <= gap_start())
89 return ptr - data;
90 else
91 return ptr - gap_end() + size1();
94 #if 0
95 buf_index buf_cookie::tell()
97 return str->buffer->tell(file->__bufp);
99 #endif
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)
110 if (pos < size1())
111 gap_left (pos);
112 else if (pos > size1())
113 gap_right (pos);
116 void edit_buffer::gap_left (int pos)
118 register buf_char *to, *from;
119 register int i;
120 int new_s1;
122 i = size1();
123 from = gap_start();
124 to = from + gap_size();
125 new_s1 = size1();
127 /* Now copy the characters. To move the gap down,
128 copy characters up. */
130 for (;;)
132 /* I gets number of characters left to copy. */
133 i = new_s1 - pos;
134 if (i == 0)
135 break;
136 #if 0
137 /* If a quit is requested, stop copying now.
138 Change POS to be where we have actually moved the gap to. */
139 if (QUITP)
141 pos = new_s1;
142 break;
144 #endif
145 /* Move at most 32000 chars before checking again for a quit. */
146 if (i > 32000)
147 i = 32000;
148 new_s1 -= i;
149 while (--i >= 0)
150 *--to = *--from;
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);
157 #ifndef OLD_STDIO
158 _gap_start = data + pos;
159 #else
160 if (gap_start_ptr == &gap_start_normal)
161 gap_start_normal = data + pos;
162 #endif
163 __gap_end_pos = to - data;
164 /* QUIT;*/
167 void edit_buffer::gap_right (int pos)
169 register buf_char *to, *from;
170 register int i;
171 int new_s1;
173 i = size1();
174 to = gap_start();
175 from = i + gap_end();
176 new_s1 = i;
178 /* Now copy the characters. To move the gap up,
179 copy characters down. */
181 while (1)
183 /* I gets number of characters left to copy. */
184 i = pos - new_s1;
185 if (i == 0)
186 break;
187 #if 0
188 /* If a quit is requested, stop copying now.
189 Change POS to be where we have actually moved the gap to. */
190 if (QUITP)
192 pos = new_s1;
193 break;
195 #endif
196 /* Move at most 32000 chars before checking again for a quit. */
197 if (i > 32000)
198 i = 32000;
199 new_s1 += i;
200 while (--i >= 0)
201 *to++ = *from++;
204 adjust_markers ((size1() + gap_size()) << 1, (pos + gap_size()) << 1,
205 - gap_size(), data);
206 #ifndef OLD_STDIO
207 _gap_start = data+pos;
208 #else
209 if (gap_start_ptr == &gap_start_normal)
210 gap_start_normal = data + pos;
211 #endif
212 __gap_end_pos = from - data;
213 /* QUIT;*/
216 /* make sure that the gap in the current buffer is at least k
217 characters wide */
219 void edit_buffer::make_gap(buf_offset k)
221 register buf_char *p1, *p2, *lim;
222 buf_char *old_data = data;
223 int s1 = size1();
225 if (gap_size() >= k)
226 return;
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);
233 if (p1 == 0)
234 abort(); /*memory_full ();*/
236 k -= gap_size(); /* Amount of increase. */
238 /* Record new location of text */
239 data = p1;
241 /* Transfer the new free space from the end to the gap
242 by shifting the second segment upward */
243 p2 = data + buf_size;
244 p1 = p2 + k;
245 lim = p2 - size2();
246 while (lim < p2)
247 *--p1 = *--p2;
249 /* Finish updating text location data */
250 __gap_end_pos += k;
252 #ifndef OLD_STDIO
253 _gap_start = data + s1;
254 #else
255 if (gap_start_ptr == &gap_start_normal)
256 gap_start_normal = data + s1;
257 #endif
259 /* adjust markers */
260 adjust_markers (s1 << 1, (buf_size << 1) + 1, k, old_data);
261 buf_size += k;
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 */
277 amount <<= 1;
279 if (_writer)
280 _writer->disconnect_gap_from_file(this);
282 for (m = mark_list(); m != NULL; m = m->chain)
284 mpos = m->_pos;
285 if (amount > 0)
287 if (mpos > high && mpos < high + amount)
288 mpos = high + amount;
290 else
292 if (mpos > low + amount && mpos <= low)
293 mpos = low + amount;
295 if (mpos > low && mpos <= high)
296 mpos += amount;
297 m->_pos = mpos;
300 // Now adjust files
301 edit_streambuf *file;
303 for (file = files; file != NULL; file = file->next) {
304 mpos = file->current() - old_data;
305 if (amount > 0)
307 if (mpos > high && mpos < high + amount)
308 mpos = high + amount;
310 else
312 if (mpos > low + amount && mpos <= low)
313 mpos = low + amount;
315 if (mpos > low && mpos <= high)
316 mpos += amount;
317 char* new_pos = data + mpos;
318 file->set_current(new_pos, file->is_reading());
322 #if 0
323 stdio_
324 __off == index at start of buffer (need only be valid after seek ? )
325 __buf ==
327 if read/read_delete/overwrite mode:
328 __endp <= min(*gap_start_ptr, edit_string->end->ptr(buffer))
330 if inserting:
331 must have *gap_start_ptr == __bufp && *gap_start_ptr+gap == __endp
332 file->edit_string->end->ptr(buffer) == *gap_start_ptr+end
333 if write_mode:
334 if before gap
335 #endif
337 int edit_streambuf::underflow()
339 if (!(_mode & ios::in))
340 return EOF;
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);
347 retry:
348 if (gptr() < egptr()) {
349 return *gptr();
351 if ((buf_char*)gptr() == str_end)
352 return EOF;
353 if (str_end <= buffer->gap_start()) {
354 setg(eback(), gptr(), str_end);
355 goto retry;
357 if (gptr() < buffer->gap_start()) {
358 setg(eback(), gptr(), buffer->gap_start());
359 goto retry;
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);
366 else
367 setg(eback(), gptr(), str_end);
368 goto retry;
371 int edit_streambuf::overflow(int ch)
373 if (_mode == ios::in)
374 return EOF;
375 struct edit_buffer *buffer = str->buffer;
376 flush_to_buffer(buffer);
377 if (ch == EOF)
378 return 0;
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);
383 retry:
384 if (pptr() < epptr()) {
385 *pptr() = ch;
386 pbump(1);
387 return (unsigned char)ch;
389 if ((buf_char*)pptr() == str_end || inserting()) {
390 /* insert instead */
391 if (buffer->_writer)
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());
396 else
397 buffer->move_gap(pptr());
398 buffer->make_gap(1);
399 setp(buffer->gap_start(), buffer->gap_end());
400 buffer->_writer = this;
401 *pptr() = ch;
402 pbump(1);
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());
412 goto retry;
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;
420 else {
421 // Otherwise, current pos is right of gap.
422 setp(pptr(), str_end);
424 goto retry;
427 void edit_streambuf::set_current(char *new_pos, int reading)
429 if (reading) {
430 setg(new_pos, new_pos, new_pos);
431 setp(NULL, NULL);
433 else {
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);
449 switch (dir) {
450 case ios::beg:
451 offset += start_pos;
452 break;
453 case ios::cur:
454 offset += cur_pos;
455 break;
456 case ios::end:
457 offset += end_pos;
458 break;
460 if (offset < start_pos || offset > end_pos)
461 return EOF;
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());
470 return EOF;
473 #if 0
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;
485 else
486 cur_pos =
487 (*buffer->gap_start_ptr - str_start) + (file->__bufp - __gap_end);
488 end_pos = ...;
489 switch (whence) {
490 case SEEK_SET:
491 new_pos = *pos;
492 break;
493 case SEEK_CUR:
494 new_pos = cur_pos + *pos;
495 break;
496 case SEEK_END:
497 new_pos = end_pos + *pos;
498 break;
500 if (new_pos > end_pos) {
501 seek to end_pos;
502 insert_nulls(new_pos - end_pos);
503 return;
505 if (str_start + new_pos <= *gap_start_ptr &* *gap_start_ptr < end) {
506 __buffer = str_start;
507 __off = 0;
508 __bufp = str_start + new_pos;
509 file->__get_limit =
510 *buffer->gap_start_ptr; /* what if gap_start_ptr == &bufp ??? */
511 } else if () {
514 *pos = new_pos;
516 #endif
518 /* Delete characters from `from' up to (but not incl) `to' */
520 void edit_buffer::delete_range (buf_index from, buf_index to)
522 register int numdel;
524 if ((numdel = to - from) <= 0)
525 return;
527 /* Make sure the gap is somewhere in or next to what we are deleting */
528 if (from > size1())
529 gap_right (from);
530 if (to < size1())
531 gap_left (to);
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)
549 abort();
552 edit_streambuf::edit_streambuf(edit_string* bstr, int mode)
554 _mode = mode;
555 str = bstr;
556 edit_buffer* buffer = bstr->buffer;
557 next = buffer->files;
558 buffer->files = this;
559 char* buf_ptr = bstr->start->ptr(buffer);
560 _inserting = 0;
561 // setb(buf_ptr, buf_ptr, 0);
562 set_current(buf_ptr, !(mode & ios::out+ios::trunc+ios::app));
563 if (_mode & ios::trunc)
564 truncate();
565 if (_mode & ios::ate)
566 seekoff(0, ios::end);
569 // Called by fclose(fp) if fp is bound to a edit_buffer.
571 #if 0
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) ;
578 *ptr = cookie->next;
579 disconnect_gap_from_file(buffer, cookie->file);
580 free (cookie);
581 return 0;
583 #endif
585 edit_streambuf::~edit_streambuf()
587 if (_mode == ios::out)
588 truncate();
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) { }
592 *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);
601 files = NULL;
602 #ifndef OLD_STDIO
603 _gap_start = data;
604 _writer = NULL;
605 #else
606 gap_start_normal = data;
607 gap_start_ptr = &gap_start_normal;
608 #endif
609 __gap_end_pos = buf_size;
610 start_mark.chain = &end_mark;
611 start_mark._pos = 0;
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;
626 delta <<= 1;
628 // check if new and old marks are opposite sides of gap
629 if (_pos <= size1 && _pos + delta > size1)
630 delta += gap_size;
631 else if (_pos >= size1 + gap_size && _pos + delta < size1 + gap_size)
632 delta -= gap_size;
634 _pos = _pos + delta;
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();
656 edit_mark **ptr;
657 for (ptr = &buf->start_mark.chain; *ptr != this; ptr = &(*ptr)->chain) ;
658 *ptr = this->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())
666 return delta;
667 return delta - buffer->gap_size();
670 buf_char * edit_string::copy_bytes(int *lenp) const
672 char *new_str;
673 int len1, len2;
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;
679 len2 = 0;
680 start2 = NULL; // To avoid a warning from g++.
682 else {
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';
691 *lenp = len1+len2;
692 return new_str;
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 */
705 int src_len;
706 buf_char *new_str;
707 new_str = src->copy_bytes(&src_len);
708 dst_file.sputn(new_str, src_len);
709 free (new_str);
710 } else {
711 edit_streambuf src_file(src, ios::in);
712 for ( ; ; ) {
713 int ch = src_file.sbumpc();
714 if (ch == EOF) break;
715 dst_file.sputc(ch);