119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: ex_join.c,v 10.17 2004/03/16 14:14:04 skimo Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 18254225Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <ctype.h> 2219304Speter#include <limits.h> 2319304Speter#include <stdio.h> 2419304Speter#include <stdlib.h> 2519304Speter#include <string.h> 2619304Speter 2719304Speter#include "../common/common.h" 2819304Speter 2919304Speter/* 3019304Speter * ex_join -- :[line [,line]] j[oin][!] [count] [flags] 3119304Speter * Join lines. 3219304Speter * 3319304Speter * PUBLIC: int ex_join __P((SCR *, EXCMD *)); 3419304Speter */ 3519304Speterint 36254225Speterex_join(SCR *sp, EXCMD *cmdp) 3719304Speter{ 3819304Speter recno_t from, to; 3919304Speter size_t blen, clen, len, tlen; 40254225Speter int echar = 0, extra, first; 41254225Speter CHAR_T *bp, *tbp = NULL; 42254225Speter CHAR_T *p; 4319304Speter 4419304Speter NEEDFILE(sp, cmdp); 4519304Speter 4619304Speter from = cmdp->addr1.lno; 4719304Speter to = cmdp->addr2.lno; 4819304Speter 4919304Speter /* Check for no lines to join. */ 5019304Speter if (!db_exist(sp, from + 1)) { 5119304Speter msgq(sp, M_ERR, "131|No following lines to join"); 5219304Speter return (1); 5319304Speter } 5419304Speter 55254225Speter GET_SPACE_RETW(sp, bp, blen, 256); 5619304Speter 5719304Speter /* 5819304Speter * The count for the join command was off-by-one, 5919304Speter * historically, to other counts for other commands. 6019304Speter */ 61254225Speter if (F_ISSET(cmdp, E_ADDR_DEF) || cmdp->addrcnt == 1) 6219304Speter ++cmdp->addr2.lno; 6319304Speter 6419304Speter clen = tlen = 0; 6519304Speter for (first = 1, 6619304Speter from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) { 6719304Speter /* 6819304Speter * Get next line. Historic versions of vi allowed "10J" while 6919304Speter * less than 10 lines from the end-of-file, so we do too. 7019304Speter */ 7119304Speter if (db_get(sp, from, 0, &p, &len)) { 7219304Speter cmdp->addr2.lno = from - 1; 7319304Speter break; 7419304Speter } 7519304Speter 7619304Speter /* Empty lines just go away. */ 7719304Speter if (len == 0) 7819304Speter continue; 7919304Speter 8019304Speter /* 8119304Speter * Get more space if necessary. Note, tlen isn't the length 8219304Speter * of the new line, it's roughly the amount of space needed. 8319304Speter * tbp - bp is the length of the new line. 8419304Speter */ 8519304Speter tlen += len + 2; 86254225Speter ADD_SPACE_RETW(sp, bp, blen, tlen); 8719304Speter tbp = bp + clen; 8819304Speter 8919304Speter /* 9019304Speter * Historic practice: 9119304Speter * 9219304Speter * If force specified, join without modification. 9319304Speter * If the current line ends with whitespace, strip leading 9419304Speter * whitespace from the joined line. 9519304Speter * If the next line starts with a ), do nothing. 9619304Speter * If the current line ends with ., insert two spaces. 9719304Speter * Else, insert one space. 9819304Speter * 9919304Speter * One change -- add ? and ! to the list of characters for 10019304Speter * which we insert two spaces. I expect that POSIX 1003.2 10119304Speter * will require this as well. 10219304Speter * 10319304Speter * Echar is the last character in the last line joined. 10419304Speter */ 10519304Speter extra = 0; 10619304Speter if (!first && !FL_ISSET(cmdp->iflags, E_C_FORCE)) { 10719304Speter if (isblank(echar)) 10819304Speter for (; len && isblank(*p); --len, ++p); 10919304Speter else if (p[0] != ')') { 110254225Speter if (STRCHR(L(".?!"), echar)) { 11119304Speter *tbp++ = ' '; 11219304Speter ++clen; 11319304Speter extra = 1; 11419304Speter } 11519304Speter *tbp++ = ' '; 11619304Speter ++clen; 11719304Speter for (; len && isblank(*p); --len, ++p); 11819304Speter } 11919304Speter } 12019304Speter 12119304Speter if (len != 0) { 122254225Speter MEMCPY(tbp, p, len); 12319304Speter tbp += len; 12419304Speter clen += len; 12519304Speter echar = p[len - 1]; 12619304Speter } else 12719304Speter echar = ' '; 12819304Speter 12919304Speter /* 13019304Speter * Historic practice for vi was to put the cursor at the first 13119304Speter * inserted whitespace character, if there was one, or the 13219304Speter * first character of the joined line, if there wasn't, or the 13319304Speter * last character of the line if joined to an empty line. If 13419304Speter * a count was specified, the cursor was moved as described 13519304Speter * for the first line joined, ignoring subsequent lines. If 13619304Speter * the join was a ':' command, the cursor was placed at the 13719304Speter * first non-blank character of the line unless the cursor was 13819304Speter * "attracted" to the end of line when the command was executed 13919304Speter * in which case it moved to the new end of line. There are 14019304Speter * probably several more special cases, but frankly, my dear, 14119304Speter * I don't give a damn. This implementation puts the cursor 14219304Speter * on the first inserted whitespace character, the first 14319304Speter * character of the joined line, or the last character of the 14419304Speter * line regardless. Note, if the cursor isn't on the joined 14519304Speter * line (possible with : commands), it is reset to the starting 14619304Speter * line. 14719304Speter */ 14819304Speter if (first) { 14919304Speter sp->cno = (tbp - bp) - (1 + extra); 15019304Speter first = 0; 15119304Speter } else 15219304Speter sp->cno = (tbp - bp) - len - (1 + extra); 15319304Speter } 15419304Speter sp->lno = cmdp->addr1.lno; 15519304Speter 15619304Speter /* Delete the joined lines. */ 15719304Speter for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; to > from; --to) 15819304Speter if (db_delete(sp, to)) 15919304Speter goto err; 16019304Speter 16119304Speter /* If the original line changed, reset it. */ 16219304Speter if (!first && db_set(sp, from, bp, tbp - bp)) { 163254225Spetererr: FREE_SPACEW(sp, bp, blen); 16419304Speter return (1); 16519304Speter } 166254225Speter FREE_SPACEW(sp, bp, blen); 16719304Speter 16819304Speter sp->rptlines[L_JOINED] += (cmdp->addr2.lno - cmdp->addr1.lno) + 1; 16919304Speter return (0); 17019304Speter} 171