xref: /plan9/sys/src/cmd/unix/drawterm/libc/fmtquote.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include <u.h>
2 #include <libc.h>
3 #include "fmtdef.h"
4 
5 /*
6  * How many bytes of output UTF will be produced by quoting (if necessary) this string?
7  * How many runes? How much of the input will be consumed?
8  * The parameter q is filled in by __quotesetup.
9  * The string may be UTF or Runes (s or r).
10  * Return count does not include NUL.
11  * Terminate the scan at the first of:
12  *	NUL in input
13  *	count exceeded in input
14  *	count exceeded on output
15  * *ninp is set to number of input bytes accepted.
16  * nin may be <0 initially, to avoid checking input by count.
17  */
18 void
__quotesetup(char * s,Rune * r,int nin,int nout,Quoteinfo * q,int sharp,int runesout)19 __quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout)
20 {
21 	int w;
22 	Rune c;
23 
24 	q->quoted = 0;
25 	q->nbytesout = 0;
26 	q->nrunesout = 0;
27 	q->nbytesin = 0;
28 	q->nrunesin = 0;
29 	if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){
30 		if(nout < 2)
31 			return;
32 		q->quoted = 1;
33 		q->nbytesout = 2;
34 		q->nrunesout = 2;
35 	}
36 	for(; nin!=0; nin--){
37 		if(s)
38 			w = chartorune(&c, s);
39 		else{
40 			c = *r;
41 			w = runelen(c);
42 		}
43 
44 		if(c == '\0')
45 			break;
46 		if(runesout){
47 			if(q->nrunesout+1 > nout)
48 				break;
49 		}else{
50 			if(q->nbytesout+w > nout)
51 				break;
52 		}
53 
54 		if((c <= L' ') || (c == L'\'') || (fmtdoquote!=0 && fmtdoquote(c))){
55 			if(!q->quoted){
56 				if(runesout){
57 					if(1+q->nrunesout+1+1 > nout)	/* no room for quotes */
58 						break;
59 				}else{
60 					if(1+q->nbytesout+w+1 > nout)	/* no room for quotes */
61 						break;
62 				}
63 				q->nrunesout += 2;	/* include quotes */
64 				q->nbytesout += 2;	/* include quotes */
65 				q->quoted = 1;
66 			}
67 			if(c == '\'')	{
68 				if(runesout){
69 					if(1+q->nrunesout+1 > nout)	/* no room for quotes */
70 						break;
71 				}else{
72 					if(1+q->nbytesout+w > nout)	/* no room for quotes */
73 						break;
74 				}
75 				q->nbytesout++;
76 				q->nrunesout++;	/* quotes reproduce as two characters */
77 			}
78 		}
79 
80 		/* advance input */
81 		if(s)
82 			s += w;
83 		else
84 			r++;
85 		q->nbytesin += w;
86 		q->nrunesin++;
87 
88 		/* advance output */
89 		q->nbytesout += w;
90 		q->nrunesout++;
91 	}
92 }
93 
94 static int
qstrfmt(char * sin,Rune * rin,Quoteinfo * q,Fmt * f)95 qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f)
96 {
97 	Rune r, *rm, *rme;
98 	char *t, *s, *m, *me;
99 	Rune *rt, *rs;
100 	ulong fl;
101 	int nc, w;
102 
103 	m = sin;
104 	me = m + q->nbytesin;
105 	rm = rin;
106 	rme = rm + q->nrunesin;
107 
108 	w = f->width;
109 	fl = f->flags;
110 	if(f->runes){
111 		if(!(fl & FmtLeft) && __rfmtpad(f, w - q->nrunesout) < 0)
112 			return -1;
113 	}else{
114 		if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0)
115 			return -1;
116 	}
117 	t = (char*)f->to;
118 	s = (char*)f->stop;
119 	rt = (Rune*)f->to;
120 	rs = (Rune*)f->stop;
121 	if(f->runes)
122 		FMTRCHAR(f, rt, rs, '\'');
123 	else
124 		FMTRUNE(f, t, s, '\'');
125 	for(nc = q->nrunesin; nc > 0; nc--){
126 		if(sin){
127 			r = *(uchar*)m;
128 			if(r < Runeself)
129 				m++;
130 			else if((me - m) >= UTFmax || fullrune(m, me-m))
131 				m += chartorune(&r, m);
132 			else
133 				break;
134 		}else{
135 			if(rm >= rme)
136 				break;
137 			r = *(uchar*)rm++;
138 		}
139 		if(f->runes){
140 			FMTRCHAR(f, rt, rs, r);
141 			if(r == '\'')
142 				FMTRCHAR(f, rt, rs, r);
143 		}else{
144 			FMTRUNE(f, t, s, r);
145 			if(r == '\'')
146 				FMTRUNE(f, t, s, r);
147 		}
148 	}
149 
150 	if(f->runes){
151 		FMTRCHAR(f, rt, rs, '\'');
152 		USED(rs);
153 		f->nfmt += rt - (Rune *)f->to;
154 		f->to = rt;
155 		if(fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0)
156 			return -1;
157 	}else{
158 		FMTRUNE(f, t, s, '\'');
159 		USED(s);
160 		f->nfmt += t - (char *)f->to;
161 		f->to = t;
162 		if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0)
163 			return -1;
164 	}
165 	return 0;
166 }
167 
168 int
__quotestrfmt(int runesin,Fmt * f)169 __quotestrfmt(int runesin, Fmt *f)
170 {
171 	int nin, outlen;
172 	Rune *r;
173 	char *s;
174 	Quoteinfo q;
175 
176 	nin = -1;
177 	if(f->flags&FmtPrec)
178 		nin = f->prec;
179 	if(runesin){
180 		r = va_arg(f->args, Rune *);
181 		s = nil;
182 	}else{
183 		s = va_arg(f->args, char *);
184 		r = nil;
185 	}
186 	if(!s && !r)
187 		return __fmtcpy(f, (void*)"<nil>", 5, 5);
188 
189 	if(f->flush)
190 		outlen = 0x7FFFFFFF;	/* if we can flush, no output limit */
191 	else if(f->runes)
192 		outlen = (Rune*)f->stop - (Rune*)f->to;
193 	else
194 		outlen = (char*)f->stop - (char*)f->to;
195 
196 	__quotesetup(s, r, nin, outlen, &q, f->flags&FmtSharp, f->runes);
197 //print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout);
198 
199 	if(runesin){
200 		if(!q.quoted)
201 			return __fmtrcpy(f, r, q.nrunesin);
202 		return qstrfmt(nil, r, &q, f);
203 	}
204 
205 	if(!q.quoted)
206 		return __fmtcpy(f, s, q.nrunesin, q.nbytesin);
207 	return qstrfmt(s, nil, &q, f);
208 }
209 
210 int
quotestrfmt(Fmt * f)211 quotestrfmt(Fmt *f)
212 {
213 	return __quotestrfmt(0, f);
214 }
215 
216 int
quoterunestrfmt(Fmt * f)217 quoterunestrfmt(Fmt *f)
218 {
219 	return __quotestrfmt(1, f);
220 }
221 
222 void
quotefmtinstall(void)223 quotefmtinstall(void)
224 {
225 	fmtinstall('q', quotestrfmt);
226 	fmtinstall('Q', quoterunestrfmt);
227 }
228 
229 int
__needsquotes(char * s,int * quotelenp)230 __needsquotes(char *s, int *quotelenp)
231 {
232 	Quoteinfo q;
233 
234 	__quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0);
235 	*quotelenp = q.nbytesout;
236 
237 	return q.quoted;
238 }
239 
240 int
__runeneedsquotes(Rune * r,int * quotelenp)241 __runeneedsquotes(Rune *r, int *quotelenp)
242 {
243 	Quoteinfo q;
244 
245 	__quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0);
246 	*quotelenp = q.nrunesout;
247 
248 	return q.quoted;
249 }
250