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