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