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