/******************* start of original comments ********************/ /* * Written by Douglas Thomson (1989/1990) * * This source code is released into the public domain. */ /* * Name: dte - Doug's Text Editor program - block commands module * Purpose: This file contains all the commands than manipulate blocks. * File: block.c * Author: Douglas Thomson * System: this file is intended to be system-independent * Date: October 1, 1989 */ /********************* end of original comments ********************/ /* * In the DTE editor, Doug only supported functions for STREAM blocks. * * The block routines have been EXTENSIVELY rewritten. This editor uses LINE, * STREAM, and BOX blocks. That is, one may mark entire lines, streams of * characters, or column blocks. Block operations are done in place. There * are no paste and cut buffers. In limited memory situations, larger block * operations can be carried out. Block operations can be done within or * across files. * * In TDE, version 1.1, I separated the BOX and LINE actions. * * In TDE, version 1.3, I put STREAM blocks back in. Added block upper case, * block lower case, and block strip high bit. * * In TDE, version 1.4, I added a block number function. Here at our lab, * I often need to number samples, lines, etc..., comes in fairly useful. * * In TDE, version 2.0, I added a box block sort function. * * In TDE, version 2.0e, I added BlockRot13, BlockFixUUE, and BlockEmailReply. * * In TDE, version 2.2, the big text buffer was changed to a double linked list. * all characters in the line of each node must be accurately counted. * * New editor name: TDE, the Thomson-Davis Editor. * Author: Frank Davis * Date: June 5, 1991, version 1.0 * Date: July 29, 1991, version 1.1 * Date: October 5, 1991, version 1.2 * Date: January 20, 1992, version 1.3 * Date: February 17, 1992, version 1.4 * Date: April 1, 1992, version 1.5 * Date: June 5, 1992, version 2.0 * Date: October 31, 1992, version 2.1 * Date: April 1, 1993, version 2.2 * Date: June 5, 1993, version 3.0 * Date: August 29, 1993, version 3.1 * Date: November 13, 1993, version 3.2 * Date: June 5, 1994, version 4.0 * * This modification of Douglas Thomson's code is released into the * public domain, Frank Davis. You may distribute it freely. */ #include "tdestr.h" #include "common.h" #include "tdefunc.h" #include "define.h" /* * Name: mark_block * Class: primary editor function * Purpose: To record the position of the start of the block in the file. * Date: June 5, 1991 * Passed: window: pointer to current window * Notes: Assume the user will mark begin and end of a block in either * line, stream, or box mode. If the user mixes types, then block * type defaults to current block type. */ int mark_block( TDE_WIN *window ) { int type; int num; long lnum; register file_infos *file; /* temporary file variable */ register TDE_WIN *win; /* put window pointer in a register */ int rc; win = window; file = win->file_info; if (win->rline > file->length || win->ll->len == EOF) return( ERROR ); if (g_status.marked == FALSE) { g_status.marked = TRUE; g_status.marked_file = file; } if (g_status.command == MarkBox) type = BOX; else if (g_status.command == MarkLine) type = LINE; else if (g_status.command == MarkStream) type = STREAM; else return( ERROR ); rc = OK; /* * define blocks for only one file. it is ok to modify blocks in any window * pointing to original marked file. */ if (file == g_status.marked_file) { /* * mark beginning and ending column regardless of block mode. */ if (file->block_type == NOTMARKED) { file->block_ec = file->block_bc = win->rcol; file->block_er = file->block_br = win->rline; } else { if (file->block_br > win->rline) { file->block_br = win->rline; if (file->block_bc < win->rcol && type != STREAM) file->block_ec = win->rcol; else file->block_bc = win->rcol; } else { if (type != STREAM) { file->block_ec = win->rcol; file->block_er = win->rline; } else { if (win->rline == file->block_br && win->rline == file->block_er) { if (win->rcol < file->block_bc) file->block_bc = win->rcol; else file->block_ec = win->rcol; } else if (win->rline == file->block_br) file->block_bc = win->rcol; else { file->block_ec = win->rcol; file->block_er = win->rline; } } } /* * if user marks ending line less than beginning line then switch */ if (file->block_er < file->block_br) { lnum = file->block_er; file->block_er = file->block_br; file->block_br = lnum; } /* * if user marks ending column less than beginning column then switch */ if ((file->block_ec < file->block_bc) && (type != STREAM || (type == STREAM && file->block_br == file->block_er))) { num = file->block_ec; file->block_ec = file->block_bc; file->block_bc = num; } } /* * block type in now defined. if user mixes block types then block * is defined as current block type. */ if (file->block_type != NOTMARKED) { /* * if block type goes to BOX, check to make sure ec is greater than * or equal to bc. ec can be less than bc in STREAM blocks. */ if (type == BOX) { if (file->block_ec < file->block_bc) { num = file->block_ec; file->block_ec = file->block_bc; file->block_bc = num; } } } assert( file->block_er >= file->block_br ); file->block_type = type; file->dirty = GLOBAL; } else { /* * block already defined */ error( WARNING, win->bottom_line, block1 ); rc = ERROR; } return( rc ); } /* * Name: unmark_block * Class: primary editor function * Purpose: To set all block information to NULL or 0 * Date: June 5, 1991 * Passed: arg_filler: variable to match array of function pointers prototype * Notes: Reset all block variables if marked, otherwise return. * If a marked block is unmarked then redraw the screen(s). */ int unmark_block( TDE_WIN *arg_filler ) { register file_infos *marked_file; if (g_status.marked == TRUE) { marked_file = g_status.marked_file; g_status.marked = FALSE; g_status.marked_file = NULL; marked_file->block_start = NULL; marked_file->block_end = NULL; marked_file->block_bc = marked_file->block_ec = 0; marked_file->block_br = marked_file->block_er = 0l; if (marked_file->block_type) marked_file->dirty = GLOBAL; marked_file->block_type = NOTMARKED; } return( OK ); } /* * Name: restore_marked_block * Class: helper function * Purpose: To restore block beginning and ending row after an editing function * Date: June 5, 1991 * Passed: window: pointer to current window * net_change: number of bytes added or subtracted * Notes: If a change has been made before the marked block then the * beginning and ending row need to be adjusted by the number of * lines added or subtracted from file. */ void restore_marked_block( TDE_WIN *window, int net_change ) { long length; register file_infos *marked_file; if (g_status.marked == TRUE && net_change != 0) { marked_file = g_status.marked_file; length = marked_file->length; /* * restore is needed only if a block is defined and window->file_info is * same as marked file and there was a net change in file length. */ if (marked_file == window->file_info) { /* * if cursor is before marked block then adjust block by net change. */ if (marked_file->block_br > window->rline) { marked_file->block_br += net_change; marked_file->block_er += net_change; marked_file->dirty = GLOBAL; /* * if cursor is somewhere in marked block don't restore, do redisplay */ } else if (marked_file->block_er >= window->rline) marked_file->dirty = GLOBAL; /* * check for lines of marked block beyond end of file */ if (marked_file->block_br > length) unmark_block( window ); else if (marked_file->block_er > length) { marked_file->block_er = length; marked_file->dirty = GLOBAL; } } } } /* * Name: prepare_block * Class: helper function * Purpose: To prepare a window/file for a block read, move or copy. * Date: June 5, 1991 * Passed: window: pointer to current window * file: pointer to file information. * text_line: pointer to line in file to prepare. * lend: line length. * bc: beginning column of BOX. * Notes: The main complication is that the cursor may be beyond the end * of the current line, in which case extra padding spaces have * to be added before the block operation can take place. * this only occurs in BOX and STREAM operations. * since we are padding a line, do not trim trailing space. */ int prepare_block( TDE_WIN *window, line_list_ptr ll, int bc ) { register int pad = 0; /* amount of padding to be added */ register int len; assert( bc >= 0 ); assert( bc < MAX_LINE_LENGTH ); assert( ll->len != EOF ); assert( g_status.copied == FALSE ); copy_line( ll ); detab_linebuff( ); len = g_status.line_buff_len; pad = bc - len; if (pad > 0) { /* * insert the padding spaces */ assert( pad >= 0 ); assert( pad < MAX_LINE_LENGTH ); memset( g_status.line_buff+len, ' ', pad ); g_status.line_buff_len += pad; } /* * if mode.inflate_tabs, let's don't entab the line until we get * thru processing this line, e.g. copying, numbering.... */ return( un_copy_line( ll, window, FALSE ) ); } /* * Name: pad_dest_line * Class: helper function * Purpose: To prepare a window/file for a block move or copy. * Date: June 5, 1991 * Passed: window: pointer to current window * dest_file: pointer to file information. * dest_line: pointer to line in file to prepare. * ll: pointer to linked list node to insert new node * Notes: We are doing a BOX action (except DELETE). We have come * to the end of the file and have no more lines. All this * routine does is add a blank line to file. */ int pad_dest_line( TDE_WIN *window, file_infos *dest_file, line_list_ptr ll ) { int rc; text_ptr l; line_list_ptr new_node; rc = OK; l = NULL; new_node = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc ); if (rc == OK) { new_node->len = 0; new_node->dirty = FALSE; new_node->line = l; if (ll->next != NULL) { ll->next->prev = new_node; new_node->next = ll->next; ll->next = new_node; new_node->prev = ll; } else { new_node->next = ll; if (ll->prev != NULL) ll->prev->next = new_node; new_node->prev = ll->prev; ll->prev = new_node; if (new_node->prev == NULL) window->file_info->line_list = new_node; } ++dest_file->length; } else { /* * file too big */ error( WARNING, window->bottom_line, block4 ); if (new_node != NULL) my_free( new_node ); rc = ERROR; } return( rc ); } /* * Name: move_copy_delete_overlay_block * Class: primary editor function * Purpose: Master BOX, STREAM, or LINE routine. * Date: June 5, 1991 * Passed: window: pointer to current window * Notes: Operations on BOXs, STREAMs, or LINEs require several common * operations. All require finding the beginning and ending marks. * This routine will handle block operations across files. Since one * must determine the relationship of source and destination blocks * within a file, it is relatively easy to expand this relationship * across files. * This is probably the most complicated routine in the editor. It * is not easy to understand. */ int move_copy_delete_overlay_block( TDE_WIN *window ) { int action; TDE_WIN *source_window; /* source window for block moves */ line_list_ptr source; /* source for block moves */ line_list_ptr dest; /* destination for block moves */ long number; /* number of characters for block moves */ int lens; /* length of source line */ int lend; /* length of destination line */ int add; /* characters being added from another line */ int block_len; /* length of the block */ line_list_ptr block_start; /* start of block in file */ line_list_ptr block_end; /* end of block in file - not same for LINE or BOX */ int prompt_line; int same; /* are dest and source files the same file? */ int source_first; /* is source file lower in memory than dest */ file_infos *source_file; file_infos *dest_file; int rcol, bc, ec; /* temporary column variables */ long rline; /* temporary real line variable */ long br, er; /* temporary line variables */ long block_num; /* starting number for block number */ long block_inc; /* increment to use for block number */ int block_just; /* left or right justify numbers? */ int block_type; int fill_char; int rc; /* * initialize block variables */ entab_linebuff( ); rc = un_copy_line( window->ll, window, TRUE ); if (g_status.marked == FALSE || rc == ERROR) return( ERROR ); switch (g_status.command) { case MoveBlock : action = MOVE; break; case DeleteBlock : action = DELETE; break; case CopyBlock : action = COPY; break; case KopyBlock : action = KOPY; break; case FillBlock : action = FILL; break; case OverlayBlock : action = OVERLAY; break; case NumberBlock : action = NUMBER; break; case SwapBlock : action = SWAP; break; default : return( ERROR ); } source_file = g_status.marked_file; source_window = g_status.window_list; for (; ptoul( source_window->file_info ) != ptoul( source_file );) source_window = source_window->next; prompt_line = window->bottom_line; dest_file = window->file_info; check_block( ); if (g_status.marked == FALSE) return( ERROR ); block_start = source_file->block_start; block_end = source_file->block_end; if (block_start == NULL || block_end == NULL) return( ERROR ); block_type = source_file->block_type; if (block_type != LINE && block_type != STREAM && block_type != BOX) return( ERROR ); dest = window->ll; rline = window->rline; if (dest->len == EOF) return( ERROR ); rc = OK; /* * set up Beginning Column, Ending Column, Beginning Row, Ending Row */ bc = source_file->block_bc; ec = source_file->block_ec; br = source_file->block_br; er = source_file->block_er; /* * if we are BOX FILLing or BOX NUMBERing, beginning column is bc, * not the column of cursor */ rcol = (action == FILL || action == NUMBER) ? bc : window->rcol; /* * must find out if source and destination file are the same. * it don't matter with FILL and DELETE - those actions only modify the * source file. */ source_first = same = FALSE; if (action == FILL) { if (block_type == BOX) { if (get_block_fill_char( window, &fill_char ) == ERROR) return( ERROR ); dest = block_start; same = TRUE; } else { /* * can only fill box blocks. */ error( WARNING, prompt_line, block2 ); return( ERROR ); } } block_inc = 1; if (action == NUMBER) { if (block_type == BOX) { if (get_block_numbers( window, &block_num, &block_inc, &block_just ) == ERROR) return( ERROR ); dest = block_start; same = TRUE; } else { /* * can only number box blocks. */ error( WARNING, prompt_line, block3a ); return( ERROR ); } } if (action == SWAP) { if (block_type != BOX) { /* * can only swap box blocks. */ error( WARNING, prompt_line, block3b ); return( ERROR ); } } if (source_file == dest_file && action != DELETE && action != FILL) { same = TRUE; if (block_type == BOX && action == MOVE) { if (rline == br && (rcol >= bc && rcol <= ec)) /* * a block moved to within the block itself has no effect */ return( ERROR ); } else if (block_type == LINE || block_type == STREAM) { if (rline >= br && rline <= er) { if (block_type == LINE) { /* * if COPYing or KOPYing within the block itself, reposition the * destination to the next line after the block (if it exists) */ if (action == COPY || action == KOPY) dest = block_end; /* * a block moved to within the block itself has no effect */ else if (action == MOVE) return( ERROR ); } else { /* * to find out if cursor is in a STREAM block we have to do * a few more tests. if cursor is on the beginning row or * ending row, then check the beginning and ending column. */ if ((rline > br && rline < er) || (br == er && rcol >= bc && rcol <= ec) || (br != er && ((rline == br && rcol >= bc) || (rline == er && rcol <= ec)))) { /* * if the cursor is in middle of STREAM, make destination * the last character following the STREAM block. */ if (action == COPY || action == KOPY) { dest = block_end; rcol = ec + 1; rline = er; } else if (action == MOVE) return( ERROR ); } } } } } if (br < rline) source_first = TRUE; /* * 1. can't create lines greater than MAX_LINE_LENGTH * 2. if we are FILLing a BOX - fill block buff once right here * 3. only allow overlaying BOXs */ block_len = (ec+1) - bc; if (block_type == BOX) { if (action != DELETE && action != FILL) { if (rcol + block_len > MAX_LINE_LENGTH) { /* * line too long */ error( WARNING, prompt_line, ltol ); return( ERROR ); } } } else if (block_type == LINE) { block_len = 0; if (action == OVERLAY) { /* * can only overlay box blocks */ error( WARNING, prompt_line, block5 ); return( ERROR ); } } else if (block_type == STREAM) { if (action == OVERLAY) { /* * can only overlay box blocks */ error( WARNING, prompt_line, block5 ); return( ERROR ); } lend = block_end->len; if (action == DELETE || action == MOVE) { /* * Is what's left on start of STREAM block line plus what's left at * end of STREAM block line too long? */ if (lend > ec) lend -= ec; else lend = 0; if (bc + lend > MAX_LINE_LENGTH) { /* * line too long */ error( WARNING, prompt_line, ltol ); return( ERROR ); } } if (action != DELETE) { /* * We are doing a MOVE, COPY, or KOPY. Find out if what's on the * current line plus the start of the STREAM line are too long. * Then find out if end of the STREAM line plus what's left of * the current line are too long. */ lens = block_start->len; /* * if we had to move the destination of the STREAM COPY or KOPY * to the end of the STREAM block, then dest and window->ll->line * will not be the same. In this case, set length to length of * first line in STREAM block. Then we can add the residue of * the first line in block plus residue of the last line of block. */ if (dest->line == window->ll->line) add = dest->len; else add = lens; /* * Is current line plus start of STREAM block line too long? */ if (lens > bc) lens -= bc; else lens = 0; if (rcol + lens > MAX_LINE_LENGTH) { /* * line too long */ error( WARNING, prompt_line, ltol ); return( ERROR ); } /* * Is residue of current line plus residue of STREAM block line * too long? */ if (add > bc) add -= bc; else add = 0; if (lend > ec) lend -= ec; else lend = 0; if (add + lend > MAX_LINE_LENGTH) { /* * line too long */ error( WARNING, prompt_line, ltol ); return( ERROR ); } } if (ptoul( block_start ) == ptoul( block_end )) { block_type = BOX; block_len = (ec+1) - bc; } } if (mode.do_backups == TRUE) { switch (action) { case MOVE : case DELETE : case COPY : case KOPY : case SWAP : window->file_info->modified = TRUE; rc = backup_file( window ); break; } switch (action) { case MOVE : case DELETE : case FILL : case NUMBER : case SWAP : source_window->file_info->modified = TRUE; if (rc != ERROR) rc = backup_file( source_window ); break; } } source = block_start; assert( block_start != NULL ); assert( block_start->len != EOF ); assert( block_end != NULL ); assert( block_end->len != EOF ); if (block_type == LINE) do_line_block( window, source_window, action, source_file, dest_file, block_start, block_end, source, dest, br, er, &rc ); else if (block_type == STREAM) do_stream_block( window, source_window, action, source_file, dest_file, block_start, block_end, source, dest, rline, br, er, bc, ec, rcol, &rc ); else do_box_block( window, source_window, action, source_file, dest_file, source, dest, br, er, block_inc, rline, block_num, block_just, fill_char, same, block_len, bc, ec, rcol, &rc ); dest_file->modified = TRUE; dest_file->dirty = GLOBAL; if (action == MOVE || action == DELETE || action == FILL || action==NUMBER) { source_file->modified = TRUE; source_file->dirty = GLOBAL; } /* * unless we are doing a KOPY, FILL, NUMBER, or OVERLAY we need to unmark * the block. if we just did a KOPY, the beginning and ending may have * changed. so, we must readjust beginning and ending rows. */ if (action == KOPY) { if (same && !source_first && block_type == LINE && rc != ERROR) { number = (er+1) - br; source_file->block_br += number; source_file->block_er += number; } } else if (action != FILL && action != OVERLAY && action != NUMBER) unmark_block( window ); show_avail_mem( ); g_status.copied = FALSE; return( rc ); } /* * Name: do_line_block * Purpose: delete, move, copy, or kopy a LINE block * Date: April 1, 1993 * Passed: window: pointer to current window * Passed: window: pointer to destination window (current window) * source_window: pointer to source window * action: block action -- KOPY, MOVE, etc... * source_file: pointer to source file structure * dest_file: pointer to destination file * block_start: pointer to first node in block * block_end: pointer to last node in block * source: pointer to source node * dest: pointer to destination node * br: beginning line number in marked block * er: ending line number in marked block * rc: return code */ void do_line_block( TDE_WIN *window, TDE_WIN *source_window, int action, file_infos *source_file, file_infos *dest_file, line_list_ptr block_start, line_list_ptr block_end, line_list_ptr source, line_list_ptr dest, long br, long er, int *rc ) { line_list_ptr temp_ll; /* temporary list pointer */ text_ptr l; int lens; /* length of source line */ long li; /* temporary line variables */ long diff; if (action == COPY || action == KOPY) { assert( br >= 1 ); assert( br <= source_file->length ); assert( er >= br ); assert( er <= source_file->length ); for (li=br; li <= er && *rc == OK; li++) { lens = source->len; assert( lens * sizeof(char) < MAX_LINE_LENGTH ); l = (text_ptr)my_malloc( lens * sizeof(char), rc ); temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc ); if (*rc == OK) { if (lens > 0) my_memcpy( l, source->line, lens ); temp_ll->line = l; temp_ll->len = lens; temp_ll->dirty = TRUE; if (dest->next != NULL) { dest->next->prev = temp_ll; temp_ll->next = dest->next; dest->next = temp_ll; temp_ll->prev = dest; } else { temp_ll->next = dest; if (dest->prev != NULL) dest->prev->next = temp_ll; temp_ll->prev = dest->prev; dest->prev = temp_ll; if (temp_ll->prev == NULL) window->file_info->line_list = temp_ll; } dest = temp_ll; source = source->next; } else { /* * file too big */ error( WARNING, window->bottom_line, dir3 ); if (l != NULL) my_free( l ); if (temp_ll != NULL) my_free( temp_ll ); *rc = ERROR; er = li - 1; } } } else if (action == MOVE) { if (dest->len != EOF && dest->next != NULL) { temp_ll = block_start; for (li=br; li <= er && *rc == OK; li++) { temp_ll->dirty = TRUE; temp_ll = temp_ll->next; } if (block_start->prev == NULL) source_file->line_list = block_end->next; if (block_start->prev != NULL) block_start->prev->next = block_end->next; block_end->next->prev = block_start->prev; dest->next->prev = block_end; block_start->prev = dest; block_end->next = dest->next; dest->next = block_start; } } else if (action == DELETE) { block_end->next->prev = block_start->prev; if (block_start->prev == NULL) source_file->line_list = block_end->next; else block_start->prev->next = block_end->next; block_end->next = NULL; while (block_start != NULL) { temp_ll = block_start; block_start = block_start->next; if (temp_ll->line != NULL) my_free( temp_ll->line ); my_free( temp_ll ); } } diff = er + 1L - br; if (action == COPY || action == KOPY || action == MOVE) dest_file->length += diff; if (action == DELETE || action == MOVE) source_file->length -= diff; if (action == DELETE && source_window->rline >= br) { source_window->rline -= diff; if (source_window->rline < br) source_window->rline = br; } /* * restore all cursors in all windows */ restore_cursors( dest_file ); if (dest_file != source_file) restore_cursors( source_file ); show_avail_mem( ); } /* * Name: do_stream_block * Purpose: delete, move, copy, or kopy a STREAM block * Date: June 5, 1991 * Passed: window: pointer to destination window (current window) * source_window: pointer to source window * action: block action -- KOPY, MOVE, etc... * source_file: pointer to source file structure * dest_file: pointer to destination file * block_start: pointer to first node in block * block_end: pointer to last node in block * source: pointer to source node * dest: pointer to destination node * rline: current line number in destination file * br: beginning line number in marked block * er: ending line number in marked block * bc: beginning column of block * ec: ending column of block * rcol: current column of cursor * rc: return code */ void do_stream_block( TDE_WIN *window, TDE_WIN *source_window, int action, file_infos *source_file, file_infos *dest_file, line_list_ptr block_start, line_list_ptr block_end, line_list_ptr source, line_list_ptr dest, long rline, long br, long er, int bc, int ec, int rcol, int *rc ) { line_list_ptr temp_ll; /* temporary list pointer */ text_ptr l; int lens; /* length of source line */ int lend; /* length of destination line */ long li; /* temporary line variables */ long diff; TDE_WIN s_w, d_w; /* a couple of temporary TDE_WINs */ dup_window_info( &s_w, source_window ); dup_window_info( &d_w, window ); s_w.rline = br; s_w.ll = block_start; s_w.visible = FALSE; d_w.rline = rline; d_w.ll = dest; d_w.visible = FALSE; /* * pad the start of the STREAM block if needed. */ lens = block_start->len; detab_a_line( block_start->line, &lens ); if (lens < bc || mode.inflate_tabs) *rc = prepare_block( &s_w, block_start, bc ); /* * pad the end of the STREAM block if needed. */ lens = block_end->len; detab_a_line( block_end->line, &lens ); if (*rc == OK && (lens < ec+1 || mode.inflate_tabs)) *rc = prepare_block( &s_w, block_end, ec+1 ); /* * pad the destination line if necessary */ copy_line( dest ); detab_linebuff( ); *rc = un_copy_line( dest, &d_w, FALSE ); lend = dest->len; if (*rc == OK && (action==MOVE || action==COPY || action==KOPY)) { if (lend < rcol || mode.inflate_tabs) *rc = prepare_block( &d_w, dest, rcol ); } if ((action == COPY || action == KOPY) && *rc == OK) { /* * concatenate the end of the STREAM block with the end of the * destination line. */ lens = dest->len - rcol; assert( lens >= 0 ); assert( lens <= MAX_LINE_LENGTH ); assert( ec + 1 >= 0 ); assert( ec + 1 <= MAX_LINE_LENGTH ); assert( rcol >= 0 ); my_memcpy( g_status.line_buff, block_end->line, ec+1 ); my_memcpy( g_status.line_buff+ec+1, dest->line+rcol, lens ); lens += ec + 1; g_status.line_buff_len = lens; temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc ); if (*rc == OK) { temp_ll->line = NULL; temp_ll->len = 0; temp_ll->dirty = FALSE; g_status.copied = TRUE; *rc = un_copy_line( temp_ll, &d_w, TRUE ); if (*rc == OK) { dest->next->prev = temp_ll; temp_ll->next = dest->next; dest->next = temp_ll; temp_ll->prev = dest; } else if (temp_ll != NULL) my_free( temp_ll ); } else { if (temp_ll != NULL) my_free( temp_ll ); } /* * file too big */ if (*rc != OK) error( WARNING, window->bottom_line, dir3 ); if (*rc == OK) { g_status.copied = FALSE; copy_line( dest ); lens = block_start->len - bc; assert( lens >= 0 ); assert( lens <= MAX_LINE_LENGTH ); assert( bc >= 0 ); assert( bc <= MAX_LINE_LENGTH ); assert( rcol >= 0 ); my_memcpy( g_status.line_buff+rcol, block_start->line+bc, lens ); lens = rcol + lens; g_status.line_buff_len = lens; *rc = un_copy_line( dest, &d_w, TRUE ); } source = block_start->next; for (li=br+1; li < er && *rc == OK; li++) { lens = source->len; temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc ); assert( lens >= 0 ); assert( lens <= MAX_LINE_LENGTH ); l = (text_ptr)my_malloc( lens * sizeof(char), rc ); if (*rc == OK) { if (lens > 0) my_memcpy( l, source->line, lens ); temp_ll->line = l; temp_ll->len = lens; temp_ll->dirty = TRUE; if (dest->next != NULL) { dest->next->prev = temp_ll; temp_ll->next = dest->next; dest->next = temp_ll; temp_ll->prev = dest; } else { temp_ll->next = dest; if (dest->prev != NULL) dest->prev->next = temp_ll; temp_ll->prev = dest->prev; dest->prev = temp_ll; if (temp_ll->prev == NULL) window->file_info->line_list = temp_ll; } dest = temp_ll; source = source->next; } else { /* * file too big */ error( WARNING, window->bottom_line, dir3 ); if (l != NULL) my_free( l ); if (temp_ll != NULL) my_free( temp_ll ); *rc = WARNING; } } } else if (action == MOVE) { /* * is the dest on the same line as the block_start? */ if (ptoul( dest ) == ptoul( block_start )) { /* * move the text between rcol and bc in block_start->line * to block_end->line + ec. */ lens = bc - rcol; lend = block_end->len - (ec + 1); g_status.copied = FALSE; copy_line( block_end ); assert( lens >= 0 ); assert( lens <= MAX_LINE_LENGTH ); assert( lend >= 0 ); assert( lend <= MAX_LINE_LENGTH ); assert( ec + lens + 1 <= MAX_LINE_LENGTH ); assert( rcol >= 0 ); my_memmove( g_status.line_buff + ec + lens + 1, g_status.line_buff + ec + 1, lend ); my_memcpy( g_status.line_buff+ec+1, block_start->line+rcol, lens ); g_status.line_buff_len = block_end->len + lens; *rc = un_copy_line( block_end, &d_w, TRUE ); /* * now, remove the text between rcol and bc on block_start->line */ if (*rc == OK) { lend = block_start->len - bc; copy_line( block_start ); assert( lend >= 0 ); assert( lend < MAX_LINE_LENGTH ); my_memmove( g_status.line_buff + rcol, g_status.line_buff + bc, lend ); assert( block_start->len - (bc - rcol) >= 0 ); assert( block_start->len - (bc - rcol) <= MAX_LINE_LENGTH ); g_status.line_buff_len = block_start->len - (bc - rcol); *rc = un_copy_line( block_start, &d_w, TRUE ); } /* * is the dest on the same line as the block_end? */ } else if (ptoul( dest ) == ptoul( block_end )) { /* * move the text between rcol and ec on block_end->line to * block_start->line + bc. */ lens = rcol - ec; lend = block_start->len - bc; g_status.copied = FALSE; copy_line( block_start ); assert( lens >= 0 ); assert( lens <= MAX_LINE_LENGTH ); assert( lend >= 0 ); assert( lend <= MAX_LINE_LENGTH ); assert( bc + lens <= MAX_LINE_LENGTH ); assert( ec + 1 >= 0 ); my_memmove( g_status.line_buff + bc + lens, g_status.line_buff + bc, lend ); my_memcpy( g_status.line_buff+bc, block_end->line+ec+1, lens ); assert( block_start->len + lens >= 0 ); assert( block_start->len + lens <= MAX_LINE_LENGTH ); g_status.line_buff_len = block_start->len + lens; *rc = un_copy_line( block_start, &d_w, TRUE ); /* * now, remove the text on block_end->line between rcol and ec */ if (*rc == OK) { lend = block_end->len - (rcol + 1); copy_line( block_end ); assert( lend >= 0 ); assert( lend <= MAX_LINE_LENGTH ); assert( ec + 1 >= 0 ); assert( rcol + 1 >= 0 ); assert( ec + 1 <= MAX_LINE_LENGTH ); assert( rcol + 1 <= MAX_LINE_LENGTH ); assert( block_end->len - (rcol - ec) >= 0 ); assert( block_end->len - (rcol - ec) <= MAX_LINE_LENGTH ); my_memmove( g_status.line_buff + ec + 1, g_status.line_buff + rcol + 1, lend ); g_status.line_buff_len = block_end->len - (rcol - ec); *rc = un_copy_line( block_end, &d_w, TRUE ); } } else { lens = dest->len - rcol; assert( ec + 1 >= 0 ); assert( ec + 1 <= MAX_LINE_LENGTH ); assert( lens >= 0 ); assert( lens <= MAX_LINE_LENGTH ); assert( rcol >= 0 ); assert( rcol <= MAX_LINE_LENGTH ); my_memcpy( g_status.line_buff, block_end->line, ec+1 ); my_memcpy( g_status.line_buff+ec+1, dest->line+rcol, lens ); lens += ec + 1; g_status.line_buff_len = lens; temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc ); if (*rc == OK) { temp_ll->line = NULL; temp_ll->len = 0; temp_ll->dirty = FALSE; g_status.copied = TRUE; *rc = un_copy_line( temp_ll, &d_w, TRUE ); if (*rc != ERROR) { dest->next->prev = temp_ll; temp_ll->next = dest->next; dest->next = temp_ll; temp_ll->prev = dest; } else if (temp_ll != NULL) my_free( temp_ll ); } else { if (temp_ll != NULL) my_free( temp_ll ); } /* * file too big */ if (*rc != OK) error( WARNING, window->bottom_line, dir3 ); if (*rc == OK) { copy_line( dest ); lens = block_start->len - bc; assert( bc >= 0 ); assert( bc <= MAX_LINE_LENGTH ); assert( lens >= 0 ); assert( lens <= MAX_LINE_LENGTH ); assert( rcol >= 0 ); assert( rcol <= MAX_LINE_LENGTH ); my_memcpy( g_status.line_buff+rcol, block_start->line+bc, lens ); g_status.line_buff_len = lens + rcol; *rc = un_copy_line( dest, &d_w, TRUE ); dest->dirty = TRUE; } if (*rc == OK && ptoul( block_start->next ) != ptoul( block_end )) { block_start->next->prev = dest; temp_ll->prev = block_end->prev; block_end->prev->next = temp_ll; dest->next = block_start->next; } if (*rc == OK) { copy_line( block_start ); detab_linebuff( ); lend = bc; lens = block_end->len - (ec + 1); assert( bc >= 0 ); assert( bc <= MAX_LINE_LENGTH ); assert( lens >= 0 ); assert( lens <= MAX_LINE_LENGTH ); assert( lend >= 0 ); assert( lend <= MAX_LINE_LENGTH ); assert( ec + 1 >= 0 ); assert( ec + 1 <= MAX_LINE_LENGTH ); assert( lens + lend >= 0 ); assert( lens + lend <= MAX_LINE_LENGTH ); my_memcpy( g_status.line_buff+bc, block_end->line+ec+1, lens ); g_status.line_buff_len = lend + lens; *rc = un_copy_line( block_start, &s_w, TRUE ); block_start->dirty = TRUE; block_start->next = block_end->next; block_end->next->prev = block_start; if (block_end->line != NULL) my_free( block_end->line ); my_free( block_end ); } } } else if (action == DELETE) { copy_line( block_start ); lens = block_end->len - (ec + 1); assert( bc >= 0 ); assert( bc <= MAX_LINE_LENGTH ); assert( lens >= 0 ); assert( lens <= MAX_LINE_LENGTH ); assert( ec + 1 >= 0 ); assert( ec + 1 <= MAX_LINE_LENGTH ); assert( bc + lens >= 0 ); assert( bc + lens <= MAX_LINE_LENGTH ); my_memcpy( g_status.line_buff+bc, block_end->line + ec+1, lens ); g_status.line_buff_len = bc + lens; *rc = un_copy_line( block_start, &s_w, TRUE ); block_start->dirty = TRUE; source = block_start->next; block_start->next = block_end->next; block_end->next->prev = block_start; block_end->next = NULL; while (source != NULL) { temp_ll = source; source = source->next; if (temp_ll->line != NULL) my_free( temp_ll->line ); my_free( temp_ll ); } } if (*rc == OK) { diff = er - br; if (action == COPY || action == KOPY || action == MOVE) dest_file->length += diff; if (action == DELETE || action == MOVE) source_file->length -= diff; if (action == DELETE && source_window->rline >= br) { source_window->rline -= diff; if (source_window->rline < br) source_window->rline = br; } } /* * restore all cursors in all windows */ restore_cursors( dest_file ); if (dest_file != source_file) restore_cursors( source_file ); show_avail_mem( ); } /* * Name: do_box_block * Purpose: delete, move, copy, or kopy a BOX block * Date: June 5, 1991 * Passed: window: pointer to destination window (current window) * source_window: pointer to source window * action: block action -- OVERLAY, FILL, etc... * source_file: pointer to source file structure * dest_file: pointer to destination file * source: pointer to source node * dest: pointer to destination node * br: beginning line number in marked block * er: ending line number in marked block * block_inc: increment used to number a block * rline: current line number in destination file * block_num: starting number when numbering a block * block_just: LEFT or RIGHT justified numbers in block * fill_char: character to fill a block * same: are source and destination files same? T or F * block_len: width of box block * bc: beginning column of block * ec: ending column of block * rcol: current column of cursor * rc: return code */ void do_box_block( TDE_WIN *window, TDE_WIN *source_window, int action, file_infos *source_file, file_infos *dest_file, line_list_ptr source, line_list_ptr dest, long br, long er, long block_inc, long rline, long block_num, int block_just, int fill_char, int same, int block_len, int bc, int ec, int rcol, int *rc ) { line_list_ptr p; /* temporary list pointer */ int lens; /* length of source line */ int lend; /* length of destination line */ int add; /* characters being added from another line */ char *block_buff; char *swap_buff; int xbc, xec; /* temporary column variables */ long li; /* temporary line variables */ long dest_add; /* number of bytes added to destination file */ TDE_WIN s_w, d_w; /* a couple of temporary TDE_WINs for BOX stuff */ int padded_file; TDE_WIN *w; padded_file = FALSE; dup_window_info( &s_w, source_window ); dup_window_info( &d_w, window ); s_w.rline = br; s_w.ll = source; s_w.visible = FALSE; d_w.rline = rline; d_w.ll = dest; d_w.visible = FALSE; block_buff = (char *)calloc( BUFF_SIZE + 2, sizeof(char) ); swap_buff = (char *)calloc( BUFF_SIZE + 2, sizeof(char) ); if (block_buff == NULL || swap_buff == NULL) { error( WARNING, window->bottom_line, block4 ); *rc = ERROR; } /* * special case for block actions. since block actions always * move forward thru the file, overlapping text in an OVERLAY * action don't do right. make the operation start at the end * of the block and work backwards. */ if (*rc == OK && (action == OVERLAY || action == SWAP) && same && rline > br && rline <= er) { /* * see if we need to add padd lines at eof. */ dest_add = rline - br; if (dest_add + er > window->file_info->length) { dest_add = dest_add - (window->file_info->length - er); p = dest_file->line_list_end->prev; for (; dest_add > 0 && *rc == OK; dest_add--) *rc = pad_dest_line( window, dest_file, p ); padded_file = TRUE; } /* * move source and dest pointers to the end of the OVERLAY */ for (li=er-br; li > 0; li--) { load_undo_buffer( dest_file, dest->line, dest->len ); dest = dest->next; ++d_w.rline; source = source->next; ++s_w.rline; } /* * work backwards so the overlapped OVERLAY block don't use * overlayed text to fill the block. same for SWAPPing blocks. */ for (li=er; *rc == OK && li >= br && !g_status.control_break; li--, s_w.rline--, d_w.rline--) { lens = find_end( source->line, source->len ); lend = find_end( dest->line, dest->len ); if (lens != 0 || lend != 0) { load_box_buff( block_buff, source, bc, ec, ' ' ); if (action == SWAP) load_box_buff( swap_buff, dest, rcol, rcol+block_len, ' ' ); *rc = copy_buff_2file( &d_w, block_buff, dest, rcol, block_len, action ); dest->dirty = TRUE; if (action == SWAP) { add = 0; *rc = copy_buff_2file( &s_w, swap_buff, source, bc, block_len, action ); source->dirty = TRUE; } } source = source->prev; dest = dest->prev; } } else { if (action == FILL) block_fill( block_buff, fill_char, block_len ); for (li=br; *rc == OK && li <= er && !g_status.control_break; li++, s_w.rline++, d_w.rline++) { lens = find_end( source->line, source->len ); lend = find_end( dest->line, dest->len ); switch (action) { case FILL : case NUMBER : case DELETE : case MOVE : load_undo_buffer( source_file, source->line, source->len ); break; case COPY : case KOPY : case OVERLAY : load_undo_buffer( dest_file, dest->line, dest->len ); break; } /* * with FILL and NUMBER operations, we're just adding chars * to the file at the source location. we don't have to * worry about bookkeeping. */ if (action == FILL || action == NUMBER) { if (action == NUMBER) { number_block_buff( block_buff, block_len, block_num, block_just ); block_num += block_inc; } *rc = copy_buff_2file( &s_w, block_buff, source, rcol, block_len, action ); source->dirty = TRUE; /* * if we are doing a BOX action and both the source and * destination are 0 then we have nothing to do. */ } else if (lens != 0 || lend != 0) { /* * do actions that may require adding to file */ if (action == MOVE || action == COPY || action == KOPY || action == OVERLAY || action == SWAP) { xbc = bc; xec = ec; if (action != OVERLAY && action != SWAP && same) { if (rcol < bc && rline > br && rline <=er) if (li >= rline) { xbc = bc + block_len; xec = ec + block_len; } } load_box_buff( block_buff, source, xbc, xec, ' ' ); if (action == SWAP) load_box_buff( swap_buff, dest, rcol, rcol+block_len, ' ' ); *rc = copy_buff_2file( &d_w, block_buff, dest, rcol, block_len, action ); dest->dirty = TRUE; if (action == SWAP && *rc == OK) { *rc = copy_buff_2file( &s_w, swap_buff, source, xbc, block_len, action ); source->dirty = TRUE; } } /* * do actions that may require deleting from file */ if (action == MOVE || action == DELETE) { lens = find_end( source->line, source->len ); if (lens >= (bc + 1)) { source->dirty = TRUE; add = block_len; xbc = bc; if (lens <= (ec + 1)) add = lens - bc; if (same && action == MOVE) { if (rcol < bc && rline >= br && rline <=er) if (li >= rline) { xbc = bc + block_len; if (lens <= (ec + block_len + 1)) add = lens - xbc; } } if (add > 0) *rc = delete_box_block( &s_w, source, xbc, add ); } } } /* * if we are doing any BOX action we need to move the source pointer * to the next line. */ source = source->next; /* * if we are doing any action other than DELETE, we need to move * the destination to the next line in marked block. * In BOX mode, we may need to pad the end of the file * with a blank line before we process the next line. */ if (action != DELETE && action != FILL && action != NUMBER) { p = dest->next; if (p->len != EOF) dest = p; else if (li < er) { padded_file = TRUE; pad_dest_line( window, dest_file, p ); dest = dest->next; } } } } if (block_buff != NULL) free( block_buff ); if (swap_buff != NULL) free( swap_buff ); if (padded_file) { w = g_status.window_list; while (w != NULL) { if (w->file_info == dest_file && w->visible) show_size( w ); w = w->next; } } show_avail_mem( ); } /* * Name: load_box_buff * Class: helper function * Purpose: copy the contents of a BOX to a block buffer. * Date: June 5, 1991 * Passed: block_buff: local buffer for block moves * ll: node to source line in file to load * bc: beginning column of BOX. used only in BOX operations. * ec: ending column of BOX. used only in BOX operations. * filler: character to fill boxes that end past eol * Notes: For BOX blocks, there are several things to take care of: * 1) The BOX begins and ends within a line - just copy the blocked * characters to the block buff. 2) the BOX begins within a line * but ends past the eol - copy all the characters within the line * to the block buff then fill with padding. 3) the BOX begins and * ends past eol - fill entire block buff with padding (filler). * the fill character varies with the block operation. for sorting * a box block, the fill character is '\0'. for adding text to * the file, the fill character is a space. */ void load_box_buff( char *block_buff, line_list_ptr ll, int bc, int ec, char filler ) { int len; int avlen; register int i; register char *bb; text_ptr s; assert( bc >= 0 ); assert( ec >= bc ); assert( ec < MAX_LINE_LENGTH ); bb = block_buff; len = ll->len; s = detab_a_line( ll->line, &len ); /* * block start may be past eol */ if (len < ec + 1) { /* * does block start past eol? - fill with pad */ assert( ec + 1 - bc >= 0 ); memset( block_buff, filler, (ec + 1) - bc ); if (len >= bc) { /* * block ends past eol - fill with pad */ avlen = len - bc; s += bc; for (i=avlen; i>0; i--) *bb++ = *s++; } } else { /* * block is within line - copy block to buffer */ avlen = (ec + 1) - bc; s = s + bc; for (i=avlen; i>0; i--) *bb++ = *s++; } } /* * Name: copy_buff_2file * Class: helper function * Purpose: copy the contents of block buffer to destination file * Date: June 5, 1991 * Passed: window: pointer to current window * block_buff: local buffer for moves * dest: pointer to destination line in destination file * rcol: if in BOX mode, destination column in destination file * block_len: if in BOX mode, width of block to copy * action: type of block action * Notes: In BOX mode, the destination line has already been prepared. * Just copy the BOX buffer to the destination line. */ int copy_buff_2file( TDE_WIN *window, char *block_buff, line_list_ptr dest, int rcol, int block_len, int action ) { char *s; char *d; int len; int pad; int add; copy_line( dest ); if (mode.inflate_tabs) detab_linebuff( ); len = g_status.line_buff_len; assert( len >= 0 ); assert( len < MAX_LINE_LENGTH ); assert( rcol >= 0 ); assert( rcol < MAX_LINE_LENGTH ); assert( block_len >= 0 ); assert( block_len < BUFF_SIZE ); if (rcol > len) { pad = rcol - len; assert( pad >= 0 ); assert( pad < MAX_LINE_LENGTH ); memset( g_status.line_buff + len, ' ', pad ); len += pad; } s = g_status.line_buff + rcol; /* * s is pointing to location to perform BOX operation. If we do a * FILL or OVERLAY, we do not necessarily add any extra space. If the * line does not extend all the thru the BOX then we add. * we always add space when we COPY, KOPY, or MOVE */ if (action == FILL || action == OVERLAY || action == NUMBER || action == SWAP) { add = len - rcol; if (add < block_len) { pad = block_len - add; assert( pad >= 0 ); assert( pad < MAX_LINE_LENGTH ); memset( g_status.line_buff + len, ' ', pad ); len += pad; } } else { d = s + block_len; add = len - rcol; assert( add >= 0 ); assert( add < MAX_LINE_LENGTH ); memmove( d, s, add ); len += block_len; } assert( rcol + block_len <= len ); assert( len >= 0 ); assert( len < MAX_LINE_LENGTH ); memmove( s, block_buff, block_len ); g_status.line_buff_len = len; if (mode.inflate_tabs) entab_linebuff( ); return( un_copy_line( dest, window, TRUE ) ); } /* * Name: block_fill * Class: helper function * Purpose: fill the block buffer with character * Date: June 5, 1991 * Passed: block_buff: local buffer for moves * fill_char: fill character * block_len: number of columns in block * Notes: Fill block_buffer for block_len characters using fill_char. This * function is used only for BOX blocks. */ void block_fill( char *block_buff, int fill_char, int block_len ) { assert( block_len >= 0 ); assert( block_len < BUFF_SIZE ); assert( block_buff != NULL ); memset( block_buff, fill_char, block_len ); } /* * Name: number_block_buff * Class: helper function * Purpose: put a number into the block buffer * Date: June 5, 1991 * Passed: block_buff: local buffer for moves * block_len: number of columns in block * block_num: long number to fill block * just: LEFT or RIGHT justified? * Notes: Fill block_buffer for block_len characters with number. * This function is used only for BOX blocks. */ void number_block_buff( char *block_buff, int block_len, long block_num, int just ) { int len; /* length of number buffer */ int i; char temp[MAX_COLS+2]; assert( block_len >= 0 ); assert( block_len < BUFF_SIZE ); block_fill( block_buff, ' ', block_len ); len = strlen( my_ltoa( block_num, temp, 10 ) ); if (just == RIGHT) { block_len--; len--; for (;block_len >= 0 && len >= 0; block_len--, len--) block_buff[block_len] = temp[len]; } else { for (i=0; block_len > 0 && i < len; block_len--, i++) block_buff[i] = temp[i]; } } /* * Name: restore_cursors * Class: helper function * Purpose: a file has been modified - must restore all cursor pointers * Date: June 5, 1991 * Passed: file: pointer to file with changes * Notes: Go through the window list and adjust the cursor pointers * as needed. */ void restore_cursors( file_infos *file ) { register TDE_WIN *window; line_list_ptr ll; long n; assert( file != NULL ); window = g_status.window_list; while (window != NULL) { if (window->file_info == file) { window->bin_offset = 0; if (window->rline < 1L) window->rline = 1L; if (window->rline > file->length) window->rline = file->length; ll = file->line_list; n = 1L; for (; n < window->rline; n++) { window->bin_offset += ll->len; ll = ll->next; } window->ll = ll; if (window->rline < (window->cline - (window->top_line+window->ruler-1))) window->cline = (int)window->rline + window->top_line+window->ruler-1; if (window->cline < window->top_line + window->ruler) window->cline = window->top_line + window->ruler; if (window->visible) show_size( window ); } window = window->next; } } /* * Name: delete_box_block * Class: helper function * Purpose: delete the marked text * Date: June 5, 1991 * Passed: s_w: source window * source: pointer to line with block to delete * bc: beginning column of block - BOX mode only * add: number of characters in block to delete * Notes: Used only for BOX blocks. Delete the block. */ int delete_box_block( TDE_WIN *s_w, line_list_ptr source, int bc, int add ) { char *s; int number; assert( s_w != NULL ); assert( source != NULL ); assert( bc >= 0 ); assert( bc < MAX_LINE_LENGTH ); assert( add >= 0 ); assert( add < MAX_LINE_LENGTH ); copy_line( source ); detab_linebuff( ); number = g_status.line_buff_len - bc; s = g_status.line_buff + bc + add; assert( number >= 0 ); assert( number < MAX_LINE_LENGTH ); assert( bc + add >= 0 ); assert( bc + add < MAX_LINE_LENGTH ); assert( add <= g_status.line_buff_len ); memmove( s - add, s, number ); g_status.line_buff_len -= add; entab_linebuff( ); return( un_copy_line( source, s_w, TRUE ) ); } /* * Name: check_block * Class: helper function * Purpose: To check that the block is still valid. * Date: June 5, 1991 * Notes: After some editing, the marked block may not be valid. For example, * deleting all the lines in a block in another window. We don't * need to keep up with the block text pointers while doing normal * editing; however, we need to refresh them before doing block stuff. */ void check_block( void ) { register file_infos *file; TDE_WIN filler; file = g_status.marked_file; if (file == NULL || file->block_br > file->length) unmark_block( &filler ); else { if (file->block_er > file->length) file->block_er = file->length; find_begblock( file ); find_endblock( file ); } } /* * Name: find_begblock * Class: helper editor function * Purpose: find the beginning line in file with marked block * Date: June 5, 1991 * Passed: file: file containing marked block * Notes: file->block_start contains starting line of marked block. */ void find_begblock( file_infos *file ) { line_list_ptr ll; long li; /* line counter (long i) */ assert( file != NULL ); assert( file->line_list != NULL ); ll = file->line_list; for (li=1; liblock_br && ll->next != NULL; li++) ll = ll->next; file->block_start = ll; } /* * Name: find_endblock * Class: helper function * Purpose: find the ending line in file with marked block * Date: June 5, 1991 * Passed: file: file containing marked block * Notes: If in LINE mode, file->block_end is set to end of line of last * line in block. If in BOX mode, file->block_end is set to * beginning of last line in marked block. If the search for the * ending line of the marked block goes past the eof, set the * ending line of the block to the last line in the file. */ void find_endblock( file_infos *file ) { line_list_ptr ll; /* start from beginning of file and go to end */ long i; /* line counter */ register file_infos *fp; assert( file != NULL ); assert( file->block_start != NULL ); fp = file; ll = fp->block_start; if (ll != NULL) { for (i=fp->block_br; i < fp->block_er && ll->next != NULL; i++) ll = ll->next; if (ll != NULL) fp->block_end = ll; else { /* * last line in marked block is NULL. if LINE block, set end to * last character in the file. if STREAM or BOX block, set end to * start of last line in file. ending row, or er, is then set to * file length. */ fp->block_end = fp->line_list_end->prev; fp->block_er = fp->length; } } } /* * Name: block_write * Class: primary editor function * Purpose: To write the currently marked block to a disk file. * Date: June 5, 1991 * Passed: window: pointer to current window * Notes: If the file already exists, the user gets to choose whether * to overwrite or append. */ int block_write( TDE_WIN *window ) { int prompt_line; int rc; file_infos *file; int block_type; char temp[MAX_COLS+2]; #if defined( __UNIX__ ) chtype display_buff[MAX_COLS+2]; /* chtype is defined in curses.h */ mode_t fattr; #else char display_buff[(MAX_COLS+2)*2]; int fattr; #endif /* * make sure block is marked OK */ entab_linebuff( ); rc = un_copy_line( window->ll, window, TRUE ); check_block( ); if (rc == OK && g_status.marked == TRUE) { prompt_line = window->bottom_line; file = g_status.marked_file; assert( file != NULL ); block_type = file->block_type; /* * find out which file to write to */ save_screen_line( 0, prompt_line, display_buff ); *g_status.rw_name = '\0'; if (get_name( block6, prompt_line, g_status.rw_name, g_display.message_color ) == OK) { /* * if the file exists, find out whether to overwrite or append */ rc = get_fattr( g_status.rw_name, &fattr ); if (rc == OK) { /* * file exists. overwrite or append? */ set_prompt( block7, prompt_line ); switch (get_oa( )) { case A_OVERWRITE : change_mode( g_status.rw_name, prompt_line ); /* * writing block to */ combine_strings( temp, block8, g_status.rw_name, "'" ); s_output( temp, prompt_line, 0, g_display.message_color ); #if defined( __UNIX__ ) refresh( ); #endif rc = hw_save( g_status.rw_name, file, file->block_br, file->block_er, block_type ); if (rc == ERROR) /* * could not write block */ error( WARNING, prompt_line, block9 ); break; case A_APPEND : /* * appending block to */ combine_strings( temp, block10, g_status.rw_name, "'" ); s_output( temp, prompt_line, 0, g_display.message_color ); #if defined( __UNIX__ ) refresh( ); #endif rc = hw_append( g_status.rw_name, file, file->block_br, file->block_er, block_type ); if (rc == ERROR) /* * could not append block */ error( WARNING, prompt_line, block11 ); break; case AbortCommand : default : rc = ERROR; break; } } else if (rc != ERROR) { /* * writing block to */ combine_strings( temp, block12, g_status.rw_name, "'" ); s_output( temp, prompt_line, 0, g_display.message_color ); #if defined( __UNIX__ ) refresh( ); #endif if (hw_save( g_status.rw_name, file, file->block_br, file->block_er, block_type ) == ERROR) { /* * could not write block */ error( WARNING, prompt_line, block9 ); rc = ERROR; } } } restore_screen_line( 0, prompt_line, display_buff ); } else rc = ERROR; return( rc ); } /* * Name: block_print * Class: primary editor function * Purpose: Print an entire file or the currently marked block. * Date: June 5, 1991 * Modified: November 13, 1993, Frank Davis per Byrial Jensen * Passed: window: pointer to current window * Notes: With the added Critical Error Handler routine, let's fflush * the print buffer first. */ int block_print( TDE_WIN *window ) { int col; int func; int prompt_line; line_list_ptr block_start; /* start of block in file */ file_infos *file; int block_type; char *p; int len; int bc; int ec; int last_c; long lbegin; long lend; long l; int color; int rc; char answer[MAX_COLS+2]; #if defined( __UNIX__ ) chtype display_buff[MAX_COLS+2]; /* chtype is defined in curses.h */ #else char display_buff[(MAX_COLS+2)*2]; #endif color = g_display.message_color; entab_linebuff( ); if (un_copy_line( window->ll, window, TRUE ) == ERROR) return( ERROR ); rc = OK; prompt_line = window->bottom_line; save_screen_line( 0, prompt_line, display_buff ); /* * print entire file or just marked block? */ assert( strlen( block13 ) < (size_t)g_display.ncols ); strcpy( answer, block13 ); col = strlen( answer ); s_output( answer, prompt_line, 0, color ); eol_clear( col, prompt_line, g_display.text_color ); xygoto( col, prompt_line ); func = col = 0; while (col != L_FILE && col != L_BLOCK && func != AbortCommand) { col = getanswerkey( ); func = getfunc( col ); if (col == ESC || func == AbortCommand) rc = ERROR; } if (rc == OK) { /* * if everything is everything, flush the printer before we start * printing. then, check the critical error flag after the flush. */ fflush( my_stdprn ); if (ceh.flag == ERROR) rc = ERROR; } if (rc != ERROR) { file = window->file_info; block_type = NOTMARKED; lend = l = file->length; block_start = file->line_list; if (col == L_FILE) block_start = file->line_list; else { check_block( ); if (g_status.marked == TRUE) { file = g_status.marked_file; block_start = file->block_start; block_type = file->block_type; lend = l = file->block_er + 1l - file->block_br; } else rc = ERROR; } if (rc != ERROR) { eol_clear( 0, prompt_line, color ); /* * printing line of press control-break to cancel. */ s_output( block14, prompt_line, 0, color ); my_ltoa( l, answer, 10 ); s_output( answer, prompt_line, 25, color ); xygoto( BLOCK14_LINE_SLOT, prompt_line ); /* * bc and ec are used only in BOX and STREAM blocks. * last_c is last column in a BOX block */ bc = file->block_bc; ec = file->block_ec; last_c = ec + 1 - bc; p = g_status.line_buff; lbegin = 1; for (col=OK; l>0 && col == OK && !g_status.control_break; l--) { my_ltoa( lbegin, answer, 10 ); s_output( answer, prompt_line, BLOCK14_LINE_SLOT, color ); g_status.copied = FALSE; if (block_type == BOX) { load_box_buff( p, block_start, bc, ec, ' ' ); len = last_c; } else if (block_type == STREAM && lbegin == 1) { len = block_start->len; detab_a_line( block_start->line, &len ); if (bc > len) len = 0; else { if (lbegin == lend) { load_box_buff( p, block_start, bc, ec, ' ' ); len = last_c; } else { len = len - bc; g_status.copied = TRUE; assert( len >= 0 ); assert( len < MAX_LINE_LENGTH ); my_memcpy( p, block_start->line + bc, len ); } } } else if (block_type == STREAM && l == 1L) { copy_line( block_start ); detab_linebuff( ); len = g_status.line_buff_len; if (len > ec + 1) len = ec + 1; } else { copy_line( block_start ); len = g_status.line_buff_len; } assert( len >= 0 ); assert( len < MAX_LINE_LENGTH ); *(p+len) = '\r'; ++len; *(p+len) = '\n'; ++len; if (fwrite( p, sizeof( char ), len, my_stdprn ) < (unsigned)len || ceh.flag == ERROR) col = ERROR; block_start = block_start->next; ++lbegin; } g_status.copied = FALSE; if (ceh.flag != ERROR) fflush( my_stdprn ); else rc = ERROR; } } g_status.copied = FALSE; restore_screen_line( 0, prompt_line, display_buff ); return( rc ); } /* * Name: get_block_fill_char * Class: helper function * Purpose: get the character to fill marked block. * Date: June 5, 1991 * Passed: window: pointer to current window * c: address of character to fill block */ int get_block_fill_char( TDE_WIN *window, int *c ) { register int col; int prompt_line; int rc; char answer[MAX_COLS+2]; #if defined( __UNIX__ ) chtype display_buff[MAX_COLS+2]; /* chtype is defined in curses.h */ #else char display_buff[(MAX_COLS+2)*2]; #endif rc = OK; prompt_line = window->bottom_line; save_screen_line( 0, prompt_line, display_buff ); /* * enter character to fill block (esc to exit) */ assert( strlen( block15 ) < (size_t)g_display.ncols ); strcpy( answer, block15 ); s_output( answer, prompt_line, 0, g_display.message_color ); col = strlen( answer ); eol_clear( col, prompt_line, g_display.text_color ); xygoto( col, prompt_line ); col = getkey( ); if (col >= 256) rc = ERROR; else *c = col; restore_screen_line( 0, prompt_line, display_buff ); return( rc ); } /* * Name: get_block_numbers * Class: helper function * Purpose: get the starting number and increment * Date: June 5, 1991 * Passed: window: pointer to current window * block_num: address of number to start numbering * block_inc: address of number to add to block_num * just: left or right justify numbers in block? */ int get_block_numbers( TDE_WIN *window, long *block_num, long *block_inc, int *just ) { int prompt_line; register int rc; register int col; char answer[MAX_COLS+2]; #if defined( __UNIX__ ) chtype display_buff[MAX_COLS+2]; /* chtype is defined in curses.h */ #else char display_buff[(MAX_COLS+2)*2]; #endif prompt_line = window->bottom_line; /* * don't assume anything on starting number - start w/ null string. */ answer[0] = '\0'; /* * enter starting number */ rc = get_name( block16, prompt_line, answer, g_display.message_color ); if (answer[0] == '\0') rc = ERROR; if (rc != ERROR) { *block_num = atol( answer ); /* * assume increment is 1 */ answer[0] = '1'; answer[1] = '\0'; /* * enter increment */ rc = get_name( block17, prompt_line, answer, g_display.message_color ); if (answer[0] == '\0') rc = ERROR; if (rc != ERROR) { *block_inc = atol( answer ); /* * now, get left or right justification. save contents of screen * in a buffer, then write contents of buffer back to screen when * we get through w/ justification. */ save_screen_line( 0, prompt_line, display_buff ); /* * left or right justify (l/r)? */ assert( strlen( block18 ) < (size_t)g_display.ncols ); strcpy( answer, block18 ); s_output( answer, prompt_line, 0, g_display.message_color ); col = strlen( answer ); eol_clear( col, prompt_line, g_display.text_color ); xygoto( col, prompt_line ); rc = get_lr( ); if (rc != ERROR) { *just = rc; rc = OK; } restore_screen_line( 0, prompt_line, display_buff ); } } /* * if everything is everything then return code = OK. */ return( rc ); } /* * Name: block_trim_trailing * Class: primary editor function * Purpose: Trim trailing space in a LINE block. * Date: June 5, 1991 * Passed: window: pointer to current window * Notes: Use copy_line and un_copy_line to do the work. */ int block_trim_trailing( TDE_WIN *window ) { int prompt_line; int rc; line_list_ptr p; /* pointer to block line */ file_infos *file; TDE_WIN *sw, s_w; long er; int trailing; /* save trailing setting */ /* * make sure block is marked OK and that this is a LINE block */ prompt_line = window->bottom_line; entab_linebuff( ); rc = un_copy_line( window->ll, window, TRUE ); check_block( ); if (rc != ERROR && g_status.marked == TRUE) { trailing = mode.trailing; mode.trailing = TRUE; file = g_status.marked_file; if (file->block_type != LINE) { /* * can only trim trailing space in line blocks */ error( WARNING, prompt_line, block21 ); return( ERROR ); } /* * initialize everything */ sw = g_status.window_list; for (; ptoul( sw->file_info ) != ptoul( file );) sw = sw->next; if (mode.do_backups == TRUE) { file->modified = TRUE; rc = backup_file( sw ); } dup_window_info( &s_w, sw ); /* * set window to invisible so the un_copy_line function will * not display the lines while trimming. */ s_w.visible = FALSE; p = file->block_start; er = file->block_er; s_w.rline = file->block_br; for (; rc == OK && s_w.rline <= er && !g_status.control_break; s_w.rline++) { /* * use the line buffer to trim space. */ copy_line( p ); rc = un_copy_line( p, &s_w, TRUE ); p = p->next; } /* * IMPORTANT: we need to reset the copied flag because the cursor may * not necessarily be on the last line of the block. */ g_status.copied = FALSE; file->dirty = GLOBAL; mode.trailing = trailing; show_avail_mem( ); } return( rc ); } /* * Name: block_email_reply * Class: primary editor function * Purpose: insert the standard replay character '>' at beginning of line * Date: June 5, 1992 * Passed: window: pointer to current window * Notes: it is customary to prepend "> " to the initial text and * ">" to replies to replies to etc... */ int block_email_reply( TDE_WIN *window ) { int prompt_line; int add; int len; int rc; char *source; /* source for block move to make room for c */ char *dest; /* destination for block move */ line_list_ptr p; /* pointer to block line */ file_infos *file; TDE_WIN *sw, s_w; long er; /* * make sure block is marked OK and that this is a LINE block */ prompt_line = window->bottom_line; entab_linebuff( ); rc = un_copy_line( window->ll, window, TRUE ); check_block( ); if (rc != ERROR && g_status.marked == TRUE) { file = g_status.marked_file; if (file->block_type != LINE) { /* * can only reply line blocks */ error( WARNING, prompt_line, block25 ); return( ERROR ); } /* * find a window that points to the file with a marked block. */ sw = g_status.window_list; for (; ptoul( sw->file_info ) != ptoul( file );) sw = sw->next; if (mode.do_backups == TRUE) { file->modified = TRUE; rc = backup_file( sw ); } /* * use a local window structure to do the dirty work. initialize * the local window structure to the beginning of the marked * block. */ dup_window_info( &s_w, sw ); /* * set s_w to invisible so the un_copy_line function will * not display the lines while doing block stuff. */ s_w.visible = FALSE; s_w.rline = file->block_br; p = file->block_start; er = file->block_er; /* * for each line in the marked block, prepend the reply character(s) */ for (; rc == OK && s_w.rline <= er && !g_status.control_break; s_w.rline++) { /* * put the line in the g_status.line_buff. use add to count the * number of characters to insert at the beginning of a line. * the original reply uses "> ", while replies to replies use ">". */ copy_line( p ); if (*(p->line) == '>') add = 1; else add = 2; /* * see if the line has room to add the ">" character. if there is * room, move everything down to make room for the * reply character(s). */ len = g_status.line_buff_len; if (len + add < MAX_LINE_LENGTH) { source = g_status.line_buff; dest = source + add; assert( len >= 0 ); assert( len < MAX_LINE_LENGTH ); assert( len + add < MAX_LINE_LENGTH ); memmove( dest, source, len ); *source = '>'; if (add > 1) *(source+1) = ' '; g_status.line_buff_len = len + add; rc = un_copy_line( p, &s_w, TRUE ); } p = p->next; g_status.copied = FALSE; } /* * IMPORTANT: we need to reset the copied flag because the cursor may * not necessarily be on the last line of the block. */ g_status.copied = FALSE; file->dirty = GLOBAL; show_avail_mem( ); } return( OK ); } /* * Name: block_convert_case * Class: primary editor function * Purpose: convert characters to lower case, upper case, strip hi bits, * or e-mail functions * Date: June 5, 1991 * Passed: window: pointer to current window */ int block_convert_case( TDE_WIN *window ) { int len; int block_type; line_list_ptr begin; register file_infos *file; TDE_WIN *sw; long number; long er; unsigned int count; int bc, ec; int block_len; int rc; void (*char_func)( text_ptr, unsigned int ); /* * make sure block is marked OK */ entab_linebuff( ); if (un_copy_line( window->ll, window, TRUE ) == ERROR) return( ERROR ); rc = OK; check_block( ); if (g_status.marked == TRUE) { /* * set char_func() to the required block function in tdeasm.c */ switch (g_status.command) { case BlockUpperCase : char_func = upper_case; break; case BlockLowerCase : char_func = lower_case; break; case BlockRot13 : char_func = rot13; break; case BlockFixUUE : char_func = fix_uue; break; case BlockStripHiBit : char_func = strip_hi; break; default : return( ERROR ); } file = g_status.marked_file; file->modified = TRUE; if (mode.do_backups == TRUE) { sw = g_status.window_list; for (; ptoul( sw->file_info ) != ptoul( file );) sw = sw->next; rc = backup_file( sw ); } if (rc == OK) { block_type = file->block_type; ec = file->block_ec; begin = file->block_start; er = file->block_er; block_len = ec + 1 - file->block_bc; for (number=file->block_br; number <= er; number++) { begin->dirty = TRUE; count = len = begin->len; bc = 0; if (block_type == STREAM) { if (number == file->block_br) { bc = file->block_bc; if (len < file->block_bc) { count = 0; bc = len; } } if (number == file->block_er) { if (ec < len) ec = len; count = ec - bc + 1; } } else if (block_type == BOX) { bc = file->block_bc; count = len >= ec ? block_len : len - bc; } if (len > bc) { assert( count < MAX_LINE_LENGTH ); assert( bc >= 0 ); assert( bc < MAX_LINE_LENGTH ); (*char_func)( begin->line+bc, count ); } begin = begin->next; } /* * IMPORTANT: we need to reset the copied flag because the cursor may * not necessarily be on the last line of the block. */ g_status.copied = FALSE; file->dirty = GLOBAL; } } else rc = ERROR; return( rc ); } /* * Name: upper_case * Purpose: To convert all lower case characters to upper characters * Date: June 5, 1991 * Modified: November 13, 1993, Frank Davis per Byrial Jensen * Passed: s: the starting point * count: number of characters to convert * Returns: none * Notes: upper_lower[] is defined in bottom of prompts.h. modify as * needed for your favorite language. */ void upper_case( text_ptr s, size_t count ) { if (s != NULL) { for (; count > 0; s++, count-- ) { if (bj_islower( *s )) /* if lowercase letter */ *s = upper_lower[*s]; /* then upcase it */ } } } /* * Name: lower_case * Purpose: To convert all upper case characters to lower characters * Date: June 5, 1991 * Modified: November 13, 1993, Frank Davis per Byrial Jensen * Passed: s: the starting point * count: number of characters to convert * Returns: none * Notes: upper_lower[] is defined in bottom of prompts.h. modify as * needed for your favorite language. */ void lower_case( text_ptr s, size_t count ) { if (s != NULL) { for (; count > 0; s++, count-- ) { if (bj_isupper( *s )) /* if uppercase letter */ *s = upper_lower[*s]; /* then lowcase it */ } } } /* * Name: rot13 * Purpose: To rotate all alphabet characters by 13 * Date: June 5, 1991 * Passed: s: the starting point * count: number of characters to convert * Returns: none * Notes: simple rot13 * i really don't know how to handle rot13 for alphabets * other than English. */ void rot13( text_ptr s, size_t count ) { register size_t c; if (s != NULL) { for (; count > 0; s++, count-- ) { c = *s; if (c >= 'a' && c <= 'm') c += 13; else if (c >= 'n' && c <= 'z') c -= 13; else if (c >= 'A' && c <= 'M') c += 13; else if (c >= 'N' && c <= 'Z') c -= 13; *s = (unsigned char)c; } } } /* * Name: fix_uue * Purpose: To fix EBCDIC ==> ASCII translation problem * Date: June 5, 1991 * Passed: s: the starting point * count: number of characters to convert * Returns: none * Notes: to fix the EBCDIC to ASCII translation problem, three characters * need to be changed, 0x5d -> 0x7c, 0xd5 -> 0x5b, 0xe5 -> 0x5d */ void fix_uue( text_ptr s, size_t count ) { if (s != NULL) { for (; count > 0; s++, count-- ) { switch (*s) { case 0x5d : *s = 0x7c; break; case 0xd5 : *s = 0x5b; break; case 0xe5 : *s = 0x5d; break; default : break; } } } } /* * Name: strip_hi * Purpose: To strip bit 7 from characters * Date: June 5, 1991 * Passed: s: the starting point, which should be normalized to a segment * count: number of characters to strip (size_t) * count should not be greater than MAX_LINE_LENGTH * Returns: none * Notes: this function is useful on WordStar files. to make a WordStar * file readable, the hi bits need to be anded off. * incidentally, soft CRs and LFs may be converted to hard CRs * and LFs. this function makes no attempt to split lines * when soft returns are converted to hard returns. */ void strip_hi( text_ptr s, size_t count ) { if (s != NULL) { for (; count > 0; s++, count-- ) { if (*s >= 0x80) *s &= 0x7f; } } }