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