1 /* $NetBSD: cut.c,v 1.4 2014/01/26 21:43:45 christos Exp $ */ 2 /*- 3 * Copyright (c) 1992, 1993, 1994 4 * The Regents of the University of California. All rights reserved. 5 * Copyright (c) 1992, 1993, 1994, 1995, 1996 6 * Keith Bostic. All rights reserved. 7 * 8 * See the LICENSE file for redistribution information. 9 */ 10 11 #include "config.h" 12 13 #include <sys/cdefs.h> 14 #if 0 15 #ifndef lint 16 static const char sccsid[] = "Id: cut.c,v 10.18 2001/06/25 15:19:09 skimo Exp (Berkeley) Date: 2001/06/25 15:19:09 "; 17 #endif /* not lint */ 18 #else 19 __RCSID("$NetBSD: cut.c,v 1.4 2014/01/26 21:43:45 christos Exp $"); 20 #endif 21 22 #include <sys/types.h> 23 #include <sys/queue.h> 24 25 #include <bitstring.h> 26 #include <ctype.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <limits.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 34 #include "common.h" 35 36 static void cb_rotate __P((SCR *)); 37 38 /* 39 * cut -- 40 * Put a range of lines/columns into a TEXT buffer. 41 * 42 * There are two buffer areas, both found in the global structure. The first 43 * is the linked list of all the buffers the user has named, the second is the 44 * unnamed buffer storage. There is a pointer, too, which is the current 45 * default buffer, i.e. it may point to the unnamed buffer or a named buffer 46 * depending on into what buffer the last text was cut. Logically, in both 47 * delete and yank operations, if the user names a buffer, the text is cut 48 * into it. If it's a delete of information on more than a single line, the 49 * contents of the numbered buffers are rotated up one, the contents of the 50 * buffer named '9' are discarded, and the text is cut into the buffer named 51 * '1'. The text is always cut into the unnamed buffer. 52 * 53 * In all cases, upper-case buffer names are the same as lower-case names, 54 * with the exception that they cause the buffer to be appended to instead 55 * of replaced. Note, however, that if text is appended to a buffer, the 56 * default buffer only contains the appended text, not the entire contents 57 * of the buffer. 58 * 59 * !!! 60 * The contents of the default buffer would disappear after most operations 61 * in historic vi. It's unclear that this is useful, so we don't bother. 62 * 63 * When users explicitly cut text into the numeric buffers, historic vi became 64 * genuinely strange. I've never been able to figure out what was supposed to 65 * happen. It behaved differently if you deleted text than if you yanked text, 66 * and, in the latter case, the text was appended to the buffer instead of 67 * replacing the contents. Hopefully it's not worth getting right, and here 68 * we just treat the numeric buffers like any other named buffer. 69 * 70 * PUBLIC: int cut __P((SCR *, ARG_CHAR_T *, MARK *, MARK *, int)); 71 */ 72 int 73 cut(SCR *sp, ARG_CHAR_T *namep, MARK *fm, MARK *tm, int flags) 74 { 75 CB *cbp; 76 ARG_CHAR_T name = '\0'; 77 db_recno_t lno; 78 int append, copy_one, copy_def; 79 80 /* 81 * If the user specified a buffer, put it there. (This may require 82 * a copy into the numeric buffers. We do the copy so that we don't 83 * have to reference count and so we don't have to deal with things 84 * like appends to buffers that are used multiple times.) 85 * 86 * Otherwise, if it's supposed to be put in a numeric buffer (usually 87 * a delete) put it there. The rules for putting things in numeric 88 * buffers were historically a little strange. There were three cases. 89 * 90 * 1: Some motions are always line mode motions, which means 91 * that the cut always goes into the numeric buffers. 92 * 2: Some motions aren't line mode motions, e.g. d10w, but 93 * can cross line boundaries. For these commands, if the 94 * cut crosses a line boundary, it goes into the numeric 95 * buffers. This includes most of the commands. 96 * 3: Some motions aren't line mode motions, e.g. d`<char>, 97 * but always go into the numeric buffers, regardless. This 98 * was the commands: % ` / ? ( ) N n { } -- and nvi adds ^A. 99 * 100 * Otherwise, put it in the unnamed buffer. 101 */ 102 append = copy_one = copy_def = 0; 103 if (namep != NULL) { 104 name = *namep; 105 if (LF_ISSET(CUT_NUMREQ) || (LF_ISSET(CUT_NUMOPT) && 106 (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno))) { 107 copy_one = 1; 108 cb_rotate(sp); 109 } 110 if ((append = ISUPPER(name))) { 111 if (!copy_one) 112 copy_def = 1; 113 name = TOLOWER(name); 114 } 115 namecb: CBNAME(sp, cbp, name); 116 } else if (LF_ISSET(CUT_NUMREQ) || (LF_ISSET(CUT_NUMOPT) && 117 (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno))) { 118 name = L('1'); 119 cb_rotate(sp); 120 goto namecb; 121 } else 122 cbp = &sp->wp->dcb_store; 123 124 copyloop: 125 /* 126 * If this is a new buffer, create it and add it into the list. 127 * Otherwise, if it's not an append, free its current contents. 128 */ 129 if (cbp == NULL) { 130 CALLOC_RET(sp, cbp, CB *, 1, sizeof(CB)); 131 cbp->name = name; 132 TAILQ_INIT(&cbp->textq); 133 LIST_INSERT_HEAD(&sp->wp->cutq, cbp, q); 134 } else if (!append) { 135 text_lfree(&cbp->textq); 136 cbp->len = 0; 137 cbp->flags = 0; 138 } 139 140 141 /* In line mode, it's pretty easy, just cut the lines. */ 142 if (LF_ISSET(CUT_LINEMODE)) { 143 cbp->flags |= CB_LMODE; 144 for (lno = fm->lno; lno <= tm->lno; ++lno) 145 if (cut_line(sp, lno, 0, ENTIRE_LINE, cbp)) 146 goto cut_line_err; 147 } else { 148 /* 149 * Get the first line. A length of ENTIRE_LINE causes cut_line 150 * to cut from the MARK to the end of the line. 151 */ 152 if (cut_line(sp, fm->lno, fm->cno, fm->lno != tm->lno ? 153 ENTIRE_LINE : (tm->cno - fm->cno) + 1, cbp)) 154 goto cut_line_err; 155 156 /* Get the intermediate lines. */ 157 for (lno = fm->lno; ++lno < tm->lno;) 158 if (cut_line(sp, lno, 0, ENTIRE_LINE, cbp)) 159 goto cut_line_err; 160 161 /* Get the last line. */ 162 if (tm->lno != fm->lno && 163 cut_line(sp, lno, 0, tm->cno + 1, cbp)) 164 goto cut_line_err; 165 } 166 167 append = 0; /* Only append to the named buffer. */ 168 sp->wp->dcbp = cbp; /* Repoint the default buffer on each pass. */ 169 170 if (copy_one) { /* Copy into numeric buffer 1. */ 171 name = L('1'); 172 CBNAME(sp, cbp, name); 173 copy_one = 0; 174 goto copyloop; 175 } 176 if (copy_def) { /* Copy into the default buffer. */ 177 cbp = &sp->wp->dcb_store; 178 copy_def = 0; 179 goto copyloop; 180 } 181 return (0); 182 183 cut_line_err: 184 text_lfree(&cbp->textq); 185 cbp->len = 0; 186 cbp->flags = 0; 187 return (1); 188 } 189 190 /* 191 * cb_rotate -- 192 * Rotate the numbered buffers up one. 193 */ 194 static void 195 cb_rotate(SCR *sp) 196 { 197 CB *cbp, *del_cbp; 198 199 del_cbp = NULL; 200 LIST_FOREACH(cbp, &sp->wp->cutq, q) 201 switch(cbp->name) { 202 case L('1'): 203 cbp->name = L('2'); 204 break; 205 case L('2'): 206 cbp->name = L('3'); 207 break; 208 case L('3'): 209 cbp->name = L('4'); 210 break; 211 case L('4'): 212 cbp->name = L('5'); 213 break; 214 case L('5'): 215 cbp->name = L('6'); 216 break; 217 case L('6'): 218 cbp->name = L('7'); 219 break; 220 case L('7'): 221 cbp->name = L('8'); 222 break; 223 case L('8'): 224 cbp->name = L('9'); 225 break; 226 case L('9'): 227 del_cbp = cbp; 228 break; 229 } 230 if (del_cbp != NULL) { 231 LIST_REMOVE(del_cbp, q); 232 text_lfree(&del_cbp->textq); 233 free(del_cbp); 234 } 235 } 236 237 /* 238 * cut_line -- 239 * Cut a portion of a single line. 240 * 241 * PUBLIC: int cut_line __P((SCR *, db_recno_t, size_t, size_t, CB *)); 242 */ 243 int 244 cut_line(SCR *sp, db_recno_t lno, size_t fcno, size_t clen, CB *cbp) 245 { 246 TEXT *tp; 247 size_t len; 248 CHAR_T *p; 249 250 /* Get the line. */ 251 if (db_get(sp, lno, DBG_FATAL, &p, &len)) 252 return (1); 253 254 /* Create a TEXT structure that can hold the entire line. */ 255 if ((tp = text_init(sp, NULL, 0, len)) == NULL) 256 return (1); 257 258 /* 259 * If the line isn't empty and it's not the entire line, 260 * copy the portion we want, and reset the TEXT length. 261 */ 262 if (len != 0) { 263 if (clen == ENTIRE_LINE) 264 clen = len - fcno; 265 MEMCPYW(tp->lb, p + fcno, clen); 266 tp->len = clen; 267 } 268 269 /* Append to the end of the cut buffer. */ 270 TAILQ_INSERT_TAIL(&cbp->textq, tp, q); 271 cbp->len += tp->len; 272 273 return (0); 274 } 275 276 /* 277 * cut_close -- 278 * Discard all cut buffers. 279 * 280 * PUBLIC: void cut_close __P((WIN *)); 281 */ 282 void 283 cut_close(WIN *wp) 284 { 285 CB *cbp; 286 287 /* Free cut buffer list. */ 288 while ((cbp = LIST_FIRST(&wp->cutq)) != NULL) { 289 if (!TAILQ_EMPTY(&cbp->textq)) 290 text_lfree(&cbp->textq); 291 LIST_REMOVE(cbp, q); 292 free(cbp); 293 } 294 295 /* Free default cut storage. */ 296 cbp = &wp->dcb_store; 297 if (!TAILQ_EMPTY(&cbp->textq)) 298 text_lfree(&cbp->textq); 299 } 300 301 /* 302 * text_init -- 303 * Allocate a new TEXT structure. 304 * 305 * PUBLIC: TEXT *text_init __P((SCR *, const CHAR_T *, size_t, size_t)); 306 */ 307 TEXT * 308 text_init(SCR *sp, const CHAR_T *p, size_t len, size_t total_len) 309 { 310 TEXT *tp; 311 312 CALLOC(sp, tp, TEXT *, 1, sizeof(TEXT)); 313 if (tp == NULL) 314 return (NULL); 315 /* ANSI C doesn't define a call to malloc(3) for 0 bytes. */ 316 if ((tp->lb_len = total_len * sizeof(CHAR_T)) != 0) { 317 MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len * sizeof(CHAR_T)); 318 if (tp->lb == NULL) { 319 free(tp); 320 return (NULL); 321 } 322 if (p != NULL && len != 0) 323 MEMCPYW(tp->lb, p, len); 324 } 325 tp->len = len; 326 return (tp); 327 } 328 329 /* 330 * text_lfree -- 331 * Free a chain of text structures. 332 * 333 * PUBLIC: void text_lfree __P((TEXTH *)); 334 */ 335 void 336 text_lfree(TEXTH *headp) 337 { 338 TEXT *tp; 339 340 while ((tp = TAILQ_FIRST(headp)) != NULL) { 341 TAILQ_REMOVE(headp, tp, q); 342 text_free(tp); 343 } 344 } 345 346 /* 347 * text_free -- 348 * Free a text structure. 349 * 350 * PUBLIC: void text_free __P((TEXT *)); 351 */ 352 void 353 text_free(TEXT *tp) 354 { 355 if (tp->lb != NULL) 356 free(tp->lb); 357 free(tp); 358 } 359