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