1 /* 2 * Copyright (c) 1994, David Greenman 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: src/sys/kern/tty_subr.c,v 1.32 1999/08/28 00:46:21 peter Exp $ 28 * $DragonFly: src/sys/kern/tty_subr.c,v 1.5 2004/10/07 01:32:03 dillon Exp $ 29 */ 30 31 /* 32 * clist support routines 33 * 34 * NOTE on cblock->c_cf: This pointer may point at the base of a cblock, 35 * which is &cblock->c_info[0], but will never 36 * point at the end of a cblock (char *)(cblk + 1) 37 * 38 * NOTE on cblock->c_cl: This pointer will never point at the base of 39 * a block but may point at the end of one. 40 * 41 * These routines may be used by more then just ttys, so a critical section 42 * must be used to access the free list, and for general safety. 43 */ 44 45 #include <sys/param.h> 46 #include <sys/kernel.h> 47 #include <sys/systm.h> 48 #include <sys/malloc.h> 49 #include <sys/tty.h> 50 #include <sys/clist.h> 51 #include <sys/thread2.h> 52 53 static void clist_init (void *); 54 SYSINIT(clist, SI_SUB_CLIST, SI_ORDER_FIRST, clist_init, NULL) 55 56 static struct cblock *cfreelist = 0; 57 int cfreecount = 0; 58 static int cslushcount; 59 static int ctotcount; 60 61 #ifndef INITIAL_CBLOCKS 62 #define INITIAL_CBLOCKS 50 63 #endif 64 65 static struct cblock *cblock_alloc (void); 66 static void cblock_alloc_cblocks (int number); 67 static void cblock_free (struct cblock *cblockp); 68 static void cblock_free_cblocks (int number); 69 70 #include "opt_ddb.h" 71 #ifdef DDB 72 #include <ddb/ddb.h> 73 74 DB_SHOW_COMMAND(cbstat, cbstat) 75 { 76 int cbsize = CBSIZE; 77 78 printf( 79 "tot = %d (active = %d, free = %d (reserved = %d, slush = %d))\n", 80 ctotcount * cbsize, ctotcount * cbsize - cfreecount, cfreecount, 81 cfreecount - cslushcount * cbsize, cslushcount * cbsize); 82 } 83 #endif /* DDB */ 84 85 /* 86 * Called from init_main.c 87 */ 88 /* ARGSUSED*/ 89 static void 90 clist_init(void *dummy) 91 { 92 /* 93 * Allocate an initial base set of cblocks as a 'slush'. 94 * We allocate non-slush cblocks with each initial ttyopen() and 95 * deallocate them with each ttyclose(). 96 * We should adjust the slush allocation. This can't be done in 97 * the i/o routines because they are sometimes called from 98 * interrupt handlers when it may be unsafe to call malloc(). 99 */ 100 cblock_alloc_cblocks(cslushcount = INITIAL_CBLOCKS); 101 KKASSERT(sizeof(struct cblock) == CBLOCK); 102 } 103 104 /* 105 * Remove a cblock from the cfreelist queue and return a pointer 106 * to it. 107 * 108 * May not block. 109 */ 110 static struct cblock * 111 cblock_alloc(void) 112 { 113 struct cblock *cblockp; 114 115 cblockp = cfreelist; 116 if (cblockp == NULL) 117 panic("clist reservation botch"); 118 KKASSERT(cblockp->c_head.ch_magic == CLIST_MAGIC_FREE); 119 cfreelist = cblockp->c_head.ch_next; 120 cblockp->c_head.ch_next = NULL; 121 cblockp->c_head.ch_magic = CLIST_MAGIC_USED; 122 cfreecount -= CBSIZE; 123 return (cblockp); 124 } 125 126 /* 127 * Add a cblock to the cfreelist queue. 128 * 129 * May not block, must be called in a critical section 130 */ 131 static void 132 cblock_free(struct cblock *cblockp) 133 { 134 if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1)) 135 bzero(cblockp->c_quote, sizeof cblockp->c_quote); 136 KKASSERT(cblockp->c_head.ch_magic == CLIST_MAGIC_USED); 137 cblockp->c_head.ch_next = cfreelist; 138 cblockp->c_head.ch_magic = CLIST_MAGIC_FREE; 139 cfreelist = cblockp; 140 cfreecount += CBSIZE; 141 } 142 143 /* 144 * Allocate some cblocks for the cfreelist queue. 145 * 146 * This routine my block, but still must be called in a critical section 147 */ 148 static void 149 cblock_alloc_cblocks(int number) 150 { 151 int i; 152 struct cblock *cbp; 153 154 for (i = 0; i < number; ++i) { 155 cbp = malloc(sizeof *cbp, M_TTYS, M_NOWAIT); 156 if (cbp == NULL) { 157 printf( 158 "clist_alloc_cblocks: M_NOWAIT malloc failed, trying M_WAITOK\n"); 159 cbp = malloc(sizeof *cbp, M_TTYS, M_WAITOK); 160 } 161 KKASSERT(((intptr_t)cbp & CROUND) == 0); 162 /* 163 * Freed cblocks have zero quotes and garbage elsewhere. 164 * Set the may-have-quote bit to force zeroing the quotes. 165 */ 166 setbit(cbp->c_quote, CBQSIZE * NBBY - 1); 167 cbp->c_head.ch_magic = CLIST_MAGIC_USED; 168 cblock_free(cbp); 169 } 170 ctotcount += number; 171 } 172 173 /* 174 * Set the cblock allocation policy for a clist. 175 */ 176 void 177 clist_alloc_cblocks(struct clist *clistp, int ccmax, int ccreserved) 178 { 179 int dcbr; 180 181 /* 182 * Allow for wasted space at the head. 183 */ 184 if (ccmax != 0) 185 ccmax += CBSIZE - 1; 186 if (ccreserved != 0) 187 ccreserved += CBSIZE - 1; 188 189 crit_enter(); 190 clistp->c_cbmax = roundup(ccmax, CBSIZE) / CBSIZE; 191 dcbr = roundup(ccreserved, CBSIZE) / CBSIZE - clistp->c_cbreserved; 192 if (dcbr >= 0) { 193 clistp->c_cbreserved += dcbr; /* atomic w/c_cbmax */ 194 cblock_alloc_cblocks(dcbr); /* may block */ 195 } else { 196 KKASSERT(clistp->c_cbcount <= clistp->c_cbreserved); 197 if (clistp->c_cbreserved + dcbr < clistp->c_cbcount) 198 dcbr = clistp->c_cbcount - clistp->c_cbreserved; 199 clistp->c_cbreserved += dcbr; /* atomic w/c_cbmax */ 200 cblock_free_cblocks(-dcbr); /* may block */ 201 } 202 KKASSERT(clistp->c_cbreserved >= 0); 203 crit_exit(); 204 } 205 206 /* 207 * Free some cblocks from the cfreelist queue back to the 208 * system malloc pool. 209 * 210 * Must be called from within a critical section. May block. 211 */ 212 static void 213 cblock_free_cblocks(int number) 214 { 215 int i; 216 217 for (i = 0; i < number; ++i) 218 free(cblock_alloc(), M_TTYS); 219 ctotcount -= number; 220 } 221 222 /* 223 * Free the cblocks reserved for a clist. 224 */ 225 void 226 clist_free_cblocks(struct clist *clistp) 227 { 228 int cbreserved; 229 230 crit_enter(); 231 if (clistp->c_cbcount != 0) 232 panic("freeing active clist cblocks"); 233 cbreserved = clistp->c_cbreserved; 234 clistp->c_cbmax = 0; 235 clistp->c_cbreserved = 0; 236 cblock_free_cblocks(cbreserved); /* may block */ 237 crit_exit(); 238 } 239 240 /* 241 * Get a character from the head of a clist. 242 */ 243 int 244 getc(struct clist *clistp) 245 { 246 int chr = -1; 247 struct cblock *cblockp; 248 249 crit_enter(); 250 if (clistp->c_cc) { 251 KKASSERT(((intptr_t)clistp->c_cf & CROUND) != 0); 252 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND); 253 chr = (u_char)*clistp->c_cf; 254 255 /* 256 * If this char is quoted, set the flag. 257 */ 258 if (isset(cblockp->c_quote, clistp->c_cf - (char *)cblockp->c_info)) 259 chr |= TTY_QUOTE; 260 261 /* 262 * Advance to next character. 263 */ 264 clistp->c_cf++; 265 clistp->c_cc--; 266 /* 267 * If we have advanced the 'first' character pointer 268 * past the end of this cblock, advance to the next one. 269 * If there are no more characters, set the first and 270 * last pointers to NULL. In either case, free the 271 * current cblock. 272 */ 273 KKASSERT(clistp->c_cf <= (char *)(cblockp + 1)); 274 if ((clistp->c_cf == (char *)(cblockp + 1)) || 275 (clistp->c_cc == 0)) { 276 if (clistp->c_cc > 0) { 277 clistp->c_cf = cblockp->c_head.ch_next->c_info; 278 } else { 279 clistp->c_cf = clistp->c_cl = NULL; 280 } 281 cblock_free(cblockp); 282 if (--clistp->c_cbcount >= clistp->c_cbreserved) 283 ++cslushcount; 284 } 285 } 286 crit_exit(); 287 return (chr); 288 } 289 290 /* 291 * Copy 'amount' of chars, beginning at head of clist 'clistp' to 292 * destination linear buffer 'dest'. Return number of characters 293 * actually copied. 294 */ 295 int 296 q_to_b(struct clist *clistp, char *dest, int amount) 297 { 298 struct cblock *cblockp; 299 struct cblock *cblockn; 300 char *dest_orig = dest; 301 int numc; 302 303 crit_enter(); 304 while (clistp && amount && (clistp->c_cc > 0)) { 305 KKASSERT(((intptr_t)clistp->c_cf & CROUND) != 0); 306 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND); 307 cblockn = cblockp + 1; /* pointer arithmetic! */ 308 numc = min(amount, (char *)cblockn - clistp->c_cf); 309 numc = min(numc, clistp->c_cc); 310 bcopy(clistp->c_cf, dest, numc); 311 amount -= numc; 312 clistp->c_cf += numc; 313 clistp->c_cc -= numc; 314 dest += numc; 315 /* 316 * If this cblock has been emptied, advance to the next 317 * one. If there are no more characters, set the first 318 * and last pointer to NULL. In either case, free the 319 * current cblock. 320 */ 321 KKASSERT(clistp->c_cf <= (char *)cblockn); 322 if ((clistp->c_cf == (char *)cblockn) || (clistp->c_cc == 0)) { 323 if (clistp->c_cc > 0) { 324 KKASSERT(cblockp->c_head.ch_next != NULL); 325 clistp->c_cf = cblockp->c_head.ch_next->c_info; 326 } else { 327 clistp->c_cf = clistp->c_cl = NULL; 328 } 329 cblock_free(cblockp); 330 if (--clistp->c_cbcount >= clistp->c_cbreserved) 331 ++cslushcount; 332 } 333 } 334 crit_exit(); 335 return (dest - dest_orig); 336 } 337 338 /* 339 * Flush 'amount' of chars, beginning at head of clist 'clistp'. 340 */ 341 void 342 ndflush(struct clist *clistp, int amount) 343 { 344 struct cblock *cblockp; 345 struct cblock *cblockn; 346 int numc; 347 348 crit_enter(); 349 while (amount && (clistp->c_cc > 0)) { 350 KKASSERT(((intptr_t)clistp->c_cf & CROUND) != 0); 351 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND); 352 cblockn = cblockp + 1; /* pointer arithmetic! */ 353 numc = min(amount, (char *)cblockn - clistp->c_cf); 354 numc = min(numc, clistp->c_cc); 355 amount -= numc; 356 clistp->c_cf += numc; 357 clistp->c_cc -= numc; 358 /* 359 * If this cblock has been emptied, advance to the next 360 * one. If there are no more characters, set the first 361 * and last pointer to NULL. In either case, free the 362 * current cblock. 363 */ 364 KKASSERT(clistp->c_cf <= (char *)cblockn); 365 if (clistp->c_cf == (char *)cblockn || clistp->c_cc == 0) { 366 if (clistp->c_cc > 0) { 367 KKASSERT(cblockp->c_head.ch_next != NULL); 368 clistp->c_cf = cblockp->c_head.ch_next->c_info; 369 } else { 370 clistp->c_cf = clistp->c_cl = NULL; 371 } 372 cblock_free(cblockp); 373 if (--clistp->c_cbcount >= clistp->c_cbreserved) 374 ++cslushcount; 375 } 376 } 377 crit_exit(); 378 } 379 380 /* 381 * Add a character to the end of a clist. Return -1 is no 382 * more clists, or 0 for success. 383 */ 384 int 385 putc(int chr, struct clist *clistp) 386 { 387 struct cblock *cblockp; 388 389 crit_enter(); 390 391 /* 392 * Note: this section may point c_cl at the base of a cblock. This 393 * is a temporary violation of the requirements for c_cl, we 394 * increment it before returning. 395 */ 396 if (clistp->c_cl == NULL) { 397 if (clistp->c_cbreserved < 1) { 398 crit_exit(); 399 printf("putc to a clist with no reserved cblocks\n"); 400 return (-1); /* nothing done */ 401 } 402 cblockp = cblock_alloc(); 403 clistp->c_cbcount = 1; 404 clistp->c_cf = clistp->c_cl = cblockp->c_info; 405 clistp->c_cc = 0; 406 } else { 407 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 408 if (((intptr_t)clistp->c_cl & CROUND) == 0) { 409 struct cblock *prev = (cblockp - 1); 410 411 if (clistp->c_cbcount >= clistp->c_cbreserved) { 412 if (clistp->c_cbcount >= clistp->c_cbmax 413 || cslushcount <= 0) { 414 crit_exit(); 415 return (-1); 416 } 417 --cslushcount; 418 } 419 cblockp = cblock_alloc(); 420 clistp->c_cbcount++; 421 prev->c_head.ch_next = cblockp; 422 clistp->c_cl = cblockp->c_info; 423 } 424 } 425 426 /* 427 * If this character is quoted, set the quote bit, if not, clear it. 428 */ 429 if (chr & TTY_QUOTE) { 430 setbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info); 431 /* 432 * Use one of the spare quote bits to record that something 433 * may be quoted. 434 */ 435 setbit(cblockp->c_quote, CBQSIZE * NBBY - 1); 436 } else { 437 clrbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info); 438 } 439 440 *clistp->c_cl++ = chr; 441 clistp->c_cc++; 442 443 crit_exit(); 444 return (0); 445 } 446 447 /* 448 * Copy data from linear buffer to clist chain. Return the 449 * number of characters not copied. 450 */ 451 int 452 b_to_q(char *src, int amount, struct clist *clistp) 453 { 454 struct cblock *cblockp; 455 char *firstbyte, *lastbyte; 456 u_char startmask, endmask; 457 int startbit, endbit, num_between, numc; 458 459 /* 460 * Avoid allocating an initial cblock and then not using it. 461 * c_cc == 0 must imply c_cbount == 0. 462 */ 463 if (amount <= 0) 464 return (amount); 465 466 crit_enter(); 467 468 /* 469 * Note: this section may point c_cl at the base of a cblock. This 470 * is a temporary violation of the requirements for c_cl. Since 471 * amount is non-zero we will not return with it in that state. 472 */ 473 if (clistp->c_cl == NULL) { 474 if (clistp->c_cbreserved < 1) { 475 crit_exit(); 476 printf("b_to_q to a clist with no reserved cblocks.\n"); 477 return (amount); /* nothing done */ 478 } 479 cblockp = cblock_alloc(); 480 clistp->c_cbcount = 1; 481 clistp->c_cf = clistp->c_cl = cblockp->c_info; 482 clistp->c_cc = 0; 483 } else { 484 /* 485 * c_cl may legally point past the end of the block, which 486 * falls through to the 'get another cblock' code below. 487 */ 488 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 489 } 490 491 while (amount) { 492 /* 493 * Get another cblock if needed. 494 */ 495 if (((intptr_t)clistp->c_cl & CROUND) == 0) { 496 struct cblock *prev = cblockp - 1; 497 498 if (clistp->c_cbcount >= clistp->c_cbreserved) { 499 if (clistp->c_cbcount >= clistp->c_cbmax 500 || cslushcount <= 0) { 501 crit_exit(); 502 return (amount); 503 } 504 --cslushcount; 505 } 506 cblockp = cblock_alloc(); 507 clistp->c_cbcount++; 508 prev->c_head.ch_next = cblockp; 509 clistp->c_cl = cblockp->c_info; 510 } 511 512 /* 513 * Copy a chunk of the linear buffer up to the end 514 * of this cblock. 515 */ 516 numc = min(amount, (char *)(cblockp + 1) - clistp->c_cl); 517 bcopy(src, clistp->c_cl, numc); 518 519 /* 520 * Clear quote bits if they aren't known to be clear. 521 * The following could probably be made into a separate 522 * "bitzero()" routine, but why bother? 523 */ 524 if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1)) { 525 startbit = clistp->c_cl - (char *)cblockp->c_info; 526 endbit = startbit + numc - 1; 527 528 firstbyte = (u_char *)cblockp->c_quote + (startbit / NBBY); 529 lastbyte = (u_char *)cblockp->c_quote + (endbit / NBBY); 530 531 /* 532 * Calculate mask of bits to preserve in first and 533 * last bytes. 534 */ 535 startmask = NBBY - (startbit % NBBY); 536 startmask = 0xff >> startmask; 537 endmask = (endbit % NBBY); 538 endmask = 0xff << (endmask + 1); 539 540 if (firstbyte != lastbyte) { 541 *firstbyte &= startmask; 542 *lastbyte &= endmask; 543 544 num_between = lastbyte - firstbyte - 1; 545 if (num_between) 546 bzero(firstbyte + 1, num_between); 547 } else { 548 *firstbyte &= (startmask | endmask); 549 } 550 } 551 552 /* 553 * ...and update pointer for the next chunk. 554 */ 555 src += numc; 556 clistp->c_cl += numc; 557 clistp->c_cc += numc; 558 amount -= numc; 559 /* 560 * If we go through the loop again, it's always 561 * for data in the next cblock, so by adding one (cblock), 562 * (which makes the pointer 1 beyond the end of this 563 * cblock) we prepare for the assignment of 'prev' 564 * above. 565 */ 566 ++cblockp; 567 } 568 crit_exit(); 569 return (amount); 570 } 571 572 /* 573 * Get the next character in the clist. Store it at dst. Don't 574 * advance any clist pointers, but return a pointer to the next 575 * character position. 576 * 577 * Must be called at spltty(). This routine may not run in a critical 578 * section and so may not call the cblock allocator/deallocator. 579 */ 580 char * 581 nextc(struct clist *clistp, char *cp, int *dst) 582 { 583 struct cblock *cblockp; 584 585 ++cp; 586 /* 587 * See if the next character is beyond the end of 588 * the clist. 589 */ 590 if (clistp->c_cc && (cp != clistp->c_cl)) { 591 /* 592 * If the next character is beyond the end of this 593 * cblock, advance to the next cblock. 594 */ 595 if (((intptr_t)cp & CROUND) == 0) 596 cp = ((struct cblock *)cp - 1)->c_head.ch_next->c_info; 597 cblockp = (struct cblock *)((intptr_t)cp & ~CROUND); 598 599 /* 600 * Get the character. Set the quote flag if this character 601 * is quoted. 602 */ 603 *dst = (u_char)*cp | (isset(cblockp->c_quote, cp - (char *)cblockp->c_info) ? TTY_QUOTE : 0); 604 605 return (cp); 606 } 607 608 return (NULL); 609 } 610 611 /* 612 * "Unput" a character from a clist. 613 */ 614 int 615 unputc(struct clist *clistp) 616 { 617 struct cblock *cblockp = 0, *cbp = 0; 618 int chr = -1; 619 620 crit_enter(); 621 622 if (clistp->c_cc) { 623 /* 624 * note that clistp->c_cl will never point at the base 625 * of a cblock (cblock->c_info) (see assert this later on), 626 * but it may point past the end of one. We temporarily 627 * violate this in the decrement below but then we fix it up. 628 */ 629 --clistp->c_cc; 630 --clistp->c_cl; 631 632 chr = (u_char)*clistp->c_cl; 633 634 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 635 636 /* 637 * Set quote flag if this character was quoted. 638 */ 639 if (isset(cblockp->c_quote, (u_char *)clistp->c_cl - cblockp->c_info)) 640 chr |= TTY_QUOTE; 641 642 /* 643 * If all of the characters have been unput in this 644 * cblock, then find the previous one and free this 645 * one. 646 * 647 * if c_cc is 0 clistp->c_cl may end up pointing at 648 * cblockp->c_info, which is illegal, but the case will be 649 * taken care of near the end of the routine. Otherwise 650 * there *MUST* be another cblock, find it. 651 */ 652 KKASSERT(clistp->c_cl >= (char *)cblockp->c_info); 653 if (clistp->c_cc && (clistp->c_cl == (char *)cblockp->c_info)) { 654 cbp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND); 655 656 while (cbp->c_head.ch_next != cblockp) 657 cbp = cbp->c_head.ch_next; 658 cbp->c_head.ch_next = NULL; 659 660 /* 661 * When the previous cblock is at the end, the 'last' 662 * pointer always points (invalidly) one past. 663 */ 664 clistp->c_cl = (char *)(cbp + 1); 665 cblock_free(cblockp); 666 if (--clistp->c_cbcount >= clistp->c_cbreserved) 667 ++cslushcount; 668 } 669 } 670 671 /* 672 * If there are no more characters on the list, then 673 * free the last cblock. It should not be possible for c->cl 674 * to be pointing past the end of a block due to our decrement 675 * of it way above. 676 */ 677 if (clistp->c_cc == 0 && clistp->c_cl) { 678 KKASSERT(((intptr_t)clistp->c_cl & CROUND) != 0); 679 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 680 cblock_free(cblockp); 681 if (--clistp->c_cbcount >= clistp->c_cbreserved) 682 ++cslushcount; 683 clistp->c_cf = clistp->c_cl = NULL; 684 } 685 686 crit_exit(); 687 return (chr); 688 } 689 690 /* 691 * Move characters in source clist to destination clist, 692 * preserving quote bits. 693 */ 694 void 695 catq(struct clist *src_clistp, struct clist *dest_clistp) 696 { 697 int chr; 698 699 crit_enter(); 700 /* 701 * If the destination clist is empty (has no cblocks atttached), 702 * and there are no possible complications with the resource counters, 703 * then we simply assign the current clist to the destination. 704 */ 705 if (!dest_clistp->c_cf 706 && src_clistp->c_cbcount <= src_clistp->c_cbmax 707 && src_clistp->c_cbcount <= dest_clistp->c_cbmax) { 708 dest_clistp->c_cf = src_clistp->c_cf; 709 dest_clistp->c_cl = src_clistp->c_cl; 710 src_clistp->c_cf = src_clistp->c_cl = NULL; 711 712 dest_clistp->c_cc = src_clistp->c_cc; 713 src_clistp->c_cc = 0; 714 dest_clistp->c_cbcount = src_clistp->c_cbcount; 715 src_clistp->c_cbcount = 0; 716 717 crit_exit(); 718 return; 719 } 720 crit_exit(); 721 722 /* 723 * XXX This should probably be optimized to more than one 724 * character at a time. 725 */ 726 while ((chr = getc(src_clistp)) != -1) 727 putc(chr, dest_clistp); 728 } 729