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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 * 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 1563 is_valid_char(uint8 c) 1564 { 1565 return c >= 32 && c < 128; 1566 } 1567 1568