f41e28620860c4f869be6c231348fcb65dcaef08
[dragonfly.git] / sys / kern / tty_subr.c
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 }