1/*
2 * Copyright 2002, Ryan Fleet.
3 * Copyright 2006-2007, Axel D��rfler, axeld@pinc-software.de.
4 *
5 * Distributed under the terms of the MIT license.
6 */
7
8
9#include <AppFileInfo.h>
10#include <String.h>
11
12#include <ctype.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16
17
18extern const char *__progname;
19
20const char *kProgramName = __progname;
21
22
23enum arg_needed {
24	switch_needed, major_version, middle_version, minor_version,
25	variety_version, internal_version, long_string, short_string
26};
27
28enum app_error {
29	e_base = B_ERRORS_END,
30	e_unknown, e_app_sys_switch, e_specify_version, e_major_version,
31	e_middle_version, e_minor_version, e_variety_version, e_internal_version,
32	e_expecting, e_long_string, e_short_string,
33	e_parameter, e_app_twice, e_sys_twice
34};
35
36enum processing_mode { no_switch, app_switch, sys_switch };
37
38
39static void
40usage()
41{
42	fprintf(stdout, "Usage: %s filename\n", kProgramName);
43	fprintf(stdout, "   [ -system <major> <middle> <minor>\n");
44	fprintf(stdout, "       [ [ d | a | b | g | gm | f ] [ <internal> ] ]\n");
45	fprintf(stdout, "       [ -short <shortVersionString> ]\n");
46	fprintf(stdout, "       [ -long <longVersionString> ] ] # system info\n");
47	fprintf(stdout, "   [ -app <major> <middle> <minor>\n");
48	fprintf(stdout, "       [ [ d | a | b | g | gm | f ] [ <internal> ] ]\n");
49	fprintf(stdout, "       [ -short <shortVersionString> ]\n");
50	fprintf(stdout, "       [ -long <longVersionString> ] ] # application info\n");
51}
52
53
54static int
55convertVariety(const char *str)
56{
57	if (!strcmp(str, "d") || !strcmp(str, "development"))
58		return 0;
59	if (!strcmp(str, "a") || !strcmp(str, "alpha"))
60		return 1;
61	if (!strcmp(str, "b") || !strcmp(str, "beta"))
62		return 2;
63	if (!strcmp(str, "g") || !strcmp(str, "gamma"))
64		return 3;
65	if (strcmp(str, "gm") || !strcmp(str, "goldenmaster"))
66		return 4;
67	if (!strcmp(str, "f") || !strcmp(str, "final"))
68		return 5;
69
70	return -1;
71}
72
73
74static void
75errorToString(BString& output, status_t error, const char *appName = NULL)
76{
77	switch (error) {
78		case e_app_sys_switch:
79			output = "-system or -app expected\n";
80			break;
81		case e_specify_version:
82			output = "you did not specify any version\n";
83			break;
84		case e_major_version:
85			output = "major version number error\n";
86			break;
87		case e_middle_version:
88			output = "middle version number error\n";
89			break;
90		case e_minor_version:
91			output = "minor version number error\n";
92			break;
93		case e_variety_version:
94			output = "variety letter error\n";
95			break;
96		case e_internal_version:
97			output = "internal version number error\n";
98			break;
99		case e_expecting:
100			output = "expecting -short, -long, -app or -system\n";
101			break;
102		case e_long_string:
103			output = "expecting long version string\n";
104			break;
105		case e_short_string:
106			output = "expecting short version string\n";
107			break;
108		case e_parameter:
109			output = "parameter error\n";
110			break;
111		case e_app_twice:
112			output = "you cannot specify the app version twice\n";
113			break;
114		case e_sys_twice:
115			output = "you cannot specify the system version twice\n";
116			break;
117		case e_unknown:
118			output = "unknown internal error\n";
119			break;
120
121		default:
122			output = strerror(error);
123
124			if (appName != NULL) {
125				output += ": ";
126				output += appName;
127			}
128			break;
129	}
130}
131
132
133static void
134errorOut(status_t error, const char *appName = NULL, bool showUsage = true)
135{
136	BString output;
137	errorToString(output, error, appName);
138
139	fprintf(stderr, "%s: %s", kProgramName, output.String());
140
141	if (showUsage)
142		usage();
143
144	exit(1);
145}
146
147
148static void
149parse(bool &systemModified, bool &appModified, arg_needed &argNeeded,
150	processing_mode &mode, version_info &systemVersion, version_info &appVersion,
151	int argc, char *argv[])
152{
153	systemModified = false;
154	appModified = false;
155	mode = no_switch;
156	argNeeded = switch_needed;
157
158	for (int i = 2; i < argc; ++i) {
159		version_info &version = mode == app_switch ? appVersion : systemVersion;
160
161		switch (argNeeded) {
162			case switch_needed:
163				if (strcmp(argv[i], "-app") == 0) {
164					if (mode == app_switch)
165						errorOut(e_app_twice);
166					if (appModified)
167						errorOut(e_parameter);
168
169					mode = app_switch;
170					argNeeded = major_version;
171					appModified = true;
172				} else if (strcmp(argv[i], "-system") == 0) {
173					if (mode == sys_switch)
174						errorOut(e_sys_twice);
175					if (systemModified)
176						errorOut(e_parameter);
177
178					mode = sys_switch;
179					argNeeded = major_version;
180					systemModified = true;
181				} else if (strcmp(argv[i], "-long") == 0) {
182					if (mode == no_switch)
183						errorOut(e_app_sys_switch);
184
185					argNeeded = long_string;
186				} else if (strcmp(argv[i], "-short") == 0) {
187					if (mode == no_switch)
188						errorOut(e_app_sys_switch);
189
190					argNeeded = short_string;
191				} else if (mode == no_switch)
192					errorOut(e_app_sys_switch);
193				else if (strncmp(argv[i], "-", 1) == 0)
194					errorOut(e_parameter);
195				else
196					errorOut(e_expecting);
197				break;
198
199			case major_version:
200				if (isalpha(argv[i][0]))
201					errorOut(e_major_version);
202
203				version.major = atoi(argv[i]);
204				argNeeded = middle_version;
205				break;
206
207			case middle_version:
208				if (isalpha(argv[i][0]))
209					errorOut(e_middle_version);
210
211				version.middle = atoi(argv[i]);
212				argNeeded = minor_version;
213				break;
214
215			case minor_version:
216				if (isalpha(argv[i][0]))
217					errorOut(e_minor_version);
218
219				version.minor = atoi(argv[i]);
220
221				if (i >= argc-1) {
222					argNeeded = switch_needed;
223					break;
224				}
225
226				argNeeded = variety_version;
227				break;
228
229			case variety_version:
230			{
231				if (!strncmp(argv[i], "-", 1)) {
232					i--;
233					argNeeded = switch_needed;
234					break;
235				}
236
237				int variety = convertVariety(argv[i]);
238				if (variety < 0)
239					errorOut(e_variety_version);
240
241				version.variety = variety;
242				argNeeded = internal_version;
243				break;
244			}
245
246			case internal_version:
247				if (isalpha(argv[i][0]))
248					errorOut(e_expecting);
249
250				version.internal = atoi(argv[i]);
251				argNeeded = switch_needed;
252				break;
253
254			case long_string:
255				strcpy(version.long_info, argv[i]);
256				argNeeded = switch_needed;
257				break;
258
259			case short_string:
260				strcpy(version.short_info, argv[i]);
261				argNeeded = switch_needed;
262				break;
263		}
264	}
265
266	if (mode == no_switch)
267		errorOut(e_app_sys_switch);
268
269	switch (argNeeded) {
270		case major_version:
271			errorOut(e_major_version);
272			break;
273		case middle_version:
274			errorOut(e_middle_version);
275			break;
276		case minor_version:
277			errorOut(e_minor_version);
278			break;
279		case variety_version:
280			errorOut(e_variety_version);
281			break;
282		case internal_version:
283			errorOut(e_internal_version);
284			break;
285		case long_string:
286			errorOut(e_long_string);
287			break;
288		case short_string:
289			errorOut(e_short_string);
290			break;
291		case switch_needed:
292			// all is well
293			break;
294	}
295}
296
297
298int
299main(int argc, char *argv[])
300{
301	if (argc < 3) {
302		if (argc < 2)
303			errorOut(e_app_sys_switch);
304
305		errorOut(e_specify_version);
306	}
307
308	// reset version infos
309
310	version_info systemVersion, appVersion;
311	memset(&systemVersion, 0, sizeof(version_info));
312	memset(&appVersion, 0, sizeof(version_info));
313
314	// process arguments
315
316	processing_mode mode;
317	arg_needed argNeeded;
318	bool systemModified, appModified;
319
320	parse(systemModified, appModified, argNeeded, mode, systemVersion,
321		appVersion, argc, argv);
322
323	// write back changes
324
325	BFile file;
326	status_t status = file.SetTo(argv[1], B_READ_WRITE);
327	if (status != B_OK)
328		errorOut(status, argv[1], false);
329
330	BAppFileInfo info;
331	status = info.SetTo(&file);
332	if (status != B_OK)
333		errorOut(status, argv[1], false);
334
335	if (systemModified ^ appModified) {
336		// clear out other app info if not present - this works around a
337		// bug in BeOS, see bug #681.
338		version_kind kind = systemModified ? B_APP_VERSION_KIND : B_SYSTEM_VERSION_KIND;
339		version_info clean;
340
341		if (info.GetVersionInfo(&clean, kind) != B_OK) {
342			memset(&clean, 0, sizeof(version_info));
343			info.SetVersionInfo(&clean, kind);
344		}
345	}
346
347	if (appModified) {
348		status = info.SetVersionInfo(&appVersion, B_APP_VERSION_KIND);
349		if (status < B_OK)
350			errorOut(status, NULL, false);
351	}
352
353	if (systemModified) {
354		status = info.SetVersionInfo(&systemVersion, B_SYSTEM_VERSION_KIND);
355		if (status < B_OK)
356			errorOut(status, NULL, false);
357	}
358
359	return 0;
360}
361
362