1 /* $OpenBSD: line.c,v 1.9 2009/10/27 23:59:47 deraadt Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * Copyright (c) 1992, 1993, 1994, 1995, 1996 7 * Keith Bostic. All rights reserved. 8 * 9 * See the LICENSE file for redistribution information. 10 */ 11 12 #include "config.h" 13 14 #include <sys/types.h> 15 #include <sys/queue.h> 16 #include <sys/time.h> 17 18 #include <bitstring.h> 19 #include <errno.h> 20 #include <limits.h> 21 #include <stdio.h> 22 #include <string.h> 23 24 #include "common.h" 25 #include "../vi/vi.h" 26 27 static int scr_update(SCR *, recno_t, lnop_t, int); 28 29 /* 30 * db_eget -- 31 * Front-end to db_get, special case handling for empty files. 32 * 33 * PUBLIC: int db_eget(SCR *, recno_t, char **, size_t *, int *); 34 */ 35 int 36 db_eget(sp, lno, pp, lenp, isemptyp) 37 SCR *sp; 38 recno_t lno; /* Line number. */ 39 char **pp; /* Pointer store. */ 40 size_t *lenp; /* Length store. */ 41 int *isemptyp; 42 { 43 recno_t l1; 44 45 if (isemptyp != NULL) 46 *isemptyp = 0; 47 48 /* If the line exists, simply return it. */ 49 if (!db_get(sp, lno, 0, pp, lenp)) 50 return (0); 51 52 /* 53 * If the user asked for line 0 or line 1, i.e. the only possible 54 * line in an empty file, find the last line of the file; db_last 55 * fails loudly. 56 */ 57 if ((lno == 0 || lno == 1) && db_last(sp, &l1)) 58 return (1); 59 60 /* If the file isn't empty, fail loudly. */ 61 if ((lno != 0 && lno != 1) || l1 != 0) { 62 db_err(sp, lno); 63 return (1); 64 } 65 66 if (isemptyp != NULL) 67 *isemptyp = 1; 68 69 return (1); 70 } 71 72 /* 73 * db_get -- 74 * Look in the text buffers for a line, followed by the cache, followed 75 * by the database. 76 * 77 * PUBLIC: int db_get(SCR *, recno_t, u_int32_t, char **, size_t *); 78 */ 79 int 80 db_get(sp, lno, flags, pp, lenp) 81 SCR *sp; 82 recno_t lno; /* Line number. */ 83 u_int32_t flags; 84 char **pp; /* Pointer store. */ 85 size_t *lenp; /* Length store. */ 86 { 87 DBT data, key; 88 EXF *ep; 89 TEXT *tp; 90 recno_t l1, l2; 91 92 /* 93 * The underlying recno stuff handles zero by returning NULL, but 94 * have to have an OOB condition for the look-aside into the input 95 * buffer anyway. 96 */ 97 if (lno == 0) 98 goto err1; 99 100 /* Check for no underlying file. */ 101 if ((ep = sp->ep) == NULL) { 102 ex_emsg(sp, NULL, EXM_NOFILEYET); 103 goto err3; 104 } 105 106 if (LF_ISSET(DBG_NOCACHE)) 107 goto nocache; 108 109 /* 110 * Look-aside into the TEXT buffers and see if the line we want 111 * is there. 112 */ 113 if (F_ISSET(sp, SC_TINPUT)) { 114 l1 = ((TEXT *)CIRCLEQ_FIRST(&sp->tiq))->lno; 115 l2 = ((TEXT *)CIRCLEQ_LAST(&sp->tiq))->lno; 116 if (l1 <= lno && l2 >= lno) { 117 #if defined(DEBUG) && 0 118 TRACE(sp, "retrieve TEXT buffer line %lu\n", (u_long)lno); 119 #endif 120 for (tp = CIRCLEQ_FIRST(&sp->tiq); 121 tp->lno != lno; tp = CIRCLEQ_NEXT(tp, q)); 122 if (lenp != NULL) 123 *lenp = tp->len; 124 if (pp != NULL) 125 *pp = tp->lb; 126 return (0); 127 } 128 /* 129 * Adjust the line number for the number of lines used 130 * by the text input buffers. 131 */ 132 if (lno > l2) 133 lno -= l2 - l1; 134 } 135 136 /* Look-aside into the cache, and see if the line we want is there. */ 137 if (lno == ep->c_lno) { 138 #if defined(DEBUG) && 0 139 TRACE(sp, "retrieve cached line %lu\n", (u_long)lno); 140 #endif 141 if (lenp != NULL) 142 *lenp = ep->c_len; 143 if (pp != NULL) 144 *pp = ep->c_lp; 145 return (0); 146 } 147 ep->c_lno = OOBLNO; 148 149 nocache: 150 /* Get the line from the underlying database. */ 151 key.data = &lno; 152 key.size = sizeof(lno); 153 switch (ep->db->get(ep->db, &key, &data, 0)) { 154 case -1: 155 goto err2; 156 case 1: 157 err1: if (LF_ISSET(DBG_FATAL)) 158 err2: db_err(sp, lno); 159 err3: if (lenp != NULL) 160 *lenp = 0; 161 if (pp != NULL) 162 *pp = NULL; 163 return (1); 164 } 165 166 /* Reset the cache. */ 167 ep->c_lno = lno; 168 ep->c_len = data.size; 169 ep->c_lp = data.data; 170 171 #if defined(DEBUG) && 0 172 TRACE(sp, "retrieve DB line %lu\n", (u_long)lno); 173 #endif 174 if (lenp != NULL) 175 *lenp = data.size; 176 if (pp != NULL) 177 *pp = ep->c_lp; 178 return (0); 179 } 180 181 /* 182 * db_delete -- 183 * Delete a line from the file. 184 * 185 * PUBLIC: int db_delete(SCR *, recno_t); 186 */ 187 int 188 db_delete(sp, lno) 189 SCR *sp; 190 recno_t lno; 191 { 192 DBT key; 193 EXF *ep; 194 195 #if defined(DEBUG) && 0 196 TRACE(sp, "delete line %lu\n", (u_long)lno); 197 #endif 198 /* Check for no underlying file. */ 199 if ((ep = sp->ep) == NULL) { 200 ex_emsg(sp, NULL, EXM_NOFILEYET); 201 return (1); 202 } 203 204 /* Update marks, @ and global commands. */ 205 if (mark_insdel(sp, LINE_DELETE, lno)) 206 return (1); 207 if (ex_g_insdel(sp, LINE_DELETE, lno)) 208 return (1); 209 210 /* Log change. */ 211 log_line(sp, lno, LOG_LINE_DELETE); 212 213 /* Update file. */ 214 key.data = &lno; 215 key.size = sizeof(lno); 216 SIGBLOCK; 217 if (ep->db->del(ep->db, &key, 0) == 1) { 218 msgq(sp, M_SYSERR, 219 "003|unable to delete line %lu", (u_long)lno); 220 return (1); 221 } 222 SIGUNBLOCK; 223 224 /* Flush the cache, update line count, before screen update. */ 225 if (lno <= ep->c_lno) 226 ep->c_lno = OOBLNO; 227 if (ep->c_nlines != OOBLNO) 228 --ep->c_nlines; 229 230 /* File now modified. */ 231 if (F_ISSET(ep, F_FIRSTMODIFY)) 232 (void)rcv_init(sp); 233 F_SET(ep, F_MODIFIED); 234 235 /* Update screen. */ 236 return (scr_update(sp, lno, LINE_DELETE, 1)); 237 } 238 239 /* 240 * db_append -- 241 * Append a line into the file. 242 * 243 * PUBLIC: int db_append(SCR *, int, recno_t, char *, size_t); 244 */ 245 int 246 db_append(sp, update, lno, p, len) 247 SCR *sp; 248 int update; 249 recno_t lno; 250 char *p; 251 size_t len; 252 { 253 DBT data, key; 254 EXF *ep; 255 int rval; 256 257 #if defined(DEBUG) && 0 258 TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); 259 #endif 260 /* Check for no underlying file. */ 261 if ((ep = sp->ep) == NULL) { 262 ex_emsg(sp, NULL, EXM_NOFILEYET); 263 return (1); 264 } 265 266 /* Update file. */ 267 key.data = &lno; 268 key.size = sizeof(lno); 269 data.data = p; 270 data.size = len; 271 SIGBLOCK; 272 if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) { 273 msgq(sp, M_SYSERR, 274 "004|unable to append to line %lu", (u_long)lno); 275 return (1); 276 } 277 SIGUNBLOCK; 278 279 /* Flush the cache, update line count, before screen update. */ 280 if (lno < ep->c_lno) 281 ep->c_lno = OOBLNO; 282 if (ep->c_nlines != OOBLNO) 283 ++ep->c_nlines; 284 285 /* File now dirty. */ 286 if (F_ISSET(ep, F_FIRSTMODIFY)) 287 (void)rcv_init(sp); 288 F_SET(ep, F_MODIFIED); 289 290 /* Log change. */ 291 log_line(sp, lno + 1, LOG_LINE_APPEND); 292 293 /* Update marks, @ and global commands. */ 294 rval = 0; 295 if (mark_insdel(sp, LINE_INSERT, lno + 1)) 296 rval = 1; 297 if (ex_g_insdel(sp, LINE_INSERT, lno + 1)) 298 rval = 1; 299 300 /* 301 * Update screen. 302 * 303 * XXX 304 * Nasty hack. If multiple lines are input by the user, they aren't 305 * committed until an <ESC> is entered. The problem is the screen was 306 * updated/scrolled as each line was entered. So, when this routine 307 * is called to copy the new lines from the cut buffer into the file, 308 * it has to know not to update the screen again. 309 */ 310 return (scr_update(sp, lno, LINE_APPEND, update) || rval); 311 } 312 313 /* 314 * db_insert -- 315 * Insert a line into the file. 316 * 317 * PUBLIC: int db_insert(SCR *, recno_t, char *, size_t); 318 */ 319 int 320 db_insert(sp, lno, p, len) 321 SCR *sp; 322 recno_t lno; 323 char *p; 324 size_t len; 325 { 326 DBT data, key; 327 EXF *ep; 328 int rval; 329 330 #if defined(DEBUG) && 0 331 TRACE(sp, "insert before %lu: len %lu {%.*s}\n", 332 (u_long)lno, (u_long)len, MIN(len, 20), p); 333 #endif 334 /* Check for no underlying file. */ 335 if ((ep = sp->ep) == NULL) { 336 ex_emsg(sp, NULL, EXM_NOFILEYET); 337 return (1); 338 } 339 340 /* Update file. */ 341 key.data = &lno; 342 key.size = sizeof(lno); 343 data.data = p; 344 data.size = len; 345 SIGBLOCK; 346 if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) { 347 msgq(sp, M_SYSERR, 348 "005|unable to insert at line %lu", (u_long)lno); 349 return (1); 350 } 351 SIGUNBLOCK; 352 353 /* Flush the cache, update line count, before screen update. */ 354 if (lno >= ep->c_lno) 355 ep->c_lno = OOBLNO; 356 if (ep->c_nlines != OOBLNO) 357 ++ep->c_nlines; 358 359 /* File now dirty. */ 360 if (F_ISSET(ep, F_FIRSTMODIFY)) 361 (void)rcv_init(sp); 362 F_SET(ep, F_MODIFIED); 363 364 /* Log change. */ 365 log_line(sp, lno, LOG_LINE_INSERT); 366 367 /* Update marks, @ and global commands. */ 368 rval = 0; 369 if (mark_insdel(sp, LINE_INSERT, lno)) 370 rval = 1; 371 if (ex_g_insdel(sp, LINE_INSERT, lno)) 372 rval = 1; 373 374 /* Update screen. */ 375 return (scr_update(sp, lno, LINE_INSERT, 1) || rval); 376 } 377 378 /* 379 * db_set -- 380 * Store a line in the file. 381 * 382 * PUBLIC: int db_set(SCR *, recno_t, char *, size_t); 383 */ 384 int 385 db_set(sp, lno, p, len) 386 SCR *sp; 387 recno_t lno; 388 char *p; 389 size_t len; 390 { 391 DBT data, key; 392 EXF *ep; 393 394 #if defined(DEBUG) && 0 395 TRACE(sp, "replace line %lu: len %lu {%.*s}\n", 396 (u_long)lno, (u_long)len, MIN(len, 20), p); 397 #endif 398 399 /* Check for no underlying file. */ 400 if ((ep = sp->ep) == NULL) { 401 ex_emsg(sp, NULL, EXM_NOFILEYET); 402 return (1); 403 } 404 405 /* Log before change. */ 406 log_line(sp, lno, LOG_LINE_RESET_B); 407 408 /* Update file. */ 409 key.data = &lno; 410 key.size = sizeof(lno); 411 data.data = p; 412 data.size = len; 413 SIGBLOCK; 414 if (ep->db->put(ep->db, &key, &data, 0) == -1) { 415 msgq(sp, M_SYSERR, 416 "006|unable to store line %lu", (u_long)lno); 417 return (1); 418 } 419 SIGUNBLOCK; 420 421 /* Flush the cache, before logging or screen update. */ 422 if (lno == ep->c_lno) 423 ep->c_lno = OOBLNO; 424 425 /* File now dirty. */ 426 if (F_ISSET(ep, F_FIRSTMODIFY)) 427 (void)rcv_init(sp); 428 F_SET(ep, F_MODIFIED); 429 430 /* Log after change. */ 431 log_line(sp, lno, LOG_LINE_RESET_F); 432 433 /* Update screen. */ 434 return (scr_update(sp, lno, LINE_RESET, 1)); 435 } 436 437 /* 438 * db_exist -- 439 * Return if a line exists. 440 * 441 * PUBLIC: int db_exist(SCR *, recno_t); 442 */ 443 int 444 db_exist(sp, lno) 445 SCR *sp; 446 recno_t lno; 447 { 448 EXF *ep; 449 450 /* Check for no underlying file. */ 451 if ((ep = sp->ep) == NULL) { 452 ex_emsg(sp, NULL, EXM_NOFILEYET); 453 return (1); 454 } 455 456 if (lno == OOBLNO) 457 return (0); 458 459 /* 460 * Check the last-line number cache. Adjust the cached line 461 * number for the lines used by the text input buffers. 462 */ 463 if (ep->c_nlines != OOBLNO) 464 return (lno <= (F_ISSET(sp, SC_TINPUT) ? 465 ep->c_nlines + (((TEXT *)CIRCLEQ_LAST(&sp->tiq))->lno - 466 ((TEXT *)CIRCLEQ_FIRST(&sp->tiq))->lno) : ep->c_nlines)); 467 468 /* Go get the line. */ 469 return (!db_get(sp, lno, 0, NULL, NULL)); 470 } 471 472 /* 473 * db_last -- 474 * Return the number of lines in the file. 475 * 476 * PUBLIC: int db_last(SCR *, recno_t *); 477 */ 478 int 479 db_last(sp, lnop) 480 SCR *sp; 481 recno_t *lnop; 482 { 483 DBT data, key; 484 EXF *ep; 485 recno_t lno; 486 487 /* Check for no underlying file. */ 488 if ((ep = sp->ep) == NULL) { 489 ex_emsg(sp, NULL, EXM_NOFILEYET); 490 return (1); 491 } 492 493 /* 494 * Check the last-line number cache. Adjust the cached line 495 * number for the lines used by the text input buffers. 496 */ 497 if (ep->c_nlines != OOBLNO) { 498 *lnop = ep->c_nlines; 499 if (F_ISSET(sp, SC_TINPUT)) 500 *lnop += ((TEXT *)CIRCLEQ_LAST(&sp->tiq))->lno - 501 ((TEXT *)CIRCLEQ_FIRST(&sp->tiq))->lno; 502 return (0); 503 } 504 505 key.data = &lno; 506 key.size = sizeof(lno); 507 508 switch (ep->db->seq(ep->db, &key, &data, R_LAST)) { 509 case -1: 510 msgq(sp, M_SYSERR, "007|unable to get last line"); 511 *lnop = 0; 512 return (1); 513 case 1: 514 *lnop = 0; 515 return (0); 516 default: 517 break; 518 } 519 520 /* Fill the cache. */ 521 memcpy(&lno, key.data, sizeof(lno)); 522 ep->c_nlines = ep->c_lno = lno; 523 ep->c_len = data.size; 524 ep->c_lp = data.data; 525 526 /* Return the value. */ 527 *lnop = (F_ISSET(sp, SC_TINPUT) && 528 ((TEXT *)CIRCLEQ_LAST(&sp->tiq))->lno > lno ? 529 ((TEXT *)CIRCLEQ_LAST(&sp->tiq))->lno : lno); 530 return (0); 531 } 532 533 /* 534 * db_err -- 535 * Report a line error. 536 * 537 * PUBLIC: void db_err(SCR *, recno_t); 538 */ 539 void 540 db_err(sp, lno) 541 SCR *sp; 542 recno_t lno; 543 { 544 msgq(sp, M_ERR, 545 "008|Error: unable to retrieve line %lu", (u_long)lno); 546 } 547 548 /* 549 * scr_update -- 550 * Update all of the screens that are backed by the file that 551 * just changed. 552 */ 553 static int 554 scr_update(sp, lno, op, current) 555 SCR *sp; 556 recno_t lno; 557 lnop_t op; 558 int current; 559 { 560 EXF *ep; 561 SCR *tsp; 562 563 if (F_ISSET(sp, SC_EX)) 564 return (0); 565 566 ep = sp->ep; 567 if (ep->refcnt != 1) 568 CIRCLEQ_FOREACH(tsp, &sp->gp->dq, q) 569 if (sp != tsp && tsp->ep == ep) 570 if (vs_change(tsp, lno, op)) 571 return (1); 572 return (current ? vs_change(sp, lno, op) : 0); 573 } 574