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