xref: /haiku/src/bin/setversion.cpp (revision 1e9cdb0bd8b5c58627915caf0f518ab5c43b4da4)
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 extern const char *__progname;
19 
20 const char *kProgramName = __progname;
21 
22 
23 enum arg_needed {
24 	switch_needed, major_version, middle_version, minor_version,
25 	variety_version, internal_version, long_string, short_string
26 };
27 
28 enum 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 
36 enum processing_mode { no_switch, app_switch, sys_switch };
37 
38 
39 static void
usage()40 usage()
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 
54 static int
convertVariety(const char * str)55 convertVariety(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 
74 static void
errorToString(BString & output,status_t error,const char * appName=NULL)75 errorToString(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 
133 static void
errorOut(status_t error,const char * appName=NULL,bool showUsage=true)134 errorOut(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 
148 static void
parse(bool & systemModified,bool & appModified,arg_needed & argNeeded,processing_mode & mode,version_info & systemVersion,version_info & appVersion,int argc,char * argv[])149 parse(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 
298 int
main(int argc,char * argv[])299 main(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