📄 ps_impl.c
字号:
#ifndef lint#ifdef sccsstatic char sccsid[] = "@(#)ps_impl.c 1.1 92/07/30";#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#endifstatic Es_handleps_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_handleps_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 notdefThe following fails because it does not match the state after a replace ofeverything by nothing. However, it is probably correct should theend_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_statusps_commit(esh) Es_handle esh;{ return(ES_SUCCESS);}static Es_handleps_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_handleps_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_indexps_get_length(esh) Es_handle esh;{ register Piece_table private = ABS_TO_REP(esh); return (private->length);}static Es_indexps_scratch_get_length(esh) Es_handle esh;{ register Piece_table private = SCRATCH_TO_REP(esh); return (private->scratch_length);}static Es_indexps_get_position(esh) Es_handle esh;{ register Piece_table private = ABS_TO_REP(esh); return(private->position);}static Es_indexps_scratch_get_position(esh) Es_handle esh;{ register Piece_table private = SCRATCH_TO_REP(esh); return(private->scratch_position);}static Es_indexps_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_indexps_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 DEBUGstatic intps_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);}#endifstatic Es_indexps_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_indexps_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) ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -