xref: /haiku/src/kits/package/hpkg/WriterImplBase.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <package/hpkg/WriterImplBase.h>
8 
9 #include <errno.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 
15 #include <algorithm>
16 #include <new>
17 
18 #include <ByteOrder.h>
19 #include <File.h>
20 
21 #include <AutoDeleter.h>
22 #include <ZlibCompressionAlgorithm.h>
23 #ifdef ZSTD_ENABLED
24 #include <ZstdCompressionAlgorithm.h>
25 #endif
26 
27 #include <package/hpkg/DataReader.h>
28 #include <package/hpkg/ErrorOutput.h>
29 
30 #include <package/hpkg/HPKGDefsPrivate.h>
31 
32 
33 namespace BPackageKit {
34 
35 namespace BHPKG {
36 
37 namespace BPrivate {
38 
39 
40 // #pragma mark - AttributeValue
41 
42 
43 WriterImplBase::AttributeValue::AttributeValue()
44 	:
45 	type(B_HPKG_ATTRIBUTE_TYPE_INVALID),
46 	encoding(-1)
47 {
48 }
49 
50 
51 WriterImplBase::AttributeValue::~AttributeValue()
52 {
53 }
54 
55 
56 void
57 WriterImplBase::AttributeValue::SetTo(int8 value)
58 {
59 	signedInt = value;
60 	type = B_HPKG_ATTRIBUTE_TYPE_INT;
61 }
62 
63 
64 void
65 WriterImplBase::AttributeValue::SetTo(uint8 value)
66 {
67 	unsignedInt = value;
68 	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
69 }
70 
71 
72 void
73 WriterImplBase::AttributeValue::SetTo(int16 value)
74 {
75 	signedInt = value;
76 	type = B_HPKG_ATTRIBUTE_TYPE_INT;
77 }
78 
79 
80 void
81 WriterImplBase::AttributeValue::SetTo(uint16 value)
82 {
83 	unsignedInt = value;
84 	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
85 }
86 
87 
88 void
89 WriterImplBase::AttributeValue::SetTo(int32 value)
90 {
91 	signedInt = value;
92 	type = B_HPKG_ATTRIBUTE_TYPE_INT;
93 }
94 
95 
96 void
97 WriterImplBase::AttributeValue::SetTo(uint32 value)
98 {
99 	unsignedInt = value;
100 	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
101 }
102 
103 
104 void
105 WriterImplBase::AttributeValue::SetTo(int64 value)
106 {
107 	signedInt = value;
108 	type = B_HPKG_ATTRIBUTE_TYPE_INT;
109 }
110 
111 
112 void
113 WriterImplBase::AttributeValue::SetTo(uint64 value)
114 {
115 	unsignedInt = value;
116 	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
117 }
118 
119 
120 void
121 WriterImplBase::AttributeValue::SetTo(CachedString* value)
122 {
123 	string = value;
124 	type = B_HPKG_ATTRIBUTE_TYPE_STRING;
125 }
126 
127 
128 void
129 WriterImplBase::AttributeValue::SetToData(uint64 size, uint64 offset)
130 {
131 	data.size = size;
132 	data.offset = offset;
133 	type = B_HPKG_ATTRIBUTE_TYPE_RAW;
134 	encoding = B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP;
135 }
136 
137 
138 void
139 WriterImplBase::AttributeValue::SetToData(uint64 size, const void* rawData)
140 {
141 	data.size = size;
142 	if (size > 0)
143 		memcpy(data.raw, rawData, size);
144 	type = B_HPKG_ATTRIBUTE_TYPE_RAW;
145 	encoding = B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE;
146 }
147 
148 
149 uint8
150 WriterImplBase::AttributeValue::ApplicableEncoding() const
151 {
152 	switch (type) {
153 		case B_HPKG_ATTRIBUTE_TYPE_INT:
154 			return _ApplicableIntEncoding(signedInt >= 0
155 				? (uint64)signedInt << 1
156 				: (uint64)(-(signedInt + 1) << 1));
157 		case B_HPKG_ATTRIBUTE_TYPE_UINT:
158 			return _ApplicableIntEncoding(unsignedInt);
159 		case B_HPKG_ATTRIBUTE_TYPE_STRING:
160 			return string->index >= 0
161 				? B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE
162 				: B_HPKG_ATTRIBUTE_ENCODING_STRING_INLINE;
163 		case B_HPKG_ATTRIBUTE_TYPE_RAW:
164 			return encoding;
165 		default:
166 			return 0;
167 	}
168 }
169 
170 
171 /*static*/ uint8
172 WriterImplBase::AttributeValue::_ApplicableIntEncoding(uint64 value)
173 {
174 	if (value <= 0xff)
175 		return B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT;
176 	if (value <= 0xffff)
177 		return B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT;
178 	if (value <= 0xffffffff)
179 		return B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT;
180 
181 	return B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT;
182 }
183 
184 
185 // #pragma mark - PackageAttribute
186 
187 
188 WriterImplBase::PackageAttribute::PackageAttribute(BHPKGAttributeID id_,
189 	uint8 type_, uint8 encoding_)
190 	:
191 	id(id_)
192 {
193 	type = type_;
194 	encoding = encoding_;
195 }
196 
197 
198 WriterImplBase::PackageAttribute::~PackageAttribute()
199 {
200 	_DeleteChildren();
201 }
202 
203 
204 void
205 WriterImplBase::PackageAttribute::AddChild(PackageAttribute* child)
206 {
207 	children.Add(child);
208 }
209 
210 
211 void
212 WriterImplBase::PackageAttribute::_DeleteChildren()
213 {
214 	while (PackageAttribute* child = children.RemoveHead())
215 		delete child;
216 }
217 
218 
219 // #pragma mark - WriterImplBase
220 
221 
222 WriterImplBase::WriterImplBase(const char* fileType, BErrorOutput* errorOutput)
223 	:
224 	fHeapWriter(NULL),
225 	fCompressionAlgorithm(NULL),
226 	fCompressionParameters(NULL),
227 	fDecompressionAlgorithm(NULL),
228 	fDecompressionParameters(NULL),
229 	fFileType(fileType),
230 	fErrorOutput(errorOutput),
231 	fFileName(NULL),
232 	fParameters(),
233 	fFile(NULL),
234 	fOwnsFile(false),
235 	fFinished(false)
236 {
237 }
238 
239 
240 WriterImplBase::~WriterImplBase()
241 {
242 	delete fHeapWriter;
243 	delete fCompressionAlgorithm;
244 	delete fCompressionParameters;
245 	delete fDecompressionAlgorithm;
246 	delete fDecompressionParameters;
247 
248 	if (fOwnsFile)
249 		delete fFile;
250 
251 	if (!fFinished && fFileName != NULL
252 		&& (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) == 0) {
253 		unlink(fFileName);
254 	}
255 }
256 
257 
258 status_t
259 WriterImplBase::Init(BPositionIO* file, bool keepFile, const char* fileName,
260 	const BPackageWriterParameters& parameters)
261 {
262 	fParameters = parameters;
263 
264 	if (fPackageStringCache.Init() != B_OK)
265 		throw std::bad_alloc();
266 
267 	if (file == NULL) {
268 		if (fileName == NULL)
269 			return B_BAD_VALUE;
270 
271 		// open file (don't truncate in update mode)
272 		int openMode = O_RDWR;
273 		if ((parameters.Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) == 0)
274 			openMode |= O_CREAT | O_TRUNC;
275 
276 		BFile* newFile = new BFile;
277 		status_t error = newFile->SetTo(fileName, openMode);
278 		if (error != B_OK) {
279 			fErrorOutput->PrintError("Failed to open %s file \"%s\": %s\n",
280 				fFileType, fileName, strerror(errno));
281 			delete newFile;
282 			return error;
283 		}
284 
285 		fFile = newFile;
286 		fOwnsFile = true;
287 	} else {
288 		fFile = file;
289 		fOwnsFile = keepFile;
290 	}
291 
292 	fFileName = fileName;
293 
294 	return B_OK;
295 }
296 
297 
298 status_t
299 WriterImplBase::InitHeapReader(size_t headerSize)
300 {
301 	// allocate the compression/decompression algorithm
302 	CompressionAlgorithmOwner* compressionAlgorithm = NULL;
303 	BReference<CompressionAlgorithmOwner> compressionAlgorithmReference;
304 
305 	DecompressionAlgorithmOwner* decompressionAlgorithm = NULL;
306 	BReference<DecompressionAlgorithmOwner> decompressionAlgorithmReference;
307 
308 	switch (fParameters.Compression()) {
309 		case B_HPKG_COMPRESSION_NONE:
310 			break;
311 		case B_HPKG_COMPRESSION_ZLIB:
312 			compressionAlgorithm = CompressionAlgorithmOwner::Create(
313 				new(std::nothrow) BZlibCompressionAlgorithm,
314 				new(std::nothrow) BZlibCompressionParameters(
315 					fParameters.CompressionLevel()));
316 			compressionAlgorithmReference.SetTo(compressionAlgorithm, true);
317 
318 			decompressionAlgorithm = DecompressionAlgorithmOwner::Create(
319 				new(std::nothrow) BZlibCompressionAlgorithm,
320 				new(std::nothrow) BZlibDecompressionParameters);
321 			decompressionAlgorithmReference.SetTo(decompressionAlgorithm, true);
322 
323 			if (compressionAlgorithm == NULL
324 				|| compressionAlgorithm->algorithm == NULL
325 				|| compressionAlgorithm->parameters == NULL
326 				|| decompressionAlgorithm == NULL
327 				|| decompressionAlgorithm->algorithm == NULL
328 				|| decompressionAlgorithm->parameters == NULL) {
329 				throw std::bad_alloc();
330 			}
331 			break;
332 #ifdef ZSTD_ENABLED
333 		case B_HPKG_COMPRESSION_ZSTD:
334 			compressionAlgorithm = CompressionAlgorithmOwner::Create(
335 				new(std::nothrow) BZstdCompressionAlgorithm,
336 				new(std::nothrow) BZstdCompressionParameters(
337 					fParameters.CompressionLevel()));
338 			compressionAlgorithmReference.SetTo(compressionAlgorithm, true);
339 
340 			decompressionAlgorithm = DecompressionAlgorithmOwner::Create(
341 				new(std::nothrow) BZstdCompressionAlgorithm,
342 				new(std::nothrow) BZstdDecompressionParameters);
343 			decompressionAlgorithmReference.SetTo(decompressionAlgorithm, true);
344 
345 			if (compressionAlgorithm == NULL
346 				|| compressionAlgorithm->algorithm == NULL
347 				|| compressionAlgorithm->parameters == NULL
348 				|| decompressionAlgorithm == NULL
349 				|| decompressionAlgorithm->algorithm == NULL
350 				|| decompressionAlgorithm->parameters == NULL) {
351 				throw std::bad_alloc();
352 			}
353 			break;
354 #endif
355 		default:
356 			fErrorOutput->PrintError("Error: Invalid heap compression\n");
357 			return B_BAD_VALUE;
358 	}
359 
360 	// create heap writer
361 	fHeapWriter = new PackageFileHeapWriter(fErrorOutput, fFile, headerSize,
362 		compressionAlgorithm, decompressionAlgorithm);
363 	fHeapWriter->Init();
364 
365 	return B_OK;
366 }
367 
368 
369 void
370 WriterImplBase::SetCompression(uint32 compression)
371 {
372 	fParameters.SetCompression(compression);
373 }
374 
375 
376 void
377 WriterImplBase::RegisterPackageInfo(PackageAttributeList& attributeList,
378 	const BPackageInfo& packageInfo)
379 {
380 	// name
381 	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_NAME, packageInfo.Name(),
382 		attributeList);
383 
384 	// summary
385 	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_SUMMARY,
386 		packageInfo.Summary(), attributeList);
387 
388 	// description
389 	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_DESCRIPTION,
390 		packageInfo.Description(), attributeList);
391 
392 	// vendor
393 	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_VENDOR,
394 		packageInfo.Vendor(), attributeList);
395 
396 	// packager
397 	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_PACKAGER,
398 		packageInfo.Packager(), attributeList);
399 
400 	// base package (optional)
401 	_AddStringAttributeIfNotEmpty(B_HPKG_ATTRIBUTE_ID_PACKAGE_BASE_PACKAGE,
402 		packageInfo.BasePackage(), attributeList);
403 
404 	// flags
405 	PackageAttribute* flags = new PackageAttribute(
406 		B_HPKG_ATTRIBUTE_ID_PACKAGE_FLAGS, B_HPKG_ATTRIBUTE_TYPE_UINT,
407 		B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT);
408 	flags->unsignedInt = packageInfo.Flags();
409 	attributeList.Add(flags);
410 
411 	// architecture
412 	PackageAttribute* architecture = new PackageAttribute(
413 		B_HPKG_ATTRIBUTE_ID_PACKAGE_ARCHITECTURE, B_HPKG_ATTRIBUTE_TYPE_UINT,
414 		B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
415 	architecture->unsignedInt = packageInfo.Architecture();
416 	attributeList.Add(architecture);
417 
418 	// version
419 	RegisterPackageVersion(attributeList, packageInfo.Version());
420 
421 	// copyright list
422 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_COPYRIGHT,
423 			packageInfo.CopyrightList(), attributeList);
424 
425 	// license list
426 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_LICENSE,
427 		packageInfo.LicenseList(), attributeList);
428 
429 	// URL list
430 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_URL,
431 		packageInfo.URLList(), attributeList);
432 
433 	// source URL list
434 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_SOURCE_URL,
435 		packageInfo.SourceURLList(), attributeList);
436 
437 	// provides list
438 	const BObjectList<BPackageResolvable>& providesList
439 		= packageInfo.ProvidesList();
440 	for (int i = 0; i < providesList.CountItems(); ++i) {
441 		BPackageResolvable* resolvable = providesList.ItemAt(i);
442 		bool hasVersion = resolvable->Version().InitCheck() == B_OK;
443 		bool hasCompatibleVersion
444 			= resolvable->CompatibleVersion().InitCheck() == B_OK;
445 
446 		PackageAttribute* provides = AddStringAttribute(
447 			B_HPKG_ATTRIBUTE_ID_PACKAGE_PROVIDES, resolvable->Name(),
448 			attributeList);
449 
450 		if (hasVersion)
451 			RegisterPackageVersion(provides->children, resolvable->Version());
452 
453 		if (hasCompatibleVersion) {
454 			RegisterPackageVersion(provides->children,
455 				resolvable->CompatibleVersion(),
456 				B_HPKG_ATTRIBUTE_ID_PACKAGE_PROVIDES_COMPATIBLE);
457 		}
458 	}
459 
460 	// requires list
461 	RegisterPackageResolvableExpressionList(attributeList,
462 		packageInfo.RequiresList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_REQUIRES);
463 
464 	// supplements list
465 	RegisterPackageResolvableExpressionList(attributeList,
466 		packageInfo.SupplementsList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_SUPPLEMENTS);
467 
468 	// conflicts list
469 	RegisterPackageResolvableExpressionList(attributeList,
470 		packageInfo.ConflictsList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_CONFLICTS);
471 
472 	// freshens list
473 	RegisterPackageResolvableExpressionList(attributeList,
474 		packageInfo.FreshensList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_FRESHENS);
475 
476 	// replaces list
477 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_REPLACES,
478 		packageInfo.ReplacesList(), attributeList);
479 
480 	// global writable file info list
481 	const BObjectList<BGlobalWritableFileInfo>& globalWritableFileInfos
482 		= packageInfo.GlobalWritableFileInfos();
483 	for (int32 i = 0; i < globalWritableFileInfos.CountItems(); ++i) {
484 		BGlobalWritableFileInfo* info = globalWritableFileInfos.ItemAt(i);
485 		PackageAttribute* attribute = AddStringAttribute(
486 			B_HPKG_ATTRIBUTE_ID_PACKAGE_GLOBAL_WRITABLE_FILE, info->Path(),
487 			attributeList);
488 
489 		if (info->IsDirectory()) {
490 			PackageAttribute* isDirectoryAttribute = new PackageAttribute(
491 				B_HPKG_ATTRIBUTE_ID_PACKAGE_IS_WRITABLE_DIRECTORY,
492 				B_HPKG_ATTRIBUTE_TYPE_UINT,
493 				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
494 			isDirectoryAttribute->unsignedInt = 1;
495 			attribute->children.Add(isDirectoryAttribute);
496 		}
497 
498 		if (info->IsIncluded()) {
499 			PackageAttribute* updateTypeAttribute = new PackageAttribute(
500 				B_HPKG_ATTRIBUTE_ID_PACKAGE_WRITABLE_FILE_UPDATE_TYPE,
501 				B_HPKG_ATTRIBUTE_TYPE_UINT,
502 				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
503 			updateTypeAttribute->unsignedInt = info->UpdateType();
504 			attribute->children.Add(updateTypeAttribute);
505 		}
506 	}
507 
508 	// user settings file info list
509 	const BObjectList<BUserSettingsFileInfo>& userSettingsFileInfos
510 		= packageInfo.UserSettingsFileInfos();
511 	for (int32 i = 0; i < userSettingsFileInfos.CountItems(); ++i) {
512 		BUserSettingsFileInfo* info = userSettingsFileInfos.ItemAt(i);
513 		PackageAttribute* attribute = AddStringAttribute(
514 			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_SETTINGS_FILE, info->Path(),
515 			attributeList);
516 
517 		if (info->IsDirectory()) {
518 			PackageAttribute* isDirectoryAttribute = new PackageAttribute(
519 				B_HPKG_ATTRIBUTE_ID_PACKAGE_IS_WRITABLE_DIRECTORY,
520 				B_HPKG_ATTRIBUTE_TYPE_UINT,
521 				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
522 			isDirectoryAttribute->unsignedInt = 1;
523 			attribute->children.Add(isDirectoryAttribute);
524 		} else {
525 			_AddStringAttributeIfNotEmpty(
526 				B_HPKG_ATTRIBUTE_ID_PACKAGE_SETTINGS_FILE_TEMPLATE,
527 				info->TemplatePath(), attribute->children);
528 		}
529 	}
530 
531 	// user list
532 	const BObjectList<BUser>& users = packageInfo.Users();
533 	for (int32 i = 0; i < users.CountItems(); ++i) {
534 		const BUser* user = users.ItemAt(i);
535 		PackageAttribute* attribute = AddStringAttribute(
536 			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER, user->Name(), attributeList);
537 
538 		_AddStringAttributeIfNotEmpty(
539 			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_REAL_NAME, user->RealName(),
540 			attribute->children);
541 		_AddStringAttributeIfNotEmpty(
542 			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_HOME, user->Home(),
543 			attribute->children);
544 		_AddStringAttributeIfNotEmpty(
545 			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_SHELL, user->Shell(),
546 			attribute->children);
547 
548 		for (int32 k = 0; k < user->Groups().CountStrings(); k++) {
549 			AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_GROUP,
550 				user->Groups().StringAt(k), attribute->children);
551 		}
552 	}
553 
554 	// group list
555 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_GROUP,
556 		packageInfo.Groups(), attributeList);
557 
558 	// post install script list
559 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_POST_INSTALL_SCRIPT,
560 		packageInfo.PostInstallScripts(), attributeList);
561 
562 	// checksum (optional, only exists in repositories)
563 	_AddStringAttributeIfNotEmpty(B_HPKG_ATTRIBUTE_ID_PACKAGE_CHECKSUM,
564 		packageInfo.Checksum(), attributeList);
565 
566 	// install path (optional)
567 	_AddStringAttributeIfNotEmpty(B_HPKG_ATTRIBUTE_ID_PACKAGE_INSTALL_PATH,
568 		packageInfo.InstallPath(), attributeList);
569 }
570 
571 
572 void
573 WriterImplBase::RegisterPackageVersion(PackageAttributeList& attributeList,
574 	const BPackageVersion& version, BHPKGAttributeID attributeID)
575 {
576 	PackageAttribute* versionMajor = AddStringAttribute(attributeID,
577 		version.Major(), attributeList);
578 
579 	if (!version.Minor().IsEmpty()) {
580 		AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_MINOR,
581 			version.Minor(), versionMajor->children);
582 		_AddStringAttributeIfNotEmpty(
583 			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_MICRO, version.Micro(),
584 			versionMajor->children);
585 	}
586 
587 	_AddStringAttributeIfNotEmpty(
588 		B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE,
589 		version.PreRelease(), versionMajor->children);
590 
591 	if (version.Revision() != 0) {
592 		PackageAttribute* versionRevision = new PackageAttribute(
593 			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_REVISION,
594 			B_HPKG_ATTRIBUTE_TYPE_UINT, B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT);
595 		versionRevision->unsignedInt = version.Revision();
596 		versionMajor->children.Add(versionRevision);
597 	}
598 }
599 
600 
601 void
602 WriterImplBase::RegisterPackageResolvableExpressionList(
603 	PackageAttributeList& attributeList,
604 	const BObjectList<BPackageResolvableExpression>& expressionList, uint8 id)
605 {
606 	for (int i = 0; i < expressionList.CountItems(); ++i) {
607 		BPackageResolvableExpression* resolvableExpr = expressionList.ItemAt(i);
608 		PackageAttribute* name = AddStringAttribute((BHPKGAttributeID)id,
609 			resolvableExpr->Name(), attributeList);
610 
611 		if (resolvableExpr->Version().InitCheck() == B_OK) {
612 			PackageAttribute* op = new PackageAttribute(
613 				B_HPKG_ATTRIBUTE_ID_PACKAGE_RESOLVABLE_OPERATOR,
614 				B_HPKG_ATTRIBUTE_TYPE_UINT,
615 				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
616 			op->unsignedInt = resolvableExpr->Operator();
617 			name->children.Add(op);
618 			RegisterPackageVersion(name->children, resolvableExpr->Version());
619 		}
620 	}
621 }
622 
623 
624 WriterImplBase::PackageAttribute*
625 WriterImplBase::AddStringAttribute(BHPKGAttributeID id, const BString& value,
626 	DoublyLinkedList<PackageAttribute>& list)
627 {
628 	PackageAttribute* attribute = new PackageAttribute(id,
629 		B_HPKG_ATTRIBUTE_TYPE_STRING, B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
630 	attribute->string = fPackageStringCache.Get(value);
631 	list.Add(attribute);
632 	return attribute;
633 }
634 
635 
636 int32
637 WriterImplBase::WriteCachedStrings(const StringCache& cache,
638 	uint32 minUsageCount)
639 {
640 	// create an array of the cached strings
641 	int32 count = cache.CountElements();
642 	CachedString** cachedStrings = new CachedString*[count];
643 	ArrayDeleter<CachedString*> cachedStringsDeleter(cachedStrings);
644 
645 	int32 index = 0;
646 	for (CachedStringTable::Iterator it = cache.GetIterator();
647 			CachedString* string = it.Next();) {
648 		cachedStrings[index++] = string;
649 	}
650 
651 	// sort it by descending usage count
652 	std::sort(cachedStrings, cachedStrings + count, CachedStringUsageGreater());
653 
654 	// assign the indices and write entries to disk
655 	int32 stringsWritten = 0;
656 	for (int32 i = 0; i < count; i++) {
657 		CachedString* cachedString = cachedStrings[i];
658 
659 		// empty strings must be stored inline, as they can't be distinguished
660 		// from the end-marker!
661 		if (strlen(cachedString->string) == 0)
662 			continue;
663 
664 		// strings that are used only once are better stored inline
665 		if (cachedString->usageCount < minUsageCount)
666 			break;
667 
668 		WriteString(cachedString->string);
669 
670 		cachedString->index = stringsWritten++;
671 	}
672 
673 	// write a terminating 0 byte
674 	Write<uint8>(0);
675 
676 	return stringsWritten;
677 }
678 
679 
680 int32
681 WriterImplBase::WritePackageAttributes(
682 	const PackageAttributeList& packageAttributes,
683 	uint32& _stringsLengthUncompressed)
684 {
685 	// write the cached strings
686 	uint64 startOffset = fHeapWriter->UncompressedHeapSize();
687 	uint32 stringsCount = WriteCachedStrings(fPackageStringCache, 2);
688 	_stringsLengthUncompressed
689 		= fHeapWriter->UncompressedHeapSize() - startOffset;
690 
691 	_WritePackageAttributes(packageAttributes);
692 
693 	return stringsCount;
694 }
695 
696 
697 void
698 WriterImplBase::WriteAttributeValue(const AttributeValue& value, uint8 encoding)
699 {
700 	switch (value.type) {
701 		case B_HPKG_ATTRIBUTE_TYPE_INT:
702 		case B_HPKG_ATTRIBUTE_TYPE_UINT:
703 		{
704 			uint64 intValue = value.type == B_HPKG_ATTRIBUTE_TYPE_INT
705 				? (uint64)value.signedInt : value.unsignedInt;
706 
707 			switch (encoding) {
708 				case B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT:
709 					Write<uint8>((uint8)intValue);
710 					break;
711 				case B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT:
712 					Write<uint16>(
713 						B_HOST_TO_BENDIAN_INT16((uint16)intValue));
714 					break;
715 				case B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT:
716 					Write<uint32>(
717 						B_HOST_TO_BENDIAN_INT32((uint32)intValue));
718 					break;
719 				case B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT:
720 					Write<uint64>(
721 						B_HOST_TO_BENDIAN_INT64((uint64)intValue));
722 					break;
723 				default:
724 				{
725 					fErrorOutput->PrintError("WriteAttributeValue(): invalid "
726 						"encoding %d for int value type %d\n", encoding,
727 						value.type);
728 					throw status_t(B_BAD_VALUE);
729 				}
730 			}
731 
732 			break;
733 		}
734 
735 		case B_HPKG_ATTRIBUTE_TYPE_STRING:
736 		{
737 			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE)
738 				WriteUnsignedLEB128(value.string->index);
739 			else
740 				WriteString(value.string->string);
741 			break;
742 		}
743 
744 		case B_HPKG_ATTRIBUTE_TYPE_RAW:
745 		{
746 			WriteUnsignedLEB128(value.data.size);
747 			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP)
748 				WriteUnsignedLEB128(value.data.offset);
749 			else
750 				fHeapWriter->AddDataThrows(value.data.raw, value.data.size);
751 			break;
752 		}
753 
754 		default:
755 			fErrorOutput->PrintError(
756 				"WriteAttributeValue(): invalid value type: %d\n", value.type);
757 			throw status_t(B_BAD_VALUE);
758 	}
759 }
760 
761 
762 void
763 WriterImplBase::WriteUnsignedLEB128(uint64 value)
764 {
765 	uint8 bytes[10];
766 	int32 count = 0;
767 	do {
768 		uint8 byte = value & 0x7f;
769 		value >>= 7;
770 		bytes[count++] = byte | (value != 0 ? 0x80 : 0);
771 	} while (value != 0);
772 
773 	fHeapWriter->AddDataThrows(bytes, count);
774 }
775 
776 
777 void
778 WriterImplBase::RawWriteBuffer(const void* buffer, size_t size, off_t offset)
779 {
780 	status_t error = fFile->WriteAtExactly(offset, buffer, size);
781 	if (error != B_OK) {
782 		fErrorOutput->PrintError(
783 			"RawWriteBuffer(%p, %lu) failed to write data: %s\n", buffer, size,
784 			strerror(error));
785 		throw error;
786 	}
787 }
788 
789 
790 void
791 WriterImplBase::_AddStringAttributeList(BHPKGAttributeID id,
792 	const BStringList& value, DoublyLinkedList<PackageAttribute>& list)
793 {
794 	for (int32 i = 0; i < value.CountStrings(); i++)
795 		AddStringAttribute(id, value.StringAt(i), list);
796 }
797 
798 
799 void
800 WriterImplBase::_WritePackageAttributes(
801 	const PackageAttributeList& packageAttributes)
802 {
803 	DoublyLinkedList<PackageAttribute>::ConstIterator it
804 		= packageAttributes.GetIterator();
805 	while (PackageAttribute* attribute = it.Next()) {
806 		uint8 encoding = attribute->ApplicableEncoding();
807 
808 		// write tag
809 		WriteUnsignedLEB128(compose_attribute_tag(
810 			attribute->id, attribute->type, encoding,
811 			!attribute->children.IsEmpty()));
812 
813 		// write value
814 		WriteAttributeValue(*attribute, encoding);
815 
816 		if (!attribute->children.IsEmpty())
817 			_WritePackageAttributes(attribute->children);
818 	}
819 
820 	WriteUnsignedLEB128(0);
821 }
822 
823 
824 }	// namespace BPrivate
825 
826 }	// namespace BHPKG
827 
828 }	// namespace BPackageKit
829