1/*	$NetBSD: tty_subr.c,v 1.43 2019/12/27 09:41:51 msaitoh Exp $	*/
2
3/*
4 * Copyright (c) 1993, 1994 Theo de Raadt
5 * All rights reserved.
6 *
7 * Per Lindqvist <pgd@compuram.bbt.se> supplied an almost fully working
8 * set of true clist functions that this is very loosely based on.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__KERNEL_RCSID(0, "$NetBSD: tty_subr.c,v 1.43 2019/12/27 09:41:51 msaitoh Exp $");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/buf.h>
37#include <sys/ioctl.h>
38#include <sys/tty.h>
39#include <sys/kmem.h>
40
41/*
42 * At compile time, choose:
43 * There are two ways the TTY_QUOTE bit can be stored. If QBITS is
44 * defined we allocate an array of bits -- 1/8th as much memory but
45 * setbit(), clrbit(), and isset() take more CPU. If QBITS is
46 * undefined, we just use an array of bytes.
47 *
48 * If TTY_QUOTE functionality isn't required by a line discipline,
49 * it can free c_cq and set it to NULL. This speeds things up,
50 * and also does not use any extra memory. This is useful for (say)
51 * a SLIP line discipline that wants a 32K ring buffer for data
52 * but doesn't need quoting.
53 */
54#define QBITS
55
56#ifdef QBITS
57#define QMEM(n)		((((n)-1)/NBBY)+1)
58#else
59#define QMEM(n)		(n)
60#endif
61
62#ifdef QBITS
63static void	clrbits(u_char *, unsigned int, unsigned int);
64#endif
65
66/*
67 * Initialize a particular clist. Ok, they are really ring buffers,
68 * of the specified length, with/without quoting support.
69 */
70int
71clalloc(struct clist *clp, int size, int quot)
72{
73
74	clp->c_cs = kmem_zalloc(size, KM_SLEEP);
75	if (quot)
76		clp->c_cq = kmem_zalloc(QMEM(size), KM_SLEEP);
77	else
78		clp->c_cq = NULL;
79
80	clp->c_cf = clp->c_cl = NULL;
81	clp->c_ce = clp->c_cs + size;
82	clp->c_cn = size;
83	clp->c_cc = 0;
84
85	return (0);
86}
87
88void
89clfree(struct clist *clp)
90{
91	if (clp->c_cs)
92		kmem_free(clp->c_cs, clp->c_cn);
93	if (clp->c_cq)
94		kmem_free(clp->c_cq, QMEM(clp->c_cn));
95	clp->c_cs = clp->c_cq = NULL;
96}
97
98/*
99 * Get a character from a clist.
100 */
101int
102getc(struct clist *clp)
103{
104	int c = -1;
105	int s;
106
107	s = spltty();
108	if (clp->c_cc == 0)
109		goto out;
110
111	c = *clp->c_cf & 0xff;
112	if (clp->c_cq) {
113#ifdef QBITS
114		if (isset(clp->c_cq, clp->c_cf - clp->c_cs) )
115			c |= TTY_QUOTE;
116#else
117		if (*(clp->c_cf - clp->c_cs + clp->c_cq))
118			c |= TTY_QUOTE;
119#endif
120	}
121	*clp->c_cf = 0; /* wipe out to avoid information disclosure */
122	if (++clp->c_cf == clp->c_ce)
123		clp->c_cf = clp->c_cs;
124	if (--clp->c_cc == 0)
125		clp->c_cf = clp->c_cl = (u_char *)0;
126out:
127	splx(s);
128	return c;
129}
130
131/*
132 * Copy clist to buffer.
133 * Return number of bytes moved.
134 */
135int
136q_to_b(struct clist *clp, u_char *cp, int count)
137{
138	int cc;
139	u_char *p = cp;
140	int s;
141
142	s = spltty();
143	/* optimize this while loop */
144	while (count > 0 && clp->c_cc > 0) {
145		cc = clp->c_cl - clp->c_cf;
146		if (clp->c_cf >= clp->c_cl)
147			cc = clp->c_ce - clp->c_cf;
148		if (cc > count)
149			cc = count;
150		memcpy(p, clp->c_cf, cc);
151		count -= cc;
152		p += cc;
153		clp->c_cc -= cc;
154		clp->c_cf += cc;
155		if (clp->c_cf == clp->c_ce)
156			clp->c_cf = clp->c_cs;
157	}
158	if (clp->c_cc == 0)
159		clp->c_cf = clp->c_cl = (u_char *)0;
160	splx(s);
161	return p - cp;
162}
163
164/*
165 * Return count of contiguous characters in clist.
166 * Stop counting if flag&character is non-null.
167 */
168int
169ndqb(struct clist *clp, int flag)
170{
171	int count = 0;
172	int i;
173	int cc;
174	int s;
175
176	s = spltty();
177	if ((cc = clp->c_cc) == 0)
178		goto out;
179
180	if (flag == 0) {
181		count = clp->c_cl - clp->c_cf;
182		if (count <= 0)
183			count = clp->c_ce - clp->c_cf;
184		goto out;
185	}
186
187	i = clp->c_cf - clp->c_cs;
188	if (flag & TTY_QUOTE) {
189		while (cc-- > 0 && !(clp->c_cs[i++] & (flag & ~TTY_QUOTE) ||
190		    isset(clp->c_cq, i))) {
191			count++;
192			if (i == clp->c_cn)
193				break;
194		}
195	} else {
196		while (cc-- > 0 && !(clp->c_cs[i++] & flag)) {
197			count++;
198			if (i == clp->c_cn)
199				break;
200		}
201	}
202out:
203	splx(s);
204	return count;
205}
206
207/*
208 * Flush count bytes from clist.
209 */
210void
211ndflush(struct clist *clp, int count)
212{
213	int cc;
214	int s;
215
216	s = spltty();
217	if (count == clp->c_cc) {
218		clp->c_cc = 0;
219		clp->c_cf = clp->c_cl = (u_char *)0;
220		goto out;
221	}
222	/* optimize this while loop */
223	while (count > 0 && clp->c_cc > 0) {
224		cc = clp->c_cl - clp->c_cf;
225		if (clp->c_cf >= clp->c_cl)
226			cc = clp->c_ce - clp->c_cf;
227		if (cc > count)
228			cc = count;
229		count -= cc;
230		clp->c_cc -= cc;
231		clp->c_cf += cc;
232		if (clp->c_cf == clp->c_ce)
233			clp->c_cf = clp->c_cs;
234	}
235	if (clp->c_cc == 0)
236		clp->c_cf = clp->c_cl = (u_char *)0;
237out:
238	splx(s);
239}
240
241/*
242 * Put a character into the output queue.
243 */
244int
245putc(int c, struct clist *clp)
246{
247	int i;
248	int s;
249
250	s = spltty();
251	if (clp->c_cc == clp->c_cn)
252		goto out;
253
254	if (clp->c_cc == 0) {
255		if (!clp->c_cs) {
256#if defined(DIAGNOSTIC) || 1
257			printf("putc: required clalloc\n");
258#endif
259			if (clalloc(clp, clp->c_cn, 1)) {
260out:
261				splx(s);
262				return -1;
263			}
264		}
265		clp->c_cf = clp->c_cl = clp->c_cs;
266	}
267
268	*clp->c_cl = c & 0xff;
269	i = clp->c_cl - clp->c_cs;
270	if (clp->c_cq) {
271#ifdef QBITS
272		if (c & TTY_QUOTE)
273			setbit(clp->c_cq, i);
274		else
275			clrbit(clp->c_cq, i);
276#else
277		q = clp->c_cq + i;
278		*q = (c & TTY_QUOTE) ? 1 : 0;
279#endif
280	}
281	clp->c_cc++;
282	clp->c_cl++;
283	if (clp->c_cl == clp->c_ce)
284		clp->c_cl = clp->c_cs;
285	splx(s);
286	return 0;
287}
288
289#ifdef QBITS
290/*
291 * optimized version of
292 *
293 * for (i = 0; i < len; i++)
294 *	clrbit(cp, off + len);
295 */
296static void
297clrbits(u_char *cp, unsigned int off, unsigned int len)
298{
299	unsigned int sbi, ebi;
300	u_char *scp, *ecp;
301	unsigned int end;
302	unsigned char mask;
303
304	scp = cp + off / NBBY;
305	sbi = off % NBBY;
306	end = off + len + NBBY - 1;
307	ecp = cp + end / NBBY - 1;
308	ebi = end % NBBY + 1;
309	if (scp >= ecp) {
310		mask = ((1 << len) - 1) << sbi;
311		*scp &= ~mask;
312	} else {
313		mask = (1 << sbi) - 1;
314		*scp++ &= mask;
315
316		mask = (1 << ebi) - 1;
317		*ecp &= ~mask;
318
319		while (scp < ecp)
320			*scp++ = 0x00;
321	}
322}
323#endif
324
325/*
326 * Copy buffer to clist.
327 * Return number of bytes not transferred.
328 */
329int
330b_to_q(const u_char *cp, int count, struct clist *clp)
331{
332	int cc;
333	const u_char *p = cp;
334	int s;
335
336	if (count <= 0)
337		return 0;
338
339	s = spltty();
340	if (clp->c_cc == clp->c_cn)
341		goto out;
342
343	if (clp->c_cc == 0) {
344		if (!clp->c_cs) {
345#if defined(DIAGNOSTIC) || 1
346			printf("b_to_q: required clalloc\n");
347#endif
348			if (clalloc(clp, clp->c_cn, 1))
349				goto out;
350		}
351		clp->c_cf = clp->c_cl = clp->c_cs;
352	}
353
354	/* optimize this while loop */
355	while (count > 0 && clp->c_cc < clp->c_cn) {
356		cc = clp->c_ce - clp->c_cl;
357		if (clp->c_cf > clp->c_cl)
358			cc = clp->c_cf - clp->c_cl;
359		if (cc > count)
360			cc = count;
361		memcpy(clp->c_cl, p, cc);
362		if (clp->c_cq) {
363#ifdef QBITS
364			clrbits(clp->c_cq, clp->c_cl - clp->c_cs, cc);
365#else
366			memset(clp->c_cl - clp->c_cs + clp->c_cq, 0, cc);
367#endif
368		}
369		p += cc;
370		count -= cc;
371		clp->c_cc += cc;
372		clp->c_cl += cc;
373		if (clp->c_cl == clp->c_ce)
374			clp->c_cl = clp->c_cs;
375	}
376out:
377	splx(s);
378	return count;
379}
380
381static int tty_global_cc;
382
383/*
384 * Given a non-NULL pointer into the clist return the pointer
385 * to the next character in the list or return NULL if no more chars.
386 *
387 * Callers must not allow getc's to happen between firstc's and getc's
388 * so that the pointer becomes invalid.  Note that interrupts are NOT
389 * masked.
390 */
391u_char *
392nextc(struct clist *clp, u_char *cp, int *c)
393{
394
395	if (clp->c_cf == cp) {
396		/*
397		 * First time initialization.
398		 */
399		tty_global_cc = clp->c_cc;
400	}
401	if (tty_global_cc == 0 || cp == NULL)
402		return NULL;
403	if (--tty_global_cc == 0)
404		return NULL;
405	if (++cp == clp->c_ce)
406		cp = clp->c_cs;
407	*c = *cp & 0xff;
408	if (clp->c_cq) {
409#ifdef QBITS
410		if (isset(clp->c_cq, cp - clp->c_cs))
411			*c |= TTY_QUOTE;
412#else
413		if (*(clp->c_cf - clp->c_cs + clp->c_cq))
414			*c |= TTY_QUOTE;
415#endif
416	}
417	return cp;
418}
419
420/*
421 * Given a non-NULL pointer into the clist return the pointer
422 * to the first character in the list or return NULL if no more chars.
423 *
424 * Callers must not allow getc's to happen between firstc's and getc's
425 * so that the pointer becomes invalid.  Note that interrupts are NOT
426 * masked.
427 *
428 * *c is set to the NEXT character
429 */
430u_char *
431firstc(struct clist *clp, int *c)
432{
433	u_char *cp;
434
435	tty_global_cc = clp->c_cc;
436	if (tty_global_cc == 0)
437		return NULL;
438	cp = clp->c_cf;
439	*c = *cp & 0xff;
440	if (clp->c_cq) {
441#ifdef QBITS
442		if (isset(clp->c_cq, cp - clp->c_cs))
443			*c |= TTY_QUOTE;
444#else
445		if (*(cp - clp->c_cs + clp->c_cq))
446			*c |= TTY_QUOTE;
447#endif
448	}
449	return clp->c_cf;
450}
451
452/*
453 * Remove the last character in the clist and return it.
454 */
455int
456unputc(struct clist *clp)
457{
458	unsigned int c = -1;
459	int s;
460
461	s = spltty();
462	if (clp->c_cc == 0)
463		goto out;
464
465	if (clp->c_cl == clp->c_cs)
466		clp->c_cl = clp->c_ce - 1;
467	else
468		--clp->c_cl;
469	clp->c_cc--;
470
471	c = *clp->c_cl & 0xff;
472	if (clp->c_cq) {
473#ifdef QBITS
474		if (isset(clp->c_cq, clp->c_cl - clp->c_cs))
475			c |= TTY_QUOTE;
476#else
477		if (*(clp->c_cf - clp->c_cs + clp->c_cq))
478			c |= TTY_QUOTE;
479#endif
480	}
481	if (clp->c_cc == 0)
482		clp->c_cf = clp->c_cl = (u_char *)0;
483out:
484	splx(s);
485	return c;
486}
487
488/*
489 * Put the chars in the from queue on the end of the to queue.
490 */
491void
492catq(struct clist *from, struct clist *to)
493{
494	int c;
495
496	while ((c = getc(from)) != -1)
497		putc(c, to);
498}
499