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