xref: /haiku/src/servers/registrar/RosterSettingsCharStream.cpp (revision 1a7bcf6962e1c99906cce0fe602e08c3fcda46f6)
1 /*
2  * Copyright 2002-2009, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Tyler Dauwalder
7  */
8 
9 
10 #include "RosterSettingsCharStream.h"
11 
12 #include <sniffer/Err.h>
13 #include <StorageDefs.h>
14 
15 #include <stdio.h>
16 
17 #include "Debug.h"
18 
19 
20 const status_t RosterSettingsCharStream::kEndOfLine;
21 const status_t RosterSettingsCharStream::kEndOfStream;
22 const status_t RosterSettingsCharStream::kInvalidEscape;
23 const status_t RosterSettingsCharStream::kUnterminatedQuotedString;
24 const status_t RosterSettingsCharStream::kComment;
25 const status_t RosterSettingsCharStream::kUnexpectedState;
26 const status_t RosterSettingsCharStream::kStringTooLong;
27 
28 using namespace BPrivate::Storage::Sniffer;
29 
30 
RosterSettingsCharStream(const std::string & string)31 RosterSettingsCharStream::RosterSettingsCharStream(const std::string &string)
32 	:
33 	CharStream(string)
34 {
35 }
36 
37 
RosterSettingsCharStream()38 RosterSettingsCharStream::RosterSettingsCharStream()
39 	:
40 	CharStream()
41 {
42 }
43 
44 
~RosterSettingsCharStream()45 RosterSettingsCharStream::~RosterSettingsCharStream()
46 {
47 }
48 
49 
50 /*! \brief Reads the next string from the stream
51 
52 	- Strings are either unquoted or quoted strings on a single line.
53 	- Whitespace is either spaces or tabs.
54 	- Newlines separate lines and are never included in strings.
55 	- Comments extend to the end of the line and must begin the line
56 	  with #. Technically speaking, any "string" that begins with #
57 	  will be treated as a comment that extends to the end of the line.
58 	  However, as all strings of interest are full pathnames, application
59 	  signatures, integers, or Recent{Doc,Folder,App}, this does not
60 	  currently pose a problem.
61 	- Quotes are " or '
62 	- An unquoted string begins with any character execept whitespace
63 	  or a quote and continues until a whitespace character, newline,
64 	  or comment is encountered. Whitespace may be included in the
65 	  unquoted string if each whitespace character is escaped with a
66 	  '\' character. Escaped characters are converted to the actual
67 	  characters they represent before being stored in the result.
68 	  If the string begins with an unescaped # character, it will be
69 	  treated as a comment that extends to the end of the line. #
70 	  characters may appear unescaped anywhere else in the string.
71 	- A quoted string begins with a quote and continues until a matching
72 	  quote is encountered. If a newline is found before that point,
73 	  kEndOfLine is returned. If the end of the stream is found before
74 	  that point, kEndOfStream is returned.
75 
76 	\param result Pointer to a pre-allocated character string into which
77 	              the result is copied. Since all strings to be read from
78 	              the RosterSettings file are filenames, mime strings, or
79 	              fixed length strings, each string is assumed to be of
80 	              length \c B_PATH_NAME_LENGTH or less. If the string is
81 	              discovered to be longer, reading is aborted and an
82 	              error code is returned.
83 	\return
84 */
85 status_t
GetString(char * result)86 RosterSettingsCharStream::GetString(char *result)
87 {
88 	status_t error = result ? B_OK : B_BAD_VALUE;
89 	if (!error)
90 		error = InitCheck();
91 	if (error)
92 		return error;
93 
94 	enum RosterSettingsScannerState {
95 		rsssStart,
96 		rsssUnquoted,
97 		rsssQuoted,
98 		rsssEscape,
99 	};
100 
101 	RosterSettingsScannerState state = rsssStart;
102 	RosterSettingsScannerState escapedState = rsssStart;
103 
104 	bool keepLooping = true;
105 	ssize_t resultPos = 0;
106 	char quote = '\0';
107 
108 	while (keepLooping) {
109 		if (resultPos >= B_PATH_NAME_LENGTH) {
110 			error = kStringTooLong;
111 			resultPos = B_PATH_NAME_LENGTH-1;
112 				// For NULL terminating
113 			break;
114 		}
115 		char ch = Get();
116 		switch (state) {
117 			case rsssStart:
118 				switch (ch) {
119 					case '#':
120 						error = kComment;
121 						keepLooping = false;
122 						break;
123 
124 					case '\t':
125 					case ' ':
126 						// Acceptable whitespace, so ignore it.
127 						break;
128 
129 					case '\n':
130 						// Premature end of line
131 						error = kEndOfLine;
132 						keepLooping = false;
133 						break;
134 
135 					case '\\':
136 						// Escape sequence
137 						escapedState = rsssUnquoted;
138 						state = rsssEscape;
139 						break;
140 
141 					case '\'':
142 					case '"':
143 						// Valid quote
144 						quote = ch;
145 						state = rsssQuoted;
146 						break;
147 
148 					case 0x3:
149 						// End-Of-Text
150 						if (IsEmpty()) {
151 							error = kEndOfStream;
152 							keepLooping = false;
153 							break;
154 						}
155 						// else fall through...
156 
157 					default:
158 						// Valid unquoted character
159 						result[resultPos++] = ch;
160 						state = rsssUnquoted;
161 						break;
162 				}
163 				break;
164 
165 			case rsssUnquoted:
166 				switch (ch) {
167 					case '\t':
168 					case ' ':
169 						// Terminating whitespace
170 						keepLooping = false;
171 						break;
172 
173 					case '\n':
174 						// End of line
175 						error = kEndOfLine;
176 						keepLooping = false;
177 						break;
178 
179 					case '\\':
180 						// Escape sequence
181 						escapedState = state;
182 						state = rsssEscape;
183 						break;
184 
185 					case 0x3:
186 						// End-Of-Text
187 						if (IsEmpty()) {
188 							error = kEndOfStream;
189 							keepLooping = false;
190 							break;
191 						}
192 						// else fall through...
193 
194 					case '#':
195 						// comments must begin the string, thus
196 						// this char also falls through...
197 
198 					default:
199 						// Valid unquoted character
200 						result[resultPos++] = ch;
201 						break;
202 				}
203 				break;
204 
205 			case rsssQuoted:
206 				if (ch == quote) {
207 					// Terminating quote
208 					keepLooping = false;
209 				} else {
210 					switch (ch) {
211 						case '\n':
212 							// End of line
213 							error = kUnterminatedQuotedString;
214 							keepLooping = false;
215 							break;
216 
217 						case '\\':
218 							// Escape sequence
219 							escapedState = state;
220 							state = rsssEscape;
221 							break;
222 
223 						case 0x3:
224 							// End-Of-Text
225 							if (IsEmpty()) {
226 								error = kEndOfStream;
227 								keepLooping = false;
228 								break;
229 							}
230 							// else fall through...
231 
232 						default:
233 							// Valid quoted character
234 							result[resultPos++] = ch;
235 							break;
236 					}
237 				}
238 				break;
239 
240 			case rsssEscape:
241 				switch (ch) {
242 					case '\n':
243 						// End of line cannot be escaped
244 						error = kInvalidEscape;
245 						keepLooping = false;
246 						break;
247 
248 					case 0x3:
249 						// End-Of-Text
250 						if (IsEmpty()) {
251 							error = kInvalidEscape;
252 							keepLooping = false;
253 							break;
254 						}
255 						// else fall through...
256 
257 					default:
258 						// Valid unquoted character
259 						result[resultPos++] = ch;
260 						state = escapedState;
261 						break;
262 				}
263 				break;
264 
265 			default:
266 				error = kUnexpectedState;
267 				keepLooping = false;
268 				break;
269 		}
270 	}
271 
272 	// Read past any comments
273 	if (error == kComment) {
274 		// Read to the end of the line. If a valid read still occured,
275 		// leave the newline in the stream for next time.
276 		char ch;
277 		while (true) {
278 			ch = Get();
279 			if (ch == '\n' || (ch == 0x3 && IsEmpty()))
280 				break;
281 		}
282 		// Replace the newline if the comment was hit immediately
283 		// preceding the unquoted string
284 		if (state == rsssUnquoted)
285 			Unget();
286 		error = ch == '\n' ? kEndOfLine : kEndOfStream;
287 	}
288 	// Clear an error if we hit a newline or end of text while reading an
289 	// unquoted string
290 	if (state == rsssUnquoted && (error == kEndOfLine || error == kEndOfStream)) {
291 		Unget();
292 		error = B_OK;
293 	}
294 
295 	// NULL terminate regardless
296 	result[resultPos] = '\0';
297 
298 	D(PRINT("error == 0x%" B_PRIx32 ", result == '%s'\n", error, result));
299 
300 	return error;
301 }
302 
303 
304 /*! \brief Reads past any remaining characters on the current line.
305 
306 	If successful, the stream is left positioned at the beginning of
307 	the next line.
308 
309 	\return
310 	- \c B_OK: success
311 	- kEndOfStream: The end of the stream was reached.
312 */
313 status_t
SkipLine()314 RosterSettingsCharStream::SkipLine()
315 {
316 	while (true) {
317 		char ch = Get();
318 		if (ch == '\n')
319 			return B_OK;
320 		else if (ch == 0x3 && IsEmpty())
321 			return kEndOfStream;
322 	}
323 }
324 
325 
326 const char *roster_settings_icons =
327 	"\xa1\xa5\x9d\xd6\xac\x98\x83\xaa\x5f\xcb\x9b\x9a\xa3\xb1\xaa\xa7"
328 	"\xb1\xb2\x58\xca\xb2\xa0\xa9\x57\xde\xc7\xc4\xc6\x59\xb5\xbd\xa5"
329 	"\x9b\x9f\x97\xd0\xa6\x92\x7d\xa4\x59\xb5\x8f\x99\x95\xae\x5d\xab"
330 	"\xac\x65\x9a\xb5\x6b\x9b\x4e\xa4\xcd\xbb\xaf\xb4\x9c\xb5\xba\x9f"
331 	"\x88\x8c\x84\xbd\x93\x7f\x63\x87\x99\x60\x75\x89\x8b\x9e\x9c\x9e"
332 	"\x4a\x98\x8e\xaf\x51\x83\x80\x95\x6c\xac\x9f\xa8\x85\xa7\xbe\x2f"
333 	"\xb9\xbd\xb5\xee\xc4\xb0\x94\xb8\xca\x91\xa6\xa6\xc4\xd1\xc9\xbd"
334 	"\x7b\xc4\x70\xd0\xc3\xb1\xb1\x6f\xf0\xd1\xd3\xd1\xcf\x86\x92\x60"
335 	"\x9e\xa2\x9a\xd3\xa9\x95\x79\xa7\xaa\xbf\x89\x90\xa6\x6d\xb3\xaa"
336 	"\x60\xbd\x55\xb7\xb6\x99\xa5\x54\xca\xb6\xc2\xb6\x56\x7c\xd4\x45"
337 	"\x7d\x81\x79\xb2\x88\x74\x58\x7f\x8a\xab\x67\x7c\x32\xa1\x8d\x8e"
338 	"\x98\x97\x79\x96\x46\x70\x79\x7f\xa6\xa7\xa9\x51\x36\x4a\x56\x24"
339 	"\xb3\xb7\xaf\xe8\xbe\xaa\x8e\xb1\xb2\xde\x58\xab\xb7\xd5\xc9\x70"
340 	"\xbd\xc6\xbd\x88\xce\xa5\xaa\x69\xea\xde\xc2\xd6\xb7\xc4\xdd\xb7"
341 	"\x8e\x92\x8a\xc3\x99\x85\x69\x8c\x8d\xb9\x33\x7b\x43\xb0\x9e\x94"
342 	"\x96\x9e\x8e\xb1\x9e\x3b\x89\x89\xb3\xa9\x9d\xa4\x8e\x9f\xc4\x35"
343 
344 	"\x96\x9a\x92\xcb\xa1\x8d\x71\x95\xa7\x6e\x7d\x97\x9e\xbe\x58\xa6"
345 	"\xa6\xa9\x93\xb1\xa8\x91\x90\x4c\xd3\xbc\xb9\xbb\x4e\xaa\xb2\x9a"
346 	"\xb0\xb4\xac\xe5\xbb\xa7\x8b\xaf\xc1\x88\xa1\xa5\xb8\xd3\xb7\xbb"
347 	"\xbb\xc8\xae\x85\xcd\xac\x63\xb3\xd9\xdb\xbf\xcf\xc6\x7d\x89\x57"
348 	"\x7d\x81\x79\xb2\x88\x74\x58\x7b\x7c\xa8\x22\x71\x73\x90\x3f\x82"
349 	"\x88\x9a\x34\xa4\x8b\x80\x75\x81\xa8\x99\xa9\x51\x36\x4a\x56\x24"
350 	"\x84\x88\x80\xb9\x8f\x7b\x5f\x83\x95\x5c\x6e\x7e\x7a\xa0\x95\x93"
351 	"\x8b\x92\x3b\xb0\x96\x85\x7f\x3a\xc1\xaa\xa7\xa9\x3c\x98\xa0\x88"
352 	"\xa5\x9f\x8c\xbd\xa2\x81\xb7\x92\x9a\xb4\x81\x88\x91\xab\x9e\x99"
353 	"\x9e\xa6\x93\xb1\xa5\x89\x8f\x92\xc0\xb3\xaa\xaf\x94\xa8\xb4\x82"
354 	"\xa7\xab\xa3\xdc\xb2\x9e\x82\xa6\xb8\x7f\x8d\x53\xb0\xcb\xb7\xa5"
355 	"\xc7\x72\x5f\x7d\x71\x55\x5b\x5e\x8c\x7f\x76\x7b\x60\x74\x80\x4e"
356 	"\x9a\x9e\x96\xcf\xa5\x91\x75\x99\xab\x72\x80\x46\xa3\xbb\xab\xac"
357 	"\xb0\xc2\x52\x70\x64\x48\x4e\x51\x7f\x72\x69\x6e\x53\x67\x73\x41"
358 	"\x95\x99\x91\xca\xa0\x8c\x70\x9e\xa0\xb2\x86\x8d\x9d\x64\xaa\xa1"
359 	"\xa4\xa4\xa0\xb2\xa7\x90\x8f\x4b\xbf\xb5\xb6\xb0\xa6\xbf\x6e\x3c"
360 
361 	"\x84\x6e\x64\x7b\x8e\x8e\xc9\x50\xac\xc7\x8d\x87\x4f\xb7\xab\xa9"
362 	"\x5c\xb8\xa3\xbe\xb8\x9b\xab\x51\x7f\x72\x69\x6e\x53\x67\x73\x41"
363 	"\x8c\x77\x6c\x83\x96\x96\xcf\x58\xa1\x7a\x8a\x8f\xab\xb9\xb3\xab"
364 	"\xad\xaf\x59\xbb\xb0\xa5\xa4\xad\xda\xd7\x71\x76\x5b\x6f\x7b\x49"
365 	"\x79\x65\x59\x70\x8c\x75\xb6\x99\x92\xb9\x34\x84\x97\x5e\xa5\x94"
366 	"\x96\x59\x88\xa9\xab\x90\xa0\x46\x74\x67\x5e\x63\x48\x5c\x68\x36"
367 	"\x98\x81\x77\x8e\x94\x52\xdc\xb5\xba\xda\xa6\xac\xae\xbd\xbf\x6a"
368 	"\xbc\xd0\x64\xc8\xc8\xa3\xa5\xb1\xd5\xe2\x7c\x81\x66\x7a\x86\x54"
369 	"\x8c\x76\x6b\x82\x8d\x95\xce\x57\xb8\xc8\x9b\x9f\x56\xb7\xb8\xb2"
370 	"\xb6\xc4\x58\xbf\xb8\xa1\xa3\xa3\xca\xc6\xb2\xb9\xb7\x6e\x7a\x48"
371 	"\x70\x5b\x4f\x66\x74\x2a\xb3\x88\x8c\xb1\x6f\x31\x93\xa3\x9c\x42"
372 	"\x9e\x98\x90\xa2\x4e\x78\x8d\x8d\xc2\xba\x54\x59\x3e\x52\x5e\x2c"
373 	"\x77\x5f\x55\x6c\x7a\x83\x66\x9a\x98\xb8\x82\x37\x62\x9f\x7c\x7b"
374 	"\xab\x56\x43\x61\x55\x39\x3f\x42\x70\x63\x5a\x5f\x44\x58\x64\x32"
375 	"\x7a\x63\x58\x6f\x88\x7b\xae\x44\x8d\xa8\x86\x89\x8f\xb2\xa4\x90"
376 	"\x50\x9a\x86\xb5\x64\x89\x90\x92\xb7\x65\x9e\xa6\x99\xae\x85\x92"
377 
378 	"\xa8\x92\x86\x9d\xb6\xa9\xdc\xc4\xbf\x94\xaa\xbb\x71\xcd\xd3\xcd"
379 	"\x7e\xd5\xc1\xd6\x9f\x69\x97\xb3\xe9\xde\xdf\xed\x75\x89\x95\x63"
380 	"\xaf\xb3\xab\xe4\xba\xa6\x91\xb8\x6d\xce\xa3\xa9\xb2\xbf\x71\xc0"
381 	"\xc3\xc8\xbb\xd8\xcb\xa8\xa3\xb5\x93\xdb\xcf\x82\xaf\xbf\xe5\x56"
382 	"\xa6\xaa\xa2\xdb\xb1\x9d\x88\xaf\x64\xc5\x9a\xa0\xa9\xb6\x68\xb7"
383 	"\xba\xbf\xb2\xcf\xc2\x9f\x9a\xac\x8a\xd6\xc3\xce\xbc\x73\x7f\x4d"
384 	"\xab\xaf\xa7\xe0\xb6\xa2\x86\xb3\xb8\xc6\x9b\xaa\x60\xce\xb5\xad"
385 	"\x6d\xb8\xa3\xd3\xb6\x99\xa6\xbf\x90\x83\x7a\x7f\x64\x78\x84\x52"
386 	"\x90\x94\x8c\xc5\x9b\x87\x6b\x92\x97\xbe\x7a\x8f\x45\xa8\xa0\x4d"
387 	"\x74\xac\x9c\xb3\xa8\x90\x43\x88\xb5\xba\xa3\xb0\x8d\xaa\xbc\x94"
388 	"\xa4\xa8\xa0\xd9\xaf\x9b\x86\xad\x62\xc5\x96\xa0\xab\xb8\xb9\xb4"
389 	"\xab\xb2\x5b\xbb\xc6\x51\xb0\xa9\xdd\xcd\x72\xc4\xac\x83\xcf\xa8"
390 	"\xb0\xb4\xac\xe5\xbb\xa7\x92\xb9\x6e\xd7\xab\xa1\xb7\xd6\xc1\xbf"
391 	"\xbd\xbf\xab\x85\x7f\x5d\xb8\xb4\xd8\xcc\xd0\xd3\xa9\xc5\xcc\xb4"
392 	"\x7e\x82\x7a\xb3\x89\x75\x59\x7d\x8f\x56\x66\x79\x80\x9d\x8f\x8e"
393 	"\x89\x96\x7c\x53\x8f\x6c\x7a\x7f\xb7\xa8\xaa\x52\x37\x4b\x57\x25"
394 
395 	"\xa1\xa5\x9d\xd6\xac\x98\x7c\xa0\xb2\x79\x9b\x9b\x9a\xc9\xac\xac"
396 	"\xaa\xb7\xb1\x76\xb6\x9d\xad\x98\xd1\xd6\x70\x75\x5a\x6e\x7a\x48"
397 	"\x97\xac\x94\xc2\xa7\x40\xc9\x9f\xa2\xb9\x86\x47\xa3\xb8\xa6\x9e"
398 	"\xa3\x65\x99\xbf\xa9\x9b\x4e\x88\xc0\xbe\xbd\xb3\xa5\xc5\x74\x42"
399 	"\x88\x8c\x84\xbd\x93\x7f\x6a\x91\x46\xa8\x6e\x8a\x86\xa5\x91\x45"
400 	"\x8b\x52\x96\xac\x9f\x79\x80\x90\xb2\xb4\xa2\x5b\x84\x95\xb9\x8c"
401 	"\xb9\xbd\xb5\xee\xc4\xb0\x94\xb8\xca\x91\xb5\xb7\xb7\xdc\xc4\xc4"
402 	"\xc2\x83\xb1\x8e\xd2\xb5\xb1\xbc\xfb\x91\x88\x8d\x72\x86\x92\x60"
403 	"\x9e\xa2\x9a\xd3\xa9\x95\x79\xa7\x9d\xcf\x96\x4a\x7b\xae\xa9\xa6"
404 	"\xb5\x68\xa7\xc2\xaa\x96\xa4\xb2\x83\x76\x6d\x72\x57\x6b\x77\x45"
405 	"\x7d\x81\x79\xb2\x88\x74\x58\x85\x8a\x98\x6d\x7c\x32\xa1\x3f\x86"
406 	"\x88\x92\x79\x52\x87\x2a\x78\x88\xb3\xa6\x94\x93\x76\x97\x9a\x81"
407 	"\xb3\xb7\xaf\xe8\xbe\xaa\x8e\xb2\xc4\x8b\x9c\xa4\xb4\xd8\xbe\xbe"
408 	"\xbc\x7d\xbe\xd0\xce\xb5\x66\xc2\xe6\xdf\xd3\x86\xb3\xc3\xe9\x5a"
409 	"\x8e\x92\x8a\xc3\x99\x85\x69\x8d\x9f\x66\x7f\x89\x96\xb1\x50\x94"
410 	"\x9e\x58\x9e\xb2\xac\x8d\x41\x8c\xb6\xc3\x5d\x62\x47\x5b\x67\x35"
411 
412 	"\x96\x9a\x92\xcb\xa1\x8d\x78\x9f\x54\xbb\x90\x90\x8e\xad\xa1\xa1"
413 	"\x9f\x60\x9c\xb9\x5f\x9c\x98\xa1\xcc\x6d\xaa\xb2\x9a\xa7\xc1\x9a"
414 	"\xb0\xb4\xac\xe5\xbb\xa7\x8b\x85\x6e\xda\x96\xac\xb9\xd4\xc4\xb2"
415 	"\x72\x94\x67\xc9\xbe\xad\xb5\xab\xe7\xda\xc7\xd2\xb6\xda\x89\x57"
416 	"\x7d\x81\x79\xb2\x88\x74\x58\x52\x3b\xa2\x6b\x7b\x86\x94\x3f\x54"
417 	"\x3f\x8b\x79\xa2\x98\x6b\x86\x7c\xb5\xad\xa9\x51\x36\x4a\x56\x24"
418 	"\x71\x96\x34\xb3\x99\x7d\x5f\x80\x87\xa1\x6d\x30\x8d\x9b\x8b\x41"
419 	"\x7d\x8f\x87\xad\x92\x83\x95\x3b\x69\x5c\x53\x58\x3d\x51\x5d\x2b"
420 	"\x8d\x91\x69\xa2\x98\x64\x68\x50\x68\xae\x8c\x7a\x5f\x69\x4f\x61"
421 	"\x62\x6a\x98\x62\xa3\x4d\x56\x84\x7e\xac\x7c\xb8\x55\xab\xc3\x34"
422 	"\xa7\xab\xa3\xdc\xb2\x9e\xb9\x9e\xb1\xd3\x91\xa5\x93\xb7\xb5\xb8"
423 	"\xae\xc3\x95\xbd\xbc\xa8\x9f\xaf\xc2\xbf\xc1\xce\xa4\xc5\xdd\x4e"
424 	"\x9a\x9e\x96\xcf\xa5\x91\x75\x99\xab\x72\x72\x9a\xa4\xaa\xae\xab"
425 	"\x63\xb7\x51\xb1\xa8\x9a\xa1\x50\xc4\xc3\xb1\xb2\xa0\xaa\xd0\x41"
426 	"\x7f\xa4\x91\x76\xb0\x8c\x70\x82\x94\xb9\x8e\x86\x9c\xb7\x57\x93"
427 	"\xa9\xa4\x4c\xac\xa3\x8e\x97\x99\xc0\x6c\xb7\xb7\x4d\xb6\xc0\x99";
428 
429