xref: /haiku/src/tools/opd_to_package_info/opd_to_package_info.cpp (revision 1026b0a1a76dc88927bb8175c470f638dc5464ee)
1 /*
2  * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <ctype.h>
8 #include <errno.h>
9 #include <getopt.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <Message.h>
15 #include <String.h>
16 
17 
18 enum {
19 	FLAG_MANDATORY_FIELD	= 0x01,
20 	FLAG_LIST_ATTRIBUTE		= 0x02,
21 	FLAG_DONT_QUOTE			= 0x04,
22 };
23 
24 
25 extern const char* __progname;
26 const char* kCommandName = __progname;
27 
28 
29 static const char* kUsage =
30 	"Usage: %s [ <options> ] <optional package description> "
31 		"[ <package info> ]\n"
32 	"Converts an .OptionalPackageDescription to a .PackageInfo. If "
33 		"<package info>\n"
34 	"is not specified, the output is printed to stdout.\n"
35 	"Note that the generated .PackageInfo will not be complete. For several\n"
36 	"fields an empty string will be used, unless specified via an option.\n"
37 	"The \"provides\" and \"requires\" lists will always be empty, though\n"
38 	"\n"
39 	"Options:\n"
40 	"  -a <arch>        - Use the given architecture string. Default is to "
41 		"guess from the file name.\n"
42 	"  -d <description> - Use the given descripton string. Default is to use\n"
43 	"                     the summary.\n"
44 	"  -h, --help       - Print this usage info.\n"
45 	"  -p <packager>    - Use the given packager string. Default is an empty "
46 		"string.\n"
47 	"  -s <summary>     - Use the given summary string. Default is an empty "
48 		"string.\n"
49 	"  -v <version>     - Use the given version string. Overrides the version\n"
50 	"                     from the input file.\n"
51 	"  -V <vendor>      - Use the given vendor string. Default is an empty "
52 		"string.\n"
53 ;
54 
55 
56 static void
57 print_usage_and_exit(bool error)
58 {
59     fprintf(error ? stderr : stdout, kUsage, kCommandName);
60     exit(error ? 1 : 0);
61 }
62 
63 
64 static const char*
65 guess_architecture(const char* name)
66 {
67 	if (strstr(name, "x86") != NULL) {
68 		if (strstr(name, "gcc4") != NULL)
69 			return "x86";
70 
71 		return "x86_gcc2";
72 	}
73 
74 	return NULL;
75 }
76 
77 
78 struct OuputWriter {
79 	OuputWriter(FILE* output, const BMessage& package)
80 		:
81 		fOutput(output),
82 		fPackage(package)
83 	{
84 	}
85 
86 	void WriteAttribute(const char* attributeName, const char* fieldName,
87 		const char* defaultValue, uint32 flags)
88 	{
89 		if (fieldName != NULL) {
90 			int32 count;
91 			type_code type;
92 			if (fPackage.GetInfo(fieldName, &type, &count) != B_OK) {
93 				if ((flags & FLAG_MANDATORY_FIELD) != 0) {
94 					fprintf(stderr, "Error: Missing mandatory field \"%s\" in "
95 						"input file.\n", fieldName);
96 					exit(1);
97 				}
98 				count = 0;
99 			}
100 
101 			if (count > 0) {
102 				if (count == 1) {
103 					const char* value;
104 					fPackage.FindString(fieldName, &value);
105 					_WriteSingleElementAttribute(attributeName, value, flags);
106 				} else {
107 					fprintf(fOutput, "\n%s {\n", attributeName);
108 
109 					for (int32 i = 0; i < count; i++) {
110 						fprintf(fOutput, "\t");
111 						const char* value;
112 						fPackage.FindString(fieldName, i, &value);
113 						_WriteValue(value, flags);
114 						fputc('\n', fOutput);
115 					}
116 
117 					fputs("}\n", fOutput);
118 				}
119 
120 				return;
121 			}
122 		}
123 
124 		// write the default value
125 		if (defaultValue != NULL)
126 			_WriteSingleElementAttribute(attributeName, defaultValue, flags);
127 	}
128 
129 private:
130 	void _WriteSingleElementAttribute(const char* attributeName,
131 		const char* value, uint32 flags)
132 	{
133 		fputs(attributeName, fOutput);
134 
135 		int32 indentation = 16 - (int32)strlen(attributeName);
136 		if (indentation > 0)
137 			indentation = (indentation + 3) / 4;
138 		else
139 			indentation = 1;
140 
141 		for (int32 i = 0; i < indentation; i++)
142 			fputc('\t', fOutput);
143 
144 		_WriteValue(value, flags);
145 		fputc('\n',  fOutput);
146 	}
147 
148 	void _WriteValue(const char* value, uint32 flags)
149 	{
150 		BString escapedValue(value);
151 
152 		if ((flags & FLAG_DONT_QUOTE) != 0) {
153 			escapedValue.CharacterEscape("\\\"' \t", '\\');
154 			fputs(escapedValue.String(), fOutput);
155 		} else {
156 			escapedValue.CharacterEscape("\\\"", '\\');
157 			fprintf(fOutput, "\"%s\"", escapedValue.String());
158 		}
159 	}
160 
161 private:
162 	FILE*			fOutput;
163 	const BMessage&	fPackage;
164 };
165 
166 
167 int
168 main(int argc, const char* const* argv)
169 {
170 	const char* architecture = NULL;
171 	const char* version = NULL;
172 	const char* summary = "";
173 	const char* description = "";
174 	const char* packager = "";
175 	const char* vendor = "";
176 
177 	while (true) {
178 		static const struct option kLongOptions[] = {
179 			{ "help", no_argument, 0, 'h' },
180 			{ 0, 0, 0, 0 }
181 		};
182 
183 		opterr = 0; // don't print errors
184 		int c = getopt_long(argc, (char**)argv, "+ha:d:p:s:v:V:", kLongOptions,
185 			NULL);
186 		if (c == -1)
187 			break;
188 
189 		switch (c) {
190 			case 'a':
191 				architecture = optarg;
192 				break;
193 
194 			case 'd':
195 				description = optarg;
196 				break;
197 
198 			case 'h':
199 				print_usage_and_exit(false);
200 				break;
201 
202 			case 'p':
203 				packager = optarg;
204 				break;
205 
206 			case 's':
207 				summary = optarg;
208 				break;
209 
210 			case 'v':
211 				version = optarg;
212 				break;
213 
214 			case 'V':
215 				vendor = optarg;
216 				break;
217 
218 			default:
219 				print_usage_and_exit(true);
220 				break;
221 		}
222 	}
223 
224 	// One or two argument should remain -- the input file and optionally the
225 	// output file.
226 	if (optind + 1 != argc && optind + 2 != argc)
227 		print_usage_and_exit(true);
228 
229 	const char* opdName = argv[optind++];
230 	const char* packageInfoName = optind < argc ? argv[optind++] : NULL;
231 
232 	// guess architecture from the input file name, if not given
233 	if (architecture == NULL) {
234 		const char* fileName = strrchr(opdName, '/');
235 		if (fileName == NULL)
236 			fileName = opdName;
237 		else
238 			fileName++;
239 
240 		// Try to guess from the file name.
241 		architecture = guess_architecture(fileName);
242 
243 		// If we've got nothing yet, try to guess from the file name.
244 		if (architecture == NULL && fileName != opdName)
245 			architecture = guess_architecture(opdName);
246 
247 		// fallback is "any"
248 		if (architecture == NULL)
249 			architecture = "any";
250 	}
251 
252 	// open the input
253 	FILE* input = fopen(opdName, "r");
254 	if (input == NULL) {
255 		fprintf(stderr, "Failed to open input file \"%s\": %s\n", opdName,
256 			strerror(errno));
257 		exit(1);
258 	}
259 
260 	// open the output
261 	FILE* output = packageInfoName != NULL
262 		? fopen(packageInfoName, "w+") : stdout;
263 	if (output == NULL) {
264 		fprintf(stderr, "Failed to open output file \"%s\": %s\n",
265 			packageInfoName, strerror(errno));
266 		exit(1);
267 	}
268 
269 	// read and parse the input file
270 	BMessage package;
271 	BString fieldName;
272 	BString fieldValue;
273 	char lineBuffer[LINE_MAX];
274 	bool seenPackageAttribute = false;
275 
276 	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), input)) {
277 		// chop off line break
278 		size_t lineLen = strlen(line);
279 		if (lineLen > 0 && line[lineLen - 1] == '\n')
280 			line[--lineLen] = '\0';
281 
282 		// flush previous field, if a new field begins, otherwise append
283 		if (lineLen == 0 || !isspace(line[0])) {
284 			// new field -- flush the previous one
285 			if (fieldName.Length() > 0) {
286 				fieldValue.Trim();
287 				package.AddString(fieldName.String(), fieldValue);
288 				fieldName = "";
289 			}
290 		} else if (fieldName.Length() > 0) {
291 			// append to current field
292 			fieldValue += line;
293 			continue;
294 		} else {
295 			// bogus line -- ignore
296 			continue;
297 		}
298 
299 		if (lineLen == 0)
300 			continue;
301 
302 		// parse new field
303 		char* colon = strchr(line, ':');
304 		if (colon == NULL) {
305 			// bogus line -- ignore
306 			continue;
307 		}
308 
309 		fieldName.SetTo(line, colon - line);
310 		fieldName.Trim();
311 		if (fieldName.Length() == 0) {
312 			// invalid field name
313 			continue;
314 		}
315 
316 		fieldValue = colon + 1;
317 
318 		if (fieldName == "Package") {
319 			if (seenPackageAttribute) {
320 				fprintf(stderr, "Duplicate \"Package\" attribute!\n");
321 				exit(1);
322 			}
323 
324 			seenPackageAttribute = true;
325 		}
326 	}
327 
328 	// write the output
329 	OuputWriter writer(output, package);
330 
331 	// name
332 	writer.WriteAttribute("name", "Package", NULL,
333 		FLAG_MANDATORY_FIELD | FLAG_DONT_QUOTE);
334 
335 	// version
336 	writer.WriteAttribute("version", "Version", version, FLAG_DONT_QUOTE);
337 
338 	// architecture
339 	fprintf(output, "architecture\t%s\n", architecture);
340 
341 	// summary
342 	fprintf(output, "summary\t\t\t\"%s\"\n", summary);
343 
344 	// description
345 	if (description != NULL)
346 		fprintf(output, "description\t\t\"%s\"\n", description);
347 	else
348 		fprintf(output, "description\t\t\"%s\"\n", summary);
349 
350 	// packager
351 	fprintf(output, "packager\t\t\"%s\"\n", packager);
352 
353 	// vendor
354 	fprintf(output, "vendor\t\t\t\"%s\"\n", vendor);
355 
356 	// copyrights
357 	writer.WriteAttribute("copyrights", "Copyright", NULL,
358 		FLAG_MANDATORY_FIELD | FLAG_LIST_ATTRIBUTE);
359 
360 	// licenses
361 	writer.WriteAttribute("licenses", "License", NULL, FLAG_LIST_ATTRIBUTE);
362 
363 	// empty provides
364 	fprintf(output, "\nprovides {\n}\n");
365 
366 	// empty requires
367 	fprintf(output, "\nrequires {\n}\n");
368 
369 	// URLs
370 	writer.WriteAttribute("urls", "URL", NULL, FLAG_LIST_ATTRIBUTE);
371 
372 	// source URLs
373 	writer.WriteAttribute("source-urls", "SourceURL", NULL,
374 		FLAG_LIST_ATTRIBUTE);
375 
376 	return 0;
377 }
378