From 84d89d2ae0c6a8c7657bd23919c804858fd1c6b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Wed, 7 Jun 2006 08:28:16 +0000 Subject: [PATCH] added hey v1.2.8, written by Attila Mezei Changes : I renamed to .cpp and include the header file in the main file git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17749 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/bin/Jamfile | 1 + src/bin/hey.cpp | 1439 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1440 insertions(+) create mode 100644 src/bin/hey.cpp diff --git a/src/bin/Jamfile b/src/bin/Jamfile index 5778ebe4e3..5396bb584c 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -66,6 +66,7 @@ StdBinCommands dstcheck.cpp # factor.cpp ffm.cpp + hey.cpp iroster.cpp listattr.cpp listres.cpp diff --git a/src/bin/hey.cpp b/src/bin/hey.cpp new file mode 100644 index 0000000000..d94b43fef2 --- /dev/null +++ b/src/bin/hey.cpp @@ -0,0 +1,1439 @@ +// hey +// a small scripting utility +// written by Attila Mezei (attila.mezei@mail.datanet.hu) +// contributions by Sander Stoks, Peter Folk, Chris Herborth, Marco Nelissen, Scott Lindsey and others +// +// public domain, use it at your own risk +// +// 1.2.8: (Sander Stoks): Added command-line option -o which will output the "result" value +// in the reply message to stdout, so you can use it in shell scripting more easily: +// "hey Becasso get AspectRatio of Canvas 0" +// outputs +// Reply BMessage(B_REPLY): +// "result" (B_DOUBLE_TYPE) : 0.600 +// but "hey -o Becasso get AspectRatio of Canvas 0" +// outputs 0.600000 directly. +// +// 1.2.7: by Sander Stoks: Made a fork since I don't think Attila still supports "hey", and +// because the latest version on BeBits seems to be 1.2.4. +// Changes w.r.t. 1.2.6: When an application returns an error on a message, hey now +// keeps iterating over applications with the same signature. This is useful because, +// for instance, Terminal starts as a new process for each instance, so it previously +// wouldn't work to move a specific Terminal window using hey. You can now say +// "hey Terminal set Frame of Window foo to BRect[...]". +// +// 1.2.6: syntax extended by Sander Stoks to contain: +// do the x of y -3 of z '"1"' +// I.e. "do" => B_EXECUTE_PROPERTY, optional "the" makes direct specifiers +// more like english, bare reverse-index-specifiers are now handled, and +// named specifiers can contain only digits by quoting it (but make sure the +// shell passed the quotes through). +// +// Hey(target,const char*,reply) was previously limited to 100 tokens. It +// now uses a vector<> so it's only limited by available memory. +// +// Also, the archive name is now Y2K compliant =) +// +// 1.2.3: new option: -s for silent processing (no reply or errors printed) AM +// +// 1.2.2: Fixes by Marco Nelissen (marcone@xs4all.nl) +// - fixed parsing of negative numbers +// - fixed "with" syntax, which was broken (after a create, "with" would be taken as a specifier) +// +// 1.2.1: compiled for x86, R4 with minor modifications at BPropertyInfo +// +// 1.2.0: the syntax is extended by Sander Stoks (sander@adamation.com) to contain +// with name= [and name= [...]] +// at the end of the command which will add additional data to the scripting message. E.g: +// hey Becasso create Canvas with name=MyCanvas and size=BRect(100,100,300,300) +// Also a small interpreter is included. +// +// Detailed printout of B_PROPERTY_INFO in BMessages. Better than BPropertyInfo::PrintToStream(). +// Also prints usage info for a property if defined. +// +// 1.1.1: minor change from chrish@qnx.com to return -1 if an error is +// sent back in the reply message; also added B_COUNT_PROPERTIES support +// +// The range specifier sent to the target was 1 greater than it should've been. Fixed. +// +// 'hey' made the assumption that the first thread in a team will be the +// application thread (and therefore have the application's name). +// This was not always the case. Fix from Scott Lindsey . +// +//v1.1.0: Flattened BPropertyInfo is printed if found in the reply of B_GET_SUPPORTED_SUITES +// 1,2,3 and 4 character message constant is supported (e.g. '1', '12', '123', '1234') +// Alpha is sent with rgb_color +// +//v1.0.0 First public release + + +#include +#include +#include +#include +#include +#include +#include + +int32 HeyInterpreterThreadHook(void* arg); + +status_t Hey(BMessenger* target, const char* arg, BMessage* reply); +bool isSpace(char c); +status_t Hey(BMessenger* target, char* argv[], int32* argx, int32 argc, BMessage* reply); +status_t add_specifier(BMessage *to_message, char *argv[], int32 *argx, int32 argc); +status_t add_data(BMessage *to_message, char *argv[], int32 *argx); +status_t add_with(BMessage *to_message, char *argv[], int32 *argx, int32 argc); +void add_message_contents(BList *textlist, BMessage *msg, int32 level); +char *get_datatype_string(int32 type); +char *format_data(int32 type, char *ptr, long size); +void print_message(BMessage *message); +char *id_to_string(long ID, char *here); +bool is_valid_char(uint8 c); + +const char VERSION[]="v1.2.8"; + +#define DEBUG_HEY 0 // 1: prints the script message to be sent to the target application, 0: prints only the reply + + +// test, these should be zero for normal operation +#define TEST_VALUEINFO 0 + + +// flag for silent mode +bool silent; +// flag for stdout mode +bool output; + +int parse(BMessenger& the_application, int argc, char *argv[], int32 argapp) +{ + if(!the_application.IsValid()){ + if(!silent) fprintf(stderr, "Cannot find the application (%s)\n", argv[argapp]); + return -1; + } + + if (argc < 3) { + if(!silent) fprintf(stderr, "Cannot find the verb!\n"); + return -1; + } + + + BMessage the_reply; + int32 argx = argapp+1; +// const char *test_string = "set File of Window Sample to file(/boot/home/media/images/BeLogo.psd)"; +// status_t err = Hey(&the_application, test_string, &the_reply); + status_t err = Hey(&the_application, argv, &argx, argc, &the_reply); + + if (err!=B_OK) { + if(!silent) fprintf(stderr, "Error when sending message to %s!\n", argv[argapp]); + return -1; + } else { + if(the_reply.what==(uint32)B_MESSAGE_NOT_UNDERSTOOD || the_reply.what==(uint32)B_ERROR){ // I do it myself + if(the_reply.HasString("message")){ + if(!silent) printf("%s (error 0x%8lX)\n", the_reply.FindString("message"), the_reply.FindInt32("error")); + }else{ + if(!silent) printf("error 0x%8lX\n", the_reply.FindInt32("error")); + } + return 1; + }else{ + if(!silent){ + if(output){ + type_code tc; + if(the_reply.GetInfo("result", &tc) == B_OK){ + if(tc==B_INT8_TYPE){ + int8 v; + the_reply.FindInt8("result", &v); + printf("%d\n", v); + } else if(tc==B_INT16_TYPE){ + int16 v; + the_reply.FindInt16("result", &v); + printf("%d\n", v); + } else if(tc==B_INT32_TYPE){ + int32 v; + the_reply.FindInt32("result", &v); + printf("%ld\n", v); + } else if(tc==B_UINT8_TYPE){ + uint8 v; + the_reply.FindInt8("result", (int8*)&v); + printf("%u\n", v); + } else if(tc==B_UINT16_TYPE){ + uint16 v; + the_reply.FindInt16("result", (int16*)&v); + printf("%u\n", v); + } else if(tc==B_UINT32_TYPE){ + uint32 v; + the_reply.FindInt32("result", (int32*)&v); + printf("%lu\n", v); + } else if(tc==B_STRING_TYPE){ + const char* v; + the_reply.FindString("result", &v); + printf("%s\n", v); + } else if(tc==B_FLOAT_TYPE){ + float v; + the_reply.FindFloat("result", &v); + printf("%f\n", v); + } else if(tc==B_DOUBLE_TYPE){ + double v; + the_reply.FindDouble("result", &v); + printf("%f\n", v); + } else if(tc==B_BOOL_TYPE){ + bool v; + the_reply.FindBool("result", &v); + printf("%s\n", v?"true":"false"); + } else { + printf("Unsupported type\n"); + } + } + } + else { + printf("Reply "); + print_message(&the_reply); + printf("\n"); + } + } + } + } + return 0; +} + +int main(int argc, char *argv[]) +{ + BApplication app("application/x-amezei-hey"); + + if (argc < 2) { + fprintf(stderr, "hey %s, written by Attila Mezei (attila.mezei@mail.datanet.hu)\n" \ + "usage: hey [-s][-o] [let do] >* [to ] [with name= [and name=]*]\n" \ + "where : DO|GET|SET|COUNT|CREATE|DELETE|GETSUITES|QUIT|SAVE|LOAD|'what'\n" \ + " : [the] [ | name | \"name\" | '\"name\"' ]\n" \ + " : int | -int | '['int']' | '['-int']' | '['startint to end']'\n" \ + " : \"string\" | | | bool(value) | int8(value)\n" \ + " | int16(value) | int32(value) | float(value) | double(value)\n" \ + " | BPoint(x,y) | BRect(l,t,r,b) | rgb_color(r,g,b,a) | file(path)\n" \ + "options: -s: silent\n" \ + " -o: output result to stdout for easy parsing\n\n", VERSION); + // Updated Usage string to reflect "do", "the", bare -index, and '"name"' changes below + // -- pfolk@uni.uiuc.edu 1999-11-03 + + return -1; + } + + int32 argapp = 1; + silent=false; + output=false; + + // Updated option mechanism --SS + for (int i = 0; i < argc; i++) + { + if(strcmp(argv[i], "-s")==0 || strcmp(argv[i], "-S")==0){ + silent=true; + argapp++; + } + if(strcmp(argv[i], "-o")==0 || strcmp(argv[i], "-O")==0){ + output=true; + argapp++; + } + } + + // find the application + BMessenger the_application; + BList team_list; + team_id teamid; + app_info appinfo; + + be_roster->GetAppList(&team_list); + for(int32 i=0;iGetRunningAppInfo(teamid, &appinfo); + if(strcmp(appinfo.signature, argv[argapp])==0){ + the_application=BMessenger(appinfo.signature); + if (!parse(the_application, argc, argv, argapp)) + return 0; + }else{ + if(strcmp(appinfo.ref.name, argv[argapp])==0){ + the_application=BMessenger(0, teamid); + if (!parse(the_application, argc, argv, argapp)) + return 0; + } + } + } + + return -1; +} + + +int32 HeyInterpreterThreadHook(void* arg) +{ + if (arg) { + BMessage environment(*(BMessage*) arg); + char* prompt = "Hey"; + if (environment.HasString("prompt")) environment.FindString("prompt", (const char **)&prompt); + printf("%s> ", prompt); + + BMessenger target; + if (environment.HasMessenger("Target")) environment.FindMessenger("Target", &target); + + char command[1024]; + status_t err; + BMessage reply; + while (gets(command)) { + reply.MakeEmpty(); + err = Hey(&target, command, &reply); + if (!err) { + print_message(&reply); + } else { + printf("Error!\n"); + } + printf("%s> ", prompt); + } + + return 0; + + } else { + return 1; + } +} + +status_t Hey(BMessenger* target, const char* arg, BMessage* reply) +{ + vector argv; // number of tokens is now limited only by memory -- pfolk@uni.uiuc.edu 1999-11-03 + char* tokens = new char[strlen(arg)*2]; + char* currentToken = tokens; + int32 tokenNdex = 0; + int32 argNdex = 0; + bool inquotes = false; + + while (arg[argNdex] != 0) { // for each character in arg + if (arg[argNdex] == '\"') inquotes = !inquotes; + if (!inquotes && isSpace(arg[argNdex])) { // if the character is white space + if (tokenNdex!=0) { // close off currentToken token + currentToken[tokenNdex] = 0; + argv.push_back(currentToken); + currentToken += tokenNdex+1; + tokenNdex=0; + argNdex++; + } else { // just skip the whitespace + argNdex++; + } + } else { // copy char into current token + currentToken[tokenNdex] = arg[argNdex]; + tokenNdex++; + argNdex++; + } + } + + if (tokenNdex!=0) { // close off currentToken token + currentToken[tokenNdex] = 0; + argv.push_back(currentToken); + } + argv.push_back(NULL); + + int32 argx = 0; + status_t ret = Hey(target, argv.begin(), &argx, argv.size()-1, reply); + // This used to be "return Hey(...);"---so tokens wasn't delete'd. -- pfolk@uni.uiuc.edu 1999-11-03 + delete tokens; + return ret; +} + +bool isSpace(char c) +{ + switch (c) { + case ' ': + case '\t': + return true; + + default: + return false; + } +} + +status_t Hey(BMessenger* target, char* argv[], int32* argx, int32 argc, BMessage* reply) +{ + bool direct_what = false; + BMessage the_message; + if(strcasecmp(argv[*argx], "let")==0){ // added "let" -- sander@adamation.com 31may2000 + BMessage get_target (B_GET_PROPERTY); + get_target.AddSpecifier ("Messenger"); + // parse the specifiers + (*argx)++; + status_t result=B_OK; + while((result=add_specifier(&get_target, argv, argx, argc))==B_OK){}; + + if(result!=B_ERROR){ // bad syntax + if(!silent) fprintf(stderr, "Bad specifier syntax!\n"); + return result; + } + BMessage msgr; + if (target && target->IsValid()) { + result = target->SendMessage(&get_target, &msgr); + if (result!=B_OK) return result; + result = msgr.FindMessenger ("result", target); + if (result!=B_OK) { + if (!silent) fprintf(stderr, "Couldn't retrieve the BMessenger!\n"); + return result; + } + } + if (!argv[*argx]) { + if (!silent) fprintf(stderr, "Syntax error - forgot \"do\"?\n"); + return B_ERROR; + } + } + if(strcasecmp(argv[*argx], "do")==0){ // added "do" -- pfolk@uni.uiuc.edu 1999-11-03 + the_message.what=B_EXECUTE_PROPERTY; + }else if(strcasecmp(argv[*argx], "get")==0){ + the_message.what=B_GET_PROPERTY; + }else if(strcasecmp(argv[*argx], "set")==0){ + the_message.what=B_SET_PROPERTY; + }else if(strcasecmp(argv[*argx], "create")==0){ + the_message.what=B_CREATE_PROPERTY; + }else if(strcasecmp(argv[*argx], "delete")==0){ + the_message.what=B_DELETE_PROPERTY; + }else if(strcasecmp(argv[*argx], "quit")==0){ + the_message.what=B_QUIT_REQUESTED; + }else if(strcasecmp(argv[*argx], "save")==0){ + the_message.what=B_SAVE_REQUESTED; + }else if(strcasecmp(argv[*argx], "load")==0){ + the_message.what=B_REFS_RECEIVED; + }else if(strcasecmp(argv[*argx], "count")==0){ + the_message.what=B_COUNT_PROPERTIES; + }else if(strcasecmp(argv[*argx], "getsuites")==0){ + the_message.what=B_GET_SUPPORTED_SUITES; + }else{ + switch(strlen(argv[*argx])){ // can be a message constant if 1,2,3 or 4 chars + case 1: + the_message.what=(int32)argv[*argx][0]; + break; + case 2: + the_message.what=(((int32)argv[*argx][0])<<8)|(((int32)argv[*argx][1])); + break; + case 3: + the_message.what=(((int32)argv[*argx][0])<<16)|(((int32)argv[*argx][1])<<8)|(((int32)argv[*argx][2])); + break; + case 4: + the_message.what=(((int32)argv[*argx][0])<<24)|(((int32)argv[*argx][1])<<16)|(((int32)argv[*argx][2])<<8)|(((int32)argv[*argx][3])); + break; + default: + // maybe this is a user defined command, ask for the supported suites + bool found=false; + if (target && target->IsValid()) { + BMessage rply; + if(target->SendMessage(&BMessage(B_GET_SUPPORTED_SUITES), &rply)==B_OK){ + // if all goes well, rply contains all kinds of property infos + int32 j=0; + void *voidptr; + int32 sizefound; + BPropertyInfo propinfo; + const value_info *vinfo; + int32 vinfo_index, vinfo_count; + +// const char *str; +// while (rply.FindString("suites", j++, &str) == B_OK) +// printf ("Suite %ld: %s\n", j, str); +// +// j = 0; + while(rply.FindData("messages", B_PROPERTY_INFO_TYPE, j++, (const void **)&voidptr, &sizefound)==B_OK && !found){ + if(propinfo.Unflatten(B_PROPERTY_INFO_TYPE, (const void *)voidptr, sizefound)==B_OK){ + vinfo=propinfo.Values(); + vinfo_index=0; + vinfo_count=propinfo.CountValues(); +#if TEST_VALUEINFO>0 + value_info vinfo[10]={ {"Backup", 'back', B_COMMAND_KIND, "This command backs up your hard drive."}, + {"Abort", 'abor', B_COMMAND_KIND, "Stops the current operation..."}, + {"Type Code", 'type', B_TYPE_CODE_KIND, "Type code info..."} + }; + vinfo_count=3; +#endif + + while(vinfo_index0 + printf("FOUND COMMAND \"%s\" = %lX\n", vinfo[vinfo_index].name, the_message.what); +#endif + break; + } + vinfo_index++; + } + + } + } + } + } + + + if(!found){ + if(!silent) fprintf(stderr, "Bad verb (\"%s\")\n", argv[*argx]); + return -1; + } + } + direct_what = true; + } + + status_t result=B_OK; + (*argx)++; + + // One exception: Single data item at end of line. + if (direct_what && *argx == argc - 1 && argv[*argx] != NULL) { + add_data(&the_message, argv, argx); + } + else { + // parse the specifiers + if(the_message.what!=B_REFS_RECEIVED){ // LOAD has no specifier + while((result=add_specifier(&the_message, argv, argx, argc))==B_OK){}; + + if(result!=B_ERROR){ // bad syntax + if(!silent) fprintf(stderr, "Bad specifier syntax!\n"); + return result; + } + } + } + + // if verb is SET or LOAD, there should be a to + if((the_message.what==B_SET_PROPERTY || the_message.what==B_REFS_RECEIVED) && argv[*argx]!=NULL){ + if(strcasecmp(argv[*argx], "to")==0){ + (*argx)++; + } + result=add_data(&the_message, argv, argx); + if(result!=B_OK){ + if(result==B_FILE_NOT_FOUND){ + if(!silent) fprintf(stderr, "File not found!\n"); + }else{ + if(!silent) fprintf(stderr, "Invalid 'to...' value format!\n"); + } + return result; + } + } + + add_with(&the_message, argv, argx, argc); + +#if DEBUG_HEY>0 + fprintf(stderr, "Send "); + print_message(&the_message); + fprintf(stderr, "\n"); +#endif + + if (target && target->IsValid()) { + if (reply) { + result = target->SendMessage(&the_message, reply); + } else { + result = target->SendMessage(&the_message); + } + } + return result; +} + +// There can be a with =() [and = ...] +// I treat "and" just the same as "with", it's just to make the script syntax more English-like. +status_t add_with(BMessage *to_message, char *argv[], int32 *argx, int32 argc) +{ + status_t result=B_OK; + if(*argx < argc - 1 && argv[++(*argx)]!=NULL){ + // printf ("argv[%ld] = %s\n", *argx, argv[*argx]); + if(strcasecmp(argv[*argx], "with")==0){ + // printf ("\"with\" detected!\n"); + (*argx)++; + bool done = false; + do + { + result=add_data(to_message, argv, argx); + if(result!=B_OK){ + if(result==B_FILE_NOT_FOUND){ + if(!silent) fprintf(stderr, "File not found!\n"); + }else{ + if(!silent) fprintf(stderr, "Invalid 'with...' value format!\n"); + } + return result; + } + (*argx)++; + // printf ("argc = %d, argv[%d] = %s\n", argc, *argx, argv[*argx]); + if (*argx < argc - 1 && strcasecmp(argv[*argx], "and")==0) + { + (*argx)++; + } + else + done = true; + } while (!done); + } + } + return result; +} + +// returns B_OK if successful +// B_ERROR if no more specifiers +// B_BAD_SCRIPT_SYNTAX if syntax error +status_t add_specifier(BMessage *to_message, char *argv[], int32 *argx, int32 argc) +{ + + char *property=argv[*argx]; + + if(property==NULL) return B_ERROR; // no more specifiers + + (*argx)++; + + if(strcasecmp(property, "do")==0){ // Part of the "hey App let Specifier do Verb". + return B_ERROR; // no more specifiers + } + + if(strcasecmp(property, "to")==0){ // it is the 'to' string!!! + return B_ERROR; // no more specifiers + } + + if(strcasecmp(property, "with")==0){ // it is the 'with' string!!! + *argx -= 2; + add_with (to_message, argv, argx, argc); + return B_ERROR; // no more specifiers + } + + if(strcasecmp(property, "of")==0){ // skip "of", read real property + property=argv[*argx]; + if(property==NULL) return B_BAD_SCRIPT_SYNTAX; // bad syntax + (*argx)++; + } + + if(strcasecmp(property, "the")==0){ // skip "the", read real property -- pfolk@uni.uiuc.edu 1999-11-03 + property=argv[*argx]; + if(property==NULL) return B_BAD_SCRIPT_SYNTAX; // bad syntax + (*argx)++; + } + + // decide the specifier + + char *specifier=NULL; + if(to_message->what==B_CREATE_PROPERTY) // create is always direct. without this, a "with" would be taken as a specifier + (*argx)--; + else + specifier=argv[*argx]; + if(specifier==NULL){ // direct specifier + to_message->AddSpecifier(property); + return B_ERROR; // no more specifiers + } + + (*argx)++; + + if(strcasecmp(specifier, "of")==0){ // direct specifier + to_message->AddSpecifier(property); + return B_OK; + } + + if(strcasecmp(specifier, "to")==0){ // direct specifier + to_message->AddSpecifier(property); + return B_ERROR; // no more specifiers + } + + + if(specifier[0]=='['){ // index, reverse index or range + char *end; + int32 ix1, ix2; + if(specifier[1]=='-'){ // reverse index + ix1=strtoul(specifier+2, &end, 10); + BMessage revspec(B_REVERSE_INDEX_SPECIFIER); + revspec.AddString("property", property); + revspec.AddInt32("index", ix1); + to_message->AddSpecifier(&revspec); + }else{ // index or range + ix1=strtoul(specifier+1, &end, 10); + if(end[0]==']'){ // it was an index + to_message->AddSpecifier(property, ix1); + return B_OK; + }else{ + specifier=argv[*argx]; + if(specifier==NULL){ + // I was wrong, it was just an index + to_message->AddSpecifier(property, ix1); + return B_OK; + } + (*argx)++; + if(strcasecmp(specifier, "to")==0){ + specifier=argv[*argx]; + if(specifier==NULL){ + return B_BAD_SCRIPT_SYNTAX; // wrong syntax + } + (*argx)++; + ix2=strtoul(specifier, &end, 10); + to_message->AddSpecifier(property, ix1, ix2-ix1>0 ? ix2-ix1 : 1); + return B_OK; + }else{ + return B_BAD_SCRIPT_SYNTAX; // wrong syntax + } + } + } + }else{ // name specifier + // if it contains only digits, it will be an index... + bool index_spec=true; + bool reverse = specifier[0]=='-'; + // accept bare reverse-index-specs -- pfolk@uni.uiuc.edu 1999-11-03 + size_t speclen = strlen(specifier); + for (int32 i=(reverse?1:0); i<(int32)speclen; ++i){ + if(specifier[i]<'0' || specifier[i]>'9'){ + index_spec=false; + break; + } + } + + if(index_spec){ + if (reverse) + { + // Copied from above -- pfolk@uni.uiuc.edu 1999-11-03 + BMessage revspec(B_REVERSE_INDEX_SPECIFIER); + revspec.AddString("property", property); + revspec.AddInt32("index", atol(specifier+1)); + to_message->AddSpecifier(&revspec); + } + else to_message->AddSpecifier(property, atol(specifier)); + }else{ + // Allow any name by counting an initial " as a literal-string indicator + // -- pfolk@uni.uiuc.edu 1999-11-03 + if(specifier[0]=='\"') + { + if (specifier[speclen-1]=='\"') + specifier[speclen-1]='\0'; + ++specifier; + --speclen; + } + to_message->AddSpecifier(property, specifier); + } + + } + + return B_OK; +} + + +status_t add_data(BMessage *to_message, char *argv[], int32 *argx) +{ + char *valuestring=argv[*argx]; + + if(valuestring==NULL) return B_ERROR; + + // try to interpret it as an integer or float + bool contains_only_digits=true; + bool is_floating_point=false; + for(int32 i=0;i<(int32)strlen(valuestring);i++){ + if(i!=0 || valuestring[i]!='-') { + if(valuestring[i]<'0' || valuestring[i]>'9'){ + if(valuestring[i]=='.'){ + is_floating_point=true; + }else{ + contains_only_digits=false; + break; + } + } + } + } + //printf("%d %d\n", contains_only_digits,is_floating_point); + if(contains_only_digits){ + if(is_floating_point){ + to_message->AddFloat("data", atof(valuestring)); + return B_OK; + }else{ + to_message->AddInt32("data", atol(valuestring)); + return B_OK; + } + } + + // if true or false, it is bool + if(strcasecmp(valuestring, "true")==0){ + to_message->AddBool("data", true); + return B_OK; + }else if(strcasecmp(valuestring, "false")==0){ + to_message->AddBool("data", false); + return B_OK; + } + + // Add support for "=()" here: + // The type is then added under the name "name". + + #define MAX_NAME_LENGTH 128 + char curname[MAX_NAME_LENGTH]; + strcpy (curname, "data"); // This is the default. + + char *s = valuestring; + while (*++s && *s != '=') + // Look for a '=' character... + ; + if (*s == '=') // We found a = + { + *s = 0; + strcpy (curname, valuestring); // Use the new + valuestring = s + 1; // Reposition the valuestring ptr. + } + + // must begin with a type( value ) + if(strncasecmp(valuestring, "int8", strlen("int8"))==0){ + to_message->AddInt8(curname, atol(valuestring+strlen("int8("))); + return B_OK; + }else if(strncasecmp(valuestring, "int16", strlen("int16"))==0){ + to_message->AddInt16(curname, atol(valuestring+strlen("int16("))); + return B_OK; + }else if(strncasecmp(valuestring, "int32", strlen("int32"))==0){ + to_message->AddInt32(curname, atol(valuestring+strlen("int32("))); + return B_OK; + }else if(strncasecmp(valuestring, "int64", strlen("int64"))==0){ + to_message->AddInt64(curname, atol(valuestring+strlen("int64("))); + return B_OK; + }else if(strncasecmp(valuestring, "bool", strlen("bool"))==0){ + if(strncasecmp(valuestring+strlen("bool("), "true", 4)==0){ + to_message->AddBool(curname, true); + }else if(strncasecmp(valuestring+strlen("bool("), "false", 5)==0){ + to_message->AddBool(curname, false); + }else{ + to_message->AddBool(curname, atol(valuestring+strlen("bool("))==0 ? false : true); + } + return B_OK; + }else if(strncasecmp(valuestring, "float", strlen("float"))==0){ + to_message->AddFloat(curname, atof(valuestring+strlen("float("))); + return B_OK; + }else if(strncasecmp(valuestring, "double", strlen("double"))==0){ + to_message->AddDouble(curname, atof(valuestring+strlen("double("))); + return B_OK; + }else if(strncasecmp(valuestring, "BPoint", strlen("BPoint"))==0){ + float x,y; + x=atof(valuestring+strlen("BPoint(")); + if(strchr(valuestring, ',')){ + y=atof(strchr(valuestring, ',')+1); + }else if(strchr(valuestring, ' ')){ + y=atof(strchr(valuestring, ' ')+1); + }else{ // bad syntax + y=0.0f; + } + to_message->AddPoint(curname, BPoint(x,y)); + return B_OK; + }else if(strncasecmp(valuestring, "BRect", strlen("BRect"))==0){ + float l=0.0f, t=0.0f, r=0.0f, b=0.0f; + char *ptr; + l=atof(valuestring+strlen("BRect(")); + ptr=strchr(valuestring, ','); + if(ptr){ + t=atof(ptr+1); + ptr=strchr(ptr+1, ','); + if(ptr){ + r=atof(ptr+1); + ptr=strchr(ptr+1, ','); + if(ptr){ + b=atof(ptr+1); + } + } + } + + to_message->AddRect(curname, BRect(l,t,r,b)); + return B_OK; + }else if(strncasecmp(valuestring, "rgb_color", strlen("rgb_color"))==0){ + rgb_color clr; + char *ptr; + clr.red=atol(valuestring+strlen("rgb_color(")); + ptr=strchr(valuestring, ','); + if(ptr){ + clr.green=atol(ptr+1); + ptr=strchr(ptr+1, ','); + if(ptr){ + clr.blue=atol(ptr+1); + ptr=strchr(ptr+1, ','); + if(ptr){ + clr.alpha=atol(ptr+1); + } + } + } + + to_message->AddData(curname, B_RGB_COLOR_TYPE, &clr, sizeof(rgb_color)); + return B_OK; + }else if(strncasecmp(valuestring, "file", strlen("file"))==0){ + entry_ref file_ref; + + // remove the last ] or ) + if(valuestring[strlen(valuestring)-1]==')' || valuestring[strlen(valuestring)-1]==']'){ + valuestring[strlen(valuestring)-1]=0; + } + + if(get_ref_for_path(valuestring+5, &file_ref)!=B_OK){ + return B_FILE_NOT_FOUND; + } + + // check if the ref is valid + BEntry entry; + if(entry.SetTo(&file_ref)!=B_OK) return B_FILE_NOT_FOUND; + //if(!entry.Exists()) return B_FILE_NOT_FOUND; + + // add both ways, refsreceived needs it as "refs" while scripting needs "data" + to_message->AddRef("refs", &file_ref); + to_message->AddRef(curname, &file_ref); + return B_OK; + }else{ // it is string + // does it begin with a quote? + if(valuestring[0]=='\"'){ + if(valuestring[strlen(valuestring)-1]=='\"') valuestring[strlen(valuestring)-1]=0; + to_message->AddString(curname, valuestring+1); + }else{ + to_message->AddString(curname, valuestring); + } + return B_OK; + } + + return B_OK; +} + + + +void print_message(BMessage *message) +{ + + BList textlist; + add_message_contents(&textlist, message, 0); + + + printf("BMessage(%s):\n", get_datatype_string(message->what)); + for(int32 i=0;iCountNames(B_ANY_TYPE); + for(i=0;iGetInfo(B_ANY_TYPE, i, &namefound, &typefound); + j=0; + + while(msg->FindData(namefound, typefound, j++, (const void **)&voidptr, &sizefound)==B_OK){ + datatype=get_datatype_string(typefound); + content=format_data(typefound, (char*)voidptr, sizefound); + textline=(char*)malloc(20+level*4+strlen(namefound)+strlen(datatype)+strlen(content)); + memset(textline, 32, 20+level*4); + sprintf(textline+level*4, "\"%s\" (%s) : %s", namefound, datatype, content); + textlist->AddItem(textline); + delete [] datatype; + delete [] content; + + if(typefound==B_MESSAGE_TYPE){ + msg->FindMessage(namefound, j-1, &a_message); + add_message_contents(textlist, &a_message, level+1); + }else + if(typefound==B_RAW_TYPE && strcmp(namefound, "_previous_")==0){ + if(a_message.Unflatten((const char *)voidptr)==B_OK){ + add_message_contents(textlist, &a_message, level+1); + } + } + } + + } + +} + + + +char *get_datatype_string(int32 type) +{ + char *str=new char[128]; + + switch(type){ + case B_ANY_TYPE: strcpy(str, "B_ANY_TYPE"); break; + case B_ASCII_TYPE: strcpy(str, "B_ASCII_TYPE"); break; + case B_BOOL_TYPE: strcpy(str, "B_BOOL_TYPE"); break; + case B_CHAR_TYPE: strcpy(str, "B_CHAR_TYPE"); break; + case B_COLOR_8_BIT_TYPE: strcpy(str, "B_COLOR_8_BIT_TYPE"); break; + case B_DOUBLE_TYPE: strcpy(str, "B_DOUBLE_TYPE"); break; + case B_FLOAT_TYPE: strcpy(str, "B_FLOAT_TYPE"); break; + case B_GRAYSCALE_8_BIT_TYPE: strcpy(str, "B_GRAYSCALE_8_BIT_TYPE"); break; + case B_INT64_TYPE: strcpy(str, "B_INT64_TYPE"); break; + case B_INT32_TYPE: strcpy(str, "B_INT32_TYPE"); break; + case B_INT16_TYPE: strcpy(str, "B_INT16_TYPE"); break; + case B_INT8_TYPE: strcpy(str, "B_INT8_TYPE"); break; + case B_MESSAGE_TYPE: strcpy(str, "B_MESSAGE_TYPE"); break; + case B_MESSENGER_TYPE: strcpy(str, "B_MESSENGER_TYPE"); break; + case B_MIME_TYPE: strcpy(str, "B_MIME_TYPE"); break; + case B_MONOCHROME_1_BIT_TYPE: strcpy(str, "B_MONOCHROME_1_BIT_TYPE"); break; + case B_OBJECT_TYPE: strcpy(str, "B_OBJECT_TYPE"); break; + case B_OFF_T_TYPE: strcpy(str, "B_OFF_T_TYPE"); break; + case B_PATTERN_TYPE: strcpy(str, "B_PATTERN_TYPE"); break; + case B_POINTER_TYPE: strcpy(str, "B_POINTER_TYPE"); break; + case B_POINT_TYPE: strcpy(str, "B_POINT_TYPE"); break; + case B_RAW_TYPE: strcpy(str, "B_RAW_TYPE"); break; + case B_RECT_TYPE: strcpy(str, "B_RECT_TYPE"); break; + case B_REF_TYPE: strcpy(str, "B_REF_TYPE"); break; + case B_RGB_32_BIT_TYPE: strcpy(str, "B_RGB_32_BIT_TYPE"); break; + case B_RGB_COLOR_TYPE: strcpy(str, "B_RGB_COLOR_TYPE"); break; + case B_SIZE_T_TYPE: strcpy(str, "B_SIZE_T_TYPE"); break; + case B_SSIZE_T_TYPE : strcpy(str, "B_SSIZE_T_TYPE"); break; + case B_STRING_TYPE: strcpy(str, "B_STRING_TYPE"); break; + case B_TIME_TYPE : strcpy(str, "B_TIME_TYPE"); break; + case B_UINT64_TYPE : strcpy(str, "B_UINT64_TYPE"); break; + case B_UINT32_TYPE: strcpy(str, "B_UINT32_TYPE"); break; + case B_UINT16_TYPE : strcpy(str, "B_UINT16_TYPE"); break; + case B_UINT8_TYPE : strcpy(str, "B_UINT8_TYPE"); break; + case B_PROPERTY_INFO_TYPE: strcpy(str, "B_PROPERTY_INFO_TYPE"); break; + // message constants: + case B_ABOUT_REQUESTED : strcpy(str, "B_ABOUT_REQUESTED"); break; + case B_WINDOW_ACTIVATED : strcpy(str, "B_WINDOW_ACTIVATED"); break; + case B_ARGV_RECEIVED : strcpy(str, "B_ARGV_RECEIVED"); break; + case B_QUIT_REQUESTED : strcpy(str, "B_QUIT_REQUESTED"); break; + case B_CANCEL : strcpy(str, "B_CANCEL"); break; + case B_KEY_DOWN : strcpy(str, "B_KEY_DOWN"); break; + case B_KEY_UP : strcpy(str, "B_KEY_UP"); break; + case B_MINIMIZE : strcpy(str, "B_MINIMIZE"); break; + case B_MOUSE_DOWN : strcpy(str, "B_MOUSE_DOWN"); break; + case B_MOUSE_MOVED : strcpy(str, "B_MOUSE_MOVED"); break; + case B_MOUSE_ENTER_EXIT : strcpy(str, "B_MOUSE_ENTER_EXIT"); break; + case B_MOUSE_UP : strcpy(str, "B_MOUSE_UP"); break; + case B_PULSE : strcpy(str, "B_PULSE"); break; + case B_READY_TO_RUN : strcpy(str, "B_READY_TO_RUN"); break; + case B_REFS_RECEIVED : strcpy(str, "B_REFS_RECEIVED"); break; + case B_SCREEN_CHANGED : strcpy(str, "B_SCREEN_CHANGED"); break; + case B_VALUE_CHANGED : strcpy(str, "B_VALUE_CHANGED"); break; + case B_VIEW_MOVED : strcpy(str, "B_VIEW_MOVED"); break; + case B_VIEW_RESIZED : strcpy(str, "B_VIEW_RESIZED"); break; + case B_WINDOW_MOVED : strcpy(str, "B_WINDOW_MOVED"); break; + case B_WINDOW_RESIZED : strcpy(str, "B_WINDOW_RESIZED"); break; + case B_WORKSPACES_CHANGED : strcpy(str, "B_WORKSPACES_CHANGED"); break; + case B_WORKSPACE_ACTIVATED : strcpy(str, "B_WORKSPACE_ACTIVATED"); break; + case B_ZOOM : strcpy(str, "B_ZOOM"); break; + case _APP_MENU_ : strcpy(str, "_APP_MENU_"); break; + case _BROWSER_MENUS_ : strcpy(str, "_BROWSER_MENUS_"); break; + case _MENU_EVENT_ : strcpy(str, "_MENU_EVENT_"); break; + case _QUIT_ : strcpy(str, "_QUIT_"); break; + case _VOLUME_MOUNTED_ : strcpy(str, "_VOLUME_MOUNTED_"); break; + case _VOLUME_UNMOUNTED_ : strcpy(str, "_VOLUME_UNMOUNTED_"); break; + case _MESSAGE_DROPPED_ : strcpy(str, "_MESSAGE_DROPPED_"); break; + case _MENUS_DONE_ : strcpy(str, "_MENUS_DONE_"); break; + case _SHOW_DRAG_HANDLES_ : strcpy(str, "_SHOW_DRAG_HANDLES_"); break; + case B_SET_PROPERTY : strcpy(str, "B_SET_PROPERTY"); break; + case B_GET_PROPERTY : strcpy(str, "B_GET_PROPERTY"); break; + case B_CREATE_PROPERTY : strcpy(str, "B_CREATE_PROPERTY"); break; + case B_DELETE_PROPERTY : strcpy(str, "B_DELETE_PROPERTY"); break; + case B_COUNT_PROPERTIES : strcpy(str, "B_COUNT_PROPERTIES"); break; + case B_EXECUTE_PROPERTY : strcpy(str, "B_EXECUTE_PROPERTY"); break; + case B_GET_SUPPORTED_SUITES : strcpy(str, "B_GET_SUPPORTED_SUITES"); break; + case B_CUT : strcpy(str, "B_CUT"); break; + case B_COPY : strcpy(str, "B_COPY"); break; + case B_PASTE : strcpy(str, "B_PASTE"); break; + case B_SELECT_ALL : strcpy(str, "B_SELECT_ALL"); break; + case B_SAVE_REQUESTED : strcpy(str, "B_SAVE_REQUESTED"); break; + case B_MESSAGE_NOT_UNDERSTOOD : strcpy(str, "B_MESSAGE_NOT_UNDERSTOOD"); break; + case B_NO_REPLY : strcpy(str, "B_NO_REPLY"); break; + case B_REPLY : strcpy(str, "B_REPLY"); break; + case B_SIMPLE_DATA : strcpy(str, "B_SIMPLE_DATA"); break; + //case B_MIME_DATA : strcpy(str, "B_MIME_DATA"); break; + case B_ARCHIVED_OBJECT : strcpy(str, "B_ARCHIVED_OBJECT"); break; + case B_UPDATE_STATUS_BAR : strcpy(str, "B_UPDATE_STATUS_BAR"); break; + case B_RESET_STATUS_BAR : strcpy(str, "B_RESET_STATUS_BAR"); break; + case B_NODE_MONITOR : strcpy(str, "B_NODE_MONITOR"); break; + case B_QUERY_UPDATE : strcpy(str, "B_QUERY_UPDATE"); break; + case B_BAD_SCRIPT_SYNTAX: strcpy(str, "B_BAD_SCRIPT_SYNTAX"); break; + + // specifiers: + case B_NO_SPECIFIER : strcpy(str, "B_NO_SPECIFIER"); break; + case B_DIRECT_SPECIFIER : strcpy(str, "B_DIRECT_SPECIFIER"); break; + case B_INDEX_SPECIFIER : strcpy(str, "B_INDEX_SPECIFIER"); break; + case B_REVERSE_INDEX_SPECIFIER : strcpy(str, "B_REVERSE_INDEX_SPECIFIER"); break; + case B_RANGE_SPECIFIER : strcpy(str, "B_RANGE_SPECIFIER"); break; + case B_REVERSE_RANGE_SPECIFIER : strcpy(str, "B_REVERSE_RANGE_SPECIFIER"); break; + case B_NAME_SPECIFIER : strcpy(str, "B_NAME_SPECIFIER"); break; + + case B_ERROR : strcpy(str, "B_ERROR"); break; + + default: // unknown + id_to_string(type, str); + break; + } + + return str; + +} + + +char *format_data(int32 type, char *ptr, long size) +{ + char idtext[32]; + char *str; + float *fptr; + double *dptr; +// BRect *brptr; + long i; + entry_ref aref; + BEntry entry; + BPath path; + int64 i64; + int32 i32; + int16 i16; + int8 i8; + uint64 ui64; + uint32 ui32; + uint16 ui16; + uint8 ui8; + BMessage anothermsg; + BPropertyInfo propinfo; + const property_info *pinfo; + int32 pinfo_index; + const value_info *vinfo; + int32 vinfo_index, vinfo_count; + char *tempstr; + + + if(size<=0L){ + str=new char; + *str=0; + return str; + } + + switch(type){ + case B_MIME_TYPE: + case B_ASCII_TYPE: + case B_STRING_TYPE: + if(size>512) size=512; + str=new char[size+4]; + *str='\"'; + strncpy(str+1, ptr, size); + strcat(str, "\""); + break; + case B_POINTER_TYPE: + str=new char[64]; + sprintf(str, "%p", *(void**)ptr); + break; + + case B_REF_TYPE: + str=new char[1024]; + anothermsg.AddData("myref", B_REF_TYPE, ptr, size); + anothermsg.FindRef("myref", &aref); + if(entry.SetTo(&aref)==B_OK){ + entry.GetPath(&path); + strcpy(str, path.Path()); + }else{ + strcpy(str, "invalid entry_ref"); + } + break; + + case B_SSIZE_T_TYPE: + case B_INT64_TYPE: + str=new char[64]; + i64=*(int64*)ptr; + sprintf(str, "%Ld (0x%LX)", i64, i64); + break; + + case B_SIZE_T_TYPE: + case B_INT32_TYPE: + str=new char[64]; + i32=*(int32*)ptr; + sprintf(str, "%ld (0x%08lX)", i32, i32); + break; + + case B_INT16_TYPE: + str=new char[64]; + i16=*(int16*)ptr; + sprintf(str, "%d (0x%04X)", i16, i16); + break; + + case B_CHAR_TYPE: + case B_INT8_TYPE: + str=new char[64]; + i8=*(int8*)ptr; + sprintf(str, "%d (0x%02X)", i8, i8); + break; + + case B_UINT64_TYPE: + str=new char[64]; + ui64=*(uint64*)ptr; + sprintf(str, "%Lu (0x%LX)", ui64, ui64); + break; + + case B_UINT32_TYPE: + str=new char[64]; + ui32=*(uint32*)ptr; + sprintf(str, "%lu (0x%08lX)", ui32, ui32); + break; + + case B_UINT16_TYPE: + str=new char[64]; + ui16=*(uint16*)ptr; + sprintf(str, "%u (0x%04X)", ui16, ui16); + break; + + case B_UINT8_TYPE: + str=new char[64]; + ui8=*(uint8*)ptr; + sprintf(str, "%u (0x%02X)", ui8, ui8); + break; + + case B_BOOL_TYPE: + str=new char[10]; + if(*ptr){ + strcpy(str, "TRUE"); + }else{ + strcpy(str, "FALSE"); + } + break; + + case B_FLOAT_TYPE: + str=new char[40]; + fptr=(float*)ptr; + sprintf(str, "%.3f", *fptr); + break; + + case B_DOUBLE_TYPE: + str=new char[40]; + dptr=(double*)ptr; + sprintf(str, "%.3f", *dptr); + break; + + case B_RECT_TYPE: + str=new char[200]; + fptr=(float*)ptr; + sprintf(str, "BRect(%.1f, %.1f, %.1f, %.1f)", fptr[0], fptr[1], fptr[2], fptr[3]); + break; + + case B_POINT_TYPE: + str=new char[200]; + fptr=(float*)ptr; + sprintf(str, "BPoint(%.1f, %.1f)", fptr[0], fptr[1]); + break; + + case B_RGB_COLOR_TYPE: + str=new char[64]; + sprintf(str, "Red=%u Green=%u Blue=%u Alpha=%u", ((uint8*)ptr)[0], ((uint8*)ptr)[1], ((uint8*)ptr)[2], ((uint8*)ptr)[3] ); + break; + + case B_COLOR_8_BIT_TYPE: + str=new char[size*6+4]; + *str=0; + for(i=0;i "); break; + } + } + strcat(str, "\n"); + + // is there usage info? + if(pinfo[pinfo_index].usage){ + strcat(str, " Usage: "); + strcat(str, pinfo[pinfo_index].usage); + strcat(str, "\n"); + } + + + pinfo_index++; // take next propertyinfo + } + + + // handle value infos.... + vinfo=propinfo.Values(); + vinfo_index=0; + vinfo_count=propinfo.CountValues(); +#if TEST_VALUEINFO>0 + value_info vinfo[10]={ {"Backup", 'back', B_COMMAND_KIND, "This command backs up your hard drive."}, + {"Abort", 'abor', B_COMMAND_KIND, "Stops the current operation..."}, + {"Type Code", 'type', B_TYPE_CODE_KIND, "Type code info..."} + }; + vinfo_count=3; +#endif + + if(vinfo && vinfo_count>0){ + sprintf(str+strlen(str), "\n name value kind\n--------------------------------------------------------------------------------\n"); + + while(vinfo_index>24)&255; + uint8 digit1=(ID>>16)&255; + uint8 digit2=(ID>>8)&255; + uint8 digit3=(ID)&255; + bool itsvalid=false; + + if(digit0==0){ + if(digit1==0){ + if(digit2==0){ + // 1 digits + if(is_valid_char(digit3) ) itsvalid=TRUE; + sprintf(here, "'%c'", digit3); + }else{ + // 2 digits + if(is_valid_char(digit2) && is_valid_char(digit3) ) itsvalid=TRUE; + sprintf(here, "'%c%c'", digit2, digit3); + } + }else{ + // 3 digits + if(is_valid_char(digit1) && is_valid_char(digit2) && is_valid_char(digit3) ) itsvalid=TRUE; + sprintf(here, "'%c%c%c'", digit1, digit2, digit3); + } + }else{ + // 4 digits + if(is_valid_char(digit0) && is_valid_char(digit1) && is_valid_char(digit2) && is_valid_char(digit3) ) itsvalid=TRUE; + sprintf(here, "'%c%c%c%c'", digit0, digit1, digit2, digit3); + } + + if(!itsvalid){ + sprintf(here, "%ldL", ID); + } + + return here; +} + + +bool is_valid_char(uint8 c) +{ + return (c>=32 && c<128); +} +