skip_blocks as $key => $value) { if (@ereg($key, $line)) { $this->parser_skipping = true; } if (@ereg($value, $line)) { $this->parser_skipping = false; } } } return $this->parser_skipping; } function IsKeywordReserved($keyword) { $keyword = strtolower($keyword); if (in_array($keyword, $this->reserved_keywords)) return true; if (($this->current_class != null) && ($this->dump["master"][$this->current_class["name"]] ["field_names"] != null) && in_array($keyword, $this->dump["master"][$this->current_class["name"]] ["field_names"])) return true; } // Extracts the name from an Objective-C method definition function ExtractObjcMethodName ($method) { // cut out comments first $method = eregi_replace("(/\*.*\*/)", "", $method); $method = eregi_replace("//.*$", "", $method); $method = trim($method, " "); $params = explode(":", $method); $name = ""; if (count($params) > 1) { foreach ($params as $value) { $value = trim($value, " "); if (eregi("([a-zA-Z0-9_]+)[[:space:]]*$", $value, $captures)) $name .= $captures[1].":"; /* // paremeter with no label (or first parameter) if (eregi("\([a-zA-Z0-9_]+\)[[:space:]]*([a-zA-Z0-9]+)[[:space:]]*$", $value, $captures)) { if ($name != "") { $name .= ":"; } else { $name .= $captures[1].":"; } continue; } // parameter with label if (eregi("\([a-zA-Z0-9_]+\)[[:space:]]*[a-zA-Z0-9_]+[[:space:]]+([a-zA-Z0-9]+)$", $value, $captures)) $name .= $captures[1].":"; */ } } else { if (eregi("([a-zA-Z0-9_]+)[[:space:]]*(;)*$", $method, $captures)) $name = $captures[1]; } return $name; } function MaybeRenameMethod(&$name, $isclassmethod) { if ($isclassmethod) { foreach ($this->replace_class_methods as $org_name => $replace_name) { if ($name == $org_name) { $name = $replace_name; break; } } } else { foreach ($this->replace_instance_methods as $org_name => $replace_name) { if ($name == $org_name) { $name = $replace_name; break; } } } } // in case of a non-function pointer and non-pointer type, // split a C field expression (e.g. "long afield" or // "long int :32") into its type and field name. Necessary // to recognise name that "long int :32" means "a field // without a name whose type/alignment is "long int" and that is // bitpacked function ExtractCFieldSimpleTypeAndSingleName($param) { // print("Converting field $param\n"); // remove all "const" occurrences to simplify matching $clean_param = str_replace_word("const", "", $param); $got_type = false; // assume not a pointer type if (preg_match("!^\s*(unsigned|signed)\b!", $clean_param, $signedunsigned)) { $got_type = true; $clean_param = preg_replace("!^\s*(unsigned|signed)\b!", "", $clean_param); } if (preg_match("!^\s*(char|short|int|long\s+long\s+int|long\s+int|long\s+long|long)\b!", $clean_param, $inttype)) { $got_type = true; $clean_param = preg_replace("!^\s*(char|short|int|long\s+long\s+int|long\s+int|long\s+long|long)\b!", "", $clean_param); } if ($got_type) { // remove bitfield and array, are encoded later $result["name"] = trim(preg_replace("!(?::.*)?(?:\[[^]]*\])?!","",$clean_param)); $result["type"] = trim(preg_replace("!\s+!", " ", $signedunsigned[0] . " " . $inttype[0])); } else { // remove "struct" $clean_param = str_replace_word("struct", "", $clean_param); // has to be "type fieldname" preg_match("!^\s*(\w+)\b\s*(\w+)(?:\[[0-9]*\])?\s*!", $clean_param, $matches); $result["name"] = $matches[2]; // can be empty $result["type"] = $matches[1]; } // print("field conversion result: ".$result["name"].": ".$result["type"]."\n"); return $result; } function ExtractCParaNameAndType($param) { // print("Converting $param\n"); // remove all "const" occurrences to simplify matching $clean_param = str_replace_word("const", "", $param); // remove all spaces between multiple pointer modifiers $clean_param = trim(preg_replace("![*]\s+[*]!", "**", $clean_param)); // 1) varargs if ($clean_param == "...") { $result["name"] = ""; $result["pointermods"] = ""; $result["type"] = "..."; // 2) type is a function pointer (does not yet match arrays of function // pointers!) } elseif (preg_match($this->pregex_function_pointer_c_paratype, $clean_param, $matches)) { $result["name"] = $matches[3]; // not correct; simply assume no "pointer to function pointer" types are // used $result["pointermods"] = ""; $funcptrtype = $this->ParseFunctionDeclaration($matches[1], $matches[2], "", $matches[4], false, ""); $result["type"] = $this->AddCallback($matches[3], $funcptrtype); // 3) any other pointer type (including arrays) } elseif (preg_match("!^([^*]*)([*]+)\s*(\w+(?:\[[0-9]*\])?)?\s*$!", $clean_param, $matches)){ $result["name"] = $matches[3]; // can be empty $result["pointermods"] = $matches[2]; $result["type"] = trim($matches[1]); // 4) basic C type (including arrays) } else { // definitely not a pointer type $result["pointermods"] = ""; $got_type = false; if (preg_match("!^\s*(unsigned|signed)!", $clean_param, $signedunsigned)) { $got_type = true; $clean_param = preg_replace("!^\s*(unsigned|signed)!", "", $clean_param); } if (preg_match("!^\s*(char|short|int|long\s+long\s+int|long\s+int|long\s+long|long)!", $clean_param, $inttype)) { $got_type = true; $clean_param = preg_replace("!^\s*(char|short|int|long\s+long\s+int|long\s+int|long\s+long|long)!", "", $clean_param); } if ($got_type) { $result["name"] = trim($clean_param); $result["type"] = trim($signedunsigned[0] . " " . $inttype[0]); } else { // remove "struct" $clean_param = str_replace_word("struct", "", $clean_param); // has to be "type paraname", or just "type" preg_match("!^\s*(\w+)\b\s*(\w+(?:\[[0-9]*\])?)?\s*!", $clean_param, $matches); $result["name"] = $matches[2]; // can be empty $result["type"] = $matches[1]; } } // print("param conversion result: ".$result["name"].": ".$result["pointermods"].$result["type"]."\n"); return $result; } // Returns the parameter modifier string for a callback parameter type function GetCallbackParameterModifiers ($type, $name) { $modifiers = ""; $type = trim($type, "*"); // if the name starts with * it's a pointer // don't process framework classes since they're always pointers if (ereg("^[[:space:]]*\*", $name)) { if (!in_array($type, $this->cocoa_classes)) $modifiers = "var "; } // double ** are always var if (ereg("^[[:space:]]*\*\*", $name)) { $modifiers = "var "; } return $modifiers; } // Print string to output file function PrintOutput ($indent, $string) { for ($i=0; $i < $indent; $i++) { $indent_string .= " "; } if (($this->output) && (!$this->show)) fwrite($this->output, "$indent_string$string\n"); if ($this->show) print("$indent_string$string\n"); } // Returns a class hierarchy array function GetClassHierarchy ($class, &$hierarchy) { if (!$hierarchy) $hierarchy = array(); $hierarchy[] = $class["name"]; if ($class["super_class"]) { $hierarchy[] = $this->GetClassHierarchy($class["super_class"], $hierarchy); } return $class["name"]; } // Returns all protected keywords in a class hierarchy function GetProtectedKeywords ($in_class) { $this->GetClassHierarchy($in_class, $hierarchy); $keywords = array(); foreach ($hierarchy as $class) { if ($this->dump["master"][$class]["protected_keywords"]) { foreach ($this->dump["master"][$class]["protected_keywords"] as $keyword) $keywords[] = strtolower($keyword); } } return $keywords; } // Gets the preferred property name from attributes function GetPropertyName ($kind, $params, &$name) { foreach ($params as $value) { $pair = explode("=", $value); if ($pair[0] == $kind) { $name = $pair[1]; return true; break; } } } // Checks if a line has a property and returns the parts function LineHasProperty ($line, &$parts) { if (eregi($this->regex_objc_property_attributes, $line, $captures)) { $parts = $captures; //print_r($parts); return true; } elseif (eregi($this->regex_objc_property, $line, $captures)) { $parts = $captures; //print_r($parts); return true; } } // Returns header a category should be moved to function FindCategoryHeader ($category) { foreach ($this->dump as $name => $header) { if ((@array_key_exists($category, $header["classes"])) && ($category != "NSObject")) { return $name; } } } // Checks if $class (array) contains $method (array) function ClassContainsMethod ($class, $method) { if ($class["methods"]) { foreach ($class["methods"] as $key) { if ($key["name"] == $method["name"]) return true; } } } function GetAnonBitFieldName() { $name = "_anonbitfield_".$this->current_header["name_clean"].$this->current_header["anonbitfields"]; $this->current_header["anonbitfields"]++; return $name; } // create a variant normal record with a first anonymous field of type // first_bitpacked_type so that the compiler may correctly align it // the actual bitpacked record is embedded inside function BitPackedForceAlignment($first_bitpacked_type, $firstindent, $otherindents) { $result = $firstindent . "case byte of\n" . $otherindents ."0: (" . $this->GetAnonBitFieldName() . ": $first_bitpacked_type);\n" . $otherindents . "1: (data: bitpacked record"; return $result; } function EncodePointerModifiers($type, $pointertype) { if ($pointertype[0] == "*") $type = $this->ReplacePointerType($type); if ($pointertype[1] == "*") { if ($type == "Pointer") { $type = "PPointer"; } elseif ($type == "PChar") { $type = "PPChar"; } elseif (in_array($type, $this->cocoa_classes)) { $type = $type.$this->class_pointer_suffix; } else { // to match MacOSAll types $type = $type."Ptr"; } } return $type; } /** * DOCSETS UTILITIES */ function FindDocumentationForMethod ($class, $name) { if ($this->docset) { $doc = $this->docset[$class][$name]; if ($doc) return "{ $doc }"; } } function FindDocumentationForType ($name) { if ($this->docset) { foreach ($this->docset as $class) { foreach ($class as $type => $text) { if ($type == $name) return "{ $text }"; } } } } /** * ADDING LANGUAGE STRUCTURE UTILITIES */ // Adds a method structure to a class and performs checks for overloaded methods function AddMethodToClass (&$method, &$class) { // ignore methods if (in_array($method["name"], $this->ignore_methods)) return false; // add comment to the method $method["comment"] = $this->InsertCurrentComment(); if (@!in_array($method["name"], $class["declared_methods"])) { $class["all"][$method["name"]] = $method; $class["protected_keywords"][] = $method["name"]; $class["declared_methods"][] = $method["name"]; $this->dump["all_methods"][$class["name"]][] = $method["objc_method"]; if ($this->show_added_messages) print(" @ Added ".$method["name"]." to ".$class["name"]."\n"); $this->method_count ++; return true; } else { print(" ! ".$method["def"]." already exists in ".$class["name"]." defined as ".$class["all"][$method["name"]]["def"]."\n"); } } // Adds a typedef to the header and handles organization to prevent order conflicts function AddTypeDef (&$header, $typedef) { //$this->AppendCurrentMacro($header["types"]["typedef"]); $this->AppendCurrentComment($header["types"]["typedef"]); if ($this->comment_terminated) $header["types"]["typedef"][] = $this->InsertCurrentComment(); $header["types"]["typedef"][] = $typedef; } // adds a function callback type with name $name, and returns // the name of the callback type function AddCallback($name, $function_pointer) { if ($this->current_header) { $type = $this->current_header["name_clean"].ucwords($name); if (!@in_array($function_pointer, $this->current_header["types"]["callbacks"])) { $count = 0; while (@array_key_exists($type, $this->current_header["types"]["callbacks"])) { $count ++; $type = "$type$count"; } // append the new type to the the current class $this->current_header["types"]["callbacks"][$type] = $function_pointer; } else { // Use the name of the existing callback of matching type $type = array_search($function_pointer, $this->current_header["types"]["callbacks"]); } return $type; } return "invalid_callback_type_because_no_current_header"; } /** * OBJC FORMATTING UTILITIES */ // Performs additional formatting on Objective-c type i.e. (out NSObject **) function FormatObjcType ($type, &$modifiers) { $modifiers = ""; // toss out all const identifiers $type = istr_replace_word("const", "", $type); // replace inout paramaters $type = istr_replace_word("inout", "", $type); $type = istr_replace_word("out", "", $type); // Translate protocol which type conforms to (id ) $type = preg_replace("!id\s*<([^,>]*)>!", "$1Protocol", $type); // Remove other protocol types $type = trim(eregi_replace("<[^>]*>", "", $type)); // Replace types before cleaning $type = $this->ReplaceObjcType($type); // Remove array brackets (NSUInteger[])p $type = trim(eregi_replace("\[[0-9]*\]", "", $type)); // var params to non-object types (NSRange *) if (ereg("([a-zA-Z0-9_]+)[[:space:]]*[*]+$", $type, $captures)) { if (!in_array($captures[1], $this->cocoa_classes)) { $type = $this->ReplaceReferenceParameterType(trim($type,"* ")); //$modifiers = "var "; } } // Handle NS*** pointers (NSError **) if (ereg("(NS[a-zA-Z0-9_]+)[[:space:]]*\*\*$", $type, $captures)) { if (in_array($captures[1], $this->cocoa_classes)) { $type = trim($type,"* ")."$this->class_pointer_suffix"; } } // clean the type $type = trim($type, "* "); //print("$type\n"); return $type; } // Performs additional formatting on Objective-c parameter types function FormatObjcParams ($string, &$variable_arguments) { $params = explode(":", $string); $string = ""; $variable_arguments = false; // print_r($params); if (count($params) > 0) { foreach ($params as $value) { // parameter is varargs if (eregi("(.*),[[:space:]]*\.\.\.", $value)) { $string .= ":(id)$this->varargs_param_name"; $variable_arguments = true; //print("$value\n"); continue; } if (preg_match("!^[^(]*$!",$value)) { // parameter without type -> id $value = "(id)".$value; } // normal parameter if (ereg("\((.*)\)", $value, $captures)) { $new_value = $this->ReplaceObjcType($captures[1]); if ($new_value != $captures[1]) $value = ereg_replace("\((.*)\)", "($new_value)", $value); $string .= ":$value"; continue; } } } $string = ltrim($string, ":"); return $string; } /** * SYNTAX MAKING UTILITIES */ // Makes a paramater list string with options to modify function MakeParamList ($param_array, $use_handle, $cast_handle, $direct, $register_selector) { $params = ""; foreach ($param_array as $pair) { // register selector parameters if (($register_selector) && ($pair["type"] == "SEL")) { $params .= "sel_registerName(".$pair["name"]."), "; continue; } // use the object handle for NSObject descendants if ($use_handle) { if (in_array($pair["type"], $this->cocoa_classes)) { // cast the param to the original class type if ($cast_handle) { if ($direct == ACCESS_HANDLE_DIRECT) { $params .= $pair["type"]."(".$pair["name"].".Handle), "; } else { $params .= $pair["type"]."(GetHandle(".$pair["name"].")), "; } } else { if ($direct == ACCESS_HANDLE_DIRECT) { $params .= $pair["name"].".Handle, "; } else { $params .= "GetHandle(".$pair["name"]."), "; } } } else { if (($this->objects_are_wrappers) && ($pair["type"] == $this->objc_id)) { // id is always a wrapper if ($direct == ACCESS_HANDLE_DIRECT) { $params .= $pair["type"]."(".$pair["name"].".Handle), "; } else { $params .= $pair["type"]."(GetHandle(".$pair["name"].")), "; } } else { $params .= $pair["name"].", "; } } } else { // append without modification $params .= $pair["name"].", "; } } return trim($params, ", "); } // Makes a list of paramameter variables with NS*** class types cast to "id" or the original class function MakeObjcTypeParamList ($param_array, $objc_type) { $params = ""; foreach ($param_array as $pair) { if (in_array($pair["type"], $this->cocoa_classes)) { if ($objc_type) { $params .= "$this->objc_id(".$pair["name"]."), "; } else { $params .= $pair["type"]."(".$pair["name"]."), "; } } else { $params .= $pair["name"].", "; } } return trim($params, ", "); } // Makes a struct field into an inline array (or returns field un-changed) function MakeFieldInlineArray ($io_field, $line, $name, $type) { if (eregi("\[([^]]+)\];", $line, $array_size)) { if ($array_size[1] == "") $io_field = "$name: array[0..0] of $type; { dynamically expanding, 0 elements in C }"; else if ($array_size[1] == "0") $io_field = "$name: record end; { array of 0 elements in C, does not allocate space }"; else // array_size[1] may be a symbolic constant rather than a number, so don't calculate in php $io_field = "$name: array[0..($array_size[1])-1] of $type;"; } return $io_field; } // Makes a type bitpacked (or returns type un-changed) function MakeFieldBitPacked ($ioType, $field, &$bitpacked) { $bitpacked = false; if (preg_match("!:([0-9]+)\s*(?:__attribute__\(\([^)]*\)\))?\s*;\s*$!", $field, $bitpack)) { $length = (int)$bitpack[1]; if ($length > 1) { $ioType = "0..((1 shl $length)-1)"; } else { $ioType = "0..$length"; } $bitpacked = true; } return $ioType; } /** * REPLACEMENT UTILITIES */ // Replace type with pointer equivalent function ReplacePointerType ($type) { $found = false; // all classes are implicit pointer types if (in_array($type, $this->cocoa_classes)) return $type; // function pointer types are sometimes also implicit // pointers if (in_array($type, $this->implicit_function_pointer_types)) return $type; // PPointer = ^Pointer if ($type == "Pointer") return "PPointer"; if ($type == "PChar") return "PPChar"; // use preferred pointer type foreach ($this->pointer_types as $objc_type => $replace_type) { if ($objc_type == $type) { $found = true; $type = $replace_type; break; } } //$type = "Pointer {".$type."}"; // use generic pointer type if (!$found) $type = $type."Ptr"; return $type; } // Replace objc type with preferred type function ReplaceObjcType ($type) { foreach ($this->replace_types as $objc_type => $replace_type) { if ($objc_type == $type) { $type = $replace_type; break; } } return $type; } // Replace garbage collector hints function ReplaceGarbageCollectorHints ($string, &$io_hint) { $io_hint = false; foreach ($this->garbage_collector_hints as $hint) { $out_string = str_ireplace($hint, "", $string); if ($out_string != $string) { $io_hint = $hint; $string = $out_string; } } return $string; } // Replace remote message passing modifiers function ReplaceRemoteMessagingModifiers ($string, &$io_modifier) { $io_hint = false; foreach ($this->remote_messaging_modifiers as $modifier) { $out_string = preg_replace("!\b".$modifier."\b!", "", $string); if ($out_string != $string) { $io_modifier = $modifier; $string = $out_string; } } return trim($string); } // Replace type of reference parameter with pointer function ReplaceReferenceParameterType ($type) { $type = $this->ReplaceObjcType($type); return $this->ReplacePointerType($type); } // Replace a framework class with generic id and comment hint function ReplaceFrameworkClassWithID ($string) { foreach ($this->cocoa_classes as $class) { $string = istr_replace_word($class, $this->objc_id, $string); } return $string; } /** * MACRO UTILITIES */ // Insert macro blocks to replace c-style blocks function InsertMacroBlocks ($line, &$in_macro_block) { // only insert if we are in a block already. // NOTE: this does not handle nesting! if ($in_macro_block) { // macro else statment if (eregi("#else", $line)) { return "{\$else}"; } // macro endif statment if (eregi("#endif", $line)) { $in_macro_block = false; return "{\$endif}"; } } foreach ($this->macro_blocks as $key => $value) { if (eregi($key, $line, $captures)) { $in_macro_block = true; // replace the c-macro with a Pascal version if ($value == "*") { $captures[0] = trim($captures[0], "#"); return "{\$".$captures[0]."}"; } else { return "{".$value."}"; } } } } // Appends the current macro to a recipient and resets it's reference function AppendCurrentMacro (&$recipient) { if ($this->in_macro_block) { $this->in_macro_block = false; $recipient[] = $this->macro_block; //if (is_array($recipient)) $recipient[] = $this->macro_block; //if (is_string($recipient)) $recipient .= $this->macro_block; } } // Returns the current macro and resets it's reference function InsertCurrentMacro () { if ($this->in_macro_block) { $this->in_macro_block = false; return $this->macro_block; } else { return null; } } // Build the current macro block function BuildMacroBlocks ($line) { // only insert if we are in a block already. // NOTE: this does not handle nesting! if ($this->in_macro_block) { // macro else statment if (eregi("#else", $line)) { $this->macro_block = "{\$else}"; } // macro endif statment if (eregi("#endif", $line)) { $this->in_macro_block = false; $this->macro_block = "{\$endif}"; } } foreach ($this->macro_blocks as $key => $value) { if (eregi($key, $line, $captures)) { $this->in_macro_block = true; // replace the c-macro with a Pascal version if ($value == "*") { $captures[0] = trim($captures[0], "#"); $this->macro_block = "{\$".$captures[0]."}"; } else { $this->macro_block = "{".$value."}"; } } } } // Remove OS X versions macros from a line // NOTE: These should be re-inlcuded in Pascal function RemoveVersionMacros ($line, &$deprecatedmods) { if (preg_match($this->pregex_deprecated_macro,$line)) { $deprecatedmods = DeprecatedMacroToDirective($line); } else { $deprecatedmods = ""; } foreach ($this->version_macros as $macro) { $line = eregi_replace($macro, "", $line); } return $line; } /** * CONVERTING UTILITIES */ // Converts an Objective-c parameter string to Pascal function ConvertObjcParamsToPascal ($string, $protected_keywords) { $params = explode(":", $string); $list = array(); $list["pairs"] = array(); $param_list = array(); //print_r($params); //print("$string\n"); if (count($params) > 0) { foreach ($params as $value) { $value = trim($value); $valid = false; $modifiers = ""; $value = $this->ReplaceRemoteMessagingModifiers($value, $null); // function pointer (callback) if (preg_match($this->pregex_function_pointer_objc_paratype, $value, $captures)) { $name = $captures[5]; $function_pointer = $this->ParseFunctionDeclaration($captures[1], $captures[2], "", $captures[4], false, ""); $type = $this->AddCallback($name, $function_pointer); $valid = true; } elseif ((eregi("\(([a-zA-Z_]+).*\)([a-zA-Z_]+).*\.\.\.", $value, $captures)) || (eregi("(.*),[[:space:]]*\.\.\.", $value, $captures))) { // variable arguments $name = $captures[2]; $type = $captures[1]; $valid = true; } elseif (eregi("\((.*)\)[[:space:]]*([a-zA-Z_]+)", $value, $captures)) { // standard parameter $captures[1] = trim($captures[1]); $type = $this->FormatObjcType($captures[1], $modifiers); $name = $captures[2]; $valid = true; } if ($valid) { // protect reserved keywords if ($this->IsKeywordReserved($name)) $name .= "_"; if (!in_array($type, $this->reserved_types)) { if ($this->IsKeywordReserved($type)) $type .= "_"; } // can't have "boolean: boolean;" parameters if (preg_match("!\b$name\b!i",$type)) $name .= "_"; // print("para name: \"$name\"\n"); // print_r("protected: \"$protected_keywords\"\n"); while (@in_array(strtolower($name), $protected_keywords)) $name .= "_"; // case-insensitive in_array: // if( preg_match("/\b$name\b/i", join(" ", array_values($protected_keywords))) ) $name .= "_"; // if (@in_array($type, $protected_keywords)) $type .= "_"; // replace objc types $type = $this->ReplaceObjcType($type); // make sure we label duplicate params, which are allowed in Objective-C while (in_array($name, $param_list)) { $count ++; $name = "$name$count"; } // id is always a wrapper if (($this->objects_are_wrappers) && ($type == $this->objc_id)) { $name_list = "$type(GetHandle($name))"; } else { $name_list = $name; } // add modifiers to the name if there are any $name_with_modifiers = $modifiers.$name; // create pair array $pair["name"] = $name; $pair["type"] = $type; // append list $list["pairs"][] = $pair; $list["string_with_modifiers"] .= "$name_with_modifiers: $type; "; $list["string"] .= "$name: $type; "; $list["list"] .= "$name_list, "; $param_list[] = $name; } } } // clean up the strings $list["string"] = trim($list["string"], "; "); $list["string_with_modifiers"] = trim($list["string_with_modifiers"], "; "); $list["list"] = trim($list["list"], ", "); return $list; } // Converts an Objective-C method to Pascal format function ConvertObjcMethodToPascal ($class, $source, $parts, $protected_keywords, $has_params, $deprecatedmods) { //print("$source\n"); //print_r($parts); // replace "hinted" params comment with hinted type if ($this->replace_hinted_params) { // param string if (eregi("(/\*[[:space:]]*(.*)[[:space:]]*\*/)", $parts[4], $captures)) { // ??? change the parameter to the hinted type //$parts[4] = eregi_replace("(/\*.*\*/)", $captures[2], $parts[4]); //$parts[4] = trim($parts[4], " "); } // return type if (eregi("(/\*[[:space:]]*(.*)[[:space:]]*\*/)", $parts[2], $captures)) $parts[2] = $this->ReplaceRemoteMessagingModifiers($captures[2], $null); //print_r($parts); } else { // remove comments from params and return type $parts[4] = eregi_replace("(/\*.*\*/)", "", $parts[4]); $parts[4] = trim($parts[4]); $parts[2] = eregi_replace("(/\*.*\*/)", "", $parts[2]); $parts[2] = $this->ReplaceRemoteMessagingModifiers($parts[2], $null); } $return_type_clean = $parts[2]; $return_type_pointers = preg_replace("![^*]+!e", "", $return_type_clean); $return_type_clean = trim($return_type_clean,"* "); // perform preformatting before attempting to protect keywords $parts[2] = $this->FormatObjcType($parts[2], $modifiers); $parts[4] = $this->FormatObjcParams($parts[4], $variable_arguments); //print($parts[4]."\n"); if ($has_params) { $name = $this->ConvertObjcMethodName($source); // merge default protected keywords for the class/category if ($this->default_protected["*"]) $protected_keywords = array_merge($this->default_protected["*"], $protected_keywords); if ($this->default_protected[$class]) $protected_keywords = array_merge($this->default_protected[$class], $protected_keywords); $param_array = $this->ConvertObjcParamsToPascal($parts[4], $protected_keywords); $params = "(".$param_array["string"].")"; $params_with_modifiers = "(".$param_array["string_with_modifiers"].")"; } else { $params = ""; $params_with_modifiers = ""; // no parameters -> definitely no underscore normally, but there are some // conflicts... $name = $parts[3]; // clean it up if ($this->trailing_underscore) { if (in_array($source, $this->trailing_underscore_methods)) $name = $name . "_"; } $param_array = null; $variable_arguments = false; } // rename method if required $this->MaybeRenameMethod($name,$parts[1]=="+"); // protect method name from keywords if ($this->IsKeywordReserved($name)) $name .= "_"; // replace objc type $return_type = $this->ConvertReturnType($return_type_clean,$return_type_pointers); $virtual = ""; $class_prefix = ""; // determine the type based on return value if (ereg($this->regex_procedure_type, $return_type_clean.$return_type_pointers)) { $kind = "procedure"; } else { $kind = "function"; } // determine if this is a class method if ($parts[1] == "+") { $class_prefix = "class "; // These methods probably return the an allocated instance of the class, a typical convenience method. // ??? Ack! $class may be the category or protocol name //if ($return_type == $this->objc_id) $return_type = $class; } // Replace SEL with the string equivalent if ($this->register_selectors) { $params_with_modifiers = str_replace_word("SEL", $this->sel_string, $params_with_modifiers); } // detect blocks (not yet supported) $has_blocks = strpos("$return_type $params_with_modifiers","^"); // make method templates if ($kind != "function") { if ($variable_arguments) $modifier .= " varargs;"; $method = "$class_prefix$kind $name$params_with_modifiers;$modifier$virtual"; $method_template = "[KIND] [PREFIX]$name"."[PARAMS];$modifier"; } else { if ($variable_arguments) $return_type = "$return_type; varargs"; $method = $class_prefix."function $name$params_with_modifiers: $return_type;$modifier$virtual"; $method_template = "[KIND] [PREFIX]$name"."[PARAMS]: [RETURN];$modifier"; } $method_template_procedure = "procedure [PREFIX]$name"."[PARAMS];$modifier"; $method_template_function = "function [PREFIX]$name"."[PARAMS]: [RETURN];$modifier"; // build structure $struct["def"] = $method; $struct["template"] = $method_template; $struct["template_function"] = $method_template_function; $struct["template_procedure"] = $method_template_procedure; $struct["objc_method"] = $this->ExtractObjcMethodName($source); $struct["class_prefix"] = $class_prefix; if ($deprecatedmods != "") $struct["deprecated"] = $deprecatedmods.";"; //$struct["def_objc"] = eregi("(.*);", $source, $captures[1]); if ($return_type == "void") $return_type = ""; $struct["return"] = $return_type; if (in_array($return_type, $this->cocoa_classes)) $struct["returns_wrapper"] = true; $struct["param_string_clean"] = trim($params, "()"); $struct["param_string_clean_with_modifiers"] = trim($params_with_modifiers, "()"); $struct["param_string"] = $params; $struct["param_string_with_modifiers"] = $params_with_modifiers; $struct["param_array"] = $param_array["pairs"]; $struct["param_list"] = $param_array["list"]; $struct["class"] = $class; $struct["name"] = $name; $struct["kind"] = $kind; if ($has_blocks === false) $struct["blocks_disable_comment"] = ""; else $struct["blocks_disable_comment"] = "// "; if ($struct["param_array"] != null) $struct["has_params"] = true; // FPC bug work around if (strlen($name) > $this->maximum_method_length) { $struct["can_override"] = false; print(" # WARNING: method $name can't override because the name is too long\n"); $this->warning_count ++; } return $struct; } // Converts a C parameter string to Pascal function ConvertCParamsPascal ($string) { // print("params: $string\n"); if ((trim($string) == "void") || (trim($string) == "")) return ""; $params = explode(",", $string); $count = 0; $param_string = ""; foreach ($params as $param) { $name_type = $this->ExtractCParaNameAndType($param); $type = $name_type["type"]; $name = $name_type["name"]; $pointertype = $name_type["pointermods"]; // create name if none is specified if ($name == "") { $count ++; $name = "param$count"; } // remove const keywords $type = str_replace_word("const", "", $type); $type = $this->ReplaceObjcType($type); // Remove array brackets (NSUInteger[])p if (eregi("\[[0-9]*\]", $name)) { $orgtype = $this->EncodePointerModifiers($type,$pointertype); $pointertype .= "*"; $type = $this->EncodePointerModifiers($type,$pointertype)." {array of $orgtype}"; $name = eregi_replace("\[[0-9]*\]", "", $name); } else { $type = $this->EncodePointerModifiers($type,$pointertype); } $modifier = ""; if ($this->IsKeywordReserved($name)) $name .= "_"; // multiple parameters if ($type == "...") { $param_string .= "varargs: array of const"; break; } $param_string .= "$modifier$name: $type; "; } $param_string = trim($param_string, "; "); //print("$param_string\n"); return $param_string; } // Converts an Objective-c method name to Pascal function ConvertObjcMethodName ($method) { $params = explode(":", $method); $name = ""; $count = 0; if (count($params) > 1) { foreach ($params as $value) { if (eregi("([a-zA-Z0-9]+)$", $value, $captures)) $name .= $captures[1]."_"; } } else { if (eregi("([a-zA-Z0-9]+)(;)*$", $params[0], $captures)) $name .= $captures[1]."_"; } // clean it up if ($this->trailing_underscore) { if (!in_array($method, $this->trailing_underscore_methods)) $name = trim($name, "_"); } $name = $this->ReplaceObjcType($name); return $name; } // Convert a method return type to Pascal function ConvertReturnType ($type, $pointertype) { $type = $this->ReplaceGarbageCollectorHints($type, $null); $type = $this->ReplaceRemoteMessagingModifiers($type, $null); // format the return type to make sure it's clean $type = $this->FormatObjcType($type, $null_modifier); // translate type to Pascal $type = $this->ReplaceObjcType($type); // incorportate pointer modifiers $type = $this->EncodePointerModifiers($type,$pointertype); return $type; } /** * USER PATCHES */ function InsertPatches ($header) { $path = "$this->root/patches/".$header["name_clean"].".patch"; if ($handle = @fopen($path, "r")) { $text = file_get_contents($path); $this->PrintOutput(0, $text); fclose($handle); } } function HeaderContainsPatch ($header) { if ($handle = @fopen("$this->root/patches/".$header["name_clean"].".patch", "r")) { fclose($handle); return true; } } /** * COMMENTS */ // Trim a comment string function TrimComment ($comment, $preserve_line_endings) { // trim line breaks if (!$preserve_line_endings) $comment = trim($comment, "\n"); // remove remains of C comments $comment = eregi_replace("^[!*/ ]+", "", $comment); $comment = eregi_replace("[!*/ ]+$", "", $comment); // remove all Pascal comment characters to prevent nesting $comment = str_replace(array("{", "}"), "", $comment); //print("$comment\n"); return $comment; } // Builds comments from $line spaning multiple lines or at the end of lines // Returns a terminated comment string or a fragment in $fragment for futher processing function BuildComment ($line, $file) { // set the current comment header being parsed $this->comment_header = $file; // comment parsing is off, bail! if (!$this->parse_comments) return; // single-line comment at start of line if (eregi("^[[:space:]]*//(.*)", $line, $captures)) { //print("$captures[1]\n"); // ignore comment foreach ($this->ignore_comments as $pattern) { if (eregi($pattern, $captures[1])) return; } $this->comment_terminated = $this->TrimComment($captures[1], false); return; } elseif (eregi("[[:space:]]+//(.*)", $line, $captures)) { // single-line comments at end of line //print("$captures[1]\n"); // ignore comment foreach ($this->ignore_comments as $pattern) { if (eregi($pattern, $captures[1])) return; } $this->comment_eol = $captures[1]; return; } // multi-line comments terminated if (eregi("/\*(.*)\*/", $line, $captures)) { //print("$captures[1]\n"); // ignore comment foreach ($this->ignore_comments as $pattern) { if (eregi($pattern, $captures[1])) return; } $this->comment_terminated = $this->TrimComment($captures[1], false); return; } // terminate comment fragment if ($this->comment_fragment_open) { if (eregi("(.*)\*/", $line, $captures)) { // append fragment $comment = $this->TrimComment($captures[1], false); if ($comment) $this->comment_fragment .= $comment; // closed comment block if (!$captures[1]) { $this->comment_block_closed = true; } // set terminated comment with fragment $this->comment_terminated = $this->comment_fragment; // add extra line break for comment blocks if (($this->comment_block_open) && ($this->comment_block_closed)) { // ??? the printing will cut this out if we add line endings! $this->comment_terminated = "$this->comment_fragment"; } $this->comment_fragment = null; $this->comment_fragment_open = false; $this->comment_block_open = false; $this->comment_block_closed = false; return; } else { // build the fragment $comment = $this->TrimComment($line, true); // ignore comment and stop building fragment foreach ($this->ignore_comments as $pattern) { if (eregi($pattern, $comment)) { $this->comment_fragment = null; $this->comment_fragment_open = false; $this->comment_block_open = false; $this->comment_block_closed = false; return; } } if (($this->comment_fragment_previous != $line) && ($comment)) $this->comment_fragment .= $comment."\n"; $this->comment_fragment_previous = $line; return; } } // start comment fragment if (eregi("/\*(.*)", $line, $captures)) { $this->comment_terminated = null; // ignore comment foreach ($this->ignore_comments as $pattern) { if (eregi($pattern, $captures[1])) return; } $this->comment_fragment_open = true; $this->comment_fragment = ""; $this->comment_block_open = true; $this->comment_block_closed = false; // prevent against empty comments if ((!$captures[1]) || ($captures[1] == "\n")) { $this->comment_block_open = true; return; } // append the line if valid $comment = $this->TrimComment($captures[1], false); if ($comment) $this->comment_fragment .= $comment; return; } } // Resets current comment references function ResetComment () { $this->comment_fragment = null; $this->comment_eol = null; $this->comment_terminated = null; $this->comment_block_open = false; $this->comment_block_closed = false; } // Checks/sets if a comment is a duplicate in the file function CheckDuplicateComment ($comment) { if ((@!in_array($comment, $this->dump[$this->comment_header]["comments"])) && ($comment)) { $this->dump[$this->comment_header]["comments"][] = $comment; return true; } else { return false; } } // Appends the eol comment to the output and clears the reference function AppendEOLComment () { if ($this->comment_eol) { $comment = " // ".$this->TrimComment($this->comment_eol, false); $this->comment_eol = ""; return $comment; } else { return null; } } // Inserts the recently terminated comment to the output on a new line and clears the reference function InsertCurrentComment () { if (($this->comment_terminated) && ($this->CheckDuplicateComment($this->comment_terminated))) { if ($this->comment_terminated != $this->comment_previous) $comment = "$this->comment_break{".$this->comment_padding_left.$this->TrimComment($this->comment_terminated, false).$this->comment_padding_right."}"; $this->comment_previous = $this->comment_terminated; $this->comment_terminated = ""; return $comment; } else { return null; } } // Appends the recently terminated comment to a recipient and clears the reference function AppendCurrentComment (&$recipient) { if (($this->comment_terminated) && ($this->CheckDuplicateComment($this->comment_terminated)) && (gettype($recipient) == "array")) { if ($this->comment_terminated != $this->comment_previous) $comment = "$this->comment_break{".$this->comment_padding_left.$this->TrimComment($this->comment_terminated, false).$this->comment_padding_right."}"; $recipient[] = $comment; //if (is_array($recipient)) $recipient[] = $comment; //if (is_string($recipient)) $recipient .= $comment; $this->comment_previous = $this->comment_terminated; $this->comment_terminated = ""; } } // Removes all comments from a line function RemoveComments ($line) { // remove single-line comments $line = eregi_replace("[[:space:]]+//(.*)", "", $line); // remove multi-line comments /* ... */ $line = eregi_replace("/\*.*\*/", "", $line); return $line; } /** * PRINTING METHODS */ function PrintClass ($class) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ ".$class["name"]." }"); if ($class["comment"]) $this->PrintOutput(0, $class["comment"]); //print_r($class["methods"]); // print super class or protocol which the class conforms to if ($class["adopts"]) { if ($class["super"]) { $this->PrintOutput(1, $class["name"]." = objcclass external (".$class["super"].", ".$class["adopts"].")"); } else { $this->PrintOutput(1, $class["name"]." = objcclass external (".$class["adopts"].")"); } } elseif ($class["super"]) { $this->PrintOutput(1, $class["name"]." = objcclass external (".$class["super"].")"); } else { $this->PrintOutput(1, $class["name"]." = objcclass external"); } // print instance variables if ($class["ivars"]) { $this->PrintOutput(1, "private"); foreach ($class["ivars"] as $ivar) { $this->PrintOutput(2, $ivar); } } // print methods (public) $this->PrintOutput(2, ""); $this->PrintOutput(1, "public"); // print class-level methods if ($class["methods"]) { foreach ($class["methods"] as $method) { if ($method["comment"]) $this->PrintOutput(2, $method["comment"]); if ($method["documentation"]) $this->PrintOutput(2, $method["documentation"]); $this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]." message '".$method["objc_method"]."';".$method["deprecated"]); } } // print adopted protocol methods if (count($class["protocols"]) > 0) { $this->PrintOutput(0, ""); $this->PrintOutput(2, "{ Adopted Protocols }"); //print_r($this->dump["protocols"]); foreach ($class["protocols"] as $name) { if ($this->dump["protocols"][$name]) { foreach ($this->dump["protocols"][$name] as $method) { if (!$this->ClassContainsMethod($class, $method)) $this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]); } } } } $this->PrintOutput(1, "end;"); } function PrintCategory ($class, $category) { // declare real category if external if ($category["external"]) { $new_name = " name '".$category["external_name"]."'"; } $category_name = $category["name"].$this->category_suffix; $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ $category_name }"); if ($category["comment"]) $this->PrintOutput(0, $category["comment"]); // print super class or protocol which the class conforms to $this->PrintOutput(1, "$category_name = objccategory external$new_name (".$category["super"].")"); // print methods if ($category["methods"]) { foreach ($category["methods"] as $method) { if ($method["comment"]) $this->PrintOutput(2, $method["comment"]); $this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]." message '".$method["objc_method"]."';".$method["deprecated"]); } } $this->PrintOutput(1, "end;"); } function PrintHeader ($header) { global $version; //print_r($header); //print_r($this->dump["categories"]); // open the output file if we not printing to terminal if (!$this->show) { if ($this->merge_headers) { $this->output = fopen($header["path_merge"], "w+"); } else { $this->output = fopen($header["path"], "w+"); } } $this->PrintOutput(0, "{ Parsed from ".ucfirst($header["framework"]).".framework ".$header["name"]." }"); //$date = @date("D M j G:i:s T Y"); //$this->PrintOutput(0, "{ Version: $version - $date }"); //$this->PrintOutput(0, ""); $macro = strtoupper(substr($header["name"], 0, (strripos($header["name"], ".")))); $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$ifdef TYPES}"); $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_T}"); $this->PrintOutput(0, "{\$define $macro"."_PAS_T}"); $this->PrintTypes($header, false); $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$endif}"); $this->PrintOutput(0, "{\$endif}"); $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$ifdef RECORDS}"); $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_R}"); $this->PrintOutput(0, "{\$define $macro"."_PAS_R}"); // Records from types $this->PrintRecords($header); $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$endif}"); $this->PrintOutput(0, "{\$endif}"); $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$ifdef FUNCTIONS}"); $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_F}"); $this->PrintOutput(0, "{\$define $macro"."_PAS_F}"); $this->PrintFunctions($header); $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$endif}"); $this->PrintOutput(0, "{\$endif}"); $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$ifdef EXTERNAL_SYMBOLS}"); $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_S}"); $this->PrintOutput(0, "{\$define $macro"."_PAS_S}"); $this->PrintExternalSymbols($header); $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$endif}"); $this->PrintOutput(0, "{\$endif}"); // insert user patches if ($this->HeaderContainsPatch($header)) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$ifdef USER_PATCHES}"); //$this->PrintOutput(0, "{\$ifndef $macro"."_PAS_PATCH}"); //$this->PrintOutput(0, "{\$define $macro"."_PAS_PATCH}"); $this->InsertPatches($header); $this->PrintOutput(0, ""); //$this->PrintOutput(0, "{\$endif}"); $this->PrintOutput(0, "{\$endif}"); } // print class/protocol forward declarations if (($header["classes"]) || ($header["protocols"])) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$ifdef FORWARD}"); if ($header["protocols"]) { foreach ($header["protocols"] as $protocol) $this->PrintOutput(1, $protocol["name"]."$this->protocol_suffix = objcprotocol;"); } if ($header["classes"]) { foreach ($header["classes"] as $class) { if ($class["name"]) { $this->PrintOutput(1, $class["name"]." = objcclass;"); $this->PrintOutput(1, $class["name"].$this->class_pointer_suffix." = ^".$class["name"].";"); // for consistency also offer Ptr-name variant $this->PrintOutput(1, $class["name"]."Ptr = ".$class["name"].$this->class_pointer_suffix.";"); } } } $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$endif}"); } // print classes if ($header["classes"]) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$ifdef CLASSES}"); $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_C}"); $this->PrintOutput(0, "{\$define $macro"."_PAS_C}"); foreach ($header["classes"] as $class) { if ($class["name"]) $this->PrintClass($class); } if (count($header["categories"]) > 0) { foreach ($header["categories"] as $category) { $this->PrintCategory($class, $category); } } $this->PrintOutput(0, ""); $this->PrintOutput(0, "{\$endif}"); $this->PrintOutput(0, "{\$endif}"); } // print protocols if ($header["protocols"]) { $this->PrintOutput(0, "{\$ifdef PROTOCOLS}"); $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_P}"); $this->PrintOutput(0, "{\$define $macro"."_PAS_P}"); foreach ($header["protocols"] as $protocol) { $this->PrintOutput(1, ""); $this->PrintOutput(0, "{ ".$protocol["name"]." Protocol }"); if ($protocol["comment"]) $this->PrintOutput(0, $protocol["comment"]); $this->PrintOutput(1, $protocol["name"]."$this->protocol_suffix = objcprotocol external name '".$protocol["name"]."'"); // print methods if ($protocol["methods"]) { $section=""; foreach ($protocol["methods"] as $name => $method) { // print the required/optional section if ($method["section"] != $section) { $section = $method["section"]; $this->PrintOutput(1, $section); } if ($method["comment"]) $this->PrintOutput(2, $method["comment"]); $this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]." message '".$method["objc_method"]."';".$method["deprecated"]); } } $this->PrintOutput(1, "end;"); } $this->PrintOutput(0, "{\$endif}"); $this->PrintOutput(0, "{\$endif}"); } } // Prints all externally defined symbols function PrintExternalSymbols ($header) { if (!$this->dump[$header["name"]]["types"]) return; foreach ($this->dump[$header["name"]]["types"] as $key => $type_array) { // External string constants if ($key == "string_constant") { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ External string constants }"); $this->PrintOutput(0, "var"); foreach ($type_array as $type) $this->PrintOutput(1, $type); } if ($key == "external_symbol") { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ External symbols }"); $this->PrintOutput(0, "var"); foreach ($type_array as $type) $this->PrintOutput(1, $type); } } } // Prints all types in the header function PrintTypes ($header) { if (!$this->dump[$header["name"]]["types"]) return; foreach ($this->dump[$header["name"]]["types"] as $key => $type_array) { $section_printed = false; // External defines if ($key == "defines") { foreach ($type_array as $type) { if (!$section_printed) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ Defines }"); $this->PrintOutput(0, "const"); $section_printed = true; } $this->PrintOutput(1, $type); } } // Enumerations if ($key == "enums") { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ Constants }"); foreach ($type_array as $block) { $section_printed = false; foreach ($block as $type) { if (!$section_printed) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "const"); $section_printed = true; } $this->PrintOutput(1, $type); } } } // Typedefs if (($key == "typedef") || ($key == "named_enums")) { foreach ($type_array as $type) { if (!$section_printed) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ Types }"); $this->PrintOutput(0, "type"); $section_printed = true; } $this->PrintOutput(1, $type); } } // CallBacks if ($key == "callbacks") { foreach ($type_array as $name => $type) { if (!$section_printed) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ Callbacks }"); $this->PrintOutput(0, "type"); $section_printed = true; } $this->PrintOutput(1, "$name = $type"); } } } } // Prints all records in the header function PrintRecords ($header) { if (!$this->dump[$header["name"]]["types"]) return; foreach ($this->dump[$header["name"]]["types"] as $key => $type_array) { // Structures if ($key == "structs") { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ Records }"); foreach ($type_array as $type) { $this->PrintOutput(0, "type"); $this->PrintOutput(1, $type); } } } } // Prints all external functions in the header function PrintFunctions ($header) { if (!$this->dump[$header["name"]]["types"]) return; foreach ($this->dump[$header["name"]]["types"] as $key => $type_array) { if ($key == "functions") { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ Functions }"); foreach ($type_array as $type) $this->PrintOutput(0, $type); } } } // Prints all classes from the header in reference format (not for compiling) function PrintHeaderReference ($header, $path) { $this->output = fopen($path, "w+"); //$this->PrintOutput(0, "{ ".ucfirst($header["framework"]).".framework ".$header["name"]." }"); $this->PrintOutput(0, "unit ".$header["name_clean"].";"); $this->PrintOutput(0, "interface"); $this->PrintOutput(0, "uses"); $this->PrintOutput(1, "ctypes, objc, MacOSAll, AnonClassDefinitions;"); if ($header["classes"]) { foreach ($header["classes"] as $class) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "type"); $this->PrintOutput(1, $class["name"]."Ref = ".$this->objc_id.";"); $this->PrintOutput(1, $class["name"]."Ptr = Pointer;"); } } // types $this->PrintTypes($header); $this->PrintRecords($header); $this->PrintFunctions($header); $this->PrintExternalSymbols($header); if ($header["classes"]) { foreach ($header["classes"] as $class) { if (in_array($class["name"], $this->cocoa_classes)) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "type"); $this->PrintOutput(1, $class["name"]." = object(".$class["super"].")"); // print class-level methods if (count($class["methods"]) > 0) { $this->PrintOutput(0, ""); foreach ($class["methods"] as $method) { $this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]); } } // print category-level methods if (count($class["categories"]) > 0) { foreach ($class["categories"] as $name => $category) { $this->PrintOutput(0, ""); $this->PrintOutput(2, "{ Category: $name }"); if ($category["methods"]) { foreach ($category["methods"] as $method) { $this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]); } } } } $this->PrintOutput(1, "end;"); } } } // print procedural protocols if ($header["protocols"]) { foreach ($header["protocols"] as $protocol) { if ($protocol["methods"]) { $this->PrintOutput(0, ""); $this->PrintOutput(0, "{ Protocol: ".$protocol["name"]." }"); foreach ($protocol["methods"] as $name => $method) { if ($method["kind"] != "constructor") { $this->PrintProtocolDeclaration($protocol, $method); } } } } } $this->PrintOutput(0, ""); $this->PrintOutput(0, "implementation"); $this->PrintOutput(0, "end."); } // Merges two headers by difference function MergeHeader ($header) { /* diff QLPreviewPanel_ORIGINAL.inc QLPreviewPanel_UPDATED.inc > diff.patch patch QLPreviewPanel_ORIGINAL.inc -i diff.patch -o output.txt patch QLPreviewPanel.inc -i header.patch -o QLPreviewPanel.inc.merged also add -section="types,classes" which only prints into the sections and copies text from the previous version if not specified */ // set the paths to use $path = $header["path"]; $patch = "$this->root$this->out/$this->framework/header.patch"; $merged = $header["path_merge"]; // create a patch using diff exec("/usr/bin/diff \"$path\" \"$merged\" > \"$patch\""); // parse the diff file by mode $lines = file($patch); $content = ""; $section_lines = null; $section_part = null; $count = 0; $section_id = 0; //print_r($lines); if ($lines) { foreach ($lines as $line) { $count++; // remove line endings to aid regex $line = trim($line, "\n"); // parse section if (($section_lines) && (!eregi("^[0-9]+", $line))) { // append line to section $section_lines[] = "$line\n"; // the section id is adding if (eregi("^>", $line)) $section_part[$section_id] = true; if (eregi("^<", $line)) $section_part[$section_id] = 0; // section is changing type if ($line == "---") { $section_id++; continue; } } // line is a new section or eof if ((eregi("^[0-9]+", $line)) || ($count == count($lines))) { // append the section to the content // we only accept sections where the first part (before ---) contains additions ">" //print_r($section_part); if (($section_part[0]) && (!$section_part[1])) $content .= implode($section_lines); // init the new section $section_lines = array(); $section_lines[] = "$line\n"; $section_id = 0; $section_part = array(null, null); continue; } } //print($content); // write the parsed patch back to the file if ($content) { file_put_contents($patch, $content); } else { $content = null; } } // patch the header to the merged file if ($content) { exec("/usr/bin/patch \"$path\" -i \"$patch\" -o \"$merged\" "); // swap the contents of the merged file with the original file_put_contents($path, file_get_contents($merged)); } // delete the temporary files unlink($patch); unlink($merged); } function PrintGlobalClassInfo($all_classes, $defined_classes, $anon_classes) { // add all classes as anonymous external classes to a separate unit. // They will be overridden by the actual definitions in the translated // headers part of the main unit, but this way they can appear as record // field types and as callback parameters // open the output file if we not printing to terminal if (!$this->show) { $this->output = fopen("$this->root$this->out/AnonClassDefinitions".ucfirst($this->framework).".pas", "w+"); } $this->PrintOutput(0, "{ Parsed from ".ucfirst($this->framework)." }"); $date = @date("D M j G:i:s T Y"); $this->PrintOutput(0, ""); // allows parameter names conflicting with field names $this->PrintOutput(0, "{\$mode delphi}"); $this->PrintOutput(0, "{\$modeswitch objectivec1}"); // enables "external" after the semi-colon $this->PrintOutput(0, "{\$modeswitch cvar}"); $this->PrintOutput(0, ""); $this->PrintOutPut(0,"unit AnonClassDefinitions".ucfirst($this->framework).";"); $this->PrintOutput(0, ""); $this->PrintOutput(0, "interface"); $this->PrintOutput(0, ""); $this->PrintOutput(0, "type"); foreach ($all_classes as $class) { $this->PrintOutput(1, $class." = objcclass external;"); } $this->PrintOutput(0, ""); $this->PrintOutput(0, "implementation"); $this->PrintOutput(0, ""); $this->PrintOutput(0, "end."); // Now all anonymous external classes that have no real definition to an // include file that is added to the main unit. This way it is possible // to declare variables of these types in user programs without having to // include the unit above will all anonymous classes (should not be used) // open the output file if we not printing to terminal if (!$this->show) { $this->output = fopen("$this->root$this->out/$this->framework/AnonIncludeClassDefinitions".ucfirst($this->framework).".inc", "w+"); } $this->PrintOutput(0, "{ Parsed from ".ucfirst($this->framework)." }"); $date = @date("D M j G:i:s T Y"); // add all classes as anonymous external classes. They will be overridden // by the actual definitions in the translated headers, but this way they // can appear as record field types and as callback parameters $first = true; foreach ($anon_classes as $class) { if (!in_array($class,$defined_classes)) { if ($first) { $this->PrintOutput(0, "type"); $first = false; } $this->PrintOutput(1, $class." = objcclass external;"); } } } // Prints all headers parsed function PrintAllHeaders ($output_path, $ignore_output, $only_files, $print_header_references) { //print("• Printing ".count($this->dump)." headers...\n"); foreach ($this->dump as $file => $header) { // the framework is set to not print, ignore the header if (!$this->frameworks[$header["framework"]]["print"]) continue; if (eregi("^[a-zA-Z0-9]+\.h", $file)) { // ignore these files if (@in_array($header["path_partial"], $ignore_output)) continue; // only parse these files if ((@count($only_files) > 0) && (@!in_array($header["name"], $only_files))) continue; $name_clean = substr($file, 0, (strripos($file, "."))); // assign output path if ($output_path != "") { $header["path"] = $output_path."/".$name_clean.".inc"; } // print the header $this->PrintHeader($header); // merge the headers if (($this->merge_headers) && (!$this->show)) { $this->MergeHeader($header); } if ($print_header_references) $this->PrintHeaderReference($header, $this->root.$this->out."/reference/".$name_clean.".pas"); if (!$this->show) print("* Printed $name_clean.h to ".$header["path"]."\n"); } } // print global stuff (for Obj-P: a unit containing all classes as // anonymous external classes, so they can be used before they are // declared) $this->PrintGlobalClassInfo($this->cocoa_classes, $this->defined_cocoa_classes, $this->anon_cocoa_classes); } /** * PRE-PARSING METHODS */ // Parse all "pre-defined" category methods in a header function PreparseCategoryMethods ($file) { $contents = file_get_contents($file); $file_name = substr($file, (strripos($file, "/")) + 1, strlen($file)); $lines = explode("\n", $contents); foreach ($lines as $line) { // skip blocks if ($this->SkipBlock($line)) continue; // parse category if ($got_category) { // build method fragment if ($method_fragment) $method_fragment .= " ".trim($line, " "); // found method fragment termination if (($method_fragment) && (preg_match($this->pregex_objc_method_terminate, $line))) { $line = $method_fragment; $method_fragment = null; } // found method $method = null; if (preg_match($this->pregex_objc_method_params, $line, $captures)) { $method = $this->ConvertObjcMethodToPascal($current_category, $line, $captures, array(), true, ""); } elseif (preg_match($this->pregex_objc_method_no_params, $line, $captures)) { $method = $this->ConvertObjcMethodToPascal($current_category, $line, $captures, array(), false, ""); } elseif (preg_match($this->pregex_objc_method_partial, $line, $captures)) { $method_fragment = $line; } // append to classes if (($method) && ($current_class)) { $this->dump[$category_owner]["category_methods"][] = $method["name"]; //print($method["name"]."\n"); } // found the end if (ereg("^@end", $line)) $got_category = false; } // got category if (eregi($this->regex_objc_category, $line, $captures)) { $category_owner = $this->FindCategoryHeader($captures[1]); if ($category_owner) { $got_category = true; $current_category = $captures[2]; $current_class = $captures[1]; } else { $current_class = null; } } } return $this->dump[$category_owner]["category_methods"]; } // Preparses a class for protected keywords function PreparseClass ($lines, $line_count) { $protected_keywords = array(); for ($i=$line_count; $i < count($lines); $i++) { $line = $lines[$i - 1]; // skip blocks if ($this->SkipBlock($line)) continue; // build method fragment if ($method_fragment) $method_fragment .= " ".trim($line, " "); // found method fragment termination if (($method_fragment) && (preg_match($this->pregex_objc_method_terminate, $line))) { $line = $method_fragment; $method_fragment = null; } // found method if (preg_match($this->pregex_objc_method_params, $line, $captures)) { $method = $this->ConvertObjcMethodToPascal($current, $line, $captures, $protected_keywords, true, ""); $this->current_class["protected_keywords"][] = strtolower($method["name"]); } elseif (preg_match($this->pregex_objc_method_no_params, $line, $captures)) { $method = $this->ConvertObjcMethodToPascal($current, $line, $captures, $protected_keywords, false, ""); $this->current_class["protected_keywords"][] = strtolower($method["name"]); } elseif (preg_match($this->pregex_objc_method_partial, $line, $captures)) { $method_fragment = $line; } // class ended if (ereg("^@end", $line)) return $protected_keywords; } } /** * PARSING METHODS */ function ParseNormalField($line, $protected_keywords, &$field_bitpacked, &$bitpacked_real_type, &$this_class_field_names) { if (preg_match("!^\s*([^-{}*@+,<;:]*struct[^-*@+,<;:]+|[^-{}*@+,<;:]+)\b\s*(?:<([^>]*)>)?\s*([*]*)\s*(\w+)((?:\s*,\s*[^:;[]+)*)?\s*(:[0-9]+)?\s*(\[.*\])?\s*(?:__attribute__\(\(([^)]*)\)\))?\s*;!", $line, $captures)) { // regular field // captures[1]: type name (may be multiple words, and may not be complete // in case of an anonymous bitfield without a name) // Curly braces are only allowed in case of structs // captures[2]: in case of id, the protocol list, empty // otherwise // captures[3]: all pointer modifiers (*, **, ...), but only "*" and "**" // currently handled // captures[4]: the field name (may be last part of type in case of // anonymous bit field, e.g. "unsigned long : 32") // captures[5]: in case of multiple fields, the other field names (note // that things will go wrong if some of the fields in the // list are arrays/bitfields, that is only supported for // a single field) // captures[6]: bitfield specification if any, empty otherwise // captures[7]: array specification if any, empty otherwise // captures[8]: attributes if any, empty otherwise if ($captures[3] != "") { // pointer type -> ok (cannot be wrongly interpreted) $type = trim($captures[1]); $pointertype = $captures[3]; $name = trim($captures[4]); // print("pointer field: $name: $pointertype $type\n"); } else { // make sure we properly interpret stuff like "unsigned int :32" // print("regular: type = \"$captures[1]\", name = $captures[3]\n"); $pair = $this->ExtractCFieldSimpleTypeAndSingleName($captures[1]." ".$captures[4]); $type = $pair["type"]; $name = $pair["name"]; if ($name == "") $name = $this->GetAnonBitFieldName(); // print("regular field: \"$name\": $type\n"); } // print("field \"$name\": \"$type\", attr: $captures[8]\n"); // if we have id and there's only one protocol in the // in the list, we can replace id with the protocol if (($type == "id") && ($captures[2] != "") && !strstr(",", $captures[2])) { // print("id: $type -> $captures[2]Protocol\n"); $type = $captures[2]."Protocol"; } // in case of "type field1, field2, field3", ", field2, field3" gets // stored in othernames $othernames = $captures[5]; $field_prefix = ""; // Multiple Objective-C fields cannot have the same name, but they // are case-insensitive and some only differ in case (only "reserved" // fields until now, but that can change) while (in_array(strtolower("$field_prefix$name"),$protected_keywords)) $field_prefix.="_"; if ($this_class_field_names != null) { while (in_array(strtolower("$field_prefix$name"),$this_class_field_names)) $field_prefix.="_"; } if ($this->IsKeywordReserved($field_prefix.$name)) $field_prefix .= "_"; // protect the name of these fields, in case there are methods with // the same name $this_class_field_names[] = strtolower($field_prefix.$name); if ($othernames != "") { $other_lower_case_names = preg_split("/\s*,\s*/",$othernames,-1,PREG_SPLIT_NO_EMPTY); array_walk($other_lower_case_names,strtolowerref); // should actually also check these for conflicts and add underscores if necessary $this_class_field_names = array_merge ($this_class_field_names, $other_lower_case_names); } /* // we should also add prefixes to the other names if required, but my // php-fu is too weak to replace the elements in original string // efficiently, and this problem does not occur in the supported headers foreach (explode($othernames, ",") as $othername) { while (in_array(strtolower("$field_prefix$othername"),$protected_keywords)) $field_prefix.="_"; if ($this->IsKeywordReserved($field_prefix.$othernamename)) $field_prefix .= "_"; } */ // remove "struct" from the type $type = preg_replace("!(\b)struct\b!","\1",$type); // clean/convert type $type = $this->ReplaceObjcType($type); $bitpacked_real_type = $type; $type = $this->MakeFieldBitPacked($type, $line, $field_bitpacked); // add pointer modifiers $type = $this->EncodePointerModifiers($type,$pointertype); $field = "$field_prefix$name$othernames: $type"; if ($captures[8] && strstr("deprecated",$captures[8])) $field .= " deprecated"; $field .= ";"; $field = $this->MakeFieldInlineArray($field, $line, $name, $type); $field = eregi_replace("<.*>", "", $field); return $field; } return ""; } function ParseInstanceVariables ($line, &$struct, $protected_keywords, &$this_class_field_names) { $field = null; $field_bitpacked = false; // print("$line\n"); // insert macros if ($macro = $this->InsertMacroBlocks($line, $this->inside_macro_block)) { if ($struct["valid"]) { $struct["fields"][] = $macro; return null; } else { return $macro; } } // got inline struct, probably a reference to a private struct if (eregi("[[:space:]]*struct[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+\*([a-zA-Z0-9_]+)", $line, $captures)) { if ($struct["valid"]) { // ??? These don't compile as inline records which I thought they did... //$struct["fields"][] = "$captures[1] = record end;"; //$struct["fields"][] = "$captures[1]Pointer = ^$captures[1];"; $struct["fields"][] = "$captures[2]: Pointer;"; $struct["ignore"][] = "$captures[2]"; return null; } else { //$fields = array(); //$fields[] = "$captures[1] = record end;"; //$fields[] = "$captures[1]Ptr = ^$captures[1];"; $field = "_$captures[2]: Pointer;"; return $field; } } // got struct if (eregi("^[[:space:]]*struct.*{", $line)) { $struct["valid"] = true; $struct["ispsuedostruct"] = false; $struct["fieldnames"] = array(); return null; } // create an anonymous struct in case we have bitpacked fields without a // surrounding struct if (!$struct["valid"] && preg_match("!.*:[0-9]+\s*;!", $line)) { $struct["valid"] = true; $struct["ispsuedostruct"] = true; $struct["fieldnames"] = array(); } // end of bunch of bitfields -> go to normal mode again if ($struct["ispsuedostruct"] && !preg_match("!.*:[0-9]+\s*;!", $line)) { $struct["name"] = "_anoninternstruct_".$this->current_header["name_clean"].$this->current_header["anoninternstrucs"]; $this->current_header["anoninternstrucs"]++; $struct["isfinished"] = true; // make sure the current field isn't added anymore $struct["valid"] = false; } // end of struct if (eregi("^[[:space:]]*}[[:space:]]*([a-zA-Z_0-9]+);", $line, $captures)) { $struct["name"] = "_".trim($captures[1], " "); //print_r($struct); $struct["isfinished"] = true; return "struct"; } // set field prefix to protect scope if (!$struct["valid"]) $field_prefix = "_"; // remove null-defined macros: $line = str_ireplace($this->null_macros, "", $line); // replace garbage collector hints in the field $line = $this->ReplaceGarbageCollectorHints($line, $garbage_collector_hint); if (preg_match($this->pregex_function_pointer, $line, $captures)) { // function pointer // print("function pointer: $line\n"); $field =$this->ParseFunctionDeclaration($captures[1], $captures[2], $captures[3], $captures[4], false, ""); } else { if (!$struct["valid"]) $field = $this->ParseNormalField($line,$protected_keywords,$field_bitpacked,$bitpacked_real_type,$this_class_field_names); else // don't register the names of fields of embedded structs and field names of the current // class, but do track them for the current struct as there may be conflicts due to // Pascal's case-insensitivity $field = $this->ParseNormalField($line,$protected_keywords,$field_bitpacked,$bitpacked_real_type,$struct["fieldnames"]); } // mark the field as having a garbage collector field if ($garbage_collector_hint) $field = "$field {garbage collector: $garbage_collector_hint }"; // return field if ($struct["valid"]) { if (!$struct["bitpacked"]) $struct["bitpacked_first_type"] = $bitpacked_real_type; if ($field_bitpacked) $struct["bitpacked"] = true; $struct["fields"][] = $field; } else { return $field; } } // Parses a struct field into a list function ParseStructList ($line, $input, $name, $type) { $field = ""; $list = explode(",", $input); if (count($list) > 1) { $field = " "; foreach ($list as $key) { $key = trim($key, " "); $field .= "$key, "; } $field = rtrim($field, ", "); $field .= ": $type;\n"; } else { $field = " $name: $type;"; $field = $this->MakeFieldInlineArray($field, $line, $name, $type)."\n"; } return $field; } // Parses $line into a function declaration string. Handles both // function pointers (isexternfunc = false) and external functions // (isexternfunc = true) function ParseFunctionDeclaration($rettypestr, $retpointertypestr, $funcname, $parastr, $isexternfunc, $deprecatedmods) { if ($deprecatedmods != "") $deprecatedmods .= ";"; if ($this->IsKeywordReserved($funcname)) $funcname .= "_"; $rettype = trim(str_replace_word("const","",$rettypestr)); $rettype = $this->ReplaceObjcType($rettype); $rettype = $this->EncodePointerModifiers($rettype,$retpointertypestr); $params = $this->ConvertCParamsPascal($parastr); if ($rettype == "void") $result = "procedure "; else $result = "function "; // if no name specified, the caller will add it if ($funcname != "") { if (!$isexternfunc) $result = "$funcname: " . $result; else $result .= $funcname; } if ($params != "") $params = "(" . $params . ")"; if ($rettype == "void") $result .= $params . "; cdecl;"; else $result .= $params . ": " . $rettype . "; cdecl;"; if ($isexternfunc) $result .= " external;$deprecatedmods"; else $result .= "\n"; return $result; } // Parses $line into the combined $struct_fields string function ParseStructFields ($line, $protected_keywords, &$struct_fields, &$any_field_parsed, &$found_any_bitpacked, &$all_bitpacked, &$first_bitpacked_type) { if (preg_match($this->pregex_function_pointer, $line, $captures)) { $struct_fields .= " " . $this->ParseFunctionDeclaration($captures[1], $captures[2], $captures[3], $captures[4], false, ""); $all_bitpacked = false; } else { // better: keep for entire struct, so we can escape conflicting names due to // case-insensitivity $tempnewprotected = array(); $new_field = $this->ParseNormalField($line,$protected_keywords,$field_bitpacked,$bitpacked_real_type,$tempnewprotected); // print("field: '$new_field', bitpacked: $field_bitpacked, any: $found_any_bitpacked, all: $all_bitpacked\n"); if ($new_field != "") { $found_any_bitpacked |= $field_bitpacked; if (!$any_field_parsed) { $all_bitpacked = $field_bitpacked; $first_bitpacked_type = $bitpacked_real_type; $any_field_parsed=true; } else $all_bitpacked &= $field_bitpacked; $struct_fields .= " " . $new_field . $this->AppendEOLComment() . "\n"; } // print(" after: any: $found_any_bitpacked, all: $all_bitpacked\n"); } } // Parse a single enum field function ParseEnumFields ($line, $file_name, &$block_count, &$auto_increment) { // insert macros //if ($macro = $this->InsertMacroBlocks($line, $this->inside_macro_block)) $this->dump[$file_name]["types"]["enums"][$block_count][] = $macro; if (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*=[[:space:]]*[(]*([a-zA-Z_]+)[)]*[,]*[[:space:]]*$", $line, $captures)) { // string value $captures[2] = trim($captures[2], ", "); $this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$captures[2].";".$this->AppendEOLComment(); } elseif (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*=[[:space:]]*[(]*([0-9-]+)[)]*[,]*[[:space:]]*$", $line, $captures)) { // integer value $captures[2] = trim($captures[2], ", "); $this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$captures[2].";".$this->AppendEOLComment(); $auto_increment = $captures[2] + 1; } elseif (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*=[[:space:]]*[(]*([0-9]+[xX]+[a-fA-F0-9]+)[)]*", $line, $captures)) { // hexadecimal value $captures[2] = trim($captures[2], ", "); $auto_increment = $captures[2] + 1; $captures[2] = eregi_replace("^0x", "$", $captures[2]); $this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$captures[2].";".$this->AppendEOLComment(); } elseif (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*=[[:space:]]*([a-zA-Z0-9]+[[:space:]]*<<[[:space:]]*[a-zA-Z0-9]+)", $line, $captures)) { // << shl value, no () $captures[2] = ereg_replace("[[:space:]]?<<[[:space:]]?", " shl ", $captures[2]); // remove integer type hints $captures[2] = ereg_replace("([0-9]+)[UL]+([[:space:]]+)shl([[:space:]])", "\\1\\2shl\\3", $captures[2]); $captures[2] = ereg_replace("([[:space:]])shl([[:space:]]+)([0-9]+)[UL]+", "\\1shl\\2\\3", $captures[2]); $this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$captures[2].";".$this->AppendEOLComment(); $operands = preg_split("/\s*shl\s*/", $captures[2]); $auto_increment = ($operands[0] << $operands[1]) + 1; } elseif (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*=[[:space:]]*\(([a-zA-Z0-9]+[[:space:]]*<<[[:space:]]*[a-zA-Z0-9]+)\)", $line, $captures)) { // << shl value $captures[2] = trim($captures[2], ", "); $captures[2] = ereg_replace("[[:space:]]?<<[[:space:]]?", " shl ", $captures[2]); // remove integer type hints $captures[2] = ereg_replace("([0-9]+)[UL]+([[:space:]]+)shl([[:space:]])", "\\1\\2shl\\3", $captures[2]); $captures[2] = ereg_replace("([[:space:]])shl([[:space:]]+)([0-9]+)[UL]+", "\\1shl\\2\\3", $captures[2]); $this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$captures[2].";".$this->AppendEOLComment(); $operands = preg_split("/\s*shl\s*/", $captures[2]); $auto_increment = ($operands[0] << $operands[1]) + 1; } elseif (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*[,}]*[[:space:]]*$", $line, $captures)) { // non-value // omit lines which started nested structures. // bad practice but the single-line regex parser can't handle them if (!eregi("[=|]+", $line)) { $captures[1] = trim($captures[1], ", "); $this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$auto_increment.";"; $auto_increment ++; } } } // Parse external symbols, enums and typedef's from the header function ParseHeaderTypes ($file) { $contents = file_get_contents($file); $file_name = substr($file, (strripos($file, "/")) + 1, strlen($file)); $any_field_parsed = false; $any_field_bitpacked = false; $all_fields_bitpacked = false; // reset comments from previous parsing sections $this->ResetComment(); $lines = explode("\n", $contents); foreach ($lines as $line) { // skip blocks if ($this->SkipBlock($line)) continue; // ignore lines if (in_array($line, $this->ignore_lines)) continue; // build comments $this->BuildComment($line, $file_name); // build macro blocks $this->BuildMacroBlocks($line); // garbage collector hints $line = $this->ReplaceGarbageCollectorHints($line, $garbage_collector_hint); // remove macros $line = $this->RemoveVersionMacros($line, $deprecatedmods); // remove comments $line = $this->RemoveComments($line); $line = trim($line, " "); if ($got_struct) { // insert macros if ($macro = $this->InsertMacroBlocks($line, $this->inside_macro_block)) $struct_fields .= "$macro\n"; // collect fields $this->ParseStructFields($line, array(), $struct_fields, $any_field_parsed, $any_field_bitpacked, $all_fields_bitpacked, $first_bitpacked_type); // got end of struct if (ereg("^}[[:space:]]*([a-zA-Z_0-9]+);", $line, $captures)) { if ($struct_name == "") { $struct_name = $captures[1]; } else { $struct_type = $captures[1]; } // ignore this struct if (in_array($struct_name, $this->ignore_types)) continue; $struct = "$struct_comment$struct_name = $this->record_keyword\n"; if ($any_field_bitpacked) { $struct .= $this->BitPackedForceAlignment($first_bitpacked_type, " ", " ") . "\n"; $struct_fields = str_replace(" "," ",$struct_fields); $struct_fields .= " end;\n );\n"; } $struct .= $struct_fields; $struct .= " end$deprecatedmods;\n"; if (($struct_type) && ($struct_name != $struct_type)) { $struct .= "$struct_type = $struct_name;\n"; } // pointer type $struct .= $struct_name."Ptr = ^".$struct_name.";\n"; $this->dump[$file_name]["types"]["structs"][] = $struct; $this->dump["global_structs"][] = $struct_name; $got_struct = false; $any_field_parsed = false; $any_field_bitpacked = false; $all_fields_bitpacked = false; } } // got single-line struct if (ereg("^typedef[[:space:]]+struct[[:space:]]+{(.*)}[[:space:]]+([a-zA-Z0-9_]+)", $line, $captures)) { $struct_name = trim($captures[2], " "); if (!in_array($struct_name, $this->ignore_types)) { // break the struct into lines $single_struct_fields = ""; $fields = explode(";", $captures[1]); $comment = $this->InsertCurrentComment(); $this->ResetComment(); // parse each line foreach ($fields as $field) { $field = trim($field); $this->ParseStructFields($field.";", array(), $single_struct_fields, $any_field_parsed, $any_field_bitpacked, $all_fields_bitpacked, $first_bitpacked_type); } // merge the fields into the definition $struct = "$comment\n"."$struct_name = "; $struct .= "$this->record_keyword\n"; if ($any_field_bitpacked) { $struct .= $this->BitPackedForceAlignment($first_bitpacked_type, " ", " ") . "\n"; $single_struct_fields = str_replace(" "," ",$single_struct_fields); $single_struct_fields .= " end;\n );\n"; } else ; $struct .= $single_struct_fields; $struct .= " end$deprecatedmods;\n"; // pointer type $struct .= $struct_name."Ptr = ^".$struct_name.";\n"; $this->dump[$file_name]["types"]["structs"][] = $struct; $this->dump["global_structs"][] = $struct_name; $any_field_parsed = false; $any_field_bitpacked = false; $all_fields_bitpacked = false; //print("$single_struct_fields\n"); } // got begin of struct } elseif (ereg("^typedef struct(.*){", $line, $captures)) { $struct_name = trim($captures[1], " "); if (!in_array($struct_name, $this->ignore_types)) { $struct_type = null; $struct_fields = ""; $struct_comment = $this->InsertCurrentComment(); $this->ResetComment(); if ($struct_comment != "") $struct_comment = "$struct_comment\n"; $got_struct = true; print("Parsing struct $struct_name\n"); } } // got function pointer type if (preg_match($this->pregex_function_pointer_typedef, $line, $captures)) { $typestr = $this->ParseFunctionDeclaration($captures[1], $captures[2], "", $captures[6], false, $deprecatedmods); $functypename = $captures[7]; if ($functypename == "") $functypename = $captures[5]; if ($functypename == "") $functypename = $captures[4]; $this->dump[$file_name]["types"]["callbacks"][$functypename] = $typestr; // record if it is a function type instead of a function pointer type if ($captures[3] == "") $this->implicit_function_pointer_types[] = $functypename; continue; } // #defines $got_define = false; if (ereg("#[[:space:]]*define[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+\(\(.*\)(.*)\)", $line, $captures)) { // named constant with type case $got_define = true; } elseif (ereg("#[[:space:]]*define[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+[(]*([0-9.-]+)[)]*", $line, $captures)) { //integer $got_define = true; } if ($got_define) { $define_name = $captures[1]; if (!in_array($define_name, $this->ignore_types)) { $define_name = $this->ReplaceObjcType($define_name); //if ($this->comment_terminated) $this->dump[$file_name]["types"]["defines"][] = $this->InsertCurrentComment(); $this->AppendCurrentComment($this->dump[$file_name]["types"]["defines"]); $this->dump[$file_name]["types"]["defines"][] = $define_name." = ".$captures[2].";".$this->AppendEOLComment(); } else { $this->ResetComment(); } } // parse enum fields if (($got_enum) || ($got_named_enum)) { // print($line.", auto_inc = $auto_increment\n"); $this->ParseEnumFields($line, $file_name, &$block_count, &$auto_increment); // found the end if (ereg("^};", $line)) $got_enum = false; } // ==== got inline named enum === if (ereg("^[[:space:]]*enum[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]*{(.*)};", $line, $captures)) { //print("$line\n"); $enum_name = trim($captures[1], " "); if (!in_array($enum_name, $this->ignore_types)) { $block_count ++; $auto_increment = 0; // break the enum into lines $fields = explode(",", $captures[2]); //$this->AppendCurrentMacro($this->dump[$file_name]["types"]["enums"][$block_count]); $this->AppendCurrentComment($this->dump[$file_name]["types"]["enums"][$block_count]); if ($this->comment_terminated) $this->dump[$file_name]["types"]["enums"][$block_count][] = $this->InsertCurrentComment(); $this->ResetComment(); // parse each line foreach ($fields as $field) { $field = trim($field, " "); $this->ParseEnumFields($field.",", $file_name, &$block_count, &$auto_increment); } } continue; } // ==== got inline enum === if (ereg("^[[:space:]]*enum[[:space:]]*{(.*)};", $line, $captures)) { //print("$line\n"); $block_count ++; $auto_increment = 0; // break the enum into lines $fields = explode(",", $captures[1]); $this->AppendCurrentComment($this->dump[$file_name]["types"]["enums"][$block_count]); //if ($this->comment_terminated) $this->dump[$file_name]["types"]["enums"][$block_count][] = $this->InsertCurrentComment(); $this->ResetComment(); // parse each line foreach ($fields as $field) { $field = trim($field, " "); $this->ParseEnumFields($field.",", $file_name, &$block_count, &$auto_increment); } continue; } // ==== got enum === if (ereg("^enum", $line)) { $got_enum = true; $block_count ++; $auto_increment = 0; $this->AppendCurrentComment($this->dump[$file_name]["types"]["enums"][$block_count]); //if ($this->comment_terminated) $this->dump[$file_name]["types"]["enums"][$block_count][] = $this->InsertCurrentComment(); } // terminate named enum if ($got_named_enum) { if (ereg("^}[[:space:]]*([a-zA-Z0-9_]+);", $line, $captures)) { $got_named_enum = false; $named_enum = trim($named_enum, ", \n"); $name = $captures[1]; if (!in_array($name, $this->ignore_types)) { $this->dump[$file_name]["types"]["named_enums"][] = "$name = culong;"; $this->dump["global_types"][$name] = $name; } } } // ==== got named enum === if (ereg("^typedef enum {", $line)) { $got_named_enum = true; $named_enum = ""; $auto_increment = 0; $block_count ++; $this->AppendCurrentComment($this->dump[$file_name]["types"]["named_enums"][$block_count]); //if ($this->comment_terminated) $this->dump[$file_name]["types"]["named_enums"][] = $this->InsertCurrentComment(); } // ==== external functions === // doesn't work when $this->external_string_macros is added to // the string at initialisation time, because it can still change // later (while loading frameworks.xml) if (preg_match("!^(?:$this->external_string_macros)+".$this->pregex_external_function_end, $line, $captures)) { // ignore symbols if (in_array($captures[3], $this->ignore_types)) continue; $typestr = $this->ParseFunctionDeclaration($captures[1], $captures[2], $captures[3], $captures[4], true, $deprecatedmods); $this->dump[$file_name]["types"]["functions"][] = $typestr; continue; } // ==== external string constant === if (eregi("^($this->external_string_macros)+[[:space:]]+NSString[[:space:]]*\*[[:space:]]*(const)*[[:space:]]*([a-zA-Z0-9_]+)", $line, $captures)) { $name = $captures[3]; if (in_array($name, $this->ignore_types)) continue; // insert comments $this->AppendCurrentComment($this->dump[$file_name]["types"]["string_constant"]); //if ($this->comment_terminated) $this->dump[$file_name]["types"]["string_constant"][] = $this->InsertCurrentComment(); $this->dump[$file_name]["types"]["string_constant"][] = "$name: $this->string_macro$deprecatedmods; cvar; external;"; } // ==== external symbol === if (eregi("^($this->external_string_macros)+[[:space:]]+([a-zA-Z0-9_ ]+)[[:space:]]+([a-zA-Z0-9_]+)", $line, $captures)) { $name = $captures[3]; $type = $captures[2]; // ignore symbols if (in_array($name, $this->ignore_types)) continue; $type = istr_replace_word("const", "", $type); $type = $this->ReplaceObjcType(trim($type, " ")); $this->AppendCurrentComment($this->dump[$file_name]["types"]["external_symbol"]); //if ($this->comment_terminated) $this->dump[$file_name]["types"]["external_symbol"][] = $this->InsertCurrentComment(); $this->dump[$file_name]["types"]["external_symbol"][] = "$name: $type$deprecatedmods; cvar; external;"; } // ==== got typedef === if (ereg("^typedef[[:space:]]+struct[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+([a-zA-Z0-9_]+);", $line, $captures)) { // defined struct type $real_type = $captures[1]; $struct_type = $captures[1]; $new_type = $captures[2]; // ignore types if (in_array($struct_type, $this->ignore_types)) continue; if (in_array($new_type, $this->ignore_types)) continue; $this->AddTypeDef($this->dump[$file_name], "$struct_type = record end$deprecatedmods;"); $struct_type = $this->ReplaceObjcType($struct_type); if ($new_type != $struct_type) { $this->AddTypeDef($this->dump[$file_name], "$new_type = $struct_type$deprecatedmods;"); $this->dump["global_types"][$new_type] = $real_type; } $this->opaque_structs[] = $struct_type; // also add pointer type to the opaque struct $this->AddTypeDef($this->dump[$file_name], $new_type."Ptr = ^$new_type$deprecatedmods;"); $this->dump["global_types"][$struct_type] = "record end"; $this->dump["global_types"][$new_type."Ptr"] = "^".$new_type; } elseif (ereg("^typedef[[:space:]]+struct[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+([a-zA-Z0-9_*]+);", $line, $captures)) { // pointer to struct $real_type = $captures[1]; $clean_name = trim($captures[2], "*"); $pointer_type = $captures[1]; // ignore types if (in_array($clean_name, $this->ignore_types)) continue; $pointer_type = "Pointer"; $this->AddTypeDef($this->dump[$file_name], "$clean_name = $pointer_type$deprecatedmods;"); $this->dump["global_types"][$clean_name] = $real_type; // also add pointer type $this->AddTypeDef($this->dump[$file_name], $clean_name."Ptr = ^$clean_name$deprecatedmods;"); $this->dump["global_types"][$clean_name."Ptr"] = "^".$clean_name; } elseif (ereg("^typedef[[:space:]]+(const)*[[:space:]]*struct[[:space:]]+([a-zA-Z0-9_*]+)[[:space:]]+([a-zA-Z0-9_]+);", $line, $captures)) { // struct type (complex) $real_type = $captures[1]; $typedef_name = $captures[3]; // ignore types if (in_array($typedef_name, $this->ignore_types)) continue; $captures[2] = $this->FormatObjcType($captures[2], $modifiers); $this->AddTypeDef($this->dump[$file_name], $typedef_name." = ".$captures[2].$deprecatedmods.";"); $this->dump["global_types"][$typedef_name] = $real_type; // also add pointer type $this->AddTypeDef($this->dump[$file_name], $typedef_name."Ptr = ^$typedef_name$deprecatedmods;"); $this->dump["global_types"][$typedef_name."Ptr"] = "^".$typedef_name; } elseif (ereg("^typedef[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+([a-zA-Z0-9_*]+);", $line, $captures)) { // single-word type $real_type = $captures[1]; // type is a pointer if ($captures[2][0] == "*") { $captures[2] = trim($captures[2], "*"); $captures[1] = $this->ReplaceObjcType($captures[1]); // ignore types if (in_array($captures[2], $this->ignore_types)) continue; $this->AddTypeDef($this->dump[$file_name], $captures[2]." = ^".$captures[1]."$deprecatedmods;"); $this->dump["global_types"][$captures[2]] = $real_type; } else { $captures[2] = trim($captures[2], "*"); $captures[1] = $this->ReplaceObjcType($captures[1]); // ignore types if (in_array($captures[2], $this->ignore_types)) continue; $this->AddTypeDef($this->dump[$file_name],$captures[2]." = ".$captures[1]."$deprecatedmods;"); } // also add pointer type $this->AddTypeDef($this->dump[$file_name], $captures[2]."Ptr = ^$captures[2]$deprecatedmods;"); $this->dump["global_types"][$captures[2]."Ptr"] = "^".$captures[2]; } elseif (ereg("^typedef[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+([a-zA-Z0-9_*]+);", $line, $captures)) { // double-word type $real_type = $captures[1]; $typedef_name = trim($captures[3], "*"); $long_type = $captures[1]." ".$captures[2]; $long_type = $this->ReplaceObjcType($long_type); // ignore types if (in_array($captures[2], $this->ignore_types)) continue; $this->AddTypeDef($this->dump[$file_name], $typedef_name." = $long_type$deprecatedmods;"); $this->dump["global_types"][$typedef_name] = $real_type; // also add pointer type $this->AddTypeDef($this->dump[$file_name], $typedef_name."Ptr = ^$typedef_name$deprecatedmods;"); $this->dump["global_types"][$typedef_name."Ptr"] = "^".$typedef_name; } } //print_r($this->dump[$file_name]["types"]); } // Parse all protocols in a header function ParseHeaderProtocols ($file) { $contents = file_get_contents($file); $file_name = substr($file, (strripos($file, "/")) + 1, strlen($file)); $section = null; // reset comments from previous parsing sections $this->ResetComment(); $lines = explode("\n", $contents); foreach ($lines as $line) { // skip blocks if ($this->SkipBlock($line)) continue; // ignore lines if (in_array($line, $this->ignore_lines)) continue; // remove macros $line = $this->RemoveVersionMacros($line, $deprecatedmods); // build comments if (!$got_got_protocol) $this->BuildComment($line, $file_name); // parse protocol if ($got_protocol) { // build comments $this->BuildComment($line, $file_name); // remove comments $line = $this->RemoveComments($line); // found @optional/@required section if (eregi("^[[:space:]]*@(optional|required)+", $line, $captures)) { $section = $captures[1]; } // found property if ($this->LineHasProperty($line, $captures)) { $properties = $this->ParseClassProperty($current_protocol, $captures, $deprecatedmods); foreach ($properties as $property) { if ($property["setter"]) { $property["setter"]["comment"] = $this->InsertCurrentComment(); $property["setter"]["section"] = $section; $property["setter"]["documentation"] = $this->FindDocumentationForMethod($current_protocol, $property["setter"]["property"]); $this->current_header["protocols"][$current_protocol]["methods"][$property["setter"]["objc_method"]] = $property["setter"]; // append to master list of protocols $this->dump["protocols"][$current_protocol][] = $property["setter"]; } if ($property["getter"]) { $property["getter"]["comment"] = $this->InsertCurrentComment(); $property["getter"]["section"] = $section; $property["getter"]["documentation"] = $this->FindDocumentationForMethod($current_protocol, $property["getter"]["property"]); $this->current_header["protocols"][$current_protocol]["methods"][$property["getter"]["objc_method"]] = $property["getter"]; // append to master list of protocols $this->dump["protocols"][$current_protocol][] = $property["getter"]; } } continue; } // found method $method = null; if (preg_match($this->pregex_objc_method_params, $line, $captures)) { $method = $this->ConvertObjcMethodToPascal($current_protocol, $line, $captures, array(), true, $deprecatedmods); } elseif (preg_match($this->pregex_objc_method_no_params, $line, $captures)) { $method = $this->ConvertObjcMethodToPascal($current_protocol, $line, $captures, array(), false, $deprecatedmods); } elseif (preg_match($this->pregex_objc_method_partial, $line, $captures)) { $method_fragment = $line; } // append to classes if (($method) && (!in_array($method["name"], $this->ignore_methods)) ) { // add comment to the method $method["comment"] = $this->InsertCurrentComment(); // add optional/required section to the method $method["section"] = $section; // add documentation for method $method["documentation"] = $this->FindDocumentationForMethod($current_protocol, $method["objc_method"]); $this->current_header["protocols"][$current_protocol]["methods"][$method["objc_method"]] = $method; // append to master list of protocols $this->dump["protocols"][$current_protocol][] = $method; } // found the end if (ereg("^@end", $line)) { $this->ResetComment(); $got_protocol = false; } } // got protocol if ((eregi($this->regex_objc_protocol, $line, $captures)) && (!eregi(".*;$", $line))) { $got_protocol = true; $current_protocol = $captures[1]; print("+ Protocol $current_protocol\n"); if ($this->comment_terminated) $this->current_header["protocols"][$current_protocol]["comment"] = $this->InsertCurrentComment(); $this->current_header["protocols"][$current_protocol]["name"] = $captures[1]; } } //print_r($this->current_class); } // Parse all categories in a header function ParseHeaderCategories ($file) { $contents = file_get_contents($file); $file_name = substr($file, (strripos($file, "/")) + 1, strlen($file)); // reset comments from previous parsing sections $this->ResetComment(); $lines = explode("\n", $contents); foreach ($lines as $line) { // skip blocks if ($this->SkipBlock($line)) continue; // ignore lines if (in_array($line, $this->ignore_lines)) continue; // remove macros $line = $this->RemoveVersionMacros($line, $deprecatedmods); // build comments if (!$got_category) $this->BuildComment($line, $file_name); // parse category if ($got_category) { // build comments $this->BuildComment($line, $file_name); // remove comments $line = $this->RemoveComments($line); // found property if ($this->LineHasProperty($line, $captures)) { $properties = $this->ParseClassProperty($current_category, $captures, $deprecatedmods); if (!in_array($current_category, $this->ignore_categories)) { foreach ($properties as $property) { if ($property["setter"]) { if ($this->AddMethodToClass($property["setter"], $this->current_class)) { $this->dump[$category_owner]["classes"][$current_class]["categories"][$current_category]["methods"][] = $property["setter"]; $this->dump[$category_owner]["categories"][$current_category]["methods"][] = $property["setter"]; } } if ($property["getter"]) { if ($this->AddMethodToClass($property["getter"], $this->current_class)) { $this->dump[$category_owner]["classes"][$current_class]["categories"][$current_category]["methods"][] = $property["getter"]; $this->dump[$category_owner]["categories"][$current_category]["methods"][] = $property["getter"]; } } } } continue; } // build method fragment if ($method_fragment) $method_fragment .= " ".trim($line); // found method fragment termination if (($method_fragment) && (preg_match($this->pregex_objc_method_terminate, $line))) { $line = $method_fragment; $method_fragment = null; } // found method $method = null; if (preg_match($this->pregex_objc_method_params, $line, $captures)) { $method = $this->ConvertObjcMethodToPascal($current_category, $line, $captures, $this->GetProtectedKeywords($this->current_class), true, $deprecatedmods); } elseif (preg_match($this->pregex_objc_method_no_params, $line, $captures)) { $method = $this->ConvertObjcMethodToPascal($current_category, $line, $captures, $this->GetProtectedKeywords($this->current_class), false, $deprecatedmods); } elseif (preg_match($this->pregex_objc_method_partial, $line, $captures)) { $method_fragment = $line; } // append to classes if (($method) && !in_array($current_category, $this->ignore_categories)) { if ($current_class) { if ($this->AddMethodToClass($method, $this->current_class)) { $this->dump[$category_owner]["classes"][$current_class]["categories"][$current_category]["methods"][] = $method; $this->dump[$category_owner]["categories"][$current_category]["methods"][] = $method; } } } // found the end if (ereg("^@end", $line)) { $got_category = false; continue; } } // got category if (eregi($this->regex_objc_category, $line, $captures)) { $got_category = true; $category_owner = $file_name; $category_name = $captures[2]; $current_class = $captures[1]; $current_category = $category_name; if (!in_array($current_category, $this->ignore_categories)) { // Protect category names against duplicate identifiers by appending the class it extends to the name if ((count($this->dump["categories"][$current_category]) > 0) || (in_array($category_name, $this->cocoa_classes))) { $current_category = $category_name."_".$current_class; $this->dump[$file_name]["categories"][$current_category]["external"] = true; } else { $this->dump[$file_name]["categories"][$current_category]["external"] = false; } $this->current_class = &$this->dump[$category_owner]["classes"][$current_class]; // insert into headers category array $this->dump[$file_name]["categories"][$current_category]["name"] = $current_category; $this->dump[$file_name]["categories"][$current_category]["super"] = $current_class; $this->dump[$file_name]["categories"][$current_category]["comment"] = $this->InsertCurrentComment(); $this->dump[$file_name]["categories"][$current_category]["external_name"] = $category_name; // append to master list of categories $this->dump["categories"][$category_name][] = $current_class; } } } //print_r($this->current_class); } // Parse a property into accessor methods function ParseClassProperty ($class, $parts, $deprecatedmods) { $method = array(); //print_r($parts); // property has attributes if (count($parts) == 5) { //$property["parameters"] = explode(",", $parts[1]); $property["parameters"] = preg_split("/\s*,\s*/", $parts[1]); $attributes = $parts[1]; $type = $parts[2]; $pointertype = $parts[3]; $content = $parts[4]; } else { $property["parameters"] = array(); $type = $parts[1]; $pointertype = $parts[2]; $content = $parts[3]; } // unspecified type -> id if ($type == "") $type = "id"; // get property list $list = explode(",", $content); if (count($list) > 1) { $property_list = array(); foreach ($list as $key) { // clean the name and remove the return type $property_list[] = trim($key); } //print_r($property_list); } else { $property_list = array($content); } $methods = array(); foreach ($property_list as $property_name) { // property name if (eregi("([a-zA-Z0-9_]+)[[:space:]]*$", $property_name, $captures)) { $property["name"] = ucwords($captures[1]); $property["name_raw"] = $captures[1]; } // property type $type = $this->ConvertReturnType($type,$pointertype); // prepare for appending if ($deprecatedmods != "") $deprecatedmods .= ";"; // setter if (!in_array("readonly", $property["parameters"])) { $method["setter"] = array(); $name = $property["name"]; if (!$this->GetPropertyName("setter", $property["parameters"], $name)) { $name = "set$name"; } // protect method name from keywords if ($this->IsKeywordReserved($name)) $name .= "_"; $method["setter"]["def"] = "procedure $name (newValue: $type);"; $method["setter"]["objc_method"] = "$name:"; $method["setter"]["class"] = $class; $method["setter"]["name"] = $name; $method["setter"]["kind"] = "procedure"; $method["setter"]["deprecated"] = $deprecatedmods; $method["setter"]["property"] = $property["name_raw"]; //$method["setter"]["comment"] = $this->InsertCurrentComment(); } // getter $method["getter"] = array(); $name = $property["name"]; if (!$this->GetPropertyName("getter", $property["parameters"], $name)) { $name = strtolower(substr($name, 0, 1)) . substr($name, 1); } // protect method name from keywords if ($this->IsKeywordReserved($name)) $name .= "_"; $method["getter"]["def"] = "function $name: $type;"; $method["getter"]["objc_method"] = $name; $method["getter"]["class"] = $class; $method["getter"]["name"] = $name; $method["getter"]["kind"] = "function"; $method["getter"]["deprecated"] = $deprecatedmods; $method["getter"]["property"] = $property["name_raw"]; //$method["getter"]["comment"] = $this->InsertCurrentComment(); // append to array of methods $methods[] = $method; } //print_r($methods); return $methods; } // Parse header classes and methods function ParseHeaderClasses ($file) { $contents = file_get_contents($file); $file_name = substr($file, (strripos($file, "/")) + 1, strlen($file)); $line_count = 0; // reset comments from previous parsing sections $this->ResetComment(); $lines = explode("\n", $contents); foreach ($lines as $line) { $line_count++; // skip blocks if ($this->SkipBlock($line)) continue; // ignore lines if (in_array($line, $this->ignore_lines)) continue; // remove macros $line = $this->RemoveVersionMacros($line, $deprecatedmods); // remove external class macros $line = eregi_replace("^[A-Z0-9]+_EXTERN_CLASS[[:space:]]+", "", $line); // build comments if (!$got_class) $this->BuildComment($line, $file_name); // parse instance vars if ($got_instance_vars) { // scope compiler directive if (eregi($this->regex_scope_compiler_directive, $line, $captures)) { $this->instance_var_scope = $captures[1]; continue; } // remove comments $line = $this->RemoveComments($line); // parse instance variables $result = $this->ParseInstanceVariables($line, $struct, $this->GetProtectedKeywords($this->current_class),$this->dump["master"][$this->current_class["name"]]["field_names"]); // parse structures if ($struct["isfinished"]) { //print_r($struct); //$this->dump[$file_name]["classes"][$current]["ivars"][] = $struct["name"].": $current"."_".$struct["name"].";"; $this->dump[$file_name]["classes"][$current]["ivars_structs"][] = $struct; // print inline-record type $this->dump[$file_name]["classes"][$current]["ivars"][] = $struct["name"].": ".$this->record_keyword; if ($struct["bitpacked"]) { $this->dump[$file_name]["classes"][$current]["ivars"][] = $this->BitPackedForceAlignment($struct["bitpacked_first_type"], " ", " "); } // print fields if ($struct["fields"]) { foreach ($struct["fields"] as $field) $this->dump[$file_name]["classes"][$current]["ivars"][] = " ".$field; } if ($struct["bitpacked"]) { $this->dump[$file_name]["classes"][$current]["ivars"][] = " end;"; $this->dump[$file_name]["classes"][$current]["ivars"][] = " );"; } $this->dump[$file_name]["classes"][$current]["ivars"][] = " end;"; $struct = null; } if(($result != null) && ($result != "struct")) { //print($result); // add a single string or an array of fields to the ivars array if (count($result) <= 1) { $this->dump[$file_name]["classes"][$current]["ivars"][] = $result; } else { foreach ($result as $field) { $this->dump[$file_name]["classes"][$current]["ivars"][] = $field; } } } // instance var section terminated. if (preg_match("!^\s*}\s*[;]*$!", $line)) { $struct = null; $got_instance_vars = false; $this->instance_var_scope = null; } } elseif ($got_class) { // parse the class // the instance variable section started after the class line and no other ivar's were parsed yet if (!$this->dump[$file_name]["classes"][$current]["ivars"]) { if (preg_match("!{\s*$!", $line)) { $got_instance_vars = true; continue; } } // build comments $this->BuildComment($line, $file_name); // remove comments $line = $this->RemoveComments($line); // found property if ($this->LineHasProperty($line, $captures)) { $properties = $this->ParseClassProperty($current, $captures, $deprecatedmods); foreach ($properties as $property) { if ($property["setter"]) { if ($this->AddMethodToClass($property["setter"], $this->dump[$file_name]["classes"][$current])) { $this->dump[$file_name]["classes"][$current]["methods"][] = $property["setter"]; } } if ($property["getter"]) { if ($this->AddMethodToClass($property["getter"], $this->dump[$file_name]["classes"][$current])) { $this->dump[$file_name]["classes"][$current]["methods"][] = $property["getter"]; } } } continue; } // build method fragment if ($method_fragment) $method_fragment .= " ".trim($line, " "); // found method fragment termination if (($method_fragment) && (preg_match($this->pregex_objc_method_terminate, $line))) { $line = $method_fragment; $method_fragment = null; } // found method if (preg_match($this->pregex_objc_method_params, $line, $captures)) { $method = $this->ConvertObjcMethodToPascal($current, $line, $captures, $this->GetProtectedKeywords($this->current_class), true, $deprecatedmods); // add documentation for method $method["documentation"] = $this->FindDocumentationForMethod($current, $method["objc_method"]); if ($this->AddMethodToClass($method, $this->dump[$file_name]["classes"][$current])) { //if ($this->comment_terminated) $method["comment"] = $this->InsertCurrentComment(); $this->dump[$file_name]["classes"][$current]["methods"][] = $method; } } elseif (preg_match($this->pregex_objc_method_no_params, $line, $captures)) { $method = $this->ConvertObjcMethodToPascal($current, $line, $captures, $this->GetProtectedKeywords($this->current_class), false, $deprecatedmods); // add documentation for method $method["documentation"] = $this->FindDocumentationForMethod($current, $method["objc_method"]); if ($this->AddMethodToClass($method, $this->dump[$file_name]["classes"][$current])) { //if ($this->comment_terminated) $method["comment"] = $this->InsertCurrentComment(); $this->dump[$file_name]["classes"][$current]["methods"][] = $method; } } elseif (preg_match($this->pregex_objc_method_partial, $line, $captures)) { $method_fragment = $line; } // found the end if (ereg("^@end", $line)) { $got_class = false; $this->ResetComment(); } } // ==== got class ==== if ((eregi($this->regex_objc_class, $line, $captures)) || (eregi($this->regex_objc_class_no_super, $line, $captures))) { $current = $captures[1]; $got_class = true; $has_superclass = true; // check for instance variable section if (preg_match("!{\s*$!", $line)) $got_instance_vars = true; // get the protocols which the class adopts if (eregi($this->regex_objc_class, $line, $captures)) { if ($captures[3]) $this->dump[$file_name]["classes"][$current]["adopts"] = $captures[3]; } else { if ($captures[2]) $this->dump[$file_name]["classes"][$current]["adopts"] = $captures[2]; $has_superclass=false; } // clean up the conforms string if ($this->dump[$file_name]["classes"][$current]["adopts"]) { $conform_protocols = explode(",", $this->dump[$file_name]["classes"][$current]["adopts"]); $protocol_list = array(); foreach ($conform_protocols as $protocol) { $protocol = trim($protocol, "<> "); $protocol_clean .= $protocol."$this->protocol_suffix, "; $protocol_list[] = $protocol; } $protocol_clean = trim($protocol_clean, ", "); $this->dump[$file_name]["classes"][$current]["adopts"] = $protocol_clean; $this->dump[$file_name]["classes"][$current]["protocols"] = $protocol_list; $protocol_clean = ""; } $this->dump[$file_name]["classes"][$current]["name"] = $captures[1]; if ($has_superclass) { $this->dump[$file_name]["classes"][$current]["super"] = $captures[2]; $this->dump[$file_name]["classes"][$current]["super_class"] = &$this->dump["master"][$captures[2]]; } $this->dump[$file_name]["classes"][$current]["file_name"] = $file_name; $this->dump[$file_name]["classes"][$current]["file_clean"] = substr($file_name, 0, (strripos($file_name, "."))); $this->dump[$file_name]["classes"][$current]["protected_keywords"] = array(); $this->dump[$file_name]["classes"][$current]["declared_methods"] = array(); $this->dump[$file_name]["classes"][$current]["comment"] = $this->InsertCurrentComment(); $this->dump[$file_name]["category_methods"] = array(); $this->current_class = &$this->dump[$file_name]["classes"][$current]; // append master class listing $this->dump["master"][$current] = &$this->dump[$file_name]["classes"][$current]; // preparse for protected keywords $this->PreparseClass($lines, $line_count); // preparse for category methods that may present naming conflicts $category_methods = $this->PreparseCategoryMethods($file); // add category methods to protected keywords if ($category_methods) $this->current_class["protected_keywords"] = array_merge($this->current_class["protected_keywords"], $category_methods); // print class hierarchy if ($this->show_class_hierarchy) { $this->GetClassHierarchy($this->current_class, $hierarchy); $hierarchy_string = ""; foreach ($hierarchy as $value) { $hierarchy_string .= "$value->"; } $hierarchy_string = trim($hierarchy_string, "->"); print(" - $current: $hierarchy_string\n"); } $this->class_count ++; //print_r($this->dump[$file_name]["classes"][$current]); } } //print_r($this->dump[$file_name]["classes"][$current]); } // Parse categories which depend on another header function ParseHeaderDependents ($file) { $file_name = substr($file, (strripos($file, "/")) + 1, strlen($file)); $this->ParseHeaderCategories($file); print("+ Parsed $file_name for dependents\n"); } // Main entry to parse a header function ParseHeader ($file) { $file_name = substr($file, (strripos($file, "/")) + 1, strlen($file)); $name_clean = substr($file_name, 0, (strripos($file_name, "."))); // get the framework we're parsing from if it was specified during a batch parse if ((!$this->framework) && (eregi("/([a-zA-Z]+)\.framework", $file, $captures))) $this->framework = strtolower($captures[1]); // set the dump for the header $this->dump[$file_name]["path"] = "$this->root$this->out/$this->framework/$name_clean.inc"; $this->dump[$file_name]["path_partial"] = "$this->framework/$name_clean.inc"; $this->dump[$file_name]["path_merge"] = $this->dump[$file_name]["path"].".merge"; $this->dump[$file_name]["framework"] = $this->framework; $this->dump[$file_name]["name"] = $file_name; $this->dump[$file_name]["name_clean"] = $name_clean; $this->dump[$file_name]["anoninternstrucs"] = 0; $this->dump[$file_name]["anonbitfields"] = 0; $this->current_header = &$this->dump[$file_name]; // parse each section of the header $this->ParseHeaderTypes($file); $this->ParseHeaderProtocols($file); $this->ParseHeaderClasses($file); print("+ Parsed $file_name\n"); } // Parses the docset at $path for the current framework function ParseFrameworkDocset ($path) { $name = basename($path); $parser = new DocSetParser($path); if ($parser->parse_directory($this->docset_paths[$name])) { $this->docset = $parser->methods; print("+ Parsed documentation for $name.\n"); } } // Parse all headers assigned in $this->frameworks function ParseAllFrameworks ($ignore_files, $parse_only) { foreach ($this->frameworks as $framework_name => $framework_info) { // framework is disabled if ($framework_info["enabled"] != 1) continue; // set the current framework being parsed $this->framework = $framework_name; // get the root file path if ($this->out != "/") { $path = $this->root.$this->out."/".$framework_info["root"]; } else { $path = $this->root.$framework_info["root"]; } // Parse the framework docset if ($this->parse_docsets) $this->ParseFrameworkDocset($framework_info["docset"]); // Load the header if found if (file_exists($path)) { $contents = file_get_contents($path); $lines = explode("\n", $contents); foreach ($lines as $line) { $header = null; $path = null; // parse the header path from the {$include} macro if (eregi($framework_info["include_pattern"], $line, $captures)) { $header = $captures[1].".h"; $path = $framework_info["headers"]."/$header"; } // parse the header path from {-parse} directive if (eregi("^\{-parse[[:space:]]+(.*)[[:space:]]*\}", $line, $captures)) { $header = $captures[1]; $path = $framework_info["headers"]."/$header"; } // parse the header if valid if (file_exists($path)) { // main header if ($parse_only) { if (@in_array($header, $parse_only)) $this->ParseHeader($path); } elseif (@!in_array($header, $ignore_files)) { $this->ParseHeader($path); } // header dependents if ($parse_only) { if (@in_array($header, $parse_only)) $this->ParseHeaderDependents($path); } elseif (@!in_array($header, $ignore_files)) { $this->ParseHeaderDependents($path); } } elseif ($header) { print("*** The header $path could not be found. ***\n"); $this->warning_count++; } } } else { die("FATAL: The master include \"$path\" is missing.\n"); } } // diagnostics print("\n• Parsed $this->method_count methods in $this->class_count classes.\n\n"); if ($this->warning_count > 0) print("• $this->warning_count warnings were encountered.\n\n"); } /** * MAIN METHODS */ // Parse all classes/categories (non-delegate) from the header function CollectHeaderClasses ($file) { // can't find the header, bail if (!file_exists($file)) return; $contents = file_get_contents($file); $lines = explode("\n", $contents); foreach ($lines as $line) { // remove external class macros $line = eregi_replace("^[A-Z0-9]+_EXTERN_CLASS[[:space:]]+", "", $line); // remove version macro's (some can appear before a class) $line = $this->RemoveVersionMacros($line, $dummy_deprecated_mods); // classes if (eregi($this->regex_objc_class, $line, $captures)) { $this->defined_cocoa_classes[] = $captures[1]; // may already have been parsed as an anonymous class if (!in_array($captures[1], $this->cocoa_classes)) $this->cocoa_classes[] = $captures[1]; } if (eregi($this->regex_objc_class_no_super, $line, $captures)) { $this->defined_cocoa_classes[] = $captures[1]; // may already have been parsed as an anonymous class if (!in_array($captures[1], $this->cocoa_classes)) $this->cocoa_classes[] = $captures[1]; } // anonymous classes === if (eregi($this->regex_objc_anon_class, $line, $captures)) { $anon_classes = explode(",", $captures[1]); foreach ($anon_classes as $anon_class) { $anon_class=trim($anon_class); // may already have been parsed as a regular class if (!in_array($anon_class, $this->cocoa_classes)) { $this->cocoa_classes[] = $anon_class; if (!in_array($anon_class, $this->anon_cocoa_classes)) { $this->anon_cocoa_classes[] = $anon_class; } } } } // categories if (eregi($this->regex_objc_category, $line, $captures)) { $this->cocoa_categories[] = $captures[1]; } } } // Build array of all known classes in frameworks function BuildFrameworkClasses () { foreach ($this->frameworks as $framework_name => $framework_info) { // framework is disabled if ($framework_info["enabled"] != 1) continue; if ($handle = @opendir($framework_info["headers"])) { while (($file = readdir($handle)) !== false) { if (eregi($framework_info["header_pattern"], $file)) { $this->CollectHeaderClasses($framework_info["headers"]."/$file"); } } closedir($handle); } else { die("FATAL: The framework \"$framework_name\" can not be located at ".$framework_info["headers"]); } } } // Process a single and print output function ProcessFile ($file, $print) { // set the current framework to null so it's parsed from the framework $this->framework = null; $this->ParseHeader($file); $this->ParseHeaderDependents($file); if ($print) $this->PrintAllHeaders("", null, null, false); } // Loads parser settings from the XML file function LoadFrameworksXML ($framework_path) { $xml = new SimpleXMLElement(file_get_contents("frameworks.xml")); foreach ($xml as $framework) { $this->frameworks[(string) $framework->name]["root"] = (string) $framework->root; $this->frameworks[(string) $framework->name]["headers"] = (string) $framework->headers; if ($framework_path != "") $this->frameworks[(string) $framework->name]["headers"] = preg_replace("!^.*/System/Library/Frameworks!", $framework_path, $this->frameworks[(string) $framework->name]["headers"]); $this->frameworks[(string) $framework->name]["include_pattern"] = (string) $framework->include_pattern; $this->frameworks[(string) $framework->name]["header_pattern"] = (string) $framework->header_pattern; $this->frameworks[(string) $framework->name]["external_macro"] = (string) $framework->external_macro; $this->frameworks[(string) $framework->name]["ignore_types"] = (string) $framework->ignore_types; $this->frameworks[(string) $framework->name]["ignore_methods"] = (string) $framework->ignore_methods; $this->frameworks[(string) $framework->name]["replace_types"] = $framework->replace_types; $this->frameworks[(string) $framework->name]["ignore_lines"] = $framework->ignore_lines; $this->frameworks[(string) $framework->name]["ignore_comments"] = $framework->ignore_comments; $this->frameworks[(string) $framework->name]["docset"] = (string)$framework->docset; $this->frameworks[(string) $framework->name]["enabled"] = false; $this->frameworks[(string) $framework->name]["print"] = true; } } function __construct ($directory, $out_directory, $frameworks, $frameworks_path, $show) { $this->root = $directory; $this->out = $out_directory; $this->show = $show; // load defined frameworks from xml $this->LoadFrameworksXML($frameworks_path); // enable frameworks requested by the command line options if ($frameworks) { foreach ($frameworks as $name) { $name_clean = trim($name, "^ "); $this->frameworks[$name_clean]["enabled"] = true; // apply options from framework definition if ($this->frameworks[$name_clean]["external_macro"]) $this->external_string_macros .= "|".$this->frameworks[$name_clean]["external_macro"]; if ($this->frameworks[$name_clean]["ignore_types"]) $this->ignore_types = array_merge($this->ignore_types, explode(",", $this->frameworks[$name_clean]["ignore_types"])); if ($this->frameworks[$name_clean]["ignore_methods"]) $this->ignore_methods = array_merge($this->ignore_methods, explode(",", $this->frameworks[$name_clean]["ignore_methods"])); if ($this->frameworks[$name_clean]["ignore_lines"]) { foreach ($this->frameworks[$name_clean]["ignore_lines"]->line as $line) { if (!in_array($line, $this->ignore_lines)) $this->ignore_lines[] = (string)$line; } } if ($this->frameworks[$name_clean]["ignore_comments"]) { foreach ($this->frameworks[$name_clean]["ignore_comments"]->line as $line) { if (!in_array($line, $this->ignore_comments)) $this->ignore_comments[] = (string)$line; } } if ($this->frameworks[$name_clean]["replace_types"]) { foreach ($this->frameworks[$name_clean]["replace_types"]->type as $type) { $pair = explode("=", (string)$type); $this->replace_types[$pair[0]] = $pair[1]; } } // print mode if ($name[0] == "^") $this->frameworks[$name_clean]["print"] = false; } } //print_r($this->ignore_comments); //print_r($this->ignore_lines); //print_r($this->frameworks); //exit; $this->BuildFrameworkClasses(); } } ?>