1 /*- 2 * Copyright (c) 1992, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 1992, 1993, 1994, 1995, 1996 5 * Keith Bostic. All rights reserved. 6 * 7 * See the LICENSE file for redistribution information. 8 */ 9 10 #include "config.h" 11 12 #ifndef lint 13 static const char sccsid[] = "$Id: ex_join.c,v 10.17 2004/03/16 14:14:04 skimo Exp $"; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/queue.h> 18 #include <sys/time.h> 19 20 #include <bitstring.h> 21 #include <ctype.h> 22 #include <limits.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 #include "../common/common.h" 28 29 /* 30 * ex_join -- :[line [,line]] j[oin][!] [count] [flags] 31 * Join lines. 32 * 33 * PUBLIC: int ex_join(SCR *, EXCMD *); 34 */ 35 int 36 ex_join(SCR *sp, EXCMD *cmdp) 37 { 38 recno_t from, to; 39 size_t blen, clen, len, tlen; 40 int echar = 0, extra, first; 41 CHAR_T *bp, *tbp = NULL; 42 CHAR_T *p; 43 44 NEEDFILE(sp, cmdp); 45 46 from = cmdp->addr1.lno; 47 to = cmdp->addr2.lno; 48 49 /* Check for no lines to join. */ 50 if (!db_exist(sp, from + 1)) { 51 msgq(sp, M_ERR, "131|No following lines to join"); 52 return (1); 53 } 54 55 GET_SPACE_RETW(sp, bp, blen, 256); 56 57 /* 58 * The count for the join command was off-by-one, 59 * historically, to other counts for other commands. 60 */ 61 if (F_ISSET(cmdp, E_ADDR_DEF) || cmdp->addrcnt == 1) 62 ++cmdp->addr2.lno; 63 64 clen = tlen = 0; 65 for (first = 1, 66 from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) { 67 /* 68 * Get next line. Historic versions of vi allowed "10J" while 69 * less than 10 lines from the end-of-file, so we do too. 70 */ 71 if (db_get(sp, from, 0, &p, &len)) { 72 cmdp->addr2.lno = from - 1; 73 break; 74 } 75 76 /* Empty lines just go away. */ 77 if (len == 0) 78 continue; 79 80 /* 81 * Get more space if necessary. Note, tlen isn't the length 82 * of the new line, it's roughly the amount of space needed. 83 * tbp - bp is the length of the new line. 84 */ 85 tlen += len + 2; 86 ADD_SPACE_RETW(sp, bp, blen, tlen); 87 tbp = bp + clen; 88 89 /* 90 * Historic practice: 91 * 92 * If force specified, join without modification. 93 * If the current line ends with whitespace, strip leading 94 * whitespace from the joined line. 95 * If the next line starts with a ), do nothing. 96 * If the current line ends with ., insert two spaces. 97 * Else, insert one space. 98 * 99 * One change -- add ? and ! to the list of characters for 100 * which we insert two spaces. I expect that POSIX 1003.2 101 * will require this as well. 102 * 103 * Echar is the last character in the last line joined. 104 */ 105 extra = 0; 106 if (!first && !FL_ISSET(cmdp->iflags, E_C_FORCE)) { 107 if (isblank(echar)) 108 for (; len && isblank(*p); --len, ++p); 109 else if (p[0] != ')') { 110 if (STRCHR(L(".?!"), echar)) { 111 *tbp++ = ' '; 112 ++clen; 113 extra = 1; 114 } 115 *tbp++ = ' '; 116 ++clen; 117 for (; len && isblank(*p); --len, ++p); 118 } 119 } 120 121 if (len != 0) { 122 MEMCPY(tbp, p, len); 123 tbp += len; 124 clen += len; 125 echar = p[len - 1]; 126 } else 127 echar = ' '; 128 129 /* 130 * Historic practice for vi was to put the cursor at the first 131 * inserted whitespace character, if there was one, or the 132 * first character of the joined line, if there wasn't, or the 133 * last character of the line if joined to an empty line. If 134 * a count was specified, the cursor was moved as described 135 * for the first line joined, ignoring subsequent lines. If 136 * the join was a ':' command, the cursor was placed at the 137 * first non-blank character of the line unless the cursor was 138 * "attracted" to the end of line when the command was executed 139 * in which case it moved to the new end of line. There are 140 * probably several more special cases, but frankly, my dear, 141 * I don't give a damn. This implementation puts the cursor 142 * on the first inserted whitespace character, the first 143 * character of the joined line, or the last character of the 144 * line regardless. Note, if the cursor isn't on the joined 145 * line (possible with : commands), it is reset to the starting 146 * line. 147 */ 148 if (first) { 149 sp->cno = (tbp - bp) - (1 + extra); 150 first = 0; 151 } else 152 sp->cno = (tbp - bp) - len - (1 + extra); 153 } 154 sp->lno = cmdp->addr1.lno; 155 156 /* Delete the joined lines. */ 157 for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; to > from; --to) 158 if (db_delete(sp, to)) 159 goto err; 160 161 /* If the original line changed, reset it. */ 162 if (!first && db_set(sp, from, bp, tbp - bp)) { 163 err: FREE_SPACEW(sp, bp, blen); 164 return (1); 165 } 166 FREE_SPACEW(sp, bp, blen); 167 168 sp->rptlines[L_JOINED] += (cmdp->addr2.lno - cmdp->addr1.lno) + 1; 169 return (0); 170 } 171