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