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