xref: /trunk/main/ucb/workben/ucb/srcharg.cxx (revision 2f86921c)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_ucb.hxx"
26 
27 #include <limits>
28 #include <com/sun/star/ucb/RuleOperator.hpp>
29 #include <com/sun/star/ucb/SearchInfo.hpp>
30 #include <com/sun/star/util/Date.hpp>
31 #include <tools/date.hxx>
32 #include <tools/inetmime.hxx>
33 #include <tools/string.hxx>
34 
35 #ifndef CHAOS_UCBDEMO_SRCHARG_HXX
36 #include <srcharg.hxx>
37 #endif
38 
39 namespace unnamed_chaos_ucbdemo_srcharg {}
40 using namespace unnamed_chaos_ucbdemo_srcharg;
41 	// unnamed namespaces don't work well yet...
42 
43 using namespace com::sun::star;
44 
45 //============================================================================
46 //
47 //  skipWhiteSpace
48 //
49 //============================================================================
50 
51 namespace unnamed_chaos_ucbdemo_srcharg {
52 
skipWhiteSpace(sal_Unicode const * & rBegin,sal_Unicode const * pEnd)53 void skipWhiteSpace(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
54 {
55 	while (rBegin != pEnd
56 		   && (*rBegin == '\n' || *rBegin == '\t' || *rBegin == ' '))
57 		++rBegin;
58 }
59 
60 //============================================================================
61 //
62 //  scanAtom
63 //
64 //============================================================================
65 
scanAtom(sal_Unicode const * & rBegin,sal_Unicode const * pEnd)66 String scanAtom(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
67 {
68 	sal_Unicode const * pTheBegin = rBegin;
69 	while (rBegin != pEnd && INetMIME::isAlpha(*rBegin))
70 		++rBegin;
71 	return String(pTheBegin, rBegin - pTheBegin);
72 }
73 
74 //============================================================================
75 //
76 //  scanProperty
77 //
78 //============================================================================
79 
scanProperty(sal_Unicode const * & rBegin,sal_Unicode const * pEnd)80 String scanProperty(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
81 {
82 	sal_Unicode const * pTheBegin = rBegin;
83 	while (rBegin != pEnd
84 		   && !(*rBegin == '\n' || *rBegin == '\t' || *rBegin == ' '))
85 		++rBegin;
86 	return String(pTheBegin, rBegin - pTheBegin);
87 }
88 
89 //============================================================================
90 //
91 //  scanOperator
92 //
93 //============================================================================
94 
scanOperator(sal_Unicode const * & rBegin,sal_Unicode const * pEnd)95 String scanOperator(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
96 {
97 	sal_Unicode const * pTheBegin = rBegin;
98 	while (rBegin != pEnd
99 		   && (INetMIME::isAlpha(*rBegin) || *rBegin == '!'
100 			   || *rBegin >= '<' && *rBegin <= '>'))
101 		++rBegin;
102 	return String(pTheBegin, rBegin - pTheBegin);
103 }
104 
105 }
106 
107 //============================================================================
108 //
109 //  parseSearchArgument
110 //
111 //============================================================================
112 
parseSearchArgument(String const & rInput,ucb::SearchInfo & rInfo)113 bool parseSearchArgument(String const & rInput, ucb::SearchInfo & rInfo)
114 {
115 	/* Format of rInput:
116 
117 	   argument = *option [criterium *("OR" criterium)]
118 
119 	   option = ("--RECURSE" "=" ("NONE" / "ONE" / "DEEP"))
120 	                / (("--BASE" / "--FOLDERVIEW" / "--DOCVIEW"
121 					            / "--INDIRECT")
122 					       "=" bool)
123 
124 	   criterium = "EMPTY" / (term *("AND" term))
125 
126 	   term = text-term / date-term / numeric-term / bool-term
127 
128 	   text-term = property ("CONT" / "!CONT" / ">=" / "<=" / "==" / "!=")
129 	                   string *("-C" / "-R")
130 
131 	   date-term = property
132 	                   (((">=" / "<=" / "==" / "!=") date)
133 					        / (("OLDER" / "YOUNGER") number))
134 
135 	   numeric-term = property (">=" / "<=" / "==" / "!=") number
136 
137 	   bool-term = property ("TRUE" / "FALSE")
138 
139 	   property = 1*VCHAR
140 
141 	   string = DQUOTE
142 	                *(<any Unicode code point except DQUOTE or "\">
143 					      / ("\" %x75 4HEXDIG)  ; \uHHHH
144 					      / ("\" (DQUOTE / "\")))
145 					DQUOTE
146 
147 	   date = 1*2DIGIT "/" 1*2DIGIT "/" 4DIGIT  ; mm/dd/yyyy
148 
149 	   number = ["+" / "-"] 1*DIGIT
150 	*/
151 
152 	sal_Unicode const * p = rInput.GetBuffer();
153 	sal_Unicode const * pEnd = p + rInput.Len();
154 
155 	// Parse options:
156 	rInfo.Recursion = ucb::SearchRecursion_ONE_LEVEL;
157 	rInfo.IncludeBase = true;
158 	rInfo.RespectFolderViewRestrictions = true;
159 	rInfo.RespectDocViewRestrictions = false;
160 	rInfo.FollowIndirections = false;
161 	enum OptionID { OPT_RECURSE, OPT_BASE, OPT_FOLDERVIEW, OPT_DOCVIEW,
162 					OPT_INDIRECT, OPT_Count };
163 	struct OptionInfo
164 	{
165 		bool m_bSpecified;
166 		sal_Bool * m_pValue;
167 	};
168 	OptionInfo aOptions[OPT_Count];
169 	aOptions[OPT_RECURSE].m_bSpecified = false;
170 	aOptions[OPT_RECURSE].m_pValue = 0;
171 	aOptions[OPT_BASE].m_bSpecified = false;
172 	aOptions[OPT_BASE].m_pValue = &rInfo.IncludeBase;
173 	aOptions[OPT_FOLDERVIEW].m_bSpecified = false;
174 	aOptions[OPT_FOLDERVIEW].m_pValue
175 		= &rInfo.RespectFolderViewRestrictions;
176 	aOptions[OPT_DOCVIEW].m_bSpecified = false;
177 	aOptions[OPT_DOCVIEW].m_pValue = &rInfo.RespectDocViewRestrictions;
178 	aOptions[OPT_INDIRECT].m_bSpecified = false;
179 	aOptions[OPT_INDIRECT].m_pValue = &rInfo.FollowIndirections;
180 	while (p != pEnd)
181 	{
182 		sal_Unicode const * q = p;
183 
184 		skipWhiteSpace(q, pEnd);
185 		if (pEnd - q < 2 || *q++ != '-' || *q++ != '-')
186 			break;
187 		String aOption(scanAtom(q, pEnd));
188 		OptionID eID;
189 		if (aOption.EqualsIgnoreCaseAscii("recurse"))
190 			eID = OPT_RECURSE;
191 		else if (aOption.EqualsIgnoreCaseAscii("base"))
192 			eID = OPT_BASE;
193 		else if (aOption.EqualsIgnoreCaseAscii("folderview"))
194 			eID = OPT_FOLDERVIEW;
195 		else if (aOption.EqualsIgnoreCaseAscii("docview"))
196 			eID = OPT_DOCVIEW;
197 		else if (aOption.EqualsIgnoreCaseAscii("indirect"))
198 			eID = OPT_INDIRECT;
199 		else
200 			break;
201 
202 		if (aOptions[eID].m_bSpecified)
203 			break;
204 		aOptions[eID].m_bSpecified = true;
205 
206 		skipWhiteSpace(q, pEnd);
207 		if (q == pEnd || *q++ != '=')
208 			break;
209 
210 		skipWhiteSpace(q, pEnd);
211 		String aValue(scanAtom(q, pEnd));
212 		if (eID == OPT_RECURSE)
213 		{
214 			if (aValue.EqualsIgnoreCaseAscii("none"))
215 				rInfo.Recursion = ucb::SearchRecursion_NONE;
216 			else if (aValue.EqualsIgnoreCaseAscii("one"))
217 				rInfo.Recursion = ucb::SearchRecursion_ONE_LEVEL;
218 			else if (aValue.EqualsIgnoreCaseAscii("deep"))
219 				rInfo.Recursion = ucb::SearchRecursion_DEEP;
220 			else
221 				break;
222 		}
223 		else if (aValue.EqualsIgnoreCaseAscii("true"))
224 			*aOptions[eID].m_pValue = true;
225 		else if (aValue.EqualsIgnoreCaseAscii("false"))
226 			*aOptions[eID].m_pValue = false;
227 		else
228 			break;
229 
230 		p = q;
231 	}
232 
233 	// Parse criteria:
234 	ucb::SearchCriterium aCriterium;
235 	for (;;)
236 	{
237 		sal_Unicode const * q = p;
238 
239 		// Parse either property name or "empty":
240 		skipWhiteSpace(q, pEnd);
241 		String aProperty(scanProperty(q, pEnd));
242 		sal_Unicode const * pPropertyEnd = q;
243 
244 		// Parse operator:
245 		skipWhiteSpace(q, pEnd);
246 		String aOperator(scanOperator(q, pEnd));
247 		struct Operator
248 		{
249 			sal_Char const * m_pName;
250 			sal_Int16 m_nText;
251 			sal_Int16 m_nDate;
252 			sal_Int16 m_nNumeric;
253 			sal_Int16 m_nBool;
254 		};
255 		static Operator const aOperators[]
256 			= { { "cont", ucb::RuleOperator::CONTAINS, 0, 0, 0 },
257 				{ "!cont", ucb::RuleOperator::CONTAINSNOT, 0, 0, 0 },
258 				{ ">=", ucb::RuleOperator::GREATEREQUAL,
259 				  ucb::RuleOperator::GREATEREQUAL,
260 				  ucb::RuleOperator::GREATEREQUAL, 0 },
261 				{ "<=", ucb::RuleOperator::LESSEQUAL,
262 				  ucb::RuleOperator::LESSEQUAL, ucb::RuleOperator::LESSEQUAL,
263 				  0 },
264 				{ "==", ucb::RuleOperator::EQUAL, ucb::RuleOperator::EQUAL,
265 				  ucb::RuleOperator::EQUAL, 0 },
266 				{ "!=", ucb::RuleOperator::NOTEQUAL,
267 				  ucb::RuleOperator::NOTEQUAL, ucb::RuleOperator::NOTEQUAL,
268 				  0 },
269 				{ "true", 0, 0, 0, ucb::RuleOperator::VALUE_TRUE },
270 				{ "false", 0, 0, 0, ucb::RuleOperator::VALUE_FALSE } };
271 		int const nOperatorCount = sizeof aOperators / sizeof (Operator);
272 		Operator const * pTheOperator = 0;
273 		for (int i = 0; i < nOperatorCount; ++i)
274 			if (aOperator.EqualsIgnoreCaseAscii(aOperators[i].m_pName))
275 			{
276 				pTheOperator = aOperators + i;
277 				break;
278 			}
279 		bool bTerm = pTheOperator != 0;
280 
281 		sal_Int16 nOperatorID;
282 		uno::Any aTheOperand;
283 		bool bCaseSensitive = false;
284 		bool bRegularExpression = false;
285 		if (bTerm)
286 		{
287 			skipWhiteSpace(q, pEnd);
288 			bool bHasOperand = false;
289 
290 			// Parse string operand:
291 			if (!bHasOperand && pTheOperator->m_nText)
292 			{
293 				if (q != pEnd && *q == '"')
294 				{
295 					String aString;
296 					for (sal_Unicode const * r = q + 1;;)
297 					{
298 						if (r == pEnd)
299 							break;
300 						sal_Unicode c = *r++;
301 						if (c == '"')
302 						{
303 							bHasOperand = true;
304 							aTheOperand <<= rtl::OUString(aString);
305 							nOperatorID = pTheOperator->m_nText;
306 							q = r;
307 							break;
308 						}
309 						if (c == '\\')
310 						{
311 							if (r == pEnd)
312 								break;
313 							c = *r++;
314 							if (c == 'u')
315 							{
316 								if (pEnd - r < 4)
317 									break;
318 								c = 0;
319 								bool bBad = false;
320 								for (int i = 0; i < 4; ++i)
321 								{
322 									int nWeight
323 										= INetMIME::getHexWeight(*r++);
324 									if (nWeight < 0)
325 									{
326 										bBad = false;
327 										break;
328 									}
329 									c = sal_Unicode(c << 4 | nWeight);
330 								}
331 								if (bBad)
332 									break;
333 							}
334 							else if (c != '"' && c != '\\')
335 								break;
336 						}
337 						aString += c;
338 					}
339 				}
340 
341 				// Parse "-C" and "-R":
342 				if (bHasOperand)
343 					for (;;)
344 					{
345 						skipWhiteSpace(q, pEnd);
346 						if (pEnd - q >= 2 && q[0] == '-'
347 							&& (q[1] == 'C' || q[1] == 'c')
348 							&& !bCaseSensitive)
349 						{
350 							bCaseSensitive = true;
351 							q += 2;
352 						}
353 						else if (pEnd - q >= 2 && q[0] == '-'
354 								 && (q[1] == 'R' || q[1] == 'r')
355 								 && !bRegularExpression)
356 						{
357 							bRegularExpression = true;
358 							q += 2;
359 						}
360 						else
361 							break;
362 					}
363 			}
364 
365 			// Parse date operand:
366 			if (!bHasOperand && pTheOperator->m_nDate != 0)
367 			{
368 				sal_Unicode const * r = q;
369 				bool bOK = true;
370 				USHORT nMonth = 0;
371 				if (bOK && r != pEnd && INetMIME::isDigit(*r))
372 					nMonth = INetMIME::getWeight(*r++);
373 				else
374 					bOK = false;
375 				if (bOK && r != pEnd && INetMIME::isDigit(*r))
376 					nMonth = 10 * nMonth + INetMIME::getWeight(*r++);
377 				if (!(bOK && r != pEnd && *r++ == '/'))
378 					bOK = false;
379 				USHORT nDay = 0;
380 				if (bOK && r != pEnd && INetMIME::isDigit(*r))
381 					nDay = INetMIME::getWeight(*r++);
382 				else
383 					bOK = false;
384 				if (bOK && r != pEnd && INetMIME::isDigit(*r))
385 					nDay = 10 * nDay + INetMIME::getWeight(*r++);
386 				if (!(bOK && r != pEnd && *r++ == '/'))
387 					bOK = false;
388 				USHORT nYear = 0;
389 				for (int i = 0; bOK && i < 4; ++i)
390 					if (r != pEnd && INetMIME::isDigit(*r))
391 						nYear = 10 * nYear + INetMIME::getWeight(*r++);
392 					else
393 						bOK = false;
394 				if (bOK && Date(nDay, nMonth, nYear).IsValid())
395 				{
396 					bHasOperand = true;
397 					aTheOperand <<= util::Date(nDay, nMonth, nYear);
398 					nOperatorID = pTheOperator->m_nDate;
399 					q = r;
400 				}
401 			}
402 
403 			// Parse number operand:
404 			if (!bHasOperand && pTheOperator->m_nNumeric != 0)
405 			{
406 				sal_Unicode const * r = q;
407 				bool bNegative = false;
408 				if (*r == '+')
409 					++r;
410 				else if (*r == '-')
411 				{
412 					bNegative = true;
413 					++r;
414 				}
415 				sal_Int64 nNumber = 0;
416 				bool bDigits = false;
417 				while (r != pEnd && INetMIME::isDigit(*r))
418 				{
419 					nNumber = 10 * nNumber + INetMIME::getWeight(*r++);
420 					if (nNumber > std::numeric_limits< sal_Int32 >::max())
421 					{
422 						bDigits = false;
423 						break;
424 					}
425 				}
426 				if (bDigits)
427 				{
428 					bHasOperand = true;
429 					aTheOperand
430 						<<= sal_Int32(bNegative ? -sal_Int32(nNumber) :
431 									              sal_Int32(nNumber));
432 					nOperatorID = pTheOperator->m_nNumeric;
433 					q = r;
434 				}
435 			}
436 
437 			// Bool operator has no operand:
438 			if (!bHasOperand && pTheOperator->m_nBool != 0)
439 			{
440 				bHasOperand = true;
441 				nOperatorID = pTheOperator->m_nBool;
442 			}
443 
444 			bTerm = bHasOperand;
445 		}
446 
447 		bool bEmpty = false;
448 		if (bTerm)
449 		{
450 			aCriterium.Terms.realloc(aCriterium.Terms.getLength() + 1);
451 			aCriterium.Terms[aCriterium.Terms.getLength() - 1]
452 				= ucb::RuleTerm(aProperty, aTheOperand, nOperatorID,
453 								bCaseSensitive, bRegularExpression);
454 		}
455 		else if (aCriterium.Terms.getLength() == 0
456 				 && aProperty.EqualsIgnoreCaseAscii("empty"))
457 		{
458 			bEmpty = true;
459 			q = pPropertyEnd;
460 		}
461 
462 		if (!(bTerm || bEmpty))
463 			break;
464 
465 		p = q;
466 		skipWhiteSpace(p, pEnd);
467 
468 		q = p;
469 		String aConnection(scanAtom(q, pEnd));
470 		if (p == pEnd || aConnection.EqualsIgnoreCaseAscii("or"))
471 		{
472 			rInfo.Criteria.realloc(rInfo.Criteria.getLength() + 1);
473 			rInfo.Criteria[rInfo.Criteria.getLength() - 1] = aCriterium;
474 			aCriterium = ucb::SearchCriterium();
475 			p = q;
476 		}
477 		else if (bTerm && aConnection.EqualsIgnoreCaseAscii("and"))
478 			p = q;
479 		else
480 			break;
481 	}
482 
483 	skipWhiteSpace(p, pEnd);
484 	return p == pEnd;
485 }
486 
487