xref: /haiku/src/bin/package/command_create.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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 
28 using BPackageKit::BHPKG::BPackageWriter;
29 using BPackageKit::BHPKG::BPackageWriterListener;
30 using BPackageKit::BHPKG::BPackageWriterParameters;
31 
32 
33 int
34 command_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