xref: /haiku/src/kits/package/PackageInfoStringBuilder.h (revision 7457ccb4b2f4786525d3b7bda42598487d57ab7d)
1 /*
2  * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 #ifndef PACKAGE_INFO_STRING_BUILDER_H
6 #define PACKAGE_INFO_STRING_BUILDER_H
7 
8 
9 #include <ctype.h>
10 
11 #include <DataIO.h>
12 #include <package/PackageInfo.h>
13 
14 
15 namespace BPackageKit {
16 
17 
18 struct BPackageInfo::StringBuilder {
19 	StringBuilder()
20 		:
21 		fData(),
22 		fError(B_OK),
23 		fBasePackage()
24 	{
25 	}
26 
27 	status_t Error() const
28 	{
29 		return fError;
30 	}
31 
32 	status_t GetString(BString& _string) const
33 	{
34 		if (fError != B_OK) {
35 			_string = BString();
36 			return fError;
37 		}
38 
39 		_string.SetTo((const char*)fData.Buffer(), fData.BufferLength());
40 		return (size_t)_string.Length() == fData.BufferLength()
41 			? B_OK : B_NO_MEMORY;
42 	}
43 
44 	template<typename Value>
45 	StringBuilder& Write(const char* attribute, Value value)
46 	{
47 		if (_IsValueEmpty(value))
48 			return *this;
49 
50 		_Write(attribute);
51 		_Write('\t');
52 		_WriteValue(value);
53 		_Write('\n');
54 		return *this;
55 	}
56 
57 	StringBuilder& WriteFlags(const char* attribute, uint32 flags)
58 	{
59 		if ((flags & B_PACKAGE_FLAG_APPROVE_LICENSE) == 0
60 			&& (flags & B_PACKAGE_FLAG_SYSTEM_PACKAGE) == 0) {
61 			return *this;
62 		}
63 
64 		_Write(attribute);
65 		_Write('\t');
66 
67 		if ((flags & B_PACKAGE_FLAG_APPROVE_LICENSE) != 0)
68 			_Write(" approve_license");
69 		if ((flags & B_PACKAGE_FLAG_SYSTEM_PACKAGE) != 0)
70 			_Write(" system_package");
71 
72 		_Write('\n');
73 		return *this;
74 	}
75 
76 	StringBuilder& BeginRequires(const BString& basePackage)
77 	{
78 		fBasePackage = basePackage;
79 		return *this;
80 	}
81 
82 	StringBuilder& EndRequires()
83 	{
84 		fBasePackage.Truncate(0);
85 		return *this;
86 	}
87 
88 private:
89 	void _WriteValue(const char* value)
90 	{
91 		_WriteMaybeQuoted(value);
92 	}
93 
94 	void _WriteValue(const BPackageVersion& value)
95 	{
96 		if (fError != B_OK)
97 			return;
98 
99 		if (value.InitCheck() != B_OK) {
100 			fError = B_BAD_VALUE;
101 			return;
102 		}
103 
104 		_Write(value.ToString());
105 	}
106 
107 	void _WriteValue(const BStringList& value)
108 	{
109 		int32 count = value.CountStrings();
110 		if (count == 1) {
111 			_WriteMaybeQuoted(value.StringAt(0));
112 		} else {
113 			_Write("{\n", 2);
114 
115 			int32 count = value.CountStrings();
116 			for (int32 i = 0; i < count; i++) {
117 				_Write('\t');
118 				_WriteMaybeQuoted(value.StringAt(i));
119 				_Write('\n');
120 			}
121 
122 			_Write('}');
123 		}
124 	}
125 
126 	template<typename Value>
127 	void _WriteValue(const BObjectList<Value>& value)
128 	{
129 		// Note: The fBasePackage solution is disgusting, but any attempt of
130 		// encapsulating the stringification via templates seems to result in
131 		// an Internal Compiler Error with gcc 2.
132 
133 		_Write("{\n", 2);
134 
135 		int32 count = value.CountItems();
136 		for (int32 i = 0; i < count; i++) {
137 			_Write('\t');
138 			_WriteListElement(value.ItemAt(i));
139 			_Write('\n');
140 		}
141 
142 		_Write('}');
143 	}
144 
145 	template<typename Value>
146 	void _WriteListElement(const Value* value)
147 	{
148 		_Write(value->ToString());
149 		if (!fBasePackage.IsEmpty()
150 			&& value->Name() == fBasePackage) {
151 			_Write(" base");
152 		}
153 	}
154 
155 	void _WriteListElement(const BGlobalWritableFileInfo* value)
156 	{
157 		_WriteMaybeQuoted(value->Path());
158 		if (value->IsDirectory()) {
159 			_Write(' ');
160 			_Write("directory");
161 		}
162 		if (value->IsIncluded()) {
163 			_Write(' ');
164 			_Write(kWritableFileUpdateTypes[value->UpdateType()]);
165 		}
166 	}
167 
168 	void _WriteListElement(const BUserSettingsFileInfo* value)
169 	{
170 		_WriteMaybeQuoted(value->Path());
171 		if (value->IsDirectory()) {
172 			_Write(" directory");
173 		} else if (!value->TemplatePath().IsEmpty()) {
174 			_Write(" template ");
175 			_WriteMaybeQuoted(value->TemplatePath());
176 		}
177 	}
178 
179 	void _WriteListElement(const BUser* value)
180 	{
181 		_WriteMaybeQuoted(value->Name());
182 
183 		if (!value->RealName().IsEmpty()) {
184 			_Write(" real-name ");
185 			_WriteMaybeQuoted(value->RealName());
186 		}
187 
188 		if (!value->Home().IsEmpty()) {
189 			_Write(" home ");
190 			_WriteMaybeQuoted(value->Home());
191 		}
192 
193 		if (!value->Shell().IsEmpty()) {
194 			_Write(" shell ");
195 			_WriteMaybeQuoted(value->Shell());
196 		}
197 
198 		if (!value->Groups().IsEmpty()) {
199 			_Write(" groups ");
200 			BString groups = value->Groups().Join(" ");
201 			if (groups.IsEmpty()) {
202 				if (fError == B_OK)
203 					fError = B_NO_MEMORY;
204 				return;
205 			}
206 			_Write(groups);
207 		}
208 	}
209 
210 	static inline bool _IsValueEmpty(const char* value)
211 	{
212 		return value[0] == '\0';
213 	}
214 
215 	static inline bool _IsValueEmpty(const BPackageVersion& value)
216 	{
217 		return false;
218 	}
219 
220 	template<typename List>
221 	static inline bool _IsValueEmpty(const List& value)
222 	{
223 		return value.IsEmpty();
224 	}
225 
226 	void _WriteMaybeQuoted(const char* data)
227 	{
228 		// check whether quoting is needed
229 		bool needsQuoting = false;
230 		bool needsEscaping = false;
231 		for (const char* it = data; *it != '\0'; it++) {
232 			if (isalnum(*it) || *it == '.' || *it == '-' || *it == '_'
233 				|| *it == ':' || *it == '+') {
234 				continue;
235 			}
236 
237 			needsQuoting = true;
238 
239 			if (*it == '\t' || *it == '\n' || *it == '"' || *it == '\\') {
240 				needsEscaping = true;
241 				break;
242 			}
243 		}
244 
245 		if (!needsQuoting) {
246 			_Write(data);
247 			return;
248 		}
249 
250 		// we need quoting
251 		_Write('"');
252 
253 		// escape the string, if necessary
254 		if (needsEscaping) {
255 			const char* start = data;
256 			const char* end = data;
257 			while (*end != '\0') {
258 				char replacement[2];
259 				switch (*end) {
260 					case '\t':
261 						replacement[1] = 't';
262 						break;
263 					case '\n':
264 						replacement[1] = 'n';
265 						break;
266 					case '"':
267 					case '\\':
268 						replacement[1] = *end;
269 						break;
270 					default:
271 						end++;
272 						continue;
273 				}
274 
275 				if (start < end)
276 					_Write(start, end - start);
277 
278 				replacement[0] = '\\';
279 				_Write(replacement, 2);
280 				start = ++end;
281 			}
282 
283 			if (start < end)
284 				_Write(start, end - start);
285 		} else
286 			_Write(data);
287 
288 		_Write('"');
289 	}
290 
291 	inline void _Write(char data)
292 	{
293 		_Write(&data, 1);
294 	}
295 
296 	inline void _Write(const char* data)
297 	{
298 		_Write(data, strlen(data));
299 	}
300 
301 	inline void _Write(const BString& data)
302 	{
303 		_Write(data, data.Length());
304 	}
305 
306 	void _Write(const void* data, size_t size)
307 	{
308 		if (fError == B_OK) {
309 			ssize_t bytesWritten = fData.Write(data, size);
310 			if (bytesWritten < 0)
311 				fError = bytesWritten;
312 		}
313 	}
314 
315 private:
316 	BMallocIO	fData;
317 	status_t	fError;
318 	BString		fBasePackage;
319 };
320 
321 
322 } // namespace BPackageKit
323 
324 
325 #endif	// PACKAGE_INFO_STRING_BUILDER_H
326