1from __future__ import absolute_import, division, print_function 2import re 3import string 4import sys 5 6# types translated into "int" 7simpletypes = ["int", "gint", "guint", "gboolean", "size_t", "gssize", "time_t"] 8 9# List "excluded" contains functions that shouldn't be exported via 10# DBus. If you remove a function from this list, please make sure 11# that it does not break "make" with the configure option 12# "--enable-dbus" turned on. 13 14excluded = [\ 15 # I don't remember why this function is excluded; something to do 16 # with the fact that it takes a (const) GList as a parameter. 17 "purple_presence_add_list", 18 19 # These functions are excluded because they involve value of the 20 # type PurpleConvPlacementFunc, which is a pointer to a function and 21 # (currently?) can't be translated into a DBus type. Normally, 22 # functions with untranslatable types are skipped, but this script 23 # assumes that all non-pointer type names beginning with "Purple" 24 # are enums, which is not true in this case. 25 "purple_conv_placement_add_fnc", 26 "purple_conv_placement_get_fnc", 27 "purple_conv_placement_get_current_func", 28 "purple_conv_placement_set_current_func", 29 30 # Similar to the above: 31 "purple_account_set_register_callback", 32 "purple_account_unregister", 33 "purple_connection_new_unregister", 34 35 # These functions are excluded because they involve setting arbitrary 36 # data via pointers for protocols and UIs. This just won't work. 37 "purple_blist_get_ui_data", 38 "purple_blist_set_ui_data", 39 "purple_blist_node_get_ui_data", 40 "purple_blist_node_set_ui_data", 41 "purple_buddy_get_protocol_data", 42 "purple_buddy_set_protocol_data", 43 44 # This is excluded because this script treats PurpleLogReadFlags* 45 # as pointer to a struct, instead of a pointer to an enum. This 46 # causes a compilation error. Someone should fix this script. 47 "purple_log_read", 48 ] 49 50# This is a list of functions that return a GList* or GSList * whose elements 51# are strings, not pointers to objects. 52stringlists = [ 53 "purple_prefs_get_path_list", 54 "purple_prefs_get_string_list", 55 "purple_uri_list_extract_filenames", 56 "purple_uri_list_extract_uris", 57 "purple_prefs_get_children_names", 58] 59 60# This is a list of functions that return a GList* or GSList* that should 61# not be freed. Ideally, this information should be obtained from the Doxygen 62# documentation at some point. 63constlists = [ 64 "purple_account_get_status_types", 65 "purple_accounts_get_all", 66 "purple_account_option_get_list", 67 "purple_connections_get_all", 68 "purple_connections_get_connecting", 69 "purple_get_conversations", 70 "purple_get_ims", 71 "purple_get_chats", 72 "purple_conv_chat_get_users", 73 "purple_conv_chat_get_ignored", 74 "purple_mime_document_get_fields", 75 "purple_mime_document_get_parts", 76 "purple_mime_part_get_fields", 77 "purple_notify_user_info_get_entries", 78 "purple_request_fields_get_required", 79 "purple_request_field_list_get_selected", 80 "purple_request_field_list_get_items", 81 "purple_savedstatuses_get_all", 82 "purple_status_type_get_attrs", 83 "purple_presence_get_statuses", 84 "purple_conversation_get_message_history", 85] 86 87pointer = "#pointer#" 88 89class MyException(Exception): 90 pass 91 92myexception = MyException() 93 94def ctopascal(name): 95 newname = "" 96 for word in name.split("_"): 97 newname += word.capitalize() 98 return newname 99 100class Parameter(object): 101 def __init__(self, type, name): 102 self.name = name 103 self.type = type 104 105 def fromtokens(tokens, parameternumber = -1): 106 if len(tokens) == 0: 107 raise myexception 108 if (len(tokens) == 1) or (tokens[-1] == pointer): 109 if parameternumber >= 0: 110 return Parameter(tokens, "param%i" % parameternumber) 111 else: 112 raise myexception 113 else: 114 return Parameter(tokens[:-1], tokens[-1]) 115 116 fromtokens = staticmethod(fromtokens) 117 118class Binding(object): 119 def __init__(self, functiontext, paramtexts): 120 self.function = Parameter.fromtokens(functiontext.split()) 121 122 if self.function.name in excluded: 123 raise myexception 124 125 self.params = [] 126 for i in range(len(paramtexts)): 127 self.params.append(Parameter.fromtokens(paramtexts[i].split(), i)) 128 129 self.call = "%s(%s)" % (self.function.name, 130 ", ".join(param.name for param in self.params)) 131 132 133 def process(self): 134 for param in self.params: 135 self.processinput(param.type, param.name) 136 137 self.processoutput(self.function.type, "RESULT") 138 self.flush() 139 140 141 def processinput(self, type, name): 142 const = False 143 unsigned = False 144 if type[0] == "const": 145 type = type[1:] 146 const = True 147 148 if type[0] == "unsigned": 149 type = type[1:] 150 unsigned = True 151 152 if len(type) == 1: 153 # simple types (int, gboolean, etc.) and enums 154 if (type[0] in simpletypes) or ((type[0].startswith("Purple") and not type[0].endswith("Callback"))): 155 return self.inputsimple(type, name, unsigned) 156 157 # pointers ... 158 if (len(type) == 2) and (type[1] == pointer): 159 # strings 160 if type[0] in ["char", "gchar"]: 161 if const: 162 return self.inputstring(type, name, unsigned) 163 else: 164 raise myexception 165 166 elif type[0] == "GHashTable": 167 return self.inputhash(type, name) 168 169 # known object types are transformed to integer handles 170 elif type[0].startswith("Purple") or type[0] == "xmlnode": 171 return self.inputpurplestructure(type, name) 172 173 # special case for *_get_data functions, be careful here... 174 elif (type[0] == "size_t" or type[0] == "gsize") and name == "len": 175 return self.inputgetdata(type, name) 176 177 # unknown pointers are always replaced with NULL 178 else: 179 return self.inputpointer(type, name) 180 181 raise myexception 182 183 184 def processoutput(self, type, name): 185 const = False 186 unsigned = False 187 # the "void" type is simple ... 188 if type == ["void"]: 189 return self.outputvoid(type, name) 190 191 if type[0] == "const": 192 type = type[1:] 193 const = True 194 195 if type[0] == "unsigned": 196 type = type[1:] 197 unsigned = True 198 199 # a string 200 if type == ["char", pointer] or type == ["gchar", pointer]: 201 return self.outputstring(type, name, const) 202 203 # simple types (ints, booleans, enums, ...) 204 if (len(type) == 1) and \ 205 ((type[0] in simpletypes) or (type[0].startswith("Purple"))): 206 return self.outputsimple(type, name, unsigned) 207 208 # pointers ... 209 if (len(type) == 2) and (type[1] == pointer): 210 211 # handles 212 if type[0].startswith("Purple"): 213 return self.outputpurplestructure(type, name) 214 215 if type[0] in ["GList", "GSList"]: 216 return self.outputlist(type, name) 217 218 # Special case for *_get_data functions 219 if type[0] == "gconstpointer": 220 return self.outputgetdata(type, name) 221 222 raise myexception 223 224 225class ClientBinding (Binding): 226 def __init__(self, functiontext, paramtexts, knowntypes, headersonly): 227 Binding.__init__(self, functiontext, paramtexts) 228 self.knowntypes = knowntypes 229 self.headersonly = headersonly 230 self.paramshdr = [] 231 self.decls = [] 232 self.inputparams = [] 233 self.outputparams = [] 234 self.returncode = [] 235 236 def flush(self): 237 paramslist = ", ".join(self.paramshdr) 238 if (paramslist == "") : 239 paramslist = "void" 240 print("%s %s(%s)" % (self.functiontype, self.function.name, 241 paramslist), end=' ') 242 243 if self.headersonly: 244 print(";") 245 return 246 247 print("{") 248 249 for decl in self.decls: 250 print(decl) 251 252 print('dbus_g_proxy_call(purple_proxy, "%s", NULL,' % ctopascal(self.function.name)) 253 254 for type_name in self.inputparams: 255 print("\t%s, %s, " % type_name, end=' ') 256 print("G_TYPE_INVALID,") 257 258 for type_name in self.outputparams: 259 print("\t%s, &%s, " % type_name, end=' ') 260 print("G_TYPE_INVALID);") 261 262 for code in self.returncode: 263 print(code) 264 265 print("}\n") 266 267 268 def definepurplestructure(self, type): 269 if (self.headersonly) and (type[0] not in self.knowntypes): 270 print("struct _%s;" % type[0]) 271 print("typedef struct _%s %s;" % (type[0], type[0])) 272 self.knowntypes.append(type[0]) 273 274 def inputsimple(self, type, name, us): 275 self.paramshdr.append("%s %s" % (type[0], name)) 276 if us: 277 self.inputparams.append(("G_TYPE_UINT", name)) 278 else: 279 self.inputparams.append(("G_TYPE_INT", name)) 280 281 def inputstring(self, type, name, us): 282 if us: 283 self.paramshdr.append("const unsigned char *%s" % name) 284 else: 285 self.paramshdr.append("const char *%s" % name) 286 self.inputparams.append(("G_TYPE_STRING", name)) 287 288 def inputpurplestructure(self, type, name): 289 self.paramshdr.append("const %s *%s" % (type[0], name)) 290 self.inputparams.append(("G_TYPE_INT", "GPOINTER_TO_INT(%s)" % name)) 291 self.definepurplestructure(type) 292 293 def inputpointer(self, type, name): 294 name += "_NULL" 295 self.paramshdr.append("const %s *%s" % (type[0], name)) 296 self.inputparams.append(("G_TYPE_INT", "0")) 297 298 def inputhash(self, type, name): 299 self.paramshdr.append("const GHashTable *%s" % name) 300 self.inputparams.append(('dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING)', name)) 301 302 def outputvoid(self, type, name): 303 self.functiontype = "void" 304 305 def outputstring(self, type, name, const): 306 self.functiontype = "char*" 307 self.decls.append("char *%s = NULL;" % name) 308 self.outputparams.append(("G_TYPE_STRING", name)) 309# self.returncode.append("NULLIFY(%s);" % name) 310 self.returncode.append("return %s;" % name); 311 312 def outputsimple(self, type, name, us): 313 self.functiontype = type[0] 314 self.decls.append("%s %s = 0;" % (type[0], name)) 315 if us: 316 self.outputparams.append(("G_TYPE_UINT", name)) 317 else: 318 self.outputparams.append(("G_TYPE_INT", name)) 319 self.returncode.append("return %s;" % name); 320 321 # we could add "const" to the return type but this would probably 322 # be a nuisance 323 def outputpurplestructure(self, type, name): 324 name = name + "_ID" 325 self.functiontype = "%s*" % type[0] 326 self.decls.append("int %s = 0;" % name) 327 self.outputparams.append(("G_TYPE_INT", "%s" % name)) 328 self.returncode.append("return (%s*) GINT_TO_POINTER(%s);" % (type[0], name)); 329 self.definepurplestructure(type) 330 331 def outputlist(self, type, name): 332 self.functiontype = "%s*" % type[0] 333 self.decls.append("GArray *%s;" % name) 334 self.outputparams.append(('dbus_g_type_get_collection("GArray", G_TYPE_INT)', name)) 335 self.returncode.append("return garray_int_to_%s(%s);" % 336 (type[0].lower(), name)); 337 338 # Special case for *_get_data functions, don't need client bindings, 339 # but do need the name so it doesn't crash 340 def inputgetdata(self, type, name): 341 raise myexception 342 def outputgetdata(self, type, name): 343 raise myexception 344 345class ServerBinding (Binding): 346 def __init__(self, functiontext, paramtexts): 347 Binding.__init__(self, functiontext, paramtexts) 348 self.dparams = "" 349 self.cparams = [] 350 self.cdecls = [] 351 self.ccode = [] 352 self.cparamsout = [] 353 self.ccodeout = [] 354 self.argfunc = "dbus_message_get_args" 355 356 def flush(self): 357 print("static DBusMessage*") 358 print("%s_DBUS(DBusMessage *message_DBUS, DBusError *error_DBUS) {" % \ 359 self.function.name) 360 361 print("\tDBusMessage *reply_DBUS;") 362 363 for decl in self.cdecls: 364 print(decl) 365 366 print("\t%s(message_DBUS, error_DBUS," % self.argfunc,end=' ') 367 for param in self.cparams: 368 print("DBUS_TYPE_%s, &%s," % param, end=' ') 369 print("DBUS_TYPE_INVALID);") 370 371 print("\tCHECK_ERROR(error_DBUS);") 372 373 for code in self.ccode: 374 print(code) 375 376 print("\treply_DBUS = dbus_message_new_method_return (message_DBUS);") 377 378 print("\tdbus_message_append_args(reply_DBUS,", end=' ') 379 for param in self.cparamsout: 380 if type(param) is str: 381 print("%s," % param, end=' ') 382 else: 383 print("DBUS_TYPE_%s, &%s," % param, end=' ') 384 print("DBUS_TYPE_INVALID);") 385 386 for code in self.ccodeout: 387 print(code) 388 389 print("\treturn reply_DBUS;\n}\n") 390 391 392 def addstring(self, *items): 393 for item in items: 394 self.dparams += item + r"\0" 395 396 def addintype(self, type, name): 397 self.addstring("in", type, name) 398 399 def addouttype(self, type, name): 400 self.addstring("out", type, name) 401 402 403 # input parameters 404 405 def inputsimple(self, type, name, us): 406 if us: 407 self.cdecls.append("\tdbus_uint32_t %s;" % name) 408 self.cparams.append(("UINT32", name)) 409 self.addintype("u", name) 410 else: 411 self.cdecls.append("\tdbus_int32_t %s;" % name) 412 self.cparams.append(("INT32", name)) 413 self.addintype("i", name) 414 415 def inputstring(self, type, name, us): 416 if us: 417 self.cdecls.append("\tconst unsigned char *%s;" % name) 418 else: 419 self.cdecls.append("\tconst char *%s;" % name) 420 self.cparams.append(("STRING", name)) 421 self.ccode.append("\t%s = (%s && %s[0]) ? %s : NULL;" % (name,name,name,name)) 422 self.addintype("s", name) 423 424 def inputhash(self, type, name): 425 self.argfunc = "purple_dbus_message_get_args" 426 self.cdecls.append("\tDBusMessageIter %s_ITER;" % name) 427 self.cdecls.append("\tGHashTable *%s;" % name) 428 self.cparams.append(("ARRAY", "%s_ITER" % name)) 429 self.ccode.append("\t%s = purple_dbus_iter_hash_table(&%s_ITER, error_DBUS);" \ 430 % (name, name)) 431 self.ccode.append("\tCHECK_ERROR(error_DBUS);") 432 self.ccodeout.append("\tg_hash_table_destroy(%s);" % name) 433 self.addintype("a{ss}", name) 434 435 def inputpurplestructure(self, type, name): 436 self.cdecls.append("\tdbus_int32_t %s_ID;" % name) 437 self.cdecls.append("\t%s *%s;" % (type[0], name)) 438 self.cparams.append(("INT32", name + "_ID")) 439 self.ccode.append("\tPURPLE_DBUS_ID_TO_POINTER(%s, %s_ID, %s, error_DBUS);" % \ 440 (name, name, type[0])) 441 self.addintype("i", name) 442 443 def inputpointer(self, type, name): 444 self.cdecls.append("\tdbus_int32_t %s_NULL;" % name) 445 self.cdecls .append("\t%s *%s;" % (type[0], name)) 446 self.cparams.append(("INT32", name + "_NULL")) 447 self.ccode .append("\t%s = NULL;" % name) 448 self.addintype("i", name) 449 450 # output parameters 451 452 def outputvoid(self, type, name): 453 self.ccode.append("\t%s;" % self.call) # just call the function 454 455 def outputstring(self, type, name, const): 456 if const: 457 self.cdecls.append("\tconst char *%s;" % name) 458 else: 459 self.cdecls.append("\tchar *%s;" % name) 460 self.ccode.append("\tif ((%s = %s) == NULL)" % (name, self.call)) 461 self.ccode.append("\t\t%s = \"\";" % (name)) 462 self.cparamsout.append(("STRING", name)) 463 self.addouttype("s", name) 464 if not const: 465 self.ccodeout.append("\tg_free(%s);" % name) 466 467 def outputsimple(self, type, name, us): 468 if us: 469 self.cdecls.append("\tdbus_uint32_t %s;" % name) 470 self.cparamsout.append(("UINT32", name)) 471 self.addouttype("u", name) 472 else: 473 self.cdecls.append("\tdbus_int32_t %s;" % name) 474 self.cparamsout.append(("INT32", name)) 475 self.addouttype("i", name) 476 self.ccode.append("\t%s = %s;" % (name, self.call)) 477 478 def outputpurplestructure(self, type, name): 479 self.cdecls.append("\tdbus_int32_t %s;" % name) 480 self.ccode .append("\tPURPLE_DBUS_POINTER_TO_ID(%s, %s, error_DBUS);" % (name, self.call)) 481 self.cparamsout.append(("INT32", name)) 482 self.addouttype("i", name) 483 484 # GList*, GSList*, assume that list is a list of objects 485 # unless the function is in stringlists 486 def outputlist(self, type, name): 487 self.cdecls.append("\tdbus_int32_t %s_LEN;" % name) 488 self.ccodeout.append("\tg_free(%s);" % name) 489 490 self.cdecls.append("\t%s *list;" % type[0]); 491 492 if self.function.name in stringlists: 493 self.cdecls.append("\tchar **%s;" % name) 494 self.ccode.append("\tlist = %s;" % self.call) 495 self.ccode.append("\t%s = (char **)purple_%s_to_array(list, FALSE, &%s_LEN);" % \ 496 (name, type[0], name)) 497 self.cparamsout.append("DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &%s, %s_LEN" \ 498 % (name, name)) 499 if (not (self.function.name in constlists)): 500 type_name = type[0].lower()[1:] 501 self.ccodeout.append("\tg_%s_foreach(list, (GFunc)g_free, NULL);" % type_name) 502 self.ccodeout.append("\tg_%s_free(list);" % type_name) 503 self.addouttype("as", name) 504 else: 505 self.cdecls.append("\tdbus_int32_t *%s;" % name) 506 self.ccode.append("\tlist = %s;" % self.call) 507 self.ccode.append("\t%s = purple_dbusify_%s(list, FALSE, &%s_LEN);" % \ 508 (name, type[0], name)) 509 if (not (self.function.name in constlists)): 510 self.ccode.append("\tg_%s_free(list);" % type[0].lower()[1:]) 511 self.cparamsout.append("DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &%s, %s_LEN" \ 512 % (name, name)) 513 self.addouttype("ai", name) 514 515 # Special case for *_get_data functions 516 def inputgetdata(self, type, name): 517 self.cdecls.append("\tsize_t %s = 0;" % name) 518 return True 519 def outputgetdata(self, type, name): 520 # This is a total hack, but self.call is set up before the parameters 521 # are processed, so we can't tell it to pass a parameter by reference. 522 self.call = "%s(%s)" % (self.function.name, 523 ", ".join([(param.name, "&len")[param.name == "len"] for param in self.params])) 524 525 self.cdecls.append("\tgconstpointer %s;" % name) 526 self.ccode.append("\t%s = %s;" % (name, self.call)) 527 self.cparamsout.append("DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &%s, %s" \ 528 % (name, "len")) 529 self.addouttype("ay", name) 530 531class BindingSet(object): 532 regexp = r"^(\w[^()]*)\(([^()]*)\)\s*;\s*$"; 533 534 def __init__(self, inputfile, fprefix): 535 self.inputiter = iter(inputfile) 536 self.functionregexp = \ 537 re.compile("^%s(\w[^()]*)\(([^()]*)\)\s*;\s*$" % fprefix) 538 self.typeregexp = re.compile("^\w+\s*\*?\s*$") 539 540 541 542 def process(self): 543 print("/* Generated by %s. Do not edit! */" % sys.argv[0]) 544 545 for line in self.inputiter: 546 words = line.split() 547 if len(words) == 0: # empty line 548 continue 549 if line[0] == "#": # preprocessor directive 550 continue 551 if words[0] in ["typedef", "struct", "enum", "static"]: 552 continue 553 554 # accumulate lines until the parentheses are balance or an 555 # empty line has been encountered 556 myline = line.strip() 557 while (myline.count("(") > myline.count(")")) or self.typeregexp.match(myline): 558 newline = next(self.inputiter).strip() 559 if len(newline) == 0: 560 break 561 myline += " " + newline 562 563 # is this a function declaration? 564 thematch = self.functionregexp.match( 565 myline.replace("*", " " + pointer + " ")) 566 567 if thematch is None: 568 continue 569 570 functiontext = thematch.group(1) 571 paramstext = thematch.group(2).strip() 572 573 if (paramstext == "void") or (paramstext == ""): 574 paramtexts = [] 575 else: 576 paramtexts = paramstext.split(",") 577 578 try: 579 self.processfunction(functiontext, paramtexts) 580 except MyException: 581# sys.stderr.write(myline + "\n") 582 pass 583 except: 584# sys.stderr.write(myline + "\n") 585 raise 586 587 self.flush() 588 589class ServerBindingSet (BindingSet): 590 def __init__(self, inputfile, fprefix): 591 BindingSet.__init__(self, inputfile, fprefix) 592 self.functions = [] 593 594 595 def processfunction(self, functiontext, paramtexts): 596 binding = ServerBinding(functiontext, paramtexts) 597 binding.process() 598 self.functions.append((binding.function.name, binding.dparams)) 599 600 def flush(self): 601 print("static PurpleDBusBinding bindings_DBUS[] = { ") 602 for function, params in self.functions: 603 print('{"%s", "%s", %s_DBUS},' % \ 604 (ctopascal(function), params, function)) 605 606 print("{NULL, NULL, NULL}") 607 print("};") 608 609 print("#define PURPLE_DBUS_REGISTER_BINDINGS(handle) purple_dbus_register_bindings(handle, bindings_DBUS)") 610 611class ClientBindingSet (BindingSet): 612 def __init__(self, inputfile, fprefix, headersonly): 613 BindingSet.__init__(self, inputfile, fprefix) 614 self.functions = [] 615 self.knowntypes = [] 616 self.headersonly = headersonly 617 618 def processfunction(self, functiontext, paramtexts): 619 binding = ClientBinding(functiontext, paramtexts, self.knowntypes, self.headersonly) 620 binding.process() 621 622 def flush(self): 623 pass 624 625# Main program 626 627options = {} 628 629for arg in sys.argv[1:]: 630 if arg[0:2] == "--": 631 mylist = arg[2:].split("=",1) 632 command = mylist[0] 633 if len(mylist) > 1: 634 options[command] = mylist[1] 635 else: 636 options[command] = None 637 638if "export-only" in options: 639 fprefix = "DBUS_EXPORT\s+" 640else: 641 fprefix = "" 642 643#sys.stderr.write("%s: Functions not exported:\n" % sys.argv[0]) 644 645if "client" in options: 646 bindings = ClientBindingSet(sys.stdin, fprefix, 647 "headers" in options) 648else: 649 bindings = ServerBindingSet(sys.stdin, fprefix) 650bindings.process() 651 652 653 654 655