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