OSDN Git Service

Revert last patch
[pf3gnuchains/gcc-fork.git] / libio / editbuf.cc
1 /* This is part of libio/iostream, providing -*- C++ -*- input/output.
2 Copyright (C) 1993 Free Software Foundation
3
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.
9
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.
14
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.
18
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.
24
25 Written by Per Bothner (bothner@cygnus.com). */
26
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>
35
36 /* NOTE: Some of the code here is taken from GNU emacs */
37 /* Hence this file falls under the GNU License! */
38
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().
54
55 int edit_streambuf::truncate()
56 {
57     str->buffer->delete_range(str->buffer->tell((buf_char*)pptr()),
58                               str->buffer->tell(str->end));
59     return 0;
60 }
61
62 #ifdef OLD_STDIO
63 inline void  disconnect_gap_from_file(edit_buffer* buffer, FILE* fp)
64 {
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;
69 }
70 #endif
71
72 void edit_streambuf::flush_to_buffer(edit_buffer* buffer)
73 {
74     if (pptr() > buffer->_gap_start && pptr() < buffer->gap_end())
75         buffer->_gap_start = pptr();
76 }
77
78 void edit_streambuf::disconnect_gap_from_file(edit_buffer* buffer)
79 {
80     if (buffer->_writer != this) return;
81     flush_to_buffer(buffer);
82     setp(pptr(),pptr());
83     buffer->_writer = NULL;    
84 }
85
86 buf_index edit_buffer::tell(buf_char *ptr)
87 {
88     if (ptr <= gap_start())
89         return ptr - data;
90     else
91         return ptr - gap_end() + size1();
92 }
93
94 #if 0
95 buf_index buf_cookie::tell()
96 {
97     return str->buffer->tell(file->__bufp);
98 }
99 #endif
100
101 buf_index edit_buffer::tell(edit_mark*mark)
102 {
103     return tell(data + mark->index_in_buffer(this));
104 }
105
106 // adjust the position of the gap
107
108 void edit_buffer::move_gap(buf_offset pos)
109 {
110   if (pos < size1())
111     gap_left (pos);
112   else if (pos > size1())
113     gap_right (pos);
114 }
115
116 void edit_buffer::gap_left (int pos)
117 {
118   register buf_char *to, *from;
119   register int i;
120   int new_s1;
121
122   i = size1();
123   from = gap_start();
124   to = from + gap_size();
125   new_s1 = size1();
126
127   /* Now copy the characters.  To move the gap down,
128      copy characters up.  */
129
130   for (;;)
131     {
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)
140         {
141           pos = new_s1;
142           break;
143         }
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;
151     }
152
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;*/
165 }
166
167 void edit_buffer::gap_right (int pos)
168 {
169   register buf_char *to, *from;
170   register int i;
171   int new_s1;
172
173   i = size1();
174   to = gap_start();
175   from = i + gap_end();
176   new_s1 = i;
177
178   /* Now copy the characters.  To move the gap up,
179      copy characters down.  */
180
181   while (1)
182     {
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)
191         {
192           pos = new_s1;
193           break;
194         }
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++;
202     }
203
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;*/
214 }
215
216 /* make sure that the gap in the current buffer is at least k
217    characters wide */
218
219 void edit_buffer::make_gap(buf_offset k)
220 {
221   register buf_char *p1, *p2, *lim;
222   buf_char *old_data = data;
223   int s1 = size1();
224
225   if (gap_size() >= k)
226     return;
227
228   /* Get more than just enough */
229   if (buf_size > 1000) k += 2000;
230   else k += /*200;*/ 20; // for testing!
231
232   p1 = (buf_char *) realloc (data, s1 + size2() + k);
233   if (p1 == 0)
234     abort(); /*memory_full ();*/
235
236   k -= gap_size();                      /* Amount of increase.  */
237
238   /* Record new location of text */
239   data = p1;
240
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;
248
249   /* Finish updating text location data */
250   __gap_end_pos += k;
251
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
258
259   /* adjust markers */
260   adjust_markers (s1 << 1, (buf_size << 1) + 1, k, old_data);
261   buf_size += k;
262 }
263
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'.  */
269
270 void edit_buffer::adjust_markers(register mark_pointer low,
271                                  register mark_pointer high,
272                                  int amount, buf_char *old_data)
273 {
274   register struct edit_mark *m;
275   register mark_pointer mpos;
276   /* convert to mark_pointer */
277   amount <<= 1;
278
279   if (_writer)
280       _writer->disconnect_gap_from_file(this);
281
282   for (m = mark_list(); m != NULL; m = m->chain)
283     {
284       mpos = m->_pos;
285       if (amount > 0)
286         {
287           if (mpos > high && mpos < high + amount)
288             mpos = high + amount;
289         }
290       else
291         {
292           if (mpos > low + amount && mpos <= low)
293             mpos = low + amount;
294         }
295       if (mpos > low && mpos <= high)
296         mpos += amount;
297       m->_pos = mpos;
298     }
299
300     // Now adjust files
301     edit_streambuf *file;
302
303     for (file = files; file != NULL; file = file->next) {
304         mpos = file->current() - old_data;
305         if (amount > 0)
306         {
307           if (mpos > high && mpos < high + amount)
308             mpos = high + amount;
309         }
310         else
311         {
312           if (mpos > low + amount && mpos <= low)
313             mpos = low + amount;
314         }
315         if (mpos > low && mpos <= high)
316             mpos += amount;
317         char* new_pos = data + mpos;
318         file->set_current(new_pos, file->is_reading());
319     }
320 }
321
322 #if 0
323 stdio_
324    __off == index at start of buffer (need only be valid after seek ? )
325    __buf ==
326
327 if read/read_delete/overwrite mode:
328      __endp <= min(*gap_start_ptr, edit_string->end->ptr(buffer))
329
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
336
337 int edit_streambuf::underflow()
338 {
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);
345     }
346     buf_char *str_end = str->end->ptr(buffer);
347   retry:
348     if (gptr() < egptr()) {
349         return *gptr();
350     }
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;
356     }
357     if (gptr() < buffer->gap_start()) {
358         setg(eback(), gptr(), buffer->gap_start());
359         goto retry;
360     }
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);
365     }
366     else
367         setg(eback(), gptr(), str_end);
368     goto retry;
369 }
370
371 int edit_streambuf::overflow(int ch)
372 {
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);
381     }
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;
388     }
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;
404     }
405     if (str_end <= buffer->gap_start()) {
406         // Entire string is left of gap.
407         setp(pptr(), str_end);
408     }
409     else if (pptr() < buffer->gap_start()) {
410         // Current pos is left of gap.
411         setp(pptr(), buffer->gap_start());
412         goto retry;
413     }
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;
419     }
420     else {
421         // Otherwise, current pos is right of gap.
422         setp(pptr(), str_end);
423     }
424     goto retry;
425 }
426
427 void edit_streambuf::set_current(char *new_pos, int reading)
428 {
429     if (reading) {
430         setg(new_pos, new_pos, new_pos);
431         setp(NULL, NULL);
432     }
433     else {
434         setg(NULL, NULL, NULL);
435         setp(new_pos, new_pos);
436     }
437 }
438
439 // Called by fseek(fp, pos, whence) if fp is bound to a edit_buffer.
440
441 streampos edit_streambuf::seekoff(streamoff offset, _seek_dir dir,
442                                   int /* =ios::in|ios::out*/)
443 {
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;
459     }
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.
468     }
469     set_current(new_pos, is_reading());
470     return EOF;
471 }
472
473 #if 0
474 int buf_seek(void *arg_cookie, fpos_t * pos, int whence)
475 {
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;
499     }
500     if (new_pos > end_pos) {
501         seek to end_pos;
502         insert_nulls(new_pos - end_pos);
503         return;
504     }
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 () {
512         
513     }
514     *pos = new_pos;
515 }
516 #endif
517
518 /* Delete characters from `from' up to (but not incl) `to' */
519
520 void edit_buffer::delete_range (buf_index from, buf_index to)
521 {
522   register int numdel;
523
524   if ((numdel = to - from) <= 0)
525     return;
526
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);
532
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);
537
538    __gap_end_pos = to + gap_size();
539   _gap_start = data + from;
540 }
541
542 void edit_buffer::delete_range(struct edit_mark *start, struct edit_mark *end)
543 {
544     delete_range(tell(start), tell(end));
545 }
546
547 void buf_delete_chars(struct edit_buffer *, struct edit_mark *, size_t)
548 {
549  abort();
550 }
551
552 edit_streambuf::edit_streambuf(edit_string* bstr, int mode)
553 {
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);
567 }
568
569 // Called by fclose(fp) if fp is bound to a edit_buffer.
570
571 #if 0
572 static int buf_close(void *arg)
573 {
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;
582 }
583 #endif
584
585 edit_streambuf::~edit_streambuf()
586 {
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;
593
594     disconnect_gap_from_file(str->buffer);
595 }
596
597 edit_buffer::edit_buffer()
598 {
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;
614 }
615
616 // Allocate a new mark, which is adjusted by 'delta' bytes from 'this'.
617 // Restrict new mark to lie within 'str'.
618
619 edit_mark::edit_mark(struct edit_string *str, long delta)
620 {
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;
627
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;
633
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);
639 }
640
641 // A (slow) way to find the buffer a mark belongs to.
642
643 edit_buffer * edit_mark::buffer()
644 {
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));
649 }
650
651 edit_mark::~edit_mark()
652 {
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;
659 }
660
661 int edit_string::length() const
662 {
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();
668 }
669
670 buf_char * edit_string::copy_bytes(int *lenp) const
671 {
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++.
681     }
682     else {
683         len1 = buffer->gap_start() - start1;
684         start2 = buffer->gap_end();
685         len2 = end->ptr(buffer) - start2;
686     }
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;
693 }
694
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.
700
701 void edit_string::assign(struct edit_string *src)
702 {
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);
716         }
717     }
718 }