xref: /haiku/src/libs/stdc++/legacy/editbuf.cc (revision 16d5c24e533eb14b7b8a99ee9f3ec9ba66335b1e)
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 
35 /* NOTE: Some of the code here is taken from GNU emacs */
36 /* Hence this file falls under the GNU License! */
37 
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().
53 
truncate()54 int edit_streambuf::truncate()
55 {
56     str->buffer->delete_range(str->buffer->tell((buf_char*)pptr()),
57 			      str->buffer->tell(str->end));
58     return 0;
59 }
60 
61 #ifdef OLD_STDIO
disconnect_gap_from_file(edit_buffer * buffer,FILE * fp)62 inline void  disconnect_gap_from_file(edit_buffer* buffer, FILE* fp)
63 {
64     if (buffer->gap_start_ptr != &fp->__bufp)
65 	return;
66     buffer->gap_start_normal = fp->__bufp;
67     buffer->gap_start_ptr = &buffer->gap_start_normal;
68 }
69 #endif
70 
flush_to_buffer(edit_buffer * buffer)71 void edit_streambuf::flush_to_buffer(edit_buffer* buffer)
72 {
73     if (pptr() > buffer->_gap_start && pptr() < buffer->gap_end())
74 	buffer->_gap_start = pptr();
75 }
76 
disconnect_gap_from_file(edit_buffer * buffer)77 void edit_streambuf::disconnect_gap_from_file(edit_buffer* buffer)
78 {
79     if (buffer->_writer != this) return;
80     flush_to_buffer(buffer);
81     setp(pptr(),pptr());
82     buffer->_writer = NULL;
83 }
84 
tell(buf_char * ptr)85 buf_index edit_buffer::tell(buf_char *ptr)
86 {
87     if (ptr <= gap_start())
88 	return ptr - data;
89     else
90 	return ptr - gap_end() + size1();
91 }
92 
93 #if 0
94 buf_index buf_cookie::tell()
95 {
96     return str->buffer->tell(file->__bufp);
97 }
98 #endif
99 
tell(edit_mark * mark)100 buf_index edit_buffer::tell(edit_mark*mark)
101 {
102     return tell(data + mark->index_in_buffer(this));
103 }
104 
105 // adjust the position of the gap
106 
move_gap(buf_offset pos)107 void edit_buffer::move_gap(buf_offset pos)
108 {
109   if (pos < size1())
110     gap_left (pos);
111   else if (pos > size1())
112     gap_right (pos);
113 }
114 
gap_left(int pos)115 void edit_buffer::gap_left (int pos)
116 {
117   register buf_char *to, *from;
118   register int i;
119   int new_s1;
120 
121   i = size1();
122   from = gap_start();
123   to = from + gap_size();
124   new_s1 = size1();
125 
126   /* Now copy the characters.  To move the gap down,
127      copy characters up.  */
128 
129   for (;;)
130     {
131       /* I gets number of characters left to copy.  */
132       i = new_s1 - pos;
133       if (i == 0)
134 	break;
135 #if 0
136       /* If a quit is requested, stop copying now.
137 	 Change POS to be where we have actually moved the gap to.  */
138       if (QUITP)
139 	{
140 	  pos = new_s1;
141 	  break;
142 	}
143 #endif
144       /* Move at most 32000 chars before checking again for a quit.  */
145       if (i > 32000)
146 	i = 32000;
147       new_s1 -= i;
148       while (--i >= 0)
149 	*--to = *--from;
150     }
151 
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);
156 #ifndef OLD_STDIO
157   _gap_start = data + pos;
158 #else
159   if (gap_start_ptr == &gap_start_normal)
160 	gap_start_normal = data + pos;
161 #endif
162   __gap_end_pos = to - data;
163 /*  QUIT;*/
164 }
165 
gap_right(int pos)166 void edit_buffer::gap_right (int pos)
167 {
168   register buf_char *to, *from;
169   register int i;
170   int new_s1;
171 
172   i = size1();
173   to = gap_start();
174   from = i + gap_end();
175   new_s1 = i;
176 
177   /* Now copy the characters.  To move the gap up,
178      copy characters down.  */
179 
180   while (1)
181     {
182       /* I gets number of characters left to copy.  */
183       i = pos - new_s1;
184       if (i == 0)
185 	break;
186 #if 0
187       /* If a quit is requested, stop copying now.
188 	 Change POS to be where we have actually moved the gap to.  */
189       if (QUITP)
190 	{
191 	  pos = new_s1;
192 	  break;
193 	}
194 #endif
195       /* Move at most 32000 chars before checking again for a quit.  */
196       if (i > 32000)
197 	i = 32000;
198       new_s1 += i;
199       while (--i >= 0)
200 	*to++ = *from++;
201     }
202 
203   adjust_markers ((size1() + gap_size()) << 1, (pos + gap_size()) << 1,
204 	- gap_size(), data);
205 #ifndef OLD_STDIO
206   _gap_start = data+pos;
207 #else
208   if (gap_start_ptr == &gap_start_normal)
209 	gap_start_normal = data + pos;
210 #endif
211   __gap_end_pos = from - data;
212 /*  QUIT;*/
213 }
214 
215 /* make sure that the gap in the current buffer is at least k
216    characters wide */
217 
make_gap(buf_offset k)218 void edit_buffer::make_gap(buf_offset k)
219 {
220   register buf_char *p1, *p2, *lim;
221   buf_char *old_data = data;
222   int s1 = size1();
223 
224   if (gap_size() >= k)
225     return;
226 
227   /* Get more than just enough */
228   if (buf_size > 1000) k += 2000;
229   else k += /*200;*/ 20; // for testing!
230 
231   p1 = (buf_char *) realloc (data, s1 + size2() + k);
232   if (p1 == 0)
233     abort(); /*memory_full ();*/
234 
235   k -= gap_size();			/* Amount of increase.  */
236 
237   /* Record new location of text */
238   data = p1;
239 
240   /* Transfer the new free space from the end to the gap
241      by shifting the second segment upward */
242   p2 = data + buf_size;
243   p1 = p2 + k;
244   lim = p2 - size2();
245   while (lim < p2)
246     *--p1 = *--p2;
247 
248   /* Finish updating text location data */
249   __gap_end_pos += k;
250 
251 #ifndef OLD_STDIO
252   _gap_start = data + s1;
253 #else
254   if (gap_start_ptr == &gap_start_normal)
255 	gap_start_normal = data + s1;
256 #endif
257 
258   /* adjust markers */
259   adjust_markers (s1 << 1, (buf_size << 1) + 1, k, old_data);
260   buf_size += k;
261 }
262 
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'.  */
268 
adjust_markers(register mark_pointer low,register mark_pointer high,int amount,buf_char * old_data)269 void edit_buffer::adjust_markers(register mark_pointer low,
270 				 register mark_pointer high,
271 				 int amount, buf_char *old_data)
272 {
273   register struct edit_mark *m;
274   register mark_pointer mpos;
275   /* convert to mark_pointer */
276   amount <<= 1;
277 
278   if (_writer)
279       _writer->disconnect_gap_from_file(this);
280 
281   for (m = mark_list(); m != NULL; m = m->chain)
282     {
283       mpos = m->_pos;
284       if (amount > 0)
285 	{
286 	  if (mpos > high && mpos < high + amount)
287 	    mpos = high + amount;
288 	}
289       else
290 	{
291 	  if (mpos > low + amount && mpos <= low)
292 	    mpos = low + amount;
293 	}
294       if (mpos > low && mpos <= high)
295 	mpos += amount;
296       m->_pos = mpos;
297     }
298 
299     // Now adjust files
300     edit_streambuf *file;
301 
302     for (file = files; file != NULL; file = file->next) {
303 	mpos = file->current() - old_data;
304 	if (amount > 0)
305 	{
306 	  if (mpos > high && mpos < high + amount)
307 	    mpos = high + amount;
308 	}
309 	else
310 	{
311 	  if (mpos > low + amount && mpos <= low)
312 	    mpos = low + amount;
313 	}
314 	if (mpos > low && mpos <= high)
315 	    mpos += amount;
316 	char* new_pos = data + mpos;
317 	file->set_current(new_pos, file->is_reading());
318     }
319 }
320 
321 #if 0
322 stdio_
323    __off == index at start of buffer (need only be valid after seek ? )
324    __buf ==
325 
326 if read/read_delete/overwrite mode:
327      __endp <= min(*gap_start_ptr, edit_string->end->ptr(buffer))
328 
329 if inserting:
330      must have *gap_start_ptr == __bufp && *gap_start_ptr+gap == __endp
331      file->edit_string->end->ptr(buffer) == *gap_start_ptr+end
332 if write_mode:
333      if before gap
334 #endif
335 
underflow()336 int edit_streambuf::underflow()
337 {
338     if (!(_mode & ios::in))
339 	return EOF;
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);
344     }
345     buf_char *str_end = str->end->ptr(buffer);
346   retry:
347     if (gptr() < egptr()) {
348 	return *gptr();
349     }
350     if ((buf_char*)gptr() == str_end)
351 	return EOF;
352     if (str_end <= buffer->gap_start()) {
353 	setg(eback(), gptr(), str_end);
354 	goto retry;
355     }
356     if (gptr() < buffer->gap_start()) {
357 	setg(eback(), gptr(), buffer->gap_start());
358 	goto retry;
359     }
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);
364     }
365     else
366 	setg(eback(), gptr(), str_end);
367     goto retry;
368 }
369 
overflow(int ch)370 int edit_streambuf::overflow(int ch)
371 {
372     if (_mode == ios::in)
373 	return EOF;
374     struct edit_buffer *buffer = str->buffer;
375     flush_to_buffer(buffer);
376     if (ch == EOF)
377 	return 0;
378     if (is_reading()) { // Must switch from get to put mode.
379 	set_current(gptr(), 0);
380     }
381     buf_char *str_end = str->end->ptr(buffer);
382   retry:
383     if (pptr() < epptr()) {
384 	*pptr() = ch;
385 	pbump(1);
386 	return (unsigned char)ch;
387     }
388     if ((buf_char*)pptr() == str_end || inserting()) {
389 	/* insert instead */
390 	if (buffer->_writer)
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());
395 	else
396 	    buffer->move_gap(pptr());
397 	buffer->make_gap(1);
398 	setp(buffer->gap_start(), buffer->gap_end());
399 	buffer->_writer = this;
400 	*pptr() = ch;
401 	pbump(1);
402 	return (unsigned char)ch;
403     }
404     if (str_end <= buffer->gap_start()) {
405 	// Entire string is left of gap.
406 	setp(pptr(), str_end);
407     }
408     else if (pptr() < buffer->gap_start()) {
409 	// Current pos is left of gap.
410 	setp(pptr(), buffer->gap_start());
411 	goto retry;
412     }
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;
418     }
419     else {
420 	// Otherwise, current pos is right of gap.
421 	setp(pptr(), str_end);
422     }
423     goto retry;
424 }
425 
set_current(char * new_pos,int reading)426 void edit_streambuf::set_current(char *new_pos, int reading)
427 {
428     if (reading) {
429 	setg(new_pos, new_pos, new_pos);
430 	setp(NULL, NULL);
431     }
432     else {
433 	setg(NULL, NULL, NULL);
434 	setp(new_pos, new_pos);
435     }
436 }
437 
438 // Called by fseek(fp, pos, whence) if fp is bound to a edit_buffer.
439 
seekoff(streamoff offset,_seek_dir dir,int)440 streampos edit_streambuf::seekoff(streamoff offset, _seek_dir dir,
441 				  int /* =ios::in|ios::out*/)
442 {
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);
448     switch (dir) {
449       case ios::beg:
450 	offset += start_pos;
451 	break;
452       case ios::cur:
453 	offset += cur_pos;
454 	break;
455       case ios::end:
456 	offset += end_pos;
457 	break;
458     }
459     if (offset < start_pos || offset > end_pos)
460 	return EOF;
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.
467     }
468     set_current(new_pos, is_reading());
469     return EOF;
470 }
471 
472 #if 0
473 int buf_seek(void *arg_cookie, fpos_t * pos, int whence)
474 {
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;
484     else
485 	cur_pos =
486 	    (*buffer->gap_start_ptr - str_start) + (file->__bufp - __gap_end);
487     end_pos = ...;
488     switch (whence) {
489       case SEEK_SET:
490 	new_pos = *pos;
491 	break;
492       case SEEK_CUR:
493 	new_pos = cur_pos + *pos;
494 	break;
495       case SEEK_END:
496 	new_pos = end_pos + *pos;
497 	break;
498     }
499     if (new_pos > end_pos) {
500 	seek to end_pos;
501 	insert_nulls(new_pos - end_pos);
502 	return;
503     }
504     if (str_start + new_pos <= *gap_start_ptr &* *gap_start_ptr < end) {
505 	__buffer = str_start;
506         __off = 0;
507 	__bufp = str_start + new_pos;
508 	file->__get_limit =
509 	    *buffer->gap_start_ptr; /* what if gap_start_ptr == &bufp ??? */
510     } else if () {
511 
512     }
513     *pos = new_pos;
514 }
515 #endif
516 
517 /* Delete characters from `from' up to (but not incl) `to' */
518 
delete_range(buf_index from,buf_index to)519 void edit_buffer::delete_range (buf_index from, buf_index to)
520 {
521   register int numdel;
522 
523   if ((numdel = to - from) <= 0)
524     return;
525 
526   /* Make sure the gap is somewhere in or next to what we are deleting */
527   if (from > size1())
528     gap_right (from);
529   if (to < size1())
530     gap_left (to);
531 
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);
536 
537    __gap_end_pos = to + gap_size();
538   _gap_start = data + from;
539 }
540 
delete_range(struct edit_mark * start,struct edit_mark * end)541 void edit_buffer::delete_range(struct edit_mark *start, struct edit_mark *end)
542 {
543     delete_range(tell(start), tell(end));
544 }
545 
buf_delete_chars(struct edit_buffer *,struct edit_mark *,size_t)546 void buf_delete_chars(struct edit_buffer *, struct edit_mark *, size_t)
547 {
548  abort();
549 }
550 
edit_streambuf(edit_string * bstr,int mode)551 edit_streambuf::edit_streambuf(edit_string* bstr, int mode)
552 {
553     _mode = mode;
554     str = bstr;
555     edit_buffer* buffer = bstr->buffer;
556     next = buffer->files;
557     buffer->files = this;
558     char* buf_ptr = bstr->start->ptr(buffer);
559     _inserting = 0;
560 //    setb(buf_ptr, buf_ptr, 0);
561     set_current(buf_ptr, !(mode & ios::out+ios::trunc+ios::app));
562     if (_mode & ios::trunc)
563 	truncate();
564     if (_mode & ios::ate)
565 	seekoff(0, ios::end);
566 }
567 
568 // Called by fclose(fp) if fp is bound to a edit_buffer.
569 
570 #if 0
571 static int buf_close(void *arg)
572 {
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) ;
577     *ptr = cookie->next;
578     disconnect_gap_from_file(buffer, cookie->file);
579     free (cookie);
580     return 0;
581 }
582 #endif
583 
~edit_streambuf()584 edit_streambuf::~edit_streambuf()
585 {
586     if (_mode == ios::out)
587 	truncate();
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) { }
591     *ptr = next;
592 
593     disconnect_gap_from_file(str->buffer);
594 }
595 
edit_buffer()596 edit_buffer::edit_buffer()
597 {
598     buf_size = /*200;*/ 15; /* for testing! */
599     data = (buf_char*)malloc(buf_size);
600     files = NULL;
601 #ifndef OLD_STDIO
602     _gap_start = data;
603     _writer = NULL;
604 #else
605     gap_start_normal = data;
606     gap_start_ptr = &gap_start_normal;
607 #endif
608     __gap_end_pos = buf_size;
609     start_mark.chain = &end_mark;
610     start_mark._pos = 0;
611     end_mark.chain = NULL;
612     end_mark._pos = 2 * buf_size + 1;
613 }
614 
615 // Allocate a new mark, which is adjusted by 'delta' bytes from 'this'.
616 // Restrict new mark to lie within 'str'.
617 
edit_mark(struct edit_string * str,long delta)618 edit_mark::edit_mark(struct edit_string *str, long delta)
619 {
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;
625     delta <<= 1;
626 
627     // check if new and old marks are opposite sides of gap
628     if (_pos <= size1 && _pos + delta > size1)
629 	delta += gap_size;
630     else if (_pos >= size1 + gap_size && _pos + delta < size1 + gap_size)
631 	delta -= gap_size;
632 
633     _pos = _pos + delta;
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);
638 }
639 
640 // A (slow) way to find the buffer a mark belongs to.
641 
buffer()642 edit_buffer * edit_mark::buffer()
643 {
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));
648 }
649 
~edit_mark()650 edit_mark::~edit_mark()
651 {
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();
655     edit_mark **ptr;
656     for (ptr = &buf->start_mark.chain; *ptr != this; ptr = &(*ptr)->chain) ;
657     *ptr = this->chain;
658 }
659 
length() const660 int edit_string::length() const
661 {
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())
665 	return delta;
666     return delta - buffer->gap_size();
667 }
668 
copy_bytes(int * lenp) const669 buf_char * edit_string::copy_bytes(int *lenp) const
670 {
671     char *new_str;
672     int len1, len2;
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;
678 	len2 = 0;
679 	start2 = NULL; // To avoid a warning from g++.
680     }
681     else {
682 	len1 = buffer->gap_start() - start1;
683 	start2 = buffer->gap_end();
684 	len2 = end->ptr(buffer) - start2;
685     }
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';
690     *lenp = len1+len2;
691     return new_str;
692 }
693 
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.
699 
assign(struct edit_string * src)700 void edit_string::assign(struct edit_string *src)
701 {
702     edit_streambuf dst_file(this, ios::out);
703     if (buffer == src->buffer /*&& ???*/) { /* overly conservative */
704 	int src_len;
705 	buf_char *new_str;
706 	new_str = src->copy_bytes(&src_len);
707 	dst_file.sputn(new_str, src_len);
708 	free (new_str);
709     } else {
710 	edit_streambuf src_file(src, ios::in);
711 	for ( ; ; ) {
712 	    int ch = src_file.sbumpc();
713 	    if (ch == EOF) break;
714 	    dst_file.sputc(ch);
715 	}
716     }
717 }
718