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