xref: /plan9-contrib/sys/src/cmd/unix/drawterm/libc/fmtquote.c (revision 8ccd4a6360d974db7bd7bbd4f37e7018419ea908)
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
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-=w){
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'\'') || (doquote!=nil && doquote(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
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 = f->to;
118 	s = f->stop;
119 	rt = f->to;
120 	rs = 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
169 _quotestrfmt(int runesin, Fmt *f)
170 {
171 	int outlen;
172 	Rune *r;
173 	char *s;
174 	Quoteinfo q;
175 
176 	f->flags &= ~FmtPrec;	/* ignored for %q %Q, so disable for %s %S in easy case */
177 	if(runesin){
178 		r = va_arg(f->args, Rune *);
179 		s = nil;
180 	}else{
181 		s = va_arg(f->args, char *);
182 		r = nil;
183 	}
184 	if(!s && !r)
185 		return _fmtcpy(f, "<nil>", 5, 5);
186 
187 	if(f->flush)
188 		outlen = 0x7FFFFFFF;	/* if we can flush, no output limit */
189 	else if(f->runes)
190 		outlen = (Rune*)f->stop - (Rune*)f->to;
191 	else
192 		outlen = (char*)f->stop - (char*)f->to;
193 
194 	_quotesetup(s, r, -1, outlen, &q, f->flags&FmtSharp, f->runes);
195 //print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout);
196 
197 	if(runesin){
198 		if(!q.quoted)
199 			return _fmtrcpy(f, r, q.nrunesin);
200 		return qstrfmt(nil, r, &q, f);
201 	}
202 
203 	if(!q.quoted)
204 		return _fmtcpy(f, s, q.nrunesin, q.nbytesin);
205 	return qstrfmt(s, nil, &q, f);
206 }
207 
208 int
209 quotestrfmt(Fmt *f)
210 {
211 	return _quotestrfmt(0, f);
212 }
213 
214 int
215 quoterunestrfmt(Fmt *f)
216 {
217 	return _quotestrfmt(1, f);
218 }
219 
220 void
221 quotefmtinstall(void)
222 {
223 	fmtinstall('q', quotestrfmt);
224 	fmtinstall('Q', quoterunestrfmt);
225 }
226 
227 int
228 _needsquotes(char *s, int *quotelenp)
229 {
230 	Quoteinfo q;
231 
232 	_quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0);
233 	*quotelenp = q.nbytesout;
234 
235 	return q.quoted;
236 }
237 
238 int
239 _runeneedsquotes(Rune *r, int *quotelenp)
240 {
241 	Quoteinfo q;
242 
243 	_quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0);
244 	*quotelenp = q.nrunesout;
245 
246 	return q.quoted;
247 }
248