|
|
1.1 root 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 GNU CC; see the file COPYING. If not, write to
17: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 ([email protected]). */
26:
27: #ifdef __GNUG__
28: #pragma implementation
29: #endif
30: #include "libioP.h"
31: #include "editbuf.h"
32: #include <stddef.h>
33: extern "C" {
34: #include <stdlib.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 mode /* =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 *buf, struct edit_mark *mark, size_t count)
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.