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#include <Path.h>
18
19#include <package/PackageInfo.h>
20#include <package/hpkg/HPKGDefs.h>
21#include <package/hpkg/PackageWriter.h>
22
23#include "package.h"
24#include "PackageWriterListener.h"
25#include "PackageWritingUtils.h"
26
27
28using BPackageKit::BHPKG::BPackageWriter;
29using BPackageKit::BHPKG::BPackageWriterListener;
30using BPackageKit::BHPKG::BPackageWriterParameters;
31
32
33int
34command_create(int argc, const char* const* argv)
35{
36	const char* changeToDirectory = NULL;
37	const char* packageInfoFileName = NULL;
38	const char* installPath = NULL;
39	bool isBuildPackage = false;
40	bool quiet = false;
41	bool verbose = false;
42	int32 compressionLevel = BPackageKit::BHPKG::B_HPKG_COMPRESSION_LEVEL_BEST;
43	int32 compression = parse_compression_argument(NULL);
44
45	while (true) {
46		static struct option sLongOptions[] = {
47			{ "help", no_argument, 0, 'h' },
48			{ "quiet", no_argument, 0, 'q' },
49			{ "verbose", no_argument, 0, 'v' },
50			{ 0, 0, 0, 0 }
51		};
52
53		opterr = 0; // don't print errors
54		int c = getopt_long(argc, (char**)argv, "+b0123456789C:hi:I:z:qv",
55			sLongOptions, NULL);
56		if (c == -1)
57			break;
58
59		switch (c) {
60			case '0':
61			case '1':
62			case '2':
63			case '3':
64			case '4':
65			case '5':
66			case '6':
67			case '7':
68			case '8':
69			case '9':
70				compressionLevel = c - '0';
71				break;
72
73			case 'b':
74				isBuildPackage = true;
75				break;
76
77			case 'C':
78				changeToDirectory = optarg;
79				break;
80
81			case 'h':
82				print_usage_and_exit(false);
83				break;
84
85			case 'i':
86				packageInfoFileName = optarg;
87				break;
88
89			case 'I':
90				installPath = optarg;
91				break;
92
93			case 'z':
94				compression = parse_compression_argument(optarg);
95				break;
96
97			case 'q':
98				quiet = true;
99				break;
100
101			case 'v':
102				verbose = true;
103				break;
104
105			default:
106				print_usage_and_exit(true);
107				break;
108		}
109	}
110
111	// The remaining arguments is the package file, i.e. one more argument.
112	if (optind + 1 != argc)
113		print_usage_and_exit(true);
114
115	const char* packageFileName = argv[optind++];
116
117	// -I is only allowed when -b is given
118	if (installPath != NULL && !isBuildPackage) {
119		fprintf(stderr, "Error: \"-I\" is only allowed when \"-b\" is "
120			"given.\n");
121		return 1;
122	}
123
124	BPath outputPath(packageFileName, NULL, true);
125	BPath inputPath(changeToDirectory, NULL, true);
126	BPath parent;
127	while (outputPath.GetParent(&parent) == B_OK) {
128		if (outputPath == inputPath) {
129			fprintf(stderr, "Error: output package can't be in the same "
130				"directory as input files.");
131			return 1;
132		}
133		outputPath = parent;
134	}
135
136	// create package
137	BPackageWriterParameters writerParameters;
138	writerParameters.SetCompressionLevel(compressionLevel);
139	if (compressionLevel == 0) {
140		writerParameters.SetCompression(
141			BPackageKit::BHPKG::B_HPKG_COMPRESSION_NONE);
142	}
143
144	if (compressionLevel == 0)
145		compression = BPackageKit::BHPKG::B_HPKG_COMPRESSION_NONE;
146	writerParameters.SetCompression(compression);
147
148	PackageWriterListener listener(verbose, quiet);
149	BPackageWriter packageWriter(&listener);
150	status_t result = packageWriter.Init(packageFileName, &writerParameters);
151	if (result != B_OK)
152		return 1;
153
154	// If a package info file has been specified explicitly, open it.
155	int packageInfoFD = -1;
156	if (packageInfoFileName != NULL) {
157		packageInfoFD = open(packageInfoFileName, O_RDONLY);
158		if (packageInfoFD < 0) {
159			fprintf(stderr, "Error: Failed to open package info file \"%s\": "
160				"%s\n", packageInfoFileName, strerror(errno));
161			return 1;
162		}
163	}
164
165	// change directory, if requested
166	if (changeToDirectory != NULL) {
167		if (chdir(changeToDirectory) != 0) {
168			listener.PrintError(
169				"Error: Failed to change the current working directory to "
170				"\"%s\": %s\n", changeToDirectory, strerror(errno));
171			return 1;
172		}
173	}
174
175	if (isBuildPackage)
176		packageWriter.SetCheckLicenses(false);
177
178	// set install path, if specified
179	if (installPath != NULL) {
180		result = packageWriter.SetInstallPath(installPath);
181		if (result != B_OK) {
182			fprintf(stderr, "Error: Failed to set the package install path: "
183				"%s\n", strerror(result));
184			return 1;
185		}
186	}
187
188	// add all files of the current directory, save for the .PackageInfo
189	if (!isBuildPackage) {
190		if (add_current_directory_entries(packageWriter, listener, true)
191				!= B_OK) {
192			return 1;
193		}
194	}
195
196	// add the .PackageInfo
197	result = packageWriter.AddEntry(
198		BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME, packageInfoFD);
199	if (result != B_OK)
200		return 1;
201
202	// write the package
203	result = packageWriter.Finish();
204	if (result != B_OK)
205		return 1;
206
207	if (verbose)
208		printf("\nsuccessfully created package '%s'\n", packageFileName);
209
210	return 0;
211}
212