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 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
140 bool silent;
141 // flag for stdout mode
142 bool output;
143
144 status_t
parse(BMessenger & the_application,int argc,char * argv[],int32 argapp)145 parse(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
246 void
usage(int exitCode)247 usage(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
265 int
main(int argc,char * argv[])266 main(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
328 int32
HeyInterpreterThreadHook(void * arg)329 HeyInterpreterThreadHook(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
361 status_t
Hey(BMessenger * target,const char * arg,BMessage * reply)362 Hey(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
406 bool
isSpace(char c)407 isSpace(char c)
408 {
409 switch (c) {
410 case ' ':
411 case '\t':
412 return true;
413
414 default:
415 return false;
416 }
417 }
418
419
420 status_t
Hey(BMessenger * target,char * argv[],int32 * argx,int32 argc,BMessage * reply)421 Hey(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.
625 status_t
add_with(BMessage * to_message,char * argv[],int32 * argx,int32 argc)626 add_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
662 status_t
add_specifier(BMessage * to_message,char * argv[],int32 * argx,int32 argc)663 add_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
816 status_t
add_data(BMessage * to_message,char * argv[],int32 * argx)817 add_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
975 void
print_message(BMessage * message)976 print_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
991 void
add_message_contents(BList * textlist,BMessage * msg,int32 level)992 add_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
1039 char *
get_datatype_string(int32 type)1040 get_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
1157 char *
format_data(int32 type,char * ptr,long size)1158 format_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
1522 char *
id_to_string(long ID,char * here)1523 id_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
1562 bool
is_valid_char(uint8 c)1563 is_valid_char(uint8 c)
1564 {
1565 return c >= 32 && c < 128;
1566 }
1567
1568