Disallow writes to filesystems mounted read-only via NULLFS. In this case
[dragonfly.git] / sys / kern / tty_subr.c
CommitLineData
984263bc
MD
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 $
efda3bd0 28 * $DragonFly: src/sys/kern/tty_subr.c,v 1.8 2006/09/05 00:55:45 dillon Exp $
984263bc
MD
29 */
30
31/*
32 * clist support routines
a38e57d5
MD
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.
984263bc
MD
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>
a38e57d5 51#include <sys/thread2.h>
984263bc 52
402ed7e1 53static void clist_init (void *);
984263bc
MD
54SYSINIT(clist, SI_SUB_CLIST, SI_ORDER_FIRST, clist_init, NULL)
55
56static struct cblock *cfreelist = 0;
57int cfreecount = 0;
58static int cslushcount;
59static int ctotcount;
60
61#ifndef INITIAL_CBLOCKS
62#define INITIAL_CBLOCKS 50
63#endif
64
402ed7e1
RG
65static struct cblock *cblock_alloc (void);
66static void cblock_alloc_cblocks (int number);
67static void cblock_free (struct cblock *cblockp);
68static void cblock_free_cblocks (int number);
984263bc
MD
69
70#include "opt_ddb.h"
71#ifdef DDB
72#include <ddb/ddb.h>
73
74DB_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*/
89static void
a38e57d5 90clist_init(void *dummy)
984263bc
MD
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);
a38e57d5 101 KKASSERT(sizeof(struct cblock) == CBLOCK);
984263bc
MD
102}
103
104/*
105 * Remove a cblock from the cfreelist queue and return a pointer
106 * to it.
a38e57d5
MD
107 *
108 * May not block.
984263bc 109 */
a38e57d5
MD
110static struct cblock *
111cblock_alloc(void)
984263bc
MD
112{
113 struct cblock *cblockp;
114
115 cblockp = cfreelist;
116 if (cblockp == NULL)
117 panic("clist reservation botch");
a38e57d5
MD
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;
984263bc
MD
122 cfreecount -= CBSIZE;
123 return (cblockp);
124}
125
126/*
127 * Add a cblock to the cfreelist queue.
a38e57d5
MD
128 *
129 * May not block, must be called in a critical section
984263bc 130 */
a38e57d5
MD
131static void
132cblock_free(struct cblock *cblockp)
984263bc
MD
133{
134 if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1))
135 bzero(cblockp->c_quote, sizeof cblockp->c_quote);
a38e57d5
MD
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;
984263bc
MD
139 cfreelist = cblockp;
140 cfreecount += CBSIZE;
141}
142
143/*
144 * Allocate some cblocks for the cfreelist queue.
a38e57d5 145 *
0c9bc5ef 146 * This routine may block, but still must be called in a critical section
984263bc
MD
147 */
148static void
a38e57d5 149cblock_alloc_cblocks(int number)
984263bc
MD
150{
151 int i;
152 struct cblock *cbp;
153
154 for (i = 0; i < number; ++i) {
efda3bd0 155 cbp = kmalloc(sizeof *cbp, M_TTYS, M_NOWAIT);
984263bc
MD
156 if (cbp == NULL) {
157 printf(
158"clist_alloc_cblocks: M_NOWAIT malloc failed, trying M_WAITOK\n");
efda3bd0 159 cbp = kmalloc(sizeof *cbp, M_TTYS, M_WAITOK);
984263bc 160 }
a38e57d5 161 KKASSERT(((intptr_t)cbp & CROUND) == 0);
984263bc
MD
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);
a38e57d5 167 cbp->c_head.ch_magic = CLIST_MAGIC_USED;
984263bc
MD
168 cblock_free(cbp);
169 }
170 ctotcount += number;
171}
172
173/*
a38e57d5 174 * Set the cblock allocation policy for a clist.
984263bc
MD
175 */
176void
a38e57d5 177clist_alloc_cblocks(struct clist *clistp, int ccmax, int ccreserved)
984263bc
MD
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
a38e57d5 189 crit_enter();
984263bc
MD
190 clistp->c_cbmax = roundup(ccmax, CBSIZE) / CBSIZE;
191 dcbr = roundup(ccreserved, CBSIZE) / CBSIZE - clistp->c_cbreserved;
a38e57d5
MD
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);
984263bc
MD
197 if (clistp->c_cbreserved + dcbr < clistp->c_cbcount)
198 dcbr = clistp->c_cbcount - clistp->c_cbreserved;
a38e57d5
MD
199 clistp->c_cbreserved += dcbr; /* atomic w/c_cbmax */
200 cblock_free_cblocks(-dcbr); /* may block */
984263bc 201 }
a38e57d5
MD
202 KKASSERT(clistp->c_cbreserved >= 0);
203 crit_exit();
984263bc
MD
204}
205
206/*
207 * Free some cblocks from the cfreelist queue back to the
208 * system malloc pool.
a38e57d5
MD
209 *
210 * Must be called from within a critical section. May block.
984263bc
MD
211 */
212static void
a38e57d5 213cblock_free_cblocks(int number)
984263bc
MD
214{
215 int i;
216
217 for (i = 0; i < number; ++i)
efda3bd0 218 kfree(cblock_alloc(), M_TTYS);
984263bc
MD
219 ctotcount -= number;
220}
221
222/*
223 * Free the cblocks reserved for a clist.
984263bc
MD
224 */
225void
a38e57d5 226clist_free_cblocks(struct clist *clistp)
984263bc 227{
a38e57d5
MD
228 int cbreserved;
229
230 crit_enter();
984263bc
MD
231 if (clistp->c_cbcount != 0)
232 panic("freeing active clist cblocks");
a38e57d5 233 cbreserved = clistp->c_cbreserved;
984263bc
MD
234 clistp->c_cbmax = 0;
235 clistp->c_cbreserved = 0;
a38e57d5
MD
236 cblock_free_cblocks(cbreserved); /* may block */
237 crit_exit();
984263bc
MD
238}
239
240/*
241 * Get a character from the head of a clist.
242 */
243int
0ced1954 244clist_getc(struct clist *clistp)
984263bc
MD
245{
246 int chr = -1;
984263bc
MD
247 struct cblock *cblockp;
248
a38e57d5 249 crit_enter();
984263bc 250 if (clistp->c_cc) {
a38e57d5 251 KKASSERT(((intptr_t)clistp->c_cf & CROUND) != 0);
984263bc
MD
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 */
a38e57d5
MD
273 KKASSERT(clistp->c_cf <= (char *)(cblockp + 1));
274 if ((clistp->c_cf == (char *)(cblockp + 1)) ||
275 (clistp->c_cc == 0)) {
984263bc 276 if (clistp->c_cc > 0) {
a38e57d5 277 clistp->c_cf = cblockp->c_head.ch_next->c_info;
984263bc
MD
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 }
a38e57d5 286 crit_exit();
984263bc
MD
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 */
295int
a38e57d5 296q_to_b(struct clist *clistp, char *dest, int amount)
984263bc
MD
297{
298 struct cblock *cblockp;
299 struct cblock *cblockn;
300 char *dest_orig = dest;
301 int numc;
984263bc 302
a38e57d5 303 crit_enter();
984263bc 304 while (clistp && amount && (clistp->c_cc > 0)) {
a38e57d5 305 KKASSERT(((intptr_t)clistp->c_cf & CROUND) != 0);
984263bc
MD
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 */
a38e57d5
MD
321 KKASSERT(clistp->c_cf <= (char *)cblockn);
322 if ((clistp->c_cf == (char *)cblockn) || (clistp->c_cc == 0)) {
984263bc 323 if (clistp->c_cc > 0) {
a38e57d5
MD
324 KKASSERT(cblockp->c_head.ch_next != NULL);
325 clistp->c_cf = cblockp->c_head.ch_next->c_info;
984263bc
MD
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 }
a38e57d5 334 crit_exit();
984263bc
MD
335 return (dest - dest_orig);
336}
337
338/*
339 * Flush 'amount' of chars, beginning at head of clist 'clistp'.
340 */
341void
a38e57d5 342ndflush(struct clist *clistp, int amount)
984263bc
MD
343{
344 struct cblock *cblockp;
345 struct cblock *cblockn;
346 int numc;
984263bc 347
a38e57d5 348 crit_enter();
984263bc 349 while (amount && (clistp->c_cc > 0)) {
a38e57d5 350 KKASSERT(((intptr_t)clistp->c_cf & CROUND) != 0);
984263bc
MD
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 */
a38e57d5
MD
364 KKASSERT(clistp->c_cf <= (char *)cblockn);
365 if (clistp->c_cf == (char *)cblockn || clistp->c_cc == 0) {
984263bc 366 if (clistp->c_cc > 0) {
a38e57d5
MD
367 KKASSERT(cblockp->c_head.ch_next != NULL);
368 clistp->c_cf = cblockp->c_head.ch_next->c_info;
984263bc
MD
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 }
a38e57d5 377 crit_exit();
984263bc
MD
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 */
384int
0ced1954 385clist_putc(int chr, struct clist *clistp)
984263bc
MD
386{
387 struct cblock *cblockp;
984263bc 388
a38e57d5 389 crit_enter();
984263bc 390
a38e57d5
MD
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 */
984263bc
MD
396 if (clistp->c_cl == NULL) {
397 if (clistp->c_cbreserved < 1) {
a38e57d5 398 crit_exit();
984263bc
MD
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) {
a38e57d5 414 crit_exit();
984263bc
MD
415 return (-1);
416 }
417 --cslushcount;
418 }
419 cblockp = cblock_alloc();
420 clistp->c_cbcount++;
a38e57d5 421 prev->c_head.ch_next = cblockp;
984263bc
MD
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);
a38e57d5 436 } else {
984263bc 437 clrbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
a38e57d5 438 }
984263bc
MD
439
440 *clistp->c_cl++ = chr;
441 clistp->c_cc++;
442
a38e57d5 443 crit_exit();
984263bc
MD
444 return (0);
445}
446
447/*
448 * Copy data from linear buffer to clist chain. Return the
449 * number of characters not copied.
450 */
451int
a38e57d5 452b_to_q(char *src, int amount, struct clist *clistp)
984263bc
MD
453{
454 struct cblock *cblockp;
455 char *firstbyte, *lastbyte;
456 u_char startmask, endmask;
457 int startbit, endbit, num_between, numc;
984263bc
MD
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
a38e57d5 466 crit_enter();
984263bc
MD
467
468 /*
a38e57d5
MD
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.
984263bc
MD
472 */
473 if (clistp->c_cl == NULL) {
474 if (clistp->c_cbreserved < 1) {
a38e57d5 475 crit_exit();
984263bc
MD
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 {
a38e57d5
MD
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 */
984263bc
MD
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) {
a38e57d5 501 crit_exit();
984263bc
MD
502 return (amount);
503 }
504 --cslushcount;
505 }
506 cblockp = cblock_alloc();
507 clistp->c_cbcount++;
a38e57d5 508 prev->c_head.ch_next = cblockp;
984263bc
MD
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.
7a44d1cb 521 * The following could probably be made into a separate
984263bc
MD
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 */
a38e57d5 566 ++cblockp;
984263bc 567 }
a38e57d5 568 crit_exit();
984263bc
MD
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.
a38e57d5
MD
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.
984263bc
MD
579 */
580char *
a38e57d5 581nextc(struct clist *clistp, char *cp, int *dst)
984263bc
MD
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)
a38e57d5 596 cp = ((struct cblock *)cp - 1)->c_head.ch_next->c_info;
984263bc
MD
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 */
614int
0ced1954 615clist_unputc(struct clist *clistp)
984263bc
MD
616{
617 struct cblock *cblockp = 0, *cbp = 0;
984263bc
MD
618 int chr = -1;
619
a38e57d5 620 crit_enter();
984263bc
MD
621
622 if (clistp->c_cc) {
a38e57d5
MD
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 */
984263bc
MD
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.
a38e57d5
MD
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.
984263bc 651 */
a38e57d5
MD
652 KKASSERT(clistp->c_cl >= (char *)cblockp->c_info);
653 if (clistp->c_cc && (clistp->c_cl == (char *)cblockp->c_info)) {
984263bc
MD
654 cbp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
655
a38e57d5
MD
656 while (cbp->c_head.ch_next != cblockp)
657 cbp = cbp->c_head.ch_next;
658 cbp->c_head.ch_next = NULL;
984263bc
MD
659
660 /*
661 * When the previous cblock is at the end, the 'last'
662 * pointer always points (invalidly) one past.
663 */
a38e57d5 664 clistp->c_cl = (char *)(cbp + 1);
984263bc
MD
665 cblock_free(cblockp);
666 if (--clistp->c_cbcount >= clistp->c_cbreserved)
667 ++cslushcount;
984263bc
MD
668 }
669 }
670
671 /*
672 * If there are no more characters on the list, then
a38e57d5
MD
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.
984263bc 676 */
a38e57d5
MD
677 if (clistp->c_cc == 0 && clistp->c_cl) {
678 KKASSERT(((intptr_t)clistp->c_cl & CROUND) != 0);
984263bc
MD
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
a38e57d5 686 crit_exit();
984263bc
MD
687 return (chr);
688}
689
690/*
691 * Move characters in source clist to destination clist,
692 * preserving quote bits.
693 */
694void
a38e57d5 695catq(struct clist *src_clistp, struct clist *dest_clistp)
984263bc 696{
a38e57d5 697 int chr;
984263bc 698
a38e57d5 699 crit_enter();
984263bc
MD
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
a38e57d5 717 crit_exit();
984263bc
MD
718 return;
719 }
a38e57d5 720 crit_exit();
984263bc
MD
721
722 /*
723 * XXX This should probably be optimized to more than one
724 * character at a time.
725 */
0ced1954
MD
726 while ((chr = clist_getc(src_clistp)) != -1)
727 clist_putc(chr, dest_clistp);
984263bc 728}