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