1/*
2 * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include <errno.h>
9#include <fcntl.h>
10#include <getopt.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15
16#include <Entry.h>
17
18#include <package/PackageInfo.h>
19#include <package/hpkg/HPKGDefs.h>
20#include <package/hpkg/PackageWriter.h>
21
22#include "package.h"
23#include "PackageWriterListener.h"
24#include "PackageWritingUtils.h"
25
26
27using namespace BPackageKit::BHPKG;
28
29
30int
31command_add(int argc, const char* const* argv)
32{
33	const char* changeToDirectory = NULL;
34	const char* packageInfoFileName = NULL;
35	bool quiet = false;
36	bool verbose = false;
37	bool force = false;
38	int32 compressionLevel = BPackageKit::BHPKG::B_HPKG_COMPRESSION_LEVEL_BEST;
39
40	while (true) {
41		static struct option sLongOptions[] = {
42			{ "help", no_argument, 0, 'h' },
43			{ "quiet", no_argument, 0, 'q' },
44			{ "verbose", no_argument, 0, 'v' },
45			{ 0, 0, 0, 0 }
46		};
47
48		opterr = 0; // don't print errors
49		int c = getopt_long(argc, (char**)argv, "+0123456789C:fhi:qv",
50			sLongOptions, NULL);
51		if (c == -1)
52			break;
53
54		switch (c) {
55			case '0':
56			case '1':
57			case '2':
58			case '3':
59			case '4':
60			case '5':
61			case '6':
62			case '7':
63			case '8':
64			case '9':
65				compressionLevel = c - '0';
66				break;
67
68			case 'C':
69				changeToDirectory = optarg;
70				break;
71
72			case 'h':
73				print_usage_and_exit(false);
74				break;
75
76			case 'f':
77				force = true;
78				break;
79
80			case 'i':
81				packageInfoFileName = optarg;
82				break;
83
84			case 'q':
85				quiet = true;
86				break;
87
88			case 'v':
89				verbose = true;
90				break;
91
92			default:
93				print_usage_and_exit(true);
94				break;
95		}
96	}
97
98	// The remaining arguments are the package file and the entries to add.
99	if (optind >= argc)
100		print_usage_and_exit(true);
101
102	const char* packageFileName = argv[optind++];
103
104	// entries must be specified, if a .PackageInfo hasn't been specified via
105	// an option
106	if (optind >= argc && packageInfoFileName == NULL)
107		print_usage_and_exit(true);
108
109	const char* const* entriesToAdd = argv + optind;
110	int entriesToAddCount = argc - optind;
111
112	// create package
113	BPackageWriterParameters writerParameters;
114	writerParameters.SetFlags(
115		B_HPKG_WRITER_UPDATE_PACKAGE | (force ? B_HPKG_WRITER_FORCE_ADD : 0));
116	writerParameters.SetCompressionLevel(compressionLevel);
117	if (compressionLevel == 0) {
118		writerParameters.SetCompression(
119			BPackageKit::BHPKG::B_HPKG_COMPRESSION_NONE);
120	}
121
122	PackageWriterListener listener(verbose, quiet);
123	BPackageWriter packageWriter(&listener);
124	status_t result = packageWriter.Init(packageFileName, &writerParameters);
125	if (result != B_OK)
126		return 1;
127
128	// If a package info file has been specified explicitly, open it.
129	int packageInfoFD = -1;
130	if (packageInfoFileName != NULL) {
131		packageInfoFD = open(packageInfoFileName, O_RDONLY);
132		if (packageInfoFD < 0) {
133			fprintf(stderr, "Error: Failed to open package info file \"%s\": "
134				"%s\n", packageInfoFileName, strerror(errno));
135		}
136	}
137
138	// change directory, if requested
139	if (changeToDirectory != NULL) {
140		if (chdir(changeToDirectory) != 0) {
141			listener.PrintError(
142				"Error: Failed to change the current working directory to "
143				"\"%s\": %s\n", changeToDirectory, strerror(errno));
144		}
145	}
146
147	// add the entries
148	for (int i = 0; i < entriesToAddCount; i++) {
149		const char* entry = entriesToAdd[i];
150
151		if (strcmp(entry, ".") == 0) {
152			// add all entries in the current directory; skip .PackageInfo,
153			// if a different file was specified
154			if (add_current_directory_entries(packageWriter,
155					listener, packageInfoFileName != NULL) != B_OK)
156				return 1;
157		} else {
158			// skip .PackageInfo, if a different file was specified
159			if (packageInfoFileName != NULL
160				&& strcmp(entry, B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
161				continue;
162			}
163
164			if (packageWriter.AddEntry(entry) != B_OK)
165				return 1;
166		}
167	}
168
169	// add the .PackageInfo, if explicitly specified
170	if (packageInfoFileName != NULL) {
171		result = packageWriter.AddEntry(B_HPKG_PACKAGE_INFO_FILE_NAME,
172			packageInfoFD);
173		if (result != B_OK)
174			return 1;
175	}
176
177	// write the package
178	result = packageWriter.Finish();
179	if (result != B_OK)
180		return 1;
181
182	if (verbose)
183		printf("\nsuccessfully created package '%s'\n", packageFileName);
184
185	return 0;
186}
187