/******************* 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 ********************/ /* * 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. One disadvantage of not using buffers is that block * operations can be slow. The most complicated routine in this editor is by * far "move_copy_delete_overlay_block( window )". I put some comments in, * but it is still a bitch. Come to think of it, most of these block functions * are a bitch. * * Maybe in the next version I'll use buffers to speed up block operations. * * In tde, version 1.1, I separated the BOX and LINE actions. LINE actions * are a LOT faster, now. Still need to speed up BOX 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. * * 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 * * 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 * 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( WINDOW *window ) { int type; int num; long lnum; register file_infos *file; /* temporary file variable */ register WINDOW *win; /* put window pointer in a register */ int rc; win = window; file = win->file_info; if (win->rline > file->length) 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; 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; } } } file->block_type = type; file->dirty = GLOBAL; } else { /* * block already defined */ error( WARNING, win->bottom_line, block1 ); rc = ERROR; } return( rc ); } /* * Name: unmark_block * 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 block is unmarked then redraw the screen(s). */ int unmark_block( WINDOW *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 * 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( WINDOW *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 * 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. */ int prepare_block( WINDOW *window, text_ptr text_line, int lend, int bc ) { register int pad; /* amount of padding to be added */ register char *source; /* source for block moves */ copy_line( text_line, window->bottom_line ); /* * work out how much padding is required to extend the current * line to the cursor position */ pad = bc - lend; /* * make room for the padding spaces */ source = g_status.line_buff + lend; memmove( source+pad, source, pad+2 ); /* * insert the padding spaces */ memset( source, ' ', pad ); un_copy_line( text_line, window, FALSE ); return( pad ); } /* * Name: pad_dest_line * 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. * 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. */ void pad_dest_line( WINDOW *window, file_infos *dest_file, text_ptr dest_line) { /* * put linefeed in line_buff. dest_line should be pointing to * file->end_text - 1. since we inserted line feed, increment file length. */ g_status.line_buff[0] = '\n'; g_status.line_buff[1] = CONTROL_Z; g_status.copied = TRUE; un_copy_line( dest_line, window, FALSE ); ++dest_file->length; } /* * Name: move_copy_delete_overlay_block * 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. * The big differences are whether to delete the source block, copy * the source block, or leave the source block marked. * 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. There are several caveats. Most deal with the * difference between LINE and BOX operations others deal with * differences between operations within a file and operations * across files. * This is probably the most complicated routine in the editor. It * is not easy to understand. */ int move_copy_delete_overlay_block( WINDOW *window ) { int action; WINDOW *source_window; /* source window for block moves */ text_ptr source; /* source for block moves */ text_ptr dest; /* destination for block moves */ text_ptr p; /* temporary text pointer */ 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 */ text_ptr block_start; /* start of block in file */ text_ptr block_end; /* end of block in file - not same for LINE or BOX */ char block_buff[BUFF_SIZE+2]; int prompt_line; int same; /* are these files the same */ int source_first; /* is source file lower in memory than dest */ file_infos *source_file, *dest_file; int rcol, bc, ec; /* temporary column variables */ int xbc, xec; /* temporary column variables */ long rline; /* temporary real line variable */ long br, er, li; /* temporary line variables */ long dest_add; /* number of bytes added to destination file */ long source_sub; /* number of bytes sub from source file */ long diff; 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? */ unsigned long block_size; int block_type; int fill_char; WINDOW s_w, d_w; /* a couple of temporary WINDOWs for BOX stuff */ int padded_file; WINDOW *w; /* * initialize block variables */ un_copy_line( window->cursor, window, TRUE ); if (g_status.marked == FALSE) 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; } 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; block_type = source_file->block_type; dest = window->cursor = cpf( window->cursor ); rline = window->rline; /* * 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; dest_add = source_sub = 0; /* * if this is a LINE action, put the text below the current line */ if (block_type == LINE && action != DELETE) if ((p = find_next( dest )) != NULL) dest = p; /* * 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. */ 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 ); } } 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, block3 ); 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 = cpf( 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) || (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 = cpf( block_end ); rcol = ec + 1; rline = er; } else if (action == MOVE) return( ERROR ); } } } } } /* * must know if source of block is before or after destination */ source_first = FALSE; if (ptoul( dest ) > ptoul( source_file->block_start )) source_first = TRUE; if (same && block_type == BOX) { if ( rline >= br) source_first = TRUE; } /* * work out how much has to be moved */ if (block_type == BOX) { block_size = ((ec+1) - bc) * ((er+1) - br); if (action != DELETE) block_size += ((rcol+1) * ((er+1) - br)); else block_size = 0; } else if (block_type == LINE || block_type == STREAM) { if (action == COPY || action == KOPY) block_size = ptoul( block_end ) - ptoul( block_start ); else block_size = 0; } else return( ERROR ); /* * check that there is room to add block to file */ if (ptoul( g_status.end_mem ) + block_size >= ptoul( g_status.max_mem )) { /* * not enough memory for block. */ error( WARNING, prompt_line, block4 ); return( ERROR ); } /* * set the command to word wrap so the un_copy_line function will * not display the lines while doing block stuff. */ g_status.command = WordWrap; /* * 1. can't create lines greater than g_display.line_length * 2. if we are FILLing a BOX - fill block buff once right here * 3. only allow overlaying BOXs */ if (block_type == BOX) { block_len = (ec+1) - bc; if (action != DELETE && action != FILL) { if (rcol + block_len > MAX_LINE_LENGTH) { /* * line too long */ error( WARNING, prompt_line, ltol ); return( ERROR ); } } else if (action == FILL) block_fill( block_buff, fill_char, block_len ); } 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) { lend = linelen( block_end ); 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 = linelen( block_start ); /* * if we had to move the destination of the STREAM COPY or KOPY * to the end of the STREAM block, then dest and window->cursor * 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 (ptoul( dest ) == ptoul( window->cursor )) add = linelen( dest ); 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 ); } } } /* * all block actions go forward thru file - check those pointers */ source = cpf( block_start ); dest = cpf( dest ); if (block_type == LINE || block_type == STREAM) { if (block_type == STREAM) { dup_window_info( &s_w, source_window ); dup_window_info( &d_w, window ); s_w.rline = br; d_w.rline = rline; /* * pad the start of the STREAM block if needed. */ lens = linelen( block_start ); if (lens < bc+1) { add = prepare_block( &s_w, block_start, lens, bc ); if (br != er) block_end += add; if (source_first) dest += add; } block_start += bc; source = cpf( block_start ); /* * pad the end of the STREAM block if needed. */ lens = linelen( block_end ); if (lens < ec+1) { add = prepare_block( &s_w, block_end, lens, ec+1 ); if (source_first) dest += add; } block_end += ec + 1; /* * pad the destination line if necessary */ lend = linelen( dest ); if (action==MOVE || action==COPY || action==KOPY) { if (lend < rcol+1) { add = prepare_block( &d_w, dest, lend, rcol ); if (!source_first) { source += add; block_start += add; block_end += add; } } dest += rcol; } } diff = ptoul( block_end ) - ptoul( block_start ); dest_add = source_sub = diff; if (action != DELETE) { p = addltop( diff, dest ); number = ptoul( g_status.end_mem ) - ptoul( dest ); hw_move( p, dest, number ); g_status.end_mem = addltop( diff, g_status.end_mem); } if (action != DELETE && !source_first) source = addltop( diff, source ); if (action == COPY || action == KOPY || action == MOVE) hw_move( dest, source, diff ); if (action == DELETE || action == MOVE) { p = addltop( diff, source ); number = ptoul( g_status.end_mem ) - ptoul( p ); hw_move( source, p, number ); g_status.end_mem = addltop( -diff, g_status.end_mem); } if (action == DELETE) dest_add = 0; else if (action == COPY || action == KOPY) source_sub = 0; diff = block_type == LINE ? (er+1l) - br : 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; } /* * the block action is now complete. restore all the start_text and * end_text pointers for all open files. */ if (action == MOVE || action == DELETE) restore_start_end( dest_file, source_file, dest_add, -source_sub, source_first ); else restore_start_end( dest_file, source_file, dest_add, source_sub, source_first ); /* * restore all cursors in all windows */ restore_cursors( dest_file, source_file ); } else { padded_file = FALSE; dup_window_info( &s_w, source_window ); dup_window_info( &d_w, window ); s_w.rline = br; /* * 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 (action == OVERLAY && 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); for (; dest_add > 0; dest_add--) { p = addltop( -1, dest_file->end_text ); pad_dest_line( window, dest_file, p ); } padded_file = TRUE; dup_window_info( &s_w, source_window ); dup_window_info( &d_w, window ); } /* * move source and dest pointers to the end of the OVERLAY */ for (li=er-br; li > 0; li--) { load_undo_buffer( dest ); dest = find_next( dest ); ++d_w.rline; source = find_next( source ); ++s_w.rline; } /* * work backwards so the overlapped OVERLAY block don't use * overlayed text to fill the block. */ source = cpb( source ); dest = cpb( dest ); for (li=er; li >= br && !g_status.control_break; li--, s_w.rline--, d_w.rline--) { lens = linelen( source ); lend = linelen( dest ); if (lens != 0 || lend != 0) { d_w.cursor = dest; load_box_buff( block_buff, cpf( source ), bc, ec, ' ' ); if (lend < (rcol+1)) prepare_block( &d_w, cpf( dest ), lend, rcol ); copy_buff_2file( &d_w, block_buff, cpf( dest ), rcol, block_len, OVERLAY ); } source = find_prev( source ); dest = find_prev( dest ); } } else { for (li=br; li<=er && !g_status.control_break; li++, s_w.rline++, d_w.rline++) { lens = linelen( source ); lend = linelen( dest ); switch (action) { case FILL : case NUMBER : case DELETE : case MOVE : load_undo_buffer( source ); break; case COPY : case KOPY : case OVERLAY : load_undo_buffer( dest ); break; } if (action == FILL || action == NUMBER) { s_w.cursor = source; add = 0; if (lens < (rcol+1)) add = prepare_block( &s_w, source, lens, rcol ); if (action == NUMBER) { number_block_buff( block_buff, block_len, block_num, block_just ); block_num += block_inc; } add += copy_buff_2file( &s_w, block_buff, source, rcol, block_len, action ); /* * 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) { d_w.cursor = dest; xbc = bc; xec = ec; if (action != OVERLAY && 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, ' ' ); add = 0; if (lend < (rcol+1)) add = prepare_block( &d_w, dest, lend, rcol ); add += copy_buff_2file( &d_w, block_buff, dest, rcol, block_len, action ); if (!source_first) source += add; } /* * do actions that may require deleting from file */ if (action == MOVE || action == DELETE) { s_w.cursor = source; if (lens >= (bc + 1)) { if (same && action == MOVE) lens = linelen( source ); 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; } } delete_box_block( &s_w, source, xbc, add, prompt_line ); if (action == MOVE && source_first) { if (!same || s_w.rline != d_w.rline) { dest = addltop( -add, dest ); dest = cpf( dest ); } } } } } /* * if we are doing any BOX action we need to move the source pointer * to the next line. */ source = find_next( source ); /* * 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 = find_next( dest ); if (p != NULL && *p != CONTROL_Z) dest = p; else { padded_file = TRUE; p = addltop( -1, dest_file->end_text ); pad_dest_line( window, dest_file, p ); dest = find_next( dest ); if (!source_first) ++source; } } } } 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; } } } 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) { number = (er+1) - br; source_file->block_br += number; source_file->block_er += number; } else if (same && !source_first && window->rline == br && block_type == BOX) { add = (ec+1) - bc; source_file->block_bc += add; source_file->block_ec += add; } } else if (action != FILL && action != OVERLAY && action != NUMBER) unmark_block( window ); show_avail_mem( ); g_status.copied = FALSE; return( OK ); } /* * Name: load_box_buff * Purpose: copy the contents of a BOX to a block buffer. * Date: June 5, 1991 * Passed: block_buff: local buffer for block moves * source: source line in file * 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, text_ptr source, int bc, int ec, char filler ) { int len, pad, avlen; register int i; register char *bb; bb = block_buff; len = linelen( source ); /* * block start may be past eol */ if (len < ec + 1) { /* * does block start past eol? - fill with pad */ if (len < bc) { pad = (ec + 1) - bc; for (i=pad; i>0; i--) *bb++ = filler; } else { /* * block ends past eol - fill with pad */ pad = (ec + 1) - len; avlen = len - bc; source = source + bc; for (i=avlen; i>0; i--) *bb++ = *source++; for (i=pad; i>0; i--) *bb++ = filler; } } else { /* * block is within line - copy block to buffer */ avlen = (ec + 1) - bc; source = source + bc; for (i=avlen; i>0; i--) *bb++ = *source++; } *bb++ = CONTROL_Z; *bb = '\0'; } /* * Name: copy_buff_2file * 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( WINDOW *window, char *block_buff, text_ptr dest, int rcol, int block_len, int action ) { register char *s; char *d; int i; int rc; rc = 0; copy_line( dest, window->bottom_line ); 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) { i = linelen( s ); if (i < block_len) { rc = block_len - i; d = s + rc; i = block_len + 1 + linelen( g_status.line_buff ) - rcol; memmove( d, s, i ); } } else { rc = block_len; d = s + block_len; i = block_len + 1 + linelen( g_status.line_buff ) - rcol; memmove( d, s, i ); } memmove( s, block_buff, block_len ); un_copy_line( dest, window, FALSE ); return( rc ); } /* * Name: block_fill * 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 ) { memset( block_buff, fill_char, block_len ); *(block_buff+block_len) = CONTROL_Z; } /* * Name: number_block_buff * 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]; /* buffer for long number to ascii conversion */ block_fill( block_buff, ' ', block_len ); len = strlen( 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_start_end * Purpose: a file has been modified - must restore all start and end pointers * Date: June 5, 1991 * Passed: dest_file: pointer to destination file structure * source_file: pointer to source file structure * dest_mod: net modifications in the destination file * source_mod: net modifications in the source file * source_first: we must know which file is stored first in memory * Notes: Go through the file list and adjust the start_text and end_text * file pointers as needed. There are several cases that must be * be considered. 1) destination file and source file could be the * same. 2) if the file pointer we're looking at is below both * the source and destination, no action is needed. 3) the file * we're looking at could be between the source and destination. * 4) the file we're looking at could be either source or destination. * 5) the file we're looking at could be past both source and dest. * Use unsigned longs to compare pointers. */ void restore_start_end( file_infos *df, file_infos *source_file, long dest_mod, long source_mod, int source_first ) { int same; long net_mod; unsigned long sst; /* source start_text - keep these around for if's */ unsigned long dst; /* destination start_text */ unsigned long ost; /* open_file start_text */ register file_infos *open_file; register file_infos *dest_file; dest_file = df; net_mod = dest_mod + source_mod; sst = ptoul( source_file->start_text ); dst = ptoul( dest_file->start_text ); same = sst == dst ? TRUE : FALSE; for (open_file=g_status.file_list; open_file != NULL; open_file=open_file->next) { sst = ptoul( source_file->start_text ); dst = ptoul( dest_file->start_text ); ost = ptoul( open_file->start_text ); if (ost == sst) { if (same) source_file->end_text = addltop( net_mod, source_file->end_text); else if (source_first) source_file->end_text = addltop( source_mod, source_file->end_text); else { source_file->start_text = addltop( dest_mod, source_file->start_text); source_file->end_text = addltop( net_mod, source_file->end_text); } } else if (ost == dst) { if (source_first) { dest_file->start_text = addltop( source_mod, dest_file->start_text); dest_file->end_text = addltop( net_mod, dest_file->end_text); } else dest_file->end_text = addltop( dest_mod, dest_file->end_text); } else if (ost > sst) { if (ost < dst) { open_file->start_text = addltop( source_mod, open_file->start_text); open_file->end_text = addltop( source_mod, open_file->end_text); } else { open_file->start_text = addltop( net_mod, open_file->start_text); open_file->end_text = addltop( net_mod, open_file->end_text); } } else if (ost > dst) { if (ost < sst) { open_file->start_text = addltop( dest_mod, open_file->start_text); open_file->end_text = addltop( dest_mod, open_file->end_text); } else { open_file->start_text = addltop( net_mod, open_file->start_text); open_file->end_text = addltop( net_mod, open_file->end_text); } } } } /* * Name: restore_cursors * Purpose: a file has been modified - must restore all cursor pointers * Date: June 5, 1991 * Passed: dest_file: target file for block actions * source_file: source file for block actions * Notes: Go through the window list and adjust the cursor pointers * as needed. This could be done by using the changes made by * the block actions, but it would be a real pain in the neck. * I chose to use the brute force approach. */ void restore_cursors( file_infos *dest_file, file_infos *source_file ) { register WINDOW *window; register file_infos *file; text_ptr p; long beg_line, cur_line, test_line; unsigned long df, sf, f; df = ptoul( (text_ptr)dest_file ); sf = ptoul( (text_ptr)source_file ); window = g_status.window_list; while (window != NULL) { file = window->file_info; f = ptoul( (text_ptr)file ); beg_line = 1; cur_line = window->rline; if (cur_line > file->length) { file->end_text = cpb( file->end_text ); p = find_prev( file->end_text-1 ); window->cursor = p != NULL ? p : file->start_text; window->rline = file->length; test_line = cur_line - file->length; if (test_line<(long)(window->cline-(window->top_line+window->ruler-1))) window->cline -= test_line; } else { file->start_text = cpf( file->start_text ); for (p=file->start_text; p!=NULL && beg_linecursor = p; else { window->cursor = file->start_text; cur_line = file->length; } window->rline = cur_line; } if (window->rline <= 0l) window->rline = 1l; 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 ((f == df || f == sf) && window->visible ) show_size( window ); window = window->next; } } /* * Name: delete_box_block * 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 * prompt_line: line to display error message if needed * Notes: Used only for BOX blocks. Delete the block. */ void delete_box_block( WINDOW *s_w, text_ptr source, int bc, int add, int prompt_line ) { char *s; int number; number = linelen( source ) - bc + 2; copy_line( source, prompt_line ); s = g_status.line_buff + bc + add; memmove( s - add, s, number ); un_copy_line( source, s_w, FALSE ); } /* * Name: check_block * 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; WINDOW filler; file = g_status.marked_file; if (file == NULL || file->block_br > file->length) unmark_block( &filler ); else { if (file->length < file->block_er) file->block_er = file->length; find_begblock( file ); find_endblock( file ); } } /* * Name: find_begblock * 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 ) { text_ptr next; /* start from beginning of file and go to end */ long i; /* line counter */ next = cpf( file->start_text ); for (i=1; iblock_br && next != NULL; i++) next = find_next( next ); if (next != NULL) file->block_start = next; } /* * Name: find_endblock * 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 ) { text_ptr next; /* start from beginning of file and go to end */ long i; /* line counter */ int end_column; register file_infos *fp; fp = file; next = cpf( fp->start_text ); for (i=1; iblock_er && next != NULL; i++) next = find_next( next ); if (next != NULL) { end_column = linelen( next ); if (next[end_column] == '\n') ++end_column; /* * if LINE block somewhere in the file, set block_end to first * line past end of marked block. */ fp->block_end = fp->block_type == LINE ? next + end_column : next; } 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->end_text = cpb( fp->end_text ); if (fp->block_type == LINE) fp->block_end = fp->end_text - 1; else { next = find_prev( fp->end_text - 1 ); fp->block_end = next != NULL ? next : fp->end_text - 1; } fp->block_er = fp->length; } } /* * Name: block_write * 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( WINDOW *window ) { int prompt_line; int rc; char buff[MAX_COLS+2]; /* buffer for char and attribute */ char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute */ text_ptr block_start; /* start of block in file */ text_ptr block_end; /* end of block in file */ file_infos *file; int block_type; int fattr; /* * make sure block is marked OK */ rc = ERROR; un_copy_line( window->cursor, window, TRUE ); check_block( ); if (g_status.marked == TRUE) { prompt_line = window->bottom_line; file = g_status.marked_file; block_start = file->block_start; block_end = file->block_end; block_type = file->block_type; /* * find out which file to write to */ save_screen_line( 0, prompt_line, line_buff ); 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( buff, block8, g_status.rw_name, "'" ); s_output( buff, prompt_line, 0, g_display.message_color ); rc = hw_save( g_status.rw_name, block_start, block_end, block_type ); if (rc == ERROR) /* * could not write block */ error( WARNING, prompt_line, block9 ); break; case A_APPEND : /* * appending block to */ combine_strings( buff, block10, g_status.rw_name, "'" ); s_output( buff, prompt_line, 0, g_display.message_color ); rc = hw_append( g_status.rw_name, block_start, block_end, block_type ); if (rc == ERROR) /* * could not append block */ error( WARNING, prompt_line, block11 ); break; case AbortCommand : rc = ERROR; break; } } else if (rc != ERROR) { /* * writing block to */ combine_strings( buff, block12, g_status.rw_name, "'" ); s_output( buff, prompt_line, 0, g_display.message_color ); if (hw_save( g_status.rw_name, block_start, block_end, block_type ) == ERROR) { /* * could not write block */ error( WARNING, prompt_line, block9 ); rc = ERROR; } } } restore_screen_line( 0, prompt_line, line_buff ); } else rc = ERROR; return( rc ); } /* * Name: block_print * Purpose: Print an entire file or the currently marked block. * Date: June 5, 1991 * Passed: window: pointer to current window * Notes: With the added Critical Error Handler routine, let's fflush * the print buffer first. */ int block_print( WINDOW *window ) { char answer[MAX_COLS]; /* entire file or just marked block? */ char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute */ int col, func; int prompt_line; text_ptr block_start; /* start of block in file */ text_ptr block_end; /* end of block in file */ file_infos *file; int block_type; char *p; int len; int bc, ec, last_c; unsigned long lbegin, lend; long l; int color; int rc; rc = OK; color = g_display.message_color; un_copy_line( window->cursor, window, TRUE ); prompt_line = window->bottom_line; save_screen_line( 0, prompt_line, line_buff ); /* * print entire file or just marked block? */ 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 != 'f' && col != 'F' && col != 'b' && col != 'B' && func != AbortCommand) { col = getkey( ); func = getfunc( col ); if (col == ESC) { func = AbortCommand; rc = ERROR; } } fflush( stdprn ); if (ceh.flag == ERROR) { func = AbortCommand; rc = ERROR; } if (func != AbortCommand) { file = window->file_info; if (col == 'f' || col == 'F') { block_start = file->start_text; block_end = cpb( file->end_text ) - 1; block_type = NOTMARKED; l = file->length; } else if (col == 'b' || col == 'B') { check_block( ); if (g_status.marked == TRUE) { file = g_status.marked_file; block_start = file->block_start; block_end = file->block_end; block_type = file->block_type; l = file->block_er + 1l - file->block_br; } else { col = AbortCommand; rc = ERROR; } } if (block_type == LINE) { block_end = cpb( block_end ); block_end = find_prev( block_end ); } if (col != AbortCommand) { eol_clear( 0, prompt_line, color ); /* * printing line of press control-break to cancel. */ s_output( block14, prompt_line, 0, color ); ltoa( l, answer, 10 ); s_output( answer, prompt_line, 25, color ); xygoto( 14, prompt_line ); block_start = cpf( block_start ); if (block_type == BOX || block_type == STREAM) { bc = file->block_bc; ec = file->block_ec; last_c = ec + 1 - bc; } p = g_status.line_buff; lend = ptoul( block_end ); for (l=1,col=OK; ptoul( block_start ) <= lend && col == OK && !g_status.control_break; l++) { ltoa( l, answer, 10 ); s_output( answer, prompt_line, 14, color ); g_status.copied = FALSE; if (block_type == BOX) { load_box_buff( p, block_start, bc, ec, ' ' ); *(p+last_c) = '\n'; *(p+last_c+1) = CONTROL_Z; } else if (block_type == STREAM && l == 1) { len = linelen( block_start ); lbegin = ptoul( block_start ); block_start += bc < len ? bc : len; copy_line( block_start, prompt_line ); if (lbegin == lend) { if (len > ec) { *(p+last_c) = '\n'; *(p+last_c+1) = CONTROL_Z; } } } else if (block_type == STREAM && ptoul( block_start )==lend) { copy_line( block_start, prompt_line ); if (linelen( block_start ) > (unsigned)ec) { *(p+ec+1) = '\n'; *(p+ec+2) = CONTROL_Z; } } else copy_line( block_start, prompt_line ); len = find_CONTROL_Z( p ); if (fwrite( p, sizeof( char ), len, stdprn ) < (unsigned)len || ceh.flag == ERROR) col = ERROR; if (col != ERROR) { fputc( '\r', stdprn ); if (ceh.flag == ERROR) rc = col = ERROR; } block_start = find_next( block_start ); if (block_start == NULL) block_start = block_end + 1; } g_status.copied = FALSE; if (ceh.flag != ERROR) fflush( stdprn ); } } g_status.copied = FALSE; restore_screen_line( 0, prompt_line, line_buff ); return( rc ); } /* * Name: get_block_fill_char * 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( WINDOW *window, int *c ) { char answer[MAX_COLS]; char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute */ register int col; int prompt_line; int rc; rc = OK; prompt_line = window->bottom_line; save_screen_line( 0, prompt_line, line_buff ); /* * enter character to file block (esc to exit) */ 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, line_buff ); return( rc ); } /* * Name: get_block_numbers * 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( WINDOW *window, long *block_num, long *block_inc, int *just ) { char answer[MAX_COLS]; int prompt_line; register int rc; char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute */ register int col; 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, line_buff ); /* * left or right justify (l/r)? */ 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, line_buff ); } } /* * if everything is everything then return code = OK. */ return( rc ); } /* * Name: block_expand_tabs * Purpose: Expand tabs in a marked block. * Date: June 5, 1991 * Passed: window: pointer to current window * Notes: Tabs are expanded using the current tab interval. * Lines are checked to make sure they are not too long. */ int block_expand_tabs( WINDOW *window ) { int prompt_line; int len; int tab; int tab_size; int dirty; int spaces; int net_change; text_ptr p; /* pointer to block line */ file_infos *file; WINDOW *sw, s_w; long er; int i; char *b, *d, *lb; /* * make sure block is marked OK and that this is a LINE block */ prompt_line = window->bottom_line; un_copy_line( window->cursor, window, TRUE ); check_block( ); if (g_status.marked == TRUE) { file = g_status.marked_file; if (file->block_type != LINE) { /* * can only expand tabs in line blocks */ error( WARNING, prompt_line, block20 ); return( ERROR ); } /* * set the command to word wrap so the un_copy_line function will * not display the lines while expanding. */ g_status.command = WordWrap; /* * initialize everything */ dirty = FALSE; tab_size = mode.tab_size; sw = g_status.window_list; for (; ptoul( sw->file_info ) != ptoul( file );) sw = sw->next; dup_window_info( &s_w, sw ); p = cpf( file->block_start ); er = file->block_er; lb = g_status.line_buff; s_w.rline = file->block_br; for (; s_w.rline <= er && !g_status.control_break; s_w.rline++) { /* * use the line buffer to expand LINE blocks. */ tab = FALSE; len = linelen( p ); net_change = 0; g_status.copied = FALSE; copy_line( p, prompt_line ); for (b=lb, i=1; *b != CONTROL_Z; b++) { /* * each line in the LINE block is copied to the g_status.line_buff. * look at the text in the buffer and expand tabs. */ if (*b == '\t') { tab = TRUE; spaces = i % tab_size; if (spaces) spaces = tab_size - spaces; if (spaces) { d = b + spaces; memmove( d, b, linelen( b )+2 ); } memset( b, ' ', spaces+1 ); net_change += spaces; i += spaces + 1; b += spaces; } else i++; } /* * if any tabs were found, write g_status.line_buff to file. */ if (tab) { un_copy_line( p, &s_w, TRUE ); dirty = TRUE; } p = find_next( p ); } /* * 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; if (dirty) { check_block( ); file->dirty = GLOBAL; } } return( OK ); } /* * Name: block_trim_trailing * 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( WINDOW *window ) { int prompt_line; text_ptr p; /* pointer to block line */ file_infos *file; WINDOW *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; un_copy_line( window->cursor, window, TRUE ); check_block( ); if (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 ); } /* * set the command to word wrap so the un_copy_line function will * not display the lines while trimming. */ g_status.command = WordWrap; /* * initialize everything */ sw = g_status.window_list; for (; ptoul( sw->file_info ) != ptoul( file );) sw = sw->next; dup_window_info( &s_w, sw ); p = cpf( file->block_start ); er = file->block_er; s_w.rline = file->block_br; for (; s_w.rline <= er && !g_status.control_break; s_w.rline++) { /* * use the line buffer to trim space. */ copy_line( p, prompt_line ); un_copy_line( p, &s_w, TRUE ); p = find_next( p ); } /* * 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; } return( OK ); } /* * Name: block_convert_case * Purpose: convert characters to lower case, upper case, or strip hi bits * Date: June 5, 1991 * Passed: window: pointer to current window */ int block_convert_case( WINDOW *window ) { int len; int block_type; text_ptr begin; text_ptr end; /* pointer to block line */ register file_infos *file; unsigned long number; unsigned long er; unsigned int count; int bc, ec; int block_len; void (*char_func)( text_ptr, unsigned int ); /* * make sure block is marked OK */ un_copy_line( window->cursor, window, TRUE ); 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_asm; break; case BlockLowerCase : char_func = lower_asm; break; case BlockStripHiBit : char_func = strip_asm; break; } file = g_status.marked_file; block_type = file->block_type; bc = file->block_bc; ec = file->block_ec; begin = cpf( file->block_start ); end = cpf( file->block_end ); /* * if this is a LINE or STREAM block, process characters in * chunks of 0xf000. */ if (block_type == LINE || block_type == STREAM) { if (block_type == STREAM) { len = linelen( begin ); begin += len < bc ? len : bc; len = linelen( end ); end += len < ec ? len : ec + 1; } number = ptoul( end ) - ptoul( begin ); count = 0xf000; begin = nptos( begin ); while (number > count) { (*char_func)( begin, count ); number -= count; begin = nptos( begin + count ); } /* * now less than 0xf000 is left, so finish off the conversion */ (*char_func)( begin, (unsigned)number ); /* * For BOX blocks, process characters by lines */ } else { begin = cpf( begin ); er = file->block_er; block_len = ec + 1 - bc; for (number=file->block_br; number <= er; number++) { len = linelen( begin ); if (len > bc) { count = len >= ec ? block_len : len - bc; (*char_func)( begin+bc, count ); } begin = find_next( begin ); } } /* * 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; file->modified = TRUE; } return( OK ); } /* * Name: sort_box_block * Purpose: sort lines according to text in marked BOX block * Date: June 5, 1992 * Passed: window: pointer to current window * Notes: insertion sort the lines in the BOX buff according to stuff in * a box block. */ int sort_box_block( WINDOW *window ) { int prompt_line; int block_type; unsigned int low; unsigned int high; register file_infos *file; int rc; char lines[MAX_COLS]; char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute */ /* * make sure block is marked OK */ rc = OK; prompt_line = window->bottom_line; un_copy_line( window->cursor, window, TRUE ); check_block( ); if (g_status.marked == TRUE) { file = g_status.marked_file; block_type = file->block_type; if (block_type == BOX) { /* * sort ascending or descending? */ rc = get_sort_order( window ); if (rc != ERROR) { /* * check pointers and begin and ending line numbers. */ g_status.end_mem = cpf( g_status.end_mem ); file->block_start = cpf( file->block_start ); sort.block_len = file->block_ec + 1 - file->block_bc; low = 1; high = (unsigned int)(file->block_er - file->block_br + 1l); /* * save the prompt line and print out the master sort message. */ save_screen_line( 0, prompt_line, line_buff ); eol_clear( 0, prompt_line, g_display.text_color ); /* * sorting line of press control-break to cancel */ s_output( block22, prompt_line, 0, g_display.message_color ); ultoa( high, lines, 10 ); s_output( lines, prompt_line, 22, g_display.message_color ); /* * simple insertion sort the box block. */ sort.string1 = (char *)calloc( BUFF_SIZE+2, sizeof(char) ); sort.pivot = (char *)calloc( BUFF_SIZE+2, sizeof(char) ); if (sort.string1 != NULL && sort.pivot != NULL) insertion_sort_block( low, high, prompt_line ); free( sort.string1 ); free( sort.pivot ); /* * after sort finishes, restore prompt line and mark file as dirty. */ restore_screen_line( 0, prompt_line, line_buff ); file->dirty = GLOBAL; file->modified = TRUE; restore_cursors( file, file ); } } else { /* * can only sort box blocks */ error( WARNING, prompt_line, block23 ); rc = ERROR; } } else { /* * box not marked */ error( WARNING, prompt_line, block24 ); rc = ERROR; } return( rc ); } /* * Name: insertion_sort_block * Purpose: sort lines according to text in marked BOX block * Date: June 5, 1992 * Passed: low: starting line in box block * high: ending line in a box block * prompt_line: line to display messages * Notes: Insertion sort the lines in the BOX buff according to stuff in * a box block. * use whatever memory that is between the end of the file buffer * and absolute end of memory to switch lines. that way, we don't * have to allocate space on the stack or in the heap for * switching long lines. * tde explicity supports lines as long as 1040 chars. by using the * space between end_mem and max_mem when we swap lines, we can * implicitly handle fairly long lines. */ void insertion_sort_block(unsigned int low, unsigned int high, int prompt_line) { unsigned int change; /* relative line number for insertion sort */ unsigned int down; /* relative line number for insertion sort */ register unsigned int pivot; /* relative line number of pivot in block */ text_ptr pivot_text; /* pointer to actual text in block */ text_ptr next_pivot; /* pointer to next line after pivot line */ text_ptr down_text; /* pointer used to compare text */ char lines[MAX_COLS]; /* * make sure we have more than 1 line to sort. */ if (low < high) { /* * reset the control-break flag then initialize the sort structure. */ sort.bc = g_status.marked_file->block_bc; sort.ec = g_status.marked_file->block_ec; sort.compare = bm.search_case == IGNORE ? _fmemicmp : _fmemcmp; sort.free_mem = ptoul( g_status.max_mem ) - ptoul( g_status.end_mem ); /* * setup pointer to pivot and next pivot. when we swap lines, we * we will likely loose track of the next line, because swapping * variable length lines will leave the pivot_text pointer in an * unstable position. so, we need to save next_line. */ pivot_text = set_sort_begin( low + 1 ); next_pivot = find_next( pivot_text ); xygoto( 13, prompt_line ); for (pivot=low+1; pivot <= high && !g_status.control_break; pivot++) { /* * print out current line */ ultoa( pivot, lines, 10 ); s_output( lines, prompt_line, 13, g_display.message_color ); /* * set up the pivot array. the pivot contains the key we want * to sort. */ load_pivot( pivot_text ); down_text = find_prev( cpb( pivot_text ) ); change = pivot; for (down=pivot-1; down >= low; down--) { /* * lets keep comparing the keys until we find the hole for * pivot. */ if (compare_pivot( down_text ) > 0) { /* * if we are not at the first line of the block, find * the previous line. set change to down so we will know * when we need to swap lines. */ change = down; if (down > low) down_text = find_prev( down_text ); } else { /* * depending on the sort order, key is either bigger or * smaller than the rest of the lines in the top of the * box block. since we may have just done a find_prev * in the if above, we need to move down_text back to * the line that needs to be swapped. */ down_text = find_next( cpf( down_text ) ); break; } } if (change != pivot) slide_down( down_text, pivot_text ); next_pivot = find_next( pivot_text = next_pivot ); } } } /* * Name: set_sort_begin * Purpose: set pointer to line in block * Date: June 5, 1992 * Passed: line: number of line in block */ text_ptr set_sort_begin( unsigned int line ) { text_ptr text; register unsigned int i; text = g_status.marked_file->block_start; for (i=1; i < line; i++) text = find_next( text ); return( text ); } /* * Name: slide_down * Purpose: slide everything down then put the high line in low memory * Date: June 5, 1992 * Passed: low_text: pointer to line in lowest memory * high_text: pointer to line in high memory * Notes: copy high line to end of memory, then slide all the text from * the low line to the space left by the high line. to complete * the move, copy the high line from end of memory to low memory. */ void slide_down( text_ptr low_text, text_ptr high_text ) { unsigned long number; register unsigned int len2; low_text = nptos( low_text ); len2 = linelen( high_text = nptos( high_text ) ) + 1; if ((unsigned long)len2 > sort.free_mem) len2 = (unsigned)sort.free_mem - 1; number = ptoul( high_text ) - ptoul( low_text ); _fmemcpy( g_status.end_mem, high_text, len2 ); hw_move( low_text+len2, low_text, number ); _fmemcpy( low_text, g_status.end_mem, len2 ); } /* * Name: load_pivot * Purpose: load pivot point for insertion sort * Date: June 5, 1992 * Passed: text: line that contains the pivot */ void load_pivot( text_ptr text ) { load_box_buff( sort.pivot, cpf( text ), sort.bc, sort.ec, '\0' ); } /* * Name: compare_pivot * Purpose: compare pivot string with text string * Date: June 5, 1992 * Passed: text: pointer to current line */ int compare_pivot( text_ptr text ) { load_box_buff( sort.string1, cpf( text ), sort.bc, sort.ec, '\0' ); if (sort.sort_order == ASCENDING) return( (*sort.compare)( sort.string1, sort.pivot, sort.block_len ) ); else return( (*sort.compare)( sort.pivot, sort.string1, sort.block_len ) ); }