xref: /haiku/src/kits/package/PackageInfoStringBuilder.h (revision 16c83730262f1e4f0fc69d80744bb36dcfbbe3af)
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 		int32 count = value.CountItems();
134 		if (count == 1) {
135 			_WriteListElement(value.ItemAt(0));
136 		} else {
137 			_Write("{\n", 2);
138 
139 			int32 count = value.CountItems();
140 			for (int32 i = 0; i < count; i++) {
141 				_Write('\t');
142 				_WriteListElement(value.ItemAt(i));
143 				_Write('\n');
144 			}
145 
146 			_Write('}');
147 		}
148 	}
149 
150 	template<typename Value>
151 	void _WriteListElement(const Value* value)
152 	{
153 		_Write(value->ToString());
154 		if (!fBasePackage.IsEmpty()
155 			&& value->Name() == fBasePackage) {
156 			_Write(" base");
157 		}
158 	}
159 
160 	void _WriteListElement(const BGlobalWritableFileInfo* value)
161 	{
162 		_WriteMaybeQuoted(value->Path());
163 		if (value->IsDirectory()) {
164 			_Write(' ');
165 			_Write("directory");
166 		}
167 		if (value->IsIncluded()) {
168 			_Write(' ');
169 			_Write(kWritableFileUpdateTypes[value->UpdateType()]);
170 		}
171 	}
172 
173 	void _WriteListElement(const BUserSettingsFileInfo* value)
174 	{
175 		_WriteMaybeQuoted(value->Path());
176 		if (value->IsDirectory()) {
177 			_Write(" directory");
178 		} else if (!value->TemplatePath().IsEmpty()) {
179 			_Write(" template ");
180 			_WriteMaybeQuoted(value->TemplatePath());
181 		}
182 	}
183 
184 	void _WriteListElement(const BUser* value)
185 	{
186 		_WriteMaybeQuoted(value->Name());
187 
188 		if (!value->RealName().IsEmpty()) {
189 			_Write(" real-name ");
190 			_WriteMaybeQuoted(value->RealName());
191 		}
192 
193 		if (!value->Home().IsEmpty()) {
194 			_Write(" home ");
195 			_WriteMaybeQuoted(value->Home());
196 		}
197 
198 		if (!value->Shell().IsEmpty()) {
199 			_Write(" shell ");
200 			_WriteMaybeQuoted(value->Shell());
201 		}
202 
203 		if (!value->Groups().IsEmpty()) {
204 			_Write(" groups ");
205 			BString groups = value->Groups().Join(" ");
206 			if (groups.IsEmpty()) {
207 				if (fError == B_OK)
208 					fError = B_NO_MEMORY;
209 				return;
210 			}
211 			_Write(groups);
212 		}
213 	}
214 
215 	static inline bool _IsValueEmpty(const char* value)
216 	{
217 		return value[0] == '\0';
218 	}
219 
220 	static inline bool _IsValueEmpty(const BPackageVersion& value)
221 	{
222 		return false;
223 	}
224 
225 	template<typename List>
226 	static inline bool _IsValueEmpty(const List& value)
227 	{
228 		return value.IsEmpty();
229 	}
230 
231 	void _WriteMaybeQuoted(const char* data)
232 	{
233 		// check whether quoting is needed
234 		bool needsQuoting = false;
235 		bool needsEscaping = false;
236 		for (const char* it = data; *it != '\0'; it++) {
237 			if (isalnum(*it) || *it == '.' || *it == '-' || *it == '_'
238 				|| *it == ':' || *it == '+') {
239 				continue;
240 			}
241 
242 			needsQuoting = true;
243 
244 			if (*it == '\t' || *it == '\n' || *it == '"' || *it == '\\') {
245 				needsEscaping = true;
246 				break;
247 			}
248 		}
249 
250 		if (!needsQuoting) {
251 			_Write(data);
252 			return;
253 		}
254 
255 		// we need quoting
256 		_Write('"');
257 
258 		// escape the string, if necessary
259 		if (needsEscaping) {
260 			const char* start = data;
261 			const char* end = data;
262 			while (*end != '\0') {
263 				char replacement[2];
264 				switch (*end) {
265 					case '\t':
266 						replacement[1] = 't';
267 						break;
268 					case '\n':
269 						replacement[1] = 'n';
270 						break;
271 					case '"':
272 					case '\\':
273 						replacement[1] = *end;
274 						break;
275 					default:
276 						end++;
277 						continue;
278 				}
279 
280 				if (start < end)
281 					_Write(start, end - start);
282 
283 				replacement[0] = '\\';
284 				_Write(replacement, 2);
285 			}
286 		} else
287 			_Write(data);
288 
289 		_Write('"');
290 	}
291 
292 	inline void _Write(char data)
293 	{
294 		_Write(&data, 1);
295 	}
296 
297 	inline void _Write(const char* data)
298 	{
299 		_Write(data, strlen(data));
300 	}
301 
302 	inline void _Write(const BString& data)
303 	{
304 		_Write(data, data.Length());
305 	}
306 
307 	void _Write(const void* data, size_t size)
308 	{
309 		if (fError == B_OK) {
310 			ssize_t bytesWritten = fData.Write(data, size);
311 			if (bytesWritten < 0)
312 				fError = bytesWritten;
313 		}
314 	}
315 
316 private:
317 	BMallocIO	fData;
318 	status_t	fError;
319 	BString		fBasePackage;
320 };
321 
322 
323 } // namespace BPackageKit
324 
325 
326 #endif	// PACKAGE_INFO_STRING_BUILDER_H
327