xref: /dflybsd-src/sys/kern/tty_subr.c (revision 2efb75f3055c1746efc358d68dbc2bf526faaf61)
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