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