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