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