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