📄 ps_impl.c
字号:
} 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_indexadjust_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 voidps_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_tps_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 intps_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));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -