Files
Arquivotheca.SunOS-4.1.4/usr.lib/libsuntool/text/ps_impl.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

1665 lines
53 KiB
C

#ifndef lint
#ifdef sccs
static char sccsid[] = "@(#)ps_impl.c 1.1 94/10/31";
#endif
#endif
/*
* Copyright (c) 1986, 1987 by Sun Microsystems Inc.
*/
/*
* Entity stream implementation for manager of two other streams.
*
* Conceptually, suppose we want the piece source to contain:
* AB0CD12E345FGH67I8JK9LMN
* and the original source contains:
* ABCDEFGHIJKLMN
* then the operation sequence:
* replace start:2 stop_plus_one:2 with:"0"
* replace start:5 stop_plus_one:5 with:"12"
* etc
* will generate a scratch source that looks like:
* xx0xx12xx345xx67xx8xx9
* and the piece table will alternate with:
* virtual pos:0, length:2, source:original, source_pos:0
* virtual pos:2, length:1, source:scratch, source_pos:sizeof(xx)
* virtual pos:3, length:2, source:original, source_pos:2
* virtual pos:5, length:2, source:scratch, source_pos:2*sizeof(xx)+1
* etc
*
* It is important that a piece in the piece table represent a contiguous
* run of entities in the original or scratch stream. Thus, if a break is
* detected during the first read of a piece, a new piece must be created.
*
* 87Mar23: A major new feature is support for a maximum size that the
* scratch stream is allowed to grow to. The basic design:
* Most of the piece_stream code believes that the scratch stream grows
* without bound. However, in reality, if the client sets a maximum size on
* the scratch stream, the ops vector for the scratch stream is modified
* so that the logical index space of [0..private->scratch_length) is
* mapped to the physical index space of [0..private->scratch_max_len),
* essentially by making all accesses to the logical indices be modulo
* private->scratch_max_len.
* Some special care must be taken with UNDO and secondary piece streams,
* as these both can use logical indices to reference entities that are no
* longer physically available.
*/
#include <suntool/primal.h>
#include <suntool/textsw_impl.h>
#include <stdio.h>
#include <varargs.h>
#include <suntool/ps_impl.h>
#include <suntool/alert.h>
#include <suntool/frame.h>
#include "sunwindow/sv_malloc.h"
extern char *calloc();
extern char *malloc();
static Es_status ps_commit();
static Es_handle ps_destroy(), ps_scratch_destroy();
static caddr_t ps_get();
static Es_index ps_get_length(), ps_scratch_get_length();
static Es_index ps_get_position(), ps_scratch_get_position();
static Es_index ps_set_position(), ps_scratch_set_position();
static Es_index ps_read(), ps_scratch_read();
static Es_index ps_replace(), ps_scratch_replace();
static int ps_set();
static void make_current_valid();
static Es_index write_header_etc();
static struct es_ops ps_ops = {
ps_commit,
ps_destroy,
ps_get,
ps_get_length,
ps_get_position,
ps_set_position,
ps_read,
ps_replace,
ps_set
};
static int max_transcript_alert_displayed; /* default = 0 */
#define INVALIDATE_CURRENT(private) \
(private)->current = CURRENT_NULL
#define SET_POSITION(private, to) \
INVALIDATE_CURRENT(private); (private)->position = (to)
#define VALID_PIECE_INDEX(private, index) \
((index < (private)->pieces.last_plus_one) && \
(PIECES_IN_TABLE(private)[index].pos != ES_INFINITY))
static char *wrap_msg = "\n\
*** Text is lost because the maximum edit log size has been exceeded. ***\n\n\n";
#ifdef DEBUG
#define MAX_PIECE_LENGTH 100000
#define MAX_LENGTH 4000000
#endif
static Es_handle
ps_NEW(piece_count)
int piece_count;
{
extern ft_object ft_create();
Es_handle esh = NEW(Es_object);
register Piece_table private = NEW(struct piece_table_object);
if (esh == NULL || private == NULL)
goto AllocFailed;
private->magic = PS_MAGIC;
if (piece_count > 0) {
private->pieces = FT_CLIENT_CREATE(piece_count, Piece_object);
if (private->pieces.seq == NULL)
goto AllocFailed;
FT_CLEAR_ALL(private->pieces);
} else
private->pieces.last_plus_one = 0;
esh->data = (caddr_t)private;
esh->ops = &ps_ops;
return(esh);
AllocFailed:
if (private)
free((char *)private);
if (esh)
free((char *)esh);
return((Es_handle)NULL);
}
extern Es_handle
ps_create(client_data, original, scratch)
caddr_t client_data;
Es_handle original, scratch;
{
extern ft_object ft_create();
extern int ft_set();
Es_handle esh = ps_NEW(100);
register Piece_table private;
register Piece pieces;
if (es_set_position(scratch, 0) != 0) {
(void) fprintf(stderr,
"ps_create: cannot reset scratch stream.\n");
return(NULL);
}
if (esh == NULL)
goto AllocFailed;
private = ABS_TO_REP(esh);
SET_POSITION(private, 0);
private->length = (original != ES_NULL) ? es_get_length(original) : 0;
pieces = PIECES_IN_TABLE(private);
if (private->length > 0) {
pieces[0].pos = es_set_position(original, 0);
PS_SET_ORIGINAL_SANDP(pieces[0], pieces[0].pos);
#ifdef notdef
The following fails because it does not match the state after a replace of
everything by nothing. However, it is probably correct should the
end_of_stream condition change to an empty piece at the end.
} else {
pieces[0].pos = 0;
PS_SET_SCRATCH_SANDP(pieces[0], pieces[0].pos);
#endif
}
pieces[0].length = private->length;
private->original = original;
private->scratch = scratch;
private->last_write_plus_one = ES_INFINITY;
private->rec_start = ES_INFINITY;
private->rec_insert = ES_INFINITY;
private->oldest_not_undone_mark = ES_INFINITY;
private->rec_insert_len = 0;
private->client_data = client_data;
private->scratch_max_len = ES_INFINITY;
/* scratch_length need not be valid, but must be < scratch_max_len */
private->scratch_length = 0;
private->scratch_ops = (Es_ops)0;
private->status = ES_SUCCESS;
return(esh);
AllocFailed:
(void) fprintf(stderr, "ps_create: alloc failure.\n");
return(NULL);
}
/* ARGSUSED */
static Es_status
ps_commit(esh)
Es_handle esh;
{
return(ES_SUCCESS);
}
static Es_handle
ps_destroy(esh)
Es_handle esh;
{
register Piece_table private = ABS_TO_REP(esh);
free((char *)esh);
ft_destroy(&private->pieces);
free((char *)private);
return NULL;
}
static Es_handle
ps_scratch_destroy(esh)
Es_handle esh;
{
register Piece_table private = SCRATCH_TO_REP(esh);
free((char *)esh->ops);
esh->ops = private->scratch_ops;
return(es_destroy(esh));
}
static Es_index
ps_get_length(esh)
Es_handle esh;
{
register Piece_table private = ABS_TO_REP(esh);
return (private->length);
}
static Es_index
ps_scratch_get_length(esh)
Es_handle esh;
{
register Piece_table private = SCRATCH_TO_REP(esh);
return (private->scratch_length);
}
static Es_index
ps_get_position(esh)
Es_handle esh;
{
register Piece_table private = ABS_TO_REP(esh);
return(private->position);
}
static Es_index
ps_scratch_get_position(esh)
Es_handle esh;
{
register Piece_table private = SCRATCH_TO_REP(esh);
return(private->scratch_position);
}
static Es_index
ps_set_position(esh, pos)
Es_handle esh;
register Es_index pos;
{
register Piece_table private = ABS_TO_REP(esh);
register Piece pieces = PIECES_IN_TABLE(private);
if (pos > private->length) {
ASSUME(pos == ES_INFINITY || private->parent);
pos = private->length;
} else if (pos < pieces[0].pos) {
pos = pieces[0].pos;
if (pos == ES_INFINITY) /* Checks for empty source. */
pos = 0;
}
ASSUME(0 <= pos && pos <= MAX_LENGTH);
/* Client's often lose track of the position, so check if setting to
* current position to avoid invalidating caches unnecessarily.
*/
if (pos != private->position) {
/* Try to maintain private->current */
if (private->current != CURRENT_NULL) {
if ((pos < pieces[private->current].pos) ||
(pos >= pieces[private->current].pos +
pieces[private->current].length))
INVALIDATE_CURRENT(private);
}
private->position = pos;
}
return (private->position);
}
static Es_index
ps_scratch_set_position(esh, pos)
Es_handle esh;
register Es_index pos;
{
register Piece_table private = SCRATCH_TO_REP(esh);
if (pos > private->scratch_length)
pos = private->scratch_length;
private->scratch_position = pos;
pos = pos % private->scratch_max_len;
pos = private->scratch_ops->set_position(esh, pos);
ASSUME(pos == (private->scratch_position % private->scratch_max_len));
return(private->scratch_position);
}
#ifdef DEBUG
static int
ps_pieces_are_consistent(private)
register Piece_table private;
{
register Piece pieces = PIECES_IN_TABLE(private);
register Es_index pos = pieces[0].pos;
register int current;
ASSUME(pos == 0 || pos == ES_INFINITY || private->parent);
for (current = 0; VALID_PIECE_INDEX(private, current); current++) {
ASSUME(pos == pieces[current].pos);
ASSUME(0 < pieces[current].length || (current == 0 && pos == 0));
ASSUME(pieces[current].length < MAX_PIECE_LENGTH);
pos += pieces[current].length;
ASSUME(pos <= private->length);
ASSUME(PS_SANDP_POS(pieces[current]) < MAX_LENGTH);
}
return(!0);
}
#endif
static Es_index
ps_scratch_read(esh, len, bufp, resultp)
Es_handle esh;
u_int len,
*resultp;
register char *bufp;
{
register Piece_table private = SCRATCH_TO_REP(esh);
register Es_index first_valid, last_plus_one;
Es_index remainder;
long int count_read;
if (!SCRATCH_HAS_WRAPPED(private)) {
private->scratch_position =
private->scratch_ops->read(esh, len, bufp, resultp);
} else {
first_valid = SCRATCH_FIRST_VALID(private);
last_plus_one = private->scratch_position + len;
/*
* scratch stream has wrapped around, so valid "logical" scratch
* indices are [first_valid..scratch_length).
* Watch for:
* a) read of characters that no longer exist, and
* b) read that is "split" around physical scratch end.
* First, look to see if read begins at valid position.
*/
if (private->scratch_position < first_valid) {
/* Invalid start, but may extend into valid positions.
* However, caller does not know that start is invalid; only
* way to communicate this is to read nothing this time.
* Move position to start of valid range (which changes
* private->scratch_position as a side-effect).
*/
(void) es_set_position(esh, first_valid);
*resultp = 0;
goto Return;
}
/* Second, look for "split" read. */
if ((private->scratch_position / private->scratch_max_len) !=
((last_plus_one-1) / private->scratch_max_len)) {
/* Split => read at end of scratch, then at start. */
remainder = private->scratch_max_len -
private->scratch_ops->get_position(esh);
(void) private->scratch_ops->read(esh, remainder, bufp,
&count_read);
(void) private->scratch_ops->set_position(esh, 0);
(void) private->scratch_ops->read(esh, len-count_read,
bufp+count_read, resultp);
*resultp += count_read;
} else {
(void) private->scratch_ops->read(esh, len, bufp, resultp);
}
/* Update the scratch_position, and watch out for read that
* ends exactly at multiple of scratch_max_len, because this
* requires repositioning of physical scratch stream to make
* succeeding calls work properly.
*/
private->scratch_position += *resultp;
if ((private->scratch_position % private->scratch_max_len) ==
0) {
(void) private->scratch_ops->set_position(esh, 0);
}
}
Return:
ASSUME(private->scratch_ops->get_position(esh) ==
(private->scratch_position % private->scratch_max_len));
ASSUME(ps_pieces_are_consistent(private));
return(private->scratch_position);
}
static Es_index
ps_scratch_replace(esh, last_plus_one, count, buf, count_used)
Es_handle esh;
Es_index last_plus_one;
long int count, *count_used;
char *buf;
{
register Piece_table private = SCRATCH_TO_REP(esh);
register Es_index first_valid, max_lpo;
Es_index remainder;
long int count_replaced;
if (last_plus_one > private->scratch_length) {
last_plus_one = private->scratch_length;
}
max_lpo = private->scratch_position + count;
if (last_plus_one > max_lpo) {
max_lpo = last_plus_one;
}
ASSUME(last_plus_one >= private->scratch_position);
if (!SCRATCH_HAS_WRAPPED(private) &&
(max_lpo <= private->scratch_max_len) ) {
private->scratch_position =
private->scratch_ops->replace(esh, last_plus_one, count,
buf, count_used);
private->scratch_length =
private->scratch_ops->get_length(esh);
} else {
first_valid = (SCRATCH_HAS_WRAPPED(private))
? SCRATCH_FIRST_VALID(private) : 0;
*count_used = count;
/*
* Either scratch stream has wrapped around, or it is about to
* wrap around. In either case, the valid "logical"
* scratch indices are [first_valid..scratch_length).
* Bytes that vanish into hole at start of scratch stream are
* treated as used.
* Watch for:
* a) range of characters that no longer exist, and
* b) "split" around physical scratch end.
* First, look to see if replace begins at valid position.
*/
remainder = first_valid - private->scratch_position;
if (remainder > 0) {
/* Invalid start, but may extend into valid positions.
* Move position to start of valid range (which changes
* private->scratch_position as a side-effect).
*/
(void) es_set_position(esh, first_valid);
if (last_plus_one < first_valid) {
/* Entire range to replace is invalid. */
goto Return;
} else if (count > 0) {
/* Discard any new bytes replacing invalid bytes. */
if (count > remainder) {
count -= remainder;
buf += remainder;
} else {
count = 0;
}
}
}
/* Second, look for "split" replace. */
if ((private->scratch_position / private->scratch_max_len) !=
((max_lpo-1) / private->scratch_max_len) ) {
/* Split => replace at end of scratch, then at start.
* NOTE: caller guarantees that count > remainder
*/
remainder = private->scratch_max_len -
private->scratch_ops->get_position(esh);
ASSUME(count > remainder);
(void) private->scratch_ops->replace(
esh,
private->scratch_max_len,
remainder,
buf, &count_replaced);
(void) private->scratch_ops->set_position(esh, 0);
(void) private->scratch_ops->replace(
esh,
count - remainder,
count - remainder,
buf + remainder, &count_replaced);
} else {
/* If here, then overwrite physical scratch after a wrap. */
(void) private->scratch_ops->replace(
esh,
(private->scratch_position %
private->scratch_max_len) + count,
count, buf, &count_replaced);
}
private->scratch_position += count;
if (private->scratch_position > private->scratch_length) {
private->scratch_length = private->scratch_position;
}
}
Return:
ASSUME(private->scratch_ops->get_position(esh) ==
(private->scratch_position % private->scratch_max_len));
return(private->scratch_position);
}
static Piece
split_piece(pieces, current, delta)
ft_handle pieces;
register int current;
register long int delta;
/* Splits the piece_object pieces[current] in two with the second piece, the
* new pieces[current+1], starting at pieces[current].pos+delta.
* Returns a properly cast sequence, as old cached version may be invalidated
* by the call to ft_shift_up.
*/
{
register Piece old, new;
register int is_scratch;
register Es_index source_pos;
ft_shift_up(pieces, current+1, current+2, 10);
/* Can invalidate &pieces.seq[i] */
old = ((Piece)(pieces->seq)) + current;
new = old + 1;
is_scratch = PS_SANDP_SOURCE(old[0]);
source_pos = PS_SANDP_POS(old[0]);
new->pos = old->pos + delta;
source_pos += delta;
new->length = old->length - delta;
old->length = delta;
PS_SET_SANDP(new[0], source_pos, is_scratch);
return((Piece)(pieces->seq));
}
static Es_index
ps_read(esh, len, bufp, resultp)
Es_handle esh;
u_int len,
*resultp;
register char *bufp;
{
#ifdef DEBUG_TRACE
Es_index next, pos;
char temp;
pos = (ABS_TO_REP(esh))->position;
next = ps_debug_read(esh, len, bufp, resultp);
(void) fprintf(stdout,
"ps_read at pos %d of %d chars got %d chars; %s %d ...\n",
pos, len, *resultp, "next read at", next);
#ifdef DEBUG_TRACE_CONTENTS
temp = bufp[*resultp];
bufp[*resultp] = '\0';
(void) fprintf(stdout, "%s\n^^^\n", bufp);
bufp[*resultp] = temp;
#endif
return(next);
}
static Es_index
ps_debug_read(esh, len, bufp, resultp)
Es_handle esh;
u_int len,
*resultp;
register char *bufp;
{
#endif
/*
* "current" must be signed, because it can become -1 when deciding whether
* to combine pieces containing invalid physical scratch indices.
*/
int read_count, save_current;
Es_index next_pos, original_len;
register int current, to_read;
register long int delta;
register Es_index current_pos;
register Es_handle current_esh;
register Piece pieces;
register Piece_table private = ABS_TO_REP(esh);
if (private->length - private->position < len) {
len = private->length - private->position;
}
pieces = PIECES_IN_TABLE(private);
*resultp = 0;
if (private->current == CURRENT_NULL) {
current =
ft_bounding_index(&private->pieces, private->position);
} else
current = private->current;
while (VALID_PIECE_INDEX(private, current) && len > 0) {
delta = private->position - pieces[current].pos;
ASSERT(*resultp == 0 || delta == 0);
current_esh = (PS_SANDP_SOURCE(pieces[current]) ?
private->scratch :
private->original);
current_pos = PS_SANDP_POS(pieces[current]) + delta;
next_pos = es_set_position(current_esh, current_pos);
ASSERT(next_pos == current_pos);
to_read = pieces[current].length - delta;
if (to_read > len)
to_read = len;
next_pos = es_read(current_esh, to_read, bufp, &read_count);
/* BUG ALERT! If we ever support entities that are not bytes,
* the following addition must have a
* "* es_get(current_esh, ES_SIZE_OF_ENTITY)"
*/
bufp += read_count;
len -= read_count;
*resultp += read_count;
private->position += read_count; /* zaps private->current */
if (read_count < to_read) {
if (current_esh == private->original) {
/* The original entity stream has holes in it,
* and the initialization assumed it was contiguous,
* so the pieces and length must be corrected.
*/
pieces = split_piece(
&private->pieces, current, delta+read_count);
current++;
/* Compute the gap size */
delta = next_pos - (current_pos + read_count);
ASSERT(pieces[current].length > delta);
pieces[current].length -= delta;
private->length -= delta;
PS_SET_ORIGINAL_SANDP(pieces[current], next_pos);
} else {
/* The scratch entity stream has wrapped around, making a
* hole from [0..scratch_first_valid).
*/
ASSUME(SCRATCH_HAS_WRAPPED(private) ||
(private->parent &&
SCRATCH_HAS_WRAPPED(ABS_TO_REP(private->parent)) ));
/* Caller needs to be told where next successful read
* is, so search for piece containing valid scratch
* indices (WARNING: there may not be one).
*/
for (save_current = current,
current_pos = private->position;
VALID_PIECE_INDEX(private, current);
current++) {
ASSERT(PS_SANDP_SOURCE(pieces[current]));
delta = PS_SANDP_POS(pieces[current]);
if (delta >= next_pos) {
/* next_pos not referenced by pieces => go to start
* this piece, which is first beyond next_pos
*/
next_pos = delta;
} else if (next_pos >= delta+pieces[current].length) {
continue;
}
private->position =
(next_pos - delta) + pieces[current].pos;
/* Allow uniform handling of current below. */
current++;
break;
}
if (current_pos == private->position)
private->position = es_get_length(esh);
/* Although the pieces are correct it speeds up later
* reads if "runs of now invalid scratch references"
* are combined; however, only completely invalid
* pieces can be combined (else the record headers will
* be incorrectly included as part of the visible text).
* -2 backs over terminating and partially valid pieces.
*/
current -= 2;
if (save_current < current) {
delta = PS_LAST_PLUS_ONE(pieces[current]);
pieces[save_current].length =
delta - pieces[save_current].pos;
ft_shift_out(&private->pieces,
save_current+1, current+1);
}
/* A read at the beginning of the scratch stream which
* tries to read out of the start of the hole should
* give the client (an appropriate part of) wrap_msg.
* However, don't do this until AFTER finding out where
* next valid position is, otherwise can return too
* much text.
*/
for (current = 0, original_len = 0;
VALID_PIECE_INDEX(private, current);
current++) {
if (PS_SANDP_SOURCE(pieces[current]))
break;
original_len = PS_LAST_PLUS_ONE(pieces[current]);
}
current = private->pieces.last_plus_one;
INVALIDATE_CURRENT(private);
ASSUME(original_len <= current_pos);
read_count = strlen(wrap_msg);
if (*resultp == 0 &&
current_pos < original_len + read_count) {
FILE * console_fd;
if (private->position < original_len + read_count)
read_count = private->position - original_len;
read_count -= current_pos - original_len;
if (len < read_count)
read_count = len;
bcopy(wrap_msg+current_pos, bufp, read_count);
*resultp = read_count;
/* tell user that they are going to wrap */
/* Use the same flag for console error message */
if (max_transcript_alert_displayed != 1) {
console_fd = fopen("/dev/console", "a");
fprintf(console_fd, "The text transcript in a Commands window filled up, and the oldest saved text will be erased from the top as more is typed at the bottom.\n");
fflush(console_fd);
max_transcript_alert_displayed = 1;
fclose(console_fd);
}
#ifdef notdef
if (max_transcript_alert_displayed != 1) {
Event alert_event;
(void) alert_prompt(
(Frame)0,
&alert_event,
ALERT_MESSAGE_STRINGS,
"Text has been lost in a cmdtool transcript because",
"the maximum edit log size has been exceeded.",
0,
ALERT_BUTTON_YES, "Continue",
ALERT_TRIGGER, ACTION_STOP,
/* with trigger, yes or no bring it down */
0);
max_transcript_alert_displayed = 1;
}
#endif
}
}
/* All of the above code is free and easy with local
* variables because it expects to exit the loop here, so
* it will need to be rewritten if this break is removed!
*/
break;
}
current++;
}
if (VALID_PIECE_INDEX(private, current)) {
private->current = current;
if (pieces[current].pos > private->position)
private->current--;
/* Short read advances current anyway, so undo the advance. */
} else
INVALIDATE_CURRENT(private);
ASSUME(ps_pieces_are_consistent(private));
return (private->position);
}
/* The routines ps_replace, ps_insert_pieces, and ps_undo_to_mark all perform
* similar functions and a fix to one needs to be reflected in the others.
*/
static Es_index
ps_replace(esh, last_plus_one, count, buf, count_used)
Es_handle esh;
Es_index last_plus_one;
long int count, *count_used;
char *buf;
{
#ifdef DEBUG_TRACE
Es_index next, pos;
char temp;
pos = (ABS_TO_REP(esh))->position;
next = ps_debug_replace(esh, last_plus_one, count, buf, count_used);
(void) fprintf(stdout,
"ps_replace [%d..%d) by %d chars (used %d); next pos %d ...\n",
pos, last_plus_one, count, *count_used, "next read at", next);
if (buf) {
temp = buf[count];
buf[count] = '\0';
(void) fprintf(stdout, "%s\n^^^\n", buf);
buf[count] = temp;
}
return(next);
}
static Es_index
ps_debug_replace(esh, last_plus_one, count, buf, count_used)
Es_handle esh;
Es_index last_plus_one;
long int count, *count_used;
char *buf;
{
#endif
/* Assume that the piece stream looks like:
* O1O S1S O2O S2S S3S O3O O4O S4S
* where XnX means n-th chunk in stream X, and X2X may actually
* occur earlier in stream X than X1X.
* The possible cases are:
* 1) Replace an empty region, with subcases:
* 1a) Region is at end of previous replace, hence it can be treated
* as an extension of the previous replace, and the associated
* scratch piece can simply be lengthened. (S4S)
* 1b) Otherwise, a new piece must be created, and the piece that
* contains the empty region must be split (XnX -> XnX S5S XmX),
* unless the empty region is at the beginning of that piece
* (XnX -> S5S XnX).
* 2) Replace a region completely within one piece:
* 2a) If within latest scratch region, (1a) applies.
* 2b) If the region exactly matches the piece, re-cycle the piece to
* point at the new contents.
* 2c) Otherwise, (1b) applies.
* 3) Replace a region spanning multiple pieces.
* 3a) For partially enclosed pieces, (1) applies.
* 3b) For completely enclosed pieces, delete them all, except
* possibly one to use as in (2b) if (3a) does not occur.
*
* WARNING! Changes to the pieces can only be made AFTER calls to
* es_replace(scratch, ...) succeed, unless those changes do not
* break the assertions about the pieces OR are backed out in case
* of es_replace() failing.
*/
Es_index scratch_length;
Es_index es_temp, new_rec_insert;
long int long_temp;
int delete_pieces_length, replace_used;
register int current, end_index;
register long int delta; /* Has 2 meanings! */
register Es_handle scratch;
register Piece pieces;
register Piece_table private = ABS_TO_REP(esh);
*count_used = 0;
if (buf == NULL && count != 0) {
private->status = ES_INVALID_ARGUMENTS;
return(ES_CANNOT_SET);
}
if (private->parent != NULL) {
private->status = ES_INVALID_HANDLE;
return(ES_CANNOT_SET);
}
scratch = private->scratch;
scratch_length = es_set_position(scratch, ES_INFINITY);
new_rec_insert = ES_INFINITY; /* => extending current */
if (last_plus_one > private->length) {
last_plus_one = private->length;
}
ASSUME(last_plus_one >= private->position);
pieces = PIECES_IN_TABLE(private);
if (private->length == 0) {
/* Special case occurring after everything replaced by nothing. */
ASSUME(pieces[0].pos == ES_INFINITY);
if (count == 0) {
/* Nothing replaced by nothing => leave well enough alone! */
return(private->position);
}
pieces[current = 0].pos = 0;
goto Append_Only;
}
delta = get_current_offset(private);
current = private->current;
if (last_plus_one > private->position) {
/* Replace a NON-empty region */
if (delta != 0) {
/* Split the current piece at the insert point. */
pieces = split_piece(&private->pieces, current, delta);
current++;
}
if (last_plus_one > PS_LAST_PLUS_ONE(pieces[current])) {
/* Replace region spanning multiple pieces */
end_index =
ft_bounding_index(&private->pieces, last_plus_one-1);
/* -1 ensures that pieces[end_index] < last_plus_one,
* preventing creation of a 0-length piece below.
*/
ASSERT(VALID_PIECE_INDEX(private, end_index));
} else {
/* Replace region within a single piece */
end_index = current;
}
if (last_plus_one < PS_LAST_PLUS_ONE(pieces[end_index])) {
/* Split the end piece at the last_plus_one point */
pieces = split_piece(&private->pieces, end_index,
last_plus_one-pieces[end_index].pos);
}
/* Replace region now exactly equals [current..end_index] pieces.
* Make sure all es_replace(scratch, ...) succeed before
* modifying private->... (including pieces) any further.
*/
new_rec_insert =
write_header_etc(scratch, private, last_plus_one,
(long)count, buf, count_used,
&es_temp, &delete_pieces_length,
current, end_index+1);
if (new_rec_insert == ES_CANNOT_SET) {
goto Error_Return;
}
if (count == 0) {
current--; /* Make ft_shift_out & ft_add_delta work */
}
/* Recycle current piece, so don't shift it out */
ft_shift_out(&private->pieces, current+1, end_index+1);
/* Meaning 2: total change to length */
delta = count - delete_pieces_length;
} else if (count == 0) {
/* Empty region replaced by nothing => leave well enough alone!
* Not catching this case creates an extra 0-length piece.
*/
return(private->position);
} else {
/* Replace an empty region */
int at_end_of_stream = PS_IS_AT_END(private, delta, current);
if (private->position == private->last_write_plus_one) {
es_temp = es_replace(scratch, ES_INFINITY,
count, buf, count_used);
if (es_temp == ES_CANNOT_SET) {
goto Error_Return;
}
ASSUME(count == *count_used);
if (private->rec_insert_len == 0) {
/* Last replace was a delete, so no scratch piece exists */
/* Split the current piece at the insert point */
pieces = split_piece(&private->pieces, current, delta);
if (delta != 0) {
ASSUME(at_end_of_stream);
current++;
delta = 0;
}
PS_SET_SCRATCH_SANDP(pieces[current],
private->rec_insert+sizeof(long_temp));
} else if (at_end_of_stream) {
/* Last replace was at end of stream => don't back up */
} else {
ASSERT(delta == 0 && current > 0);
current--;
delta = private->position - pieces[current].pos;
}
ASSERT(delta == pieces[current].length);
delta = count; /* Meaning 2: total change to length */
private->rec_insert_len += delta;
pieces[current].length += delta;
/* Correct the insert length (this is very inefficient) */
long_temp = private->rec_insert_len;
es_temp = es_set_position(scratch, private->rec_insert);
ASSERT(es_temp == private->rec_insert);
es_temp = es_replace(scratch,
private->rec_insert+sizeof(long_temp),
sizeof(long_temp), (char *)&long_temp,
&replace_used);
} else {
if (delta != 0) {
/* Split the current piece at the insert point */
pieces = split_piece(&private->pieces, current, delta);
current++;
if (at_end_of_stream) {
/* Append at the very end of the stream will create
* a zero length piece which is unnecessary, so we
* detect this case and recycle the piece.
*/
goto Append_Only;
}
}
/* Handle the delta == 0 case. Create new piece */
pieces = split_piece(&private->pieces, current, 0L);
Append_Only:
/* Make sure all es_replace(scratch, ...) succeed before
* modifying private->... (including pieces) any further.
*/
new_rec_insert =
write_header_etc(scratch, private, last_plus_one, count,
buf, count_used, &es_temp, (int *)0,
-1, -1);
if (new_rec_insert == ES_CANNOT_SET) {
/* Reverse all damaging changes to pieces */
if (private->length == 0) {
pieces[0].pos = ES_INFINITY;
} else if (pieces[current].length == 0) {
ft_shift_out(&private->pieces, current, current+1);
}
goto Error_Return;
}
delta = count; /* Meaning 2: total change to length */
}
}
/* Update (if necessary) the fields tracking the insert record.
* Also fix up the associated piece fields.
*/
if (new_rec_insert != ES_INFINITY) {
private->rec_insert = new_rec_insert;
private->rec_insert_len = count;
private->rec_start = scratch_length;
if (private->oldest_not_undone_mark == ES_INFINITY)
private->oldest_not_undone_mark = scratch_length;
ASSUME(count == *count_used);
if (count != 0) {
pieces[current].length = count;
ASSERT(es_temp == private->rec_insert+sizeof(long_temp));
PS_SET_SCRATCH_SANDP(pieces[current], es_temp);
}
}
/* Adjust position, etc. to reflect the overall replace */
ft_add_delta(private->pieces, current+1, delta);
private->length += delta;
SET_POSITION(private, private->position + count);
private->last_write_plus_one = private->position;
ASSUME(0 <= private->position && private->position <= MAX_LENGTH);
ASSUME(ps_pieces_are_consistent(private));
return(private->position);
Error_Return:
private->status = (Es_status)es_get(scratch, ES_STATUS);
/* Copy scratch status first in case attempt to "back out"
* failed es_replace's modify the status.
*/
(void) es_set_position(scratch, scratch_length);
(void) es_replace(scratch, ES_INFINITY, 0, NULL, count_used);
INVALIDATE_CURRENT(private);
ASSUME(ps_pieces_are_consistent(private));
return(ES_CANNOT_SET);
}
static Es_index
write_record_header(esh, private, last_plus_one, dp_count)
Es_handle esh;
register Piece_table private;
Es_index last_plus_one;
int dp_count;
{
struct piece_record_header r_header;
register Es_index result;
int replace_used;
r_header.pos_prev_rec = private->rec_start;
r_header.flags = 0;
r_header.start = private->position;
r_header.stop_plus_one = last_plus_one;
r_header.dp_count = dp_count;
result = es_replace(esh, ES_INFINITY,
sizeof(r_header), (char *)&r_header,
&replace_used);
return(result);
}
static int
record_deleted_pieces(esh, private, pieces, first, last_plus_one, next)
Es_handle esh;
Piece_table private;
Piece pieces;
int first, last_plus_one;
Es_index *next;
{
struct deleted_piece d_header;
int dummy;
register Piece current, stop_plus_one;
register int result = 0;
register Es_index replace_result;
stop_plus_one = &pieces[last_plus_one];
for (current = &pieces[first]; current < stop_plus_one; current++) {
d_header.source_and_pos = current->source_and_pos;
result += (d_header.length = current->length);
replace_result =
es_replace(esh, ES_INFINITY,
sizeof(d_header), (char *)&d_header, &dummy);
if (replace_result == ES_CANNOT_SET)
break;
}
*next = replace_result;
return(result);
}
/* Returns the new value for private->rec_insert */
static Es_index
write_header_etc(esh, private, last_plus_one, count, buf, count_used,
contents_start,
deleted_pieces_length, first_deleted, last_plus_one_deleted)
register Es_handle esh;
register Piece_table private;
Es_index last_plus_one;
long int count;
long int *count_used;
char *buf;
Es_index *contents_start;
int *deleted_pieces_length;
int first_deleted, last_plus_one_deleted;
{
register Es_index result;
Es_index es_temp;
int replace_used;
/* Write the record header (and possibly) deleted pieces. */
result = write_record_header(esh, private, last_plus_one,
last_plus_one_deleted-first_deleted);
if (result == ES_CANNOT_SET)
return(ES_CANNOT_SET);
if (first_deleted < last_plus_one_deleted) {
*deleted_pieces_length =
record_deleted_pieces(esh, private, PIECES_IN_TABLE(private),
first_deleted, last_plus_one_deleted,
&es_temp);
if (es_temp == ES_CANNOT_SET)
return(ES_CANNOT_SET);
result = es_temp;
}
*contents_start =
es_replace(esh, ES_INFINITY, sizeof(count),
(char *)&count, &replace_used);
if (*contents_start == ES_CANNOT_SET)
return(ES_CANNOT_SET);
/* Do the insert */
if (count != 0) {
es_temp = es_replace(esh, ES_INFINITY, count, buf, count_used);
if (es_temp == ES_CANNOT_SET)
return(ES_CANNOT_SET);
}
return(result);
}
static Es_handle
ps_pieces_for_span(esh, first, last_plus_one, to_recycle)
Es_handle esh;
Es_index first, last_plus_one;
Es_handle to_recycle;
{
register Piece_table private = ABS_TO_REP(esh);
register Piece result_pieces;
register int is_scratch;
register Es_index delta, source_pos;
int first_index, last_index;
Es_handle result = (Es_handle)0;
Piece_table r_private;
if (last_plus_one > private->length)
last_plus_one = private->length;
if (first >= last_plus_one) {
goto Bad_Args;
}
ASSUME(allock());
first_index = ft_bounding_index(&private->pieces, first);
last_index = ft_bounding_index(&private->pieces, last_plus_one-1);
if (to_recycle) {
result = to_recycle;
r_private = ABS_TO_REP(result);
ASSUME(r_private->magic == PS_MAGIC && r_private->parent == esh);
if (r_private->pieces.last_plus_one <= (last_index-first_index)) {
ft_destroy(&r_private->pieces); /* Zeros .last_plus_one */
}
} else {
result = ps_NEW(0);
if (result == (Es_handle)0)
goto Out_Of_Memory;
r_private = ABS_TO_REP(result);
r_private->parent = esh;
r_private->original = private->original;
r_private->scratch = private->scratch;
r_private->last_write_plus_one = r_private->rec_insert =
r_private->rec_start = ES_CANNOT_SET;
r_private->rec_insert_len = -1;
}
if (r_private->pieces.last_plus_one == 0) {
r_private->pieces =
FT_CLIENT_CREATE(last_index-first_index+1, Piece_object);
if (r_private->pieces.seq == (Es_index *)0)
goto Out_Of_Memory;
}
FT_CLEAR_ALL(r_private->pieces);
copy_pieces(&r_private->pieces, 0, &private->pieces,
first_index, last_index+1);
/*
* Fix up the end pieces.
*/
result_pieces = &PIECES_IN_TABLE(r_private)[last_index-first_index];
result_pieces->length = last_plus_one - result_pieces->pos;
result_pieces = &PIECES_IN_TABLE(r_private)[0];
delta = first - result_pieces->pos;
if (delta != 0) {
result_pieces->pos = first;
is_scratch = PS_SANDP_SOURCE(*result_pieces);
source_pos = PS_SANDP_POS(*result_pieces);
source_pos += delta;
result_pieces->length -= delta;
PS_SET_SANDP(*result_pieces, source_pos, is_scratch);
}
SET_POSITION(r_private, first);
r_private->length = last_plus_one;
ASSUME(allock());
return(result);
Out_Of_Memory:
if (result) {
es_destroy(result);
result = (Es_handle)0;
}
Bad_Args:
if (to_recycle)
es_destroy(to_recycle);
return(result);
}
static
copy_pieces(to_table, to_index, from_table, first, last_plus_one)
register ft_handle to_table, from_table;
int to_index, first, last_plus_one;
{
register int sizeof_element = from_table->sizeof_element;
ASSERT(to_table->sizeof_element == sizeof_element);
bcopy((char *)from_table->seq + first*sizeof_element,
(char *)to_table->seq + to_index*sizeof_element,
(last_plus_one-first)*sizeof_element);
}
static int
get_current_offset(private)
register Piece_table private;
{
register Piece current;
register int result;
if (private->current == CURRENT_NULL)
private->current =
ft_bounding_index(&private->pieces, private->position);
ASSERT(VALID_PIECE_INDEX(private, private->current));
current = &PIECES_IN_TABLE(private)[private->current];
result = private->position - current->pos;
ASSUME(result < current->length ||
PS_IS_AT_END(private, result, private->current) );
return(result);
}
static void
ps_insert_pieces(esh, to_insert)
Es_handle esh, to_insert;
/*
* Much of this routine is stolen from ps_replace.
* A special convention established by this routine is that if the scratch
* stream has a replace record with start == stop_plus_one and a non-zero
* deleted piece count, then it is a result of inserting pieces, rather than
* characters.
*/
{
ft_handle rep = (ft_handle)
&(ABS_TO_REP(to_insert))->pieces;
long int long_temp;
int at_end_of_stream, replace_used;
long int delta;
int save_last_plus_one;
Es_index scratch_length, es_temp;
register Piece pieces;
register int current, last_valid;
register Piece_table private = ABS_TO_REP(esh);
register Es_handle scratch = private->scratch;
ASSERT(rep != FT_NULL);
last_valid = ft_bounding_index(rep, ES_INFINITY-1);
ASSERT(last_valid != rep->last_plus_one);
/* Prepare the insertion area. */
pieces = PIECES_IN_TABLE(private);
if (private->length == 0 && pieces[0].pos == ES_INFINITY) {
/* Special case that occurs if replaced everything by nothing. */
current = 0;
delta = 0;
at_end_of_stream = 1;
pieces[0].pos = 0;
pieces[0].length = 0;
PS_SET_SCRATCH_SANDP(pieces[0], 0);
} else {
INVALIDATE_CURRENT(private);
delta = get_current_offset(private);
current = private->current;
if (delta == 0) {
at_end_of_stream = 0;
} else {
at_end_of_stream = (delta == pieces[current].length);
/*
* Split the current piece at the insert point.
* This creates a zero length piece iff at_end_of_stream.
*/
pieces = split_piece(&private->pieces, current, delta);
current++;
}
}
#ifdef DEBUG
long_temp = pieces[current].pos;
#endif
/* Do the insert and associated bookkeeping */
ft_shift_up(&private->pieces, current, current+last_valid+1,
last_valid+1);
pieces = PIECES_IN_TABLE(private);
copy_pieces(&private->pieces, current, rep, 0, last_valid+1);
ASSERT(allock());
/* Offset inserted pieces so they match the insertion point.
* BUG ALERT: need version of ft_add_delta that takes stop index,
* but for now, monkey with the piece table itself.
*/
save_last_plus_one = private->pieces.last_plus_one;
private->pieces.last_plus_one = current+last_valid+1;
ft_add_delta(private->pieces, current,
private->position - rep->seq[0]);
private->pieces.last_plus_one = save_last_plus_one;
ASSERT(long_temp == pieces[current+last_valid+1].pos);
/* Write header info to scratch. */
scratch_length = es_set_position(scratch, ES_INFINITY);
es_temp = write_record_header(scratch, private, private->position,
last_valid+1);
if (es_temp != ES_CANNOT_SET) {
/* Can modify private->... iff es_replace succeeds */
private->rec_insert = es_temp;
private->rec_start = scratch_length;
if (private->oldest_not_undone_mark == ES_INFINITY)
private->oldest_not_undone_mark = scratch_length;
}
delta = record_deleted_pieces(
scratch, private, pieces,
current, current+last_valid+1,
&private->rec_insert);
long_temp = 0;
(void) es_replace(scratch, ES_INFINITY,
sizeof(long_temp), (char *)&long_temp,
&replace_used);
ASSERT(allock());
/* Adjust position, etc. to reflect the overall replace */
if (at_end_of_stream) {
/* Flush the extraneous zero length piece we created. */
ASSERT(pieces[current+last_valid+1].length == 0);
pieces[current+last_valid+1].pos = ES_INFINITY;
} else {
ft_add_delta(private->pieces, current+last_valid+1, delta);
}
private->last_write_plus_one = ES_INFINITY;
private->length += delta;
SET_POSITION(private, private->position + delta);
}
static Es_index
adjust_pos_after_edit(pos, start, delta)
register Es_index pos, start, delta;
{
if (delta > 0)
return((start <= pos) ? pos+delta : pos);
else if (start < pos)
return((start-delta < pos) ? pos+delta : start);
else
return(pos);
}
static void
ps_undo_to_mark(esh, mark, notify_proc, notify_data)
Es_handle esh;
Es_index mark;
int (*notify_proc)();
caddr_t notify_data;
/*
* The notify_proc gets called with the notify_data, the start of the affected
* range, and the delta. Thus a negative delta means [start..start-delta)
* contains the affected positions, while a positive delta indicates an
* insertion of entities to fill the range [start..start+delta).
*/
{
register Piece_table private = ABS_TO_REP(esh);
register Es_handle scratch = private->scratch;
register Piece pieces, this_piece;
register Es_index current_pos, scratch_pos,
save_pos = private->position;
Es_index delta,
mark_pos = mark;
struct piece_record_header r_header;
struct deleted_piece d_header;
int current, i, read;
long int insert_len;
if (es_get_length(scratch) == 0)
return;
/* For a bounded scratch stream, the mark_pos may now be invalid and
* need to be adjusted. The new value need not be exactly at a record
* header (luckily, else all of the record headers would have to be
* read twice).
*/
if (SCRATCH_HAS_WRAPPED(private) &&
mark_pos < SCRATCH_FIRST_VALID(private)) {
mark_pos = SCRATCH_FIRST_VALID(private);
}
/* Read back the information from the scratch source and undo it */
for (; (private->rec_start != ES_INFINITY) &&
(private->rec_start >= mark_pos);
private->rec_start = r_header.pos_prev_rec) {
(void) es_set_position(scratch, private->rec_start);
(void) es_read(scratch, sizeof(r_header),
(char *)&r_header, &read);
ASSUME(read == sizeof(r_header));
/* Check to see if piece is flagged as already undone.
* If not, make sure it is flagged now.
*/
if (r_header.flags & PS_ALREADY_UNDONE)
continue;
r_header.flags |= PS_ALREADY_UNDONE;
(void) es_set_position(scratch, private->rec_start);
(void) es_replace(scratch, private->rec_start+sizeof(r_header),
sizeof(r_header), (char *)&r_header, &read);
ASSUME(read == sizeof(r_header));
if (private->oldest_not_undone_mark == private->rec_start)
private->oldest_not_undone_mark = ES_INFINITY;
current_pos = r_header.start;
SET_POSITION(private, current_pos);
if (private->length == 0 &&
PIECES_IN_TABLE(private)[0].pos == ES_INFINITY) {
/* Special case occurs when nothing replaced everything.
* BUG ALERT: later on, we depend on the [0].pos being left
* as ES_INFINITY, else the ft_shift_up corrupts the
* piece table.
*/
current = 0;
delta = 0;
} else {
delta = get_current_offset(private);
current = private->current;
}
if ((r_header.start == r_header.stop_plus_one) &&
(r_header.dp_count != 0)) {
/* Remove the inserted pieces.
* Since ps_replace does NOT coalesce pieces, a single
* inserted piece may be turned into multiple pieces via
* sequences of "type-in edit-chars-deleting type-in", and
* UNDO of those sequences also does NOT coalesce the pieces,
* so when we go to UNDO the original insert we must not rely
* on a one-to-one mapping of the pieces.
*/
int piece_length, piece_count = 0;
ASSERT(delta == 0);
pieces = PIECES_IN_TABLE(private);
this_piece = &pieces[current];
for (i = r_header.dp_count; i > 0; i--) {
(void) es_read(scratch, sizeof(d_header),
(char *)&d_header, &read);
ASSERT(this_piece->pos == current_pos-delta);
ASSERT(this_piece->source_and_pos == d_header.source_and_pos);
delta -= d_header.length;
for (piece_length = d_header.length; piece_length > 0;
piece_count++) {
ASSERT(this_piece->length <= piece_length);
piece_length -= this_piece->length;
this_piece++;
}
}
ft_shift_out(&private->pieces, current,
current+piece_count);
ft_add_delta(private->pieces, current, delta);
save_pos = adjust_pos_after_edit(save_pos, current_pos, delta);
private->length += delta;
if (notify_proc) {
scratch_pos = es_get_position(scratch);
notify_proc(notify_data, current_pos, delta);
(void) es_set_position(scratch, scratch_pos);
}
} else {
/* Put back the deleted pieces */
if (delta == 0) {
ft_add_delta(private->pieces, current,
(long)(r_header.stop_plus_one-r_header.start));
} else {
ASSERT(PS_IS_AT_END(private, delta, current));
current++;
}
ft_shift_up(&private->pieces, current,
(int)(current+r_header.dp_count),
(int)r_header.dp_count);
pieces = PIECES_IN_TABLE(private);
this_piece = &pieces[current];
delta = r_header.stop_plus_one-current_pos;
save_pos = adjust_pos_after_edit(save_pos, current_pos, delta);
for (i = 0; i < r_header.dp_count; i++, this_piece++) {
(void) es_read(scratch, sizeof(d_header),
(char *)&d_header, &read);
this_piece->pos = current_pos;
this_piece->length = d_header.length;
this_piece->source_and_pos = d_header.source_and_pos;
current_pos += d_header.length;
}
ASSERT(current_pos == r_header.stop_plus_one);
if (delta != 0) { /* 0 iff no deleted pieces. */
private->length += delta;
if (notify_proc) {
scratch_pos = es_get_position(scratch);
notify_proc(notify_data, r_header.start, delta);
(void) es_set_position(scratch, scratch_pos);
}
}
}
(void) es_read(scratch, sizeof(insert_len),
(char *)&insert_len, &read);
if (insert_len > 0) {
/*
* Remove the inserted text.
* Note that the user sequence: type-in edit-char type-in ...
* can cause this insert to span multiple pieces in the
* piece table.
*/
current += r_header.dp_count;
this_piece = &pieces[current];
for (delta = 0, i = 0; delta < insert_len;
delta += this_piece->length, this_piece++) {
ASSERT(this_piece->pos == current_pos+delta);
ASSERT(this_piece->length <= insert_len-delta);
i++;
}
ft_shift_out(&private->pieces, current, current+i);
ft_add_delta(private->pieces, current, -insert_len);
save_pos =
adjust_pos_after_edit(save_pos, current_pos, -insert_len);
private->length -= insert_len;
if (notify_proc)
notify_proc(notify_data, current_pos, -insert_len);
}
ASSUME(ps_pieces_are_consistent(private));
}
(void) es_set_position(scratch, ES_INFINITY);
SET_POSITION(private, save_pos);
private->last_write_plus_one = ES_INFINITY;
}
static caddr_t
ps_get(esh, attribute, va_alist)
Es_handle esh;
Es_attribute attribute;
va_dcl
{
register Piece_table private = ABS_TO_REP(esh);
Es_index first, last_plus_one;
Es_handle pieces_for_span, to_recycle;
va_list args;
if ((private->magic != PS_MAGIC) && (attribute != ES_TYPE))
return((caddr_t)0);
switch (attribute) {
case ES_CLIENT_DATA:
return(private->client_data);
case ES_UNDO_MARK:
private->last_write_plus_one = ES_INFINITY;
/* +1 below is because 0 == ES_NULL_UNDO_MARK */
return((caddr_t)(es_get_length(private->scratch)+1));
case ES_HANDLE_FOR_SPAN:
va_start(args);
#ifdef lint
first = (args ? 0 : 0);
last_plus_one = 0;
to_recycle = 0;
#else
first = va_arg(args, Es_index);
last_plus_one = va_arg(args, Es_index);
to_recycle = va_arg(args, Es_handle);
#endif
pieces_for_span =
ps_pieces_for_span(esh, first, last_plus_one, to_recycle);
va_end(args);
return((caddr_t)pieces_for_span);
case ES_HAS_EDITS:
return((caddr_t)(private->oldest_not_undone_mark != ES_INFINITY));
case ES_PS_ORIGINAL:
return((caddr_t)(private->original));
case ES_PS_SCRATCH:
return((caddr_t)(private->scratch));
case ES_PS_SCRATCH_MAX_LEN:
return((caddr_t)(private->scratch_max_len));
case ES_STATUS:
return((caddr_t)(private->status));
case ES_SIZE_OF_ENTITY:
return(es_get(private->original, ES_SIZE_OF_ENTITY));
case ES_TYPE:
return((caddr_t)ES_TYPE_PIECE);
default:
return(0);
}
}
static int
ps_set(esh, attrs)
Es_handle esh;
caddr_t *attrs;
{
register Piece_table private = ABS_TO_REP(esh);
int (*notify_proc)() = (int (*)())0;
caddr_t notify_data = 0;
Es_index undo_mark;
Es_index first;
Es_handle to_recycle;
Es_status status_dummy = ES_SUCCESS;
register Es_status *status;
status = &status_dummy;
if (private->magic != PS_MAGIC)
*status = ES_INVALID_TYPE;
for (; *attrs && (*status == ES_SUCCESS); attrs = attr_next(attrs)) {
switch ((Es_attribute)*attrs) {
case ES_CLIENT_DATA:
private->client_data = attrs[1];
break;
case ES_HANDLE_TO_INSERT:
to_recycle = (Es_handle)LINT_CAST(attrs[1]);
if (private->scratch_max_len == ES_INFINITY) {
ps_insert_pieces(esh, to_recycle);
} else {
/* When scratch is bounded, copy contents, not pieces. */
*status = es_copy(to_recycle, esh, FALSE);
}
break;
case ES_PS_ORIGINAL:
/* Caller should destroy the old private->original iff
* return value is ES_SUCCESS, allowing for caller to
* recover in case of errors.
*/
to_recycle = (Es_handle)LINT_CAST(attrs[1]);
first = es_get_position(private->original);
if (to_recycle == ES_NULL) {
*status = ES_INVALID_HANDLE;
} else if (es_get_length(private->original) !=
es_get_length(to_recycle)) {
*status = ES_INCONSISTENT_LENGTH;
} else if (first != es_set_position(to_recycle, first)) {
*status = ES_INCONSISTENT_POS;
} else {
private->original = to_recycle;
}
break;
case ES_PS_SCRATCH_MAX_LEN:
first = (Es_index)LINT_CAST(attrs[1]);
if (first < SCRATCH_MIN_LEN ||
first < es_get_length(private->scratch)) {
*status = ES_INCONSISTENT_LENGTH;
} else if (first >= ES_INFINITY) {
if (private->scratch_max_len != ES_INFINITY) {
*status = ES_INCONSISTENT_LENGTH;
}
} else {
if (private->scratch_max_len == ES_INFINITY) {
es_set(private->scratch, ES_CLIENT_DATA, esh, 0);
private->scratch_max_len = first;
private->scratch_length =
es_get_length(private->scratch);
private->scratch_position =
es_get_position(private->scratch);
/* Modify the scratch ops vector. */
private->scratch_ops = private->scratch->ops;
private->scratch->ops =
(Es_ops)sv_malloc(sizeof(struct es_ops));
*private->scratch->ops = *private->scratch_ops;
private->scratch->ops->destroy = ps_scratch_destroy;
private->scratch->ops->get_length =
ps_scratch_get_length;
private->scratch->ops->get_position =
ps_scratch_get_position;
private->scratch->ops->set_position =
ps_scratch_set_position;
private->scratch->ops->read = ps_scratch_read;
private->scratch->ops->replace = ps_scratch_replace;
}
}
break;
case ES_STATUS:
private->status = (Es_status)LINT_CAST(attrs[1]);
break;
case ES_STATUS_PTR:
status = (Es_status *)LINT_CAST(attrs[1]);
*status = status_dummy;
break;
case ES_UNDO_MARK:
/* -1 below is because 0 == ES_NULL_UNDO_MARK */
undo_mark = ((Es_index)LINT_CAST(attrs[1]))-1;
ps_undo_to_mark(esh, undo_mark, notify_proc, notify_data);
break;
case ES_UNDO_NOTIFY_PAIR:
notify_proc = (int (*)())LINT_CAST(attrs[1]);
notify_data = attrs[2];
break;
default:
break;
}
}
return((*status == ES_SUCCESS));
}