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
33namespace BPackageKit {
34
35namespace BHPKG {
36
37namespace BPrivate {
38
39
40// #pragma mark - AttributeValue
41
42
43WriterImplBase::AttributeValue::AttributeValue()
44	:
45	type(B_HPKG_ATTRIBUTE_TYPE_INVALID),
46	encoding(-1)
47{
48}
49
50
51WriterImplBase::AttributeValue::~AttributeValue()
52{
53}
54
55
56void
57WriterImplBase::AttributeValue::SetTo(int8 value)
58{
59	signedInt = value;
60	type = B_HPKG_ATTRIBUTE_TYPE_INT;
61}
62
63
64void
65WriterImplBase::AttributeValue::SetTo(uint8 value)
66{
67	unsignedInt = value;
68	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
69}
70
71
72void
73WriterImplBase::AttributeValue::SetTo(int16 value)
74{
75	signedInt = value;
76	type = B_HPKG_ATTRIBUTE_TYPE_INT;
77}
78
79
80void
81WriterImplBase::AttributeValue::SetTo(uint16 value)
82{
83	unsignedInt = value;
84	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
85}
86
87
88void
89WriterImplBase::AttributeValue::SetTo(int32 value)
90{
91	signedInt = value;
92	type = B_HPKG_ATTRIBUTE_TYPE_INT;
93}
94
95
96void
97WriterImplBase::AttributeValue::SetTo(uint32 value)
98{
99	unsignedInt = value;
100	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
101}
102
103
104void
105WriterImplBase::AttributeValue::SetTo(int64 value)
106{
107	signedInt = value;
108	type = B_HPKG_ATTRIBUTE_TYPE_INT;
109}
110
111
112void
113WriterImplBase::AttributeValue::SetTo(uint64 value)
114{
115	unsignedInt = value;
116	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
117}
118
119
120void
121WriterImplBase::AttributeValue::SetTo(CachedString* value)
122{
123	string = value;
124	type = B_HPKG_ATTRIBUTE_TYPE_STRING;
125}
126
127
128void
129WriterImplBase::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
138void
139WriterImplBase::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
149uint8
150WriterImplBase::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
172WriterImplBase::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
188WriterImplBase::PackageAttribute::PackageAttribute(BHPKGAttributeID id_,
189	uint8 type_, uint8 encoding_)
190	:
191	id(id_)
192{
193	type = type_;
194	encoding = encoding_;
195}
196
197
198WriterImplBase::PackageAttribute::~PackageAttribute()
199{
200	_DeleteChildren();
201}
202
203
204void
205WriterImplBase::PackageAttribute::AddChild(PackageAttribute* child)
206{
207	children.Add(child);
208}
209
210
211void
212WriterImplBase::PackageAttribute::_DeleteChildren()
213{
214	while (PackageAttribute* child = children.RemoveHead())
215		delete child;
216}
217
218
219// #pragma mark - WriterImplBase
220
221
222WriterImplBase::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
240WriterImplBase::~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
258status_t
259WriterImplBase::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
298status_t
299WriterImplBase::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
369void
370WriterImplBase::SetCompression(uint32 compression)
371{
372	fParameters.SetCompression(compression);
373}
374
375
376void
377WriterImplBase::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
572void
573WriterImplBase::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
601void
602WriterImplBase::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
624WriterImplBase::PackageAttribute*
625WriterImplBase::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
636int32
637WriterImplBase::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
680int32
681WriterImplBase::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
697void
698WriterImplBase::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
762void
763WriterImplBase::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
777void
778WriterImplBase::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
790void
791WriterImplBase::_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
799void
800WriterImplBase::_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