xref: /haiku/src/bin/hey.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 // hey
2 // a small scripting utility
3 // written by Attila Mezei (attila.mezei@mail.datanet.hu)
4 // contributions by Sander Stoks, Peter Folk, Chris Herborth, Marco Nelissen, Scott Lindsey and others
5 //
6 // public domain, use it at your own risk
7 //
8 // 1.2.8:	(Sander Stoks): Added command-line option -o which will output the "result" value
9 //		in the reply message to stdout, so you can use it in shell scripting more easily:
10 //		"hey Becasso get AspectRatio of Canvas 0"
11 //		outputs
12 //		Reply BMessage(B_REPLY):
13 //		   "result" (B_DOUBLE_TYPE) : 0.600
14 //		but "hey -o Becasso get AspectRatio of Canvas 0"
15 //		outputs 0.600000 directly.
16 //
17 // 1.2.7:	by Sander Stoks: Made a fork since I don't think Attila still supports "hey", and
18 //		because the latest version on BeBits seems to be 1.2.4.
19 //		Changes w.r.t. 1.2.6: When an application returns an error on a message, hey now
20 //		keeps iterating over applications with the same signature.  This is useful because,
21 //		for instance, Terminal starts as a new process for each instance, so it previously
22 //		wouldn't work to move a specific Terminal window using hey.  You can now say
23 //		"hey Terminal set Frame of Window foo to BRect[...]".
24 //
25 // 1.2.6:	syntax extended by Sander Stoks <sander@stoks.nl to allow:
26 //		"hey Application let Specifier do ..."
27 //		allowing you to send messages directly to other handlers than the app itself.
28 //		In cooperation with the new Application defined commands (note that some
29 //		Be classes, e.g. BWindow, publish commands as well) this allows, for example:
30 //		"hey NetPositive let Window 0 do MoveBy BPoint[10,10]"
31 //		Note that this is partly redundant, since
32 //		"hey NetPositive let Window 0 do get Title"
33 //		duplicates "hey NetPositive get Title of Window 0"
34 //		But with the old system,
35 //		"hey NetPositive MoveBy of Window 0 with data=BPoint[10,10]"
36 //		couldn't work ("MoveBy" is not defined by the Application itself).
37 //
38 // 1.2.5:   value_info is supported in BPropertyInfo. This means when you send GETSUITES (B_GET_SUPPORTED_SUITES)
39 //	the value info is printed after the property infos, like this:
40 //		   "messages" (B_PROPERTY_INFO_TYPE) :
41 //		        property   commands                            specifiers
42 //		--------------------------------------------------------------------------------
43 //		          Suites   B_GET_PROPERTY                      DIRECT
44 //		       Messenger   B_GET_PROPERTY                      DIRECT
45 //		    InternalName   B_GET_PROPERTY                      DIRECT
46 //
47 //		            name   value                               kind
48 //		--------------------------------------------------------------------------------
49 //		          Backup   0x6261636B ('back')                 COMMAND
50 //		                   Usage: This command backs up your hard drive.
51 //		           Abort   0x61626F72 ('abor')                 COMMAND
52 //		                   Usage: Stops the current operation...
53 //		       Type Code   0x74797065 ('type')                 TYPE CODE
54 //		                   Usage: Type code info...
55 //
56 //	You can also use the application defined commands (as the verb) with hey:
57 //		hey MyBackupApp Backup "Maui"
58 //
59 // 1.2.4:   the syntax is extended by Peter Folk <pfolk@uni.uiuc.edu> to contain:
60 //      do the x of y -3 of z '"1"'
61 //      I.e. "do" => B_EXECUTE_PROPERTY, optional "the" makes direct specifiers
62 //      more like english, bare reverse-index-specifiers are now handled, and
63 //      named specifiers can contain only digits by quoting it (but make sure the
64 //      shell passed the quotes through).
65 //
66 //      Hey(target,const char*,reply) was previously limited to 100 tokens.  It
67 //      now uses a vector<> so it's only limited by available memory.
68 //
69 //      Also, the archive name is now Y2K compliant =)
70 //
71 // 1.2.3:	new option: -s for silent processing (no reply or errors printed) AM
72 //
73 // 1.2.2:	Fixes by Marco Nelissen (marcone@xs4all.nl)
74 //      - fixed parsing of negative numbers
75 //		- fixed "with" syntax, which was broken (after a create, "with" would be taken as a specifier)
76 //
77 // 1.2.1:	compiled for x86, R4 with minor modifications at BPropertyInfo
78 //
79 // 1.2.0:	the syntax is extended by Sander Stoks (sander@adamation.com) to contain
80 //		with name=<value> [and name=<value> [...]]
81 //		at the end of the command which will add additional data to the scripting message. E.g:
82 //		hey Becasso create Canvas with name=MyCanvas and size=BRect(100,100,300,300)
83 //		Also a small interpreter is included.
84 //
85 //		Detailed printout of B_PROPERTY_INFO in BMessages. Better than BPropertyInfo::PrintToStream().
86 //		Also prints usage info for a property if defined.
87 //
88 // 1.1.1:	minor change from chrish@qnx.com to return -1 if an error is
89 //		sent back in the reply message; also added B_COUNT_PROPERTIES support
90 //
91 //		The range specifier sent to the target was 1 greater than it should've been. Fixed.
92 //
93 //		'hey' made the assumption that the first thread in a team will be the
94 //		application thread (and therefore have the application's name).
95 //		This was not always the case. Fix from Scott Lindsey <wombat@gobe.com>.
96 //
97 //v1.1.0:	Flattened BPropertyInfo is printed if found in the reply of B_GET_SUPPORTED_SUITES
98 //		1,2,3 and 4 character message constant is supported (e.g. '1', '12', '123', '1234')
99 //		Alpha is sent with rgb_color
100 //
101 //v1.0.0	First public release
102 
103 
104 #include <stdio.h>
105 #include <stdlib.h>
106 #include <string.h>
107 #include <strings.h>
108 #include <AppKit.h>
109 #include <Path.h>
110 #include <SupportDefs.h>
111 
112 int32 HeyInterpreterThreadHook(void* arg);
113 
114 status_t Hey(BMessenger* target, const char* arg, BMessage* reply);
115 bool isSpace(char c);
116 status_t Hey(BMessenger* target, char* argv[], int32* argx, int32 argc, BMessage* reply);
117 status_t add_specifier(BMessage *to_message, char *argv[], int32 *argx, int32 argc);
118 status_t add_data(BMessage *to_message, char *argv[], int32 *argx);
119 status_t add_with(BMessage *to_message, char *argv[], int32 *argx, int32 argc);
120 void add_message_contents(BList *textlist, BMessage *msg, int32 level);
121 char *get_datatype_string(int32 type);
122 char *format_data(int32 type, char *ptr, long size);
123 void print_message(BMessage *message);
124 char *id_to_string(long ID, char *here);
125 bool is_valid_char(uint8 c);
126 
127 const char VERSION[] = "v1.2.8";
128 
129 #define MAX_INPUT_SIZE 1024
130 	// Maximum amount of input data that "hey" can process at a time
131 
132 #define DEBUG_HEY 0		// 1: prints the script message to be sent to the target application, 0: prints only the reply
133 
134 
135 // test, these should be zero for normal operation
136 #define TEST_VALUEINFO 0
137 
138 
139 // flag for silent mode
140 bool silent;
141 // flag for stdout mode
142 bool output;
143 
144 status_t
145 parse(BMessenger& the_application, int argc, char *argv[], int32 argapp)
146 {
147 	if (!the_application.IsValid()) {
148 		if (!silent)
149 			fprintf(stderr, "Cannot find the application (%s)\n", argv[argapp]);
150 		return B_ERROR;
151 	}
152 
153 	if (argc < 3) {
154 		if (!silent)
155 			fprintf(stderr, "Cannot find the verb!\n");
156 		return B_ERROR;
157 	}
158 
159 
160 	BMessage the_reply;
161 	int32 argx = argapp+1;
162 	status_t err = Hey(&the_application, argv, &argx, argc, &the_reply);
163 
164 	if (err != B_OK) {
165 		if (!silent) {
166 			fprintf(stderr, "Error when sending message to %s!\n",
167 				argv[argapp]);
168 		}
169 		return B_ERROR;
170 	} else {
171 		if (the_reply.what == (uint32)B_MESSAGE_NOT_UNDERSTOOD
172 			|| the_reply.what == (uint32)B_ERROR) {	// I do it myself
173 			if (the_reply.HasString("message")) {
174 				if (!silent) {
175 					printf("%s (error 0x%8" B_PRIx32 ")\n",
176 						the_reply.FindString("message"),
177 						the_reply.FindInt32("error"));
178 				}
179 			} else {
180 				if (!silent) {
181 					printf("error 0x%8" B_PRIx32 "\n",
182 						the_reply.FindInt32("error"));
183 				}
184 			}
185 			return 1;
186 		} else {
187 			if (!silent) {
188 				if (output) {
189 					type_code tc;
190 					if (the_reply.GetInfo("result", &tc) == B_OK) {
191 						if (tc == B_INT8_TYPE) {
192 							int8 v;
193 							the_reply.FindInt8("result", &v);
194 							printf("%d\n", v);
195 						} else if (tc == B_INT16_TYPE) {
196 							int16 v;
197 							the_reply.FindInt16("result", &v);
198 							printf("%d\n", v);
199 						} else if (tc == B_INT32_TYPE) {
200 							int32 v;
201 							the_reply.FindInt32("result", &v);
202 							printf("%" B_PRId32 "\n", v);
203 						} else if (tc == B_UINT8_TYPE) {
204 							uint8 v;
205 							the_reply.FindInt8("result", (int8*)&v);
206 							printf("%u\n", v);
207 						} else if (tc == B_UINT16_TYPE) {
208 							uint16 v;
209 							the_reply.FindInt16("result", (int16*)&v);
210 							printf("%u\n", v);
211 						} else if (tc == B_UINT32_TYPE) {
212 							uint32 v;
213 							the_reply.FindInt32("result", (int32*)&v);
214 							printf("%" B_PRIu32 "\n", v);
215 						} else if (tc == B_STRING_TYPE) {
216 							const char* v;
217 							the_reply.FindString("result", &v);
218 							printf("%s\n", v);
219 						} else if (tc == B_FLOAT_TYPE) {
220 							float v;
221 							the_reply.FindFloat("result", &v);
222 							printf("%f\n", v);
223 						} else if (tc == B_DOUBLE_TYPE) {
224 							double v;
225 							the_reply.FindDouble("result", &v);
226 							printf("%f\n", v);
227 						} else if (tc == B_BOOL_TYPE) {
228 							bool v;
229 							the_reply.FindBool("result", &v);
230 							printf("%s\n", v ? "true" : "false");
231 						} else
232 							printf("Unsupported type\n");
233 					}
234 				} else {
235 					printf("Reply ");
236 					print_message(&the_reply);
237 					printf("\n");
238 				}
239 			}
240 		}
241 	}
242 	return B_OK;
243 }
244 
245 
246 void
247 usage(int exitCode)
248 {
249 	fprintf(exitCode == EXIT_SUCCESS ? stdout : stderr,
250 		"hey %s, written by Attila Mezei (attila.mezei@mail.datanet.hu)\n"
251 		"usage: hey [-s][-o] <app|signature|teamid> [let <specifier> do] <verb> <specifier_1> <of\n"
252 		"           <specifier_n>>* [to <value>] [with name=<value> [and name=<value>]*]\n"
253 		"where  <verb> : DO|GET|SET|COUNT|CREATE|DELETE|GETSUITES|QUIT|SAVE|LOAD|'what'\n"
254 		"  <specifier> : [the] <property_name> [ <index> | name | \"name\" | '\"name\"' ]\n"
255 		"      <index> : int | -int | '['int']' | '['-int']' | '['startint to end']'\n"
256 		"      <value> : \"string\" | <integer> | <float> | bool(value) | int8(value)\n"
257 		"                | int16(value) | int32(value) | float(value) | double(value)\n"
258 		"                | BPoint(x,y) | BRect(l,t,r,b) | rgb_color(r,g,b,a) | file(path)\n"
259 		"options: -s: silent\n"
260 		"         -o: output result to stdout for easy parsing\n\n", VERSION);
261 	exit(exitCode);
262 }
263 
264 
265 int
266 main(int argc, char *argv[])
267 {
268 	BApplication app("application/x-amezei-hey");
269 
270 	if (argc < 2)
271 		usage(1);
272 
273 	int32 argapp = 1;
274 	silent = false;
275 	output = false;
276 
277 	// Updated option mechanism
278 	for (int i = 0; i < argc; i++) {
279 		if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "-S") == 0) {
280 			silent = true;
281 			argapp++;
282 		} else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "-O") == 0) {
283 			output = true;
284 			argapp++;
285 		} else  if (strcmp(argv[1], "-h") == 0
286 			|| strcmp(argv[1], "--help") == 0)
287 			usage(0);
288 	}
289 
290 	// find the application
291 	BMessenger the_application;
292 	BList team_list;
293 	team_id teamid;
294 	app_info appinfo;
295 
296 	teamid = atoi(argv[argapp]);
297 	if (teamid > 0) {
298 		if (be_roster->GetRunningAppInfo(teamid, &appinfo) != B_OK)
299 			return 1;
300 		the_application=BMessenger(NULL, teamid);
301 		if (!parse(the_application, argc, argv, argapp))
302 			return 0;
303 		return 1;
304 	}
305 
306 	be_roster->GetAppList(&team_list);
307 
308 	for (int32 i = 0; i < team_list.CountItems(); i++) {
309 		teamid = (team_id)(addr_t)team_list.ItemAt(i);
310 		be_roster->GetRunningAppInfo(teamid, &appinfo);
311 		if (strcmp(appinfo.signature, argv[argapp]) == 0) {
312 			the_application=BMessenger(appinfo.signature);
313 			if (!parse(the_application, argc, argv, argapp))
314 				return 0;
315 		} else {
316 			if (strcmp(appinfo.ref.name, argv[argapp]) == 0) {
317 				the_application = BMessenger(0, teamid);
318 				if (!parse(the_application, argc, argv, argapp))
319 					return 0;
320 			}
321 		}
322 	}
323 
324 	return 1;
325 }
326 
327 
328 int32
329 HeyInterpreterThreadHook(void* arg)
330 {
331 	if (!arg)
332 		return 1;
333 
334 	BMessage environment(*(BMessage*) arg);
335 	const char* prompt = "Hey";
336 	if (environment.HasString("prompt"))
337 		environment.FindString("prompt", &prompt);
338 	printf("%s> ", prompt);
339 
340 	BMessenger target;
341 	if (environment.HasMessenger("Target"))
342 		environment.FindMessenger("Target", &target);
343 
344 	char command[MAX_INPUT_SIZE];
345 	status_t err;
346 	BMessage reply;
347 	while (fgets(command, sizeof(command), stdin)) {
348 		reply.MakeEmpty();
349 		err = Hey(&target, command, &reply);
350 		if (!err) {
351 			print_message(&reply);
352 		} else {
353 			printf("Error!\n");
354 		}
355 		printf("%s> ", prompt);
356 	}
357 
358 	return 0;
359 }
360 
361 status_t
362 Hey(BMessenger* target, const char* arg, BMessage* reply)
363 {
364 	BList argv;
365 	char* tokens = new char[strlen(arg) * 2];
366 		// number of tokens is limited only by memory
367 	char* currentToken = tokens;
368 	int32 tokenNdex = 0;
369 	int32 argNdex = 0;
370 	bool inquotes = false;
371 
372 	while (arg[argNdex] != 0) { // for each character in arg
373 		if (arg[argNdex] == '\"')
374 			inquotes = !inquotes;
375 		if (!inquotes && isSpace(arg[argNdex])) { // if the character is white space
376 			if (tokenNdex != 0) { //  close off currentToken token
377 				currentToken[tokenNdex] = 0;
378 				argv.AddItem(currentToken);
379 				currentToken += tokenNdex + 1;
380 				tokenNdex = 0;
381 				argNdex++;
382 			} else { // just skip the whitespace
383 				argNdex++;
384 			}
385 		} else { // copy char into current token
386 			currentToken[tokenNdex] = arg[argNdex];
387 			tokenNdex++;
388 			argNdex++;
389 		}
390 	}
391 
392 	if (tokenNdex!=0) { //  close off currentToken token
393 		currentToken[tokenNdex] = 0;
394 		argv.AddItem(currentToken);
395 	}
396 	argv.AddItem(NULL);
397 
398 	int32 argx = 0;
399 	status_t ret = Hey(target, (char **)argv.Items(), &argx, argv.CountItems() - 1, reply);
400 	  // This used to be "return Hey(...);"---so tokens wasn't delete'd.
401 	delete[] tokens;
402 	return ret;
403 }
404 
405 
406 bool
407 isSpace(char c)
408 {
409 	switch (c) {
410 		case ' ':
411 		case '\t':
412 			return true;
413 
414 		default:
415 			return false;
416 	}
417 }
418 
419 
420 status_t
421 Hey(BMessenger* target, char* argv[], int32* argx, int32 argc, BMessage* reply)
422 {
423 	bool direct_what = false;
424 	BMessage the_message;
425 	if (strcasecmp(argv[*argx], "let") == 0) {
426 		BMessage get_target (B_GET_PROPERTY);
427 		get_target.AddSpecifier ("Messenger");
428 			// parse the specifiers
429 		(*argx)++;
430 		status_t result = B_OK;
431 		while ((result = add_specifier(&get_target, argv, argx, argc)) == B_OK)
432 			;
433 
434 		if (result != B_ERROR) {
435 			if (!silent)
436 				fprintf(stderr, "Bad specifier syntax!\n");
437 			return result;
438 		}
439 		BMessage msgr;
440 		if (target && target->IsValid()) {
441 			result = target->SendMessage(&get_target, &msgr);
442 			if (result != B_OK)
443 				return result;
444 			result = msgr.FindMessenger ("result", target);
445 			if (result != B_OK) {
446 				if (!silent)
447 					fprintf(stderr, "Couldn't retrieve the BMessenger!\n");
448 				return result;
449 			}
450 		}
451 		if (!argv[*argx]) {
452 			if (!silent)
453 				fprintf(stderr, "Syntax error - forgot \"do\"?\n");
454 			return B_ERROR;
455 		}
456 	}
457 	if (strcasecmp(argv[*argx], "do") == 0)
458 		the_message.what = B_EXECUTE_PROPERTY;
459 	else if (strcasecmp(argv[*argx], "get") == 0)
460 		the_message.what = B_GET_PROPERTY;
461 	else if (strcasecmp(argv[*argx], "set") == 0)
462 		the_message.what = B_SET_PROPERTY;
463 	else if (strcasecmp(argv[*argx], "create") == 0)
464 		the_message.what = B_CREATE_PROPERTY;
465 	else if (strcasecmp(argv[*argx], "delete") == 0)
466 		the_message.what = B_DELETE_PROPERTY;
467 	else if (strcasecmp(argv[*argx], "quit") == 0)
468 		the_message.what = B_QUIT_REQUESTED;
469 	else if (strcasecmp(argv[*argx], "save") == 0)
470 		the_message.what = B_SAVE_REQUESTED;
471 	else if (strcasecmp(argv[*argx], "load") == 0)
472 		the_message.what = B_REFS_RECEIVED;
473 	else if (strcasecmp(argv[*argx], "count") == 0)
474 		the_message.what = B_COUNT_PROPERTIES;
475 	else if (strcasecmp(argv[*argx], "getsuites") == 0)
476 		the_message.what = B_GET_SUPPORTED_SUITES;
477 	else {
478 		switch(strlen(argv[*argx])) {
479 				// can be a message constant if 1,2,3 or 4 chars
480 			case 1:
481 				the_message.what = (int32)argv[*argx][0];
482 				break;
483 			case 2:
484 				the_message.what = (((int32)argv[*argx][0]) << 8)
485 					| (((int32)argv[*argx][1]));
486 				break;
487 			case 3:
488 				the_message.what = (((int32)argv[*argx][0]) << 16)
489 					| (((int32)argv[*argx][1]) << 8)
490 					| (((int32)argv[*argx][2]));
491 				break;
492 			case 4:
493 				the_message.what = (((int32)argv[*argx][0]) << 24)
494 					| (((int32)argv[*argx][1]) << 16)
495 					| (((int32)argv[*argx][2]) << 8)
496 					| (((int32)argv[*argx][3]));
497 				break;
498 			default:
499 				// maybe this is a user defined command, ask for the supported suites
500 				bool found = false;
501 				if (target && target->IsValid()) {
502 					BMessage reply;
503 					if (target->SendMessage(B_GET_SUPPORTED_SUITES, &reply)
504 						== B_OK) {
505 						// if all goes well, reply contains all kinds of
506 						// property infos
507 						int32 j = 0;
508 						void *voidptr;
509 						ssize_t sizefound;
510 						BPropertyInfo propinfo;
511 						const value_info *vinfo;
512 						int32 vinfo_index, vinfo_count;
513 
514 //						const char *str;
515 //						while (rply.FindString("suites", j++, &str) == B_OK)
516 //							printf ("Suite %ld: %s\n", j, str);
517 //
518 //						j = 0;
519 						while (reply.FindData("messages", B_PROPERTY_INFO_TYPE,
520 								j++, (const void **)&voidptr, &sizefound)
521 							== B_OK && !found) {
522 							if (propinfo.Unflatten(B_PROPERTY_INFO_TYPE,
523 									(const void *)voidptr, sizefound) == B_OK) {
524 								vinfo = propinfo.Values();
525 								vinfo_index = 0;
526 								vinfo_count = propinfo.CountValues();
527 #if TEST_VALUEINFO>0
528 								value_info vinfo[10] = {
529 									{"Backup", 'back', B_COMMAND_KIND,
530 										"This command backs up your hard"
531 										" drive."},
532 									{"Abort", 'abor', B_COMMAND_KIND,
533 										"Stops the current operation..."},
534 									{"Type Code", 'type', B_TYPE_CODE_KIND,
535 										"Type code info..."}
536 								};
537 								vinfo_count = 3;
538 #endif
539 
540 								while (vinfo_index < vinfo_count) {
541 									if (strcmp(vinfo[vinfo_index].name,
542 											argv[*argx]) == 0) {
543 										found = true;
544 										the_message.what =
545 											vinfo[vinfo_index].value;
546 #if TEST_VALUEINFO>0
547 										printf("FOUND COMMAND \"%s\" = %lX\n",
548 											vinfo[vinfo_index].name,
549 											the_message.what);
550 #endif
551 										break;
552 									}
553 									vinfo_index++;
554 								}
555 							}
556 						}
557 					}
558 				}
559 
560 				if (!found) {
561 					if (!silent)
562 						fprintf(stderr, "Bad verb (\"%s\")\n", argv[*argx]);
563 					return -1;
564 				}
565 		}
566 		direct_what = true;
567 	}
568 
569 	status_t result = B_OK;
570 	(*argx)++;
571 
572 	// One exception: Single data item at end of line.
573 	if (direct_what && *argx == argc - 1 && argv[*argx] != NULL)
574 		add_data(&the_message, argv, argx);
575 	else {
576 		// parse the specifiers
577 		if (the_message.what != B_REFS_RECEIVED) {
578 				// LOAD has no specifier
579 			while ((result = add_specifier(&the_message, argv, argx, argc))
580 				== B_OK)
581 				;
582 
583 			if (result != B_ERROR) {
584 				if (!silent)
585 					fprintf(stderr, "Bad specifier syntax!\n");
586 				return result;
587 			}
588 		}
589 	}
590 
591 	// if verb is SET or LOAD, there should be a to <value>
592 	if ((the_message.what == B_SET_PROPERTY || the_message.what == B_REFS_RECEIVED) && argv[*argx] != NULL) {
593 		if (strcasecmp(argv[*argx], "to") == 0)
594 			(*argx)++;
595 		result = add_data(&the_message, argv, argx);
596 		if (result != B_OK) {
597 			if (result == B_ENTRY_NOT_FOUND) {
598 				if (!silent)
599 					fprintf(stderr, "File not found!\n");
600 			} else if (!silent)
601 				fprintf(stderr, "Invalid 'to...' value format!\n");
602 			return result;
603 		}
604 	}
605 
606 	add_with(&the_message, argv, argx, argc);
607 
608 #if DEBUG_HEY>0
609 	fprintf(stderr, "Send ");
610 	print_message(&the_message);
611 	fprintf(stderr, "\n");
612 #endif
613 
614 	if (target && target->IsValid()) {
615 		if (reply)
616 			result = target->SendMessage(&the_message, reply);
617 		else
618 			result = target->SendMessage(&the_message);
619 	}
620 	return result;
621 }
622 
623 // There can be a with <name>=<type>() [and <name>=<type> ...]
624 // I treat "and" just the same as "with", it's just to make the script syntax more English-like.
625 status_t
626 add_with(BMessage *to_message, char *argv[], int32 *argx, int32 argc)
627 {
628 	status_t result = B_OK;
629 	if (*argx < argc - 1 && argv[++(*argx)] != NULL) {
630 		// printf ("argv[%ld] = %s\n", *argx, argv[*argx]);
631 		if (strcasecmp(argv[*argx], "with") == 0) {
632 			// printf ("\"with\" detected!\n");
633 			(*argx)++;
634 			bool done = false;
635 			do {
636 				result = add_data(to_message, argv, argx);
637 				if (result != B_OK) {
638 					if (result == B_ENTRY_NOT_FOUND) {
639 						if (!silent)
640 							fprintf(stderr, "File not found!\n");
641 					} else {
642 						if (!silent)
643 							fprintf(stderr, "Invalid 'with...' value format!\n");
644 					}
645 					return result;
646 				}
647 				(*argx)++;
648 				// printf ("argc = %d, argv[%d] = %s\n", argc, *argx, argv[*argx]);
649 				if (*argx < argc - 1 && strcasecmp(argv[*argx], "and") == 0)
650 					(*argx)++;
651 				else
652 					done = true;
653 			} while (!done);
654 		}
655 	}
656 	return result;
657 }
658 
659 // returns B_OK if successful
660 //         B_ERROR if no more specifiers
661 //         B_BAD_SCRIPT_SYNTAX if syntax error
662 status_t
663 add_specifier(BMessage *to_message, char *argv[], int32 *argx, int32 argc)
664 {
665 	char *property = argv[*argx];
666 
667 	if (property == NULL)
668 		return B_ERROR;		// no more specifiers
669 
670 	(*argx)++;
671 
672 	if (strcasecmp(property, "do") == 0) {
673 			// Part of the "hey App let Specifier do Verb".
674 		return B_ERROR;	// no more specifiers
675 	}
676 
677 	if (strcasecmp(property, "to") == 0) {
678 		return B_ERROR;
679 			// no more specifiers
680 	}
681 
682 	if (strcasecmp(property, "with") == 0) {
683 		*argx -= 2;
684 		add_with(to_message, argv, argx, argc);
685 		return B_ERROR;
686 			// no more specifiers
687 	}
688 
689 	if (strcasecmp(property, "of") == 0) {
690 			// skip "of", read real property
691 		property = argv[*argx];
692 		if (property == NULL)
693 			return B_BAD_SCRIPT_SYNTAX;
694 		(*argx)++;
695 	}
696 
697 	if (strcasecmp(property, "the") == 0) {
698 			// skip "the", read real property
699 		property = argv[*argx];
700 		if (property == NULL)
701 			return B_BAD_SCRIPT_SYNTAX;
702 		(*argx)++;
703 	}
704 
705 	// decide the specifier
706 
707 	char *specifier = NULL;
708 	if (to_message->what == B_CREATE_PROPERTY) {
709 			// create is always direct. without this, a "with" would be
710 			// taken as a specifier
711 		(*argx)--;
712 	} else
713 		specifier = argv[*argx];
714 	if (specifier == NULL) {
715 			// direct specifier
716 		to_message->AddSpecifier(property);
717 		return B_ERROR;
718 			// no more specifiers
719 	}
720 
721 	(*argx)++;
722 
723 	if (strcasecmp(specifier, "of") == 0) {
724 			// direct specifier
725 		to_message->AddSpecifier(property);
726 		return B_OK;
727 	}
728 
729 	if (strcasecmp(specifier, "to") == 0) {
730 			// direct specifier
731 		to_message->AddSpecifier(property);
732 		return B_ERROR;
733 			// no more specifiers
734 	}
735 
736 
737 	if (specifier[0] == '[') {
738 			// index, reverse index or range
739 		char *end;
740 		int32 ix1, ix2;
741 		if (specifier[1] == '-') {
742 				// reverse index
743 			ix1 = strtoul(specifier + 2, &end, 10);
744 			BMessage revspec(B_REVERSE_INDEX_SPECIFIER);
745 			revspec.AddString("property", property);
746 			revspec.AddInt32("index", ix1);
747 			to_message->AddSpecifier(&revspec);
748 		} else {
749 				// index or range
750 			ix1 = strtoul(specifier + 1, &end, 10);
751 			if (end[0] == ']') {
752 					// it was an index
753 				to_message->AddSpecifier(property, ix1);
754 				return B_OK;
755 			} else {
756 				specifier = argv[*argx];
757 				if (specifier == NULL) {
758 						// I was wrong, it was just an index
759 					to_message->AddSpecifier(property, ix1);
760 					return B_OK;
761 				}
762 				(*argx)++;
763 				if (strcasecmp(specifier, "to") == 0) {
764 					specifier = argv[*argx];
765 					if (specifier == NULL)
766 						return B_BAD_SCRIPT_SYNTAX;
767 					(*argx)++;
768 					ix2 = strtoul(specifier, &end, 10);
769 					to_message->AddSpecifier(property, ix1, ix2 - ix1 > 0
770 							? ix2 - ix1 : 1);
771 					return B_OK;
772 				} else
773 					return B_BAD_SCRIPT_SYNTAX;
774 			}
775 		}
776 	} else {
777 		// name specifier
778 		// if it contains only digits, it will be an index...
779 		bool index_spec = true;
780 		bool reverse = specifier[0] == '-';
781 		// accept bare reverse-index-specs
782 		size_t speclen = strlen(specifier);
783 		for (int32 i = (reverse ? 1 : 0); i < (int32)speclen; ++i) {
784 			if (specifier[i] < '0' || specifier[i] > '9') {
785 				index_spec = false;
786 				break;
787 			}
788 		}
789 
790 		if (index_spec) {
791 			if (reverse) {
792 				// Copied from above
793 				BMessage revspec(B_REVERSE_INDEX_SPECIFIER);
794 				revspec.AddString("property", property);
795 				revspec.AddInt32("index", atol(specifier + 1));
796 				to_message->AddSpecifier(&revspec);
797 			} else
798 				to_message->AddSpecifier(property, atol(specifier));
799 		} else {
800 			// Allow any name by counting an initial " as a literal-string
801 			// indicator
802 			if (specifier[0] == '\"') {
803 				if (specifier[speclen - 1] == '\"')
804 					specifier[speclen - 1] = '\0';
805 				++specifier;
806 				--speclen;
807 			}
808 			to_message->AddSpecifier(property, specifier);
809 		}
810 	}
811 
812 	return B_OK;
813 }
814 
815 
816 status_t
817 add_data(BMessage *to_message, char *argv[], int32 *argx)
818 {
819 	char *valuestring = argv[*argx];
820 
821 	if (valuestring == NULL)
822 		return B_ERROR;
823 
824 	// try to interpret it as an integer or float
825 	bool contains_only_digits = true;
826 	bool is_floating_point = false;
827 	for (int32 i = 0; i < (int32)strlen(valuestring); i++) {
828 		if (i != 0 || valuestring[i] != '-') {
829 			if (valuestring[i] < '0' || valuestring[i] > '9') {
830 				if (valuestring[i] == '.') {
831 					is_floating_point = true;
832 				} else {
833 					contains_only_digits = false;
834 					break;
835 				}
836 			}
837 		}
838 	}
839 	//printf("%d %d\n",	contains_only_digits,is_floating_point);
840 	if (contains_only_digits) {
841 		if (is_floating_point) {
842 			to_message->AddFloat("data", atof(valuestring));
843 			return B_OK;
844 		} else {
845 			to_message->AddInt32("data", atol(valuestring));
846 			return B_OK;
847 		}
848 	}
849 
850 	// if true or false, it is bool
851 	if (strcasecmp(valuestring, "true") == 0) {
852 		to_message->AddBool("data", true);
853 		return B_OK;
854 	} else if (strcasecmp(valuestring, "false") == 0) {
855 		to_message->AddBool("data", false);
856 		return B_OK;
857 	}
858 
859 	// Add support for "<name>=<type>()" here:
860 	// The type is then added under the name "name".
861 
862 	#define MAX_NAME_LENGTH 128
863 	char curname[MAX_NAME_LENGTH];
864 	strcpy (curname, "data");	// This is the default.
865 
866 	char *s = valuestring;
867 	while (*++s && *s != '=')
868 		// Look for a '=' character...
869 		;
870 	if (*s == '=') {	// We found a <name>=
871 		*s = 0;
872 		strcpy (curname, valuestring);	// Use the new <name>
873 		valuestring = s + 1;			// Reposition the valuestring ptr.
874 	}
875 
876 	// must begin with a type( value )
877 	if (strncasecmp(valuestring, "int8", strlen("int8")) == 0)
878 		to_message->AddInt8(curname, atol(valuestring + strlen("int8(")));
879 	else if (strncasecmp(valuestring, "int16", strlen("int16")) == 0)
880 		to_message->AddInt16(curname, atol(valuestring + strlen("int16(")));
881 	else if (strncasecmp(valuestring, "int32", strlen("int32")) == 0)
882 		to_message->AddInt32(curname, atol(valuestring + strlen("int32(")));
883 	else if (strncasecmp(valuestring, "int64", strlen("int64")) == 0)
884 		to_message->AddInt64(curname, atol(valuestring + strlen("int64(")));
885 	else if (strncasecmp(valuestring, "bool", strlen("bool")) == 0) {
886 		if (strncasecmp(valuestring + strlen("bool("), "true", 4) == 0)
887 			to_message->AddBool(curname, true);
888 		else if (strncasecmp(valuestring + strlen("bool("), "false", 5) == 0)
889 			to_message->AddBool(curname, false);
890 		else
891 			to_message->AddBool(curname, atol(valuestring + strlen("bool(")) == 0 ? false : true);
892 	} else if (strncasecmp(valuestring, "float", strlen("float")) == 0)
893 		to_message->AddFloat(curname, atof(valuestring + strlen("float(")));
894 	else if (strncasecmp(valuestring, "double", strlen("double")) == 0)
895 		to_message->AddDouble(curname, atof(valuestring + strlen("double(")));
896 	else if (strncasecmp(valuestring, "BPoint", strlen("BPoint")) == 0) {
897 		float x, y;
898 		x = atof(valuestring + strlen("BPoint("));
899 		if (strchr(valuestring, ','))
900 			y = atof(strchr(valuestring, ',') + 1);
901 		else if (strchr(valuestring, ' '))
902 			y = atof(strchr(valuestring, ' ') + 1);
903 		else	// bad syntax
904 			y = 0.0f;
905 		to_message->AddPoint(curname, BPoint(x,y));
906 	} else if (strncasecmp(valuestring, "BRect", strlen("BRect")) == 0) {
907 		float l = 0.0f, t = 0.0f, r = 0.0f, b = 0.0f;
908 		char *ptr;
909 		l = atof(valuestring + strlen("BRect("));
910 		ptr = strchr(valuestring, ',');
911 		if (ptr) {
912 			t = atof(ptr + 1);
913 			ptr = strchr(ptr + 1, ',');
914 			if (ptr) {
915 				r = atof(ptr + 1);
916 				ptr = strchr(ptr + 1, ',');
917 				if (ptr)
918 					b = atof(ptr + 1);
919 			}
920 		}
921 
922 		to_message->AddRect(curname, BRect(l,t,r,b));
923 	} else if (strncasecmp(valuestring, "rgb_color", strlen("rgb_color")) == 0) {
924 		rgb_color clr;
925 		char *ptr;
926 		clr.red = atol(valuestring + strlen("rgb_color("));
927 		ptr = strchr(valuestring, ',');
928 		if (ptr) {
929 			clr.green = atol(ptr + 1);
930 			ptr = strchr(ptr + 1, ',');
931 			if (ptr) {
932 				clr.blue = atol(ptr + 1);
933 				ptr = strchr(ptr + 1, ',');
934 				if (ptr)
935 					clr.alpha = atol(ptr + 1);
936 			}
937 		}
938 
939 		to_message->AddData(curname, B_RGB_COLOR_TYPE, &clr, sizeof(rgb_color));
940 	} else if (strncasecmp(valuestring, "file", strlen("file")) == 0) {
941 		entry_ref file_ref;
942 
943 		// remove the last ] or )
944 		if (valuestring[strlen(valuestring) - 1] == ')'
945 			|| valuestring[strlen(valuestring) - 1] == ']')
946 			valuestring[strlen(valuestring) - 1] = 0;
947 
948 		if (get_ref_for_path(valuestring + 5, &file_ref) != B_OK)
949 			return B_ENTRY_NOT_FOUND;
950 
951 		// check if the ref is valid
952 		BEntry entry;
953 		if (entry.SetTo(&file_ref) != B_OK)
954 			return B_ENTRY_NOT_FOUND;
955 		//if(!entry.Exists())  return B_ENTRY_NOT_FOUND;
956 
957 		// add both ways, refsreceived needs it as "refs" while scripting needs "data"
958 		to_message->AddRef("refs", &file_ref);
959 		to_message->AddRef(curname, &file_ref);
960 	} else {
961 		// it is string
962 		// does it begin with a quote?
963 		if (valuestring[0] == '\"') {
964 			if (valuestring[strlen(valuestring) - 1] == '\"')
965 				valuestring[strlen(valuestring) - 1] = 0;
966 			to_message->AddString(curname, valuestring + 1);
967 		} else
968 			to_message->AddString(curname, valuestring);
969 	}
970 
971 	return B_OK;
972 }
973 
974 
975 void
976 print_message(BMessage *message)
977 {
978 	BList textlist;
979 	add_message_contents(&textlist, message, 0);
980 
981 	char *whatString = get_datatype_string(message->what);
982 	printf("BMessage(%s):\n", whatString);
983 	delete[] whatString;
984 	for (int32 i = 0; i < textlist.CountItems(); i++) {
985 		printf("   %s\n", (char*)textlist.ItemAt(i));
986 		free(textlist.ItemAt(i));
987 	}
988 }
989 
990 
991 void
992 add_message_contents(BList *textlist, BMessage *msg, int32 level)
993 {
994 	int32 count;
995 	int32 i, j;
996 	type_code typefound;
997 	ssize_t sizefound;
998 #ifdef HAIKU_TARGET_PLATFORM_DANO
999 	const char *namefound;
1000 #else
1001 	char *namefound;
1002 #endif
1003 	void *voidptr;
1004 	BMessage a_message;
1005 	char *textline, *datatype, *content;
1006 
1007 	// go though all message data
1008 	count = msg->CountNames(B_ANY_TYPE);
1009 	for (i=0; i < count; i++) {
1010 		msg->GetInfo(B_ANY_TYPE, i, &namefound, &typefound);
1011 		j = 0;
1012 
1013 		while (msg->FindData(namefound, typefound, j++, (const void **)&voidptr,
1014 				&sizefound) == B_OK) {
1015 			datatype = get_datatype_string(typefound);
1016 			content = format_data(typefound, (char*)voidptr, sizefound);
1017 			textline = (char*)malloc(20 + level * 4 + strlen(namefound)
1018 					+ strlen(datatype) + strlen(content));
1019 			memset(textline, 32, 20 + level * 4);
1020 			sprintf(textline + level * 4, "\"%s\" (%s) : %s", namefound,
1021 				datatype, content);
1022 			textlist->AddItem(textline);
1023 			delete[] datatype;
1024 			delete[] content;
1025 
1026 			if (typefound == B_MESSAGE_TYPE) {
1027 				msg->FindMessage(namefound, j - 1, &a_message);
1028 				add_message_contents(textlist, &a_message, level + 1);
1029 			} else if (typefound == B_RAW_TYPE && strcmp(namefound,
1030 					"_previous_") == 0) {
1031 				if (a_message.Unflatten((const char *)voidptr) == B_OK)
1032 					add_message_contents(textlist, &a_message, level + 1);
1033 			}
1034 		}
1035 	}
1036 }
1037 
1038 
1039 char *
1040 get_datatype_string(int32 type)
1041 {
1042 	char *str = new char[128];
1043 
1044 	switch (type) {
1045 		case B_ANY_TYPE:	strcpy(str, "B_ANY_TYPE"); break;
1046 		case B_ASCII_TYPE:	strcpy(str, "B_ASCII_TYPE"); break;
1047 		case B_BOOL_TYPE:	strcpy(str, "B_BOOL_TYPE"); break;
1048 		case B_CHAR_TYPE:	strcpy(str, "B_CHAR_TYPE"); break;
1049 		case B_COLOR_8_BIT_TYPE:	strcpy(str, "B_COLOR_8_BIT_TYPE"); break;
1050 		case B_DOUBLE_TYPE:	strcpy(str, "B_DOUBLE_TYPE"); break;
1051 		case B_FLOAT_TYPE:	strcpy(str, "B_FLOAT_TYPE"); break;
1052 		case B_GRAYSCALE_8_BIT_TYPE:	strcpy(str, "B_GRAYSCALE_8_BIT_TYPE"); break;
1053 		case B_INT64_TYPE:	strcpy(str, "B_INT64_TYPE"); break;
1054 		case B_INT32_TYPE:	strcpy(str, "B_INT32_TYPE"); break;
1055 		case B_INT16_TYPE:	strcpy(str, "B_INT16_TYPE"); break;
1056 		case B_INT8_TYPE:	strcpy(str, "B_INT8_TYPE"); break;
1057 		case B_MESSAGE_TYPE:	strcpy(str, "B_MESSAGE_TYPE"); break;
1058 		case B_MESSENGER_TYPE:	strcpy(str, "B_MESSENGER_TYPE"); break;
1059 		case B_MIME_TYPE:	strcpy(str, "B_MIME_TYPE"); break;
1060 		case B_MONOCHROME_1_BIT_TYPE:	strcpy(str, "B_MONOCHROME_1_BIT_TYPE"); break;
1061 		case B_OBJECT_TYPE:	strcpy(str, "B_OBJECT_TYPE"); break;
1062 		case B_OFF_T_TYPE:	strcpy(str, "B_OFF_T_TYPE"); break;
1063 		case B_PATTERN_TYPE:	strcpy(str, "B_PATTERN_TYPE"); break;
1064 		case B_POINTER_TYPE:	strcpy(str, "B_POINTER_TYPE"); break;
1065 		case B_POINT_TYPE:	strcpy(str, "B_POINT_TYPE"); break;
1066 		case B_RAW_TYPE:	strcpy(str, "B_RAW_TYPE"); break;
1067 		case B_RECT_TYPE:	strcpy(str, "B_RECT_TYPE"); break;
1068 		case B_REF_TYPE:	strcpy(str, "B_REF_TYPE"); break;
1069 		case B_RGB_32_BIT_TYPE:	strcpy(str, "B_RGB_32_BIT_TYPE"); break;
1070 		case B_RGB_COLOR_TYPE:	strcpy(str, "B_RGB_COLOR_TYPE"); break;
1071 		case B_SIZE_T_TYPE:	strcpy(str, "B_SIZE_T_TYPE"); break;
1072 		case B_SSIZE_T_TYPE:	strcpy(str, "B_SSIZE_T_TYPE"); break;
1073 		case B_STRING_TYPE:	strcpy(str, "B_STRING_TYPE"); break;
1074 		case B_TIME_TYPE :	strcpy(str, "B_TIME_TYPE"); break;
1075 		case B_UINT64_TYPE:	strcpy(str, "B_UINT64_TYPE"); break;
1076 		case B_UINT32_TYPE:	strcpy(str, "B_UINT32_TYPE"); break;
1077 		case B_UINT16_TYPE:	strcpy(str, "B_UINT16_TYPE"); break;
1078 		case B_UINT8_TYPE:	strcpy(str, "B_UINT8_TYPE"); break;
1079 		case B_PROPERTY_INFO_TYPE: strcpy(str, "B_PROPERTY_INFO_TYPE"); break;
1080 		// message constants:
1081 		case B_ABOUT_REQUESTED:	strcpy(str, "B_ABOUT_REQUESTED"); break;
1082 		case B_WINDOW_ACTIVATED:	strcpy(str, "B_WINDOW_ACTIVATED"); break;
1083 		case B_ARGV_RECEIVED:	strcpy(str, "B_ARGV_RECEIVED"); break;
1084 		case B_QUIT_REQUESTED:	strcpy(str, "B_QUIT_REQUESTED"); break;
1085 		case B_CANCEL:	strcpy(str, "B_CANCEL"); break;
1086 		case B_KEY_DOWN:	strcpy(str, "B_KEY_DOWN"); break;
1087 		case B_KEY_UP:	strcpy(str, "B_KEY_UP"); break;
1088 		case B_MINIMIZE:	strcpy(str, "B_MINIMIZE"); break;
1089 		case B_MOUSE_DOWN:	strcpy(str, "B_MOUSE_DOWN"); break;
1090 		case B_MOUSE_MOVED:	strcpy(str, "B_MOUSE_MOVED"); break;
1091 		case B_MOUSE_ENTER_EXIT:	strcpy(str, "B_MOUSE_ENTER_EXIT"); break;
1092 		case B_MOUSE_UP:	strcpy(str, "B_MOUSE_UP"); break;
1093 		case B_PULSE:	strcpy(str, "B_PULSE"); break;
1094 		case B_READY_TO_RUN:	strcpy(str, "B_READY_TO_RUN"); break;
1095 		case B_REFS_RECEIVED:	strcpy(str, "B_REFS_RECEIVED"); break;
1096 		case B_SCREEN_CHANGED:	strcpy(str, "B_SCREEN_CHANGED"); break;
1097 		case B_VALUE_CHANGED:	strcpy(str, "B_VALUE_CHANGED"); break;
1098 		case B_VIEW_MOVED:	strcpy(str, "B_VIEW_MOVED"); break;
1099 		case B_VIEW_RESIZED:	strcpy(str, "B_VIEW_RESIZED"); break;
1100 		case B_WINDOW_MOVED:	strcpy(str, "B_WINDOW_MOVED"); break;
1101 		case B_WINDOW_RESIZED:	strcpy(str, "B_WINDOW_RESIZED"); break;
1102 		case B_WORKSPACES_CHANGED:	strcpy(str, "B_WORKSPACES_CHANGED"); break;
1103 		case B_WORKSPACE_ACTIVATED:	strcpy(str, "B_WORKSPACE_ACTIVATED"); break;
1104 		case B_ZOOM:	strcpy(str, "B_ZOOM"); break;
1105 		case _APP_MENU_:	strcpy(str, "_APP_MENU_"); break;
1106 		case _BROWSER_MENUS_:	strcpy(str, "_BROWSER_MENUS_"); break;
1107 		case _MENU_EVENT_:	strcpy(str, "_MENU_EVENT_"); break;
1108 		case _QUIT_:	strcpy(str, "_QUIT_"); break;
1109 		case _VOLUME_MOUNTED_:	strcpy(str, "_VOLUME_MOUNTED_"); break;
1110 		case _VOLUME_UNMOUNTED_:	strcpy(str, "_VOLUME_UNMOUNTED_"); break;
1111 		case _MESSAGE_DROPPED_:	strcpy(str, "_MESSAGE_DROPPED_"); break;
1112 		case _MENUS_DONE_:	strcpy(str, "_MENUS_DONE_"); break;
1113 		case _SHOW_DRAG_HANDLES_:	strcpy(str, "_SHOW_DRAG_HANDLES_"); break;
1114 		case B_SET_PROPERTY:	strcpy(str, "B_SET_PROPERTY"); break;
1115 		case B_GET_PROPERTY:	strcpy(str, "B_GET_PROPERTY"); break;
1116 		case B_CREATE_PROPERTY:	strcpy(str, "B_CREATE_PROPERTY"); break;
1117 		case B_DELETE_PROPERTY:	strcpy(str, "B_DELETE_PROPERTY"); break;
1118 		case B_COUNT_PROPERTIES:	strcpy(str, "B_COUNT_PROPERTIES"); break;
1119 		case B_EXECUTE_PROPERTY:      strcpy(str, "B_EXECUTE_PROPERTY"); break;
1120 		case B_GET_SUPPORTED_SUITES:	strcpy(str, "B_GET_SUPPORTED_SUITES"); break;
1121 		case B_CUT:	strcpy(str, "B_CUT"); break;
1122 		case B_COPY:	strcpy(str, "B_COPY"); break;
1123 		case B_PASTE:	strcpy(str, "B_PASTE"); break;
1124 		case B_SELECT_ALL:	strcpy(str, "B_SELECT_ALL"); break;
1125 		case B_SAVE_REQUESTED:	strcpy(str, "B_SAVE_REQUESTED"); break;
1126 		case B_MESSAGE_NOT_UNDERSTOOD:	strcpy(str, "B_MESSAGE_NOT_UNDERSTOOD"); break;
1127 		case B_NO_REPLY:	strcpy(str, "B_NO_REPLY"); break;
1128 		case B_REPLY:	strcpy(str, "B_REPLY"); break;
1129 		case B_SIMPLE_DATA:	strcpy(str, "B_SIMPLE_DATA"); break;
1130 		//case B_MIME_DATA	 :	strcpy(str, "B_MIME_DATA"); break;
1131 		case B_ARCHIVED_OBJECT:	strcpy(str, "B_ARCHIVED_OBJECT"); break;
1132 		case B_UPDATE_STATUS_BAR:	strcpy(str, "B_UPDATE_STATUS_BAR"); break;
1133 		case B_RESET_STATUS_BAR:	strcpy(str, "B_RESET_STATUS_BAR"); break;
1134 		case B_NODE_MONITOR:	strcpy(str, "B_NODE_MONITOR"); break;
1135 		case B_QUERY_UPDATE:	strcpy(str, "B_QUERY_UPDATE"); break;
1136 		case B_BAD_SCRIPT_SYNTAX: strcpy(str, "B_BAD_SCRIPT_SYNTAX"); break;
1137 
1138 		// specifiers:
1139 		case B_NO_SPECIFIER:	strcpy(str, "B_NO_SPECIFIER"); break;
1140 		case B_DIRECT_SPECIFIER:	strcpy(str, "B_DIRECT_SPECIFIER"); break;
1141 		case B_INDEX_SPECIFIER:		strcpy(str, "B_INDEX_SPECIFIER"); break;
1142 		case B_REVERSE_INDEX_SPECIFIER:	strcpy(str, "B_REVERSE_INDEX_SPECIFIER"); break;
1143 		case B_RANGE_SPECIFIER:	strcpy(str, "B_RANGE_SPECIFIER"); break;
1144 		case B_REVERSE_RANGE_SPECIFIER:	strcpy(str, "B_REVERSE_RANGE_SPECIFIER"); break;
1145 		case B_NAME_SPECIFIER:	strcpy(str, "B_NAME_SPECIFIER"); break;
1146 
1147 		case B_ERROR:	strcpy(str, "B_ERROR"); break;
1148 
1149 		default:	// unknown
1150 			id_to_string(type, str);
1151 			break;
1152 	}
1153 	return str;
1154 }
1155 
1156 
1157 char *
1158 format_data(int32 type, char *ptr, long size)
1159 {
1160 	char idtext[32];
1161 	char *str;
1162 	float *fptr;
1163 	double *dptr;
1164 	entry_ref aref;
1165 	BEntry entry;
1166 	BPath path;
1167 	int64 i64;
1168 	int32 i32;
1169 	int16 i16;
1170 	int8 i8;
1171 	uint64 ui64;
1172 	uint32 ui32;
1173 	uint16 ui16;
1174 	uint8 ui8;
1175 	BMessage anothermsg;
1176 	char *tempstr;
1177 
1178 	if (size <= 0L) {
1179 		str = new char[1];
1180 		*str = 0;
1181 		return str;
1182 	}
1183 
1184 	switch (type) {
1185 		case B_MIME_TYPE:
1186 		case B_ASCII_TYPE:
1187 		case B_STRING_TYPE:
1188 			if (size > 512)
1189 				size = 512;
1190 			str = new char[size + 4];
1191 			*str='\"';
1192 			strncpy(str + 1, ptr, size);
1193 			strcat(str, "\"");
1194 			break;
1195 
1196 		case B_POINTER_TYPE:
1197 			str = new char[64];
1198 			sprintf(str, "%p", *(void**)ptr);
1199 			break;
1200 
1201 		case B_REF_TYPE:
1202 			str = new char[1024];
1203 			anothermsg.AddData("myref", B_REF_TYPE, ptr, size);
1204 			anothermsg.FindRef("myref", &aref);
1205 			if (entry.SetTo(&aref)==B_OK){
1206 				entry.GetPath(&path);
1207 				strcpy(str, path.Path());
1208 			} else
1209 				strcpy(str, "invalid entry_ref");
1210 			break;
1211 
1212 		case B_SSIZE_T_TYPE:
1213 		case B_INT64_TYPE:
1214 			str = new char[64];
1215 			i64 = *(int64*)ptr;
1216 			sprintf(str, "%" B_PRId64 " (0x%" B_PRIx64 ")", i64, i64);
1217 			break;
1218 
1219 		case B_SIZE_T_TYPE:
1220 		case B_INT32_TYPE:
1221 			str = new char[64];
1222 			i32 = *(int32*)ptr;
1223 			sprintf(str, "%" B_PRId32 " (0x%08" B_PRId32 ")", i32, i32);
1224 			break;
1225 
1226 		case B_INT16_TYPE:
1227 			str = new char[64];
1228 			i16 = *(int16*)ptr;
1229 			sprintf(str, "%d (0x%04X)", i16, i16);
1230 			break;
1231 
1232 		case B_CHAR_TYPE:
1233 		case B_INT8_TYPE:
1234 			str = new char[64];
1235 			i8 = *(int8*)ptr;
1236 			sprintf(str, "%d (0x%02X)", i8, i8);
1237 			break;
1238 
1239 		case B_UINT64_TYPE:
1240 			str = new char[64];
1241 			ui64 = *(uint64*)ptr;
1242 			sprintf(str, "%" B_PRIu64 " (0x%" B_PRIx64 ")", ui64, ui64);
1243 			break;
1244 
1245 		case B_UINT32_TYPE:
1246 			str = new char[64];
1247 			ui32 = *(uint32*)ptr;
1248 			sprintf(str, "%" B_PRIu32 " (0x%08" B_PRIx32 ")", ui32, ui32);
1249 			break;
1250 
1251 		case B_UINT16_TYPE:
1252 			str = new char[64];
1253 			ui16 = *(uint16*)ptr;
1254 			sprintf(str, "%u (0x%04X)", ui16, ui16);
1255 			break;
1256 
1257 		case B_UINT8_TYPE:
1258 			str = new char[64];
1259 			ui8 = *(uint8*)ptr;
1260 			sprintf(str, "%u (0x%02X)", ui8, ui8);
1261 			break;
1262 
1263 		case B_BOOL_TYPE:
1264 			str = new char[10];
1265 			if (*ptr)
1266 				strcpy(str, "TRUE");
1267 			else
1268 				strcpy(str, "FALSE");
1269 			break;
1270 
1271 		case B_FLOAT_TYPE:
1272 			str = new char[40];
1273 			fptr = (float*)ptr;
1274 			sprintf(str, "%.3f", *fptr);
1275 			break;
1276 
1277 		case B_DOUBLE_TYPE:
1278 			str = new char[40];
1279 			dptr = (double*)ptr;
1280 			sprintf(str, "%.3f", *dptr);
1281 			break;
1282 
1283 		case B_RECT_TYPE:
1284 			str = new char[200];
1285 			fptr = (float*)ptr;
1286 			sprintf(str, "BRect(%.1f, %.1f, %.1f, %.1f)", fptr[0], fptr[1],
1287 				fptr[2], fptr[3]);
1288 			break;
1289 
1290 		case B_POINT_TYPE:
1291 			str = new char[200];
1292 			fptr = (float*)ptr;
1293 			sprintf(str, "BPoint(%.1f, %.1f)", fptr[0], fptr[1]);
1294 			break;
1295 
1296 		case B_RGB_COLOR_TYPE:
1297 			str = new char[64];
1298 			sprintf(str, "Red=%u  Green=%u  Blue=%u  Alpha=%u",
1299 				((uint8*)ptr)[0], ((uint8*)ptr)[1], ((uint8*)ptr)[2],
1300 				((uint8*)ptr)[3]);
1301 			break;
1302 
1303 		case B_COLOR_8_BIT_TYPE:
1304 			str = new char[size * 6 + 4];
1305 			*str = 0;
1306 			for (int32 i = 0; i < min_c(256, size); i++) {
1307 				sprintf(idtext, "%u ", ((unsigned char*)ptr)[i]);
1308 				strcat(str,idtext);
1309 			}
1310 			*(str+strlen(str)-2) = 0;
1311 			break;
1312 
1313 		case B_MESSAGE_TYPE:
1314 			str = new char[64];
1315 			if (anothermsg.Unflatten((const char *)ptr) == B_OK) {
1316 				char *whatString = get_datatype_string(anothermsg.what);
1317 				sprintf(str, "what=%s", whatString);
1318 				delete[] whatString;
1319 			} else
1320 				strcpy(str, "error when unflattening");
1321 			break;
1322 
1323 		case B_PROPERTY_INFO_TYPE:
1324 		{
1325 			BPropertyInfo propinfo;
1326 			if (propinfo.Unflatten(B_PROPERTY_INFO_TYPE, (const void *)ptr,
1327 					size) == B_OK) {
1328 				str = new char[size * 32];	// an approximation
1329 
1330 				const property_info *pinfo = propinfo.Properties();
1331 
1332 				sprintf(str, "\n        property   commands                            "
1333 					"specifiers              types\n-----------------------------------"
1334 					"----------------------------------------------------------------\n");
1335 				for (int32 pinfo_index = 0; pinfo_index < propinfo.CountProperties(); pinfo_index++) {
1336 					strcat(str,  "                "
1337 						+ (strlen(pinfo[pinfo_index].name) < 16 ?
1338 							strlen(pinfo[pinfo_index].name) : 16));
1339 					strcat(str, pinfo[pinfo_index].name);
1340 					strcat(str, "   ");
1341 					char *start = str + strlen(str);
1342 
1343 					for (int32 i = 0; i < 10 && pinfo[pinfo_index].commands[i];
1344 							i++) {
1345 						tempstr = get_datatype_string(
1346 							pinfo[pinfo_index].commands[i]);
1347 						strcat(str, tempstr);
1348 						strcat(str, " ");
1349 						delete[] tempstr;
1350 					}
1351 
1352 					// pad the rest with spaces
1353 					if (strlen(start) < 36) {
1354 						strcat(str, "                                    "
1355 							+ strlen(start));
1356 					} else
1357 						strcat(str, "  " );
1358 
1359 					for (int32 i = 0; i < 10 && pinfo[pinfo_index].specifiers[i]; i++) {
1360 						switch (pinfo[pinfo_index].specifiers[i]) {
1361 							case B_NO_SPECIFIER:
1362 								strcat(str, "NONE ");
1363 								break;
1364 							case B_DIRECT_SPECIFIER:
1365 								strcat(str, "DIRECT ");
1366 								break;
1367 							case B_INDEX_SPECIFIER:
1368 								strcat(str, "INDEX ");
1369 								break;
1370 							case B_REVERSE_INDEX_SPECIFIER:
1371 								strcat(str, "REV.INDEX ");
1372 								break;
1373 							case B_RANGE_SPECIFIER:
1374 								strcat(str, "RANGE ");
1375 								break;
1376 							case B_REVERSE_RANGE_SPECIFIER:
1377 								strcat(str, "REV.RANGE ");
1378 								break;
1379 							case B_NAME_SPECIFIER:
1380 								strcat(str, "NAME ");
1381 								break;
1382 							case B_ID_SPECIFIER:
1383 								strcat(str, "ID ");
1384 								break;
1385 							default:
1386 								strcat(str, "<NONE> ");
1387 								break;
1388 							}
1389 						}
1390 
1391 						// pad the rest with spaces
1392 						if (strlen(start) < 60) {
1393 							strcat(str, "                                      "
1394 								"                      " + strlen(start));
1395 						} else
1396 							strcat(str, "  ");
1397 						for (int32 i = 0; i < 10
1398 								&& pinfo[pinfo_index].types[i] != 0; i++) {
1399 							uint32 type = pinfo[pinfo_index].types[i];
1400 							char str2[6];
1401 							snprintf(str2, sizeof(str2), "%c%c%c%c ",
1402 								int(type & 0xFF000000) >> 24,
1403 								int(type & 0xFF0000) >> 16,
1404 								int(type & 0xFF00) >> 8,
1405 								(int)type & 0xFF);
1406 							strcat(str, str2);
1407 						}
1408 
1409 						for (int32 i = 0; i < 3; i++) {
1410 							for (int32 j = 0; j < 5
1411 									&& pinfo[pinfo_index].ctypes[i].pairs[j].type
1412 										!= 0; j++) {
1413 								uint32 type = pinfo[pinfo_index].ctypes[i].pairs[j].type;
1414 								char str2[strlen(pinfo[pinfo_index].ctypes[i].pairs[j].name) + 8];
1415 								snprintf(str2, sizeof(str2),
1416 									"(%s %c%c%c%c)",
1417 									pinfo[pinfo_index].ctypes[i].pairs[j].name,
1418 									int(type & 0xFF000000) >> 24,
1419 									int(type & 0xFF0000) >> 16,
1420 									int(type & 0xFF00) >> 8,
1421 									(int)type & 0xFF);
1422 								strcat(str, str2);
1423 							}
1424 						}
1425 						strcat(str, "\n");
1426 
1427 						// is there usage info?
1428 						if (pinfo[pinfo_index].usage) {
1429 							strcat(str, "                   Usage: ");
1430 							strcat(str, pinfo[pinfo_index].usage);
1431 							strcat(str, "\n");
1432 						}
1433 					}
1434 
1435 
1436 					// handle value infos....
1437 				const value_info *vinfo = propinfo.Values();
1438 				int32 vinfo_count = propinfo.CountValues();
1439 #if TEST_VALUEINFO>0
1440 				value_info vinfo[10] = {
1441 					{"Backup", 'back', B_COMMAND_KIND,
1442 						"This command backs up your hard drive."},
1443 					{"Abort", 'abor', B_COMMAND_KIND,
1444 						"Stops the current operation..."},
1445 					{"Type Code", 'type', B_TYPE_CODE_KIND,
1446 						"Type code info..."}
1447 				};
1448 				vinfo_count = 3;
1449 #endif
1450 
1451 				if (vinfo && vinfo_count > 0) {
1452 					sprintf(str + strlen(str),
1453 						"\n            name   value                            "
1454 						"   kind\n---------------------------------------------"
1455 						"-----------------------------------\n");
1456 
1457 					for (int32 vinfo_index = 0; vinfo_index < vinfo_count;
1458 						vinfo_index++) {
1459 						char *start = str + strlen(str);
1460 						strcat(str, "                " + (strlen(vinfo[vinfo_index].name) < 16 ? strlen(vinfo[vinfo_index].name) : 16));
1461 						strcat(str, vinfo[vinfo_index].name);
1462 						strcat(str, "   ");
1463 
1464 						sprintf(str + strlen(str), "0x%8" B_PRIx32 " (",
1465 							vinfo[vinfo_index].value);
1466 						id_to_string(vinfo[vinfo_index].value, str + strlen(str));
1467 						strcat(str, ")");
1468 
1469 							// pad the rest with spaces
1470 						if (strlen(start) < 36 + 19) {
1471 							strcat(str, "                                      "
1472 								"                 " + strlen(start));
1473 						} else
1474 							strcat(str, "  ");
1475 
1476 						switch (vinfo[vinfo_index].kind) {
1477 							case B_COMMAND_KIND:
1478 								strcat(str, "COMMAND         ");
1479 								break;
1480 
1481 							case B_TYPE_CODE_KIND:
1482 								strcat(str, "TYPE CODE       ");
1483 								break;
1484 
1485 							default:
1486 								strcat(str, "unknown         ");
1487 								break;
1488 						}
1489 
1490 						strcat(str, "\n");
1491 
1492 							// is there usage info?
1493 						if (vinfo[vinfo_index].usage) {
1494 							strcat(str, "                   Usage: ");
1495 							strcat(str, vinfo[vinfo_index].usage);
1496 							strcat(str, "\n");
1497 						}
1498 					}
1499 				}
1500 			} else {
1501 				str = new char[64];
1502 				strcpy(str, "error when unflattening");
1503 			}
1504 			break;
1505 		}
1506 
1507 		default:
1508 			str = new char[min_c(256, size) * 20 + 4];
1509 			*str = 0;
1510 			for (int32 i = 0; i < min_c(256, size); i++) {
1511 				sprintf(idtext, "0x%02X, ", (uint16)ptr[i]);
1512 				strcat(str, idtext);
1513 			}
1514 			*(str + strlen(str) - 2) = 0;
1515 			break;
1516 	}
1517 
1518 	return str;
1519 }
1520 
1521 
1522 char *
1523 id_to_string(long ID, char *here)
1524 {
1525 	uint8 digit0 = (ID>>24)& 255;
1526 	uint8 digit1 = (ID>>16)& 255;
1527 	uint8 digit2 = (ID>>8) & 255;
1528 	uint8 digit3 = (ID) & 255;
1529 	bool itsvalid = false;
1530 
1531 	if (digit0 == 0) {
1532 		if (digit1 == 0) {
1533 			if (digit2 == 0) {
1534 				// 1 digits
1535 				itsvalid = is_valid_char(digit3);
1536 				sprintf(here, "'%c'", digit3);
1537 			} else {
1538 				// 2 digits
1539 				itsvalid = is_valid_char(digit2) && is_valid_char(digit3);
1540 				sprintf(here, "'%c%c'", digit2, digit3);
1541 			}
1542 		} else {
1543 			// 3 digits
1544 			itsvalid = is_valid_char(digit1) && is_valid_char(digit2)
1545 				&& is_valid_char(digit3);
1546 			sprintf(here, "'%c%c%c'", digit1, digit2, digit3);
1547 		}
1548 	} else {
1549 		// 4 digits
1550 		itsvalid = is_valid_char(digit0) && is_valid_char(digit1)
1551 			&& is_valid_char(digit2) && is_valid_char(digit3);
1552 		sprintf(here, "'%c%c%c%c'", digit0, digit1, digit2, digit3);
1553 	}
1554 
1555 	if (!itsvalid)
1556 		sprintf(here, "%ldL", ID);
1557 
1558 	return here;
1559 }
1560 
1561 
1562 bool
1563 is_valid_char(uint8 c)
1564 {
1565 	return c >= 32 && c < 128;
1566 }
1567 
1568