diff --git a/src/main/java/edu/lu/uni/serval/fixminer/ediff/HierarchicalRegrouperForC.java b/src/main/java/edu/lu/uni/serval/fixminer/ediff/HierarchicalRegrouperForC.java index 5eaba40..f641755 100644 --- a/src/main/java/edu/lu/uni/serval/fixminer/ediff/HierarchicalRegrouperForC.java +++ b/src/main/java/edu/lu/uni/serval/fixminer/ediff/HierarchicalRegrouperForC.java @@ -8,8 +8,10 @@ import com.github.gumtreediff.tree.ITree; import edu.lu.uni.serval.utils.ListSorter; +import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -46,9 +48,9 @@ public class HierarchicalRegrouperForC { */ HierarchicalActionSet actionSet = null; for(Action act : actions){ - if(act.getNode().getType() == 19 && act.getNode().getLabel().equals("")){ - continue; - } +// if(act.getNode().getType() == 19 && act.getNode().getLabel().equals("")){ +// continue; +// } Action parentAct = findParentAction(act, actions); if (parentAct == null) { @@ -122,6 +124,27 @@ public class HierarchicalRegrouperForC { return null; } + public List postOrder(HierarchicalActionSet a) { + List trees = new ArrayList<>(); + getAllSubActions(a, trees); + return trees; + } + private void getAllSubActions(HierarchicalActionSet a,List as) { + + List subActions = a.getSubActions(); + if (subActions.size() != 0){ + for (HierarchicalActionSet s : subActions) { + getAllSubActions(s, as); + } + + } + as.add(a); +// List b = new ArrayList(); +// for (HierarchicalActionSet child: this.getSubActions()) +// b.add(child); +// return b; + } + private HierarchicalActionSet removeParentNode(HierarchicalActionSet actionSet){ List subActions = actionSet.getSubActions(); Action action = actionSet.getAction(); @@ -143,9 +166,16 @@ public class HierarchicalRegrouperForC { private HierarchicalActionSet removeBlocks(HierarchicalActionSet actionSet){ List subActions = actionSet.getSubActions(); + Predicate predicate = x->NodeMap_new.getKeysByValue(NodeMap_new.StatementMap,x.getAstNodeType()).size() == 1 ; + Predicate predicate1 = x->!x.getAstNodeType().equals("block"); + + Action action = actionSet.getAction(); if (subActions.size() == 1){ HierarchicalActionSet subaction = subActions.get(0); + if(!postOrder(subaction).stream().anyMatch(predicate.and(predicate1))){ + return actionSet; + } Action action1 = subaction.getAction(); //else,then,block if(action.getClass().equals(action1.getClass()) && action.getName().equals("UPD")) { diff --git a/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestInputCases.java b/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestInputCases.java index 0c2f159..8d161d5 100644 --- a/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestInputCases.java +++ b/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestInputCases.java @@ -1,5 +1,6 @@ package edu.lu.uni.serval.fixminer.ediff; +import edu.lu.uni.serval.utils.CallShell; import edu.lu.uni.serval.utils.EDiffHelper; import org.junit.Assert; import org.junit.Ignore; @@ -68,8 +69,12 @@ public class TestInputCases { // List hierarchicalActionSets = getHierarchicalActionSets("php-src_f4a70fa_7f527d_Zend#zend_object_handlers.c");// wrong // List hierarchicalActionSets = getHierarchicalActionSets("wireshark_26f28b_84f7a0_plugins#profinet#packet-pn-dcp.c");//ok // List hierarchicalActionSets = getHierarchicalActionSets("wireshark_a9e926_877728_epan#dissectors#packet-ndps.c");//wrong +// List hierarchicalActionSets = getHierarchicalActionSets("php-src_041867_5d66c3_ext#pdo#pdo_dbh.c");//wrong +// List hierarchicalActionSets = getHierarchicalActionSets("php-src_d810cf_2d8fd4_Zend#zend_builtin_functions.c");//wrong + List hierarchicalActionSets = getHierarchicalActionSets("php-src_c56492_008fb2_sapi#phpdbg#phpdbg.c");//wrong + // List hierarchicalActionSets = getHierarchicalActionSets("php-src_67a100_505f61_TSRM#tsrm_virtual_cwd.c");// - List hierarchicalActionSets = getHierarchicalActionSets("cpython_2cf5d3_9a0d7a_Objects#object.c");// +// List hierarchicalActionSets = getHierarchicalActionSets("cpython_2cf5d3_9a0d7a_Objects#object.c");// // List hierarchicalActionSets1 = getHierarchicalActionSets("php-src_fc0349_0ed538_Zend#zend.c");// // List hierarchicalActionSets = getHierarchicalActionSets("php-src_477e2b3_98599a_main#fopen_wrappers.c");// @@ -81,6 +86,15 @@ public class TestInputCases { } @Test + public void test_php_src_a0a56e_7afbb7() throws IOException { + List hierarchicalActionSets = getHierarchicalActionSets("php-src_a0a56e_7afbb7_ext#com_dotnet#com_extension.c");//ok + Assert.assertEquals(hierarchicalActionSets.size(),1); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@( ) STD_PHP_INI_ENTRY \"com.allow_dcom\" \"0\" PHP_INI_SYSTEM OnUpdateBool allow_dcom zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_verbose\" \"0\" PHP_INI_ALL OnUpdateBool autoreg_verbose zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_typelib\" \"0\" PHP_INI_ALL OnUpdateBool autoreg_on zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_casesensitive\" \"1\" PHP_INI_ALL OnUpdateBool autoreg_case_sensitive zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.code_page\" \"\" PHP_INI_ALL OnUpdateLong code_page zend_com_dotnet_globals com_dotnet_globals PHP_INI_ENTRY \"com.typelib_file\" \"\" PHP_INI_SYSTEM OnTypeLibFileUpdate PHP_INI_END static PHP_GINIT_FUNCTION com_dotnet memset com_dotnet_globals 0 * com_dotnet_globals com_dotnet_globals -> code_page = CP_ACP PHP_MINIT_FUNCTION com_dotnet zend_class_entry ce * tmp REGISTER_INI_ENTRIES php_com_wrapper_minit INIT_FUNC_ARGS_PASSTHRU php_com_persist_minit INIT_FUNC_ARGS_PASSTHRU INIT_CLASS_ENTRY ce \"com_exception\" NULL php_com_exception_class_entry = zend_register_internal_class_ex &ce zend_exception_get_default(TSRMLS_C) NULL TSRMLS_CC php_com_exception_class_entry -> ce_flags |= ZEND_ACC_FINAL INIT_CLASS_ENTRY ce \"com_safearray_proxy\" NULL php_com_saproxy_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_saproxy_class_entry -> ce_flags |= ZEND_ACC_FINAL php_com_saproxy_class_entry -> get_iterator = php_com_saproxy_iter_get INIT_CLASS_ENTRY ce \"variant\" NULL ce . create_object = php_com_object_new php_com_variant_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_variant_class_entry -> get_iterator = php_com_iter_get INIT_CLASS_ENTRY ce \"com\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get zend_ts_hash_init & php_com_typelibraries 0 NULL php_com_typelibrary_dtor 1 HAVE_MSCOREE_H INIT_CLASS_ENTRY ce \"dotnet\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get COM_CONST x REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT) COM_CONST CLSCTX_INPROC_SERVER COM_CONST CLSCTX_INPROC_HANDLER COM_CONST CLSCTX_LOCAL_SERVER COM_CONST CLSCTX_REMOTE_SERVER COM_CONST CLSCTX_SERVER COM_CONST CLSCTX_ALL 0 COM_CONST VT_NULL COM_CONST VT_EMPTY COM_CONST VT_UI1 COM_CONST VT_I1 COM_CONST VT_UI2 COM_CONST VT_I2 COM_CONST VT_UI4 COM_CONST VT_I4 COM_CONST VT_R4 COM_CONST VT_R8 COM_CONST VT_BOOL COM_CONST VT_ERROR COM_CONST VT_CY COM_CONST VT_DATE COM_CONST VT_BSTR COM_CONST VT_DECIMAL COM_CONST VT_UNKNOWN COM_CONST VT_DISPATCH COM_CONST VT_VARIANT COM_CONST VT_INT COM_CONST VT_UINT COM_CONST VT_ARRAY COM_CONST VT_BYREF COM_CONST CP_ACP COM_CONST CP_MACCP COM_CONST CP_OEMCP COM_CONST CP_UTF7 COM_CONST CP_UTF8 COM_CONST CP_SYMBOL COM_CONST CP_THREAD_ACP COM_CONST VARCMP_LT COM_CONST VARCMP_EQ COM_CONST VARCMP_GT COM_CONST VARCMP_NULL COM_CONST NORM_IGNORECASE COM_CONST NORM_IGNORENONSPACE COM_CONST NORM_IGNORESYMBOLS COM_CONST NORM_IGNOREWIDTH COM_CONST NORM_IGNOREKANATYPE NORM_IGNOREKASHIDA COM_CONST NORM_IGNOREKASHIDA COM_CONST DISP_E_DIVBYZERO COM_CONST DISP_E_OVERFLOW COM_CONST DISP_E_BADINDEX COM_CONST MK_E_UNAVAILABLE return SUCCESS @TO@ ( ) STD_PHP_INI_ENTRY \"com.allow_dcom\" \"0\" PHP_INI_SYSTEM OnUpdateBool allow_dcom zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_verbose\" \"0\" PHP_INI_ALL OnUpdateBool autoreg_verbose zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_typelib\" \"0\" PHP_INI_ALL OnUpdateBool autoreg_on zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_casesensitive\" \"1\" PHP_INI_ALL OnUpdateBool autoreg_case_sensitive zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.code_page\" \"\" PHP_INI_ALL OnUpdateLong code_page zend_com_dotnet_globals com_dotnet_globals PHP_INI_ENTRY \"com.typelib_file\" \"\" PHP_INI_SYSTEM OnTypeLibFileUpdate PHP_INI_END static PHP_GINIT_FUNCTION com_dotnet memset com_dotnet_globals 0 * com_dotnet_globals com_dotnet_globals -> code_page = CP_ACP PHP_MINIT_FUNCTION com_dotnet zend_class_entry ce * tmp php_com_wrapper_minit INIT_FUNC_ARGS_PASSTHRU php_com_persist_minit INIT_FUNC_ARGS_PASSTHRU INIT_CLASS_ENTRY ce \"com_exception\" NULL php_com_exception_class_entry = zend_register_internal_class_ex &ce zend_exception_get_default(TSRMLS_C) NULL TSRMLS_CC php_com_exception_class_entry -> ce_flags |= ZEND_ACC_FINAL INIT_CLASS_ENTRY ce \"com_safearray_proxy\" NULL php_com_saproxy_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_saproxy_class_entry -> ce_flags |= ZEND_ACC_FINAL php_com_saproxy_class_entry -> get_iterator = php_com_saproxy_iter_get INIT_CLASS_ENTRY ce \"variant\" NULL ce . create_object = php_com_object_new php_com_variant_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_variant_class_entry -> get_iterator = php_com_iter_get INIT_CLASS_ENTRY ce \"com\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get zend_ts_hash_init & php_com_typelibraries 0 NULL php_com_typelibrary_dtor 1 HAVE_MSCOREE_H INIT_CLASS_ENTRY ce \"dotnet\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get REGISTER_INI_ENTRIES COM_CONST x REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT) COM_CONST CLSCTX_INPROC_SERVER COM_CONST CLSCTX_INPROC_HANDLER COM_CONST CLSCTX_LOCAL_SERVER COM_CONST CLSCTX_REMOTE_SERVER COM_CONST CLSCTX_SERVER COM_CONST CLSCTX_ALL 0 COM_CONST VT_NULL COM_CONST VT_EMPTY COM_CONST VT_UI1 COM_CONST VT_I1 COM_CONST VT_UI2 COM_CONST VT_I2 COM_CONST VT_UI4 COM_CONST VT_I4 COM_CONST VT_R4 COM_CONST VT_R8 COM_CONST VT_BOOL COM_CONST VT_ERROR COM_CONST VT_CY COM_CONST VT_DATE COM_CONST VT_BSTR COM_CONST VT_DECIMAL COM_CONST VT_UNKNOWN COM_CONST VT_DISPATCH COM_CONST VT_VARIANT COM_CONST VT_INT COM_CONST VT_UINT COM_CONST VT_ARRAY COM_CONST VT_BYREF COM_CONST CP_ACP COM_CONST CP_MACCP COM_CONST CP_OEMCP COM_CONST CP_UTF7 COM_CONST CP_UTF8 COM_CONST CP_SYMBOL COM_CONST CP_THREAD_ACP COM_CONST VARCMP_LT COM_CONST VARCMP_EQ COM_CONST VARCMP_GT COM_CONST VARCMP_NULL COM_CONST NORM_IGNORECASE COM_CONST NORM_IGNORENONSPACE COM_CONST NORM_IGNORESYMBOLS COM_CONST NORM_IGNOREWIDTH COM_CONST NORM_IGNOREKANATYPE NORM_IGNOREKASHIDA COM_CONST NORM_IGNOREKASHIDA COM_CONST DISP_E_DIVBYZERO COM_CONST DISP_E_OVERFLOW COM_CONST DISP_E_BADINDEX COM_CONST MK_E_UNAVAILABLE return SUCCESS @AT@ 4914 @LENGTH@ 3385\n" + + "---UPD expr@@( ) STD_PHP_INI_ENTRY \"com.allow_dcom\" \"0\" PHP_INI_SYSTEM OnUpdateBool allow_dcom zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_verbose\" \"0\" PHP_INI_ALL OnUpdateBool autoreg_verbose zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_typelib\" \"0\" PHP_INI_ALL OnUpdateBool autoreg_on zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_casesensitive\" \"1\" PHP_INI_ALL OnUpdateBool autoreg_case_sensitive zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.code_page\" \"\" PHP_INI_ALL OnUpdateLong code_page zend_com_dotnet_globals com_dotnet_globals PHP_INI_ENTRY \"com.typelib_file\" \"\" PHP_INI_SYSTEM OnTypeLibFileUpdate PHP_INI_END static PHP_GINIT_FUNCTION com_dotnet memset com_dotnet_globals 0 * com_dotnet_globals com_dotnet_globals -> code_page = CP_ACP PHP_MINIT_FUNCTION com_dotnet zend_class_entry ce * tmp REGISTER_INI_ENTRIES php_com_wrapper_minit INIT_FUNC_ARGS_PASSTHRU php_com_persist_minit INIT_FUNC_ARGS_PASSTHRU INIT_CLASS_ENTRY ce \"com_exception\" NULL php_com_exception_class_entry = zend_register_internal_class_ex &ce zend_exception_get_default(TSRMLS_C) NULL TSRMLS_CC php_com_exception_class_entry -> ce_flags |= ZEND_ACC_FINAL INIT_CLASS_ENTRY ce \"com_safearray_proxy\" NULL php_com_saproxy_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_saproxy_class_entry -> ce_flags |= ZEND_ACC_FINAL php_com_saproxy_class_entry -> get_iterator = php_com_saproxy_iter_get INIT_CLASS_ENTRY ce \"variant\" NULL ce . create_object = php_com_object_new php_com_variant_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_variant_class_entry -> get_iterator = php_com_iter_get INIT_CLASS_ENTRY ce \"com\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get zend_ts_hash_init & php_com_typelibraries 0 NULL php_com_typelibrary_dtor 1 HAVE_MSCOREE_H INIT_CLASS_ENTRY ce \"dotnet\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get COM_CONST x REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT) COM_CONST CLSCTX_INPROC_SERVER COM_CONST CLSCTX_INPROC_HANDLER COM_CONST CLSCTX_LOCAL_SERVER COM_CONST CLSCTX_REMOTE_SERVER COM_CONST CLSCTX_SERVER COM_CONST CLSCTX_ALL 0 COM_CONST VT_NULL COM_CONST VT_EMPTY COM_CONST VT_UI1 COM_CONST VT_I1 COM_CONST VT_UI2 COM_CONST VT_I2 COM_CONST VT_UI4 COM_CONST VT_I4 COM_CONST VT_R4 COM_CONST VT_R8 COM_CONST VT_BOOL COM_CONST VT_ERROR COM_CONST VT_CY COM_CONST VT_DATE COM_CONST VT_BSTR COM_CONST VT_DECIMAL COM_CONST VT_UNKNOWN COM_CONST VT_DISPATCH COM_CONST VT_VARIANT COM_CONST VT_INT COM_CONST VT_UINT COM_CONST VT_ARRAY COM_CONST VT_BYREF COM_CONST CP_ACP COM_CONST CP_MACCP COM_CONST CP_OEMCP COM_CONST CP_UTF7 COM_CONST CP_UTF8 COM_CONST CP_SYMBOL COM_CONST CP_THREAD_ACP COM_CONST VARCMP_LT COM_CONST VARCMP_EQ COM_CONST VARCMP_GT COM_CONST VARCMP_NULL COM_CONST NORM_IGNORECASE COM_CONST NORM_IGNORENONSPACE COM_CONST NORM_IGNORESYMBOLS COM_CONST NORM_IGNOREWIDTH COM_CONST NORM_IGNOREKANATYPE NORM_IGNOREKASHIDA COM_CONST NORM_IGNOREKASHIDA COM_CONST DISP_E_DIVBYZERO COM_CONST DISP_E_OVERFLOW COM_CONST DISP_E_BADINDEX COM_CONST MK_E_UNAVAILABLE return SUCCESS @TO@ ( ) STD_PHP_INI_ENTRY \"com.allow_dcom\" \"0\" PHP_INI_SYSTEM OnUpdateBool allow_dcom zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_verbose\" \"0\" PHP_INI_ALL OnUpdateBool autoreg_verbose zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_typelib\" \"0\" PHP_INI_ALL OnUpdateBool autoreg_on zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.autoregister_casesensitive\" \"1\" PHP_INI_ALL OnUpdateBool autoreg_case_sensitive zend_com_dotnet_globals com_dotnet_globals STD_PHP_INI_ENTRY \"com.code_page\" \"\" PHP_INI_ALL OnUpdateLong code_page zend_com_dotnet_globals com_dotnet_globals PHP_INI_ENTRY \"com.typelib_file\" \"\" PHP_INI_SYSTEM OnTypeLibFileUpdate PHP_INI_END static PHP_GINIT_FUNCTION com_dotnet memset com_dotnet_globals 0 * com_dotnet_globals com_dotnet_globals -> code_page = CP_ACP PHP_MINIT_FUNCTION com_dotnet zend_class_entry ce * tmp php_com_wrapper_minit INIT_FUNC_ARGS_PASSTHRU php_com_persist_minit INIT_FUNC_ARGS_PASSTHRU INIT_CLASS_ENTRY ce \"com_exception\" NULL php_com_exception_class_entry = zend_register_internal_class_ex &ce zend_exception_get_default(TSRMLS_C) NULL TSRMLS_CC php_com_exception_class_entry -> ce_flags |= ZEND_ACC_FINAL INIT_CLASS_ENTRY ce \"com_safearray_proxy\" NULL php_com_saproxy_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_saproxy_class_entry -> ce_flags |= ZEND_ACC_FINAL php_com_saproxy_class_entry -> get_iterator = php_com_saproxy_iter_get INIT_CLASS_ENTRY ce \"variant\" NULL ce . create_object = php_com_object_new php_com_variant_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_variant_class_entry -> get_iterator = php_com_iter_get INIT_CLASS_ENTRY ce \"com\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get zend_ts_hash_init & php_com_typelibraries 0 NULL php_com_typelibrary_dtor 1 HAVE_MSCOREE_H INIT_CLASS_ENTRY ce \"dotnet\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get REGISTER_INI_ENTRIES COM_CONST x REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT) COM_CONST CLSCTX_INPROC_SERVER COM_CONST CLSCTX_INPROC_HANDLER COM_CONST CLSCTX_LOCAL_SERVER COM_CONST CLSCTX_REMOTE_SERVER COM_CONST CLSCTX_SERVER COM_CONST CLSCTX_ALL 0 COM_CONST VT_NULL COM_CONST VT_EMPTY COM_CONST VT_UI1 COM_CONST VT_I1 COM_CONST VT_UI2 COM_CONST VT_I2 COM_CONST VT_UI4 COM_CONST VT_I4 COM_CONST VT_R4 COM_CONST VT_R8 COM_CONST VT_BOOL COM_CONST VT_ERROR COM_CONST VT_CY COM_CONST VT_DATE COM_CONST VT_BSTR COM_CONST VT_DECIMAL COM_CONST VT_UNKNOWN COM_CONST VT_DISPATCH COM_CONST VT_VARIANT COM_CONST VT_INT COM_CONST VT_UINT COM_CONST VT_ARRAY COM_CONST VT_BYREF COM_CONST CP_ACP COM_CONST CP_MACCP COM_CONST CP_OEMCP COM_CONST CP_UTF7 COM_CONST CP_UTF8 COM_CONST CP_SYMBOL COM_CONST CP_THREAD_ACP COM_CONST VARCMP_LT COM_CONST VARCMP_EQ COM_CONST VARCMP_GT COM_CONST VARCMP_NULL COM_CONST NORM_IGNORECASE COM_CONST NORM_IGNORENONSPACE COM_CONST NORM_IGNORESYMBOLS COM_CONST NORM_IGNOREWIDTH COM_CONST NORM_IGNOREKANATYPE NORM_IGNOREKASHIDA COM_CONST NORM_IGNOREKASHIDA COM_CONST DISP_E_DIVBYZERO COM_CONST DISP_E_OVERFLOW COM_CONST DISP_E_BADINDEX COM_CONST MK_E_UNAVAILABLE return SUCCESS @AT@ 4914 @LENGTH@ 3385\n" + + "------UPD block@@zend_class_entry ce * tmp REGISTER_INI_ENTRIES php_com_wrapper_minit INIT_FUNC_ARGS_PASSTHRU php_com_persist_minit INIT_FUNC_ARGS_PASSTHRU INIT_CLASS_ENTRY ce \"com_exception\" NULL php_com_exception_class_entry = zend_register_internal_class_ex &ce zend_exception_get_default(TSRMLS_C) NULL TSRMLS_CC php_com_exception_class_entry -> ce_flags |= ZEND_ACC_FINAL INIT_CLASS_ENTRY ce \"com_safearray_proxy\" NULL php_com_saproxy_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_saproxy_class_entry -> ce_flags |= ZEND_ACC_FINAL php_com_saproxy_class_entry -> get_iterator = php_com_saproxy_iter_get INIT_CLASS_ENTRY ce \"variant\" NULL ce . create_object = php_com_object_new php_com_variant_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_variant_class_entry -> get_iterator = php_com_iter_get INIT_CLASS_ENTRY ce \"com\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get zend_ts_hash_init & php_com_typelibraries 0 NULL php_com_typelibrary_dtor 1 HAVE_MSCOREE_H INIT_CLASS_ENTRY ce \"dotnet\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get COM_CONST x REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT) COM_CONST CLSCTX_INPROC_SERVER COM_CONST CLSCTX_INPROC_HANDLER COM_CONST CLSCTX_LOCAL_SERVER COM_CONST CLSCTX_REMOTE_SERVER COM_CONST CLSCTX_SERVER COM_CONST CLSCTX_ALL 0 COM_CONST VT_NULL COM_CONST VT_EMPTY COM_CONST VT_UI1 COM_CONST VT_I1 COM_CONST VT_UI2 COM_CONST VT_I2 COM_CONST VT_UI4 COM_CONST VT_I4 COM_CONST VT_R4 COM_CONST VT_R8 COM_CONST VT_BOOL COM_CONST VT_ERROR COM_CONST VT_CY COM_CONST VT_DATE COM_CONST VT_BSTR COM_CONST VT_DECIMAL COM_CONST VT_UNKNOWN COM_CONST VT_DISPATCH COM_CONST VT_VARIANT COM_CONST VT_INT COM_CONST VT_UINT COM_CONST VT_ARRAY COM_CONST VT_BYREF COM_CONST CP_ACP COM_CONST CP_MACCP COM_CONST CP_OEMCP COM_CONST CP_UTF7 COM_CONST CP_UTF8 COM_CONST CP_SYMBOL COM_CONST CP_THREAD_ACP COM_CONST VARCMP_LT COM_CONST VARCMP_EQ COM_CONST VARCMP_GT COM_CONST VARCMP_NULL COM_CONST NORM_IGNORECASE COM_CONST NORM_IGNORENONSPACE COM_CONST NORM_IGNORESYMBOLS COM_CONST NORM_IGNOREWIDTH COM_CONST NORM_IGNOREKANATYPE NORM_IGNOREKASHIDA COM_CONST NORM_IGNOREKASHIDA COM_CONST DISP_E_DIVBYZERO COM_CONST DISP_E_OVERFLOW COM_CONST DISP_E_BADINDEX COM_CONST MK_E_UNAVAILABLE return SUCCESS @TO@ zend_class_entry ce * tmp php_com_wrapper_minit INIT_FUNC_ARGS_PASSTHRU php_com_persist_minit INIT_FUNC_ARGS_PASSTHRU INIT_CLASS_ENTRY ce \"com_exception\" NULL php_com_exception_class_entry = zend_register_internal_class_ex &ce zend_exception_get_default(TSRMLS_C) NULL TSRMLS_CC php_com_exception_class_entry -> ce_flags |= ZEND_ACC_FINAL INIT_CLASS_ENTRY ce \"com_safearray_proxy\" NULL php_com_saproxy_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_saproxy_class_entry -> ce_flags |= ZEND_ACC_FINAL php_com_saproxy_class_entry -> get_iterator = php_com_saproxy_iter_get INIT_CLASS_ENTRY ce \"variant\" NULL ce . create_object = php_com_object_new php_com_variant_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_variant_class_entry -> get_iterator = php_com_iter_get INIT_CLASS_ENTRY ce \"com\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get zend_ts_hash_init & php_com_typelibraries 0 NULL php_com_typelibrary_dtor 1 HAVE_MSCOREE_H INIT_CLASS_ENTRY ce \"dotnet\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get REGISTER_INI_ENTRIES COM_CONST x REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT) COM_CONST CLSCTX_INPROC_SERVER COM_CONST CLSCTX_INPROC_HANDLER COM_CONST CLSCTX_LOCAL_SERVER COM_CONST CLSCTX_REMOTE_SERVER COM_CONST CLSCTX_SERVER COM_CONST CLSCTX_ALL 0 COM_CONST VT_NULL COM_CONST VT_EMPTY COM_CONST VT_UI1 COM_CONST VT_I1 COM_CONST VT_UI2 COM_CONST VT_I2 COM_CONST VT_UI4 COM_CONST VT_I4 COM_CONST VT_R4 COM_CONST VT_R8 COM_CONST VT_BOOL COM_CONST VT_ERROR COM_CONST VT_CY COM_CONST VT_DATE COM_CONST VT_BSTR COM_CONST VT_DECIMAL COM_CONST VT_UNKNOWN COM_CONST VT_DISPATCH COM_CONST VT_VARIANT COM_CONST VT_INT COM_CONST VT_UINT COM_CONST VT_ARRAY COM_CONST VT_BYREF COM_CONST CP_ACP COM_CONST CP_MACCP COM_CONST CP_OEMCP COM_CONST CP_UTF7 COM_CONST CP_UTF8 COM_CONST CP_SYMBOL COM_CONST CP_THREAD_ACP COM_CONST VARCMP_LT COM_CONST VARCMP_EQ COM_CONST VARCMP_GT COM_CONST VARCMP_NULL COM_CONST NORM_IGNORECASE COM_CONST NORM_IGNORENONSPACE COM_CONST NORM_IGNORESYMBOLS COM_CONST NORM_IGNOREWIDTH COM_CONST NORM_IGNOREKANATYPE NORM_IGNOREKASHIDA COM_CONST NORM_IGNOREKASHIDA COM_CONST DISP_E_DIVBYZERO COM_CONST DISP_E_OVERFLOW COM_CONST DISP_E_BADINDEX COM_CONST MK_E_UNAVAILABLE return SUCCESS @AT@ 5962 @LENGTH@ 2498\n" + + "---------MOV expr@@REGISTER_INI_ENTRIES @TO@ block@@zend_class_entry ce * tmp REGISTER_INI_ENTRIES php_com_wrapper_minit INIT_FUNC_ARGS_PASSTHRU php_com_persist_minit INIT_FUNC_ARGS_PASSTHRU INIT_CLASS_ENTRY ce \"com_exception\" NULL php_com_exception_class_entry = zend_register_internal_class_ex &ce zend_exception_get_default(TSRMLS_C) NULL TSRMLS_CC php_com_exception_class_entry -> ce_flags |= ZEND_ACC_FINAL INIT_CLASS_ENTRY ce \"com_safearray_proxy\" NULL php_com_saproxy_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_saproxy_class_entry -> ce_flags |= ZEND_ACC_FINAL php_com_saproxy_class_entry -> get_iterator = php_com_saproxy_iter_get INIT_CLASS_ENTRY ce \"variant\" NULL ce . create_object = php_com_object_new php_com_variant_class_entry = zend_register_internal_class &ce TSRMLS_CC php_com_variant_class_entry -> get_iterator = php_com_iter_get INIT_CLASS_ENTRY ce \"com\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get zend_ts_hash_init & php_com_typelibraries 0 NULL php_com_typelibrary_dtor 1 HAVE_MSCOREE_H INIT_CLASS_ENTRY ce \"dotnet\" NULL ce . create_object = php_com_object_new tmp = zend_register_internal_class_ex & ce php_com_variant_class_entry \"variant\" TSRMLS_CC tmp -> get_iterator = php_com_iter_get COM_CONST x REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT) COM_CONST CLSCTX_INPROC_SERVER COM_CONST CLSCTX_INPROC_HANDLER COM_CONST CLSCTX_LOCAL_SERVER COM_CONST CLSCTX_REMOTE_SERVER COM_CONST CLSCTX_SERVER COM_CONST CLSCTX_ALL 0 COM_CONST VT_NULL COM_CONST VT_EMPTY COM_CONST VT_UI1 COM_CONST VT_I1 COM_CONST VT_UI2 COM_CONST VT_I2 COM_CONST VT_UI4 COM_CONST VT_I4 COM_CONST VT_R4 COM_CONST VT_R8 COM_CONST VT_BOOL COM_CONST VT_ERROR COM_CONST VT_CY COM_CONST VT_DATE COM_CONST VT_BSTR COM_CONST VT_DECIMAL COM_CONST VT_UNKNOWN COM_CONST VT_DISPATCH COM_CONST VT_VARIANT COM_CONST VT_INT COM_CONST VT_UINT COM_CONST VT_ARRAY COM_CONST VT_BYREF COM_CONST CP_ACP COM_CONST CP_MACCP COM_CONST CP_OEMCP COM_CONST CP_UTF7 COM_CONST CP_UTF8 COM_CONST CP_SYMBOL COM_CONST CP_THREAD_ACP COM_CONST VARCMP_LT COM_CONST VARCMP_EQ COM_CONST VARCMP_GT COM_CONST VARCMP_NULL COM_CONST NORM_IGNORECASE COM_CONST NORM_IGNORENONSPACE COM_CONST NORM_IGNORESYMBOLS COM_CONST NORM_IGNOREWIDTH COM_CONST NORM_IGNOREKANATYPE NORM_IGNOREKASHIDA COM_CONST NORM_IGNOREKASHIDA COM_CONST DISP_E_DIVBYZERO COM_CONST DISP_E_OVERFLOW COM_CONST DISP_E_BADINDEX COM_CONST MK_E_UNAVAILABLE return SUCCESS @AT@ 5994 @LENGTH@ 20\n"); + } + @Test public void test_php_src_a29791_796ff1() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("php-src_a29791_796ff1_Zend#zend_API.c");//ok Assert.assertEquals(hierarchicalActionSets.size(),4); @@ -277,7 +291,7 @@ public class TestInputCases { "---------MOV name@@subs ][walk ][1 - '0' @TO@ expr@@'\\\\' == * walk && '0' <= walk ][1 && '9' >= walk ][1 && subs ][walk ][1 - '0' . rm_so > - 1 && subs ][walk ][1 - '0' . rm_eo > - 1 && subs ][walk ][1 - '0' . rm_so <= subs ][walk ][1 - '0' . rm_eo @AT@ 9806 @LENGTH@ 21\n" + "---------MOV name@@subs ][walk ][1 - '0' @TO@ expr@@'\\\\' == * walk && '0' <= walk ][1 && '9' >= walk ][1 && subs ][walk ][1 - '0' . rm_so > - 1 && subs ][walk ][1 - '0' . rm_eo > - 1 && subs ][walk ][1 - '0' . rm_so <= subs ][walk ][1 - '0' . rm_eo @AT@ 9835 @LENGTH@ 21\n"); } - @Test + @Test//TODO public void test_php_src_f91b3d_5f1bff() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("php-src_f91b3d_5f1bff_ext#ereg#ereg.c");//removeParentForSingle?? and others maybe Assert.assertEquals(hierarchicalActionSets.size(),2); @@ -677,55 +691,55 @@ public class TestInputCases { @Test public void test_php_src_470a39_b38730() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("php-src_470a39_b38730_Zend#zend_stream.c");//can write test case - Assert.assertEquals(hierarchicalActionSets.size(),2); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ buf . st_size @TO@ S_ISREG if ! S_ISREG buf . st_mode 0 buf . st_size @AT@ 2021 @LENGTH@ 27\n" + - "---INS ifdef@@S_ISREG @TO@ block@@ buf . st_size @AT@ 2024 @LENGTH@ 7\n" + - "------INS directive@@ @TO@ ifdef@@S_ISREG @AT@ 2024 @LENGTH@ 0\n" + - "------INS name@@S_ISREG @TO@ ifdef@@S_ISREG @AT@ 2030 @LENGTH@ 7\n" + - "---INS if@@if ! S_ISREG buf . st_mode 0 @TO@ block@@ buf . st_size @AT@ 2043 @LENGTH@ 30\n" + - "------INS condition@@ ! S_ISREG buf . st_mode @TO@ if@@if ! S_ISREG buf . st_mode 0 @AT@ 2043 @LENGTH@ 24\n" + - "---------INS expr@@! S_ISREG buf . st_mode @TO@ condition@@ ! S_ISREG buf . st_mode @AT@ 2044 @LENGTH@ 24\n" + - "------------INS operator@@! @TO@ expr@@! S_ISREG buf . st_mode @AT@ 2044 @LENGTH@ 1\n" + - "------------INS call@@S_ISREG buf . st_mode @TO@ expr@@! S_ISREG buf . st_mode @AT@ 2045 @LENGTH@ 22\n" + - "---------------INS name@@S_ISREG @TO@ call@@S_ISREG buf . st_mode @AT@ 2045 @LENGTH@ 7\n" + - "---------------INS argument_list@@ buf . st_mode @TO@ call@@S_ISREG buf . st_mode @AT@ 2052 @LENGTH@ 14\n" + - "------------------INS argument@@buf . st_mode @TO@ argument_list@@ buf . st_mode @AT@ 2053 @LENGTH@ 13\n" + - "---------------------INS expr@@buf . st_mode @TO@ argument@@buf . st_mode @AT@ 2053 @LENGTH@ 13\n" + - "------------------------INS name@@buf . st_mode @TO@ expr@@buf . st_mode @AT@ 2053 @LENGTH@ 13\n" + - "---------------------------INS name@@buf @TO@ name@@buf . st_mode @AT@ 2053 @LENGTH@ 3\n" + - "---------------------------INS operator@@. @TO@ name@@buf . st_mode @AT@ 2056 @LENGTH@ 1\n" + - "---------------------------INS name@@st_mode @TO@ name@@buf . st_mode @AT@ 2057 @LENGTH@ 7\n" + - "------INS then@@ 0 @TO@ if@@if ! S_ISREG buf . st_mode 0 @AT@ 2067 @LENGTH@ 2\n" + - "---------INS block@@ 0 @TO@ then@@ 0 @AT@ 2067 @LENGTH@ 19\n" + - "------------INS return@@ 0 @TO@ block@@ 0 @AT@ 2072 @LENGTH@ 10\n" + - "---------------INS expr@@0 @TO@ return@@ 0 @AT@ 2079 @LENGTH@ 1\n" + - "------------------INS literal@@0 @TO@ expr@@0 @AT@ 2079 @LENGTH@ 1\n" + - "---INS endif@@ @TO@ block@@ buf . st_size @AT@ 2087 @LENGTH@ 0\n" + - "------INS directive@@ @TO@ endif@@ @AT@ 2087 @LENGTH@ 0\n"); - Assert.assertEquals(hierarchicalActionSets.get(1).toString(),"UPD block@@ buf . st_size @TO@ S_ISREG if ! S_ISREG buf . st_mode 0 buf . st_size @AT@ 3287 @LENGTH@ 27\n" + - "---INS ifdef@@S_ISREG @TO@ block@@ buf . st_size @AT@ 3360 @LENGTH@ 7\n" + - "------INS directive@@ @TO@ ifdef@@S_ISREG @AT@ 3360 @LENGTH@ 0\n" + - "------INS name@@S_ISREG @TO@ ifdef@@S_ISREG @AT@ 3366 @LENGTH@ 7\n" + - "---INS if@@if ! S_ISREG buf . st_mode 0 @TO@ block@@ buf . st_size @AT@ 3379 @LENGTH@ 30\n" + - "------INS condition@@ ! S_ISREG buf . st_mode @TO@ if@@if ! S_ISREG buf . st_mode 0 @AT@ 3379 @LENGTH@ 24\n" + - "---------INS expr@@! S_ISREG buf . st_mode @TO@ condition@@ ! S_ISREG buf . st_mode @AT@ 3380 @LENGTH@ 24\n" + - "------------INS operator@@! @TO@ expr@@! S_ISREG buf . st_mode @AT@ 3380 @LENGTH@ 1\n" + - "------------INS call@@S_ISREG buf . st_mode @TO@ expr@@! S_ISREG buf . st_mode @AT@ 3381 @LENGTH@ 22\n" + - "---------------INS name@@S_ISREG @TO@ call@@S_ISREG buf . st_mode @AT@ 3381 @LENGTH@ 7\n" + - "---------------INS argument_list@@ buf . st_mode @TO@ call@@S_ISREG buf . st_mode @AT@ 3388 @LENGTH@ 14\n" + - "------------------INS argument@@buf . st_mode @TO@ argument_list@@ buf . st_mode @AT@ 3389 @LENGTH@ 13\n" + - "---------------------INS expr@@buf . st_mode @TO@ argument@@buf . st_mode @AT@ 3389 @LENGTH@ 13\n" + - "------------------------INS name@@buf . st_mode @TO@ expr@@buf . st_mode @AT@ 3389 @LENGTH@ 13\n" + - "---------------------------INS name@@buf @TO@ name@@buf . st_mode @AT@ 3389 @LENGTH@ 3\n" + - "---------------------------INS operator@@. @TO@ name@@buf . st_mode @AT@ 3392 @LENGTH@ 1\n" + - "---------------------------INS name@@st_mode @TO@ name@@buf . st_mode @AT@ 3393 @LENGTH@ 7\n" + - "------INS then@@ 0 @TO@ if@@if ! S_ISREG buf . st_mode 0 @AT@ 3403 @LENGTH@ 2\n" + - "---------INS block@@ 0 @TO@ then@@ 0 @AT@ 3403 @LENGTH@ 19\n" + - "------------INS return@@ 0 @TO@ block@@ 0 @AT@ 3408 @LENGTH@ 10\n" + - "---------------INS expr@@0 @TO@ return@@ 0 @AT@ 3415 @LENGTH@ 1\n" + - "------------------INS literal@@0 @TO@ expr@@0 @AT@ 3415 @LENGTH@ 1\n" + - "---INS endif@@ @TO@ block@@ buf . st_size @AT@ 3423 @LENGTH@ 0\n" + - "------INS directive@@ @TO@ endif@@ @AT@ 3423 @LENGTH@ 0\n"); + Assert.assertEquals(hierarchicalActionSets.size(),0); +// Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ buf . st_size @TO@ S_ISREG if ! S_ISREG buf . st_mode 0 buf . st_size @AT@ 2021 @LENGTH@ 27\n" + +// "---INS ifdef@@S_ISREG @TO@ block@@ buf . st_size @AT@ 2024 @LENGTH@ 7\n" + +// "------INS directive@@ @TO@ ifdef@@S_ISREG @AT@ 2024 @LENGTH@ 0\n" + +// "------INS name@@S_ISREG @TO@ ifdef@@S_ISREG @AT@ 2030 @LENGTH@ 7\n" + +// "---INS if@@if ! S_ISREG buf . st_mode 0 @TO@ block@@ buf . st_size @AT@ 2043 @LENGTH@ 30\n" + +// "------INS condition@@ ! S_ISREG buf . st_mode @TO@ if@@if ! S_ISREG buf . st_mode 0 @AT@ 2043 @LENGTH@ 24\n" + +// "---------INS expr@@! S_ISREG buf . st_mode @TO@ condition@@ ! S_ISREG buf . st_mode @AT@ 2044 @LENGTH@ 24\n" + +// "------------INS operator@@! @TO@ expr@@! S_ISREG buf . st_mode @AT@ 2044 @LENGTH@ 1\n" + +// "------------INS call@@S_ISREG buf . st_mode @TO@ expr@@! S_ISREG buf . st_mode @AT@ 2045 @LENGTH@ 22\n" + +// "---------------INS name@@S_ISREG @TO@ call@@S_ISREG buf . st_mode @AT@ 2045 @LENGTH@ 7\n" + +// "---------------INS argument_list@@ buf . st_mode @TO@ call@@S_ISREG buf . st_mode @AT@ 2052 @LENGTH@ 14\n" + +// "------------------INS argument@@buf . st_mode @TO@ argument_list@@ buf . st_mode @AT@ 2053 @LENGTH@ 13\n" + +// "---------------------INS expr@@buf . st_mode @TO@ argument@@buf . st_mode @AT@ 2053 @LENGTH@ 13\n" + +// "------------------------INS name@@buf . st_mode @TO@ expr@@buf . st_mode @AT@ 2053 @LENGTH@ 13\n" + +// "---------------------------INS name@@buf @TO@ name@@buf . st_mode @AT@ 2053 @LENGTH@ 3\n" + +// "---------------------------INS operator@@. @TO@ name@@buf . st_mode @AT@ 2056 @LENGTH@ 1\n" + +// "---------------------------INS name@@st_mode @TO@ name@@buf . st_mode @AT@ 2057 @LENGTH@ 7\n" + +// "------INS then@@ 0 @TO@ if@@if ! S_ISREG buf . st_mode 0 @AT@ 2067 @LENGTH@ 2\n" + +// "---------INS block@@ 0 @TO@ then@@ 0 @AT@ 2067 @LENGTH@ 19\n" + +// "------------INS return@@ 0 @TO@ block@@ 0 @AT@ 2072 @LENGTH@ 10\n" + +// "---------------INS expr@@0 @TO@ return@@ 0 @AT@ 2079 @LENGTH@ 1\n" + +// "------------------INS literal@@0 @TO@ expr@@0 @AT@ 2079 @LENGTH@ 1\n" + +// "---INS endif@@ @TO@ block@@ buf . st_size @AT@ 2087 @LENGTH@ 0\n" + +// "------INS directive@@ @TO@ endif@@ @AT@ 2087 @LENGTH@ 0\n"); +// Assert.assertEquals(hierarchicalActionSets.get(1).toString(),"UPD block@@ buf . st_size @TO@ S_ISREG if ! S_ISREG buf . st_mode 0 buf . st_size @AT@ 3287 @LENGTH@ 27\n" + +// "---INS ifdef@@S_ISREG @TO@ block@@ buf . st_size @AT@ 3360 @LENGTH@ 7\n" + +// "------INS directive@@ @TO@ ifdef@@S_ISREG @AT@ 3360 @LENGTH@ 0\n" + +// "------INS name@@S_ISREG @TO@ ifdef@@S_ISREG @AT@ 3366 @LENGTH@ 7\n" + +// "---INS if@@if ! S_ISREG buf . st_mode 0 @TO@ block@@ buf . st_size @AT@ 3379 @LENGTH@ 30\n" + +// "------INS condition@@ ! S_ISREG buf . st_mode @TO@ if@@if ! S_ISREG buf . st_mode 0 @AT@ 3379 @LENGTH@ 24\n" + +// "---------INS expr@@! S_ISREG buf . st_mode @TO@ condition@@ ! S_ISREG buf . st_mode @AT@ 3380 @LENGTH@ 24\n" + +// "------------INS operator@@! @TO@ expr@@! S_ISREG buf . st_mode @AT@ 3380 @LENGTH@ 1\n" + +// "------------INS call@@S_ISREG buf . st_mode @TO@ expr@@! S_ISREG buf . st_mode @AT@ 3381 @LENGTH@ 22\n" + +// "---------------INS name@@S_ISREG @TO@ call@@S_ISREG buf . st_mode @AT@ 3381 @LENGTH@ 7\n" + +// "---------------INS argument_list@@ buf . st_mode @TO@ call@@S_ISREG buf . st_mode @AT@ 3388 @LENGTH@ 14\n" + +// "------------------INS argument@@buf . st_mode @TO@ argument_list@@ buf . st_mode @AT@ 3389 @LENGTH@ 13\n" + +// "---------------------INS expr@@buf . st_mode @TO@ argument@@buf . st_mode @AT@ 3389 @LENGTH@ 13\n" + +// "------------------------INS name@@buf . st_mode @TO@ expr@@buf . st_mode @AT@ 3389 @LENGTH@ 13\n" + +// "---------------------------INS name@@buf @TO@ name@@buf . st_mode @AT@ 3389 @LENGTH@ 3\n" + +// "---------------------------INS operator@@. @TO@ name@@buf . st_mode @AT@ 3392 @LENGTH@ 1\n" + +// "---------------------------INS name@@st_mode @TO@ name@@buf . st_mode @AT@ 3393 @LENGTH@ 7\n" + +// "------INS then@@ 0 @TO@ if@@if ! S_ISREG buf . st_mode 0 @AT@ 3403 @LENGTH@ 2\n" + +// "---------INS block@@ 0 @TO@ then@@ 0 @AT@ 3403 @LENGTH@ 19\n" + +// "------------INS return@@ 0 @TO@ block@@ 0 @AT@ 3408 @LENGTH@ 10\n" + +// "---------------INS expr@@0 @TO@ return@@ 0 @AT@ 3415 @LENGTH@ 1\n" + +// "------------------INS literal@@0 @TO@ expr@@0 @AT@ 3415 @LENGTH@ 1\n" + +// "---INS endif@@ @TO@ block@@ buf . st_size @AT@ 3423 @LENGTH@ 0\n" + +// "------INS directive@@ @TO@ endif@@ @AT@ 3423 @LENGTH@ 0\n"); } @Test public void test_php_src_9baa92_f9c232() throws IOException { @@ -824,15 +838,17 @@ public class TestInputCases { "------------------------INS operator@@( @TO@ expr@@hf @AT@ 1456 @LENGTH@ 1\n" + "------------------------INS name@@int @TO@ expr@@hf @AT@ 1457 @LENGTH@ 3\n" + "------------------------INS operator@@) @TO@ expr@@hf @AT@ 1460 @LENGTH@ 1\n"); - Assert.assertEquals(hierarchicalActionSets.get(1).toString(),"UPD block@@UINT result = ( UINT ) _write hf memory cb if result != cb * err = errno @TO@ UINT result = ( UINT ) _write ( int ) hf memory cb if result != cb * err = errno @AT@ 1573 @LENGTH@ 72\n" + - "---UPD expr@@UINT result = ( UINT ) _write hf memory cb @TO@ UINT result = ( UINT ) _write ( int ) hf memory cb @AT@ 1573 @LENGTH@ 42\n" + - "------UPD call@@_write hf memory cb @TO@ _write ( int ) hf memory cb @AT@ 1593 @LENGTH@ 19\n" + - "---------UPD argument_list@@hf memory cb @TO@ ( int ) hf memory cb @AT@ 1599 @LENGTH@ 12\n" + - "------------UPD argument@@hf @TO@ ( int ) hf @AT@ 1600 @LENGTH@ 2\n" + - "---------------UPD expr@@hf @TO@ ( int ) hf @AT@ 1600 @LENGTH@ 2\n" + - "------------------INS operator@@( @TO@ expr@@hf @AT@ 1605 @LENGTH@ 1\n" + - "------------------INS name@@int @TO@ expr@@hf @AT@ 1606 @LENGTH@ 3\n" + - "------------------INS operator@@) @TO@ expr@@hf @AT@ 1609 @LENGTH@ 1\n"); + Assert.assertEquals(hierarchicalActionSets.get(1).toString(),"UPD expr_stmt@@static FNFCIWRITE cb_write UINT result = ( UINT ) _write hf memory cb if result != cb * err = errno @TO@ static FNFCIWRITE cb_write UINT result = ( UINT ) _write ( int ) hf memory cb if result != cb * err = errno @AT@ 1539 @LENGTH@ 99\n" + + "---UPD expr@@static FNFCIWRITE cb_write UINT result = ( UINT ) _write hf memory cb if result != cb * err = errno @TO@ static FNFCIWRITE cb_write UINT result = ( UINT ) _write ( int ) hf memory cb if result != cb * err = errno @AT@ 1539 @LENGTH@ 99\n" + + "------UPD block@@UINT result = ( UINT ) _write hf memory cb if result != cb * err = errno @TO@ UINT result = ( UINT ) _write ( int ) hf memory cb if result != cb * err = errno @AT@ 1573 @LENGTH@ 72\n" + + "---------UPD expr@@UINT result = ( UINT ) _write hf memory cb @TO@ UINT result = ( UINT ) _write ( int ) hf memory cb @AT@ 1573 @LENGTH@ 42\n" + + "------------UPD call@@_write hf memory cb @TO@ _write ( int ) hf memory cb @AT@ 1593 @LENGTH@ 19\n" + + "---------------UPD argument_list@@hf memory cb @TO@ ( int ) hf memory cb @AT@ 1599 @LENGTH@ 12\n" + + "------------------UPD argument@@hf @TO@ ( int ) hf @AT@ 1600 @LENGTH@ 2\n" + + "---------------------UPD expr@@hf @TO@ ( int ) hf @AT@ 1600 @LENGTH@ 2\n" + + "------------------------INS operator@@( @TO@ expr@@hf @AT@ 1605 @LENGTH@ 1\n" + + "------------------------INS name@@int @TO@ expr@@hf @AT@ 1606 @LENGTH@ 3\n" + + "------------------------INS operator@@) @TO@ expr@@hf @AT@ 1609 @LENGTH@ 1\n"); Assert.assertEquals(hierarchicalActionSets.get(2).toString(),"UPD decl_stmt@@int result _close hf @TO@ int result _close ( int ) hf @AT@ 1717 @LENGTH@ 20\n" + "---UPD decl@@int result _close hf @TO@ int result _close ( int ) hf @AT@ 1717 @LENGTH@ 20\n" + "------UPD init@@_close hf @TO@ _close ( int ) hf @AT@ 1730 @LENGTH@ 9\n" + @@ -849,7 +865,7 @@ public class TestInputCases { "------UPD block@@long result = ( long ) _lseek hf dist seektype if result == - 1 * err = errno @TO@ long result = ( long ) _lseek ( int ) hf dist seektype if result == - 1 * err = errno @AT@ 1839 @LENGTH@ 77\n" + "---------UPD expr@@long result = ( long ) _lseek hf dist seektype @TO@ long result = ( long ) _lseek ( int ) hf dist seektype @AT@ 1839 @LENGTH@ 46\n" + "------------UPD call@@_lseek hf dist seektype @TO@ _lseek ( int ) hf dist seektype @AT@ 1859 @LENGTH@ 23\n" + - "---------------UPD argument_list@@hf dist seektype @TO@ ( int ) hf dist seektype @AT@ 1865 @LENGTH@ 21\n" + + "---------------UPD argument_list@@hf dist seektype @TO@ ( int ) hf dist seektype @AT@ 1865 @LENGTH@ 16\n" + "------------------UPD argument@@hf @TO@ ( int ) hf @AT@ 1866 @LENGTH@ 2\n" + "---------------------UPD expr@@hf @TO@ ( int ) hf @AT@ 1866 @LENGTH@ 2\n" + "------------------------INS operator@@( @TO@ expr@@hf @AT@ 1881 @LENGTH@ 1\n" + @@ -957,7 +973,7 @@ public class TestInputCases { "------------INS expr@@FAILURE @TO@ return@@return FAILURE @AT@ 11754 @LENGTH@ 7\n" + "---------------INS name@@FAILURE @TO@ expr@@FAILURE @AT@ 11754 @LENGTH@ 7\n"); } - @Test + @Test//TODO public void test_php_src_c71358_283565() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("php-src_c71358_283565_main#main.c");// Assert.assertEquals(hierarchicalActionSets.size(),2); @@ -1079,11 +1095,12 @@ public class TestInputCases { Properties appProps = new Properties(); appProps.load(new FileInputStream("src/main/resource/app.properties")); String srcMLPath = appProps.getProperty("srcMLPath", "FORKJOIN"); - String root = appProps.getProperty("inputPath"); +// String root = appProps.getProperty("inputPath"); + String root = "src/main/resource/testFiles"; String project = s.split("_")[0]; root = root + "/"+project+"/"; String filename = s.replace(project+"_",""); - + try{ File revFile = new File(root + "revFiles/" + filename); File prevFile = new File(root + "prevFiles/prev_" + filename); @@ -1092,6 +1109,16 @@ public class TestInputCases { List hierarchicalActionSets = parser.parseChangedSourceCodeWithGumTree2(prevFile, revFile, srcMLPath,false); return hierarchicalActionSets; + }catch (NullPointerException n){ + String cmd = "cp /Users/anil.koyuncu/projects/test/fixminer-data/patches/"+project+"/"+n.getMessage().split(root)[1] + " /Users/anil.koyuncu/projects/test/fixminerC/"+n.getMessage(); + CallShell cs = new CallShell(); + try { + cs.runShell(cmd); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } } diff --git a/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestRealCases.java b/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestRealCases.java index ccb3471..8fb842a 100644 --- a/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestRealCases.java +++ b/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestRealCases.java @@ -1,6 +1,7 @@ package edu.lu.uni.serval.fixminer.ediff; import com.github.gumtreediff.tree.ITree; +import edu.lu.uni.serval.utils.CallShell; import edu.lu.uni.serval.utils.EDiffHelper; import org.junit.Assert; import org.junit.Test; @@ -20,18 +21,18 @@ public class TestRealCases { public void test_287_A_14208510_14208532() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("287-A-14208510-14208532.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d == 3 || h == 3 printf \"YES\" 0 @TO@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" 0 d = 0 h = 0 @AT@ 199 @LENGTH@ 347\n" + - "---UPD if@@if d == 3 || h == 3 printf \"YES\" 0 @TO@ if d >= 3 || h >= 3 printf \"YES\" 0 @AT@ 449 @LENGTH@ 37\n" + - "------UPD condition@@ d == 3 || h == 3 @TO@ d >= 3 || h >= 3 @AT@ 449 @LENGTH@ 15\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d == 3 || h == 3 printf \"YES\" return 0 @TO@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" return 0 d = 0 h = 0 @AT@ 199 @LENGTH@ 197\n" + + "---UPD if@@if d == 3 || h == 3 printf \"YES\" return 0 @TO@ if d >= 3 || h >= 3 printf \"YES\" return 0 @AT@ 449 @LENGTH@ 41\n" + + "------UPD condition@@d == 3 || h == 3 @TO@ d >= 3 || h >= 3 @AT@ 449 @LENGTH@ 16\n" + "---------UPD expr@@d == 3 || h == 3 @TO@ d >= 3 || h >= 3 @AT@ 450 @LENGTH@ 16\n" + "------------UPD operator@@== @TO@ >= @AT@ 451 @LENGTH@ 2\n" + "------------UPD operator@@== @TO@ >= @AT@ 459 @LENGTH@ 2\n" + - "---INS expr_stmt@@d = 0 @TO@ block@@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d == 3 || h == 3 printf \"YES\" 0 @AT@ 548 @LENGTH@ 5\n" + + "---INS expr_stmt@@d = 0 @TO@ block@@if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d == 3 || h == 3 printf \"YES\" return 0 @AT@ 548 @LENGTH@ 5\n" + "------INS expr@@d = 0 @TO@ expr_stmt@@d = 0 @AT@ 548 @LENGTH@ 5\n" + "---------INS name@@d @TO@ expr@@d = 0 @AT@ 548 @LENGTH@ 1\n" + "---------INS operator@@= @TO@ expr@@d = 0 @AT@ 549 @LENGTH@ 1\n" + "---------INS literal@@0 @TO@ expr@@d = 0 @AT@ 550 @LENGTH@ 1\n" + - "---INS expr_stmt@@h = 0 @TO@ block@@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d == 3 || h == 3 printf \"YES\" 0 @AT@ 553 @LENGTH@ 5\n" + + "---INS expr_stmt@@h = 0 @TO@ block@@if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d == 3 || h == 3 printf \"YES\" return 0 @AT@ 553 @LENGTH@ 5\n" + "------INS expr@@h = 0 @TO@ expr_stmt@@h = 0 @AT@ 553 @LENGTH@ 5\n" + "---------INS name@@h @TO@ expr@@h = 0 @AT@ 553 @LENGTH@ 1\n" + "---------INS operator@@= @TO@ expr@@h = 0 @AT@ 554 @LENGTH@ 1\n" + @@ -63,11 +64,11 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("189-B-1682083-1682218.c"); // Assert.assertFalse(true); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@k = MIN k h - j @TO@ t = MIN t h - j @AT@ 254 @LENGTH@ 16\n" + - "---UPD expr@@k = MIN k h - j @TO@ t = MIN t h - j @AT@ 254 @LENGTH@ 16\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@k = MIN k h - j @TO@ t = MIN t h - j @AT@ 254 @LENGTH@ 15\n" + + "---UPD expr@@k = MIN k h - j @TO@ t = MIN t h - j @AT@ 254 @LENGTH@ 15\n" + "------UPD name@@k @TO@ t @AT@ 254 @LENGTH@ 1\n" + - "------UPD call@@MIN k h - j @TO@ MIN t h - j @AT@ 258 @LENGTH@ 12\n" + - "---------UPD argument_list@@ k h - j @TO@ t h - j @AT@ 261 @LENGTH@ 11\n" + + "------UPD call@@MIN k h - j @TO@ MIN t h - j @AT@ 258 @LENGTH@ 11\n" + + "---------UPD argument_list@@k h - j @TO@ t h - j @AT@ 261 @LENGTH@ 7\n" + "------------UPD argument@@k @TO@ t @AT@ 262 @LENGTH@ 1\n" + "---------------UPD expr@@k @TO@ t @AT@ 262 @LENGTH@ 1\n" + "------------------UPD name@@k @TO@ t @AT@ 262 @LENGTH@ 1\n"); @@ -80,7 +81,7 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("177-A2-1594730-1595168.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if i == ( n - 1 ) / 2 && j == ( n - 1 ) / 2 mid = a @TO@ if i == ( n + 1 ) / 2 && j == ( n + 1 ) / 2 mid = a @AT@ 350 @LENGTH@ 51\n" + - "---UPD condition@@ i == ( n - 1 ) / 2 && j == ( n - 1 ) / 2 @TO@ i == ( n + 1 ) / 2 && j == ( n + 1 ) / 2 @AT@ 350 @LENGTH@ 27\n" + + "---UPD condition@@i == ( n - 1 ) / 2 && j == ( n - 1 ) / 2 @TO@ i == ( n + 1 ) / 2 && j == ( n + 1 ) / 2 @AT@ 350 @LENGTH@ 40\n" + "------UPD expr@@i == ( n - 1 ) / 2 && j == ( n - 1 ) / 2 @TO@ i == ( n + 1 ) / 2 && j == ( n + 1 ) / 2 @AT@ 351 @LENGTH@ 40\n" + "---------UPD operator@@- @TO@ + @AT@ 356 @LENGTH@ 1\n" + "---------UPD operator@@- @TO@ + @AT@ 370 @LENGTH@ 1\n"); @@ -123,7 +124,7 @@ public class TestRealCases { //TODO not sure List hierarchicalActionSets = getHierarchicalActionSets("197-B-18221952-18221968.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"INS expr_stmt@@i -- @TO@ block@@ a ][0 /= i b ][0 /= i @AT@ 831 @LENGTH@ 4\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"INS expr_stmt@@i -- @TO@ block@@a ][0 /= i b ][0 /= i @AT@ 831 @LENGTH@ 4\n" + "---INS expr@@i -- @TO@ expr_stmt@@i -- @AT@ 831 @LENGTH@ 4\n" + "------INS name@@i @TO@ expr@@i -- @AT@ 831 @LENGTH@ 1\n" + "------INS operator@@-- @TO@ expr@@i -- @AT@ 832 @LENGTH@ 2\n"); @@ -134,8 +135,8 @@ public class TestRealCases { public void test_474_A_15226851_15226912() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("474-A-15226851-15226912.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ str ][i = s ][j + 1 @TO@ j ++ str ][i = s ][j @AT@ 560 @LENGTH@ 55\n" + - "---INS expr_stmt@@j ++ @TO@ block@@ str ][i = s ][j + 1 @AT@ 582 @LENGTH@ 4\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@str ][i = s ][j + 1 @TO@ j ++ str ][i = s ][j @AT@ 560 @LENGTH@ 19\n" + + "---INS expr_stmt@@j ++ @TO@ block@@str ][i = s ][j + 1 @AT@ 582 @LENGTH@ 4\n" + "------INS expr@@j ++ @TO@ expr_stmt@@j ++ @AT@ 582 @LENGTH@ 4\n" + "---------INS name@@j @TO@ expr@@j ++ @AT@ 582 @LENGTH@ 1\n" + "---------INS operator@@++ @TO@ expr@@j ++ @AT@ 583 @LENGTH@ 2\n" + @@ -154,7 +155,7 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("469-B-8248222-8248281.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if temp1 < r && temp2 > l for k = temp1 k <= temp2 k ++ t ][k = 1 @TO@ if temp1 <= r && temp2 >= l for k = temp1 k <= temp2 k ++ t ][k = 1 @AT@ 432 @LENGTH@ 65\n" + - "---UPD condition@@ temp1 < r && temp2 > l @TO@ temp1 <= r && temp2 >= l @AT@ 432 @LENGTH@ 19\n" + + "---UPD condition@@temp1 < r && temp2 > l @TO@ temp1 <= r && temp2 >= l @AT@ 432 @LENGTH@ 22\n" + "------UPD expr@@temp1 < r && temp2 > l @TO@ temp1 <= r && temp2 >= l @AT@ 433 @LENGTH@ 22\n" + "---------UPD operator@@< @TO@ <= @AT@ 438 @LENGTH@ 1\n" + "---------UPD operator@@> @TO@ >= @AT@ 447 @LENGTH@ 1\n"); @@ -165,8 +166,8 @@ public class TestRealCases { public void test_189_B_17295034_17295064() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("189-B-17295034-17295064.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for b = 2 b <= w b += 2 count += ( w - a + 1 ) * ( h - b + 1 ) @TO@ for b = 2 b <= h b += 2 count += ( w - a + 1 ) * ( h - b + 1 ) @AT@ 183 @LENGTH@ 63\n" + - "---UPD control@@ b = 2 b <= w b += 2 @TO@ b = 2 b <= h b += 2 @AT@ 183 @LENGTH@ 16\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for b = 2 b <= w b += 2 count += ( w - a + 1 ) * ( h - b + 1 ) @TO@ for b = 2 b <= h b += 2 count += ( w - a + 1 ) * ( h - b + 1 ) @AT@ 183 @LENGTH@ 62\n" + + "---UPD control@@b = 2 b <= w b += 2 @TO@ b = 2 b <= h b += 2 @AT@ 183 @LENGTH@ 19\n" + "------UPD condition@@b <= w @TO@ b <= h @AT@ 188 @LENGTH@ 6\n" + "---------UPD expr@@b <= w @TO@ b <= h @AT@ 188 @LENGTH@ 6\n" + "------------UPD name@@w @TO@ h @AT@ 191 @LENGTH@ 1\n"); @@ -178,21 +179,21 @@ public class TestRealCases { //TODO not sure List hierarchicalActionSets = getHierarchicalActionSets("244-B-5291533-5291541.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"INS elseif@@elseif if a == 784262 printf \"4079\\n\" @TO@ if@@if a <= 101 printf \"%d\\n\" a elseif if a == 123 printf \"113\\n\" elseif if a == 1000 printf \"352\\n\" elseif if a == 1000000000 printf \"40744\\n\" elseif if a == 999999999 printf \"40743\\n\" elseif if a == 999999998 printf \"40742\\n\" elseif if a == 999999997 printf \"40741\\n\" elseif if a == 909090901 printf \"38532\\n\" elseif if a == 142498040 printf \"21671\\n\" elseif if a == 603356456 printf \"31623\\n\" elseif if a == 64214872 printf \"15759\\n\" elseif if a == 820040584 printf \"36407\\n\" elseif if a == 442198 printf \"3071\\n\" elseif if a == 642678 printf \"3615\\n\" elseif if a == 468390 printf \"3223\\n\" elseif if a == 326806 printf \"2759\\n\" elseif if a == 940 printf \"331\\n\" elseif if a == 356 printf \"175\\n\" elseif if a == 132 printf \"114\\n\" elseif if a == 102 printf \"101\\n\" @AT@ 877 @LENGTH@ 38\n" + - "---INS if@@if a == 784262 printf \"4079\\n\" @TO@ elseif@@elseif if a == 784262 printf \"4079\\n\" @AT@ 877 @LENGTH@ 31\n" + - "------INS condition@@ a == 784262 @TO@ if@@if a == 784262 printf \"4079\\n\" @AT@ 877 @LENGTH@ 12\n" + - "---------INS expr@@a == 784262 @TO@ condition@@ a == 784262 @AT@ 878 @LENGTH@ 11\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"INS elseif@@elseif if a == 784262 printf \"4079\\n\" @TO@ if@@if a <= 101 printf \"%d\\n\" a elseif if a == 123 printf \"113\\n\" elseif if a == 1000 printf \"352\\n\" elseif if a == 1000000000 printf \"40744\\n\" elseif if a == 999999999 printf \"40743\\n\" elseif if a == 999999998 printf \"40742\\n\" elseif if a == 999999997 printf \"40741\\n\" elseif if a == 909090901 printf \"38532\\n\" elseif if a == 142498040 printf \"21671\\n\" elseif if a == 603356456 printf \"31623\\n\" elseif if a == 64214872 printf \"15759\\n\" elseif if a == 820040584 printf \"36407\\n\" elseif if a == 442198 printf \"3071\\n\" elseif if a == 642678 printf \"3615\\n\" elseif if a == 468390 printf \"3223\\n\" elseif if a == 326806 printf \"2759\\n\" elseif if a == 940 printf \"331\\n\" elseif if a == 356 printf \"175\\n\" elseif if a == 132 printf \"114\\n\" elseif if a == 102 printf \"101\\n\" @AT@ 877 @LENGTH@ 37\n" + + "---INS if@@if a == 784262 printf \"4079\\n\" @TO@ elseif@@elseif if a == 784262 printf \"4079\\n\" @AT@ 877 @LENGTH@ 30\n" + + "------INS condition@@a == 784262 @TO@ if@@if a == 784262 printf \"4079\\n\" @AT@ 877 @LENGTH@ 11\n" + + "---------INS expr@@a == 784262 @TO@ condition@@a == 784262 @AT@ 878 @LENGTH@ 11\n" + "------------INS name@@a @TO@ expr@@a == 784262 @AT@ 878 @LENGTH@ 1\n" + "------------INS operator@@== @TO@ expr@@a == 784262 @AT@ 879 @LENGTH@ 2\n" + "------------INS literal@@784262 @TO@ expr@@a == 784262 @AT@ 881 @LENGTH@ 6\n" + - "------INS then@@printf \"4079\\n\" @TO@ if@@if a == 784262 printf \"4079\\n\" @AT@ 901 @LENGTH@ 16\n" + - "---------INS block@@printf \"4079\\n\" @TO@ then@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 16\n" + - "------------INS expr_stmt@@printf \"4079\\n\" @TO@ block@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 16\n" + - "---------------INS expr@@printf \"4079\\n\" @TO@ expr_stmt@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 16\n" + - "------------------INS call@@printf \"4079\\n\" @TO@ expr@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 16\n" + - "---------------------INS name@@printf @TO@ call@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 6\n" + - "---------------------INS argument_list@@ \"4079\\n\" @TO@ call@@printf \"4079\\n\" @AT@ 907 @LENGTH@ 11\n" + - "------------------------INS argument@@\"4079\\n\" @TO@ argument_list@@ \"4079\\n\" @AT@ 908 @LENGTH@ 8\n" + + "------INS then@@printf \"4079\\n\" @TO@ if@@if a == 784262 printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" + + "---------INS block@@printf \"4079\\n\" @TO@ then@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" + + "------------INS expr_stmt@@printf \"4079\\n\" @TO@ block@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" + + "---------------INS expr@@printf \"4079\\n\" @TO@ expr_stmt@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" + + "------------------INS call@@printf \"4079\\n\" @TO@ expr@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" + + "---------------------INS name@@printf @TO@ call@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 6\n" + + "---------------------INS argument_list@@\"4079\\n\" @TO@ call@@printf \"4079\\n\" @AT@ 907 @LENGTH@ 8\n" + + "------------------------INS argument@@\"4079\\n\" @TO@ argument_list@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n" + "---------------------------INS expr@@\"4079\\n\" @TO@ argument@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n" + "------------------------------INS literal@@\"4079\\n\" @TO@ expr@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n"); @@ -201,29 +202,29 @@ public class TestRealCases { public void test_166_C_1395587_1395933() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("166-C-1395587-1395933.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if array ][( n + 1 ) / 2 == x printf \"0\\n\" elseif if last < ( n + 1 ) / 2 printf \"%d\\n\" n - 2 * last elseif if first > ( n + 1 ) / 2 printf \"%d\\n\" 2 * first - n - 1 @TO@ if array ][( n + 1 ) / 2 - 1 == x printf \"0\\n\" elseif if last < ( n + 1 ) / 2 printf \"%d\\n\" n - 2 * last elseif if first > ( n + 1 ) / 2 printf \"%d\\n\" 2 * first - n - 1 elseif if n == 1 printf \"0\\n\" @AT@ 771 @LENGTH@ 167\n" + - "---UPD condition@@ array ][( n + 1 ) / 2 == x @TO@ array ][( n + 1 ) / 2 - 1 == x @AT@ 771 @LENGTH@ 20\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if array ][( n + 1 ) / 2 == x printf \"0\\n\" elseif if last < ( n + 1 ) / 2 printf \"%d\\n\" n - 2 * last elseif if first > ( n + 1 ) / 2 printf \"%d\\n\" 2 * first - n - 1 @TO@ if array ][( n + 1 ) / 2 - 1 == x printf \"0\\n\" elseif if last < ( n + 1 ) / 2 printf \"%d\\n\" n - 2 * last elseif if first > ( n + 1 ) / 2 printf \"%d\\n\" 2 * first - n - 1 elseif if n == 1 printf \"0\\n\" @AT@ 771 @LENGTH@ 164\n" + + "---UPD condition@@array ][( n + 1 ) / 2 == x @TO@ array ][( n + 1 ) / 2 - 1 == x @AT@ 771 @LENGTH@ 26\n" + "------UPD expr@@array ][( n + 1 ) / 2 == x @TO@ array ][( n + 1 ) / 2 - 1 == x @AT@ 772 @LENGTH@ 26\n" + "---------UPD name@@array ][( n + 1 ) / 2 @TO@ array ][( n + 1 ) / 2 - 1 @AT@ 772 @LENGTH@ 21\n" + "------------UPD index@@][( n + 1 ) / 2 @TO@ ][( n + 1 ) / 2 - 1 @AT@ 778 @LENGTH@ 15\n" + "---------------UPD expr@@[( n + 1 ) / 2 @TO@ [( n + 1 ) / 2 - 1 @AT@ 778 @LENGTH@ 14\n" + "------------------INS operator@@- @TO@ expr@@[( n + 1 ) / 2 @AT@ 785 @LENGTH@ 1\n" + "------------------INS literal@@1 @TO@ expr@@[( n + 1 ) / 2 @AT@ 786 @LENGTH@ 1\n" + - "---INS elseif@@elseif if n == 1 printf \"0\\n\" @TO@ if@@if array ][( n + 1 ) / 2 == x printf \"0\\n\" elseif if last < ( n + 1 ) / 2 printf \"%d\\n\" n - 2 * last elseif if first > ( n + 1 ) / 2 printf \"%d\\n\" 2 * first - n - 1 @AT@ 925 @LENGTH@ 30\n" + - "------INS if@@if n == 1 printf \"0\\n\" @TO@ elseif@@elseif if n == 1 printf \"0\\n\" @AT@ 925 @LENGTH@ 23\n" + - "---------INS condition@@ n == 1 @TO@ if@@if n == 1 printf \"0\\n\" @AT@ 925 @LENGTH@ 7\n" + - "------------INS expr@@n == 1 @TO@ condition@@ n == 1 @AT@ 926 @LENGTH@ 6\n" + + "---INS elseif@@elseif if n == 1 printf \"0\\n\" @TO@ if@@if array ][( n + 1 ) / 2 == x printf \"0\\n\" elseif if last < ( n + 1 ) / 2 printf \"%d\\n\" n - 2 * last elseif if first > ( n + 1 ) / 2 printf \"%d\\n\" 2 * first - n - 1 @AT@ 925 @LENGTH@ 29\n" + + "------INS if@@if n == 1 printf \"0\\n\" @TO@ elseif@@elseif if n == 1 printf \"0\\n\" @AT@ 925 @LENGTH@ 22\n" + + "---------INS condition@@n == 1 @TO@ if@@if n == 1 printf \"0\\n\" @AT@ 925 @LENGTH@ 6\n" + + "------------INS expr@@n == 1 @TO@ condition@@n == 1 @AT@ 926 @LENGTH@ 6\n" + "---------------INS name@@n @TO@ expr@@n == 1 @AT@ 926 @LENGTH@ 1\n" + "---------------INS operator@@== @TO@ expr@@n == 1 @AT@ 927 @LENGTH@ 2\n" + "---------------INS literal@@1 @TO@ expr@@n == 1 @AT@ 929 @LENGTH@ 1\n" + - "---------INS then@@printf \"0\\n\" @TO@ if@@if n == 1 printf \"0\\n\" @AT@ 932 @LENGTH@ 13\n" + - "------------INS block@@printf \"0\\n\" @TO@ then@@printf \"0\\n\" @AT@ 932 @LENGTH@ 13\n" + - "---------------INS expr_stmt@@printf \"0\\n\" @TO@ block@@printf \"0\\n\" @AT@ 932 @LENGTH@ 13\n" + - "------------------INS expr@@printf \"0\\n\" @TO@ expr_stmt@@printf \"0\\n\" @AT@ 932 @LENGTH@ 13\n" + - "---------------------INS call@@printf \"0\\n\" @TO@ expr@@printf \"0\\n\" @AT@ 932 @LENGTH@ 13\n" + - "------------------------INS name@@printf @TO@ call@@printf \"0\\n\" @AT@ 932 @LENGTH@ 6\n" + - "------------------------INS argument_list@@ \"0\\n\" @TO@ call@@printf \"0\\n\" @AT@ 938 @LENGTH@ 8\n" + - "---------------------------INS argument@@\"0\\n\" @TO@ argument_list@@ \"0\\n\" @AT@ 939 @LENGTH@ 5\n" + + "---------INS then@@printf \"0\\n\" @TO@ if@@if n == 1 printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" + + "------------INS block@@printf \"0\\n\" @TO@ then@@printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" + + "---------------INS expr_stmt@@printf \"0\\n\" @TO@ block@@printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" + + "------------------INS expr@@printf \"0\\n\" @TO@ expr_stmt@@printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" + + "---------------------INS call@@printf \"0\\n\" @TO@ expr@@printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" + + "------------------------INS name@@printf @TO@ call@@printf \"0\\n\" @AT@ 932 @LENGTH@ 6\n" + + "------------------------INS argument_list@@\"0\\n\" @TO@ call@@printf \"0\\n\" @AT@ 938 @LENGTH@ 5\n" + + "---------------------------INS argument@@\"0\\n\" @TO@ argument_list@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n" + "------------------------------INS expr@@\"0\\n\" @TO@ argument@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n" + "---------------------------------INS literal@@\"0\\n\" @TO@ expr@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n"); @@ -251,7 +252,7 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("158-A-18237828-18237840.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if ara ][i >= ara ][k - 1 count ++ @TO@ if ara ][i >= ara ][k - 1 && ara ][i != 0 count ++ @AT@ 219 @LENGTH@ 34\n" + - "---UPD condition@@ ara ][i >= ara ][k - 1 @TO@ ara ][i >= ara ][k - 1 && ara ][i != 0 @AT@ 219 @LENGTH@ 19\n" + + "---UPD condition@@ara ][i >= ara ][k - 1 @TO@ ara ][i >= ara ][k - 1 && ara ][i != 0 @AT@ 219 @LENGTH@ 22\n" + "------UPD expr@@ara ][i >= ara ][k - 1 @TO@ ara ][i >= ara ][k - 1 && ara ][i != 0 @AT@ 220 @LENGTH@ 22\n" + "---------INS operator@@&& @TO@ expr@@ara ][i >= ara ][k - 1 @AT@ 236 @LENGTH@ 2\n" + "---------INS name@@ara ][i @TO@ expr@@ara ][i >= ara ][k - 1 @AT@ 239 @LENGTH@ 7\n" + @@ -286,7 +287,7 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("489-A-9343123-9343126.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if ZA ][d < ZA ][c d = c @TO@ if ZA ][d > ZA ][c d = c @AT@ 252 @LENGTH@ 24\n" + - "---UPD condition@@ ZA ][d < ZA ][c @TO@ ZA ][d > ZA ][c @AT@ 252 @LENGTH@ 16\n" + + "---UPD condition@@ZA ][d < ZA ][c @TO@ ZA ][d > ZA ][c @AT@ 252 @LENGTH@ 15\n" + "------UPD expr@@ZA ][d < ZA ][c @TO@ ZA ][d > ZA ][c @AT@ 253 @LENGTH@ 15\n" + "---------UPD operator@@< @TO@ > @AT@ 259 @LENGTH@ 1\n"); @@ -296,8 +297,8 @@ public class TestRealCases { public void test_143_A_17964626_17964657() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("143-A-17964626-17964657.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for a = 1 a < ( r1 % 10 ) a ++ for i = 1 i <= 1000 i ++ ar ][i = 0 ar ][a = 1 if a >= c1 || a >= d1 continue; b = r1 - a if ar ][b == 1 continue; else ar ][b = 1 if b >= c2 || b >= d2 continue; c = c1 - a if ar ][c == 1 continue; else ar ][c = 1 if c >= r2 || c >= d2 continue; d = d1 - a if ar ][d == 1 continue; if d >= r2 || d >= c2 continue; if b + c != d2 continue; if b + d != c2 continue; if c + d != r2 continue; if a > 9 || b > 9 || c > 9 || d > 9 continue; flag = 1 break; @TO@ for a = 1 a < r1 a ++ for i = 1 i <= 1000 i ++ ar ][i = 0 ar ][a = 1 if a >= c1 || a >= d1 continue; b = r1 - a if ar ][b == 1 continue; else ar ][b = 1 if b >= c2 || b >= d2 continue; c = c1 - a if ar ][c == 1 continue; else ar ][c = 1 if c >= r2 || c >= d2 continue; d = d1 - a if ar ][d == 1 continue; if d >= r2 || d >= c2 continue; if b + c != d2 continue; if b + d != c2 continue; if c + d != r2 continue; if a > 9 || b > 9 || c > 9 || d > 9 continue; flag = 1 break; @AT@ 187 @LENGTH@ 487\n" + - "---UPD control@@ a = 1 a < ( r1 % 10 ) a ++ @TO@ a = 1 a < r1 a ++ @AT@ 187 @LENGTH@ 22\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for a = 1 a < ( r1 % 10 ) a ++ for i = 1 i <= 1000 i ++ ar ][i = 0 ar ][a = 1 if a >= c1 || a >= d1 continue; b = r1 - a if ar ][b == 1 continue; else ar ][b = 1 if b >= c2 || b >= d2 continue; c = c1 - a if ar ][c == 1 continue; else ar ][c = 1 if c >= r2 || c >= d2 continue; d = d1 - a if ar ][d == 1 continue; if d >= r2 || d >= c2 continue; if b + c != d2 continue; if b + d != c2 continue; if c + d != r2 continue; if a > 9 || b > 9 || c > 9 || d > 9 continue; flag = 1 break; @TO@ for a = 1 a < r1 a ++ for i = 1 i <= 1000 i ++ ar ][i = 0 ar ][a = 1 if a >= c1 || a >= d1 continue; b = r1 - a if ar ][b == 1 continue; else ar ][b = 1 if b >= c2 || b >= d2 continue; c = c1 - a if ar ][c == 1 continue; else ar ][c = 1 if c >= r2 || c >= d2 continue; d = d1 - a if ar ][d == 1 continue; if d >= r2 || d >= c2 continue; if b + c != d2 continue; if b + d != c2 continue; if c + d != r2 continue; if a > 9 || b > 9 || c > 9 || d > 9 continue; flag = 1 break; @AT@ 187 @LENGTH@ 482\n" + + "---UPD control@@a = 1 a < ( r1 % 10 ) a ++ @TO@ a = 1 a < r1 a ++ @AT@ 187 @LENGTH@ 26\n" + "------UPD condition@@a < ( r1 % 10 ) @TO@ a < r1 @AT@ 193 @LENGTH@ 15\n" + "---------UPD expr@@a < ( r1 % 10 ) @TO@ a < r1 @AT@ 193 @LENGTH@ 15\n" + "------------DEL operator@@( @AT@ 195 @LENGTH@ 1\n" + @@ -314,8 +315,8 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("612-A-15750192-15750273.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for i = p i <= k i ++ printf \"%c\" a ][i @TO@ for i = p i < k i ++ printf \"%c\" a ][i @AT@ 262 @LENGTH@ 41\n" + - "---UPD control@@ i = p i <= k i ++ @TO@ i = p i < k i ++ @AT@ 262 @LENGTH@ 15\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for i = p i <= k i ++ printf \"%c\" a ][i @TO@ for i = p i < k i ++ printf \"%c\" a ][i @AT@ 262 @LENGTH@ 39\n" + + "---UPD control@@i = p i <= k i ++ @TO@ i = p i < k i ++ @AT@ 262 @LENGTH@ 17\n" + "------UPD condition@@i <= k @TO@ i < k @AT@ 267 @LENGTH@ 6\n" + "---------UPD expr@@i <= k @TO@ i < k @AT@ 267 @LENGTH@ 6\n" + "------------UPD operator@@<= @TO@ < @AT@ 268 @LENGTH@ 2\n"); @@ -327,16 +328,16 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("344-A-17290259-17290309.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for i = 0 i < n i ++ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 i = i + 2 @TO@ for i = 0 i < n - 1 i ++ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 @AT@ 158 @LENGTH@ 80\n" + - "---UPD control@@ i = 0 i < n i ++ @TO@ i = 0 i < n - 1 i ++ @AT@ 158 @LENGTH@ 14\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for i = 0 i < n i ++ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 i = i + 2 @TO@ for i = 0 i < n - 1 i ++ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 @AT@ 158 @LENGTH@ 77\n" + + "---UPD control@@i = 0 i < n i ++ @TO@ i = 0 i < n - 1 i ++ @AT@ 158 @LENGTH@ 16\n" + "------UPD condition@@i < n @TO@ i < n - 1 @AT@ 163 @LENGTH@ 5\n" + "---------UPD expr@@i < n @TO@ i < n - 1 @AT@ 163 @LENGTH@ 5\n" + "------------INS operator@@- @TO@ expr@@i < n @AT@ 166 @LENGTH@ 1\n" + "------------INS literal@@1 @TO@ expr@@i < n @AT@ 167 @LENGTH@ 1\n" + - "---UPD block@@ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 i = i + 2 @TO@ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 @AT@ 173 @LENGTH@ 74\n" + - "------UPD if@@if a ][i + 1 == a ][i d = d + 1 else f = f + 1 i = i + 2 @TO@ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 @AT@ 179 @LENGTH@ 58\n" + - "---------UPD else@@else f = f + 1 i = i + 2 @TO@ else f = f + 1 @AT@ 218 @LENGTH@ 25\n" + - "------------UPD block@@ f = f + 1 i = i + 2 @TO@ f = f + 1 @AT@ 218 @LENGTH@ 26\n" + + "---UPD block@@if a ][i + 1 == a ][i d = d + 1 else f = f + 1 i = i + 2 @TO@ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 @AT@ 173 @LENGTH@ 56\n" + + "------UPD if@@if a ][i + 1 == a ][i d = d + 1 else f = f + 1 i = i + 2 @TO@ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 @AT@ 179 @LENGTH@ 56\n" + + "---------UPD else@@else f = f + 1 i = i + 2 @TO@ else f = f + 1 @AT@ 218 @LENGTH@ 24\n" + + "------------UPD block@@f = f + 1 i = i + 2 @TO@ f = f + 1 @AT@ 218 @LENGTH@ 19\n" + "---------------DEL expr_stmt@@i = i + 2 @AT@ 233 @LENGTH@ 9\n" + "------------------DEL expr@@i = i + 2 @AT@ 233 @LENGTH@ 9\n" + "---------------------DEL name@@i @AT@ 233 @LENGTH@ 1\n" + @@ -352,24 +353,24 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("452-B-7271987-7272004.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ int i j n m x i_temp 1 scanf \"%d %d\" & n & m if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m 0 @TO@ int i j n m x i_temp 1 scanf \"%d %d\" & n & m if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m 0 0 @AT@ 115 @LENGTH@ 1270\n" + - "---UPD if@@if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m 0 @TO@ if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m 0 @AT@ 168 @LENGTH@ 1062\n" + - "------UPD elseif@@elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m 0 @TO@ elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m 0 @AT@ 1116 @LENGTH@ 247\n" + - "---------UPD if@@if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m 0 @TO@ if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m 0 @AT@ 1116 @LENGTH@ 240\n" + - "------------UPD then@@ if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m 0 @TO@ if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m 0 @AT@ 1123 @LENGTH@ 231\n" + - "---------------UPD block@@ if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m 0 @TO@ if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m 0 @AT@ 1123 @LENGTH@ 260\n" + - "------------------UPD if@@if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m 0 @TO@ if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m 0 @AT@ 1129 @LENGTH@ 230\n" + - "---------------------UPD else@@else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m 0 @TO@ else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m 0 @AT@ 1313 @LENGTH@ 51\n" + - "------------------------UPD block@@ printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m 0 @TO@ printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m 0 @AT@ 1313 @LENGTH@ 66\n" + - "---------------------------UPD expr_stmt@@printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m @TO@ printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m @AT@ 1318 @LENGTH@ 42\n" + - "------------------------------UPD expr@@printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m @TO@ printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m @AT@ 1318 @LENGTH@ 42\n" + - "---------------------------------UPD call@@printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m @TO@ printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m @AT@ 1318 @LENGTH@ 42\n" + - "------------------------------------UPD argument_list@@ \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m @AT@ 1324 @LENGTH@ 37\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@int i j n m x i_temp 1 scanf \"%d %d\" & n & m if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) return 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) return 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) return 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n return 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @TO@ int i j n m x i_temp 1 scanf \"%d %d\" & n & m if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) return 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) return 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) return 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n return 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m return 0 return 0 @AT@ 115 @LENGTH@ 1106\n" + + "---UPD if@@if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) return 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) return 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) return 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n return 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @TO@ if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) return 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) return 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) return 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n return 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m return 0 @AT@ 168 @LENGTH@ 1061\n" + + "------UPD elseif@@elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @TO@ elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m return 0 @AT@ 1116 @LENGTH@ 249\n" + + "---------UPD if@@if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @TO@ if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m return 0 @AT@ 1116 @LENGTH@ 242\n" + + "------------UPD then@@if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @TO@ if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m return 0 @AT@ 1123 @LENGTH@ 233\n" + + "---------------UPD block@@if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @TO@ if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m return 0 @AT@ 1123 @LENGTH@ 233\n" + + "------------------UPD if@@if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @TO@ if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m return 0 @AT@ 1129 @LENGTH@ 233\n" + + "---------------------UPD else@@else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @TO@ else printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m return 0 @AT@ 1313 @LENGTH@ 55\n" + + "------------------------UPD block@@printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @TO@ printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m return 0 @AT@ 1313 @LENGTH@ 50\n" + + "---------------------------UPD expr_stmt@@printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m @TO@ printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m @AT@ 1318 @LENGTH@ 41\n" + + "------------------------------UPD expr@@printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m @TO@ printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m @AT@ 1318 @LENGTH@ 41\n" + + "---------------------------------UPD call@@printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m @TO@ printf \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m @AT@ 1318 @LENGTH@ 41\n" + + "------------------------------------UPD argument_list@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m @AT@ 1324 @LENGTH@ 34\n" + "---------------------------------------UPD argument@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" + "------------------------------------------UPD expr@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" + "---------------------------------------------UPD literal@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" + - "---INS return@@ 0 @TO@ block@@ int i j n m x i_temp 1 scanf \"%d %d\" & n & m if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m 0 @AT@ 1385 @LENGTH@ 10\n" + - "------INS expr@@0 @TO@ return@@ 0 @AT@ 1392 @LENGTH@ 1\n" + + "---INS return@@return 0 @TO@ block@@int i j n m x i_temp 1 scanf \"%d %d\" & n & m if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) return 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) return 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) return 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n return 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @AT@ 1385 @LENGTH@ 8\n" + + "------INS expr@@0 @TO@ return@@return 0 @AT@ 1392 @LENGTH@ 1\n" + "---------INS literal@@0 @TO@ expr@@0 @AT@ 1392 @LENGTH@ 1\n"); @@ -380,10 +381,10 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("158-E-1314159-1314160.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@ans = max ans 86401 - dp ][k @TO@ ans = max ans 86400 - dp ][k @AT@ 377 @LENGTH@ 29\n" + - "---UPD expr@@ans = max ans 86401 - dp ][k @TO@ ans = max ans 86400 - dp ][k @AT@ 377 @LENGTH@ 29\n" + - "------UPD call@@max ans 86401 - dp ][k @TO@ max ans 86400 - dp ][k @AT@ 383 @LENGTH@ 23\n" + - "---------UPD argument_list@@ ans 86401 - dp ][k @TO@ ans 86400 - dp ][k @AT@ 386 @LENGTH@ 23\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@ans = max ans 86401 - dp ][k @TO@ ans = max ans 86400 - dp ][k @AT@ 377 @LENGTH@ 28\n" + + "---UPD expr@@ans = max ans 86401 - dp ][k @TO@ ans = max ans 86400 - dp ][k @AT@ 377 @LENGTH@ 28\n" + + "------UPD call@@max ans 86401 - dp ][k @TO@ max ans 86400 - dp ][k @AT@ 383 @LENGTH@ 22\n" + + "---------UPD argument_list@@ans 86401 - dp ][k @TO@ ans 86400 - dp ][k @AT@ 386 @LENGTH@ 18\n" + "------------UPD argument@@86401 - dp ][k @TO@ 86400 - dp ][k @AT@ 393 @LENGTH@ 14\n" + "---------------UPD expr@@86401 - dp ][k @TO@ 86400 - dp ][k @AT@ 393 @LENGTH@ 14\n" + "------------------UPD literal@@86401 @TO@ 86400 @AT@ 393 @LENGTH@ 5\n"); @@ -395,10 +396,10 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("250-A-2762401-2762408.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@folders = ceil negative / 2 @TO@ folders = ceil negative / 2.0 @AT@ 289 @LENGTH@ 28\n" + - "---UPD expr@@folders = ceil negative / 2 @TO@ folders = ceil negative / 2.0 @AT@ 289 @LENGTH@ 28\n" + - "------UPD call@@ceil negative / 2 @TO@ ceil negative / 2.0 @AT@ 299 @LENGTH@ 18\n" + - "---------UPD argument_list@@ negative / 2 @TO@ negative / 2.0 @AT@ 303 @LENGTH@ 13\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@folders = ceil negative / 2 @TO@ folders = ceil negative / 2.0 @AT@ 289 @LENGTH@ 27\n" + + "---UPD expr@@folders = ceil negative / 2 @TO@ folders = ceil negative / 2.0 @AT@ 289 @LENGTH@ 27\n" + + "------UPD call@@ceil negative / 2 @TO@ ceil negative / 2.0 @AT@ 299 @LENGTH@ 17\n" + + "---------UPD argument_list@@negative / 2 @TO@ negative / 2.0 @AT@ 303 @LENGTH@ 12\n" + "------------UPD argument@@negative / 2 @TO@ negative / 2.0 @AT@ 304 @LENGTH@ 12\n" + "---------------UPD expr@@negative / 2 @TO@ negative / 2.0 @AT@ 304 @LENGTH@ 12\n" + "------------------UPD literal@@2 @TO@ 2.0 @AT@ 313 @LENGTH@ 1\n"); @@ -410,10 +411,10 @@ public class TestRealCases { //TODO List hierarchicalActionSets = getHierarchicalActionSets("31-B-6435804-6435825.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for i = 1 i <= strlen s i ++ if n == - 1 if s ][i != '@' n = i else printf \"No solution\\n\" ( 0 ) if fl == 1 if s ][i != '@' fl = 0 if flag != 0 jj ++ cc ][jj = ',' for j = n j <= i j ++ jj ++ cc ][jj = s ][j else for j = n j <= i j ++ jj ++ cc ][jj = s ][j flag = 1 n = - 1 kon = i exit = 1 else printf \"No solution\\n\" ( 0 ) elseif if s ][i == '@' fl = 1 @TO@ for i = 1 i < strlen s i ++ if n == - 1 if s ][i != '@' n = i else printf \"No solution\\n\" ( 0 ) if fl == 1 if s ][i != '@' fl = 0 if flag != 0 jj ++ cc ][jj = ',' for j = n j <= i j ++ jj ++ cc ][jj = s ][j else for j = n j <= i j ++ jj ++ cc ][jj = s ][j flag = 1 n = - 1 kon = i exit = 1 else printf \"No solution\\n\" ( 0 ) elseif if s ][i == '@' fl = 1 @AT@ 225 @LENGTH@ 369\n" + - "---UPD control@@ i = 1 i <= strlen s i ++ @TO@ i = 1 i < strlen s i ++ @AT@ 225 @LENGTH@ 29\n" + - "------UPD condition@@i <= strlen s @TO@ i < strlen s @AT@ 233 @LENGTH@ 14\n" + - "---------UPD expr@@i <= strlen s @TO@ i < strlen s @AT@ 233 @LENGTH@ 14\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for i = 1 i <= strlen s i ++ if n == - 1 if s ][i != '@' n = i else printf \"No solution\\n\" return ( 0 ) if fl == 1 if s ][i != '@' fl = 0 if flag != 0 jj ++ cc ][jj = ',' for j = n j <= i j ++ jj ++ cc ][jj = s ][j else for j = n j <= i j ++ jj ++ cc ][jj = s ][j flag = 1 n = - 1 kon = i exit = 1 else printf \"No solution\\n\" return ( 0 ) elseif if s ][i == '@' fl = 1 @TO@ for i = 1 i < strlen s i ++ if n == - 1 if s ][i != '@' n = i else printf \"No solution\\n\" return ( 0 ) if fl == 1 if s ][i != '@' fl = 0 if flag != 0 jj ++ cc ][jj = ',' for j = n j <= i j ++ jj ++ cc ][jj = s ][j else for j = n j <= i j ++ jj ++ cc ][jj = s ][j flag = 1 n = - 1 kon = i exit = 1 else printf \"No solution\\n\" return ( 0 ) elseif if s ][i == '@' fl = 1 @AT@ 225 @LENGTH@ 368\n" + + "---UPD control@@i = 1 i <= strlen s i ++ @TO@ i = 1 i < strlen s i ++ @AT@ 225 @LENGTH@ 24\n" + + "------UPD condition@@i <= strlen s @TO@ i < strlen s @AT@ 233 @LENGTH@ 13\n" + + "---------UPD expr@@i <= strlen s @TO@ i < strlen s @AT@ 233 @LENGTH@ 13\n" + "------------UPD operator@@<= @TO@ < @AT@ 235 @LENGTH@ 2\n"); } @@ -423,8 +424,8 @@ public class TestRealCases { //TODO List hierarchicalActionSets = getHierarchicalActionSets("644-A-18166947-18166954.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for i = 0 i < b i ++ for j = 0 j < b j ++ printf \"%lld \" array ][i ][j printf \"\\n\" @TO@ for i = 0 i < a i ++ for j = 0 j < b j ++ printf \"%lld \" array ][i ][j printf \"\\n\" @AT@ 1251 @LENGTH@ 86\n" + - "---UPD control@@ i = 0 i < b i ++ @TO@ i = 0 i < a i ++ @AT@ 1251 @LENGTH@ 14\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD for@@for i = 0 i < b i ++ for j = 0 j < b j ++ printf \"%lld \" array ][i ][j printf \"\\n\" @TO@ for i = 0 i < a i ++ for j = 0 j < b j ++ printf \"%lld \" array ][i ][j printf \"\\n\" @AT@ 1251 @LENGTH@ 82\n" + + "---UPD control@@i = 0 i < b i ++ @TO@ i = 0 i < a i ++ @AT@ 1251 @LENGTH@ 16\n" + "------UPD condition@@i < b @TO@ i < a @AT@ 1256 @LENGTH@ 5\n" + "---------UPD expr@@i < b @TO@ i < a @AT@ 1256 @LENGTH@ 5\n" + "------------UPD name@@b @TO@ a @AT@ 1258 @LENGTH@ 1\n"); @@ -435,8 +436,8 @@ public class TestRealCases { public void test_5_B_10350073_10350082() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("5-B-10350073-10350082.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if ! ( ( ml - l ) % 2 ) if right sl += 1 right = 1 - right @TO@ if ( ml - l ) % 2 if right sl += 1 right = 1 - right @AT@ 515 @LENGTH@ 59\n" + - "---UPD condition@@ ! ( ( ml - l ) % 2 ) @TO@ ( ml - l ) % 2 @AT@ 515 @LENGTH@ 18\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if ! ( ( ml - l ) % 2 ) if right sl += 1 right = 1 - right @TO@ if ( ml - l ) % 2 if right sl += 1 right = 1 - right @AT@ 515 @LENGTH@ 58\n" + + "---UPD condition@@! ( ( ml - l ) % 2 ) @TO@ ( ml - l ) % 2 @AT@ 515 @LENGTH@ 20\n" + "------UPD expr@@! ( ( ml - l ) % 2 ) @TO@ ( ml - l ) % 2 @AT@ 516 @LENGTH@ 20\n" + "---------DEL operator@@! @AT@ 516 @LENGTH@ 1\n" + "---------DEL operator@@( @AT@ 518 @LENGTH@ 1\n" + @@ -448,7 +449,7 @@ public class TestRealCases { public void test_675_A_18211752_18211767() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("675-A-18211752-18211767.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"MOV return@@ 0 @TO@ block@@ if a == b printf \"YES\" else printf \"NO\" @AT@ 242 @LENGTH@ 10\n"); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"MOV return@@return 0 @TO@ block@@if a == b printf \"YES\" else printf \"NO\" @AT@ 242 @LENGTH@ 8\n"); } @@ -457,8 +458,8 @@ public class TestRealCases { //TODO List hierarchicalActionSets = getHierarchicalActionSets("158-A-18278572-18278586.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD while@@while a ][i >= a ][k && i <= n count ++ i ++ @TO@ while a ][i >= a ][k && i <= n && a ][i != 0 count ++ i ++ @AT@ 501 @LENGTH@ 45\n" + - "---UPD condition@@ a ][i >= a ][k && i <= n @TO@ a ][i >= a ][k && i <= n && a ][i != 0 @AT@ 501 @LENGTH@ 19\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD while@@while a ][i >= a ][k && i <= n count ++ i ++ @TO@ while a ][i >= a ][k && i <= n && a ][i != 0 count ++ i ++ @AT@ 501 @LENGTH@ 44\n" + + "---UPD condition@@a ][i >= a ][k && i <= n @TO@ a ][i >= a ][k && i <= n && a ][i != 0 @AT@ 501 @LENGTH@ 24\n" + "------UPD expr@@a ][i >= a ][k && i <= n @TO@ a ][i >= a ][k && i <= n && a ][i != 0 @AT@ 502 @LENGTH@ 24\n" + "---------INS operator@@&& @TO@ expr@@a ][i >= a ][k && i <= n @AT@ 518 @LENGTH@ 2\n" + "---------INS name@@a ][i @TO@ expr@@a ][i >= a ][k && i <= n @AT@ 520 @LENGTH@ 5\n" + @@ -476,7 +477,7 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("31-B-136044-136045.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"MOV if@@if flag puts ans + 1 else printf \"No solution\\n\" @TO@ block@@ long i l flag 0 tot 0 gets str + 1 l = strlen str + 1 if str ][1 == '@' || str ][l == '@' end for i = 1 i <= l - 2 i ++ if str ][i == '@' && ( str ][i + 1 == '@' || str ][i + 2 == '@' ) end for i = 1 i <= l i ++ if flag && str ][i + 1 == '@' ans ][++ tot = ',' if str ][i == '@' flag = 1 ans ][++ tot = str ][i if flag puts ans + 1 else printf \"No solution\\n\" end 0 @AT@ 937 @LENGTH@ 50\n"); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"MOV if@@if flag puts ans + 1 else printf \"No solution\\n\" @TO@ block@@long i l flag 0 tot 0 gets str + 1 l = strlen str + 1 if str ][1 == '@' || str ][l == '@' end for i = 1 i <= l - 2 i ++ if str ][i == '@' && ( str ][i + 1 == '@' || str ][i + 2 == '@' ) end for i = 1 i <= l i ++ if flag && str ][i + 1 == '@' ans ][++ tot = ',' if str ][i == '@' flag = 1 ans ][++ tot = str ][i if flag puts ans + 1 else printf \"No solution\\n\" end return 0 @AT@ 937 @LENGTH@ 48\n"); } @@ -485,7 +486,7 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("432-A-16886797-16886828.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"MOV if@@if z <= 5 - y s ++ @TO@ block@@scanf \"%d\" & z @AT@ 132 @LENGTH@ 18\n"); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"MOV if@@if z <= 5 - y s ++ @TO@ block@@scanf \"%d\" & z @AT@ 132 @LENGTH@ 18\n"); } @Test @@ -493,9 +494,9 @@ public class TestRealCases { //TODO macro List hierarchicalActionSets = getHierarchicalActionSets("507-A-16886367-16886377.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ int x 0 n 0 s 0 i j k h ][105 0 a ][105 0 c ][105 0 y 0 top 0 scanf \"%d %d\" & n & k FOR i 0 n scanf \"%d\" & c ][i FOR i 0 n a ][i = i + 1 FOR i 0 n FOR j 1 n if c ][j - 1 > c ][j s = c ][j c ][j = c ][j - 1 c ][j - 1 = s s = a ][j a ][j = a ][j - 1 a ][j - 1 = s FOR i 0 n top += c ][i if top > k break; printf \"%d\\n\" i FOR j 0 n printf \"%d \" a ][j 0 @TO@ int x 0 n 0 s 0 i j k h ][105 0 a ][105 0 c ][105 0 y 0 top 0 scanf \"%d %d\" & n & k FOR i 0 n scanf \"%d\" & c ][i FOR i 0 n a ][i = i + 1 FOR i 0 n FOR j 1 n if c ][j - 1 > c ][j s = c ][j c ][j = c ][j - 1 c ][j - 1 = s s = a ][j a ][j = a ][j - 1 a ][j - 1 = s FOR i 0 n top += c ][i if top > k break; printf \"%d\\n\" i FOR j 0 i printf \"%d \" a ][j 0 @AT@ 270 @LENGTH@ 491\n" + - "---UPD macro@@FOR j 0 n @TO@ FOR j 0 i @AT@ 701 @LENGTH@ 10\n" + - "------UPD argument_list@@ j 0 n @TO@ j 0 i @AT@ 704 @LENGTH@ 8\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@int x 0 n 0 s 0 i j k h ][105 0 a ][105 0 c ][105 0 y 0 top 0 scanf \"%d %d\" & n & k FOR i 0 n scanf \"%d\" & c ][i FOR i 0 n a ][i = i + 1 FOR i 0 n FOR j 1 n if c ][j - 1 > c ][j s = c ][j c ][j = c ][j - 1 c ][j - 1 = s s = a ][j a ][j = a ][j - 1 a ][j - 1 = s FOR i 0 n top += c ][i if top > k break; printf \"%d\\n\" i FOR j 0 n printf \"%d \" a ][j return 0 @TO@ int x 0 n 0 s 0 i j k h ][105 0 a ][105 0 c ][105 0 y 0 top 0 scanf \"%d %d\" & n & k FOR i 0 n scanf \"%d\" & c ][i FOR i 0 n a ][i = i + 1 FOR i 0 n FOR j 1 n if c ][j - 1 > c ][j s = c ][j c ][j = c ][j - 1 c ][j - 1 = s s = a ][j a ][j = a ][j - 1 a ][j - 1 = s FOR i 0 n top += c ][i if top > k break; printf \"%d\\n\" i FOR j 0 i printf \"%d \" a ][j return 0 @AT@ 270 @LENGTH@ 356\n" + + "---UPD macro@@FOR j 0 n @TO@ FOR j 0 i @AT@ 701 @LENGTH@ 9\n" + + "------UPD argument_list@@j 0 n @TO@ j 0 i @AT@ 704 @LENGTH@ 5\n" + "---------UPD argument@@n @TO@ i @AT@ 709 @LENGTH@ 1\n"); } @@ -504,9 +505,9 @@ public class TestRealCases { //TODO macro List hierarchicalActionSets = getHierarchicalActionSets("25-D-110126-110132.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ int i j k l m n int a ][1200 b ][1200 unused ][1200 int ind ][1200 int cnt ][1200 int res_a ][1200 res_b ][1200 res_c ][1200 res_d ][1200 int shima int res bef scanf \"%d\" & n m = n - 1 rep i m scanf \"%d%d\" a + i b + i , a ][i -- , b ][i -- unionInit ind n rep i m unionConnect ind a ][i b ][i shima = 0 rep i m cnt ][unionGet ind i = 1 rep i n shima += cnt ][i res = 0 bef = - 1 rep i n if cnt ][i if bef >= 0 res_c ][res = bef res_d ][res ++ = i bef = i res = 0 rep k m unused ][k = 0 rep k m unionInit ind n rep i m if unused ][i == 0 if i != k unionConnect ind a ][i b ][i rep i n cnt ][i = 0 rep i n cnt ][unionGet ind i = 1 j = 0 rep i n j += cnt ][i if j == shima unused ][k = 1 res_a ][res = a ][k res_b ][res ++ = b ][k printf \"%d\\n\" res rep i res printf \"%d %d %d %d\\n\" res_a ][i + 1 res_b ][i + 1 res_c ][i + 1 res_d ][i + 1 0 @TO@ int i j k l m n int a ][1200 b ][1200 unused ][1200 int ind ][1200 int cnt ][1200 int res_a ][1200 res_b ][1200 res_c ][1200 res_d ][1200 int shima int res bef scanf \"%d\" & n m = n - 1 rep i m scanf \"%d%d\" a + i b + i , a ][i -- , b ][i -- unionInit ind n rep i m unionConnect ind a ][i b ][i shima = 0 rep i n cnt ][unionGet ind i = 1 rep i n shima += cnt ][i res = 0 bef = - 1 rep i n if cnt ][i if bef >= 0 res_c ][res = bef res_d ][res ++ = i bef = i res = 0 rep k m unused ][k = 0 rep k m unionInit ind n rep i m if unused ][i == 0 if i != k unionConnect ind a ][i b ][i rep i n cnt ][i = 0 rep i n cnt ][unionGet ind i = 1 j = 0 rep i n j += cnt ][i if j == shima unused ][k = 1 res_a ][res = a ][k res_b ][res ++ = b ][k printf \"%d\\n\" res rep i res printf \"%d %d %d %d\\n\" res_a ][i + 1 res_b ][i + 1 res_c ][i + 1 res_d ][i + 1 0 @AT@ 411 @LENGTH@ 955\n" + - "---UPD macro@@rep i m @TO@ rep i n @AT@ 746 @LENGTH@ 8\n" + - "------UPD argument_list@@ i m @TO@ i n @AT@ 749 @LENGTH@ 6\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@int i j k l m n int a ][1200 b ][1200 unused ][1200 int ind ][1200 int cnt ][1200 int res_a ][1200 res_b ][1200 res_c ][1200 res_d ][1200 int shima int res bef scanf \"%d\" & n m = n - 1 rep i m scanf \"%d%d\" a + i b + i , a ][i -- , b ][i -- unionInit ind n rep i m unionConnect ind a ][i b ][i shima = 0 rep i m cnt ][unionGet ind i = 1 rep i n shima += cnt ][i res = 0 bef = - 1 rep i n if cnt ][i if bef >= 0 res_c ][res = bef res_d ][res ++ = i bef = i res = 0 rep k m unused ][k = 0 rep k m unionInit ind n rep i m if unused ][i == 0 if i != k unionConnect ind a ][i b ][i rep i n cnt ][i = 0 rep i n cnt ][unionGet ind i = 1 j = 0 rep i n j += cnt ][i if j == shima unused ][k = 1 res_a ][res = a ][k res_b ][res ++ = b ][k printf \"%d\\n\" res rep i res printf \"%d %d %d %d\\n\" res_a ][i + 1 res_b ][i + 1 res_c ][i + 1 res_d ][i + 1 return 0 @TO@ int i j k l m n int a ][1200 b ][1200 unused ][1200 int ind ][1200 int cnt ][1200 int res_a ][1200 res_b ][1200 res_c ][1200 res_d ][1200 int shima int res bef scanf \"%d\" & n m = n - 1 rep i m scanf \"%d%d\" a + i b + i , a ][i -- , b ][i -- unionInit ind n rep i m unionConnect ind a ][i b ][i shima = 0 rep i n cnt ][unionGet ind i = 1 rep i n shima += cnt ][i res = 0 bef = - 1 rep i n if cnt ][i if bef >= 0 res_c ][res = bef res_d ][res ++ = i bef = i res = 0 rep k m unused ][k = 0 rep k m unionInit ind n rep i m if unused ][i == 0 if i != k unionConnect ind a ][i b ][i rep i n cnt ][i = 0 rep i n cnt ][unionGet ind i = 1 j = 0 rep i n j += cnt ][i if j == shima unused ][k = 1 res_a ][res = a ][k res_b ][res ++ = b ][k printf \"%d\\n\" res rep i res printf \"%d %d %d %d\\n\" res_a ][i + 1 res_b ][i + 1 res_c ][i + 1 res_d ][i + 1 return 0 @AT@ 411 @LENGTH@ 843\n" + + "---UPD macro@@rep i m @TO@ rep i n @AT@ 746 @LENGTH@ 7\n" + + "------UPD argument_list@@i m @TO@ i n @AT@ 749 @LENGTH@ 3\n" + "---------UPD argument@@m @TO@ n @AT@ 752 @LENGTH@ 1\n"); } @@ -515,10 +516,10 @@ public class TestRealCases { //TODO List hierarchicalActionSets = getHierarchicalActionSets("490-A-14580360-14580456.c"); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ int n i j k l scanf \"%d\" & n int a ][5005 b ][5005 c ][5005 d ][5005 int w 0 x 0 y 0 for i = 1 , j = 1 , k = 1 , l = 1 i <= n i ++ scanf \"%d\" & a ][i if a ][i == 1 b ][j = i w ++ j ++ elseif if a ][i == 2 c ][k = i x ++ k ++ elseif if a ][i == 3 d ][l = i y ++ l ++ int min w if x < min min = x elseif if y < min min = y printf \"%d\\n\" min for i = 1 i <= min i ++ printf \"%d %d %d\\n\" b ][i c ][i d ][i 0 @TO@ int n i j k l scanf \"%d\" & n int a ][5005 b ][5005 c ][5005 d ][5005 int w 0 x 0 y 0 for i = 1 , j = 1 , k = 1 , l = 1 i <= n i ++ scanf \"%d\" & a ][i if a ][i == 1 b ][j = i w ++ j ++ elseif if a ][i == 2 c ][k = i x ++ k ++ elseif if a ][i == 3 d ][l = i y ++ l ++ int min w if x < min min = x if y < min min = y printf \"%d\\n\" min for i = 1 i <= min i ++ printf \"%d %d %d\\n\" b ][i c ][i d ][i 0 @AT@ 51 @LENGTH@ 642\n" + - "---UPD if@@if x < min min = x elseif if y < min min = y @TO@ if x < min min = x @AT@ 507 @LENGTH@ 46\n" + - "------DEL elseif@@elseif if y < min min = y @AT@ 548 @LENGTH@ 26\n" + - "---MOV if@@if y < min min = y @TO@ block@@ int n i j k l scanf \"%d\" & n int a ][5005 b ][5005 c ][5005 d ][5005 int w 0 x 0 y 0 for i = 1 , j = 1 , k = 1 , l = 1 i <= n i ++ scanf \"%d\" & a ][i if a ][i == 1 b ][j = i w ++ j ++ elseif if a ][i == 2 c ][k = i x ++ k ++ elseif if a ][i == 3 d ][l = i y ++ l ++ int min w if x < min min = x elseif if y < min min = y printf \"%d\\n\" min for i = 1 i <= min i ++ printf \"%d %d %d\\n\" b ][i c ][i d ][i 0 @AT@ 548 @LENGTH@ 19\n"); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@int n i j k l scanf \"%d\" & n int a ][5005 b ][5005 c ][5005 d ][5005 int w 0 x 0 y 0 for i = 1 , j = 1 , k = 1 , l = 1 i <= n i ++ scanf \"%d\" & a ][i if a ][i == 1 b ][j = i w ++ j ++ elseif if a ][i == 2 c ][k = i x ++ k ++ elseif if a ][i == 3 d ][l = i y ++ l ++ int min w if x < min min = x elseif if y < min min = y printf \"%d\\n\" min for i = 1 i <= min i ++ printf \"%d %d %d\\n\" b ][i c ][i d ][i return 0 @TO@ int n i j k l scanf \"%d\" & n int a ][5005 b ][5005 c ][5005 d ][5005 int w 0 x 0 y 0 for i = 1 , j = 1 , k = 1 , l = 1 i <= n i ++ scanf \"%d\" & a ][i if a ][i == 1 b ][j = i w ++ j ++ elseif if a ][i == 2 c ][k = i x ++ k ++ elseif if a ][i == 3 d ][l = i y ++ l ++ int min w if x < min min = x if y < min min = y printf \"%d\\n\" min for i = 1 i <= min i ++ printf \"%d %d %d\\n\" b ][i c ][i d ][i return 0 @AT@ 51 @LENGTH@ 409\n" + + "---UPD if@@if x < min min = x elseif if y < min min = y @TO@ if x < min min = x @AT@ 507 @LENGTH@ 44\n" + + "------DEL elseif@@elseif if y < min min = y @AT@ 548 @LENGTH@ 25\n" + + "---MOV if@@if y < min min = y @TO@ block@@int n i j k l scanf \"%d\" & n int a ][5005 b ][5005 c ][5005 d ][5005 int w 0 x 0 y 0 for i = 1 , j = 1 , k = 1 , l = 1 i <= n i ++ scanf \"%d\" & a ][i if a ][i == 1 b ][j = i w ++ j ++ elseif if a ][i == 2 c ][k = i x ++ k ++ elseif if a ][i == 3 d ][l = i y ++ l ++ int min w if x < min min = x elseif if y < min min = y printf \"%d\\n\" min for i = 1 i <= min i ++ printf \"%d %d %d\\n\" b ][i c ][i d ][i return 0 @AT@ 548 @LENGTH@ 18\n"); } @Test @@ -526,15 +527,15 @@ public class TestRealCases { //TODO List hierarchicalActionSets = getHierarchicalActionSets("336-A-11394760-11394769.c"); Assert.assertEquals(hierarchicalActionSets.size(), 1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(), "UPD block@@ ll x y ll zero 0 scanf \"%lld%lld\" & x & y if x >= 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" zero x + y x + y zero if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) 0 @TO@ ll x y ll zero 0 scanf \"%lld%lld\" & x & y if x >= 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" zero x + y x + y zero elseif if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) 0 @AT@ 72 @LENGTH@ 493\n" + - "---DEL if@@if x >= 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" zero x + y x + y zero @AT@ 146 @LENGTH@ 74\n" + - "---UPD if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) @TO@ if x >= 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" zero x + y x + y zero elseif if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) @AT@ 245 @LENGTH@ 246\n" + - "------MOV condition@@ x >= 0 && y >= 0 @TO@ if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) @AT@ 146 @LENGTH@ 13\n" + - "------MOV then@@ printf \"%lld %lld %lld %lld\\n\" zero x + y x + y zero @TO@ if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) @AT@ 165 @LENGTH@ 54\n" + - "------INS elseif@@elseif if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @TO@ if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) @AT@ 250 @LENGTH@ 90\n" + - "---------INS if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @TO@ elseif@@elseif if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @AT@ 250 @LENGTH@ 83\n" + - "------------MOV condition@@ x < 0 && y >= 0 @TO@ if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @AT@ 245 @LENGTH@ 12\n" + - "------------MOV then@@ printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @TO@ if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @AT@ 263 @LENGTH@ 64\n"); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(), "UPD block@@ll x y ll zero 0 scanf \"%lld%lld\" & x & y if x >= 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" zero x + y x + y zero if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) return 0 @TO@ ll x y ll zero 0 scanf \"%lld%lld\" & x & y if x >= 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" zero x + y x + y zero elseif if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) return 0 @AT@ 72 @LENGTH@ 364\n" + + "---DEL if@@if x >= 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" zero x + y x + y zero @AT@ 146 @LENGTH@ 72\n" + + "---UPD if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) @TO@ if x >= 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" zero x + y x + y zero elseif if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) @AT@ 245 @LENGTH@ 240\n" + + "------MOV condition@@x >= 0 && y >= 0 @TO@ if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) @AT@ 146 @LENGTH@ 16\n" + + "------MOV then@@printf \"%lld %lld %lld %lld\\n\" zero x + y x + y zero @TO@ if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) @AT@ 165 @LENGTH@ 52\n" + + "------INS elseif@@elseif if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @TO@ if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y elseif if x >= 0 && y < 0 printf \"%lld %lld %lld %lld\\n\" zero - ( x - y ) x - y zero else printf \"%lld %lld %lld %lld\\n\" - ( - x - y ) zero zero - ( - x - y ) @AT@ 250 @LENGTH@ 88\n" + + "---------INS if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @TO@ elseif@@elseif if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @AT@ 250 @LENGTH@ 81\n" + + "------------MOV condition@@x < 0 && y >= 0 @TO@ if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @AT@ 245 @LENGTH@ 15\n" + + "------------MOV then@@printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @TO@ if@@if x < 0 && y >= 0 printf \"%lld %lld %lld %lld\\n\" - ( - x + y ) zero zero - x + y @AT@ 263 @LENGTH@ 62\n"); } @@ -544,7 +545,7 @@ public class TestRealCases { //TODO List hierarchicalActionSets = getHierarchicalActionSets("10-A-1998522-1998523.c"); Assert.assertEquals(hierarchicalActionSets.size(), 1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(), "INS expr_stmt@@lr = r @TO@ block@@ scanf \"%d %d\" & l & r e += ( r - l ) * p1 if l - lr <= t1 e += ( l - lr ) * p1 else e += t1 * p1 if l - lr <= t1 + t2 e += ( l - lr - t1 ) * p2 else e += t2 * p2 + ( l - lr - t1 - t2 ) * p3 @AT@ 465 @LENGTH@ 6\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(), "INS expr_stmt@@lr = r @TO@ block@@scanf \"%d %d\" & l & r e += ( r - l ) * p1 if l - lr <= t1 e += ( l - lr ) * p1 else e += t1 * p1 if l - lr <= t1 + t2 e += ( l - lr - t1 ) * p2 else e += t2 * p2 + ( l - lr - t1 - t2 ) * p3 @AT@ 465 @LENGTH@ 6\n" + "---INS expr@@lr = r @TO@ expr_stmt@@lr = r @AT@ 465 @LENGTH@ 6\n" + "------INS name@@lr @TO@ expr@@lr = r @AT@ 465 @LENGTH@ 2\n" + "------INS operator@@= @TO@ expr@@lr = r @AT@ 468 @LENGTH@ 1\n" + @@ -555,8 +556,8 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("328-B-4080800-4080805.c"); Assert.assertEquals(hierarchicalActionSets.size(), 1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(), "DEL for@@for i = 0 i < 10 i ++ printf \"%d %d\\n\" num ][i tnum ][i @AT@ 453 @LENGTH@ 57\n" + - "---DEL control@@ i = 0 i < 10 i ++ @AT@ 453 @LENGTH@ 15\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(), "DEL for@@for i = 0 i < 10 i ++ printf \"%d %d\\n\" num ][i tnum ][i @AT@ 453 @LENGTH@ 55\n" + + "---DEL control@@i = 0 i < 10 i ++ @AT@ 453 @LENGTH@ 17\n" + "------DEL init@@i = 0 @AT@ 454 @LENGTH@ 5\n" + "---------DEL expr@@i = 0 @AT@ 454 @LENGTH@ 5\n" + "------------DEL name@@i @AT@ 454 @LENGTH@ 1\n" + @@ -571,12 +572,12 @@ public class TestRealCases { "---------DEL expr@@i ++ @AT@ 463 @LENGTH@ 4\n" + "------------DEL name@@i @AT@ 463 @LENGTH@ 1\n" + "------------DEL operator@@++ @AT@ 464 @LENGTH@ 2\n" + - "---DEL block@@ printf \"%d %d\\n\" num ][i tnum ][i @AT@ 467 @LENGTH@ 50\n" + - "------DEL expr_stmt@@printf \"%d %d\\n\" num ][i tnum ][i @AT@ 477 @LENGTH@ 34\n" + - "---------DEL expr@@printf \"%d %d\\n\" num ][i tnum ][i @AT@ 477 @LENGTH@ 34\n" + - "------------DEL call@@printf \"%d %d\\n\" num ][i tnum ][i @AT@ 477 @LENGTH@ 34\n" + + "---DEL block@@printf \"%d %d\\n\" num ][i tnum ][i @AT@ 467 @LENGTH@ 33\n" + + "------DEL expr_stmt@@printf \"%d %d\\n\" num ][i tnum ][i @AT@ 477 @LENGTH@ 33\n" + + "---------DEL expr@@printf \"%d %d\\n\" num ][i tnum ][i @AT@ 477 @LENGTH@ 33\n" + + "------------DEL call@@printf \"%d %d\\n\" num ][i tnum ][i @AT@ 477 @LENGTH@ 33\n" + "---------------DEL name@@printf @AT@ 477 @LENGTH@ 6\n" + - "---------------DEL argument_list@@ \"%d %d\\n\" num ][i tnum ][i @AT@ 483 @LENGTH@ 27\n" + + "---------------DEL argument_list@@\"%d %d\\n\" num ][i tnum ][i @AT@ 483 @LENGTH@ 26\n" + "------------------DEL argument@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" + "---------------------DEL expr@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" + "------------------------DEL literal@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" + @@ -601,11 +602,11 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("10-A-2106391-2106405.c"); Assert.assertEquals(hierarchicalActionSets.size(), 1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ @TO@ scanf \"%d %d\" & c & e d = c - b if d >= t1 p += t1 * p1 d -= t1 elseif if d > 0 p += d * p1 d = 0 if d >= t2 p += t2 * p2 d -= t2 elseif if d > 0 p += d * p2 d = 0 if d > 0 p += d * p3 p += p1 * ( e - c ) a = c b = e @AT@ 293 @LENGTH@ 0\n" + - "---MOV expr_stmt@@scanf \"%d %d\" & c & e @TO@ block@@ @AT@ 309 @LENGTH@ 22\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ @TO@ scanf \"%d %d\" & c & e d = c - b if d >= t1 p += t1 * p1 d -= t1 elseif if d > 0 p += d * p1 d = 0 if d >= t2 p += t2 * p2 d -= t2 elseif if d > 0 p += d * p2 d = 0 if d > 0 p += d * p3 p += p1 * ( e - c ) a = c b = e @AT@ 295 @LENGTH@ 0\n" + + "---MOV expr_stmt@@scanf \"%d %d\" & c & e @TO@ block@@ @AT@ 309 @LENGTH@ 21\n" + "---MOV expr_stmt@@d = c - b @TO@ block@@ @AT@ 347 @LENGTH@ 9\n" + - "---MOV if@@if d >= t1 p += t1 * p1 d -= t1 elseif if d > 0 p += d * p1 d = 0 @TO@ block@@ @AT@ 372 @LENGTH@ 67\n" + - "---MOV if@@if d >= t2 p += t2 * p2 d -= t2 elseif if d > 0 p += d * p2 d = 0 @TO@ block@@ @AT@ 618 @LENGTH@ 67\n" + + "---MOV if@@if d >= t1 p += t1 * p1 d -= t1 elseif if d > 0 p += d * p1 d = 0 @TO@ block@@ @AT@ 372 @LENGTH@ 65\n" + + "---MOV if@@if d >= t2 p += t2 * p2 d -= t2 elseif if d > 0 p += d * p2 d = 0 @TO@ block@@ @AT@ 618 @LENGTH@ 65\n" + "---MOV if@@if d > 0 p += d * p3 @TO@ block@@ @AT@ 857 @LENGTH@ 20\n" + "---MOV expr_stmt@@p += p1 * ( e - c ) @TO@ block@@ @AT@ 888 @LENGTH@ 19\n" + "---MOV expr_stmt@@a = c @TO@ block@@ @AT@ 917 @LENGTH@ 5\n" + @@ -618,8 +619,8 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("10-A-4557108-4561236.c"); Assert.assertEquals(hierarchicalActionSets.size(), 1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if m > t1 && m < t2 p += p2 * ( m - t1 ) @TO@ if m > t1 && m - t1 < t2 p += p2 * ( m - t1 ) @AT@ 502 @LENGTH@ 41\n" + - "---UPD condition@@ m > t1 && m < t2 @TO@ m > t1 && m - t1 < t2 @AT@ 502 @LENGTH@ 15\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if m > t1 && m < t2 p += p2 * ( m - t1 ) @TO@ if m > t1 && m - t1 < t2 p += p2 * ( m - t1 ) @AT@ 502 @LENGTH@ 40\n" + + "---UPD condition@@m > t1 && m < t2 @TO@ m > t1 && m - t1 < t2 @AT@ 502 @LENGTH@ 16\n" + "------UPD expr@@m > t1 && m < t2 @TO@ m > t1 && m - t1 < t2 @AT@ 503 @LENGTH@ 16\n" + "---------INS operator@@- @TO@ expr@@m > t1 && m < t2 @AT@ 512 @LENGTH@ 1\n" + "---------INS name@@t1 @TO@ expr@@m > t1 && m < t2 @AT@ 513 @LENGTH@ 2\n"); @@ -654,23 +655,23 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("10-D-1434543-1434549.c"); Assert.assertEquals(hierarchicalActionSets.size(), 1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"INS if@@if max == 0 printf \"0\\n\" continue; @TO@ block@@ for i = 0 i < la i ++ scanf \"%d\" & a ][i scanf \"%d\" & lb for i = 0 i < lb i ++ scanf \"%d\" & b ][i memset f 0 f for i = 0 i <= 500 i ++ p ][i = - 1 for i = 1 i <= la i ++ k = 0 for j = 1 j <= lb j ++ if b ][j - 1 < a ][i - 1 && f ][j > f ][k k = j if a ][i - 1 == b ][j - 1 && f ][k >= f ][j f ][j = f ][k + 1 p ][j = k max = 0 int t 1 for i = 1 i <= lb i ++ if max < f ][i max = f ][i t = i int k 0 int d ][501 d ][++ k = b ][t - 1 while 1 t = p ][t if t == 0 break; d ][++ k = b ][t - 1 printf \"%d\\n\" k for i = k i > 1 i -- printf \"%d \" d ][i printf \"%d\\n\" d ][1 @AT@ 692 @LENGTH@ 36\n" + - "---INS condition@@ max == 0 @TO@ if@@if max == 0 printf \"0\\n\" continue; @AT@ 692 @LENGTH@ 9\n" + - "------INS expr@@max == 0 @TO@ condition@@ max == 0 @AT@ 693 @LENGTH@ 8\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"INS if@@if max == 0 printf \"0\\n\" continue; @TO@ block@@for i = 0 i < la i ++ scanf \"%d\" & a ][i scanf \"%d\" & lb for i = 0 i < lb i ++ scanf \"%d\" & b ][i memset f 0 f for i = 0 i <= 500 i ++ p ][i = - 1 for i = 1 i <= la i ++ k = 0 for j = 1 j <= lb j ++ if b ][j - 1 < a ][i - 1 && f ][j > f ][k k = j if a ][i - 1 == b ][j - 1 && f ][k >= f ][j f ][j = f ][k + 1 p ][j = k max = 0 int t 1 for i = 1 i <= lb i ++ if max < f ][i max = f ][i t = i int k 0 int d ][501 d ][++ k = b ][t - 1 while 1 t = p ][t if t == 0 break; d ][++ k = b ][t - 1 printf \"%d\\n\" k for i = k i > 1 i -- printf \"%d \" d ][i printf \"%d\\n\" d ][1 @AT@ 692 @LENGTH@ 34\n" + + "---INS condition@@max == 0 @TO@ if@@if max == 0 printf \"0\\n\" continue; @AT@ 692 @LENGTH@ 8\n" + + "------INS expr@@max == 0 @TO@ condition@@max == 0 @AT@ 693 @LENGTH@ 8\n" + "---------INS name@@max @TO@ expr@@max == 0 @AT@ 693 @LENGTH@ 3\n" + "---------INS operator@@== @TO@ expr@@max == 0 @AT@ 696 @LENGTH@ 2\n" + "---------INS literal@@0 @TO@ expr@@max == 0 @AT@ 698 @LENGTH@ 1\n" + - "---INS then@@ printf \"0\\n\" continue; @TO@ if@@if max == 0 printf \"0\\n\" continue; @AT@ 702 @LENGTH@ 24\n" + - "------INS block@@ printf \"0\\n\" continue; @TO@ then@@ printf \"0\\n\" continue; @AT@ 702 @LENGTH@ 26\n" + - "---------INS expr_stmt@@printf \"0\\n\" @TO@ block@@ printf \"0\\n\" continue; @AT@ 703 @LENGTH@ 13\n" + - "------------INS expr@@printf \"0\\n\" @TO@ expr_stmt@@printf \"0\\n\" @AT@ 703 @LENGTH@ 13\n" + - "---------------INS call@@printf \"0\\n\" @TO@ expr@@printf \"0\\n\" @AT@ 703 @LENGTH@ 13\n" + - "------------------INS name@@printf @TO@ call@@printf \"0\\n\" @AT@ 703 @LENGTH@ 6\n" + - "------------------INS argument_list@@ \"0\\n\" @TO@ call@@printf \"0\\n\" @AT@ 709 @LENGTH@ 8\n" + - "---------------------INS argument@@\"0\\n\" @TO@ argument_list@@ \"0\\n\" @AT@ 710 @LENGTH@ 5\n" + + "---INS then@@printf \"0\\n\" continue; @TO@ if@@if max == 0 printf \"0\\n\" continue; @AT@ 702 @LENGTH@ 22\n" + + "------INS block@@printf \"0\\n\" continue; @TO@ then@@printf \"0\\n\" continue; @AT@ 702 @LENGTH@ 22\n" + + "---------INS expr_stmt@@printf \"0\\n\" @TO@ block@@printf \"0\\n\" continue; @AT@ 703 @LENGTH@ 12\n" + + "------------INS expr@@printf \"0\\n\" @TO@ expr_stmt@@printf \"0\\n\" @AT@ 703 @LENGTH@ 12\n" + + "---------------INS call@@printf \"0\\n\" @TO@ expr@@printf \"0\\n\" @AT@ 703 @LENGTH@ 12\n" + + "------------------INS name@@printf @TO@ call@@printf \"0\\n\" @AT@ 703 @LENGTH@ 6\n" + + "------------------INS argument_list@@\"0\\n\" @TO@ call@@printf \"0\\n\" @AT@ 709 @LENGTH@ 5\n" + + "---------------------INS argument@@\"0\\n\" @TO@ argument_list@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" + "------------------------INS expr@@\"0\\n\" @TO@ argument@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" + "---------------------------INS literal@@\"0\\n\" @TO@ expr@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" + - "---------INS continue@@continue; @TO@ block@@ printf \"0\\n\" continue; @AT@ 717 @LENGTH@ 9\n"); + "---------INS continue@@continue; @TO@ block@@printf \"0\\n\" continue; @AT@ 717 @LENGTH@ 9\n"); } //101-A-3317973-3317996 @@ -680,7 +681,7 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("101-A-3317973-3317996.c"); Assert.assertEquals(hierarchicalActionSets.size(), 1); Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if sum >= k break; @TO@ if sum > k break; @AT@ 858 @LENGTH@ 18\n" + - "---UPD condition@@ sum >= k @TO@ sum > k @AT@ 858 @LENGTH@ 9\n" + + "---UPD condition@@sum >= k @TO@ sum > k @AT@ 858 @LENGTH@ 8\n" + "------UPD expr@@sum >= k @TO@ sum > k @AT@ 859 @LENGTH@ 8\n" + "---------UPD operator@@>= @TO@ > @AT@ 862 @LENGTH@ 2\n"); HierarchicalActionSet actionSet = hierarchicalActionSets.get(0); @@ -695,8 +696,8 @@ public class TestRealCases { List hierarchicalActionSets = getHierarchicalActionSets("102-A-14574020-14574054.c"); Assert.assertEquals(hierarchicalActionSets.size(), 1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if min printf \"%ld \\n\" min else printf \"%d\" - 1 @TO@ if min != LONG_MAX printf \"%ld \\n\" min else printf \"%d\" - 1 @AT@ 1082 @LENGTH@ 49\n" + - "---UPD condition@@ min @TO@ min != LONG_MAX @AT@ 1082 @LENGTH@ 8\n" + + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if min printf \"%ld \\n\" min else printf \"%d\" - 1 @TO@ if min != LONG_MAX printf \"%ld \\n\" min else printf \"%d\" - 1 @AT@ 1082 @LENGTH@ 47\n" + + "---UPD condition@@min @TO@ min != LONG_MAX @AT@ 1082 @LENGTH@ 3\n" + "------UPD expr@@min @TO@ min != LONG_MAX @AT@ 1084 @LENGTH@ 3\n" + "---------INS operator@@!= @TO@ expr@@min @AT@ 1087 @LENGTH@ 2\n" + "---------INS name@@LONG_MAX @TO@ expr@@min @AT@ 1089 @LENGTH@ 8\n"); @@ -726,18 +727,30 @@ public class TestRealCases { Properties appProps = new Properties(); appProps.load(new FileInputStream("src/main/resource/app.properties")); String srcMLPath = appProps.getProperty("srcMLPath", "FORKJOIN"); - String root = appProps.getProperty("inputPath"); +// String root = appProps.getProperty("inputPath"); + String root = "src/main/resource/testFiles"; root = root + "/codeflaws/"; String filename = s; + try{ + File revFile = new File(root + "revFiles/" + filename); + File prevFile = new File(root + "prevFiles/prev_" + filename); - File revFile = new File(root + "revFiles/" + filename); - File prevFile = new File(root + "prevFiles/prev_" + filename); - - EDiffHunkParser parser = new EDiffHunkParser(); + EDiffHunkParser parser = new EDiffHunkParser(); - List hierarchicalActionSets = parser.parseChangedSourceCodeWithGumTree2(prevFile, revFile, srcMLPath,false); - return hierarchicalActionSets; + List hierarchicalActionSets = parser.parseChangedSourceCodeWithGumTree2(prevFile, revFile, srcMLPath,false); + return hierarchicalActionSets; + }catch (NullPointerException n){ + String cmd = "cp /Users/anil.koyuncu/projects/test/fixminer-data/patches/codeflaws/"+n.getMessage().split(root)[1] + " /Users/anil.koyuncu/projects/test/fixminerC/"+n.getMessage(); + CallShell cs = new CallShell(); + try { + cs.runShell(cmd); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + } diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-1998522-1998523.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-1998522-1998523.c new file mode 100644 index 0000000..92b088b --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-1998522-1998523.c @@ -0,0 +1,32 @@ +#include + +int main(int argc, char *argv[]) +{ + int p1, p2, p3, t1, t2, n, lr, l, r, e = 0; + + scanf("%d %d %d %d %d %d", &n, &p1, &p2, &p3, &t1, &t2); + + scanf("%d %d", &l, &r); + e += (r - l) * p1; + lr = r; + + while(-- n) + { + scanf("%d %d", &l, &r); + e += (r - l) * p1; + if(l - lr <= t1) + e += (l - lr) * p1; + else + { + e += t1 * p1; + if(l - lr <= t1 + t2) + e += (l - lr - t1) * p2; + else + e += t2 * p2 + (l - lr - t1 - t2) * p3; + } + } + + printf("%d\n", e); + + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-2106391-2106405.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-2106391-2106405.c new file mode 100644 index 0000000..5bb2cad --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-2106391-2106405.c @@ -0,0 +1,34 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + int n,p1,p2,p3,t1,t2,a,c,b,e,i,d=0; + long int p=0; + scanf("%d%d%d%d%d%d",&n,&p1,&p2,&p3,&t1,&t2); + scanf("%d %d",&a,&b); + if(n==1) p=(b-a)*p1; + else { p=(b-a)*p1; + for(i=1;i=t1) {p+=t1*p1; + d-=t1; + } + else if(d>0) {p+=d*p1; + d=0; + } + if(d>=t2) {p+=t2*p2; + d-=t2; + } + else if(d>0) {p+=d*p2; + d=0; + } + if(d>0) p+=d*p3; + p+=p1*(e-c); + a=c; + b=e; + } + printf("%ld",p); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-4557108-4561236.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-4557108-4561236.c new file mode 100644 index 0000000..9d06487 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-4557108-4561236.c @@ -0,0 +1,30 @@ +#include + +int i,n,m,p1,p2,p3,t1,t2,l,r,p,s,t,ar[2][101]; + +int main(int argc, char *argv[]){ + scanf("%d%d%d%d%d%d",&n,&p1,&p2,&p3,&t1,&t2); + + for (i=0; i=i+1 && ar[0][i+1]-ar[1][i]>0){ + m=ar[0][i+1]-ar[1][i]; + if (m>=t1){p+=p1*t1;} else {p+=p1*m;} + if (m-t1>=t2){p+=p2*t2;} else if (m>t1 && mt2){p+=p3*(m-(t2+t1));} + } + t=ar[1][i]; + i++; + } + + printf("%d",p); + return(0); +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-5914564-5914663.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-5914564-5914663.c new file mode 100644 index 0000000..3a3d998 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_10-A-5914564-5914663.c @@ -0,0 +1,29 @@ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int n,p1,p2,p3,t1,t2,a,b,i,lr=0,power=0; + scanf("%d %d %d %d %d %d",&n,&p1,&p2,&p3,&t1,&t2); + scanf("%d %d",&a,&b); + power+=(b-a)*p1; + lr=b; + for(i=1;i +#include +int main(int argc, char *argv[]) +{ +int a[505],b[505],la,lb,f[505],p[505]; +int i,j,n,k,max; + + + + while + (scanf("%d",&la)!=EOF) + { + for(i=0;if[k])//b[j-1]f[k]是存放到 j时,jå‰é¢çš„æœ€é•¿çš„å­åºåˆ— + k=j; + if(a[i-1]==b[j-1]&&f[k]>=f[j])//逿­¥ä¼˜åŒ–ï¼Œè¦æŽŒæ¡ + {f[j]=f[k]+1; + p[j]=k; + } + } + } + max=0; + int t=1; + for(i=1;i<=lb;i++) + { + if(max1;i--) +printf("%d ",d[i]); + printf("%d\n",d[1]); + + + } + +return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_101-A-3317973-3317996.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_101-A-3317973-3317996.c new file mode 100644 index 0000000..c4b7e67 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_101-A-3317973-3317996.c @@ -0,0 +1,79 @@ +#include +#include +char arr[100005]; +int k; +struct string +{ +int index[100005]; +int character; +}e[251]; +static int age_comp(const void *p1, const void *p2) +{ + struct string *sp1 = (struct string *) p1; + struct string *sp2 = (struct string *) p2; + + return sp1->character - sp2->character; +} + +void solve(); +int main(int argc, char *argv[]) +{ +scanf("%s",arr); +scanf("%d",&k); +solve(); +return 0; +} +void solve() +{ +int d,l,i,sum=0,j,m,dst=0,t=0; +l=strlen(arr); + +for(i=0;i<=250;i++) +e[i].character=0; +if(k>=l) +{ +printf("0\n"); +printf("\0"); +} +else +{ +for(i=0;i=k) +break; +t++; +for(j=0;j +#include +#define MAX 101 +// + +int main(int argc, char *argv[]){ + int m , n; + int i , j , k ; + int x , y; //temp + long min = LONG_MAX ; + scanf("%d %d",&n,&m); + int pairs[MAX][MAX] = {0} ; //bool array + int clothesPrices[n]; + + // clothesPrices[m] inputs.. + for( i = 0 ; i < n ; i++){ + scanf("%d",&clothesPrices[i]); + } + + // pairs inputs + for ( i = 0 ; i < m ; i++ ){ + scanf("%d %d",&x,&y); + pairs[x][y] = pairs[y][x] = 1; + } + + // check if there are three clothing items that match each other .. + for ( i = 1 ; i < n ; i++){ + for ( j = i + 1 ; j <= n ; j++){ + for ( k = j + 1 ; k <= n ; k++ ){ + if ( pairs[i][j] && pairs[j][k] && pairs[k][i] ){ + if ( clothesPrices[i-1]+clothesPrices[j-1]+clothesPrices[k-1] < min ){ + min = clothesPrices[i-1]+clothesPrices[j-1]+clothesPrices[k-1]; + //printf("%ld",min); + } + } + } + } + } + if ( min ) + printf("%ld \n",min); + else printf("%d",-1); + + return 0; + } \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_102-A-9556179-9556185.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_102-A-9556179-9556185.c new file mode 100644 index 0000000..66d2d74 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_102-A-9556179-9556185.c @@ -0,0 +1,34 @@ +#include +int a[100005]; +int b[105][105]; +int n,i,j,k,x,ans,m; +int main(int argc, char *argv[]) +{ +int hi ; +ans = 40000000; +int n; +scanf("%d%d",&n,&m); +for(i=1;i<=n;i++) +scanf("%d",&a[i]); +int x,y; +for(i=0;i +int main(int argc, char *argv[]) +{ + int i, flag=0, r1, r2, c1, c2, d1, d2, a, b, c, d, ar[1000]; + scanf("%d %d %d %d %d %d", &r1, &r2, &c1, &c2, &d1, &d2); + for(a=1; a<(r1%10); a++) + { + //printf("a=%d ", a); + for(i=1; i<=1000; i++) + ar[i]=0; + ar[a]=1; + if(a>=c1 || a>=d1) + { + //printf("continuing when a=%d\n", a); + continue; + } + b=r1-a; + if(ar[b]==1) + continue; + else + ar[b]=1; + if(b>=c2 || b>=d2) + { + continue; + } + c=c1-a; + if(ar[c]==1) + continue; + else + ar[c]=1; + if(c>=r2 || c>=d2) + { + continue; + } + d=d1-a; + if(ar[d]==1) + continue; + if(d>=r2 || d>=c2) + { + continue; + } + if(b+c!=d2) + continue; + if(b+d!=c2) + continue; + if(c+d!=r2) + continue; + if(a>9 || b>9 || c>9 || d>9) + continue; + flag=1; + break; + //} + } + if(flag==1) + printf("%d %d\n%d %d\n", a, b, c, d); + else + printf("-1\n"); + return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_158-A-18237828-18237840.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_158-A-18237828-18237840.c new file mode 100644 index 0000000..f21b9b5 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_158-A-18237828-18237840.c @@ -0,0 +1,19 @@ +#include + +int main(int argc, char *argv[]) +{ + int n,k,x,i,count=0; + scanf("%d %d",&n,&k); + int ara[n]; + for(i=0;i=ara[k-1]) + count++; + } + printf("%d",count); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_158-A-18278572-18278586.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_158-A-18278572-18278586.c new file mode 100644 index 0000000..cb0cba0 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_158-A-18278572-18278586.c @@ -0,0 +1,37 @@ +#include +#include +/* +int main(int argc, char *argv[]) +{ int n,k,l=0,i,a[50]; + scanf("%d %d",&n,&k); + for(i=1;i<=n;i++) + scanf("%d",&a[i]); + if(a[1]!=0){ + l=k; + while(l0){ + i=1; + while(a[i]>=a[k]&&i<=n){ + count++; + i++; + }} +printf("%d\n",count); +return 0;} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_158-E-1314159-1314160.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_158-E-1314159-1314160.c new file mode 100644 index 0000000..d4709bb --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_158-E-1314159-1314160.c @@ -0,0 +1,21 @@ +#include "stdio.h" + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) + +int dp[4005], n, k, t, d, i, ans; + +int main(int argc, char *argv[]) +{ + scanf( "%d%d", &n, &k ); + while(n--) + { + scanf( "%d%d", &t, &d ); + ans = max( ans, t - 1 - dp[k] ); + for( i = k; i >= 1; i-- ) dp[i] = min( dp[i-1], max( dp[i], t-1 ) + d ); + dp[0] = max( dp[0], t - 1 ) + d; + } + ans = max( ans, 86401 - dp[k] ); + printf( "%d\n", ans ); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_166-C-1395587-1395933.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_166-C-1395587-1395933.c new file mode 100644 index 0000000..e24a740 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_166-C-1395587-1395933.c @@ -0,0 +1,56 @@ +#include +#include + +int comp(const void *p1, const void *p2) { + int n1, n2; + n1 = *((const int *)p1); + n2 = *((const int *)p2); + return n1 - n2; +} + +int main(int argc, char *argv[]){ + int *array; + int i,j,k,n,x; + int first,last,flg,num; + scanf("%d %d",&n,&x); + array=(int *)calloc(n,sizeof(int)); + for(i=0;ix){ + first--; + break; + } + } + first++; + + for(last=n-1;last>=0;last--){ + if(array[last]==x){ + flg=1; + break; + }else if(array[last](n+1)/2) printf("%d\n",2*first-n-1); + }else{ + k=last; + if(k==(n+1)/2) printf("1\n"); + else if(k>(n+1)/2) printf("%d\n",2*k-n-1); + else printf("%d\n",n+2-2*k); + } + free(array); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_177-A2-1594730-1595168.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_177-A2-1594730-1595168.c new file mode 100644 index 0000000..5716b2f --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_177-A2-1594730-1595168.c @@ -0,0 +1,34 @@ +#include +#include + + +int main(int argc, char *argv[]) +{ +// int num[101][101]; + int i,j,k,n,sum; + int a, mid; + sum = 0; + + scanf("%d",&n); + + for(i=1;i<=n;i++) + { + for(j=1;j<=n;j++) + { + scanf("%d",&a); + if(i==j) + sum += a; + if(j==(n+1)/2) + sum += a; + if(i==(n+1)/2) + sum += a; + if(i+j == n+1) + sum += a; + if(i==(n-1)/2 && j==(n-1)/2) + mid = a; + } + } + sum -= (3*mid); + printf("%d\n",sum); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_189-B-1682083-1682218.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_189-B-1682083-1682218.c new file mode 100644 index 0000000..2ca741b --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_189-B-1682083-1682218.c @@ -0,0 +1,27 @@ +#include + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +typedef long long ll; + +void func(int w, int h) { + ll ans = 0, i, j, k, t; + for (i = 1; i < w; i ++) { + for (j = 1; j < h; j ++) { + k = i; + k = MIN(k, w - i); + t = j; + k = MIN(k, h - j); + ans += k * t; + } + } + printf("%lld\n", ans); +} + +int main(int argc, char *argv[]) { + int w, h; + while (scanf("%d%d", &w, &h) == 2) { + func(w, h); + } + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_189-B-17295034-17295064.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_189-B-17295034-17295064.c new file mode 100644 index 0000000..1ef826f --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_189-B-17295034-17295064.c @@ -0,0 +1,18 @@ +#include +#define MIN(i,j) i +int main(int argc, char *argv[]) +{ + int n, m, i, a[101], b[101]; + scanf("%d %d", &n, &m); + for(i=0; i<=n; i++) + scanf("%d", &a[i]); + for(i=0; i<=m; i++) + scanf("%d", &b[i]); + if(n>m) + { + int prod=a[0]*b[0]; + if(prod>0) + printf("Infinity\n"); + else + printf("-Infinity\n"); + } + else if(n==m) + { + //printf("a[0]=%d b[0]=%d\n", a[0], b[0]); + if((a[0]<0 && b[0]>0) || (a[0]>0 && b[0]<0)) + printf("-"); + a[0]=abs(a[0]); + b[0]=abs(b[0]); + int min=a[0]; + if(b[0] +int main(int argc, char *argv[]) +{ + int a; + while(scanf("%d",&a)!=EOF) + { + if(a<=101) + printf("%d\n",a); + else if(a==123) + printf("113\n"); + else if(a==1000) + printf("352\n"); + else if(a==1000000000) + printf("40744\n"); + else if(a==999999999) + printf("40743\n"); + else if(a==999999998) + printf("40742\n"); + else if(a==999999997) + printf("40741\n"); + else if(a==909090901) + printf("38532\n"); + else if(a==142498040) + printf("21671\n"); + else if(a==603356456) + printf("31623\n"); + else if(a==64214872) + printf("15759\n"); + else if(a==820040584) + printf("36407\n"); + else if(a==442198) + printf("3071\n"); + else if(a==642678) + printf("3615\n"); + else if(a==468390) + printf("3223\n"); + else if(a==326806) + printf("2759\n"); + else if(a==940) + printf("331\n"); + else if(a==356) + printf("175\n"); + else if(a==132) + printf("114\n"); + else if(a==102) + printf("101\n"); + } + return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_245-D-3671804-3671831.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_245-D-3671804-3671831.c new file mode 100644 index 0000000..8cbd23c --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_245-D-3671804-3671831.c @@ -0,0 +1,15 @@ +#include + +int main(int argc, char *argv[]) +{ + int n,ans,a,i,j; + scanf("%d",&n); + for (i=0;i +#include +#include +#define REP(i,a,b) for(i=a;i=0){ + res_c[res] = bef; res_d[res++] = i; + } + bef = i; + } + + res=0; + rep(k,m) unused[k]=0; + + rep(k,m){ + unionInit(ind,n); + rep(i,m) if(unused[i]==0) if(i!=k) unionConnect(ind,a[i],b[i]); + rep(i,n) cnt[i]=0; + rep(i,n) cnt[unionGet(ind,i)]=1; + j=0; rep(i,n) j+=cnt[i]; + if(j==shima){ + unused[k]=1; + res_a[res]=a[k]; res_b[res++]=b[k]; + } + } + + printf("%d\n",res); + rep(i,res) printf("%d %d %d %d\n",res_a[i]+1,res_b[i]+1,res_c[i]+1,res_d[i]+1); + + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_250-A-2762401-2762408.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_250-A-2762401-2762408.c new file mode 100644 index 0000000..400d394 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_250-A-2762401-2762408.c @@ -0,0 +1,58 @@ +#include +#include + +int main(int argc, char **argv) +{ + int i, n, data[100], counter = 0, negative = 0, folders = 0, printed = 0; + scanf("%d",&n); + for(i = 0; i < n; i++) + { + scanf("%d",&data[i]); + } + for(i = 0; i < n; i++) + { + if(data[i] < 0) + { + negative++; + } + } + folders = ceil(negative/2); + if(folders != 0) + { + printf("%d\n",folders); + } + negative = 0; + if(folders > 0) + { + for(i = 0; i < n; i++) + { + if(negative == 2 && folders > 1 && printed < folders-1) + { + printf("%d ",counter); + negative = 0; + counter = 0; + printed++; + } + counter++; + if(data[i] < 0) + { + negative++; + } + } + printed++; + if(folders == 1 || printed == folders) + { + printf("%d",counter); + } + } + else if(folders == 0) + { + printf("1\n"); + for(i = 0; i < n; i++) + { + counter++; + } + printf("%d",counter); + } + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_287-A-14208510-14208532.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_287-A-14208510-14208532.c new file mode 100644 index 0000000..093a806 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_287-A-14208510-14208532.c @@ -0,0 +1,28 @@ +#include + +int main(int argc, char *argv[]) +{ + char g[5][5]; + int i, j, d=0, h=0; + for(i=0; i<4; i++){ + gets(g[i]); + } + for(i=0; i<3; i++){ + for(j=0; j<3; j++){ + if(g[i][j]=='.' ) d++; + else h++; + if (g[i][j+1]=='.') d++; + else h++; + if(g[i+1][j]=='.') d++; + else h++; + if(g[i+1][j+1]=='.') d++; + else h++; + if(d==3 || h==3){ + printf("YES"); + return 0; + } + } + } + printf("NO"); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_287-A-14208521-14208532.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_287-A-14208521-14208532.c new file mode 100644 index 0000000..24a6f7d --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_287-A-14208521-14208532.c @@ -0,0 +1,28 @@ +#include + +int main(int argc, char *argv[]) +{ + char g[5][5]; + int i, j, d=0, h=0; + for(i=0; i<4; i++){ + gets(g[i]); + } + for(i=0; i<3; i++){ + for(j=0; j<3; j++){ + if(g[i][j]=='.' ) d++; + else h++; + if (g[i][j+1]=='.') d++; + else h++; + if(g[i+1][j]=='.') d++; + else h++; + if(g[i+1][j+1]=='.') d++; + else h++; + if(d>=3 || h>=3){ + printf("YES"); + return 0; + } + } + } + printf("NO"); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_31-B-136044-136045.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_31-B-136044-136045.c new file mode 100644 index 0000000..a3ce6d8 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_31-B-136044-136045.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#define eps 1e-8 +#define oo 1000000000 +#define pi 3.14159265359 +#define zero(a) (abb(a)<=eps) +#define lowbit(a) ((a)&(-(a))) +#define abb(a) ((a)>0?(a):(-(a))) +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) +#define cj(x1,y1,x2,y2) ((x1)*(y2)-(x2)*(y2)) +#define dj(x1,y1,x2,y2) ((x1)*(y1)+(y1)*(y2)) +#define dis(x1,y1,x2,y2) sqrt(((x2)-(x1))*((x2)-(x1))+((y2)-(y1))*((y2)-(y1))) + +char str[505]={'\0'},ans[505]={'\0'}; + +int main(int argc, char *argv[]) +{ + long i,l,flag=0,tot=0; + gets(str+1); + l=strlen(str+1); + if (str[1]=='@'||str[l]=='@') + goto end; + for (i=1;i<=l-2;i++) + if (str[i]=='@'&&(str[i+1]=='@'||str[i+2]=='@')) + goto end; + for (i=1;i<=l;i++) + { + if (flag&&str[i+1]=='@') + ans[++tot]=','; + if (str[i]=='@') + flag=1; + ans[++tot]=str[i]; + } + if (flag) + puts(ans+1); + else + printf("No solution\n"); + end: + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_31-B-6435804-6435825.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_31-B-6435804-6435825.c new file mode 100644 index 0000000..b1b3bbd --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_31-B-6435804-6435825.c @@ -0,0 +1,66 @@ +#include +#include + +int main(int argc, char *argv[]){ + char s[201], cc[255]; + int n, fl = 0, flag = 0, i, j, jj = -1, kon = 0, exit = 0; + scanf("%s", s); + if (s[0] != '@'){ + n = 0; + for (i = 1; i <= strlen(s); i++){ + if (n == -1){ + if (s[i] != '@') + n = i; + else{ + printf("No solution\n"); + return(0); + } + } + if (fl == 1){ + if (s[i] != '@'){ + fl = 0; + if (flag != 0){ + jj++; + cc[jj] = ','; + for (j = n; j <= i; j++){ + jj++; + cc[jj] = s[j]; + } + } + else{ + for (j = n; j <= i; j++){ + jj++; + cc[jj] = s[j]; + } + flag = 1; + } + n = -1; + kon = i; + exit = 1; + } + else{ + printf("No solution\n"); + return(0); + } + } + else if (s[i] == '@') + fl = 1; + } + + } + else { + printf("No solution\n"); + return(0); + } + if (exit == 0){ + printf("No solution\n"); + return(0); + } + for (i = 0; i <= jj; i++) + printf("%c", cc[i]); + if (kon != strlen(s)) + for (i = kon + 1; i < strlen(s); i++) + printf("%c", s[i]); +return(0); +} + diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_315-A-6149995-6150754.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_315-A-6149995-6150754.c new file mode 100644 index 0000000..ac73bdf --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_315-A-6149995-6150754.c @@ -0,0 +1,25 @@ +#include + +int main(int argc, char *argv[]) +{ + int n; + scanf("%d",&n); + int i,a[n],b[n],t,j,ans=n; + for(i=0;i +#include + +int min(int a,int b){ + if(a +typedef long long ll; +int main(int argc, char *argv[]) +{ + ll x,y; + ll zero = 0; + scanf("%lld%lld",&x,&y); + if(x>=0&&y>=0) + { + printf("%lld %lld %lld %lld\n",zero,x+y,x+y,zero); + } + if(x<0&&y>=0) + { + printf("%lld %lld %lld %lld\n",-(-x+y),zero,zero,-x+y); + } + else if(x>=0&&y<0) + { + printf("%lld %lld %lld %lld\n",zero,-(x-y),x-y,zero); + } + else + { + printf("%lld %lld %lld %lld\n",-(-x-y),zero,zero,-(-x-y)); + } + return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_344-A-17290259-17290309.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_344-A-17290259-17290309.c new file mode 100644 index 0000000..9f80de1 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_344-A-17290259-17290309.c @@ -0,0 +1,27 @@ +#include +long int i,n,d,f,a[100005]; +int main(int argc, char *argv[]) +{ + scanf("%d",&n); + + for(i=0;i +int main(int argc, char *argv[]) +{ + char input[3001],space; + int n,i,vert_domino,count_dot,count,i1,i2; + count=0; + vert_domino=0; + count_dot=0; + i1=-1; + scanf("%d",&n); + scanf("%c",&space); + scanf("%c",&input[0]); +// printf("%c",input[0]); + if(input[0]=='.') + count=1; +// { + //while(input[i]!='R' || input[i]!='L') + i=1; + while(i!=n) + { + scanf("%c",&input[i]); +// printf("%c input %d ",input[i],i); + if(input[i]=='.') + { + + count++; +// printf("%d count",count); + i++; + continue; + } + if(i1==-1 && input[i]=='R') + { + vert_domino=count; + i1=i; + count=0; +// printf("%d 2",vert_domino); + // i++; + } + if(i1==-1 && input[i]=='L') + { + count=0; + i1=i; +// printf("%c %d i1",input[i1],i1); + // i++; + } + if(input[i1]=='R' && input[i]=='L' && i!=i1+1) + { +// printf("Even\n"); + i2=i; + if(count%2!=0) + { + vert_domino++; + i1=i2; + // count=0; +// printf("%d 4",vert_domino); + // i++; + } + i1=i2; + count=0; + } + if(input[i1]=='R' && input[i]=='L' && i==i1+1) + i1=i; + if(input[i1]=='L' && input[i]=='R' && i!=i1+1) + { + i2=i; +// printf("%d i2",i2); + // if(count%2!=0) + // { + vert_domino+=count; +// printf("%d 5",vert_domino); +// printf("%d vert",vert_domino); + i1=i2; + count=0; + // i++; + // } + } + if(input[i1]=='L' && input[i]=='R' && i==i1+1) + i1=i; + i++; + } + if(input[i1]=='L' && count!=0) + vert_domino+=count; + if(count==n) + printf("%d\n",n); + else + printf("%d\n",vert_domino); + return 0; +} + + diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_432-A-16886797-16886828.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_432-A-16886797-16886828.c new file mode 100644 index 0000000..92b1b9a --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_432-A-16886797-16886828.c @@ -0,0 +1,11 @@ +#include +int main(int argc, char *argv[]){ + int x,y,i,z,s=0; + scanf("%d %d",&x,&y); + for(i=0;i +#include +#include +int main(int argc, char *argv[])//Aлгоритм обхода в глубину +{ + int i,j,n,m,x,i_temp=1; + scanf("%d %d",&n,&m); + if(n==0) + { + printf("0 1\n"); + printf("0 %d\n",m); + printf("0 0\n"); + printf("0 %d\n",(m-1)); + return 0; + } + else if(m==0) + { + printf("1 0\n"); + printf("%d 0\n",n); + printf("0 0\n"); + printf("%d 0\n",(n-1)); + return 0; + } + + else if((m==n)&&(n==1)) + { + printf("%d %d\n",n,m); + printf("0 0\n"); + printf("%d 0\n",n); + printf("0 %d\n",(m)); + return 0; + } + else if(m==n) + { + if(m+m*1.41f>(2*sqrt((double)(m*m+(m-1)*(m-1))))) + { + printf("%d %d\n",n,m); + printf("0 0\n"); + printf("%d 0\n",n); + printf("0 %d\n",n); + } + else + { + printf("%d %d\n",n-1,m); + printf("0 0\n"); + printf("%d %d\n",n,m); + printf("1 0\n"); + } + return 0; + + + } + else if(nm) + { + if(n+sqrt((double)n*n+m*m)<(2*sqrt((double)n*n+(m-1)*(m-1)))) + { + printf("%d %d\n",n,m-1); + printf("0 0\n"); + printf("%d %d\n",n,m); + printf("0 1\n"); + return 0; + } + else + { + printf("%d %d\n0 0 \n%d \n0 %d\n",n,m,n,m); + return 0; + } + + } +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_469-B-8248222-8248281.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_469-B-8248222-8248281.c new file mode 100644 index 0000000..45ecb50 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_469-B-8248222-8248281.c @@ -0,0 +1,36 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + +int p,q,l,r,z[50][2],x[50][2],i,j,t[1001]={0}; +int temp1,temp2,k,count=0; +scanf("%d%d%d%d",&p,&q,&l,&r); + +for(i=0;ir) + temp2=r; + if(temp1l) + for(k=temp1;k<=temp2;k++) + t[k]=1; + } + } +for(k=l;k<=r;k++) + if(t[k]) + count++; +printf("%d",count); +return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_474-A-15226851-15226912.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_474-A-15226851-15226912.c new file mode 100644 index 0000000..d628515 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_474-A-15226851-15226912.c @@ -0,0 +1,35 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + char ch,str[200]; + char s[]="qwertyuiopasdfghjkl;zxcvbnm,./"; + int i,len,j,l; + scanf("%c",&ch); + scanf("%s",str); + len=strlen(str); + l=strlen(s); + if(ch=='R'){ + for(i=0;i + +int main(int argc, char *argv[]) +{ + int a, b, c, d, e; + int ZA[3000], ZB[3000], ZC[3000]; + + scanf("%d", &a); + for (b = 0; b < a; ++b) + scanf("%d", ZA + b); + for (b = 0; b < a - 1; ++b) { + d = b; + for (c = b + 1; c < a; ++c) + if (ZA[d] < ZA[c]) + d = c; + if (d != b) { + ZB[e] = b; + ZC[e] = d; + c = ZA[d]; + ZA[d] = ZA[b]; + ZA[b] = c; + e += 1; + } + } + printf("%d\n", e); + for (c = 0; c < e; ++c) + printf("%d %d\n", ZB[c], ZC[c]); + + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_490-A-14580360-14580456.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_490-A-14580360-14580456.c new file mode 100644 index 0000000..4a48220 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_490-A-14580360-14580456.c @@ -0,0 +1,39 @@ +#include + +int main(int argc, char *argv[]) +{ + int n, i,j,k,l; + scanf("%d",&n); + int a[5005],b[5005],c[5005],d[5005]; + int w=0,x=0,y=0; + for(i=1,j=1,k=1,l=1;i<=n;i++){ + scanf("%d",&a[i]); + if(a[i]==1){ + b[j]=i; + w++; + j++; + } + else if(a[i]==2){ + c[k]=i; + x++; + k++; + } + else if(a[i]==3){ + d[l]=i; + y++; + l++; + } + } + int min=w; + if(x +#include +#define N 1001 + +void rep(int l, char c) { + int i; + for (i = 0; i < l; i ++) printf("%c", c); +} + +int main(int argc, char *argv[]) +{ + char lines[N][N]; + int l, ml=0, i, ll=0, res=0, j, sl, right=0; + + while (gets(lines[ll])) { + l = strlen(lines[ll]); + l > ml ? ml = l : 0; + ll ++; + } + + rep(ml + 2, '*'); + + printf("\n"); + + for (i = 0; i < ll; i ++) { + printf("*"); + + l = strlen(lines[i]); + + sl = (ml - l) / 2; + + if (!((ml - l) % 2)) { + if (right) sl += 1; + right = 1 - right; + } + + rep(sl, ' '); + + printf("%s", lines[i]); + + rep(ml - l - sl, ' '); + + printf("*\n"); + } + + rep(ml + 2, '*'); + + return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_507-A-16886367-16886377.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_507-A-16886367-16886377.c new file mode 100644 index 0000000..1461f10 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_507-A-16886367-16886377.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include + +#define MAX( a, b ) ( ( a > b) ? a : b ) +#define MIN( a, b ) ( ( a < b) ? a : b ) +#define FOR(ii,aa,bb) for(ii=aa;ii c[j]){ + s = c[j]; + c[j] = c[j-1]; + c[j-1] = s; + s = a[j]; + a[j] = a[j-1]; + a[j-1] = s; + } + + + FOR(i,0,n){ + top+=c[i]; + if(top>k) + break;} + + + printf("%d\n",i); + FOR(j,0,n) + printf("%d ",a[j]); + + return 0; + +} diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_612-A-15750192-15750273.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_612-A-15750192-15750273.c new file mode 100644 index 0000000..e334d62 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_612-A-15750192-15750273.c @@ -0,0 +1,88 @@ +#include +#include +int main(int argc, char *argv[]) +{ + int k,p,q,i,j,l=0,t=0,c=0; + scanf("%d%d%d",&k,&p,&q); + char a[100]; + scanf("%s",&a); + if(k==p+q) + { + + printf("2\n"); + for(i=0;i=0) + { + t=1; + break; + } + } + + if(t==1) + { + printf("%d\n",c+(k-c*p)/q); + for(i=0;i +#define ll long long +int main(int argc, char *argv[]) +{ + ll n,a,b,i,j; + scanf("%lld%lld%lld",&n,&a,&b); + if(n>a*b){ + printf("-1"); + } + else{ + ll array[101][101]; + for(i=0;i=0;j--){ + if(count<=n){ + array[i][j]=count;} + else{ + break; + } + count++; + } + i++; + sign*=-1; + } + } + + for(i=0;i +int main(int argc, char *argv[]) { + int f=1,a,b,c,i,sum=0; + scanf("%d %d %d",&a,&b,&c); + if(c==0){ + if(a==b) + printf("YES"); + else printf("NO"); +} + if((b-a)%c==0 && (b-a)/c>=0) + printf("YES"); + else + printf("NO"); + return 0; +} + + + + diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_680-A-18343132-18343191.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_680-A-18343132-18343191.c new file mode 100644 index 0000000..07a58de --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_680-A-18343132-18343191.c @@ -0,0 +1,29 @@ +#include +int main(int argc, char *argv[]) +{ + int i,a[5],b[101]={0},t=0,temp,max=0; + for(i=0;i<5;i++) + { + scanf("%d",&a[i]); +b[a[i]]++; +t+=a[i]; +} + for(i=0;i<5;i++) + {if(b[a[i]]==3) + { + max=3*a[i]; + break;} +} + for(i=0;i<5;i++) + {if(b[a[i]]==2) + {temp=a[i]*2; + if(temp>max) + { + max=temp; + break; + } + } +} +printf("%d",t-max); +return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/revFiles/10-A-1998522-1998523.c b/src/main/resource/testFiles/codeflaws/revFiles/10-A-1998522-1998523.c new file mode 100644 index 0000000..7e5a50b --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/10-A-1998522-1998523.c @@ -0,0 +1,34 @@ +#include + +int main(int argc, char *argv[]) +{ + int p1, p2, p3, t1, t2, n, lr, l, r, e = 0; + + scanf("%d %d %d %d %d %d", &n, &p1, &p2, &p3, &t1, &t2); + + scanf("%d %d", &l, &r); + e += (r - l) * p1; + lr = r; + + while(-- n) + { + scanf("%d %d", &l, &r); + e += (r - l) * p1; + if(l - lr <= t1) + e += (l - lr) * p1; + else + { + e += t1 * p1; + if(l - lr <= t1 + t2) + e += (l - lr - t1) * p2; + else + e += t2 * p2 + (l - lr - t1 - t2) * p3; + } + + lr = r; + } + + printf("%d\n", e); + + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/10-A-2106391-2106405.c b/src/main/resource/testFiles/codeflaws/revFiles/10-A-2106391-2106405.c new file mode 100644 index 0000000..7987f5d --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/10-A-2106391-2106405.c @@ -0,0 +1,35 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + int n,p1,p2,p3,t1,t2,a,c,b,e,i,d=0; + long int p=0; + scanf("%d%d%d%d%d%d",&n,&p1,&p2,&p3,&t1,&t2); + scanf("%d %d",&a,&b); + if(n==1) p=(b-a)*p1; + else { p=(b-a)*p1; + for(i=1;i=t1) {p+=t1*p1; + d-=t1; + } + else if(d>0) {p+=d*p1; + d=0; + } + if(d>=t2) {p+=t2*p2; + d-=t2; + } + else if(d>0) {p+=d*p2; + d=0; + } + if(d>0) p+=d*p3; + p+=p1*(e-c); + a=c; + b=e; + } + } + printf("%ld",p); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/10-A-4557108-4561236.c b/src/main/resource/testFiles/codeflaws/revFiles/10-A-4557108-4561236.c new file mode 100644 index 0000000..9593426 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/10-A-4557108-4561236.c @@ -0,0 +1,30 @@ +#include + +int i,n,m,p1,p2,p3,t1,t2,l,r,p,s,t,ar[2][101]; + +int main(int argc, char *argv[]){ + scanf("%d%d%d%d%d%d",&n,&p1,&p2,&p3,&t1,&t2); + + for (i=0; i=i+1 && ar[0][i+1]-ar[1][i]>0){ + m=ar[0][i+1]-ar[1][i]; + if (m>=t1){p+=p1*t1;} else {p+=p1*m;} + if (m-t1>=t2){p+=p2*t2;} else if (m>t1 && m-t1t2){p+=p3*(m-(t2+t1));} + } + t=ar[1][i]; + i++; + } + + printf("%d",p); + return(0); +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/10-A-5914564-5914663.c b/src/main/resource/testFiles/codeflaws/revFiles/10-A-5914564-5914663.c new file mode 100644 index 0000000..b18ea71 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/10-A-5914564-5914663.c @@ -0,0 +1,31 @@ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int n,p1,p2,p3,t1,t2,a,b,i,lr=0,power=0; + scanf("%d %d %d %d %d %d",&n,&p1,&p2,&p3,&t1,&t2); + scanf("%d %d",&a,&b); + power+=(b-a)*p1; + lr=b; + for(i=1;i +#include +int main(int argc, char *argv[]) +{ +int a[505],b[505],la,lb,f[505],p[505]; +int i,j,n,k,max; + + + + while + (scanf("%d",&la)!=EOF) + { + for(i=0;if[k])//b[j-1]f[k]是存放到 j时,jå‰é¢çš„æœ€é•¿çš„å­åºåˆ— + k=j; + if(a[i-1]==b[j-1]&&f[k]>=f[j])//逿­¥ä¼˜åŒ–ï¼Œè¦æŽŒæ¡ + {f[j]=f[k]+1; + p[j]=k; + } + } + } + max=0; + int t=1; + for(i=1;i<=lb;i++) + { + if(max1;i--) +printf("%d ",d[i]); + printf("%d\n",d[1]); + + + } + +return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/revFiles/101-A-3317973-3317996.c b/src/main/resource/testFiles/codeflaws/revFiles/101-A-3317973-3317996.c new file mode 100644 index 0000000..4509f75 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/101-A-3317973-3317996.c @@ -0,0 +1,79 @@ +#include +#include +char arr[100005]; +int k; +struct string +{ +int index[100005]; +int character; +}e[251]; +static int age_comp(const void *p1, const void *p2) +{ + struct string *sp1 = (struct string *) p1; + struct string *sp2 = (struct string *) p2; + + return sp1->character - sp2->character; +} + +void solve(); +int main(int argc, char *argv[]) +{ +scanf("%s",arr); +scanf("%d",&k); +solve(); +return 0; +} +void solve() +{ +int d,l,i,sum=0,j,m,dst=0,t=0; +l=strlen(arr); + +for(i=0;i<=250;i++) +e[i].character=0; +if(k>=l) +{ +printf("0\n"); +printf("\0"); +} +else +{ +for(i=0;ik) +break; +t++; +for(j=0;j +#include +#define MAX 101 +// + +int main(int argc, char *argv[]){ + int m , n; + int i , j , k ; + int x , y; //temp + long min = LONG_MAX ; + scanf("%d %d",&n,&m); + int pairs[MAX][MAX] = {0} ; //bool array + int clothesPrices[n]; + + // clothesPrices[m] inputs.. + for( i = 0 ; i < n ; i++){ + scanf("%d",&clothesPrices[i]); + } + + // pairs inputs + for ( i = 0 ; i < m ; i++ ){ + scanf("%d %d",&x,&y); + pairs[x][y] = pairs[y][x] = 1; + } + + // check if there are three clothing items that match each other .. + for ( i = 1 ; i < n ; i++){ + for ( j = i + 1 ; j <= n ; j++){ + for ( k = j + 1 ; k <= n ; k++ ){ + if ( pairs[i][j] && pairs[j][k] && pairs[k][i] ){ + if ( clothesPrices[i-1]+clothesPrices[j-1]+clothesPrices[k-1] < min ){ + min = clothesPrices[i-1]+clothesPrices[j-1]+clothesPrices[k-1]; + //printf("%ld",min); + } + } + } + } + } + if ( min!=LONG_MAX) + printf("%ld \n",min); + else printf("%d",-1); + + return 0; + } \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/revFiles/102-A-9556179-9556185.c b/src/main/resource/testFiles/codeflaws/revFiles/102-A-9556179-9556185.c new file mode 100644 index 0000000..fdd6297 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/102-A-9556179-9556185.c @@ -0,0 +1,34 @@ +#include +int a[100005]; +int b[105][105]; +int n,i,j,k,x,ans,m; +int main(int argc, char *argv[]) +{ +int hi ; +ans = 40000000; +int n; +scanf("%d%d",&n,&m); +for(i=1;i<=n;i++) +scanf("%d",&a[i]); +int x,y; +for(i=0;i +int main(int argc, char *argv[]) +{ + int i, flag=0, r1, r2, c1, c2, d1, d2, a, b, c, d, ar[1000]; + scanf("%d %d %d %d %d %d", &r1, &r2, &c1, &c2, &d1, &d2); + for(a=1; a=c1 || a>=d1) + { + //printf("continuing when a=%d\n", a); + continue; + } + b=r1-a; + if(ar[b]==1) + continue; + else + ar[b]=1; + if(b>=c2 || b>=d2) + { + continue; + } + c=c1-a; + if(ar[c]==1) + continue; + else + ar[c]=1; + if(c>=r2 || c>=d2) + { + continue; + } + d=d1-a; + if(ar[d]==1) + continue; + if(d>=r2 || d>=c2) + { + continue; + } + if(b+c!=d2) + continue; + if(b+d!=c2) + continue; + if(c+d!=r2) + continue; + if(a>9 || b>9 || c>9 || d>9) + continue; + flag=1; + break; + //} + } + if(flag==1) + printf("%d %d\n%d %d\n", a, b, c, d); + else + printf("-1\n"); + return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/revFiles/158-A-18237828-18237840.c b/src/main/resource/testFiles/codeflaws/revFiles/158-A-18237828-18237840.c new file mode 100644 index 0000000..5bddd33 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/158-A-18237828-18237840.c @@ -0,0 +1,19 @@ +#include + +int main(int argc, char *argv[]) +{ + int n,k,x,i,count=0; + scanf("%d %d",&n,&k); + int ara[n]; + for(i=0;i=ara[k-1]&& ara[i]!=0) + count++; + } + printf("%d",count); +return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/158-A-18278572-18278586.c b/src/main/resource/testFiles/codeflaws/revFiles/158-A-18278572-18278586.c new file mode 100644 index 0000000..a8edc11 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/158-A-18278572-18278586.c @@ -0,0 +1,37 @@ +#include +#include +/* +int main(int argc, char *argv[]) +{ int n,k,l=0,i,a[50]; + scanf("%d %d",&n,&k); + for(i=1;i<=n;i++) + scanf("%d",&a[i]); + if(a[1]!=0){ + l=k; + while(l0){ + i=1; + while(a[i]>=a[k]&&i<=n&&a[i]!=0){ + count++; + i++; + }} +printf("%d\n",count); +return 0;} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/158-E-1314159-1314160.c b/src/main/resource/testFiles/codeflaws/revFiles/158-E-1314159-1314160.c new file mode 100644 index 0000000..854181f --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/158-E-1314159-1314160.c @@ -0,0 +1,21 @@ +#include "stdio.h" + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) + +int dp[4005], n, k, t, d, i, ans; + +int main(int argc, char *argv[]) +{ + scanf( "%d%d", &n, &k ); + while(n--) + { + scanf( "%d%d", &t, &d ); + ans = max( ans, t - 1 - dp[k] ); + for( i = k; i >= 1; i-- ) dp[i] = min( dp[i-1], max( dp[i], t-1 ) + d ); + dp[0] = max( dp[0], t - 1 ) + d; + } + ans = max( ans, 86400 - dp[k] ); + printf( "%d\n", ans ); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/166-C-1395587-1395933.c b/src/main/resource/testFiles/codeflaws/revFiles/166-C-1395587-1395933.c new file mode 100644 index 0000000..5b2ba99 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/166-C-1395587-1395933.c @@ -0,0 +1,57 @@ +#include +#include + +int comp(const void *p1, const void *p2) { + int n1, n2; + n1 = *((const int *)p1); + n2 = *((const int *)p2); + return n1 - n2; +} + +int main(int argc, char *argv[]){ + int *array; + int i,j,k,n,x; + int first,last,flg,num; + scanf("%d %d",&n,&x); + array=(int *)calloc(n,sizeof(int)); + for(i=0;ix){ + first--; + break; + } + } + first++; + + for(last=n-1;last>=0;last--){ + if(array[last]==x){ + flg=1; + break; + }else if(array[last](n+1)/2) printf("%d\n",2*first-n-1); + else if(n==1) printf("0\n"); + }else{ + k=last; + if(k==(n+1)/2) printf("1\n"); + else if(k>(n+1)/2) printf("%d\n",2*k-n-1); + else printf("%d\n",n+2-2*k); + } + free(array); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/177-A2-1594730-1595168.c b/src/main/resource/testFiles/codeflaws/revFiles/177-A2-1594730-1595168.c new file mode 100644 index 0000000..5f56f5b --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/177-A2-1594730-1595168.c @@ -0,0 +1,34 @@ +#include +#include + + +int main(int argc, char *argv[]) +{ +// int num[101][101]; + int i,j,k,n,sum; + int a, mid; + sum = 0; + + scanf("%d",&n); + + for(i=1;i<=n;i++) + { + for(j=1;j<=n;j++) + { + scanf("%d",&a); + if(i==j) + sum += a; + if(j==(n+1)/2) + sum += a; + if(i==(n+1)/2) + sum += a; + if(i+j == n+1) + sum += a; + if(i==(n+1)/2 && j==(n+1)/2) + mid = a; + } + } + sum -= (3*mid); + printf("%d\n",sum); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/189-B-1682083-1682218.c b/src/main/resource/testFiles/codeflaws/revFiles/189-B-1682083-1682218.c new file mode 100644 index 0000000..98aefd0 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/189-B-1682083-1682218.c @@ -0,0 +1,27 @@ +#include + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +typedef long long ll; + +void func(int w, int h) { + ll ans = 0, i, j, k, t; + for (i = 1; i < w; i ++) { + for (j = 1; j < h; j ++) { + k = i; + k = MIN(k, w - i); + t = j; + t = MIN(t, h - j); + ans += k * t; + } + } + printf("%lld\n", ans); +} + +int main(int argc, char *argv[]) { + int w, h; + while (scanf("%d%d", &w, &h) == 2) { + func(w, h); + } + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/189-B-17295034-17295064.c b/src/main/resource/testFiles/codeflaws/revFiles/189-B-17295034-17295064.c new file mode 100644 index 0000000..5310460 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/189-B-17295034-17295064.c @@ -0,0 +1,18 @@ +#include +#define MIN(i,j) i +int main(int argc, char *argv[]) +{ + int n, m, i, a[101], b[101]; + scanf("%d %d", &n, &m); + for(i=0; i<=n; i++) + scanf("%d", &a[i]); + for(i=0; i<=m; i++) + scanf("%d", &b[i]); + if(n>m) + { + int prod=a[0]*b[0]; + if(prod>0) + printf("Infinity\n"); + else + printf("-Infinity\n"); + } + else if(n==m) + { + //printf("a[0]=%d b[0]=%d\n", a[0], b[0]); + if((a[0]<0 && b[0]>0) || (a[0]>0 && b[0]<0)) + printf("-"); + a[0]=abs(a[0]); + b[0]=abs(b[0]); + int min=a[0]; + if(b[0] +int main(int argc, char *argv[]) +{ + int a; + while(scanf("%d",&a)!=EOF) + { + if(a<=101) + printf("%d\n",a); + else if(a==123) + printf("113\n"); + else if(a==1000) + printf("352\n"); + else if(a==1000000000) + printf("40744\n"); + else if(a==999999999) + printf("40743\n"); + else if(a==999999998) + printf("40742\n"); + else if(a==999999997) + printf("40741\n"); + else if(a==909090901) + printf("38532\n"); + else if(a==142498040) + printf("21671\n"); + else if(a==603356456) + printf("31623\n"); + else if(a==64214872) + printf("15759\n"); + else if(a==820040584) + printf("36407\n"); + else if(a==442198) + printf("3071\n"); + else if(a==784262) + printf("4079\n"); + else if(a==642678) + printf("3615\n"); + else if(a==468390) + printf("3223\n"); + else if(a==326806) + printf("2759\n"); + else if(a==940) + printf("331\n"); + else if(a==356) + printf("175\n"); + else if(a==132) + printf("114\n"); + else if(a==102) + printf("101\n"); + } + return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/revFiles/245-D-3671804-3671831.c b/src/main/resource/testFiles/codeflaws/revFiles/245-D-3671804-3671831.c new file mode 100644 index 0000000..9831bb3 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/245-D-3671804-3671831.c @@ -0,0 +1,15 @@ +#include + +int main(int argc, char *argv[]) +{ + int n,ans,a,i,j; + scanf("%d",&n); + for (i=0;i +#include +#include +#define REP(i,a,b) for(i=a;i=0){ + res_c[res] = bef; res_d[res++] = i; + } + bef = i; + } + + res=0; + rep(k,m) unused[k]=0; + + rep(k,m){ + unionInit(ind,n); + rep(i,m) if(unused[i]==0) if(i!=k) unionConnect(ind,a[i],b[i]); + rep(i,n) cnt[i]=0; + rep(i,n) cnt[unionGet(ind,i)]=1; + j=0; rep(i,n) j+=cnt[i]; + if(j==shima){ + unused[k]=1; + res_a[res]=a[k]; res_b[res++]=b[k]; + } + } + + printf("%d\n",res); + rep(i,res) printf("%d %d %d %d\n",res_a[i]+1,res_b[i]+1,res_c[i]+1,res_d[i]+1); + + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/250-A-2762401-2762408.c b/src/main/resource/testFiles/codeflaws/revFiles/250-A-2762401-2762408.c new file mode 100644 index 0000000..985a465 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/250-A-2762401-2762408.c @@ -0,0 +1,58 @@ +#include +#include + +int main(int argc, char **argv) +{ + int i, n, data[100], counter = 0, negative = 0, folders = 0, printed = 0; + scanf("%d",&n); + for(i = 0; i < n; i++) + { + scanf("%d",&data[i]); + } + for(i = 0; i < n; i++) + { + if(data[i] < 0) + { + negative++; + } + } + folders = ceil(negative/2.0); + if(folders != 0) + { + printf("%d\n",folders); + } + negative = 0; + if(folders > 0) + { + for(i = 0; i < n; i++) + { + if(negative == 2 && folders > 1 && printed < folders-1) + { + printf("%d ",counter); + negative = 0; + counter = 0; + printed++; + } + counter++; + if(data[i] < 0) + { + negative++; + } + } + printed++; + if(folders == 1 || printed == folders) + { + printf("%d",counter); + } + } + else if(folders == 0) + { + printf("1\n"); + for(i = 0; i < n; i++) + { + counter++; + } + printf("%d",counter); + } + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/287-A-14208510-14208532.c b/src/main/resource/testFiles/codeflaws/revFiles/287-A-14208510-14208532.c new file mode 100644 index 0000000..436c57d --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/287-A-14208510-14208532.c @@ -0,0 +1,29 @@ +#include + +int main(int argc, char *argv[]) +{ + char g[5][5]; + int i, j, d=0, h=0; + for(i=0; i<4; i++){ + gets(g[i]); + } + for(i=0; i<3; i++){ + for(j=0; j<3; j++){ + if(g[i][j]=='.' ) d++; + else h++; + if (g[i][j+1]=='.') d++; + else h++; + if(g[i+1][j]=='.') d++; + else h++; + if(g[i+1][j+1]=='.') d++; + else h++; + if(d>=3 || h>=3){ + printf("YES"); + return 0; + } + d=0; h=0; + } + } + printf("NO"); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/287-A-14208521-14208532.c b/src/main/resource/testFiles/codeflaws/revFiles/287-A-14208521-14208532.c new file mode 100644 index 0000000..436c57d --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/287-A-14208521-14208532.c @@ -0,0 +1,29 @@ +#include + +int main(int argc, char *argv[]) +{ + char g[5][5]; + int i, j, d=0, h=0; + for(i=0; i<4; i++){ + gets(g[i]); + } + for(i=0; i<3; i++){ + for(j=0; j<3; j++){ + if(g[i][j]=='.' ) d++; + else h++; + if (g[i][j+1]=='.') d++; + else h++; + if(g[i+1][j]=='.') d++; + else h++; + if(g[i+1][j+1]=='.') d++; + else h++; + if(d>=3 || h>=3){ + printf("YES"); + return 0; + } + d=0; h=0; + } + } + printf("NO"); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/31-B-136044-136045.c b/src/main/resource/testFiles/codeflaws/revFiles/31-B-136044-136045.c new file mode 100644 index 0000000..9eb03fb --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/31-B-136044-136045.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#define eps 1e-8 +#define oo 1000000000 +#define pi 3.14159265359 +#define zero(a) (abb(a)<=eps) +#define lowbit(a) ((a)&(-(a))) +#define abb(a) ((a)>0?(a):(-(a))) +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) +#define cj(x1,y1,x2,y2) ((x1)*(y2)-(x2)*(y2)) +#define dj(x1,y1,x2,y2) ((x1)*(y1)+(y1)*(y2)) +#define dis(x1,y1,x2,y2) sqrt(((x2)-(x1))*((x2)-(x1))+((y2)-(y1))*((y2)-(y1))) + +char str[505]={'\0'},ans[505]={'\0'}; + +int main(int argc, char *argv[]) +{ + long i,l,flag=0,tot=0; + gets(str+1); + l=strlen(str+1); + if (str[1]=='@'||str[l]=='@') + goto end; + for (i=1;i<=l-2;i++) + if (str[i]=='@'&&(str[i+1]=='@'||str[i+2]=='@')) + goto end; + for (i=1;i<=l;i++) + { + if (flag&&str[i+1]=='@') + ans[++tot]=','; + if (str[i]=='@') + flag=1; + ans[++tot]=str[i]; + } + end: + if (flag) + puts(ans+1); + else + printf("No solution\n"); + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/31-B-6435804-6435825.c b/src/main/resource/testFiles/codeflaws/revFiles/31-B-6435804-6435825.c new file mode 100644 index 0000000..231f487 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/31-B-6435804-6435825.c @@ -0,0 +1,66 @@ +#include +#include + +int main(int argc, char *argv[]){ + char s[201], cc[255]; + int n, fl = 0, flag = 0, i, j, jj = -1, kon = 0, exit = 0; + scanf("%s", s); + if (s[0] != '@'){ + n = 0; + for (i = 1; i < strlen(s); i++){ + if (n == -1){ + if (s[i] != '@') + n = i; + else{ + printf("No solution\n"); + return(0); + } + } + if (fl == 1){ + if (s[i] != '@'){ + fl = 0; + if (flag != 0){ + jj++; + cc[jj] = ','; + for (j = n; j <= i; j++){ + jj++; + cc[jj] = s[j]; + } + } + else{ + for (j = n; j <= i; j++){ + jj++; + cc[jj] = s[j]; + } + flag = 1; + } + n = -1; + kon = i; + exit = 1; + } + else{ + printf("No solution\n"); + return(0); + } + } + else if (s[i] == '@') + fl = 1; + } + + } + else { + printf("No solution\n"); + return(0); + } + if (exit == 0){ + printf("No solution\n"); + return(0); + } + for (i = 0; i <= jj; i++) + printf("%c", cc[i]); + if (kon != strlen(s)) + for (i = kon + 1; i < strlen(s); i++) + printf("%c", s[i]); +return(0); +} + diff --git a/src/main/resource/testFiles/codeflaws/revFiles/315-A-6149995-6150754.c b/src/main/resource/testFiles/codeflaws/revFiles/315-A-6149995-6150754.c new file mode 100644 index 0000000..92d341b --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/315-A-6149995-6150754.c @@ -0,0 +1,28 @@ +#include + +int main(int argc, char *argv[]) +{ + int n; + scanf("%d",&n); + int i,a[n],b[n],t,j,ans=n; + for(i=0;i +#include + +int min(int a,int b){ + if(a +typedef long long ll; +int main(int argc, char *argv[]) +{ + ll x,y; + ll zero = 0; + scanf("%lld%lld",&x,&y); + if(x>=0&&y>=0) + { + printf("%lld %lld %lld %lld\n",zero,x+y,x+y,zero); + } + else if(x<0&&y>=0) + { + printf("%lld %lld %lld %lld\n",-(-x+y),zero,zero,-x+y); + } + else if(x>=0&&y<0) + { + printf("%lld %lld %lld %lld\n",zero,-(x-y),x-y,zero); + } + else + { + printf("%lld %lld %lld %lld\n",-(-x-y),zero,zero,-(-x-y)); + } + return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/revFiles/344-A-17290259-17290309.c b/src/main/resource/testFiles/codeflaws/revFiles/344-A-17290259-17290309.c new file mode 100644 index 0000000..56e3db0 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/344-A-17290259-17290309.c @@ -0,0 +1,26 @@ +#include +long int i,n,d,f,a[100005]; +int main(int argc, char *argv[]) +{ + scanf("%d",&n); + + for(i=0;i +int main(int argc, char *argv[]) +{ + char input[3001],space; + int n,i,vert_domino,count_dot,count,i1,i2; + count=0; + vert_domino=0; + count_dot=0; + i1=-1; + scanf("%d",&n); + scanf("%c",&space); + scanf("%c",&input[0]); +// printf("%c",input[0]); + if(input[0]=='.') + count=1; + else + i1=0; +// { + //while(input[i]!='R' || input[i]!='L') + i=1; + while(i!=n) + { + scanf("%c",&input[i]); +// printf("%c input %d ",input[i],i); + if(input[i]=='.') + { + + count++; +// printf("%d count",count); + i++; + continue; + } + if(i1==-1 && input[i]=='R') + { + vert_domino=count; + i1=i; + count=0; +// printf("%d 2",vert_domino); + // i++; + } + if(i1==-1 && input[i]=='L') + { + count=0; + i1=i; +// printf("%c %d i1",input[i1],i1); + // i++; + } + if(input[i1]=='R' && input[i]=='L' && i!=i1+1) + { +// printf("Even\n"); + i2=i; + if(count%2!=0) + { + vert_domino++; + i1=i2; + // count=0; +// printf("%d 4",vert_domino); + // i++; + } + i1=i2; + count=0; + } + if(input[i1]=='R' && input[i]=='L' && i==i1+1) + i1=i; + if(input[i1]=='L' && input[i]=='R' && i!=i1+1) + { + i2=i; +// printf("%d i2",i2); + // if(count%2!=0) + // { + vert_domino+=count; +// printf("%d 5",vert_domino); +// printf("%d vert",vert_domino); + i1=i2; + count=0; + // i++; + // } + } + if(input[i1]=='L' && input[i]=='R' && i==i1+1) + i1=i; + i++; + } + if(input[i1]=='L' && count!=0) + vert_domino+=count; + if(count==n) + printf("%d\n",n); + else + printf("%d\n",vert_domino); + return 0; +} + + diff --git a/src/main/resource/testFiles/codeflaws/revFiles/432-A-16886797-16886828.c b/src/main/resource/testFiles/codeflaws/revFiles/432-A-16886797-16886828.c new file mode 100644 index 0000000..2b8fb02 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/432-A-16886797-16886828.c @@ -0,0 +1,12 @@ +#include +int main(int argc, char *argv[]){ + int x,y,i,z,s=0; + scanf("%d %d",&x,&y); + for(i=0;i +#include +#include +int main(int argc, char *argv[])//Aлгоритм обхода в глубину +{ + int i,j,n,m,x,i_temp=1; + scanf("%d %d",&n,&m); + if(n==0) + { + printf("0 1\n"); + printf("0 %d\n",m); + printf("0 0\n"); + printf("0 %d\n",(m-1)); + return 0; + } + else if(m==0) + { + printf("1 0\n"); + printf("%d 0\n",n); + printf("0 0\n"); + printf("%d 0\n",(n-1)); + return 0; + } + + else if((m==n)&&(n==1)) + { + printf("%d %d\n",n,m); + printf("0 0\n"); + printf("%d 0\n",n); + printf("0 %d\n",(m)); + return 0; + } + else if(m==n) + { + if(m+m*1.41f>(2*sqrt((double)(m*m+(m-1)*(m-1))))) + { + printf("%d %d\n",n,m); + printf("0 0\n"); + printf("%d 0\n",n); + printf("0 %d\n",n); + } + else + { + printf("%d %d\n",n-1,m); + printf("0 0\n"); + printf("%d %d\n",n,m); + printf("1 0\n"); + } + return 0; + + + } + else if(nm) + { + if(n+sqrt((double)n*n+m*m)<(2*sqrt((double)n*n+(m-1)*(m-1)))) + { + printf("%d %d\n",n,m-1); + printf("0 0\n"); + printf("%d %d\n",n,m); + printf("0 1\n"); + return 0; + } + else + { + printf("%d %d\n0 0 \n%d 0\n0 %d\n",n,m,n,m); + return 0; + } + + } + return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/revFiles/469-B-8248222-8248281.c b/src/main/resource/testFiles/codeflaws/revFiles/469-B-8248222-8248281.c new file mode 100644 index 0000000..b759500 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/469-B-8248222-8248281.c @@ -0,0 +1,36 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + +int p,q,l,r,z[50][2],x[50][2],i,j,t[1001]={0}; +int temp1,temp2,k,count=0; +scanf("%d%d%d%d",&p,&q,&l,&r); + +for(i=0;ir) + temp2=r; + if(temp1<=r&&temp2>=l) + for(k=temp1;k<=temp2;k++) + t[k]=1; + } + } +for(k=l;k<=r;k++) + if(t[k]) + count++; +printf("%d",count); +return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/revFiles/474-A-15226851-15226912.c b/src/main/resource/testFiles/codeflaws/revFiles/474-A-15226851-15226912.c new file mode 100644 index 0000000..4277baa --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/474-A-15226851-15226912.c @@ -0,0 +1,36 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + char ch,str[200]; + char s[]="qwertyuiopasdfghjkl;zxcvbnm,./"; + int i,len,j,l; + scanf("%c",&ch); + scanf("%s",str); + len=strlen(str); + l=strlen(s); + if(ch=='R'){ + for(i=0;i + +int main(int argc, char *argv[]) +{ + int a, b, c, d, e; + int ZA[3000], ZB[3000], ZC[3000]; + + scanf("%d", &a); + for (b = 0; b < a; ++b) + scanf("%d", ZA + b); + for (b = 0; b < a - 1; ++b) { + d = b; + for (c = b + 1; c < a; ++c) + if (ZA[d] > ZA[c]) + d = c; + if (d != b) { + ZB[e] = b; + ZC[e] = d; + c = ZA[d]; + ZA[d] = ZA[b]; + ZA[b] = c; + e += 1; + } + } + printf("%d\n", e); + for (c = 0; c < e; ++c) + printf("%d %d\n", ZB[c], ZC[c]); + + return 0; +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/490-A-14580360-14580456.c b/src/main/resource/testFiles/codeflaws/revFiles/490-A-14580360-14580456.c new file mode 100644 index 0000000..013e312 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/490-A-14580360-14580456.c @@ -0,0 +1,39 @@ +#include + +int main(int argc, char *argv[]) +{ + int n, i,j,k,l; + scanf("%d",&n); + int a[5005],b[5005],c[5005],d[5005]; + int w=0,x=0,y=0; + for(i=1,j=1,k=1,l=1;i<=n;i++){ + scanf("%d",&a[i]); + if(a[i]==1){ + b[j]=i; + w++; + j++; + } + else if(a[i]==2){ + c[k]=i; + x++; + k++; + } + else if(a[i]==3){ + d[l]=i; + y++; + l++; + } + } + int min=w; + if(x +#include +#define N 1001 + +void rep(int l, char c) { + int i; + for (i = 0; i < l; i ++) printf("%c", c); +} + +int main(int argc, char *argv[]) +{ + char lines[N][N]; + int l, ml=0, i, ll=0, res=0, j, sl, right=0; + + while (gets(lines[ll])) { + l = strlen(lines[ll]); + l > ml ? ml = l : 0; + ll ++; + } + + rep(ml + 2, '*'); + + printf("\n"); + + for (i = 0; i < ll; i ++) { + printf("*"); + + l = strlen(lines[i]); + + sl = (ml - l) / 2; + + if ((ml - l) % 2) { + if (right) sl += 1; + right = 1 - right; + } + + rep(sl, ' '); + + printf("%s", lines[i]); + + rep(ml - l - sl, ' '); + + printf("*\n"); + } + + rep(ml + 2, '*'); + + return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/codeflaws/revFiles/507-A-16886367-16886377.c b/src/main/resource/testFiles/codeflaws/revFiles/507-A-16886367-16886377.c new file mode 100644 index 0000000..2cae737 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/507-A-16886367-16886377.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include + +#define MAX( a, b ) ( ( a > b) ? a : b ) +#define MIN( a, b ) ( ( a < b) ? a : b ) +#define FOR(ii,aa,bb) for(ii=aa;ii c[j]){ + s = c[j]; + c[j] = c[j-1]; + c[j-1] = s; + s = a[j]; + a[j] = a[j-1]; + a[j-1] = s; + } + + + FOR(i,0,n){ + top+=c[i]; + if(top>k) + break;} + + + printf("%d\n",i); + FOR(j,0,i) + printf("%d ",a[j]); + + return 0; + +} diff --git a/src/main/resource/testFiles/codeflaws/revFiles/612-A-15750192-15750273.c b/src/main/resource/testFiles/codeflaws/revFiles/612-A-15750192-15750273.c new file mode 100644 index 0000000..eb052a4 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/612-A-15750192-15750273.c @@ -0,0 +1,88 @@ +#include +#include +int main(int argc, char *argv[]) +{ + int k,p,q,i,j,l=0,t=0,c=0; + scanf("%d%d%d",&k,&p,&q); + char a[100]; + scanf("%s",&a); + if(k==p+q) + { + + printf("2\n"); + for(i=0;i=0) + { + t=1; + break; + } + } + + if(t==1) + { + printf("%d\n",c+(k-c*p)/q); + for(i=0;i +#define ll long long +int main(int argc, char *argv[]) +{ + ll n,a,b,i,j; + scanf("%lld%lld%lld",&n,&a,&b); + if(n>a*b){ + printf("-1"); + } + else{ + ll array[101][101]; + for(i=0;i=0;j--){ + if(count<=n){ + array[i][j]=count;} + else{ + break; + } + count++; + } + i++; + sign*=-1; + } + } + + for(i=0;i +int main(int argc, char *argv[]) { + int f=1,a,b,c,i,sum=0; + scanf("%d %d %d",&a,&b,&c); + if(c==0){ + if(a==b) + printf("YES"); + else printf("NO"); + return 0; +} + if((b-a)%c==0 && (b-a)/c>=0) + printf("YES"); + else + printf("NO"); + } + + + + diff --git a/src/main/resource/testFiles/codeflaws/revFiles/680-A-18343132-18343191.c b/src/main/resource/testFiles/codeflaws/revFiles/680-A-18343132-18343191.c new file mode 100644 index 0000000..7904c30 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/revFiles/680-A-18343132-18343191.c @@ -0,0 +1,28 @@ +#include +int main(int argc, char *argv[]) +{ + int i,a[5],b[101]={0},t=0,temp,max=0; + for(i=0;i<5;i++) + { + scanf("%d",&a[i]); +b[a[i]]++; +t+=a[i]; +} + for(i=0;i<5;i++) + {if(b[a[i]]>=3) + { + max=3*a[i]; + break;} +} + for(i=0;i<5;i++) + {if(b[a[i]]==2) + {temp=a[i]*2; + if(temp>max) + { + max=temp; + } + } +} +printf("%d",t-max); +return 0; +} \ No newline at end of file diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_03897e_8fcc8e_Python#sysmodule.c b/src/main/resource/testFiles/cpython/prevFiles/prev_03897e_8fcc8e_Python#sysmodule.c new file mode 100644 index 0000000..0d6d5a0 --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_03897e_8fcc8e_Python#sysmodule.c @@ -0,0 +1,1056 @@ + +/* System module */ + +/* +Various bits of information used by the interpreter are collected in +module 'sys'. +Function member: +- exit(sts): raise SystemExit +Data members: +- stdin, stdout, stderr: standard file objects +- modules: the table of modules (dictionary) +- path: module search path (list of strings) +- argv: script arguments (list of strings) +- ps1, ps2: optional primary and secondary prompts (strings) +*/ + +#include "Python.h" +#include "compile.h" +#include "frameobject.h" + +#include "osdefs.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef MS_COREDLL +extern void *PyWin_DLLhModule; +/* A string loaded from the DLL at startup: */ +extern const char *PyWin_DLLVersionString; +#endif + +PyObject * +PySys_GetObject(char *name) +{ + PyThreadState *tstate = PyThreadState_Get(); + PyObject *sd = tstate->interp->sysdict; + if (sd == NULL) + return NULL; + return PyDict_GetItemString(sd, name); +} + +FILE * +PySys_GetFile(char *name, FILE *def) +{ + FILE *fp = NULL; + PyObject *v = PySys_GetObject(name); + if (v != NULL && PyFile_Check(v)) + fp = PyFile_AsFile(v); + if (fp == NULL) + fp = def; + return fp; +} + +int +PySys_SetObject(char *name, PyObject *v) +{ + PyThreadState *tstate = PyThreadState_Get(); + PyObject *sd = tstate->interp->sysdict; + if (v == NULL) { + if (PyDict_GetItemString(sd, name) == NULL) + return 0; + else + return PyDict_DelItemString(sd, name); + } + else + return PyDict_SetItemString(sd, name, v); +} + +static PyObject * +sys_displayhook(PyObject *self, PyObject *args) +{ + PyObject *o, *outf; + PyInterpreterState *interp = PyThreadState_Get()->interp; + PyObject *modules = interp->modules; + PyObject *builtins = PyDict_GetItemString(modules, "__builtin__"); + + /* parse arguments */ + if (!PyArg_ParseTuple(args, "O:displayhook", &o)) + return NULL; + + /* Print value except if None */ + /* After printing, also assign to '_' */ + /* Before, set '_' to None to avoid recursion */ + if (o == Py_None) { + Py_INCREF(Py_None); + return Py_None; + } + if (PyObject_SetAttrString(builtins, "_", Py_None) != 0) + return NULL; + if (Py_FlushLine() != 0) + return NULL; + outf = PySys_GetObject("stdout"); + if (outf == NULL) { + PyErr_SetString(PyExc_RuntimeError, "lost sys.stdout"); + return NULL; + } + if (PyFile_WriteObject(o, outf, 0) != 0) + return NULL; + PyFile_SoftSpace(outf, 1); + if (Py_FlushLine() != 0) + return NULL; + if (PyObject_SetAttrString(builtins, "_", o) != 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static char displayhook_doc[] = +"displayhook(object) -> None\n" +"\n" +"Print an object to sys.stdout and also save it in __builtin__._\n"; + +static PyObject * +sys_excepthook(PyObject* self, PyObject* args) +{ + PyObject *exc, *value, *tb; + if (!PyArg_ParseTuple(args, "OOO:excepthook", &exc, &value, &tb)) + return NULL; + PyErr_Display(exc, value, tb); + Py_INCREF(Py_None); + return Py_None; +} + +static char excepthook_doc[] = +"excepthook(exctype, value, traceback) -> None\n" +"\n" +"Handle an exception by displaying it with a traceback on sys.stderr.\n"; + +static PyObject * +sys_exc_info(PyObject *self, PyObject *args) +{ + PyThreadState *tstate; + if (!PyArg_ParseTuple(args, ":exc_info")) + return NULL; + tstate = PyThreadState_Get(); + return Py_BuildValue( + "(OOO)", + tstate->exc_type != NULL ? tstate->exc_type : Py_None, + tstate->exc_value != NULL ? tstate->exc_value : Py_None, + tstate->exc_traceback != NULL ? + tstate->exc_traceback : Py_None); +} + +static char exc_info_doc[] = +"exc_info() -> (type, value, traceback)\n\ +\n\ +Return information about the exception that is currently being handled.\n\ +This should be called from inside an except clause only."; + +static PyObject * +sys_exit(PyObject *self, PyObject *args) +{ + /* Raise SystemExit so callers may catch it or clean up. */ + PyErr_SetObject(PyExc_SystemExit, args); + return NULL; +} + +static char exit_doc[] = +"exit([status])\n\ +\n\ +Exit the interpreter by raising SystemExit(status).\n\ +If the status is omitted or None, it defaults to zero (i.e., success).\n\ +If the status numeric, it will be used as the system exit status.\n\ +If it is another kind of object, it will be printed and the system\n\ +exit status will be one (i.e., failure)."; + +static PyObject * +sys_getdefaultencoding(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":getdefaultencoding")) + return NULL; + return PyString_FromString(PyUnicode_GetDefaultEncoding()); +} + +static char getdefaultencoding_doc[] = +"getdefaultencoding() -> string\n\ +\n\ +Return the current default string encoding used by the Unicode \n\ +implementation."; + +static PyObject * +sys_setdefaultencoding(PyObject *self, PyObject *args) +{ + char *encoding; + if (!PyArg_ParseTuple(args, "s:setdefaultencoding", &encoding)) + return NULL; + if (PyUnicode_SetDefaultEncoding(encoding)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static char setdefaultencoding_doc[] = +"setdefaultencoding(encoding)\n\ +\n\ +Set the current default string encoding used by the Unicode implementation."; + +/* + * Cached interned string objects used for calling the profile and + * trace functions. Initialized by trace_init(). + */ +static PyObject *whatstrings[4] = {NULL, NULL, NULL, NULL}; + +static int +trace_init(void) +{ + static char *whatnames[4] = {"call", "exception", "line", "return"}; + PyObject *name; + int i; + for (i = 0; i < 4; ++i) { + if (whatstrings[i] == NULL) { + name = PyString_InternFromString(whatnames[i]); + if (name == NULL) + return -1; + whatstrings[i] = name; + } + } + return 0; +} + + +static PyObject * +call_trampoline(PyThreadState *tstate, PyObject* callback, + PyFrameObject *frame, int what, PyObject *arg) +{ + PyObject *args = PyTuple_New(3); + PyObject *whatstr; + PyObject *result; + + if (args == NULL) + return NULL; + Py_INCREF(frame); + whatstr = whatstrings[what]; + Py_INCREF(whatstr); + if (arg == NULL) + arg = Py_None; + Py_INCREF(arg); + PyTuple_SET_ITEM(args, 0, (PyObject *)frame); + PyTuple_SET_ITEM(args, 1, whatstr); + PyTuple_SET_ITEM(args, 2, arg); + + /* call the Python-level function */ + PyFrame_FastToLocals(frame); + result = PyEval_CallObject(callback, args); + PyFrame_LocalsToFast(frame, 1); + if (result == NULL) + PyTraceBack_Here(frame); + + /* cleanup */ + Py_DECREF(args); + return result; +} + +static int +profile_trampoline(PyObject *self, PyFrameObject *frame, + int what, PyObject *arg) +{ + PyThreadState *tstate = frame->f_tstate; + PyObject *result; + + result = call_trampoline(tstate, self, frame, what, arg); + if (result == NULL) { + PyEval_SetProfile(NULL, NULL); + return -1; + } + Py_DECREF(result); + return 0; +} + +static int +trace_trampoline(PyObject *self, PyFrameObject *frame, + int what, PyObject *arg) +{ + PyThreadState *tstate = frame->f_tstate; + PyObject *callback; + PyObject *result; + + if (what == PyTrace_CALL) + callback = self; + else + callback = frame->f_trace; + if (callback == NULL) + return 0; + result = call_trampoline(tstate, callback, frame, what, arg); + if (result == NULL) { + PyEval_SetTrace(NULL, NULL); + Py_XDECREF(frame->f_trace); + frame->f_trace = NULL; + return -1; + } + if (result != Py_None) { + PyObject *temp = frame->f_trace; + frame->f_trace = NULL; + Py_XDECREF(temp); + frame->f_trace = result; + } + else { + Py_DECREF(result); + } + return 0; +} + +static PyObject * +sys_settrace(PyObject *self, PyObject *args) +{ + if (trace_init() == -1) + return NULL; + if (args == Py_None) + PyEval_SetTrace(NULL, NULL); + else + PyEval_SetTrace(trace_trampoline, args); + Py_INCREF(Py_None); + return Py_None; +} + +static char settrace_doc[] = +"settrace(function)\n\ +\n\ +Set the global debug tracing function. It will be called on each\n\ +function call. See the debugger chapter in the library manual."; + +static PyObject * +sys_setprofile(PyObject *self, PyObject *args) +{ + if (trace_init() == -1) + return NULL; + if (args == Py_None) + PyEval_SetProfile(NULL, NULL); + else + PyEval_SetProfile(profile_trampoline, args); + Py_INCREF(Py_None); + return Py_None; +} + +static char setprofile_doc[] = +"setprofile(function)\n\ +\n\ +Set the profiling function. It will be called on each function call\n\ +and return. See the profiler chapter in the library manual."; + +static PyObject * +sys_setcheckinterval(PyObject *self, PyObject *args) +{ + PyThreadState *tstate = PyThreadState_Get(); + if (!PyArg_ParseTuple(args, "i:setcheckinterval", &tstate->interp->checkinterval)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static char setcheckinterval_doc[] = +"setcheckinterval(n)\n\ +\n\ +Tell the Python interpreter to check for asynchronous events every\n\ +n instructions. This also affects how often thread switches occur."; + +static PyObject * +sys_setrecursionlimit(PyObject *self, PyObject *args) +{ + int new_limit; + if (!PyArg_ParseTuple(args, "i:setrecursionlimit", &new_limit)) + return NULL; + if (new_limit <= 0) { + PyErr_SetString(PyExc_ValueError, + "recursion limit must be positive"); + return NULL; + } + Py_SetRecursionLimit(new_limit); + Py_INCREF(Py_None); + return Py_None; +} + +static char setrecursionlimit_doc[] = +"setrecursionlimit(n)\n\ +\n\ +Set the maximum depth of the Python interpreter stack to n. This\n\ +limit prevents infinite recursion from causing an overflow of the C\n\ +stack and crashing Python. The highest possible limit is platform-\n\ +dependent."; + +static PyObject * +sys_getrecursionlimit(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":getrecursionlimit")) + return NULL; + return PyInt_FromLong(Py_GetRecursionLimit()); +} + +static char getrecursionlimit_doc[] = +"getrecursionlimit()\n\ +\n\ +Return the current value of the recursion limit, the maximum depth\n\ +of the Python interpreter stack. This limit prevents infinite\n\ +recursion from causing an overflow of the C stack and crashing Python."; + +#ifdef HAVE_DLOPEN +static PyObject * +sys_setdlopenflags(PyObject *self, PyObject *args) +{ + int new_val; + PyThreadState *tstate = PyThreadState_Get(); + if (!PyArg_ParseTuple(args, "i:setdlopenflags", &new_val)) + return NULL; + if (!tstate) + return NULL; + tstate->interp->dlopenflags = new_val; + Py_INCREF(Py_None); + return Py_None; +} + +static char setdlopenflags_doc[] = +"setdlopenflags(n) -> None\n\ +\n\ +Set the flags that will be used for dlopen() calls. Among other\n\ +things, this will enable a lazy resolving of symbols when imporing\n\ +a module, if called as sys.setdlopenflags(0)\n\ +To share symols across extension modules, call as\n\ +sys.setdlopenflags(dl.RTLD_NOW|dl.RTLD_GLOBAL)"; + +static PyObject * +sys_getdlopenflags(PyObject *self, PyObject *args) +{ + PyThreadState *tstate = PyThreadState_Get(); + if (!PyArg_ParseTuple(args, ":getdlopenflags")) + return NULL; + if (!tstate) + return NULL; + return PyInt_FromLong(tstate->interp->dlopenflags); +} + +static char getdlopenflags_doc[] = +"getdlopenflags() -> int\n\ +\n\ +Return the current value of the flags that are used for dlopen()\n\ +calls. The flag constants are defined in the dl module."; +#endif + +#ifdef USE_MALLOPT +/* Link with -lmalloc (or -lmpc) on an SGI */ +#include + +static PyObject * +sys_mdebug(PyObject *self, PyObject *args) +{ + int flag; + if (!PyArg_ParseTuple(args, "i:mdebug", &flag)) + return NULL; + mallopt(M_DEBUG, flag); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* USE_MALLOPT */ + +static PyObject * +sys_getrefcount(PyObject *self, PyObject *args) +{ + PyObject *arg; + if (!PyArg_ParseTuple(args, "O:getrefcount", &arg)) + return NULL; + return PyInt_FromLong(arg->ob_refcnt); +} + +#ifdef Py_TRACE_REFS +static PyObject * +sys_gettotalrefcount(PyObject *self, PyObject *args) +{ + extern long _Py_RefTotal; + if (!PyArg_ParseTuple(args, ":gettotalrefcount")) + return NULL; + return PyInt_FromLong(_Py_RefTotal); +} + +#endif /* Py_TRACE_REFS */ + +static char getrefcount_doc[] = +"getrefcount(object) -> integer\n\ +\n\ +Return the current reference count for the object. This includes the\n\ +temporary reference in the argument list, so it is at least 2."; + +#ifdef COUNT_ALLOCS +static PyObject * +sys_getcounts(PyObject *self, PyObject *args) +{ + extern PyObject *get_counts(void); + + if (!PyArg_ParseTuple(args, ":getcounts")) + return NULL; + return get_counts(); +} +#endif + +static char getframe_doc[] = +"_getframe([depth]) -> frameobject\n\ +\n\ +Return a frame object from the call stack. If optional integer depth is\n\ +given, return the frame object that many calls below the top of the stack.\n\ +If that is deeper than the call stack, ValueError is raised. The default\n\ +for depth is zero, returning the frame at the top of the call stack.\n\ +\n\ +This function should be used for internal and specialized\n\ +purposes only."; + +static PyObject * +sys_getframe(PyObject *self, PyObject *args) +{ + PyFrameObject *f = PyThreadState_Get()->frame; + int depth = -1; + + if (!PyArg_ParseTuple(args, "|i:_getframe", &depth)) + return NULL; + + while (depth > 0 && f != NULL) { + f = f->f_back; + --depth; + } + if (f == NULL) { + PyErr_SetString(PyExc_ValueError, + "call stack is not deep enough"); + return NULL; + } + Py_INCREF(f); + return (PyObject*)f; +} + + +#ifdef Py_TRACE_REFS +/* Defined in objects.c because it uses static globals if that file */ +extern PyObject *_Py_GetObjects(PyObject *, PyObject *); +#endif + +#ifdef DYNAMIC_EXECUTION_PROFILE +/* Defined in ceval.c because it uses static globals if that file */ +extern PyObject *_Py_GetDXProfile(PyObject *, PyObject *); +#endif + +static PyMethodDef sys_methods[] = { + /* Might as well keep this in alphabetic order */ + {"displayhook", sys_displayhook, 1, displayhook_doc}, + {"exc_info", sys_exc_info, 1, exc_info_doc}, + {"excepthook", sys_excepthook, 1, excepthook_doc}, + {"exit", sys_exit, 0, exit_doc}, + {"getdefaultencoding", sys_getdefaultencoding, 1, + getdefaultencoding_doc}, +#ifdef HAVE_DLOPEN + {"getdlopenflags", sys_getdlopenflags, 1, + getdlopenflags_doc}, +#endif +#ifdef COUNT_ALLOCS + {"getcounts", sys_getcounts, 1}, +#endif +#ifdef DYNAMIC_EXECUTION_PROFILE + {"getdxp", _Py_GetDXProfile, 1}, +#endif +#ifdef Py_TRACE_REFS + {"getobjects", _Py_GetObjects, 1}, + {"gettotalrefcount", sys_gettotalrefcount, 1}, +#endif + {"getrefcount", sys_getrefcount, 1, getrefcount_doc}, + {"getrecursionlimit", sys_getrecursionlimit, 1, + getrecursionlimit_doc}, + {"_getframe", sys_getframe, 1, getframe_doc}, +#ifdef USE_MALLOPT + {"mdebug", sys_mdebug, 1}, +#endif + {"setdefaultencoding", sys_setdefaultencoding, 1, + setdefaultencoding_doc}, + {"setcheckinterval", sys_setcheckinterval, 1, + setcheckinterval_doc}, +#ifdef HAVE_DLOPEN + {"setdlopenflags", sys_setdlopenflags, 1, + setdlopenflags_doc}, +#endif + {"setprofile", sys_setprofile, 0, setprofile_doc}, + {"setrecursionlimit", sys_setrecursionlimit, 1, + setrecursionlimit_doc}, + {"settrace", sys_settrace, 0, settrace_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +list_builtin_module_names(void) +{ + PyObject *list = PyList_New(0); + int i; + if (list == NULL) + return NULL; + for (i = 0; PyImport_Inittab[i].name != NULL; i++) { + PyObject *name = PyString_FromString( + PyImport_Inittab[i].name); + if (name == NULL) + break; + PyList_Append(list, name); + Py_DECREF(name); + } + if (PyList_Sort(list) != 0) { + Py_DECREF(list); + list = NULL; + } + if (list) { + PyObject *v = PyList_AsTuple(list); + Py_DECREF(list); + list = v; + } + return list; +} + +static PyObject *warnoptions = NULL; + +void +PySys_ResetWarnOptions(void) +{ + if (warnoptions == NULL || !PyList_Check(warnoptions)) + return; + PyList_SetSlice(warnoptions, 0, PyList_GET_SIZE(warnoptions), NULL); +} + +void +PySys_AddWarnOption(char *s) +{ + PyObject *str; + + if (warnoptions == NULL || !PyList_Check(warnoptions)) { + Py_XDECREF(warnoptions); + warnoptions = PyList_New(0); + if (warnoptions == NULL) + return; + } + str = PyString_FromString(s); + if (str != NULL) { + PyList_Append(warnoptions, str); + Py_DECREF(str); + } +} + +/* XXX This doc string is too long to be a single string literal in VC++ 5.0. + Two literals concatenated works just fine. If you have a K&R compiler + or other abomination that however *does* understand longer strings, + get rid of the !!! comment in the middle and the quotes that surround it. */ +static char sys_doc[] = +"This module provides access to some objects used or maintained by the\n\ +interpreter and to functions that interact strongly with the interpreter.\n\ +\n\ +Dynamic objects:\n\ +\n\ +argv -- command line arguments; argv[0] is the script pathname if known\n\ +path -- module search path; path[0] is the script directory, else ''\n\ +modules -- dictionary of loaded modules\n\ +\n\ +displayhook -- called to show results in an interactive session\n\ +excepthook -- called to handle any uncaught exception other than SystemExit\n\ + To customize printing in an interactive session or to install a custom\n\ + top-level exception handler, assign other functions to replace these.\n\ +\n\ +exitfunc -- if sys.exitfunc exists, this routine is called when Python exits\n\ + Assigning to sys.exitfunc is deprecated; use the atexit module instead.\n\ +\n\ +stdin -- standard input file object; used by raw_input() and input()\n\ +stdout -- standard output file object; used by the print statement\n\ +stderr -- standard error object; used for error messages\n\ + By assigning other file objects (or objects that behave like files)\n\ + to these, it is possible to redirect all of the interpreter's I/O.\n\ +\n\ +last_type -- type of last uncaught exception\n\ +last_value -- value of last uncaught exception\n\ +last_traceback -- traceback of last uncaught exception\n\ + These three are only available in an interactive session after a\n\ + traceback has been printed.\n\ +\n\ +exc_type -- type of exception currently being handled\n\ +exc_value -- value of exception currently being handled\n\ +exc_traceback -- traceback of exception currently being handled\n\ + The function exc_info() should be used instead of these three,\n\ + because it is thread-safe.\n\ +" +#ifndef MS_WIN16 +/* concatenating string here */ +"\n\ +Static objects:\n\ +\n\ +maxint -- the largest supported integer (the smallest is -maxint-1)\n\ +maxunicode -- the largest supported character\n\ +builtin_module_names -- tuple of module names built into this intepreter\n\ +version -- the version of this interpreter as a string\n\ +version_info -- version information as a tuple\n\ +hexversion -- version information encoded as a single integer\n\ +copyright -- copyright notice pertaining to this interpreter\n\ +platform -- platform identifier\n\ +executable -- pathname of this Python interpreter\n\ +prefix -- prefix used to find the Python library\n\ +exec_prefix -- prefix used to find the machine-specific Python library\n\ +" +#ifdef MS_WINDOWS +/* concatenating string here */ +"dllhandle -- [Windows only] integer handle of the Python DLL\n\ +winver -- [Windows only] version number of the Python DLL\n\ +" +#endif /* MS_WINDOWS */ +"__stdin__ -- the original stdin; don't touch!\n\ +__stdout__ -- the original stdout; don't touch!\n\ +__stderr__ -- the original stderr; don't touch!\n\ +__displayhook__ -- the original displayhook; don't touch!\n\ +__excepthook__ -- the original excepthook; don't touch!\n\ +\n\ +Functions:\n\ +\n\ +displayhook() -- print an object to the screen, and save it in __builtin__._\n\ +excepthook() -- print an exception and its traceback to sys.stderr\n\ +exc_info() -- return thread-safe information about the current exception\n\ +exit() -- exit the interpreter by raising SystemExit\n\ +getdlopenflags() -- returns flags to be used for dlopen() calls\n\ +getrefcount() -- return the reference count for an object (plus one :-)\n\ +getrecursionlimit() -- return the max recursion depth for the interpreter\n\ +setcheckinterval() -- control how often the interpreter checks for events\n\ +setdlopenflags() -- set the flags to be used for dlopen() calls\n\ +setprofile() -- set the global profiling function\n\ +setrecursionlimit() -- set the max recursion depth for the interpreter\n\ +settrace() -- set the global debug tracing function\n\ +" +#endif /* MS_WIN16 */ +/* end of sys_doc */ ; + +PyObject * +_PySys_Init(void) +{ + PyObject *m, *v, *sysdict; + PyObject *sysin, *sysout, *syserr; + char *s; + + m = Py_InitModule3("sys", sys_methods, sys_doc); + sysdict = PyModule_GetDict(m); + + sysin = PyFile_FromFile(stdin, "", "r", NULL); + sysout = PyFile_FromFile(stdout, "", "w", NULL); + syserr = PyFile_FromFile(stderr, "", "w", NULL); + if (PyErr_Occurred()) + return NULL; + PyDict_SetItemString(sysdict, "stdin", sysin); + PyDict_SetItemString(sysdict, "stdout", sysout); + PyDict_SetItemString(sysdict, "stderr", syserr); + /* Make backup copies for cleanup */ + PyDict_SetItemString(sysdict, "__stdin__", sysin); + PyDict_SetItemString(sysdict, "__stdout__", sysout); + PyDict_SetItemString(sysdict, "__stderr__", syserr); + PyDict_SetItemString(sysdict, "__displayhook__", + PyDict_GetItemString(sysdict, "displayhook")); + PyDict_SetItemString(sysdict, "__excepthook__", + PyDict_GetItemString(sysdict, "excepthook")); + Py_XDECREF(sysin); + Py_XDECREF(sysout); + Py_XDECREF(syserr); + PyDict_SetItemString(sysdict, "version", + v = PyString_FromString(Py_GetVersion())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "hexversion", + v = PyInt_FromLong(PY_VERSION_HEX)); + Py_XDECREF(v); + /* + * These release level checks are mutually exclusive and cover + * the field, so don't get too fancy with the pre-processor! + */ +#if PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_ALPHA + s = "alpha"; +#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_BETA + s = "beta"; +#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_GAMMA + s = "candidate"; +#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_FINAL + s = "final"; +#endif + PyDict_SetItemString(sysdict, "version_info", + v = Py_BuildValue("iiisi", PY_MAJOR_VERSION, + PY_MINOR_VERSION, + PY_MICRO_VERSION, s, + PY_RELEASE_SERIAL)); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "copyright", + v = PyString_FromString(Py_GetCopyright())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "platform", + v = PyString_FromString(Py_GetPlatform())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "executable", + v = PyString_FromString(Py_GetProgramFullPath())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "prefix", + v = PyString_FromString(Py_GetPrefix())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "exec_prefix", + v = PyString_FromString(Py_GetExecPrefix())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "maxint", + v = PyInt_FromLong(PyInt_GetMax())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "maxunicode", + v = PyInt_FromLong(PyUnicode_GetMax())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "builtin_module_names", + v = list_builtin_module_names()); + Py_XDECREF(v); + { + /* Assumes that longs are at least 2 bytes long. + Should be safe! */ + unsigned long number = 1; + char *value; + + s = (char *) &number; + if (s[0] == 0) + value = "big"; + else + value = "little"; + PyDict_SetItemString(sysdict, "byteorder", + v = PyString_FromString(value)); + Py_XDECREF(v); + } +#ifdef MS_COREDLL + PyDict_SetItemString(sysdict, "dllhandle", + v = PyLong_FromVoidPtr(PyWin_DLLhModule)); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "winver", + v = PyString_FromString(PyWin_DLLVersionString)); + Py_XDECREF(v); +#endif + if (warnoptions == NULL) { + warnoptions = PyList_New(0); + } + else { + Py_INCREF(warnoptions); + } + if (warnoptions != NULL) { + PyDict_SetItemString(sysdict, "warnoptions", warnoptions); + } + + if (PyErr_Occurred()) + return NULL; + return m; +} + +static PyObject * +makepathobject(char *path, int delim) +{ + int i, n; + char *p; + PyObject *v, *w; + + n = 1; + p = path; + while ((p = strchr(p, delim)) != NULL) { + n++; + p++; + } + v = PyList_New(n); + if (v == NULL) + return NULL; + for (i = 0; ; i++) { + p = strchr(path, delim); + if (p == NULL) + p = strchr(path, '\0'); /* End of string */ + w = PyString_FromStringAndSize(path, (int) (p - path)); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(v, i, w); + if (*p == '\0') + break; + path = p+1; + } + return v; +} + +void +PySys_SetPath(char *path) +{ + PyObject *v; + if ((v = makepathobject(path, DELIM)) == NULL) + Py_FatalError("can't create sys.path"); + if (PySys_SetObject("path", v) != 0) + Py_FatalError("can't assign sys.path"); + Py_DECREF(v); +} + +static PyObject * +makeargvobject(int argc, char **argv) +{ + PyObject *av; + if (argc <= 0 || argv == NULL) { + /* Ensure at least one (empty) argument is seen */ + static char *empty_argv[1] = {""}; + argv = empty_argv; + argc = 1; + } + av = PyList_New(argc); + if (av != NULL) { + int i; + for (i = 0; i < argc; i++) { + PyObject *v = PyString_FromString(argv[i]); + if (v == NULL) { + Py_DECREF(av); + av = NULL; + break; + } + PyList_SetItem(av, i, v); + } + } + return av; +} + +void +PySys_SetArgv(int argc, char **argv) +{ + PyObject *av = makeargvobject(argc, argv); + PyObject *path = PySys_GetObject("path"); + if (av == NULL) + Py_FatalError("no mem for sys.argv"); + if (PySys_SetObject("argv", av) != 0) + Py_FatalError("can't assign sys.argv"); + if (path != NULL) { + char *argv0 = argv[0]; + char *p = NULL; + int n = 0; + PyObject *a; +#ifdef HAVE_READLINK + char link[MAXPATHLEN+1]; + char argv0copy[2*MAXPATHLEN+1]; + int nr = 0; + if (argc > 0 && argv0 != NULL) + nr = readlink(argv0, link, MAXPATHLEN); + if (nr > 0) { + /* It's a symlink */ + link[nr] = '\0'; + if (link[0] == SEP) + argv0 = link; /* Link to absolute path */ + else if (strchr(link, SEP) == NULL) + ; /* Link without path */ + else { + /* Must join(dirname(argv0), link) */ + char *q = strrchr(argv0, SEP); + if (q == NULL) + argv0 = link; /* argv0 without path */ + else { + /* Must make a copy */ + strcpy(argv0copy, argv0); + q = strrchr(argv0copy, SEP); + strcpy(q+1, link); + argv0 = argv0copy; + } + } + } +#endif /* HAVE_READLINK */ +#if SEP == '\\' /* Special case for MS filename syntax */ + if (argc > 0 && argv0 != NULL) { + char *q; + p = strrchr(argv0, SEP); + /* Test for alternate separator */ + q = strrchr(p ? p : argv0, '/'); + if (q != NULL) + p = q; + if (p != NULL) { + n = p + 1 - argv0; + if (n > 1 && p[-1] != ':') + n--; /* Drop trailing separator */ + } + } +#else /* All other filename syntaxes */ + if (argc > 0 && argv0 != NULL) + p = strrchr(argv0, SEP); + if (p != NULL) { +#ifndef RISCOS + n = p + 1 - argv0; +#else /* don't include trailing separator */ + n = p - argv0; +#endif /* RISCOS */ +#if SEP == '/' /* Special case for Unix filename syntax */ + if (n > 1) + n--; /* Drop trailing separator */ +#endif /* Unix */ + } +#endif /* All others */ + a = PyString_FromStringAndSize(argv0, n); + if (a == NULL) + Py_FatalError("no mem for sys.path insertion"); + if (PyList_Insert(path, 0, a) < 0) + Py_FatalError("sys.path.insert(0) failed"); + Py_DECREF(a); + } + Py_DECREF(av); +} + + +/* APIs to write to sys.stdout or sys.stderr using a printf-like interface. + Adapted from code submitted by Just van Rossum. + + PySys_WriteStdout(format, ...) + PySys_WriteStderr(format, ...) + + The first function writes to sys.stdout; the second to sys.stderr. When + there is a problem, they write to the real (C level) stdout or stderr; + no exceptions are raised. + + Both take a printf-style format string as their first argument followed + by a variable length argument list determined by the format string. + + *** WARNING *** + + The format should limit the total size of the formatted output string to + 1000 bytes. In particular, this means that no unrestricted "%s" formats + should occur; these should be limited using "%.s where is a + decimal number calculated so that plus the maximum size of other + formatted text does not exceed 1000 bytes. Also watch out for "%f", + which can print hundreds of digits for very large numbers. + + */ + +static void +mywrite(char *name, FILE *fp, const char *format, va_list va) +{ + PyObject *file; + PyObject *error_type, *error_value, *error_traceback; + + PyErr_Fetch(&error_type, &error_value, &error_traceback); + file = PySys_GetObject(name); + if (file == NULL || PyFile_AsFile(file) == fp) + vfprintf(fp, format, va); + else { + char buffer[1001]; + if (vsprintf(buffer, format, va) >= sizeof(buffer)) + Py_FatalError("PySys_WriteStdout/err: buffer overrun"); + if (PyFile_WriteString(buffer, file) != 0) { + PyErr_Clear(); + fputs(buffer, fp); + } + } + PyErr_Restore(error_type, error_value, error_traceback); +} + +void +PySys_WriteStdout(const char *format, ...) +{ + va_list va; + + va_start(va, format); + mywrite("stdout", stdout, format, va); + va_end(va); +} + +void +PySys_WriteStderr(const char *format, ...) +{ + va_list va; + + va_start(va, format); + mywrite("stderr", stderr, format, va); + va_end(va); +} diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_09705f_db2a0f_Modules#_sre.c b/src/main/resource/testFiles/cpython/prevFiles/prev_09705f_db2a0f_Modules#_sre.c new file mode 100644 index 0000000..640f388 --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_09705f_db2a0f_Modules#_sre.c @@ -0,0 +1,3012 @@ +/* + * Secret Labs' Regular Expression Engine + * + * regular expression matching engine + * + * partial history: + * 1999-10-24 fl created (based on existing template matcher code) + * 2000-03-06 fl first alpha, sort of + * 2000-06-30 fl added fast search optimization + * 2000-06-30 fl added assert (lookahead) primitives, etc + * 2000-07-02 fl added charset optimizations, etc + * 2000-07-03 fl store code in pattern object, lookbehind, etc + * 2000-07-08 fl added regs attribute + * 2000-07-21 fl reset lastindex in scanner methods + * 2000-08-01 fl fixes for 1.6b1 + * 2000-08-03 fl added recursion limit + * 2000-08-07 fl use PyOS_CheckStack() if available + * 2000-08-08 fl changed findall to return empty strings instead of None + * 2000-08-27 fl properly propagate memory errors + * 2000-09-02 fl return -1 instead of None for start/end/span + * 2000-09-20 fl added expand method + * 2000-09-21 fl don't use the buffer interface for unicode strings + * 2000-10-03 fl fixed assert_not primitive; support keyword arguments + * 2000-10-24 fl really fixed assert_not; reset groups in findall + * 2000-12-21 fl fixed memory leak in groupdict + * 2001-01-02 fl properly reset pointer after failed assertion in MIN_UNTIL + * 2001-01-15 fl avoid recursion for MIN_UNTIL; fixed uppercase literal bug + * 2001-01-16 fl fixed memory leak in pattern destructor + * 2001-03-20 fl lots of fixes for 2.1b2 + * 2001-04-15 fl export copyright as Python attribute, not global + * 2001-04-28 fl added __copy__ methods (work in progress) + * 2001-05-14 fl fixes for 1.5.2 + * 2001-07-01 fl added BIGCHARSET support (from Martin von Loewis) + * 2001-10-18 fl fixed group reset issue (from Matthew Mueller) + * 2001-10-20 fl added split primitive; reenable unicode for 1.6/2.0/2.1 + * 2001-10-21 fl added sub/subn primitive + * 2001-10-22 fl check for literal sub/subn templates + * 2001-10-24 fl added finditer primitive (for 2.2 only) + * 2001-12-07 fl fixed memory leak in sub/subn (Guido van Rossum) + * + * Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved. + * + * This version of the SRE library can be redistributed under CNRI's + * Python 1.6 license. For any other use, please contact Secret Labs + * AB (info@pythonware.com). + * + * Portions of this engine have been developed in cooperation with + * CNRI. Hewlett-Packard provided funding for 1.6 integration and + * other compatibility work. + */ + +#ifndef SRE_RECURSIVE + +static char copyright[] = + " SRE 2.2.1 Copyright (c) 1997-2001 by Secret Labs AB "; + +#include "Python.h" +#include "structmember.h" /* offsetof */ + +#include "sre.h" + +#include + +/* name of this module, minus the leading underscore */ +#if !defined(SRE_MODULE) +#define SRE_MODULE "sre" +#endif + +/* defining this one enables tracing */ +#undef VERBOSE + +#if PY_VERSION_HEX >= 0x01060000 +#if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE) +/* defining this enables unicode support (default under 1.6a1 and later) */ +#define HAVE_UNICODE +#endif +#endif + +/* -------------------------------------------------------------------- */ +/* optional features */ + +/* prevent run-away recursion (bad patterns on long strings) */ + +#if !defined(USE_STACKCHECK) +#if defined(MS_WIN64) || defined(__LP64__) || defined(_LP64) +/* require smaller recursion limit for a number of 64-bit platforms: + Win64 (MS_WIN64), Linux64 (__LP64__), Monterey (64-bit AIX) (_LP64) */ +/* FIXME: maybe the limit should be 40000 / sizeof(void*) ? */ +#define USE_RECURSION_LIMIT 7500 +#else +#define USE_RECURSION_LIMIT 10000 +#endif +#endif + +/* enables fast searching */ +#define USE_FAST_SEARCH + +/* enables aggressive inlining (always on for Visual C) */ +#undef USE_INLINE + +/* enables copy/deepcopy handling (work in progress) */ +#undef USE_BUILTIN_COPY + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_DEL(op) PyMem_DEL((op)) +#endif + +/* -------------------------------------------------------------------- */ + +#if defined(_MSC_VER) +#pragma optimize("agtw", on) /* doesn't seem to make much difference... */ +#pragma warning(disable: 4710) /* who cares if functions are not inlined ;-) */ +/* fastest possible local call under MSVC */ +#define LOCAL(type) static __inline type __fastcall +#elif defined(USE_INLINE) +#define LOCAL(type) static inline type +#else +#define LOCAL(type) static type +#endif + +/* error codes */ +#define SRE_ERROR_ILLEGAL -1 /* illegal opcode */ +#define SRE_ERROR_STATE -2 /* illegal state */ +#define SRE_ERROR_RECURSION_LIMIT -3 /* runaway recursion */ +#define SRE_ERROR_MEMORY -9 /* out of memory */ + +#if defined(VERBOSE) +#define TRACE(v) printf v +#else +#define TRACE(v) +#endif + +/* -------------------------------------------------------------------- */ +/* search engine state */ + +/* default character predicates (run sre_chars.py to regenerate tables) */ + +#define SRE_DIGIT_MASK 1 +#define SRE_SPACE_MASK 2 +#define SRE_LINEBREAK_MASK 4 +#define SRE_ALNUM_MASK 8 +#define SRE_WORD_MASK 16 + +/* FIXME: this assumes ASCII. create tables in init_sre() instead */ + +static char sre_char_info[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 2, +2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, +0, 0, 16, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0 }; + +static char sre_char_lower[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, +27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, +44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, +61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, +108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, +122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, +106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, +120, 121, 122, 123, 124, 125, 126, 127 }; + +#define SRE_IS_DIGIT(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_DIGIT_MASK) : 0) +#define SRE_IS_SPACE(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_SPACE_MASK) : 0) +#define SRE_IS_LINEBREAK(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_LINEBREAK_MASK) : 0) +#define SRE_IS_ALNUM(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_ALNUM_MASK) : 0) +#define SRE_IS_WORD(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_WORD_MASK) : 0) + +static unsigned int sre_lower(unsigned int ch) +{ + return ((ch) < 128 ? sre_char_lower[ch] : ch); +} + +/* locale-specific character predicates */ + +#define SRE_LOC_IS_DIGIT(ch) ((ch) < 256 ? isdigit((ch)) : 0) +#define SRE_LOC_IS_SPACE(ch) ((ch) < 256 ? isspace((ch)) : 0) +#define SRE_LOC_IS_LINEBREAK(ch) ((ch) == '\n') +#define SRE_LOC_IS_ALNUM(ch) ((ch) < 256 ? isalnum((ch)) : 0) +#define SRE_LOC_IS_WORD(ch) (SRE_LOC_IS_ALNUM((ch)) || (ch) == '_') + +static unsigned int sre_lower_locale(unsigned int ch) +{ + return ((ch) < 256 ? tolower((ch)) : ch); +} + +/* unicode-specific character predicates */ + +#if defined(HAVE_UNICODE) + +#define SRE_UNI_IS_DIGIT(ch) Py_UNICODE_ISDIGIT((Py_UNICODE)(ch)) +#define SRE_UNI_IS_SPACE(ch) Py_UNICODE_ISSPACE((Py_UNICODE)(ch)) +#define SRE_UNI_IS_LINEBREAK(ch) Py_UNICODE_ISLINEBREAK((Py_UNICODE)(ch)) +#define SRE_UNI_IS_ALNUM(ch) Py_UNICODE_ISALNUM((Py_UNICODE)(ch)) +#define SRE_UNI_IS_WORD(ch) (SRE_UNI_IS_ALNUM((ch)) || (ch) == '_') + +static unsigned int sre_lower_unicode(unsigned int ch) +{ + return (unsigned int) Py_UNICODE_TOLOWER((Py_UNICODE)(ch)); +} + +#endif + +LOCAL(int) +sre_category(SRE_CODE category, unsigned int ch) +{ + switch (category) { + + case SRE_CATEGORY_DIGIT: + return SRE_IS_DIGIT(ch); + case SRE_CATEGORY_NOT_DIGIT: + return !SRE_IS_DIGIT(ch); + case SRE_CATEGORY_SPACE: + return SRE_IS_SPACE(ch); + case SRE_CATEGORY_NOT_SPACE: + return !SRE_IS_SPACE(ch); + case SRE_CATEGORY_WORD: + return SRE_IS_WORD(ch); + case SRE_CATEGORY_NOT_WORD: + return !SRE_IS_WORD(ch); + case SRE_CATEGORY_LINEBREAK: + return SRE_IS_LINEBREAK(ch); + case SRE_CATEGORY_NOT_LINEBREAK: + return !SRE_IS_LINEBREAK(ch); + + case SRE_CATEGORY_LOC_WORD: + return SRE_LOC_IS_WORD(ch); + case SRE_CATEGORY_LOC_NOT_WORD: + return !SRE_LOC_IS_WORD(ch); + +#if defined(HAVE_UNICODE) + case SRE_CATEGORY_UNI_DIGIT: + return SRE_UNI_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_NOT_DIGIT: + return !SRE_UNI_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_SPACE: + return SRE_UNI_IS_SPACE(ch); + case SRE_CATEGORY_UNI_NOT_SPACE: + return !SRE_UNI_IS_SPACE(ch); + case SRE_CATEGORY_UNI_WORD: + return SRE_UNI_IS_WORD(ch); + case SRE_CATEGORY_UNI_NOT_WORD: + return !SRE_UNI_IS_WORD(ch); + case SRE_CATEGORY_UNI_LINEBREAK: + return SRE_UNI_IS_LINEBREAK(ch); + case SRE_CATEGORY_UNI_NOT_LINEBREAK: + return !SRE_UNI_IS_LINEBREAK(ch); +#else + case SRE_CATEGORY_UNI_DIGIT: + return SRE_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_NOT_DIGIT: + return !SRE_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_SPACE: + return SRE_IS_SPACE(ch); + case SRE_CATEGORY_UNI_NOT_SPACE: + return !SRE_IS_SPACE(ch); + case SRE_CATEGORY_UNI_WORD: + return SRE_LOC_IS_WORD(ch); + case SRE_CATEGORY_UNI_NOT_WORD: + return !SRE_LOC_IS_WORD(ch); + case SRE_CATEGORY_UNI_LINEBREAK: + return SRE_IS_LINEBREAK(ch); + case SRE_CATEGORY_UNI_NOT_LINEBREAK: + return !SRE_IS_LINEBREAK(ch); +#endif + } + return 0; +} + +/* helpers */ + +static void +mark_fini(SRE_STATE* state) +{ + if (state->mark_stack) { + free(state->mark_stack); + state->mark_stack = NULL; + } + state->mark_stack_size = state->mark_stack_base = 0; +} + +static int +mark_save(SRE_STATE* state, int lo, int hi) +{ + void* stack; + int size; + int minsize, newsize; + + if (hi <= lo) + return 0; + + size = (hi - lo) + 1; + + newsize = state->mark_stack_size; + minsize = state->mark_stack_base + size; + + if (newsize < minsize) { + /* create new stack */ + if (!newsize) { + newsize = 512; + if (newsize < minsize) + newsize = minsize; + TRACE(("allocate stack %d\n", newsize)); + stack = malloc(sizeof(void*) * newsize); + } else { + /* grow the stack */ + while (newsize < minsize) + newsize += newsize; + TRACE(("grow stack to %d\n", newsize)); + stack = realloc(state->mark_stack, sizeof(void*) * newsize); + } + if (!stack) { + mark_fini(state); + return SRE_ERROR_MEMORY; + } + state->mark_stack = stack; + state->mark_stack_size = newsize; + } + + TRACE(("copy %d:%d to %d (%d)\n", lo, hi, state->mark_stack_base, size)); + + memcpy(state->mark_stack + state->mark_stack_base, state->mark + lo, + size * sizeof(void*)); + + state->mark_stack_base += size; + + return 0; +} + +static int +mark_restore(SRE_STATE* state, int lo, int hi) +{ + int size; + + if (hi <= lo) + return 0; + + size = (hi - lo) + 1; + + state->mark_stack_base -= size; + + TRACE(("copy %d:%d from %d\n", lo, hi, state->mark_stack_base)); + + memcpy(state->mark + lo, state->mark_stack + state->mark_stack_base, + size * sizeof(void*)); + + return 0; +} + +static void +lastmark_restore(SRE_STATE *state, int lastmark) +{ + if (state->lastmark > lastmark) { + memset( + state->mark + lastmark + 1, 0, + (state->lastmark - lastmark) * sizeof(void*) + ); + state->lastmark = lastmark; + state->lastindex = (lastmark == 0) ? -1 : (lastmark-1)/2+1; + } +} + +/* generate 8-bit version */ + +#define SRE_CHAR unsigned char +#define SRE_AT sre_at +#define SRE_COUNT sre_count +#define SRE_CHARSET sre_charset +#define SRE_INFO sre_info +#define SRE_MATCH sre_match +#define SRE_SEARCH sre_search +#define SRE_LITERAL_TEMPLATE sre_literal_template + +#if defined(HAVE_UNICODE) + +#define SRE_RECURSIVE +#include "_sre.c" +#undef SRE_RECURSIVE + +#undef SRE_LITERAL_TEMPLATE +#undef SRE_SEARCH +#undef SRE_MATCH +#undef SRE_INFO +#undef SRE_CHARSET +#undef SRE_COUNT +#undef SRE_AT +#undef SRE_CHAR + +/* generate 16-bit unicode version */ + +#define SRE_CHAR Py_UNICODE +#define SRE_AT sre_uat +#define SRE_COUNT sre_ucount +#define SRE_CHARSET sre_ucharset +#define SRE_INFO sre_uinfo +#define SRE_MATCH sre_umatch +#define SRE_SEARCH sre_usearch +#define SRE_LITERAL_TEMPLATE sre_uliteral_template +#endif + +#endif /* SRE_RECURSIVE */ + +/* -------------------------------------------------------------------- */ +/* String matching engine */ + +/* the following section is compiled twice, with different character + settings */ + +LOCAL(int) +SRE_AT(SRE_STATE* state, SRE_CHAR* ptr, SRE_CODE at) +{ + /* check if pointer is at given position */ + + int this, that; + + switch (at) { + + case SRE_AT_BEGINNING: + case SRE_AT_BEGINNING_STRING: + return ((void*) ptr == state->beginning); + + case SRE_AT_BEGINNING_LINE: + return ((void*) ptr == state->beginning || + SRE_IS_LINEBREAK((int) ptr[-1])); + + case SRE_AT_END: + return (((void*) (ptr+1) == state->end && + SRE_IS_LINEBREAK((int) ptr[0])) || + ((void*) ptr == state->end)); + + case SRE_AT_END_LINE: + return ((void*) ptr == state->end || + SRE_IS_LINEBREAK((int) ptr[0])); + + case SRE_AT_END_STRING: + return ((void*) ptr == state->end); + + case SRE_AT_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_IS_WORD((int) ptr[0]) : 0; + return this != that; + + case SRE_AT_NON_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_IS_WORD((int) ptr[0]) : 0; + return this == that; + + case SRE_AT_LOC_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_LOC_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_LOC_IS_WORD((int) ptr[0]) : 0; + return this != that; + + case SRE_AT_LOC_NON_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_LOC_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_LOC_IS_WORD((int) ptr[0]) : 0; + return this == that; + +#if defined(HAVE_UNICODE) + case SRE_AT_UNI_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_UNI_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_UNI_IS_WORD((int) ptr[0]) : 0; + return this != that; + + case SRE_AT_UNI_NON_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_UNI_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_UNI_IS_WORD((int) ptr[0]) : 0; + return this == that; +#endif + + } + + return 0; +} + +LOCAL(int) +SRE_CHARSET(SRE_CODE* set, SRE_CODE ch) +{ + /* check if character is a member of the given set */ + + int ok = 1; + + for (;;) { + switch (*set++) { + + case SRE_OP_LITERAL: + /* */ + if (ch == set[0]) + return ok; + set++; + break; + + case SRE_OP_RANGE: + /* */ + if (set[0] <= ch && ch <= set[1]) + return ok; + set += 2; + break; + + case SRE_OP_CHARSET: + /* (16 bits per code word) */ + if (ch < 256 && (set[ch >> 4] & (1 << (ch & 15)))) + return ok; + set += 16; + break; + + case SRE_OP_BIGCHARSET: + /* <256 blockindices> */ + { + int count, block; + count = *(set++); + block = ((unsigned char*)set)[ch >> 8]; + set += 128; + if (set[block*16 + ((ch & 255)>>4)] & (1 << (ch & 15))) + return ok; + set += count*16; + break; + } + + case SRE_OP_CATEGORY: + /* */ + if (sre_category(set[0], (int) ch)) + return ok; + set += 1; + break; + + case SRE_OP_NEGATE: + ok = !ok; + break; + + case SRE_OP_FAILURE: + return !ok; + + default: + /* internal error -- there's not much we can do about it + here, so let's just pretend it didn't match... */ + return 0; + } + } +} + +LOCAL(int) SRE_MATCH(SRE_STATE* state, SRE_CODE* pattern, int level); + +LOCAL(int) +SRE_COUNT(SRE_STATE* state, SRE_CODE* pattern, int maxcount, int level) +{ + SRE_CODE chr; + SRE_CHAR* ptr = state->ptr; + SRE_CHAR* end = state->end; + int i; + + /* adjust end */ + if (maxcount < end - ptr && maxcount != 65535) + end = ptr + maxcount; + + switch (pattern[0]) { + + case SRE_OP_ANY: + /* repeated dot wildcard. */ + TRACE(("|%p|%p|COUNT ANY\n", pattern, ptr)); + while (ptr < end && !SRE_IS_LINEBREAK(*ptr)) + ptr++; + break; + + case SRE_OP_ANY_ALL: + /* repeated dot wildcare. skip to the end of the target + string, and backtrack from there */ + TRACE(("|%p|%p|COUNT ANY_ALL\n", pattern, ptr)); + ptr = end; + break; + + case SRE_OP_LITERAL: + /* repeated literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT LITERAL %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) *ptr == chr) + ptr++; + break; + + case SRE_OP_LITERAL_IGNORE: + /* repeated literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT LITERAL_IGNORE %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) state->lower(*ptr) == chr) + ptr++; + break; + + case SRE_OP_NOT_LITERAL: + /* repeated non-literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT NOT_LITERAL %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) *ptr != chr) + ptr++; + break; + + case SRE_OP_NOT_LITERAL_IGNORE: + /* repeated non-literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT NOT_LITERAL_IGNORE %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) state->lower(*ptr) != chr) + ptr++; + break; + + case SRE_OP_IN: + /* repeated set */ + TRACE(("|%p|%p|COUNT IN\n", pattern, ptr)); + while (ptr < end && SRE_CHARSET(pattern + 2, *ptr)) + ptr++; + break; + + default: + /* repeated single character pattern */ + TRACE(("|%p|%p|COUNT SUBPATTERN\n", pattern, ptr)); + while ((SRE_CHAR*) state->ptr < end) { + i = SRE_MATCH(state, pattern, level); + if (i < 0) + return i; + if (!i) + break; + } + TRACE(("|%p|%p|COUNT %d\n", pattern, ptr, + (SRE_CHAR*) state->ptr - ptr)); + return (SRE_CHAR*) state->ptr - ptr; + } + + TRACE(("|%p|%p|COUNT %d\n", pattern, ptr, ptr - (SRE_CHAR*) state->ptr)); + return ptr - (SRE_CHAR*) state->ptr; +} + +#if 0 /* not used in this release */ +LOCAL(int) +SRE_INFO(SRE_STATE* state, SRE_CODE* pattern) +{ + /* check if an SRE_OP_INFO block matches at the current position. + returns the number of SRE_CODE objects to skip if successful, 0 + if no match */ + + SRE_CHAR* end = state->end; + SRE_CHAR* ptr = state->ptr; + int i; + + /* check minimal length */ + if (pattern[3] && (end - ptr) < pattern[3]) + return 0; + + /* check known prefix */ + if (pattern[2] & SRE_INFO_PREFIX && pattern[5] > 1) { + /* */ + for (i = 0; i < pattern[5]; i++) + if ((SRE_CODE) ptr[i] != pattern[7 + i]) + return 0; + return pattern[0] + 2 * pattern[6]; + } + return pattern[0]; +} +#endif + +LOCAL(int) +SRE_MATCH(SRE_STATE* state, SRE_CODE* pattern, int level) +{ + /* check if string matches the given pattern. returns <0 for + error, 0 for failure, and 1 for success */ + + SRE_CHAR* end = state->end; + SRE_CHAR* ptr = state->ptr; + int i, count; + SRE_REPEAT* rp; + int lastmark; + SRE_CODE chr; + + SRE_REPEAT rep; /* FIXME: allocate in STATE instead */ + + TRACE(("|%p|%p|ENTER %d\n", pattern, ptr, level)); + +#if defined(USE_STACKCHECK) + if (level % 10 == 0 && PyOS_CheckStack()) + return SRE_ERROR_RECURSION_LIMIT; +#endif + +#if defined(USE_RECURSION_LIMIT) + if (level > USE_RECURSION_LIMIT) + return SRE_ERROR_RECURSION_LIMIT; +#endif + + if (pattern[0] == SRE_OP_INFO) { + /* optimization info block */ + /* <1=skip> <2=flags> <3=min> ... */ + if (pattern[3] && (end - ptr) < pattern[3]) { + TRACE(("reject (got %d chars, need %d)\n", + (end - ptr), pattern[3])); + return 0; + } + pattern += pattern[1] + 1; + } + + for (;;) { + + switch (*pattern++) { + + case SRE_OP_FAILURE: + /* immediate failure */ + TRACE(("|%p|%p|FAILURE\n", pattern, ptr)); + return 0; + + case SRE_OP_SUCCESS: + /* end of pattern */ + TRACE(("|%p|%p|SUCCESS\n", pattern, ptr)); + state->ptr = ptr; + return 1; + + case SRE_OP_AT: + /* match at given position */ + /* */ + TRACE(("|%p|%p|AT %d\n", pattern, ptr, *pattern)); + if (!SRE_AT(state, ptr, *pattern)) + return 0; + pattern++; + break; + + case SRE_OP_CATEGORY: + /* match at given category */ + /* */ + TRACE(("|%p|%p|CATEGORY %d\n", pattern, ptr, *pattern)); + if (ptr >= end || !sre_category(pattern[0], ptr[0])) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_LITERAL: + /* match literal string */ + /* */ + TRACE(("|%p|%p|LITERAL %d\n", pattern, ptr, *pattern)); + if (ptr >= end || (SRE_CODE) ptr[0] != pattern[0]) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_NOT_LITERAL: + /* match anything that is not literal character */ + /* */ + TRACE(("|%p|%p|NOT_LITERAL %d\n", pattern, ptr, *pattern)); + if (ptr >= end || (SRE_CODE) ptr[0] == pattern[0]) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_ANY: + /* match anything (except a newline) */ + /* */ + TRACE(("|%p|%p|ANY\n", pattern, ptr)); + if (ptr >= end || SRE_IS_LINEBREAK(ptr[0])) + return 0; + ptr++; + break; + + case SRE_OP_ANY_ALL: + /* match anything */ + /* */ + TRACE(("|%p|%p|ANY_ALL\n", pattern, ptr)); + if (ptr >= end) + return 0; + ptr++; + break; + + case SRE_OP_IN: + /* match set member (or non_member) */ + /* */ + TRACE(("|%p|%p|IN\n", pattern, ptr)); + if (ptr >= end || !SRE_CHARSET(pattern + 1, *ptr)) + return 0; + pattern += pattern[0]; + ptr++; + break; + + case SRE_OP_GROUPREF: + /* match backreference */ + TRACE(("|%p|%p|GROUPREF %d\n", pattern, ptr, pattern[0])); + i = pattern[0]; + { + SRE_CHAR* p = (SRE_CHAR*) state->mark[i+i]; + SRE_CHAR* e = (SRE_CHAR*) state->mark[i+i+1]; + if (!p || !e || e < p) + return 0; + while (p < e) { + if (ptr >= end || *ptr != *p) + return 0; + p++; ptr++; + } + } + pattern++; + break; + + case SRE_OP_GROUPREF_IGNORE: + /* match backreference */ + TRACE(("|%p|%p|GROUPREF_IGNORE %d\n", pattern, ptr, pattern[0])); + i = pattern[0]; + { + SRE_CHAR* p = (SRE_CHAR*) state->mark[i+i]; + SRE_CHAR* e = (SRE_CHAR*) state->mark[i+i+1]; + if (!p || !e || e < p) + return 0; + while (p < e) { + if (ptr >= end || + state->lower(*ptr) != state->lower(*p)) + return 0; + p++; ptr++; + } + } + pattern++; + break; + + case SRE_OP_LITERAL_IGNORE: + TRACE(("|%p|%p|LITERAL_IGNORE %d\n", pattern, ptr, pattern[0])); + if (ptr >= end || + state->lower(*ptr) != state->lower(*pattern)) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_NOT_LITERAL_IGNORE: + TRACE(("|%p|%p|NOT_LITERAL_IGNORE %d\n", pattern, ptr, *pattern)); + if (ptr >= end || + state->lower(*ptr) == state->lower(*pattern)) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_IN_IGNORE: + TRACE(("|%p|%p|IN_IGNORE\n", pattern, ptr)); + if (ptr >= end + || !SRE_CHARSET(pattern + 1, (SRE_CODE) state->lower(*ptr))) + return 0; + pattern += pattern[0]; + ptr++; + break; + + case SRE_OP_MARK: + /* set mark */ + /* */ + TRACE(("|%p|%p|MARK %d\n", pattern, ptr, pattern[0])); + i = pattern[0]; + if (i > state->lastmark) { + state->lastmark = i; + if (i & 1) + state->lastindex = i/2 + 1; + } + state->mark[i] = ptr; + pattern++; + break; + + case SRE_OP_JUMP: + case SRE_OP_INFO: + /* jump forward */ + /* */ + TRACE(("|%p|%p|JUMP %d\n", pattern, ptr, pattern[0])); + pattern += pattern[0]; + break; + + case SRE_OP_ASSERT: + /* assert subpattern */ + /* */ + TRACE(("|%p|%p|ASSERT %d\n", pattern, ptr, pattern[1])); + state->ptr = ptr - pattern[1]; + if (state->ptr < state->beginning) + return 0; + i = SRE_MATCH(state, pattern + 2, level + 1); + if (i <= 0) + return i; + pattern += pattern[0]; + break; + + case SRE_OP_ASSERT_NOT: + /* assert not subpattern */ + /* */ + TRACE(("|%p|%p|ASSERT_NOT %d\n", pattern, ptr, pattern[1])); + state->ptr = ptr - pattern[1]; + if (state->ptr >= state->beginning) { + i = SRE_MATCH(state, pattern + 2, level + 1); + if (i < 0) + return i; + if (i) + return 0; + } + pattern += pattern[0]; + break; + + case SRE_OP_BRANCH: + /* alternation */ + /* <0=skip> code ... */ + TRACE(("|%p|%p|BRANCH\n", pattern, ptr)); + lastmark = state->lastmark; + for (; pattern[0]; pattern += pattern[0]) { + if (pattern[1] == SRE_OP_LITERAL && + (ptr >= end || (SRE_CODE) *ptr != pattern[2])) + continue; + if (pattern[1] == SRE_OP_IN && + (ptr >= end || !SRE_CHARSET(pattern + 3, (SRE_CODE) *ptr))) + continue; + state->ptr = ptr; + i = SRE_MATCH(state, pattern + 1, level + 1); + if (i) + return i; + lastmark_restore(state, lastmark); + } + return 0; + + case SRE_OP_REPEAT_ONE: + /* match repeated sequence (maximizing regexp) */ + + /* this operator only works if the repeated item is + exactly one character wide, and we're not already + collecting backtracking points. for other cases, + use the MAX_REPEAT operator */ + + /* <1=min> <2=max> item tail */ + + TRACE(("|%p|%p|REPEAT_ONE %d %d\n", pattern, ptr, + pattern[1], pattern[2])); + + if (ptr + pattern[1] > end) + return 0; /* cannot match */ + + state->ptr = ptr; + + count = SRE_COUNT(state, pattern + 3, pattern[2], level + 1); + if (count < 0) + return count; + + ptr += count; + + /* when we arrive here, count contains the number of + matches, and ptr points to the tail of the target + string. check if the rest of the pattern matches, + and backtrack if not. */ + + if (count < (int) pattern[1]) + return 0; + + if (pattern[pattern[0]] == SRE_OP_SUCCESS) { + /* tail is empty. we're finished */ + state->ptr = ptr; + return 1; + + } else if (pattern[pattern[0]] == SRE_OP_LITERAL) { + /* tail starts with a literal. skip positions where + the rest of the pattern cannot possibly match */ + chr = pattern[pattern[0]+1]; + for (;;) { + while (count >= (int) pattern[1] && + (ptr >= end || *ptr != chr)) { + ptr--; + count--; + } + if (count < (int) pattern[1]) + break; + state->ptr = ptr; + i = SRE_MATCH(state, pattern + pattern[0], level + 1); + if (i) + return i; + ptr--; + count--; + } + + } else { + /* general case */ + lastmark = state->lastmark; + while (count >= (int) pattern[1]) { + state->ptr = ptr; + i = SRE_MATCH(state, pattern + pattern[0], level + 1); + if (i) + return i; + ptr--; + count--; + lastmark_restore(state, lastmark); + } + } + return 0; + + case SRE_OP_REPEAT: + /* create repeat context. all the hard work is done + by the UNTIL operator (MAX_UNTIL, MIN_UNTIL) */ + /* <1=min> <2=max> item tail */ + TRACE(("|%p|%p|REPEAT %d %d\n", pattern, ptr, + pattern[1], pattern[2])); + + rep.count = -1; + rep.pattern = pattern; + + /* install new repeat context */ + rep.prev = state->repeat; + state->repeat = &rep; + + state->ptr = ptr; + i = SRE_MATCH(state, pattern + pattern[0], level + 1); + + state->repeat = rep.prev; + + return i; + + case SRE_OP_MAX_UNTIL: + /* maximizing repeat */ + /* <1=min> <2=max> item tail */ + + /* FIXME: we probably need to deal with zero-width + matches in here... */ + + rp = state->repeat; + if (!rp) + return SRE_ERROR_STATE; + + state->ptr = ptr; + + count = rp->count + 1; + + TRACE(("|%p|%p|MAX_UNTIL %d\n", pattern, ptr, count)); + + if (count < rp->pattern[1]) { + /* not enough matches */ + rp->count = count; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + rp->count = count - 1; + state->ptr = ptr; + return 0; + } + + if (count < rp->pattern[2] || rp->pattern[2] == 65535) { + /* we may have enough matches, but if we can + match another item, do so */ + rp->count = count; + lastmark = state->lastmark; + i = mark_save(state, 0, lastmark); + if (i < 0) + return i; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + i = mark_restore(state, 0, lastmark); + if (i < 0) + return i; + lastmark_restore(state, lastmark); + rp->count = count - 1; + state->ptr = ptr; + } + + /* cannot match more repeated items here. make sure the + tail matches */ + state->repeat = rp->prev; + i = SRE_MATCH(state, pattern, level + 1); + if (i) + return i; + state->repeat = rp; + state->ptr = ptr; + return 0; + + case SRE_OP_MIN_UNTIL: + /* minimizing repeat */ + /* <1=min> <2=max> item tail */ + + rp = state->repeat; + if (!rp) + return SRE_ERROR_STATE; + + count = rp->count + 1; + + TRACE(("|%p|%p|MIN_UNTIL %d %p\n", pattern, ptr, count, + rp->pattern)); + + state->ptr = ptr; + + if (count < rp->pattern[1]) { + /* not enough matches */ + rp->count = count; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + rp->count = count-1; + state->ptr = ptr; + return 0; + } + + /* see if the tail matches */ + state->repeat = rp->prev; + i = SRE_MATCH(state, pattern, level + 1); + if (i) + return i; + + state->ptr = ptr; + state->repeat = rp; + + if (count >= rp->pattern[2] && rp->pattern[2] != 65535) + return 0; + + rp->count = count; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + rp->count = count - 1; + state->ptr = ptr; + return 0; + + default: + TRACE(("|%p|%p|UNKNOWN %d\n", pattern, ptr, pattern[-1])); + return SRE_ERROR_ILLEGAL; + } + } + + /* can't end up here */ + /* return SRE_ERROR_ILLEGAL; -- see python-dev discussion */ +} + +LOCAL(int) +SRE_SEARCH(SRE_STATE* state, SRE_CODE* pattern) +{ + SRE_CHAR* ptr = state->start; + SRE_CHAR* end = state->end; + int status = 0; + int prefix_len = 0; + int prefix_skip = 0; + SRE_CODE* prefix = NULL; + SRE_CODE* charset = NULL; + SRE_CODE* overlap = NULL; + int flags = 0; + + if (pattern[0] == SRE_OP_INFO) { + /* optimization info block */ + /* <1=skip> <2=flags> <3=min> <4=max> <5=prefix info> */ + + flags = pattern[2]; + + if (pattern[3] > 0) { + /* adjust end point (but make sure we leave at least one + character in there, so literal search will work) */ + end -= pattern[3]-1; + if (end <= ptr) + end = ptr+1; + } + + if (flags & SRE_INFO_PREFIX) { + /* pattern starts with a known prefix */ + /* */ + prefix_len = pattern[5]; + prefix_skip = pattern[6]; + prefix = pattern + 7; + overlap = prefix + prefix_len - 1; + } else if (flags & SRE_INFO_CHARSET) + /* pattern starts with a character from a known set */ + /* */ + charset = pattern + 5; + + pattern += 1 + pattern[1]; + } + + TRACE(("prefix = %p %d %d\n", prefix, prefix_len, prefix_skip)); + TRACE(("charset = %p\n", charset)); + +#if defined(USE_FAST_SEARCH) + if (prefix_len > 1) { + /* pattern starts with a known prefix. use the overlap + table to skip forward as fast as we possibly can */ + int i = 0; + end = state->end; + while (ptr < end) { + for (;;) { + if ((SRE_CODE) ptr[0] != prefix[i]) { + if (!i) + break; + else + i = overlap[i]; + } else { + if (++i == prefix_len) { + /* found a potential match */ + TRACE(("|%p|%p|SEARCH SCAN\n", pattern, ptr)); + state->start = ptr + 1 - prefix_len; + state->ptr = ptr + 1 - prefix_len + prefix_skip; + if (flags & SRE_INFO_LITERAL) + return 1; /* we got all of it */ + status = SRE_MATCH(state, pattern + 2*prefix_skip, 1); + if (status != 0) + return status; + /* close but no cigar -- try again */ + i = overlap[i]; + } + break; + } + + } + ptr++; + } + return 0; + } +#endif + + if (pattern[0] == SRE_OP_LITERAL) { + /* pattern starts with a literal character. this is used + for short prefixes, and if fast search is disabled */ + SRE_CODE chr = pattern[1]; + end = state->end; + for (;;) { + while (ptr < end && (SRE_CODE) ptr[0] != chr) + ptr++; + if (ptr >= end) + return 0; + TRACE(("|%p|%p|SEARCH LITERAL\n", pattern, ptr)); + state->start = ptr; + state->ptr = ++ptr; + if (flags & SRE_INFO_LITERAL) + return 1; /* we got all of it */ + status = SRE_MATCH(state, pattern + 2, 1); + if (status != 0) + break; + } + } else if (charset) { + /* pattern starts with a character from a known set */ + end = state->end; + for (;;) { + while (ptr < end && !SRE_CHARSET(charset, ptr[0])) + ptr++; + if (ptr >= end) + return 0; + TRACE(("|%p|%p|SEARCH CHARSET\n", pattern, ptr)); + state->start = ptr; + state->ptr = ptr; + status = SRE_MATCH(state, pattern, 1); + if (status != 0) + break; + ptr++; + } + } else + /* general case */ + while (ptr <= end) { + TRACE(("|%p|%p|SEARCH\n", pattern, ptr)); + state->start = state->ptr = ptr++; + status = SRE_MATCH(state, pattern, 1); + if (status != 0) + break; + } + + return status; +} + +LOCAL(int) +SRE_LITERAL_TEMPLATE(SRE_CHAR* ptr, int len) +{ + /* check if given string is a literal template (i.e. no escapes) */ + while (len-- > 0) + if (*ptr++ == '\\') + return 0; + return 1; +} + +#if !defined(SRE_RECURSIVE) + +/* -------------------------------------------------------------------- */ +/* factories and destructors */ + +/* see sre.h for object declarations */ + +static PyTypeObject Pattern_Type; +static PyTypeObject Match_Type; +static PyTypeObject Scanner_Type; + +static PyObject * +_compile(PyObject* self_, PyObject* args) +{ + /* "compile" pattern descriptor to pattern object */ + + PatternObject* self; + int i, n; + + PyObject* pattern; + int flags = 0; + PyObject* code; + int groups = 0; + PyObject* groupindex = NULL; + PyObject* indexgroup = NULL; + if (!PyArg_ParseTuple(args, "OiO!|iOO", &pattern, &flags, + &PyList_Type, &code, &groups, + &groupindex, &indexgroup)) + return NULL; + + n = PyList_GET_SIZE(code); + + self = PyObject_NEW_VAR(PatternObject, &Pattern_Type, n); + if (!self) + return NULL; + + self->codesize = n; + + for (i = 0; i < n; i++) { + PyObject *o = PyList_GET_ITEM(code, i); + self->code[i] = (SRE_CODE) PyInt_AsLong(o); + } + + if (PyErr_Occurred()) { + PyObject_DEL(self); + return NULL; + } + + Py_INCREF(pattern); + self->pattern = pattern; + + self->flags = flags; + + self->groups = groups; + + Py_XINCREF(groupindex); + self->groupindex = groupindex; + + Py_XINCREF(indexgroup); + self->indexgroup = indexgroup; + + return (PyObject*) self; +} + +static PyObject * +sre_codesize(PyObject* self, PyObject* args) +{ + return Py_BuildValue("i", sizeof(SRE_CODE)); +} + +static PyObject * +sre_getlower(PyObject* self, PyObject* args) +{ + int character, flags; + if (!PyArg_ParseTuple(args, "ii", &character, &flags)) + return NULL; + if (flags & SRE_FLAG_LOCALE) + return Py_BuildValue("i", sre_lower_locale(character)); + if (flags & SRE_FLAG_UNICODE) +#if defined(HAVE_UNICODE) + return Py_BuildValue("i", sre_lower_unicode(character)); +#else + return Py_BuildValue("i", sre_lower_locale(character)); +#endif + return Py_BuildValue("i", sre_lower(character)); +} + +LOCAL(void) +state_reset(SRE_STATE* state) +{ + state->lastmark = 0; + + /* FIXME: dynamic! */ + memset(state->mark, 0, sizeof(*state->mark) * SRE_MARK_SIZE); + + state->lastindex = -1; + + state->repeat = NULL; + + mark_fini(state); +} + +static void* +getstring(PyObject* string, int* p_length, int* p_charsize) +{ + /* given a python object, return a data pointer, a length (in + characters), and a character size. return NULL if the object + is not a string (or not compatible) */ + + PyBufferProcs *buffer; + int size, bytes, charsize; + void* ptr; + +#if defined(HAVE_UNICODE) + if (PyUnicode_Check(string)) { + /* unicode strings doesn't always support the buffer interface */ + ptr = (void*) PyUnicode_AS_DATA(string); + bytes = PyUnicode_GET_DATA_SIZE(string); + size = PyUnicode_GET_SIZE(string); + charsize = sizeof(Py_UNICODE); + + } else { +#endif + + /* get pointer to string buffer */ + buffer = string->ob_type->tp_as_buffer; + if (!buffer || !buffer->bf_getreadbuffer || !buffer->bf_getsegcount || + buffer->bf_getsegcount(string, NULL) != 1) { + PyErr_SetString(PyExc_TypeError, "expected string or buffer"); + return NULL; + } + + /* determine buffer size */ + bytes = buffer->bf_getreadbuffer(string, 0, &ptr); + if (bytes < 0) { + PyErr_SetString(PyExc_TypeError, "buffer has negative size"); + return NULL; + } + + /* determine character size */ +#if PY_VERSION_HEX >= 0x01060000 + size = PyObject_Size(string); +#else + size = PyObject_Length(string); +#endif + + if (PyString_Check(string) || bytes == size) + charsize = 1; +#if defined(HAVE_UNICODE) + else if (bytes == (int) (size * sizeof(Py_UNICODE))) + charsize = sizeof(Py_UNICODE); +#endif + else { + PyErr_SetString(PyExc_TypeError, "buffer size mismatch"); + return NULL; + } + +#if defined(HAVE_UNICODE) + } +#endif + + *p_length = size; + *p_charsize = charsize; + + return ptr; +} + +LOCAL(PyObject*) +state_init(SRE_STATE* state, PatternObject* pattern, PyObject* string, + int start, int end) +{ + /* prepare state object */ + + int length; + int charsize; + void* ptr; + + memset(state, 0, sizeof(SRE_STATE)); + + state->lastindex = -1; + + ptr = getstring(string, &length, &charsize); + if (!ptr) + return NULL; + + /* adjust boundaries */ + if (start < 0) + start = 0; + else if (start > length) + start = length; + + if (end < 0) + end = 0; + else if (end > length) + end = length; + + state->charsize = charsize; + + state->beginning = ptr; + + state->start = (void*) ((char*) ptr + start * state->charsize); + state->end = (void*) ((char*) ptr + end * state->charsize); + + Py_INCREF(string); + state->string = string; + state->pos = start; + state->endpos = end; + + if (pattern->flags & SRE_FLAG_LOCALE) + state->lower = sre_lower_locale; + else if (pattern->flags & SRE_FLAG_UNICODE) +#if defined(HAVE_UNICODE) + state->lower = sre_lower_unicode; +#else + state->lower = sre_lower_locale; +#endif + else + state->lower = sre_lower; + + return string; +} + +LOCAL(void) +state_fini(SRE_STATE* state) +{ + Py_XDECREF(state->string); + mark_fini(state); +} + +/* calculate offset from start of string */ +#define STATE_OFFSET(state, member)\ + (((char*)(member) - (char*)(state)->beginning) / (state)->charsize) + +LOCAL(PyObject*) +state_getslice(SRE_STATE* state, int index, PyObject* string, int empty) +{ + int i, j; + + index = (index - 1) * 2; + + if (string == Py_None || !state->mark[index] || !state->mark[index+1]) { + if (empty) + /* want empty string */ + i = j = 0; + else { + Py_INCREF(Py_None); + return Py_None; + } + } else { + i = STATE_OFFSET(state, state->mark[index]); + j = STATE_OFFSET(state, state->mark[index+1]); + } + + return PySequence_GetSlice(string, i, j); +} + +static void +pattern_error(int status) +{ + switch (status) { + case SRE_ERROR_RECURSION_LIMIT: + PyErr_SetString( + PyExc_RuntimeError, + "maximum recursion limit exceeded" + ); + break; + case SRE_ERROR_MEMORY: + PyErr_NoMemory(); + break; + default: + /* other error codes indicate compiler/engine bugs */ + PyErr_SetString( + PyExc_RuntimeError, + "internal error in regular expression engine" + ); + } +} + +static PyObject* +pattern_new_match(PatternObject* pattern, SRE_STATE* state, int status) +{ + /* create match object (from state object) */ + + MatchObject* match; + int i, j; + char* base; + int n; + + if (status > 0) { + + /* create match object (with room for extra group marks) */ + match = PyObject_NEW_VAR(MatchObject, &Match_Type, + 2*(pattern->groups+1)); + if (!match) + return NULL; + + Py_INCREF(pattern); + match->pattern = pattern; + + Py_INCREF(state->string); + match->string = state->string; + + match->regs = NULL; + match->groups = pattern->groups+1; + + /* fill in group slices */ + + base = (char*) state->beginning; + n = state->charsize; + + match->mark[0] = ((char*) state->start - base) / n; + match->mark[1] = ((char*) state->ptr - base) / n; + + for (i = j = 0; i < pattern->groups; i++, j+=2) + if (j+1 <= state->lastmark && state->mark[j] && state->mark[j+1]) { + match->mark[j+2] = ((char*) state->mark[j] - base) / n; + match->mark[j+3] = ((char*) state->mark[j+1] - base) / n; + } else + match->mark[j+2] = match->mark[j+3] = -1; /* undefined */ + + match->pos = state->pos; + match->endpos = state->endpos; + + match->lastindex = state->lastindex; + + return (PyObject*) match; + + } else if (status == 0) { + + /* no match */ + Py_INCREF(Py_None); + return Py_None; + + } + + /* internal error */ + pattern_error(status); + return NULL; +} + +static PyObject* +pattern_scanner(PatternObject* pattern, PyObject* args) +{ + /* create search state object */ + + ScannerObject* self; + + PyObject* string; + int start = 0; + int end = INT_MAX; + if (!PyArg_ParseTuple(args, "O|ii:scanner", &string, &start, &end)) + return NULL; + + /* create scanner object */ + self = PyObject_NEW(ScannerObject, &Scanner_Type); + if (!self) + return NULL; + + string = state_init(&self->state, pattern, string, start, end); + if (!string) { + PyObject_DEL(self); + return NULL; + } + + Py_INCREF(pattern); + self->pattern = (PyObject*) pattern; + + return (PyObject*) self; +} + +static void +pattern_dealloc(PatternObject* self) +{ + Py_XDECREF(self->pattern); + Py_XDECREF(self->groupindex); + Py_XDECREF(self->indexgroup); + PyObject_DEL(self); +} + +static PyObject* +pattern_match(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + int status; + + PyObject* string; + int start = 0; + int end = INT_MAX; + static char* kwlist[] = { "pattern", "pos", "endpos", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:match", kwlist, + &string, &start, &end)) + return NULL; + + string = state_init(&state, self, string, start, end); + if (!string) + return NULL; + + state.ptr = state.start; + + TRACE(("|%p|%p|MATCH\n", PatternObject_GetCode(self), state.ptr)); + + if (state.charsize == 1) { + status = sre_match(&state, PatternObject_GetCode(self), 1); + } else { +#if defined(HAVE_UNICODE) + status = sre_umatch(&state, PatternObject_GetCode(self), 1); +#endif + } + + TRACE(("|%p|%p|END\n", PatternObject_GetCode(self), state.ptr)); + + state_fini(&state); + + return pattern_new_match(self, &state, status); +} + +static PyObject* +pattern_search(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + int status; + + PyObject* string; + int start = 0; + int end = INT_MAX; + static char* kwlist[] = { "pattern", "pos", "endpos", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:search", kwlist, + &string, &start, &end)) + return NULL; + + string = state_init(&state, self, string, start, end); + if (!string) + return NULL; + + TRACE(("|%p|%p|SEARCH\n", PatternObject_GetCode(self), state.ptr)); + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + TRACE(("|%p|%p|END\n", PatternObject_GetCode(self), state.ptr)); + + state_fini(&state); + + return pattern_new_match(self, &state, status); +} + +static PyObject* +call(char* module, char* function, PyObject* args) +{ + PyObject* name; + PyObject* mod; + PyObject* func; + PyObject* result; + + if (!args) + return NULL; + name = PyString_FromString(module); + if (!name) + return NULL; + mod = PyImport_Import(name); + Py_DECREF(name); + if (!mod) + return NULL; + func = PyObject_GetAttrString(mod, function); + Py_DECREF(mod); + if (!func) + return NULL; + result = PyObject_CallObject(func, args); + Py_DECREF(func); + Py_DECREF(args); + return result; +} + +#ifdef USE_BUILTIN_COPY +static int +deepcopy(PyObject** object, PyObject* memo) +{ + PyObject* copy; + + copy = call( + "copy", "deepcopy", + Py_BuildValue("OO", *object, memo) + ); + if (!copy) + return 0; + + Py_DECREF(*object); + *object = copy; + + return 1; /* success */ +} +#endif + +static PyObject* +join_list(PyObject* list, PyObject* pattern) +{ + /* join list elements */ + + PyObject* joiner; +#if PY_VERSION_HEX >= 0x01060000 + PyObject* function; + PyObject* args; +#endif + PyObject* result; + + switch (PyList_GET_SIZE(list)) { + case 0: + Py_DECREF(list); + return PyString_FromString(""); + case 1: + result = PyList_GET_ITEM(list, 0); + Py_INCREF(result); + Py_DECREF(list); + return result; + } + + /* two or more elements: slice out a suitable separator from the + first member, and use that to join the entire list */ + + joiner = PySequence_GetSlice(pattern, 0, 0); + if (!joiner) + return NULL; + +#if PY_VERSION_HEX >= 0x01060000 + function = PyObject_GetAttrString(joiner, "join"); + if (!function) { + Py_DECREF(joiner); + return NULL; + } + args = PyTuple_New(1); + if (!args) { + Py_DECREF(function); + Py_DECREF(joiner); + return NULL; + } + PyTuple_SET_ITEM(args, 0, list); + result = PyObject_CallObject(function, args); + Py_DECREF(args); /* also removes list */ + Py_DECREF(function); +#else + result = call( + "string", "join", + Py_BuildValue("OO", list, joiner) + ); +#endif + Py_DECREF(joiner); + + return result; +} + +static PyObject* +pattern_findall(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + PyObject* list; + int status; + int i, b, e; + + PyObject* string; + int start = 0; + int end = INT_MAX; + static char* kwlist[] = { "source", "pos", "endpos", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:findall", kwlist, + &string, &start, &end)) + return NULL; + + string = state_init(&state, self, string, start, end); + if (!string) + return NULL; + + list = PyList_New(0); + if (!list) { + state_fini(&state); + return NULL; + } + + while (state.start <= state.end) { + + PyObject* item; + + state_reset(&state); + + state.ptr = state.start; + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + if (status <= 0) { + if (status == 0) + break; + pattern_error(status); + goto error; + } + + /* don't bother to build a match object */ + switch (self->groups) { + case 0: + b = STATE_OFFSET(&state, state.start); + e = STATE_OFFSET(&state, state.ptr); + item = PySequence_GetSlice(string, b, e); + if (!item) + goto error; + break; + case 1: + item = state_getslice(&state, 1, string, 1); + if (!item) + goto error; + break; + default: + item = PyTuple_New(self->groups); + if (!item) + goto error; + for (i = 0; i < self->groups; i++) { + PyObject* o = state_getslice(&state, i+1, string, 1); + if (!o) { + Py_DECREF(item); + goto error; + } + PyTuple_SET_ITEM(item, i, o); + } + break; + } + + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + if (state.ptr == state.start) + state.start = (void*) ((char*) state.ptr + state.charsize); + else + state.start = state.ptr; + + } + + state_fini(&state); + return list; + +error: + Py_DECREF(list); + state_fini(&state); + return NULL; + +} + +#if PY_VERSION_HEX >= 0x02020000 +static PyObject* +pattern_finditer(PatternObject* pattern, PyObject* args) +{ + PyObject* scanner; + PyObject* search; + PyObject* iterator; + + scanner = pattern_scanner(pattern, args); + if (!scanner) + return NULL; + + search = PyObject_GetAttrString(scanner, "search"); + Py_DECREF(scanner); + if (!search) + return NULL; + + iterator = PyCallIter_New(search, Py_None); + Py_DECREF(search); + + return iterator; +} +#endif + +static PyObject* +pattern_split(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + PyObject* list; + PyObject* item; + int status; + int n; + int i; + void* last; + + PyObject* string; + int maxsplit = 0; + static char* kwlist[] = { "source", "maxsplit", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|i:split", kwlist, + &string, &maxsplit)) + return NULL; + + string = state_init(&state, self, string, 0, INT_MAX); + if (!string) + return NULL; + + list = PyList_New(0); + if (!list) { + state_fini(&state); + return NULL; + } + + n = 0; + last = state.start; + + while (!maxsplit || n < maxsplit) { + + state_reset(&state); + + state.ptr = state.start; + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + if (status <= 0) { + if (status == 0) + break; + pattern_error(status); + goto error; + } + + if (state.start == state.ptr) { + if (last == state.end) + break; + /* skip one character */ + state.start = (void*) ((char*) state.ptr + state.charsize); + continue; + } + + /* get segment before this match */ + item = PySequence_GetSlice( + string, STATE_OFFSET(&state, last), + STATE_OFFSET(&state, state.start) + ); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + /* add groups (if any) */ + for (i = 0; i < self->groups; i++) { + item = state_getslice(&state, i+1, string, 0); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + n = n + 1; + + last = state.start = state.ptr; + + } + + /* get segment following last match (even if empty) */ + item = PySequence_GetSlice( + string, STATE_OFFSET(&state, last), state.endpos + ); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + state_fini(&state); + return list; + +error: + Py_DECREF(list); + state_fini(&state); + return NULL; + +} + +static PyObject* +pattern_subx(PatternObject* self, PyObject* template, PyObject* string, + int count, int subn) +{ + SRE_STATE state; + PyObject* list; + PyObject* item; + PyObject* filter; + PyObject* args; + PyObject* match; + void* ptr; + int status; + int n; + int i, b, e; + int filter_is_callable; + + if (PyCallable_Check(template)) { + /* sub/subn takes either a function or a template */ + filter = template; + Py_INCREF(filter); + filter_is_callable = 1; + } else { + /* if not callable, check if it's a literal string */ + int literal; + ptr = getstring(template, &n, &b); + if (ptr) { + if (b == 1) { + literal = sre_literal_template(ptr, n); + } else { +#if defined(HAVE_UNICODE) + literal = sre_uliteral_template(ptr, n); +#endif + } + } else { + PyErr_Clear(); + literal = 0; + } + if (literal) { + filter = template; + Py_INCREF(filter); + filter_is_callable = 0; + } else { + /* not a literal; hand it over to the template compiler */ + filter = call( + SRE_MODULE, "_subx", + Py_BuildValue("OO", self, template) + ); + if (!filter) + return NULL; + filter_is_callable = PyCallable_Check(filter); + } + } + + string = state_init(&state, self, string, 0, INT_MAX); + if (!string) { + Py_DECREF(filter); + return NULL; + } + + list = PyList_New(0); + if (!list) { + Py_DECREF(filter); + state_fini(&state); + return NULL; + } + + n = i = 0; + + while (!count || n < count) { + + state_reset(&state); + + state.ptr = state.start; + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + if (status <= 0) { + if (status == 0) + break; + pattern_error(status); + goto error; + } + + b = STATE_OFFSET(&state, state.start); + e = STATE_OFFSET(&state, state.ptr); + + if (i < b) { + /* get segment before this match */ + item = PySequence_GetSlice(string, i, b); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + } else if (i == b && i == e && n > 0) + /* ignore empty match on latest position */ + goto next; + + if (filter_is_callable) { + /* pass match object through filter */ + match = pattern_new_match(self, &state, 1); + if (!match) + goto error; + args = Py_BuildValue("(O)", match); + if (!args) { + Py_DECREF(match); + goto error; + } + item = PyObject_CallObject(filter, args); + Py_DECREF(args); + Py_DECREF(match); + if (!item) + goto error; + } else { + /* filter is literal string */ + item = filter; + Py_INCREF(item); + } + + /* add to list */ + if (item != Py_None) { + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + i = e; + n = n + 1; + +next: + /* move on */ + if (state.ptr == state.start) + state.start = (void*) ((char*) state.ptr + state.charsize); + else + state.start = state.ptr; + + } + + /* get segment following last match */ + if (i < state.endpos) { + item = PySequence_GetSlice(string, i, state.endpos); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + state_fini(&state); + + Py_DECREF(filter); + + /* convert list to single string (also removes list) */ + item = join_list(list, self->pattern); + + if (!item) + return NULL; + + if (subn) + return Py_BuildValue("Ni", item, n); + + return item; + +error: + Py_DECREF(list); + state_fini(&state); + Py_DECREF(filter); + return NULL; + +} + +static PyObject* +pattern_sub(PatternObject* self, PyObject* args, PyObject* kw) +{ + PyObject* template; + PyObject* string; + int count = 0; + static char* kwlist[] = { "repl", "string", "count", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|i:sub", kwlist, + &template, &string, &count)) + return NULL; + + return pattern_subx(self, template, string, count, 0); +} + +static PyObject* +pattern_subn(PatternObject* self, PyObject* args, PyObject* kw) +{ + PyObject* template; + PyObject* string; + int count = 0; + static char* kwlist[] = { "repl", "string", "count", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|i:subn", kwlist, + &template, &string, &count)) + return NULL; + + return pattern_subx(self, template, string, count, 1); +} + +static PyObject* +pattern_copy(PatternObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + PatternObject* copy; + int offset; + + if (args != Py_None && !PyArg_ParseTuple(args, ":__copy__")) + return NULL; + + copy = PyObject_NEW_VAR(PatternObject, &Pattern_Type, self->codesize); + if (!copy) + return NULL; + + offset = offsetof(PatternObject, groups); + + Py_XINCREF(self->groupindex); + Py_XINCREF(self->indexgroup); + Py_XINCREF(self->pattern); + + memcpy((char*) copy + offset, (char*) self + offset, + sizeof(PatternObject) + self->codesize * sizeof(SRE_CODE) - offset); + + return (PyObject*) copy; +#else + PyErr_SetString(PyExc_TypeError, "cannot copy this pattern object"); + return NULL; +#endif +} + +static PyObject* +pattern_deepcopy(PatternObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + PatternObject* copy; + + PyObject* memo; + if (!PyArg_ParseTuple(args, "O:__deepcopy__", &memo)) + return NULL; + + copy = (PatternObject*) pattern_copy(self, Py_None); + if (!copy) + return NULL; + + if (!deepcopy(©->groupindex, memo) || + !deepcopy(©->indexgroup, memo) || + !deepcopy(©->pattern, memo)) { + Py_DECREF(copy); + return NULL; + } + +#else + PyErr_SetString(PyExc_TypeError, "cannot deepcopy this pattern object"); + return NULL; +#endif +} + +static PyMethodDef pattern_methods[] = { + {"match", (PyCFunction) pattern_match, METH_VARARGS|METH_KEYWORDS}, + {"search", (PyCFunction) pattern_search, METH_VARARGS|METH_KEYWORDS}, + {"sub", (PyCFunction) pattern_sub, METH_VARARGS|METH_KEYWORDS}, + {"subn", (PyCFunction) pattern_subn, METH_VARARGS|METH_KEYWORDS}, + {"split", (PyCFunction) pattern_split, METH_VARARGS|METH_KEYWORDS}, + {"findall", (PyCFunction) pattern_findall, METH_VARARGS|METH_KEYWORDS}, +#if PY_VERSION_HEX >= 0x02020000 + {"finditer", (PyCFunction) pattern_finditer, METH_VARARGS}, +#endif + {"scanner", (PyCFunction) pattern_scanner, METH_VARARGS}, + {"__copy__", (PyCFunction) pattern_copy, METH_VARARGS}, + {"__deepcopy__", (PyCFunction) pattern_deepcopy, METH_VARARGS}, + {NULL, NULL} +}; + +static PyObject* +pattern_getattr(PatternObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(pattern_methods, (PyObject*) self, name); + + if (res) + return res; + + PyErr_Clear(); + + /* attributes */ + if (!strcmp(name, "pattern")) { + Py_INCREF(self->pattern); + return self->pattern; + } + + if (!strcmp(name, "flags")) + return Py_BuildValue("i", self->flags); + + if (!strcmp(name, "groups")) + return Py_BuildValue("i", self->groups); + + if (!strcmp(name, "groupindex") && self->groupindex) { + Py_INCREF(self->groupindex); + return self->groupindex; + } + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +statichere PyTypeObject Pattern_Type = { + PyObject_HEAD_INIT(NULL) + 0, "_" SRE_MODULE ".SRE_Pattern", + sizeof(PatternObject), sizeof(SRE_CODE), + (destructor)pattern_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)pattern_getattr /*tp_getattr*/ +}; + +/* -------------------------------------------------------------------- */ +/* match methods */ + +static void +match_dealloc(MatchObject* self) +{ + Py_XDECREF(self->regs); + Py_XDECREF(self->string); + Py_DECREF(self->pattern); + PyObject_DEL(self); +} + +static PyObject* +match_getslice_by_index(MatchObject* self, int index, PyObject* def) +{ + if (index < 0 || index >= self->groups) { + /* raise IndexError if we were given a bad group number */ + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + index *= 2; + + if (self->string == Py_None || self->mark[index] < 0) { + /* return default value if the string or group is undefined */ + Py_INCREF(def); + return def; + } + + return PySequence_GetSlice( + self->string, self->mark[index], self->mark[index+1] + ); +} + +static int +match_getindex(MatchObject* self, PyObject* index) +{ + int i; + + if (PyInt_Check(index)) + return (int) PyInt_AS_LONG(index); + + i = -1; + + if (self->pattern->groupindex) { + index = PyObject_GetItem(self->pattern->groupindex, index); + if (index) { + if (PyInt_Check(index)) + i = (int) PyInt_AS_LONG(index); + Py_DECREF(index); + } else + PyErr_Clear(); + } + + return i; +} + +static PyObject* +match_getslice(MatchObject* self, PyObject* index, PyObject* def) +{ + return match_getslice_by_index(self, match_getindex(self, index), def); +} + +static PyObject* +match_expand(MatchObject* self, PyObject* args) +{ + PyObject* template; + if (!PyArg_ParseTuple(args, "O:expand", &template)) + return NULL; + + /* delegate to Python code */ + return call( + SRE_MODULE, "_expand", + Py_BuildValue("OOO", self->pattern, self, template) + ); +} + +static PyObject* +match_group(MatchObject* self, PyObject* args) +{ + PyObject* result; + int i, size; + + size = PyTuple_GET_SIZE(args); + + switch (size) { + case 0: + result = match_getslice(self, Py_False, Py_None); + break; + case 1: + result = match_getslice(self, PyTuple_GET_ITEM(args, 0), Py_None); + break; + default: + /* fetch multiple items */ + result = PyTuple_New(size); + if (!result) + return NULL; + for (i = 0; i < size; i++) { + PyObject* item = match_getslice( + self, PyTuple_GET_ITEM(args, i), Py_None + ); + if (!item) { + Py_DECREF(result); + return NULL; + } + PyTuple_SET_ITEM(result, i, item); + } + break; + } + return result; +} + +static PyObject* +match_groups(MatchObject* self, PyObject* args, PyObject* kw) +{ + PyObject* result; + int index; + + PyObject* def = Py_None; + static char* kwlist[] = { "default", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:groups", kwlist, &def)) + return NULL; + + result = PyTuple_New(self->groups-1); + if (!result) + return NULL; + + for (index = 1; index < self->groups; index++) { + PyObject* item; + item = match_getslice_by_index(self, index, def); + if (!item) { + Py_DECREF(result); + return NULL; + } + PyTuple_SET_ITEM(result, index-1, item); + } + + return result; +} + +static PyObject* +match_groupdict(MatchObject* self, PyObject* args, PyObject* kw) +{ + PyObject* result; + PyObject* keys; + int index; + + PyObject* def = Py_None; + static char* kwlist[] = { "default", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:groupdict", kwlist, &def)) + return NULL; + + result = PyDict_New(); + if (!result || !self->pattern->groupindex) + return result; + + keys = PyMapping_Keys(self->pattern->groupindex); + if (!keys) + goto failed; + + for (index = 0; index < PyList_GET_SIZE(keys); index++) { + int status; + PyObject* key; + PyObject* value; + key = PyList_GET_ITEM(keys, index); + if (!key) + goto failed; + value = match_getslice(self, key, def); + if (!value) { + Py_DECREF(key); + goto failed; + } + status = PyDict_SetItem(result, key, value); + Py_DECREF(value); + if (status < 0) + goto failed; + } + + Py_DECREF(keys); + + return result; + +failed: + Py_DECREF(keys); + Py_DECREF(result); + return NULL; +} + +static PyObject* +match_start(MatchObject* self, PyObject* args) +{ + int index; + + PyObject* index_ = Py_False; /* zero */ + if (!PyArg_ParseTuple(args, "|O:start", &index_)) + return NULL; + + index = match_getindex(self, index_); + + if (index < 0 || index >= self->groups) { + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + /* mark is -1 if group is undefined */ + return Py_BuildValue("i", self->mark[index*2]); +} + +static PyObject* +match_end(MatchObject* self, PyObject* args) +{ + int index; + + PyObject* index_ = Py_False; /* zero */ + if (!PyArg_ParseTuple(args, "|O:end", &index_)) + return NULL; + + index = match_getindex(self, index_); + + if (index < 0 || index >= self->groups) { + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + /* mark is -1 if group is undefined */ + return Py_BuildValue("i", self->mark[index*2+1]); +} + +LOCAL(PyObject*) +_pair(int i1, int i2) +{ + PyObject* pair; + PyObject* item; + + pair = PyTuple_New(2); + if (!pair) + return NULL; + + item = PyInt_FromLong(i1); + if (!item) + goto error; + PyTuple_SET_ITEM(pair, 0, item); + + item = PyInt_FromLong(i2); + if (!item) + goto error; + PyTuple_SET_ITEM(pair, 1, item); + + return pair; + + error: + Py_DECREF(pair); + return NULL; +} + +static PyObject* +match_span(MatchObject* self, PyObject* args) +{ + int index; + + PyObject* index_ = Py_False; /* zero */ + if (!PyArg_ParseTuple(args, "|O:span", &index_)) + return NULL; + + index = match_getindex(self, index_); + + if (index < 0 || index >= self->groups) { + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + /* marks are -1 if group is undefined */ + return _pair(self->mark[index*2], self->mark[index*2+1]); +} + +static PyObject* +match_regs(MatchObject* self) +{ + PyObject* regs; + PyObject* item; + int index; + + regs = PyTuple_New(self->groups); + if (!regs) + return NULL; + + for (index = 0; index < self->groups; index++) { + item = _pair(self->mark[index*2], self->mark[index*2+1]); + if (!item) { + Py_DECREF(regs); + return NULL; + } + PyTuple_SET_ITEM(regs, index, item); + } + + Py_INCREF(regs); + self->regs = regs; + + return regs; +} + +static PyObject* +match_copy(MatchObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + MatchObject* copy; + int slots, offset; + + if (args != Py_None && !PyArg_ParseTuple(args, ":__copy__")) + return NULL; + + slots = 2 * (self->pattern->groups+1); + + copy = PyObject_NEW_VAR(MatchObject, &Match_Type, slots); + if (!copy) + return NULL; + + /* this value a constant, but any compiler should be able to + figure that out all by itself */ + offset = offsetof(MatchObject, string); + + Py_XINCREF(self->pattern); + Py_XINCREF(self->string); + Py_XINCREF(self->regs); + + memcpy((char*) copy + offset, (char*) self + offset, + sizeof(MatchObject) + slots * sizeof(int) - offset); + + return (PyObject*) copy; +#else + PyErr_SetString(PyExc_TypeError, "cannot copy this match object"); + return NULL; +#endif +} + +static PyObject* +match_deepcopy(MatchObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + MatchObject* copy; + + PyObject* memo; + if (!PyArg_ParseTuple(args, "O:__deepcopy__", &memo)) + return NULL; + + copy = (MatchObject*) match_copy(self, Py_None); + if (!copy) + return NULL; + + if (!deepcopy((PyObject**) ©->pattern, memo) || + !deepcopy(©->string, memo) || + !deepcopy(©->regs, memo)) { + Py_DECREF(copy); + return NULL; + } + +#else + PyErr_SetString(PyExc_TypeError, "cannot deepcopy this match object"); + return NULL; +#endif +} + +static PyMethodDef match_methods[] = { + {"group", (PyCFunction) match_group, METH_VARARGS}, + {"start", (PyCFunction) match_start, METH_VARARGS}, + {"end", (PyCFunction) match_end, METH_VARARGS}, + {"span", (PyCFunction) match_span, METH_VARARGS}, + {"groups", (PyCFunction) match_groups, METH_VARARGS|METH_KEYWORDS}, + {"groupdict", (PyCFunction) match_groupdict, METH_VARARGS|METH_KEYWORDS}, + {"expand", (PyCFunction) match_expand, METH_VARARGS}, + {"__copy__", (PyCFunction) match_copy, METH_VARARGS}, + {"__deepcopy__", (PyCFunction) match_deepcopy, METH_VARARGS}, + {NULL, NULL} +}; + +static PyObject* +match_getattr(MatchObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(match_methods, (PyObject*) self, name); + if (res) + return res; + + PyErr_Clear(); + + if (!strcmp(name, "lastindex")) { + if (self->lastindex >= 0) + return Py_BuildValue("i", self->lastindex); + Py_INCREF(Py_None); + return Py_None; + } + + if (!strcmp(name, "lastgroup")) { + if (self->pattern->indexgroup && self->lastindex >= 0) { + PyObject* result = PySequence_GetItem( + self->pattern->indexgroup, self->lastindex + ); + if (result) + return result; + PyErr_Clear(); + } + Py_INCREF(Py_None); + return Py_None; + } + + if (!strcmp(name, "string")) { + if (self->string) { + Py_INCREF(self->string); + return self->string; + } else { + Py_INCREF(Py_None); + return Py_None; + } + } + + if (!strcmp(name, "regs")) { + if (self->regs) { + Py_INCREF(self->regs); + return self->regs; + } else + return match_regs(self); + } + + if (!strcmp(name, "re")) { + Py_INCREF(self->pattern); + return (PyObject*) self->pattern; + } + + if (!strcmp(name, "pos")) + return Py_BuildValue("i", self->pos); + + if (!strcmp(name, "endpos")) + return Py_BuildValue("i", self->endpos); + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +/* FIXME: implement setattr("string", None) as a special case (to + detach the associated string, if any */ + +statichere PyTypeObject Match_Type = { + PyObject_HEAD_INIT(NULL) + 0, "_" SRE_MODULE ".SRE_Match", + sizeof(MatchObject), sizeof(int), + (destructor)match_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)match_getattr /*tp_getattr*/ +}; + +/* -------------------------------------------------------------------- */ +/* scanner methods (experimental) */ + +static void +scanner_dealloc(ScannerObject* self) +{ + state_fini(&self->state); + Py_DECREF(self->pattern); + PyObject_DEL(self); +} + +static PyObject* +scanner_match(ScannerObject* self, PyObject* args) +{ + SRE_STATE* state = &self->state; + PyObject* match; + int status; + + state_reset(state); + + state->ptr = state->start; + + if (state->charsize == 1) { + status = sre_match(state, PatternObject_GetCode(self->pattern), 1); + } else { +#if defined(HAVE_UNICODE) + status = sre_umatch(state, PatternObject_GetCode(self->pattern), 1); +#endif + } + + match = pattern_new_match((PatternObject*) self->pattern, + state, status); + + if ((status == 0 || state->ptr == state->start) && + state->ptr < state->end) + state->start = (void*) ((char*) state->ptr + state->charsize); + else + state->start = state->ptr; + + return match; +} + + +static PyObject* +scanner_search(ScannerObject* self, PyObject* args) +{ + SRE_STATE* state = &self->state; + PyObject* match; + int status; + + state_reset(state); + + state->ptr = state->start; + + if (state->charsize == 1) { + status = sre_search(state, PatternObject_GetCode(self->pattern)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(state, PatternObject_GetCode(self->pattern)); +#endif + } + + match = pattern_new_match((PatternObject*) self->pattern, + state, status); + + if ((status == 0 || state->ptr == state->start) && + state->ptr < state->end) + state->start = (void*) ((char*) state->ptr + state->charsize); + else + state->start = state->ptr; + + return match; +} + +static PyMethodDef scanner_methods[] = { + /* FIXME: use METH_OLDARGS instead of 0 or fix to use METH_VARARGS */ + /* METH_OLDARGS is not in Python 1.5.2 */ + {"match", (PyCFunction) scanner_match, 0}, + {"search", (PyCFunction) scanner_search, 0}, + {NULL, NULL} +}; + +static PyObject* +scanner_getattr(ScannerObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(scanner_methods, (PyObject*) self, name); + if (res) + return res; + + PyErr_Clear(); + + /* attributes */ + if (!strcmp(name, "pattern")) { + Py_INCREF(self->pattern); + return self->pattern; + } + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +statichere PyTypeObject Scanner_Type = { + PyObject_HEAD_INIT(NULL) + 0, "_" SRE_MODULE ".SRE_Scanner", + sizeof(ScannerObject), 0, + (destructor)scanner_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)scanner_getattr, /*tp_getattr*/ +}; + +static PyMethodDef _functions[] = { + {"compile", _compile, METH_VARARGS}, + {"getcodesize", sre_codesize, METH_VARARGS}, + {"getlower", sre_getlower, METH_VARARGS}, + {NULL, NULL} +}; + +PyMODINIT_FUNC init_sre(void) +{ + PyObject* m; + PyObject* d; + PyObject* x; + + /* Patch object types */ + Pattern_Type.ob_type = Match_Type.ob_type = + Scanner_Type.ob_type = &PyType_Type; + + m = Py_InitModule("_" SRE_MODULE, _functions); + d = PyModule_GetDict(m); + + x = PyInt_FromLong(SRE_MAGIC); + if (x) { + PyDict_SetItemString(d, "MAGIC", x); + Py_DECREF(x); + } + + x = PyString_FromString(copyright); + if (x) { + PyDict_SetItemString(d, "copyright", x); + Py_DECREF(x); + } +} + +#endif /* !defined(SRE_RECURSIVE) */ diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_0aed3a4_404cdc_Objects#obmalloc.c b/src/main/resource/testFiles/cpython/prevFiles/prev_0aed3a4_404cdc_Objects#obmalloc.c new file mode 100644 index 0000000..503fcdf --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_0aed3a4_404cdc_Objects#obmalloc.c @@ -0,0 +1,2449 @@ +#include "Python.h" + + +/* Defined in tracemalloc.c */ +extern void _PyMem_DumpTraceback(int fd, const void *ptr); + + +/* Python's malloc wrappers (see pymem.h) */ + +/* + * Basic types + * I don't care if these are defined in or elsewhere. Axiom. + */ +#undef uchar +#define uchar unsigned char /* assuming == 8 bits */ + +#undef uint +#define uint unsigned int /* assuming >= 16 bits */ + +#undef uptr +#define uptr Py_uintptr_t + +/* Forward declaration */ +static void* _PyMem_DebugRawMalloc(void *ctx, size_t size); +static void* _PyMem_DebugRawCalloc(void *ctx, size_t nelem, size_t elsize); +static void* _PyMem_DebugRawRealloc(void *ctx, void *ptr, size_t size); +static void _PyMem_DebugRawFree(void *ctx, void *p); + +static void* _PyMem_DebugMalloc(void *ctx, size_t size); +static void* _PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize); +static void* _PyMem_DebugRealloc(void *ctx, void *ptr, size_t size); +static void _PyMem_DebugFree(void *ctx, void *p); + +static void _PyObject_DebugDumpAddress(const void *p); +static void _PyMem_DebugCheckAddress(char api_id, const void *p); + +#if defined(__has_feature) /* Clang */ + #if __has_feature(address_sanitizer) /* is ASAN enabled? */ + #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \ + __attribute__((no_address_safety_analysis)) \ + __attribute__ ((noinline)) + #else + #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS + #endif +#else + #if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */ + #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \ + __attribute__((no_address_safety_analysis)) \ + __attribute__ ((noinline)) + #else + #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS + #endif +#endif + +#ifdef WITH_PYMALLOC + +#ifdef MS_WINDOWS +# include +#elif defined(HAVE_MMAP) +# include +# ifdef MAP_ANONYMOUS +# define ARENAS_USE_MMAP +# endif +#endif + +/* Forward declaration */ +static void* _PyObject_Malloc(void *ctx, size_t size); +static void* _PyObject_Calloc(void *ctx, size_t nelem, size_t elsize); +static void _PyObject_Free(void *ctx, void *p); +static void* _PyObject_Realloc(void *ctx, void *ptr, size_t size); +#endif + + +static void * +_PyMem_RawMalloc(void *ctx, size_t size) +{ + /* PyMem_RawMalloc(0) means malloc(1). Some systems would return NULL + for malloc(0), which would be treated as an error. Some platforms would + return a pointer with no memory behind it, which would break pymalloc. + To solve these problems, allocate an extra byte. */ + if (size == 0) + size = 1; + return malloc(size); +} + +static void * +_PyMem_RawCalloc(void *ctx, size_t nelem, size_t elsize) +{ + /* PyMem_RawCalloc(0, 0) means calloc(1, 1). Some systems would return NULL + for calloc(0, 0), which would be treated as an error. Some platforms + would return a pointer with no memory behind it, which would break + pymalloc. To solve these problems, allocate an extra byte. */ + if (nelem == 0 || elsize == 0) { + nelem = 1; + elsize = 1; + } + return calloc(nelem, elsize); +} + +static void * +_PyMem_RawRealloc(void *ctx, void *ptr, size_t size) +{ + if (size == 0) + size = 1; + return realloc(ptr, size); +} + +static void +_PyMem_RawFree(void *ctx, void *ptr) +{ + free(ptr); +} + + +#ifdef MS_WINDOWS +static void * +_PyObject_ArenaVirtualAlloc(void *ctx, size_t size) +{ + return VirtualAlloc(NULL, size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +} + +static void +_PyObject_ArenaVirtualFree(void *ctx, void *ptr, size_t size) +{ + VirtualFree(ptr, 0, MEM_RELEASE); +} + +#elif defined(ARENAS_USE_MMAP) +static void * +_PyObject_ArenaMmap(void *ctx, size_t size) +{ + void *ptr; + ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (ptr == MAP_FAILED) + return NULL; + assert(ptr != NULL); + return ptr; +} + +static void +_PyObject_ArenaMunmap(void *ctx, void *ptr, size_t size) +{ + munmap(ptr, size); +} + +#else +static void * +_PyObject_ArenaMalloc(void *ctx, size_t size) +{ + return malloc(size); +} + +static void +_PyObject_ArenaFree(void *ctx, void *ptr, size_t size) +{ + free(ptr); +} +#endif + + +#define PYRAW_FUNCS _PyMem_RawMalloc, _PyMem_RawCalloc, _PyMem_RawRealloc, _PyMem_RawFree +#ifdef WITH_PYMALLOC +# define PYOBJ_FUNCS _PyObject_Malloc, _PyObject_Calloc, _PyObject_Realloc, _PyObject_Free +#else +# define PYOBJ_FUNCS PYRAW_FUNCS +#endif +#define PYMEM_FUNCS PYRAW_FUNCS + +typedef struct { + /* We tag each block with an API ID in order to tag API violations */ + char api_id; + PyMemAllocatorEx alloc; +} debug_alloc_api_t; +static struct { + debug_alloc_api_t raw; + debug_alloc_api_t mem; + debug_alloc_api_t obj; +} _PyMem_Debug = { + {'r', {NULL, PYRAW_FUNCS}}, + {'m', {NULL, PYMEM_FUNCS}}, + {'o', {NULL, PYOBJ_FUNCS}} + }; + +#define PYRAWDBG_FUNCS \ + _PyMem_DebugRawMalloc, _PyMem_DebugRawCalloc, _PyMem_DebugRawRealloc, _PyMem_DebugRawFree +#define PYDBG_FUNCS \ + _PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree + +static PyMemAllocatorEx _PyMem_Raw = { +#ifdef Py_DEBUG + &_PyMem_Debug.raw, PYRAWDBG_FUNCS +#else + NULL, PYRAW_FUNCS +#endif + }; + +static PyMemAllocatorEx _PyMem = { +#ifdef Py_DEBUG + &_PyMem_Debug.mem, PYDBG_FUNCS +#else + NULL, PYMEM_FUNCS +#endif + }; + +static PyMemAllocatorEx _PyObject = { +#ifdef Py_DEBUG + &_PyMem_Debug.obj, PYDBG_FUNCS +#else + NULL, PYOBJ_FUNCS +#endif + }; + +int +_PyMem_SetupAllocators(const char *opt) +{ + if (opt == NULL || *opt == '\0') { + /* PYTHONMALLOC is empty or is not set or ignored (-E/-I command line + options): use default allocators */ +#ifdef Py_DEBUG +# ifdef WITH_PYMALLOC + opt = "pymalloc_debug"; +# else + opt = "malloc_debug"; +# endif +#else + /* !Py_DEBUG */ +# ifdef WITH_PYMALLOC + opt = "pymalloc"; +# else + opt = "malloc"; +# endif +#endif + } + + if (strcmp(opt, "debug") == 0) { + PyMem_SetupDebugHooks(); + } + else if (strcmp(opt, "malloc") == 0 || strcmp(opt, "malloc_debug") == 0) + { + PyMemAllocatorEx alloc = {NULL, PYRAW_FUNCS}; + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc); + + if (strcmp(opt, "malloc_debug") == 0) + PyMem_SetupDebugHooks(); + } +#ifdef WITH_PYMALLOC + else if (strcmp(opt, "pymalloc") == 0 + || strcmp(opt, "pymalloc_debug") == 0) + { + PyMemAllocatorEx mem_alloc = {NULL, PYRAW_FUNCS}; + PyMemAllocatorEx obj_alloc = {NULL, PYOBJ_FUNCS}; + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &mem_alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &mem_alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &obj_alloc); + + if (strcmp(opt, "pymalloc_debug") == 0) + PyMem_SetupDebugHooks(); + } +#endif + else { + /* unknown allocator */ + return -1; + } + return 0; +} + +#undef PYRAW_FUNCS +#undef PYMEM_FUNCS +#undef PYOBJ_FUNCS +#undef PYRAWDBG_FUNCS +#undef PYDBG_FUNCS + +static PyObjectArenaAllocator _PyObject_Arena = {NULL, +#ifdef MS_WINDOWS + _PyObject_ArenaVirtualAlloc, _PyObject_ArenaVirtualFree +#elif defined(ARENAS_USE_MMAP) + _PyObject_ArenaMmap, _PyObject_ArenaMunmap +#else + _PyObject_ArenaMalloc, _PyObject_ArenaFree +#endif + }; + +static int +_PyMem_DebugEnabled(void) +{ + return (_PyObject.malloc == _PyMem_DebugMalloc); +} + +#ifdef WITH_PYMALLOC +int +_PyMem_PymallocEnabled(void) +{ + if (_PyMem_DebugEnabled()) { + return (_PyMem_Debug.obj.alloc.malloc == _PyObject_Malloc); + } + else { + return (_PyObject.malloc == _PyObject_Malloc); + } +} +#endif + +void +PyMem_SetupDebugHooks(void) +{ + PyMemAllocatorEx alloc; + + alloc.malloc = _PyMem_DebugRawMalloc; + alloc.calloc = _PyMem_DebugRawCalloc; + alloc.realloc = _PyMem_DebugRawRealloc; + alloc.free = _PyMem_DebugRawFree; + + if (_PyMem_Raw.malloc != _PyMem_DebugRawMalloc) { + alloc.ctx = &_PyMem_Debug.raw; + PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &_PyMem_Debug.raw.alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc); + } + + alloc.malloc = _PyMem_DebugMalloc; + alloc.calloc = _PyMem_DebugCalloc; + alloc.realloc = _PyMem_DebugRealloc; + alloc.free = _PyMem_DebugFree; + + if (_PyMem.malloc != _PyMem_DebugMalloc) { + alloc.ctx = &_PyMem_Debug.mem; + PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &_PyMem_Debug.mem.alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc); + } + + if (_PyObject.malloc != _PyMem_DebugMalloc) { + alloc.ctx = &_PyMem_Debug.obj; + PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &_PyMem_Debug.obj.alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc); + } +} + +void +PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) +{ + switch(domain) + { + case PYMEM_DOMAIN_RAW: *allocator = _PyMem_Raw; break; + case PYMEM_DOMAIN_MEM: *allocator = _PyMem; break; + case PYMEM_DOMAIN_OBJ: *allocator = _PyObject; break; + default: + /* unknown domain: set all attributes to NULL */ + allocator->ctx = NULL; + allocator->malloc = NULL; + allocator->calloc = NULL; + allocator->realloc = NULL; + allocator->free = NULL; + } +} + +void +PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) +{ + switch(domain) + { + case PYMEM_DOMAIN_RAW: _PyMem_Raw = *allocator; break; + case PYMEM_DOMAIN_MEM: _PyMem = *allocator; break; + case PYMEM_DOMAIN_OBJ: _PyObject = *allocator; break; + /* ignore unknown domain */ + } +} + +void +PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator) +{ + *allocator = _PyObject_Arena; +} + +void +PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator) +{ + _PyObject_Arena = *allocator; +} + +void * +PyMem_RawMalloc(size_t size) +{ + /* + * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for size < 0 is not required. + */ + if (size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyMem_Raw.malloc(_PyMem_Raw.ctx, size); +} + +void * +PyMem_RawCalloc(size_t nelem, size_t elsize) +{ + /* see PyMem_RawMalloc() */ + if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize) + return NULL; + return _PyMem_Raw.calloc(_PyMem_Raw.ctx, nelem, elsize); +} + +void* +PyMem_RawRealloc(void *ptr, size_t new_size) +{ + /* see PyMem_RawMalloc() */ + if (new_size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyMem_Raw.realloc(_PyMem_Raw.ctx, ptr, new_size); +} + +void PyMem_RawFree(void *ptr) +{ + _PyMem_Raw.free(_PyMem_Raw.ctx, ptr); +} + +void * +PyMem_Malloc(size_t size) +{ + /* see PyMem_RawMalloc() */ + if (size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyMem.malloc(_PyMem.ctx, size); +} + +void * +PyMem_Calloc(size_t nelem, size_t elsize) +{ + /* see PyMem_RawMalloc() */ + if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize) + return NULL; + return _PyMem.calloc(_PyMem.ctx, nelem, elsize); +} + +void * +PyMem_Realloc(void *ptr, size_t new_size) +{ + /* see PyMem_RawMalloc() */ + if (new_size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyMem.realloc(_PyMem.ctx, ptr, new_size); +} + +void +PyMem_Free(void *ptr) +{ + _PyMem.free(_PyMem.ctx, ptr); +} + +char * +_PyMem_RawStrdup(const char *str) +{ + size_t size; + char *copy; + + size = strlen(str) + 1; + copy = PyMem_RawMalloc(size); + if (copy == NULL) + return NULL; + memcpy(copy, str, size); + return copy; +} + +char * +_PyMem_Strdup(const char *str) +{ + size_t size; + char *copy; + + size = strlen(str) + 1; + copy = PyMem_Malloc(size); + if (copy == NULL) + return NULL; + memcpy(copy, str, size); + return copy; +} + +void * +PyObject_Malloc(size_t size) +{ + /* see PyMem_RawMalloc() */ + if (size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyObject.malloc(_PyObject.ctx, size); +} + +void * +PyObject_Calloc(size_t nelem, size_t elsize) +{ + /* see PyMem_RawMalloc() */ + if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize) + return NULL; + return _PyObject.calloc(_PyObject.ctx, nelem, elsize); +} + +void * +PyObject_Realloc(void *ptr, size_t new_size) +{ + /* see PyMem_RawMalloc() */ + if (new_size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyObject.realloc(_PyObject.ctx, ptr, new_size); +} + +void +PyObject_Free(void *ptr) +{ + _PyObject.free(_PyObject.ctx, ptr); +} + + +#ifdef WITH_PYMALLOC + +#ifdef WITH_VALGRIND +#include + +/* If we're using GCC, use __builtin_expect() to reduce overhead of + the valgrind checks */ +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +# define UNLIKELY(value) __builtin_expect((value), 0) +#else +# define UNLIKELY(value) (value) +#endif + +/* -1 indicates that we haven't checked that we're running on valgrind yet. */ +static int running_on_valgrind = -1; +#endif + +/* An object allocator for Python. + + Here is an introduction to the layers of the Python memory architecture, + showing where the object allocator is actually used (layer +2), It is + called for every object allocation and deallocation (PyObject_New/Del), + unless the object-specific allocators implement a proprietary allocation + scheme (ex.: ints use a simple free list). This is also the place where + the cyclic garbage collector operates selectively on container objects. + + + Object-specific allocators + _____ ______ ______ ________ + [ int ] [ dict ] [ list ] ... [ string ] Python core | ++3 | <----- Object-specific memory -----> | <-- Non-object memory --> | + _______________________________ | | + [ Python's object allocator ] | | ++2 | ####### Object memory ####### | <------ Internal buffers ------> | + ______________________________________________________________ | + [ Python's raw memory allocator (PyMem_ API) ] | ++1 | <----- Python memory (under PyMem manager's control) ------> | | + __________________________________________________________________ + [ Underlying general-purpose allocator (ex: C library malloc) ] + 0 | <------ Virtual memory allocated for the python process -------> | + + ========================================================================= + _______________________________________________________________________ + [ OS-specific Virtual Memory Manager (VMM) ] +-1 | <--- Kernel dynamic storage allocation & management (page-based) ---> | + __________________________________ __________________________________ + [ ] [ ] +-2 | <-- Physical memory: ROM/RAM --> | | <-- Secondary storage (swap) --> | + +*/ +/*==========================================================================*/ + +/* A fast, special-purpose memory allocator for small blocks, to be used + on top of a general-purpose malloc -- heavily based on previous art. */ + +/* Vladimir Marangozov -- August 2000 */ + +/* + * "Memory management is where the rubber meets the road -- if we do the wrong + * thing at any level, the results will not be good. And if we don't make the + * levels work well together, we are in serious trouble." (1) + * + * (1) Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles, + * "Dynamic Storage Allocation: A Survey and Critical Review", + * in Proc. 1995 Int'l. Workshop on Memory Management, September 1995. + */ + +/* #undef WITH_MEMORY_LIMITS */ /* disable mem limit checks */ + +/*==========================================================================*/ + +/* + * Allocation strategy abstract: + * + * For small requests, the allocator sub-allocates blocks of memory. + * Requests greater than SMALL_REQUEST_THRESHOLD bytes are routed to the + * system's allocator. + * + * Small requests are grouped in size classes spaced 8 bytes apart, due + * to the required valid alignment of the returned address. Requests of + * a particular size are serviced from memory pools of 4K (one VMM page). + * Pools are fragmented on demand and contain free lists of blocks of one + * particular size class. In other words, there is a fixed-size allocator + * for each size class. Free pools are shared by the different allocators + * thus minimizing the space reserved for a particular size class. + * + * This allocation strategy is a variant of what is known as "simple + * segregated storage based on array of free lists". The main drawback of + * simple segregated storage is that we might end up with lot of reserved + * memory for the different free lists, which degenerate in time. To avoid + * this, we partition each free list in pools and we share dynamically the + * reserved space between all free lists. This technique is quite efficient + * for memory intensive programs which allocate mainly small-sized blocks. + * + * For small requests we have the following table: + * + * Request in bytes Size of allocated block Size class idx + * ---------------------------------------------------------------- + * 1-8 8 0 + * 9-16 16 1 + * 17-24 24 2 + * 25-32 32 3 + * 33-40 40 4 + * 41-48 48 5 + * 49-56 56 6 + * 57-64 64 7 + * 65-72 72 8 + * ... ... ... + * 497-504 504 62 + * 505-512 512 63 + * + * 0, SMALL_REQUEST_THRESHOLD + 1 and up: routed to the underlying + * allocator. + */ + +/*==========================================================================*/ + +/* + * -- Main tunable settings section -- + */ + +/* + * Alignment of addresses returned to the user. 8-bytes alignment works + * on most current architectures (with 32-bit or 64-bit address busses). + * The alignment value is also used for grouping small requests in size + * classes spaced ALIGNMENT bytes apart. + * + * You shouldn't change this unless you know what you are doing. + */ +#define ALIGNMENT 8 /* must be 2^N */ +#define ALIGNMENT_SHIFT 3 + +/* Return the number of bytes in size class I, as a uint. */ +#define INDEX2SIZE(I) (((uint)(I) + 1) << ALIGNMENT_SHIFT) + +/* + * Max size threshold below which malloc requests are considered to be + * small enough in order to use preallocated memory pools. You can tune + * this value according to your application behaviour and memory needs. + * + * Note: a size threshold of 512 guarantees that newly created dictionaries + * will be allocated from preallocated memory pools on 64-bit. + * + * The following invariants must hold: + * 1) ALIGNMENT <= SMALL_REQUEST_THRESHOLD <= 512 + * 2) SMALL_REQUEST_THRESHOLD is evenly divisible by ALIGNMENT + * + * Although not required, for better performance and space efficiency, + * it is recommended that SMALL_REQUEST_THRESHOLD is set to a power of 2. + */ +#define SMALL_REQUEST_THRESHOLD 512 +#define NB_SMALL_SIZE_CLASSES (SMALL_REQUEST_THRESHOLD / ALIGNMENT) + +/* + * The system's VMM page size can be obtained on most unices with a + * getpagesize() call or deduced from various header files. To make + * things simpler, we assume that it is 4K, which is OK for most systems. + * It is probably better if this is the native page size, but it doesn't + * have to be. In theory, if SYSTEM_PAGE_SIZE is larger than the native page + * size, then `POOL_ADDR(p)->arenaindex' could rarely cause a segmentation + * violation fault. 4K is apparently OK for all the platforms that python + * currently targets. + */ +#define SYSTEM_PAGE_SIZE (4 * 1024) +#define SYSTEM_PAGE_SIZE_MASK (SYSTEM_PAGE_SIZE - 1) + +/* + * Maximum amount of memory managed by the allocator for small requests. + */ +#ifdef WITH_MEMORY_LIMITS +#ifndef SMALL_MEMORY_LIMIT +#define SMALL_MEMORY_LIMIT (64 * 1024 * 1024) /* 64 MB -- more? */ +#endif +#endif + +/* + * The allocator sub-allocates blocks of memory (called arenas) aligned + * on a page boundary. This is a reserved virtual address space for the + * current process (obtained through a malloc()/mmap() call). In no way this + * means that the memory arenas will be used entirely. A malloc() is + * usually an address range reservation for bytes, unless all pages within + * this space are referenced subsequently. So malloc'ing big blocks and not + * using them does not mean "wasting memory". It's an addressable range + * wastage... + * + * Arenas are allocated with mmap() on systems supporting anonymous memory + * mappings to reduce heap fragmentation. + */ +#define ARENA_SIZE (256 << 10) /* 256KB */ + +#ifdef WITH_MEMORY_LIMITS +#define MAX_ARENAS (SMALL_MEMORY_LIMIT / ARENA_SIZE) +#endif + +/* + * Size of the pools used for small blocks. Should be a power of 2, + * between 1K and SYSTEM_PAGE_SIZE, that is: 1k, 2k, 4k. + */ +#define POOL_SIZE SYSTEM_PAGE_SIZE /* must be 2^N */ +#define POOL_SIZE_MASK SYSTEM_PAGE_SIZE_MASK + +/* + * -- End of tunable settings section -- + */ + +/*==========================================================================*/ + +/* + * Locking + * + * To reduce lock contention, it would probably be better to refine the + * crude function locking with per size class locking. I'm not positive + * however, whether it's worth switching to such locking policy because + * of the performance penalty it might introduce. + * + * The following macros describe the simplest (should also be the fastest) + * lock object on a particular platform and the init/fini/lock/unlock + * operations on it. The locks defined here are not expected to be recursive + * because it is assumed that they will always be called in the order: + * INIT, [LOCK, UNLOCK]*, FINI. + */ + +/* + * Python's threads are serialized, so object malloc locking is disabled. + */ +#define SIMPLELOCK_DECL(lock) /* simple lock declaration */ +#define SIMPLELOCK_INIT(lock) /* allocate (if needed) and initialize */ +#define SIMPLELOCK_FINI(lock) /* free/destroy an existing lock */ +#define SIMPLELOCK_LOCK(lock) /* acquire released lock */ +#define SIMPLELOCK_UNLOCK(lock) /* release acquired lock */ + +/* When you say memory, my mind reasons in terms of (pointers to) blocks */ +typedef uchar block; + +/* Pool for small blocks. */ +struct pool_header { + union { block *_padding; + uint count; } ref; /* number of allocated blocks */ + block *freeblock; /* pool's free list head */ + struct pool_header *nextpool; /* next pool of this size class */ + struct pool_header *prevpool; /* previous pool "" */ + uint arenaindex; /* index into arenas of base adr */ + uint szidx; /* block size class index */ + uint nextoffset; /* bytes to virgin block */ + uint maxnextoffset; /* largest valid nextoffset */ +}; + +typedef struct pool_header *poolp; + +/* Record keeping for arenas. */ +struct arena_object { + /* The address of the arena, as returned by malloc. Note that 0 + * will never be returned by a successful malloc, and is used + * here to mark an arena_object that doesn't correspond to an + * allocated arena. + */ + uptr address; + + /* Pool-aligned pointer to the next pool to be carved off. */ + block* pool_address; + + /* The number of available pools in the arena: free pools + never- + * allocated pools. + */ + uint nfreepools; + + /* The total number of pools in the arena, whether or not available. */ + uint ntotalpools; + + /* Singly-linked list of available pools. */ + struct pool_header* freepools; + + /* Whenever this arena_object is not associated with an allocated + * arena, the nextarena member is used to link all unassociated + * arena_objects in the singly-linked `unused_arena_objects` list. + * The prevarena member is unused in this case. + * + * When this arena_object is associated with an allocated arena + * with at least one available pool, both members are used in the + * doubly-linked `usable_arenas` list, which is maintained in + * increasing order of `nfreepools` values. + * + * Else this arena_object is associated with an allocated arena + * all of whose pools are in use. `nextarena` and `prevarena` + * are both meaningless in this case. + */ + struct arena_object* nextarena; + struct arena_object* prevarena; +}; + +#define POOL_OVERHEAD _Py_SIZE_ROUND_UP(sizeof(struct pool_header), ALIGNMENT) + +#define DUMMY_SIZE_IDX 0xffff /* size class of newly cached pools */ + +/* Round pointer P down to the closest pool-aligned address <= P, as a poolp */ +#define POOL_ADDR(P) ((poolp)_Py_ALIGN_DOWN((P), POOL_SIZE)) + +/* Return total number of blocks in pool of size index I, as a uint. */ +#define NUMBLOCKS(I) ((uint)(POOL_SIZE - POOL_OVERHEAD) / INDEX2SIZE(I)) + +/*==========================================================================*/ + +/* + * This malloc lock + */ +SIMPLELOCK_DECL(_malloc_lock) +#define LOCK() SIMPLELOCK_LOCK(_malloc_lock) +#define UNLOCK() SIMPLELOCK_UNLOCK(_malloc_lock) +#define LOCK_INIT() SIMPLELOCK_INIT(_malloc_lock) +#define LOCK_FINI() SIMPLELOCK_FINI(_malloc_lock) + +/* + * Pool table -- headed, circular, doubly-linked lists of partially used pools. + +This is involved. For an index i, usedpools[i+i] is the header for a list of +all partially used pools holding small blocks with "size class idx" i. So +usedpools[0] corresponds to blocks of size 8, usedpools[2] to blocks of size +16, and so on: index 2*i <-> blocks of size (i+1)<freeblock points to +the start of a singly-linked list of free blocks within the pool. When a +block is freed, it's inserted at the front of its pool's freeblock list. Note +that the available blocks in a pool are *not* linked all together when a pool +is initialized. Instead only "the first two" (lowest addresses) blocks are +set up, returning the first such block, and setting pool->freeblock to a +one-block list holding the second such block. This is consistent with that +pymalloc strives at all levels (arena, pool, and block) never to touch a piece +of memory until it's actually needed. + +So long as a pool is in the used state, we're certain there *is* a block +available for allocating, and pool->freeblock is not NULL. If pool->freeblock +points to the end of the free list before we've carved the entire pool into +blocks, that means we simply haven't yet gotten to one of the higher-address +blocks. The offset from the pool_header to the start of "the next" virgin +block is stored in the pool_header nextoffset member, and the largest value +of nextoffset that makes sense is stored in the maxnextoffset member when a +pool is initialized. All the blocks in a pool have been passed out at least +once when and only when nextoffset > maxnextoffset. + + +Major obscurity: While the usedpools vector is declared to have poolp +entries, it doesn't really. It really contains two pointers per (conceptual) +poolp entry, the nextpool and prevpool members of a pool_header. The +excruciating initialization code below fools C so that + + usedpool[i+i] + +"acts like" a genuine poolp, but only so long as you only reference its +nextpool and prevpool members. The "- 2*sizeof(block *)" gibberish is +compensating for that a pool_header's nextpool and prevpool members +immediately follow a pool_header's first two members: + + union { block *_padding; + uint count; } ref; + block *freeblock; + +each of which consume sizeof(block *) bytes. So what usedpools[i+i] really +contains is a fudged-up pointer p such that *if* C believes it's a poolp +pointer, then p->nextpool and p->prevpool are both p (meaning that the headed +circular list is empty). + +It's unclear why the usedpools setup is so convoluted. It could be to +minimize the amount of cache required to hold this heavily-referenced table +(which only *needs* the two interpool pointer members of a pool_header). OTOH, +referencing code has to remember to "double the index" and doing so isn't +free, usedpools[0] isn't a strictly legal pointer, and we're crucially relying +on that C doesn't insert any padding anywhere in a pool_header at or before +the prevpool member. +**************************************************************************** */ + +#define PTA(x) ((poolp )((uchar *)&(usedpools[2*(x)]) - 2*sizeof(block *))) +#define PT(x) PTA(x), PTA(x) + +static poolp usedpools[2 * ((NB_SMALL_SIZE_CLASSES + 7) / 8) * 8] = { + PT(0), PT(1), PT(2), PT(3), PT(4), PT(5), PT(6), PT(7) +#if NB_SMALL_SIZE_CLASSES > 8 + , PT(8), PT(9), PT(10), PT(11), PT(12), PT(13), PT(14), PT(15) +#if NB_SMALL_SIZE_CLASSES > 16 + , PT(16), PT(17), PT(18), PT(19), PT(20), PT(21), PT(22), PT(23) +#if NB_SMALL_SIZE_CLASSES > 24 + , PT(24), PT(25), PT(26), PT(27), PT(28), PT(29), PT(30), PT(31) +#if NB_SMALL_SIZE_CLASSES > 32 + , PT(32), PT(33), PT(34), PT(35), PT(36), PT(37), PT(38), PT(39) +#if NB_SMALL_SIZE_CLASSES > 40 + , PT(40), PT(41), PT(42), PT(43), PT(44), PT(45), PT(46), PT(47) +#if NB_SMALL_SIZE_CLASSES > 48 + , PT(48), PT(49), PT(50), PT(51), PT(52), PT(53), PT(54), PT(55) +#if NB_SMALL_SIZE_CLASSES > 56 + , PT(56), PT(57), PT(58), PT(59), PT(60), PT(61), PT(62), PT(63) +#if NB_SMALL_SIZE_CLASSES > 64 +#error "NB_SMALL_SIZE_CLASSES should be less than 64" +#endif /* NB_SMALL_SIZE_CLASSES > 64 */ +#endif /* NB_SMALL_SIZE_CLASSES > 56 */ +#endif /* NB_SMALL_SIZE_CLASSES > 48 */ +#endif /* NB_SMALL_SIZE_CLASSES > 40 */ +#endif /* NB_SMALL_SIZE_CLASSES > 32 */ +#endif /* NB_SMALL_SIZE_CLASSES > 24 */ +#endif /* NB_SMALL_SIZE_CLASSES > 16 */ +#endif /* NB_SMALL_SIZE_CLASSES > 8 */ +}; + +/*========================================================================== +Arena management. + +`arenas` is a vector of arena_objects. It contains maxarenas entries, some of +which may not be currently used (== they're arena_objects that aren't +currently associated with an allocated arena). Note that arenas proper are +separately malloc'ed. + +Prior to Python 2.5, arenas were never free()'ed. Starting with Python 2.5, +we do try to free() arenas, and use some mild heuristic strategies to increase +the likelihood that arenas eventually can be freed. + +unused_arena_objects + + This is a singly-linked list of the arena_objects that are currently not + being used (no arena is associated with them). Objects are taken off the + head of the list in new_arena(), and are pushed on the head of the list in + PyObject_Free() when the arena is empty. Key invariant: an arena_object + is on this list if and only if its .address member is 0. + +usable_arenas + + This is a doubly-linked list of the arena_objects associated with arenas + that have pools available. These pools are either waiting to be reused, + or have not been used before. The list is sorted to have the most- + allocated arenas first (ascending order based on the nfreepools member). + This means that the next allocation will come from a heavily used arena, + which gives the nearly empty arenas a chance to be returned to the system. + In my unscientific tests this dramatically improved the number of arenas + that could be freed. + +Note that an arena_object associated with an arena all of whose pools are +currently in use isn't on either list. +*/ + +/* Array of objects used to track chunks of memory (arenas). */ +static struct arena_object* arenas = NULL; +/* Number of slots currently allocated in the `arenas` vector. */ +static uint maxarenas = 0; + +/* The head of the singly-linked, NULL-terminated list of available + * arena_objects. + */ +static struct arena_object* unused_arena_objects = NULL; + +/* The head of the doubly-linked, NULL-terminated at each end, list of + * arena_objects associated with arenas that have pools available. + */ +static struct arena_object* usable_arenas = NULL; + +/* How many arena_objects do we initially allocate? + * 16 = can allocate 16 arenas = 16 * ARENA_SIZE = 4MB before growing the + * `arenas` vector. + */ +#define INITIAL_ARENA_OBJECTS 16 + +/* Number of arenas allocated that haven't been free()'d. */ +static size_t narenas_currently_allocated = 0; + +/* Total number of times malloc() called to allocate an arena. */ +static size_t ntimes_arena_allocated = 0; +/* High water mark (max value ever seen) for narenas_currently_allocated. */ +static size_t narenas_highwater = 0; + +static Py_ssize_t _Py_AllocatedBlocks = 0; + +Py_ssize_t +_Py_GetAllocatedBlocks(void) +{ + return _Py_AllocatedBlocks; +} + + +/* Allocate a new arena. If we run out of memory, return NULL. Else + * allocate a new arena, and return the address of an arena_object + * describing the new arena. It's expected that the caller will set + * `usable_arenas` to the return value. + */ +static struct arena_object* +new_arena(void) +{ + struct arena_object* arenaobj; + uint excess; /* number of bytes above pool alignment */ + void *address; + static int debug_stats = -1; + + if (debug_stats == -1) { + char *opt = Py_GETENV("PYTHONMALLOCSTATS"); + debug_stats = (opt != NULL && *opt != '\0'); + } + if (debug_stats) + _PyObject_DebugMallocStats(stderr); + + if (unused_arena_objects == NULL) { + uint i; + uint numarenas; + size_t nbytes; + + /* Double the number of arena objects on each allocation. + * Note that it's possible for `numarenas` to overflow. + */ + numarenas = maxarenas ? maxarenas << 1 : INITIAL_ARENA_OBJECTS; + if (numarenas <= maxarenas) + return NULL; /* overflow */ +#if SIZEOF_SIZE_T <= SIZEOF_INT + if (numarenas > PY_SIZE_MAX / sizeof(*arenas)) + return NULL; /* overflow */ +#endif + nbytes = numarenas * sizeof(*arenas); + arenaobj = (struct arena_object *)PyMem_RawRealloc(arenas, nbytes); + if (arenaobj == NULL) + return NULL; + arenas = arenaobj; + + /* We might need to fix pointers that were copied. However, + * new_arena only gets called when all the pages in the + * previous arenas are full. Thus, there are *no* pointers + * into the old array. Thus, we don't have to worry about + * invalid pointers. Just to be sure, some asserts: + */ + assert(usable_arenas == NULL); + assert(unused_arena_objects == NULL); + + /* Put the new arenas on the unused_arena_objects list. */ + for (i = maxarenas; i < numarenas; ++i) { + arenas[i].address = 0; /* mark as unassociated */ + arenas[i].nextarena = i < numarenas - 1 ? + &arenas[i+1] : NULL; + } + + /* Update globals. */ + unused_arena_objects = &arenas[maxarenas]; + maxarenas = numarenas; + } + + /* Take the next available arena object off the head of the list. */ + assert(unused_arena_objects != NULL); + arenaobj = unused_arena_objects; + unused_arena_objects = arenaobj->nextarena; + assert(arenaobj->address == 0); + address = _PyObject_Arena.alloc(_PyObject_Arena.ctx, ARENA_SIZE); + if (address == NULL) { + /* The allocation failed: return NULL after putting the + * arenaobj back. + */ + arenaobj->nextarena = unused_arena_objects; + unused_arena_objects = arenaobj; + return NULL; + } + arenaobj->address = (uptr)address; + + ++narenas_currently_allocated; + ++ntimes_arena_allocated; + if (narenas_currently_allocated > narenas_highwater) + narenas_highwater = narenas_currently_allocated; + arenaobj->freepools = NULL; + /* pool_address <- first pool-aligned address in the arena + nfreepools <- number of whole pools that fit after alignment */ + arenaobj->pool_address = (block*)arenaobj->address; + arenaobj->nfreepools = ARENA_SIZE / POOL_SIZE; + assert(POOL_SIZE * arenaobj->nfreepools == ARENA_SIZE); + excess = (uint)(arenaobj->address & POOL_SIZE_MASK); + if (excess != 0) { + --arenaobj->nfreepools; + arenaobj->pool_address += POOL_SIZE - excess; + } + arenaobj->ntotalpools = arenaobj->nfreepools; + + return arenaobj; +} + +/* +Py_ADDRESS_IN_RANGE(P, POOL) + +Return true if and only if P is an address that was allocated by pymalloc. +POOL must be the pool address associated with P, i.e., POOL = POOL_ADDR(P) +(the caller is asked to compute this because the macro expands POOL more than +once, and for efficiency it's best for the caller to assign POOL_ADDR(P) to a +variable and pass the latter to the macro; because Py_ADDRESS_IN_RANGE is +called on every alloc/realloc/free, micro-efficiency is important here). + +Tricky: Let B be the arena base address associated with the pool, B = +arenas[(POOL)->arenaindex].address. Then P belongs to the arena if and only if + + B <= P < B + ARENA_SIZE + +Subtracting B throughout, this is true iff + + 0 <= P-B < ARENA_SIZE + +By using unsigned arithmetic, the "0 <=" half of the test can be skipped. + +Obscure: A PyMem "free memory" function can call the pymalloc free or realloc +before the first arena has been allocated. `arenas` is still NULL in that +case. We're relying on that maxarenas is also 0 in that case, so that +(POOL)->arenaindex < maxarenas must be false, saving us from trying to index +into a NULL arenas. + +Details: given P and POOL, the arena_object corresponding to P is AO = +arenas[(POOL)->arenaindex]. Suppose obmalloc controls P. Then (barring wild +stores, etc), POOL is the correct address of P's pool, AO.address is the +correct base address of the pool's arena, and P must be within ARENA_SIZE of +AO.address. In addition, AO.address is not 0 (no arena can start at address 0 +(NULL)). Therefore Py_ADDRESS_IN_RANGE correctly reports that obmalloc +controls P. + +Now suppose obmalloc does not control P (e.g., P was obtained via a direct +call to the system malloc() or realloc()). (POOL)->arenaindex may be anything +in this case -- it may even be uninitialized trash. If the trash arenaindex +is >= maxarenas, the macro correctly concludes at once that obmalloc doesn't +control P. + +Else arenaindex is < maxarena, and AO is read up. If AO corresponds to an +allocated arena, obmalloc controls all the memory in slice AO.address : +AO.address+ARENA_SIZE. By case assumption, P is not controlled by obmalloc, +so P doesn't lie in that slice, so the macro correctly reports that P is not +controlled by obmalloc. + +Finally, if P is not controlled by obmalloc and AO corresponds to an unused +arena_object (one not currently associated with an allocated arena), +AO.address is 0, and the second test in the macro reduces to: + + P < ARENA_SIZE + +If P >= ARENA_SIZE (extremely likely), the macro again correctly concludes +that P is not controlled by obmalloc. However, if P < ARENA_SIZE, this part +of the test still passes, and the third clause (AO.address != 0) is necessary +to get the correct result: AO.address is 0 in this case, so the macro +correctly reports that P is not controlled by obmalloc (despite that P lies in +slice AO.address : AO.address + ARENA_SIZE). + +Note: The third (AO.address != 0) clause was added in Python 2.5. Before +2.5, arenas were never free()'ed, and an arenaindex < maxarena always +corresponded to a currently-allocated arena, so the "P is not controlled by +obmalloc, AO corresponds to an unused arena_object, and P < ARENA_SIZE" case +was impossible. + +Note that the logic is excruciating, and reading up possibly uninitialized +memory when P is not controlled by obmalloc (to get at (POOL)->arenaindex) +creates problems for some memory debuggers. The overwhelming advantage is +that this test determines whether an arbitrary address is controlled by +obmalloc in a small constant time, independent of the number of arenas +obmalloc controls. Since this test is needed at every entry point, it's +extremely desirable that it be this fast. + +Since Py_ADDRESS_IN_RANGE may be reading from memory which was not allocated +by Python, it is important that (POOL)->arenaindex is read only once, as +another thread may be concurrently modifying the value without holding the +GIL. To accomplish this, the arenaindex_temp variable is used to store +(POOL)->arenaindex for the duration of the Py_ADDRESS_IN_RANGE macro's +execution. The caller of the macro is responsible for declaring this +variable. +*/ +#define Py_ADDRESS_IN_RANGE(P, POOL) \ + ((arenaindex_temp = (POOL)->arenaindex) < maxarenas && \ + (uptr)(P) - arenas[arenaindex_temp].address < (uptr)ARENA_SIZE && \ + arenas[arenaindex_temp].address != 0) + + +/* This is only useful when running memory debuggers such as + * Purify or Valgrind. Uncomment to use. + * +#define Py_USING_MEMORY_DEBUGGER + */ + +#ifdef Py_USING_MEMORY_DEBUGGER + +/* Py_ADDRESS_IN_RANGE may access uninitialized memory by design + * This leads to thousands of spurious warnings when using + * Purify or Valgrind. By making a function, we can easily + * suppress the uninitialized memory reads in this one function. + * So we won't ignore real errors elsewhere. + * + * Disable the macro and use a function. + */ + +#undef Py_ADDRESS_IN_RANGE + +#if defined(__GNUC__) && ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) || \ + (__GNUC__ >= 4)) +#define Py_NO_INLINE __attribute__((__noinline__)) +#else +#define Py_NO_INLINE +#endif + +/* Don't make static, to try to ensure this isn't inlined. */ +int Py_ADDRESS_IN_RANGE(void *P, poolp pool) Py_NO_INLINE; +#undef Py_NO_INLINE +#endif + +/*==========================================================================*/ + +/* malloc. Note that nbytes==0 tries to return a non-NULL pointer, distinct + * from all other currently live pointers. This may not be possible. + */ + +/* + * The basic blocks are ordered by decreasing execution frequency, + * which minimizes the number of jumps in the most common cases, + * improves branching prediction and instruction scheduling (small + * block allocations typically result in a couple of instructions). + * Unless the optimizer reorders everything, being too smart... + */ + +static void * +_PyObject_Alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize) +{ + size_t nbytes; + block *bp; + poolp pool; + poolp next; + uint size; + + _Py_AllocatedBlocks++; + + assert(nelem <= PY_SSIZE_T_MAX / elsize); + nbytes = nelem * elsize; + +#ifdef WITH_VALGRIND + if (UNLIKELY(running_on_valgrind == -1)) + running_on_valgrind = RUNNING_ON_VALGRIND; + if (UNLIKELY(running_on_valgrind)) + goto redirect; +#endif + + if (nelem == 0 || elsize == 0) + goto redirect; + + if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { + LOCK(); + /* + * Most frequent paths first + */ + size = (uint)(nbytes - 1) >> ALIGNMENT_SHIFT; + pool = usedpools[size + size]; + if (pool != pool->nextpool) { + /* + * There is a used pool for this size class. + * Pick up the head block of its free list. + */ + ++pool->ref.count; + bp = pool->freeblock; + assert(bp != NULL); + if ((pool->freeblock = *(block **)bp) != NULL) { + UNLOCK(); + if (use_calloc) + memset(bp, 0, nbytes); + return (void *)bp; + } + /* + * Reached the end of the free list, try to extend it. + */ + if (pool->nextoffset <= pool->maxnextoffset) { + /* There is room for another block. */ + pool->freeblock = (block*)pool + + pool->nextoffset; + pool->nextoffset += INDEX2SIZE(size); + *(block **)(pool->freeblock) = NULL; + UNLOCK(); + if (use_calloc) + memset(bp, 0, nbytes); + return (void *)bp; + } + /* Pool is full, unlink from used pools. */ + next = pool->nextpool; + pool = pool->prevpool; + next->prevpool = pool; + pool->nextpool = next; + UNLOCK(); + if (use_calloc) + memset(bp, 0, nbytes); + return (void *)bp; + } + + /* There isn't a pool of the right size class immediately + * available: use a free pool. + */ + if (usable_arenas == NULL) { + /* No arena has a free pool: allocate a new arena. */ +#ifdef WITH_MEMORY_LIMITS + if (narenas_currently_allocated >= MAX_ARENAS) { + UNLOCK(); + goto redirect; + } +#endif + usable_arenas = new_arena(); + if (usable_arenas == NULL) { + UNLOCK(); + goto redirect; + } + usable_arenas->nextarena = + usable_arenas->prevarena = NULL; + } + assert(usable_arenas->address != 0); + + /* Try to get a cached free pool. */ + pool = usable_arenas->freepools; + if (pool != NULL) { + /* Unlink from cached pools. */ + usable_arenas->freepools = pool->nextpool; + + /* This arena already had the smallest nfreepools + * value, so decreasing nfreepools doesn't change + * that, and we don't need to rearrange the + * usable_arenas list. However, if the arena has + * become wholly allocated, we need to remove its + * arena_object from usable_arenas. + */ + --usable_arenas->nfreepools; + if (usable_arenas->nfreepools == 0) { + /* Wholly allocated: remove. */ + assert(usable_arenas->freepools == NULL); + assert(usable_arenas->nextarena == NULL || + usable_arenas->nextarena->prevarena == + usable_arenas); + + usable_arenas = usable_arenas->nextarena; + if (usable_arenas != NULL) { + usable_arenas->prevarena = NULL; + assert(usable_arenas->address != 0); + } + } + else { + /* nfreepools > 0: it must be that freepools + * isn't NULL, or that we haven't yet carved + * off all the arena's pools for the first + * time. + */ + assert(usable_arenas->freepools != NULL || + usable_arenas->pool_address <= + (block*)usable_arenas->address + + ARENA_SIZE - POOL_SIZE); + } + init_pool: + /* Frontlink to used pools. */ + next = usedpools[size + size]; /* == prev */ + pool->nextpool = next; + pool->prevpool = next; + next->nextpool = pool; + next->prevpool = pool; + pool->ref.count = 1; + if (pool->szidx == size) { + /* Luckily, this pool last contained blocks + * of the same size class, so its header + * and free list are already initialized. + */ + bp = pool->freeblock; + assert(bp != NULL); + pool->freeblock = *(block **)bp; + UNLOCK(); + if (use_calloc) + memset(bp, 0, nbytes); + return (void *)bp; + } + /* + * Initialize the pool header, set up the free list to + * contain just the second block, and return the first + * block. + */ + pool->szidx = size; + size = INDEX2SIZE(size); + bp = (block *)pool + POOL_OVERHEAD; + pool->nextoffset = POOL_OVERHEAD + (size << 1); + pool->maxnextoffset = POOL_SIZE - size; + pool->freeblock = bp + size; + *(block **)(pool->freeblock) = NULL; + UNLOCK(); + if (use_calloc) + memset(bp, 0, nbytes); + return (void *)bp; + } + + /* Carve off a new pool. */ + assert(usable_arenas->nfreepools > 0); + assert(usable_arenas->freepools == NULL); + pool = (poolp)usable_arenas->pool_address; + assert((block*)pool <= (block*)usable_arenas->address + + ARENA_SIZE - POOL_SIZE); + pool->arenaindex = (uint)(usable_arenas - arenas); + assert(&arenas[pool->arenaindex] == usable_arenas); + pool->szidx = DUMMY_SIZE_IDX; + usable_arenas->pool_address += POOL_SIZE; + --usable_arenas->nfreepools; + + if (usable_arenas->nfreepools == 0) { + assert(usable_arenas->nextarena == NULL || + usable_arenas->nextarena->prevarena == + usable_arenas); + /* Unlink the arena: it is completely allocated. */ + usable_arenas = usable_arenas->nextarena; + if (usable_arenas != NULL) { + usable_arenas->prevarena = NULL; + assert(usable_arenas->address != 0); + } + } + + goto init_pool; + } + + /* The small block allocator ends here. */ + +redirect: + /* Redirect the original request to the underlying (libc) allocator. + * We jump here on bigger requests, on error in the code above (as a + * last chance to serve the request) or when the max memory limit + * has been reached. + */ + { + void *result; + if (use_calloc) + result = PyMem_RawCalloc(nelem, elsize); + else + result = PyMem_RawMalloc(nbytes); + if (!result) + _Py_AllocatedBlocks--; + return result; + } +} + +static void * +_PyObject_Malloc(void *ctx, size_t nbytes) +{ + return _PyObject_Alloc(0, ctx, 1, nbytes); +} + +static void * +_PyObject_Calloc(void *ctx, size_t nelem, size_t elsize) +{ + return _PyObject_Alloc(1, ctx, nelem, elsize); +} + +/* free */ + +ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS +static void +_PyObject_Free(void *ctx, void *p) +{ + poolp pool; + block *lastfree; + poolp next, prev; + uint size; +#ifndef Py_USING_MEMORY_DEBUGGER + uint arenaindex_temp; +#endif + + if (p == NULL) /* free(NULL) has no effect */ + return; + + _Py_AllocatedBlocks--; + +#ifdef WITH_VALGRIND + if (UNLIKELY(running_on_valgrind > 0)) + goto redirect; +#endif + + pool = POOL_ADDR(p); + if (Py_ADDRESS_IN_RANGE(p, pool)) { + /* We allocated this address. */ + LOCK(); + /* Link p to the start of the pool's freeblock list. Since + * the pool had at least the p block outstanding, the pool + * wasn't empty (so it's already in a usedpools[] list, or + * was full and is in no list -- it's not in the freeblocks + * list in any case). + */ + assert(pool->ref.count > 0); /* else it was empty */ + *(block **)p = lastfree = pool->freeblock; + pool->freeblock = (block *)p; + if (lastfree) { + struct arena_object* ao; + uint nf; /* ao->nfreepools */ + + /* freeblock wasn't NULL, so the pool wasn't full, + * and the pool is in a usedpools[] list. + */ + if (--pool->ref.count != 0) { + /* pool isn't empty: leave it in usedpools */ + UNLOCK(); + return; + } + /* Pool is now empty: unlink from usedpools, and + * link to the front of freepools. This ensures that + * previously freed pools will be allocated later + * (being not referenced, they are perhaps paged out). + */ + next = pool->nextpool; + prev = pool->prevpool; + next->prevpool = prev; + prev->nextpool = next; + + /* Link the pool to freepools. This is a singly-linked + * list, and pool->prevpool isn't used there. + */ + ao = &arenas[pool->arenaindex]; + pool->nextpool = ao->freepools; + ao->freepools = pool; + nf = ++ao->nfreepools; + + /* All the rest is arena management. We just freed + * a pool, and there are 4 cases for arena mgmt: + * 1. If all the pools are free, return the arena to + * the system free(). + * 2. If this is the only free pool in the arena, + * add the arena back to the `usable_arenas` list. + * 3. If the "next" arena has a smaller count of free + * pools, we have to "slide this arena right" to + * restore that usable_arenas is sorted in order of + * nfreepools. + * 4. Else there's nothing more to do. + */ + if (nf == ao->ntotalpools) { + /* Case 1. First unlink ao from usable_arenas. + */ + assert(ao->prevarena == NULL || + ao->prevarena->address != 0); + assert(ao ->nextarena == NULL || + ao->nextarena->address != 0); + + /* Fix the pointer in the prevarena, or the + * usable_arenas pointer. + */ + if (ao->prevarena == NULL) { + usable_arenas = ao->nextarena; + assert(usable_arenas == NULL || + usable_arenas->address != 0); + } + else { + assert(ao->prevarena->nextarena == ao); + ao->prevarena->nextarena = + ao->nextarena; + } + /* Fix the pointer in the nextarena. */ + if (ao->nextarena != NULL) { + assert(ao->nextarena->prevarena == ao); + ao->nextarena->prevarena = + ao->prevarena; + } + /* Record that this arena_object slot is + * available to be reused. + */ + ao->nextarena = unused_arena_objects; + unused_arena_objects = ao; + + /* Free the entire arena. */ + _PyObject_Arena.free(_PyObject_Arena.ctx, + (void *)ao->address, ARENA_SIZE); + ao->address = 0; /* mark unassociated */ + --narenas_currently_allocated; + + UNLOCK(); + return; + } + if (nf == 1) { + /* Case 2. Put ao at the head of + * usable_arenas. Note that because + * ao->nfreepools was 0 before, ao isn't + * currently on the usable_arenas list. + */ + ao->nextarena = usable_arenas; + ao->prevarena = NULL; + if (usable_arenas) + usable_arenas->prevarena = ao; + usable_arenas = ao; + assert(usable_arenas->address != 0); + + UNLOCK(); + return; + } + /* If this arena is now out of order, we need to keep + * the list sorted. The list is kept sorted so that + * the "most full" arenas are used first, which allows + * the nearly empty arenas to be completely freed. In + * a few un-scientific tests, it seems like this + * approach allowed a lot more memory to be freed. + */ + if (ao->nextarena == NULL || + nf <= ao->nextarena->nfreepools) { + /* Case 4. Nothing to do. */ + UNLOCK(); + return; + } + /* Case 3: We have to move the arena towards the end + * of the list, because it has more free pools than + * the arena to its right. + * First unlink ao from usable_arenas. + */ + if (ao->prevarena != NULL) { + /* ao isn't at the head of the list */ + assert(ao->prevarena->nextarena == ao); + ao->prevarena->nextarena = ao->nextarena; + } + else { + /* ao is at the head of the list */ + assert(usable_arenas == ao); + usable_arenas = ao->nextarena; + } + ao->nextarena->prevarena = ao->prevarena; + + /* Locate the new insertion point by iterating over + * the list, using our nextarena pointer. + */ + while (ao->nextarena != NULL && + nf > ao->nextarena->nfreepools) { + ao->prevarena = ao->nextarena; + ao->nextarena = ao->nextarena->nextarena; + } + + /* Insert ao at this point. */ + assert(ao->nextarena == NULL || + ao->prevarena == ao->nextarena->prevarena); + assert(ao->prevarena->nextarena == ao->nextarena); + + ao->prevarena->nextarena = ao; + if (ao->nextarena != NULL) + ao->nextarena->prevarena = ao; + + /* Verify that the swaps worked. */ + assert(ao->nextarena == NULL || + nf <= ao->nextarena->nfreepools); + assert(ao->prevarena == NULL || + nf > ao->prevarena->nfreepools); + assert(ao->nextarena == NULL || + ao->nextarena->prevarena == ao); + assert((usable_arenas == ao && + ao->prevarena == NULL) || + ao->prevarena->nextarena == ao); + + UNLOCK(); + return; + } + /* Pool was full, so doesn't currently live in any list: + * link it to the front of the appropriate usedpools[] list. + * This mimics LRU pool usage for new allocations and + * targets optimal filling when several pools contain + * blocks of the same size class. + */ + --pool->ref.count; + assert(pool->ref.count > 0); /* else the pool is empty */ + size = pool->szidx; + next = usedpools[size + size]; + prev = next->prevpool; + /* insert pool before next: prev <-> pool <-> next */ + pool->nextpool = next; + pool->prevpool = prev; + next->prevpool = pool; + prev->nextpool = pool; + UNLOCK(); + return; + } + +#ifdef WITH_VALGRIND +redirect: +#endif + /* We didn't allocate this address. */ + PyMem_RawFree(p); +} + +/* realloc. If p is NULL, this acts like malloc(nbytes). Else if nbytes==0, + * then as the Python docs promise, we do not treat this like free(p), and + * return a non-NULL result. + */ + +ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS +static void * +_PyObject_Realloc(void *ctx, void *p, size_t nbytes) +{ + void *bp; + poolp pool; + size_t size; +#ifndef Py_USING_MEMORY_DEBUGGER + uint arenaindex_temp; +#endif + + if (p == NULL) + return _PyObject_Alloc(0, ctx, 1, nbytes); + +#ifdef WITH_VALGRIND + /* Treat running_on_valgrind == -1 the same as 0 */ + if (UNLIKELY(running_on_valgrind > 0)) + goto redirect; +#endif + + pool = POOL_ADDR(p); + if (Py_ADDRESS_IN_RANGE(p, pool)) { + /* We're in charge of this block */ + size = INDEX2SIZE(pool->szidx); + if (nbytes <= size) { + /* The block is staying the same or shrinking. If + * it's shrinking, there's a tradeoff: it costs + * cycles to copy the block to a smaller size class, + * but it wastes memory not to copy it. The + * compromise here is to copy on shrink only if at + * least 25% of size can be shaved off. + */ + if (4 * nbytes > 3 * size) { + /* It's the same, + * or shrinking and new/old > 3/4. + */ + return p; + } + size = nbytes; + } + bp = _PyObject_Alloc(0, ctx, 1, nbytes); + if (bp != NULL) { + memcpy(bp, p, size); + _PyObject_Free(ctx, p); + } + return bp; + } +#ifdef WITH_VALGRIND + redirect: +#endif + /* We're not managing this block. If nbytes <= + * SMALL_REQUEST_THRESHOLD, it's tempting to try to take over this + * block. However, if we do, we need to copy the valid data from + * the C-managed block to one of our blocks, and there's no portable + * way to know how much of the memory space starting at p is valid. + * As bug 1185883 pointed out the hard way, it's possible that the + * C-managed block is "at the end" of allocated VM space, so that + * a memory fault can occur if we try to copy nbytes bytes starting + * at p. Instead we punt: let C continue to manage this block. + */ + if (nbytes) + return PyMem_RawRealloc(p, nbytes); + /* C doesn't define the result of realloc(p, 0) (it may or may not + * return NULL then), but Python's docs promise that nbytes==0 never + * returns NULL. We don't pass 0 to realloc(), to avoid that endcase + * to begin with. Even then, we can't be sure that realloc() won't + * return NULL. + */ + bp = PyMem_RawRealloc(p, 1); + return bp ? bp : p; +} + +#else /* ! WITH_PYMALLOC */ + +/*==========================================================================*/ +/* pymalloc not enabled: Redirect the entry points to malloc. These will + * only be used by extensions that are compiled with pymalloc enabled. */ + +Py_ssize_t +_Py_GetAllocatedBlocks(void) +{ + return 0; +} + +#endif /* WITH_PYMALLOC */ + + +/*==========================================================================*/ +/* A x-platform debugging allocator. This doesn't manage memory directly, + * it wraps a real allocator, adding extra debugging info to the memory blocks. + */ + +/* Special bytes broadcast into debug memory blocks at appropriate times. + * Strings of these are unlikely to be valid addresses, floats, ints or + * 7-bit ASCII. + */ +#undef CLEANBYTE +#undef DEADBYTE +#undef FORBIDDENBYTE +#define CLEANBYTE 0xCB /* clean (newly allocated) memory */ +#define DEADBYTE 0xDB /* dead (newly freed) memory */ +#define FORBIDDENBYTE 0xFB /* untouchable bytes at each end of a block */ + +static size_t serialno = 0; /* incremented on each debug {m,re}alloc */ + +/* serialno is always incremented via calling this routine. The point is + * to supply a single place to set a breakpoint. + */ +static void +bumpserialno(void) +{ + ++serialno; +} + +#define SST SIZEOF_SIZE_T + +/* Read sizeof(size_t) bytes at p as a big-endian size_t. */ +static size_t +read_size_t(const void *p) +{ + const uchar *q = (const uchar *)p; + size_t result = *q++; + int i; + + for (i = SST; --i > 0; ++q) + result = (result << 8) | *q; + return result; +} + +/* Write n as a big-endian size_t, MSB at address p, LSB at + * p + sizeof(size_t) - 1. + */ +static void +write_size_t(void *p, size_t n) +{ + uchar *q = (uchar *)p + SST - 1; + int i; + + for (i = SST; --i >= 0; --q) { + *q = (uchar)(n & 0xff); + n >>= 8; + } +} + +/* Let S = sizeof(size_t). The debug malloc asks for 4*S extra bytes and + fills them with useful stuff, here calling the underlying malloc's result p: + +p[0: S] + Number of bytes originally asked for. This is a size_t, big-endian (easier + to read in a memory dump). +p[S] + API ID. See PEP 445. This is a character, but seems undocumented. +p[S+1: 2*S] + Copies of FORBIDDENBYTE. Used to catch under- writes and reads. +p[2*S: 2*S+n] + The requested memory, filled with copies of CLEANBYTE. + Used to catch reference to uninitialized memory. + &p[2*S] is returned. Note that this is 8-byte aligned if pymalloc + handled the request itself. +p[2*S+n: 2*S+n+S] + Copies of FORBIDDENBYTE. Used to catch over- writes and reads. +p[2*S+n+S: 2*S+n+2*S] + A serial number, incremented by 1 on each call to _PyMem_DebugMalloc + and _PyMem_DebugRealloc. + This is a big-endian size_t. + If "bad memory" is detected later, the serial number gives an + excellent way to set a breakpoint on the next run, to capture the + instant at which this block was passed out. +*/ + +static void * +_PyMem_DebugRawAlloc(int use_calloc, void *ctx, size_t nbytes) +{ + debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; + uchar *p; /* base address of malloc'ed block */ + uchar *tail; /* p + 2*SST + nbytes == pointer to tail pad bytes */ + size_t total; /* nbytes + 4*SST */ + + bumpserialno(); + total = nbytes + 4*SST; + if (nbytes > PY_SSIZE_T_MAX - 4*SST) + /* overflow: can't represent total as a Py_ssize_t */ + return NULL; + + if (use_calloc) + p = (uchar *)api->alloc.calloc(api->alloc.ctx, 1, total); + else + p = (uchar *)api->alloc.malloc(api->alloc.ctx, total); + if (p == NULL) + return NULL; + + /* at p, write size (SST bytes), id (1 byte), pad (SST-1 bytes) */ + write_size_t(p, nbytes); + p[SST] = (uchar)api->api_id; + memset(p + SST + 1, FORBIDDENBYTE, SST-1); + + if (nbytes > 0 && !use_calloc) + memset(p + 2*SST, CLEANBYTE, nbytes); + + /* at tail, write pad (SST bytes) and serialno (SST bytes) */ + tail = p + 2*SST + nbytes; + memset(tail, FORBIDDENBYTE, SST); + write_size_t(tail + SST, serialno); + + return p + 2*SST; +} + +static void * +_PyMem_DebugRawMalloc(void *ctx, size_t nbytes) +{ + return _PyMem_DebugRawAlloc(0, ctx, nbytes); +} + +static void * +_PyMem_DebugRawCalloc(void *ctx, size_t nelem, size_t elsize) +{ + size_t nbytes; + assert(elsize == 0 || nelem <= PY_SSIZE_T_MAX / elsize); + nbytes = nelem * elsize; + return _PyMem_DebugRawAlloc(1, ctx, nbytes); +} + +/* The debug free first checks the 2*SST bytes on each end for sanity (in + particular, that the FORBIDDENBYTEs with the api ID are still intact). + Then fills the original bytes with DEADBYTE. + Then calls the underlying free. +*/ +static void +_PyMem_DebugRawFree(void *ctx, void *p) +{ + debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; + uchar *q = (uchar *)p - 2*SST; /* address returned from malloc */ + size_t nbytes; + + if (p == NULL) + return; + _PyMem_DebugCheckAddress(api->api_id, p); + nbytes = read_size_t(q); + nbytes += 4*SST; + if (nbytes > 0) + memset(q, DEADBYTE, nbytes); + api->alloc.free(api->alloc.ctx, q); +} + +static void * +_PyMem_DebugRawRealloc(void *ctx, void *p, size_t nbytes) +{ + debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; + uchar *q = (uchar *)p, *oldq; + uchar *tail; + size_t total; /* nbytes + 4*SST */ + size_t original_nbytes; + int i; + + if (p == NULL) + return _PyMem_DebugRawAlloc(0, ctx, nbytes); + + _PyMem_DebugCheckAddress(api->api_id, p); + bumpserialno(); + original_nbytes = read_size_t(q - 2*SST); + total = nbytes + 4*SST; + if (nbytes > PY_SSIZE_T_MAX - 4*SST) + /* overflow: can't represent total as a Py_ssize_t */ + return NULL; + + /* Resize and add decorations. We may get a new pointer here, in which + * case we didn't get the chance to mark the old memory with DEADBYTE, + * but we live with that. + */ + oldq = q; + q = (uchar *)api->alloc.realloc(api->alloc.ctx, q - 2*SST, total); + if (q == NULL) + return NULL; + + if (q == oldq && nbytes < original_nbytes) { + /* shrinking: mark old extra memory dead */ + memset(q + nbytes, DEADBYTE, original_nbytes - nbytes); + } + + write_size_t(q, nbytes); + assert(q[SST] == (uchar)api->api_id); + for (i = 1; i < SST; ++i) + assert(q[SST + i] == FORBIDDENBYTE); + q += 2*SST; + + tail = q + nbytes; + memset(tail, FORBIDDENBYTE, SST); + write_size_t(tail + SST, serialno); + + if (nbytes > original_nbytes) { + /* growing: mark new extra memory clean */ + memset(q + original_nbytes, CLEANBYTE, + nbytes - original_nbytes); + } + + return q; +} + +static void +_PyMem_DebugCheckGIL(void) +{ +#ifdef WITH_THREAD + if (!PyGILState_Check()) + Py_FatalError("Python memory allocator called " + "without holding the GIL"); +#endif +} + +static void * +_PyMem_DebugMalloc(void *ctx, size_t nbytes) +{ + _PyMem_DebugCheckGIL(); + return _PyMem_DebugRawMalloc(ctx, nbytes); +} + +static void * +_PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize) +{ + _PyMem_DebugCheckGIL(); + return _PyMem_DebugRawCalloc(ctx, nelem, elsize); +} + +static void +_PyMem_DebugFree(void *ctx, void *ptr) +{ + _PyMem_DebugCheckGIL(); + return _PyMem_DebugRawFree(ctx, ptr); +} + +static void * +_PyMem_DebugRealloc(void *ctx, void *ptr, size_t nbytes) +{ + _PyMem_DebugCheckGIL(); + return _PyMem_DebugRawRealloc(ctx, ptr, nbytes); +} + +/* Check the forbidden bytes on both ends of the memory allocated for p. + * If anything is wrong, print info to stderr via _PyObject_DebugDumpAddress, + * and call Py_FatalError to kill the program. + * The API id, is also checked. + */ +static void +_PyMem_DebugCheckAddress(char api, const void *p) +{ + const uchar *q = (const uchar *)p; + char msgbuf[64]; + char *msg; + size_t nbytes; + const uchar *tail; + int i; + char id; + + if (p == NULL) { + msg = "didn't expect a NULL pointer"; + goto error; + } + + /* Check the API id */ + id = (char)q[-SST]; + if (id != api) { + msg = msgbuf; + snprintf(msg, sizeof(msgbuf), "bad ID: Allocated using API '%c', verified using API '%c'", id, api); + msgbuf[sizeof(msgbuf)-1] = 0; + goto error; + } + + /* Check the stuff at the start of p first: if there's underwrite + * corruption, the number-of-bytes field may be nuts, and checking + * the tail could lead to a segfault then. + */ + for (i = SST-1; i >= 1; --i) { + if (*(q-i) != FORBIDDENBYTE) { + msg = "bad leading pad byte"; + goto error; + } + } + + nbytes = read_size_t(q - 2*SST); + tail = q + nbytes; + for (i = 0; i < SST; ++i) { + if (tail[i] != FORBIDDENBYTE) { + msg = "bad trailing pad byte"; + goto error; + } + } + + return; + +error: + _PyObject_DebugDumpAddress(p); + Py_FatalError(msg); +} + +/* Display info to stderr about the memory block at p. */ +static void +_PyObject_DebugDumpAddress(const void *p) +{ + const uchar *q = (const uchar *)p; + const uchar *tail; + size_t nbytes, serial; + int i; + int ok; + char id; + + fprintf(stderr, "Debug memory block at address p=%p:", p); + if (p == NULL) { + fprintf(stderr, "\n"); + return; + } + id = (char)q[-SST]; + fprintf(stderr, " API '%c'\n", id); + + nbytes = read_size_t(q - 2*SST); + fprintf(stderr, " %" PY_FORMAT_SIZE_T "u bytes originally " + "requested\n", nbytes); + + /* In case this is nuts, check the leading pad bytes first. */ + fprintf(stderr, " The %d pad bytes at p-%d are ", SST-1, SST-1); + ok = 1; + for (i = 1; i <= SST-1; ++i) { + if (*(q-i) != FORBIDDENBYTE) { + ok = 0; + break; + } + } + if (ok) + fputs("FORBIDDENBYTE, as expected.\n", stderr); + else { + fprintf(stderr, "not all FORBIDDENBYTE (0x%02x):\n", + FORBIDDENBYTE); + for (i = SST-1; i >= 1; --i) { + const uchar byte = *(q-i); + fprintf(stderr, " at p-%d: 0x%02x", i, byte); + if (byte != FORBIDDENBYTE) + fputs(" *** OUCH", stderr); + fputc('\n', stderr); + } + + fputs(" Because memory is corrupted at the start, the " + "count of bytes requested\n" + " may be bogus, and checking the trailing pad " + "bytes may segfault.\n", stderr); + } + + tail = q + nbytes; + fprintf(stderr, " The %d pad bytes at tail=%p are ", SST, tail); + ok = 1; + for (i = 0; i < SST; ++i) { + if (tail[i] != FORBIDDENBYTE) { + ok = 0; + break; + } + } + if (ok) + fputs("FORBIDDENBYTE, as expected.\n", stderr); + else { + fprintf(stderr, "not all FORBIDDENBYTE (0x%02x):\n", + FORBIDDENBYTE); + for (i = 0; i < SST; ++i) { + const uchar byte = tail[i]; + fprintf(stderr, " at tail+%d: 0x%02x", + i, byte); + if (byte != FORBIDDENBYTE) + fputs(" *** OUCH", stderr); + fputc('\n', stderr); + } + } + + serial = read_size_t(tail + SST); + fprintf(stderr, " The block was made by call #%" PY_FORMAT_SIZE_T + "u to debug malloc/realloc.\n", serial); + + if (nbytes > 0) { + i = 0; + fputs(" Data at p:", stderr); + /* print up to 8 bytes at the start */ + while (q < tail && i < 8) { + fprintf(stderr, " %02x", *q); + ++i; + ++q; + } + /* and up to 8 at the end */ + if (q < tail) { + if (tail - q > 8) { + fputs(" ...", stderr); + q = tail - 8; + } + while (q < tail) { + fprintf(stderr, " %02x", *q); + ++q; + } + } + fputc('\n', stderr); + } + fputc('\n', stderr); + + fflush(stderr); + _PyMem_DumpTraceback(fileno(stderr), p); +} + + +static size_t +printone(FILE *out, const char* msg, size_t value) +{ + int i, k; + char buf[100]; + size_t origvalue = value; + + fputs(msg, out); + for (i = (int)strlen(msg); i < 35; ++i) + fputc(' ', out); + fputc('=', out); + + /* Write the value with commas. */ + i = 22; + buf[i--] = '\0'; + buf[i--] = '\n'; + k = 3; + do { + size_t nextvalue = value / 10; + unsigned int digit = (unsigned int)(value - nextvalue * 10); + value = nextvalue; + buf[i--] = (char)(digit + '0'); + --k; + if (k == 0 && value && i >= 0) { + k = 3; + buf[i--] = ','; + } + } while (value && i >= 0); + + while (i >= 0) + buf[i--] = ' '; + fputs(buf, out); + + return origvalue; +} + +void +_PyDebugAllocatorStats(FILE *out, + const char *block_name, int num_blocks, size_t sizeof_block) +{ + char buf1[128]; + char buf2[128]; + PyOS_snprintf(buf1, sizeof(buf1), + "%d %ss * %" PY_FORMAT_SIZE_T "d bytes each", + num_blocks, block_name, sizeof_block); + PyOS_snprintf(buf2, sizeof(buf2), + "%48s ", buf1); + (void)printone(out, buf2, num_blocks * sizeof_block); +} + + +#ifdef WITH_PYMALLOC + +#ifdef Py_DEBUG +/* Is target in the list? The list is traversed via the nextpool pointers. + * The list may be NULL-terminated, or circular. Return 1 if target is in + * list, else 0. + */ +static int +pool_is_in_list(const poolp target, poolp list) +{ + poolp origlist = list; + assert(target != NULL); + if (list == NULL) + return 0; + do { + if (target == list) + return 1; + list = list->nextpool; + } while (list != NULL && list != origlist); + return 0; +} +#endif + +/* Print summary info to "out" about the state of pymalloc's structures. + * In Py_DEBUG mode, also perform some expensive internal consistency + * checks. + */ +void +_PyObject_DebugMallocStats(FILE *out) +{ + uint i; + const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT; + /* # of pools, allocated blocks, and free blocks per class index */ + size_t numpools[SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT]; + size_t numblocks[SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT]; + size_t numfreeblocks[SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT]; + /* total # of allocated bytes in used and full pools */ + size_t allocated_bytes = 0; + /* total # of available bytes in used pools */ + size_t available_bytes = 0; + /* # of free pools + pools not yet carved out of current arena */ + uint numfreepools = 0; + /* # of bytes for arena alignment padding */ + size_t arena_alignment = 0; + /* # of bytes in used and full pools used for pool_headers */ + size_t pool_header_bytes = 0; + /* # of bytes in used and full pools wasted due to quantization, + * i.e. the necessarily leftover space at the ends of used and + * full pools. + */ + size_t quantization = 0; + /* # of arenas actually allocated. */ + size_t narenas = 0; + /* running total -- should equal narenas * ARENA_SIZE */ + size_t total; + char buf[128]; + + fprintf(out, "Small block threshold = %d, in %u size classes.\n", + SMALL_REQUEST_THRESHOLD, numclasses); + + for (i = 0; i < numclasses; ++i) + numpools[i] = numblocks[i] = numfreeblocks[i] = 0; + + /* Because full pools aren't linked to from anything, it's easiest + * to march over all the arenas. If we're lucky, most of the memory + * will be living in full pools -- would be a shame to miss them. + */ + for (i = 0; i < maxarenas; ++i) { + uint j; + uptr base = arenas[i].address; + + /* Skip arenas which are not allocated. */ + if (arenas[i].address == (uptr)NULL) + continue; + narenas += 1; + + numfreepools += arenas[i].nfreepools; + + /* round up to pool alignment */ + if (base & (uptr)POOL_SIZE_MASK) { + arena_alignment += POOL_SIZE; + base &= ~(uptr)POOL_SIZE_MASK; + base += POOL_SIZE; + } + + /* visit every pool in the arena */ + assert(base <= (uptr) arenas[i].pool_address); + for (j = 0; + base < (uptr) arenas[i].pool_address; + ++j, base += POOL_SIZE) { + poolp p = (poolp)base; + const uint sz = p->szidx; + uint freeblocks; + + if (p->ref.count == 0) { + /* currently unused */ +#ifdef Py_DEBUG + assert(pool_is_in_list(p, arenas[i].freepools)); +#endif + continue; + } + ++numpools[sz]; + numblocks[sz] += p->ref.count; + freeblocks = NUMBLOCKS(sz) - p->ref.count; + numfreeblocks[sz] += freeblocks; +#ifdef Py_DEBUG + if (freeblocks > 0) + assert(pool_is_in_list(p, usedpools[sz + sz])); +#endif + } + } + assert(narenas == narenas_currently_allocated); + + fputc('\n', out); + fputs("class size num pools blocks in use avail blocks\n" + "----- ---- --------- ------------- ------------\n", + out); + + for (i = 0; i < numclasses; ++i) { + size_t p = numpools[i]; + size_t b = numblocks[i]; + size_t f = numfreeblocks[i]; + uint size = INDEX2SIZE(i); + if (p == 0) { + assert(b == 0 && f == 0); + continue; + } + fprintf(out, "%5u %6u " + "%11" PY_FORMAT_SIZE_T "u " + "%15" PY_FORMAT_SIZE_T "u " + "%13" PY_FORMAT_SIZE_T "u\n", + i, size, p, b, f); + allocated_bytes += b * size; + available_bytes += f * size; + pool_header_bytes += p * POOL_OVERHEAD; + quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size); + } + fputc('\n', out); + if (_PyMem_DebugEnabled()) + (void)printone(out, "# times object malloc called", serialno); + (void)printone(out, "# arenas allocated total", ntimes_arena_allocated); + (void)printone(out, "# arenas reclaimed", ntimes_arena_allocated - narenas); + (void)printone(out, "# arenas highwater mark", narenas_highwater); + (void)printone(out, "# arenas allocated current", narenas); + + PyOS_snprintf(buf, sizeof(buf), + "%" PY_FORMAT_SIZE_T "u arenas * %d bytes/arena", + narenas, ARENA_SIZE); + (void)printone(out, buf, narenas * ARENA_SIZE); + + fputc('\n', out); + + total = printone(out, "# bytes in allocated blocks", allocated_bytes); + total += printone(out, "# bytes in available blocks", available_bytes); + + PyOS_snprintf(buf, sizeof(buf), + "%u unused pools * %d bytes", numfreepools, POOL_SIZE); + total += printone(out, buf, (size_t)numfreepools * POOL_SIZE); + + total += printone(out, "# bytes lost to pool headers", pool_header_bytes); + total += printone(out, "# bytes lost to quantization", quantization); + total += printone(out, "# bytes lost to arena alignment", arena_alignment); + (void)printone(out, "Total", total); +} + +#endif /* #ifdef WITH_PYMALLOC */ + + +#ifdef Py_USING_MEMORY_DEBUGGER +/* Make this function last so gcc won't inline it since the definition is + * after the reference. + */ +int +Py_ADDRESS_IN_RANGE(void *P, poolp pool) +{ + uint arenaindex_temp = pool->arenaindex; + + return arenaindex_temp < maxarenas && + (uptr)P - arenas[arenaindex_temp].address < (uptr)ARENA_SIZE && + arenas[arenaindex_temp].address != 0; +} +#endif diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_1fc238a_a33099_Objects#dictobject.c b/src/main/resource/testFiles/cpython/prevFiles/prev_1fc238a_a33099_Objects#dictobject.c new file mode 100644 index 0000000..c50b35a --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_1fc238a_a33099_Objects#dictobject.c @@ -0,0 +1,788 @@ +/*********************************************************** +Copyright 1991, 1992, 1993 by Stichting Mathematisch Centrum, +Amsterdam, The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Mapping object implementation; using a hash table */ + +/* This file should really be called "dictobject.c", since "mapping" + is the generic name for objects with an unorderred arbitrary key + set (just like lists are sequences), but since it improves (and was + originally derived from) a file by that name I had to change its + name. For the user these objects are still called "dictionaries". */ + +#include "allobjects.h" +#include "modsupport.h" + + +/* +Table of primes suitable as keys, in ascending order. +The first line are the largest primes less than some powers of two, +the second line is the largest prime less than 6000, +and the third line is a selection from Knuth, Vol. 3, Sec. 6.1, Table 1. +The final value is a sentinel and should cause the memory allocation +of that many entries to fail (if none of the earlier values cause such +failure already). +*/ +static unsigned int primes[] = { + 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2017, 4093, + 5987, + 9551, 15683, 19609, 31397, + 0xffffffff /* All bits set -- truncation OK */ +}; + +/* Object used as dummy key to fill deleted entries */ +static object *dummy; /* Initialized by first call to newmappingobject() */ + +/* +Invariant for entries: when in use, de_value is not NULL and de_key is +not NULL and not dummy; when not in use, de_value is NULL and de_key +is either NULL or dummy. A dummy key value cannot be replaced by +NULL, since otherwise other keys may be lost. +*/ +typedef struct { + long me_hash; + object *me_key; + object *me_value; +} mappingentry; + +/* +To ensure the lookup algorithm terminates, the table size must be a +prime number and there must be at least one NULL key in the table. +The value ma_fill is the number of non-NULL keys; ma_used is the number +of non-NULL, non-dummy keys. +To avoid slowing down lookups on a near-full table, we resize the table +when it is more than half filled. +*/ +typedef struct { + OB_HEAD + int ma_fill; + int ma_used; + int ma_size; + mappingentry *ma_table; +} mappingobject; + +object * +newmappingobject() +{ + register mappingobject *mp; + if (dummy == NULL) { /* Auto-initialize dummy */ + dummy = newstringobject(""); + if (dummy == NULL) + return NULL; + } + mp = NEWOBJ(mappingobject, &Mappingtype); + if (mp == NULL) + return NULL; + mp->ma_size = primes[0]; + mp->ma_table = (mappingentry *) calloc(sizeof(mappingentry), mp->ma_size); + if (mp->ma_table == NULL) { + DEL(mp); + return err_nomem(); + } + mp->ma_fill = 0; + mp->ma_used = 0; + return (object *)mp; +} + +/* +The basic lookup function used by all operations. +This is essentially Algorithm D from Knuth Vol. 3, Sec. 6.4. +Open addressing is preferred over chaining since the link overhead for +chaining would be substantial (100% with typical malloc overhead). + +First a 32-bit hash value, 'sum', is computed from the key string. +The first character is added an extra time shifted by 8 to avoid hashing +single-character keys (often heavily used variables) too close together. +All arithmetic on sum should ignore overflow. + +The initial probe index is then computed as sum mod the table size. +Subsequent probe indices are incr apart (mod table size), where incr +is also derived from sum, with the additional requirement that it is +relative prime to the table size (i.e., 1 <= incr < size, since the size +is a prime number). My choice for incr is somewhat arbitrary. +*/ +static mappingentry *lookmapping PROTO((mappingobject *, object *, long)); +static mappingentry * +lookmapping(mp, key, hash) + register mappingobject *mp; + object *key; + long hash; +{ + register int i, incr; + register unsigned long sum = (unsigned long) hash; + register mappingentry *freeslot = NULL; + /* We must come up with (i, incr) such that 0 <= i < ma_size + and 0 < incr < ma_size and both are a function of hash */ + i = sum % mp->ma_size; + do { + sum = sum + sum + sum + 1; + incr = sum % mp->ma_size; + } while (incr == 0); + for (;;) { + register mappingentry *ep = &mp->ma_table[i]; + if (ep->me_key == NULL) { + if (freeslot != NULL) + return freeslot; + else + return ep; + } + if (ep->me_key == dummy) { + if (freeslot != NULL) + freeslot = ep; + } + else if (ep->me_hash == hash && + cmpobject(ep->me_key, key) == 0) { + return ep; + } + i = (i + incr) % mp->ma_size; + } +} + +/* +Internal routine to insert a new item into the table. +Used both by the internal resize routine and by the public insert routine. +Eats a reference to key and one to value. +*/ +static void insertmapping PROTO((mappingobject *, object *, long, object *)); +static void +insertmapping(mp, key, hash, value) + register mappingobject *mp; + object *key; + long hash; + object *value; +{ + register mappingentry *ep; + ep = lookmapping(mp, key, hash); + if (ep->me_value != NULL) { + DECREF(ep->me_value); + DECREF(key); + } + else { + if (ep->me_key == NULL) + mp->ma_fill++; + else + DECREF(ep->me_key); + ep->me_key = key; + ep->me_hash = hash; + mp->ma_used++; + } + ep->me_value = value; +} + +/* +Restructure the table by allocating a new table and reinserting all +items again. When entries have been deleted, the new table may +actually be smaller than the old one. +*/ +static int mappingresize PROTO((mappingobject *)); +static int +mappingresize(mp) + mappingobject *mp; +{ + register int oldsize = mp->ma_size; + register int newsize; + register mappingentry *oldtable = mp->ma_table; + register mappingentry *newtable; + register mappingentry *ep; + register int i; + newsize = mp->ma_size; + for (i = 0; ; i++) { + if (primes[i] > mp->ma_used*2) { + newsize = primes[i]; + break; + } + } + newtable = (mappingentry *) calloc(sizeof(mappingentry), newsize); + if (newtable == NULL) { + err_nomem(); + return -1; + } + mp->ma_size = newsize; + mp->ma_table = newtable; + mp->ma_fill = 0; + mp->ma_used = 0; + for (i = 0, ep = oldtable; i < oldsize; i++, ep++) { + if (ep->me_value != NULL) + insertmapping(mp,ep->me_key,ep->me_hash,ep->me_value); + else { + XDECREF(ep->me_key); + } + } + DEL(oldtable); + return 0; +} + +object * +mappinglookup(op, key) + object *op; + object *key; +{ + long hash; + if (!is_mappingobject(op)) { + err_badcall(); + return NULL; + } + hash = hashobject(key); + if (hash == -1) + return NULL; + return lookmapping((mappingobject *)op, key, hash) -> me_value; +} + +int +mappinginsert(op, key, value) + register object *op; + object *key; + object *value; +{ + register mappingobject *mp; + register long hash; + if (!is_mappingobject(op)) { + err_badcall(); + return -1; + } + hash = hashobject(key); + if (hash == -1) + return -1; + mp = (mappingobject *)op; + /* if fill >= 2/3 size, resize */ + if (mp->ma_fill*3 >= mp->ma_size*2) { + if (mappingresize(mp) != 0) { + if (mp->ma_fill+1 > mp->ma_size) + return -1; + } + } + INCREF(value); + INCREF(key); + insertmapping(mp, key, hash, value); + return 0; +} + +int +mappingremove(op, key) + object *op; + object *key; +{ + register mappingobject *mp; + register long hash; + register mappingentry *ep; + if (!is_mappingobject(op)) { + err_badcall(); + return -1; + } + hash = hashobject(key); + if (hash == -1) + return -1; + mp = (mappingobject *)op; + ep = lookmapping(mp, key, hash); + if (ep->me_value == NULL) { + err_setval(KeyError, key); + return -1; + } + DECREF(ep->me_key); + INCREF(dummy); + ep->me_key = dummy; + DECREF(ep->me_value); + ep->me_value = NULL; + mp->ma_used--; + return 0; +} + +void +mappingclear(op) + object *op; +{ + int i; + register mappingobject *mp; + if (!is_mappingobject(op)) + return; + mp = (mappingobject *)op; + for (i = 0; i < mp->ma_size; i++) { + XDECREF(mp->ma_table[i].me_key); + XDECREF(mp->ma_table[i].me_value); + mp->ma_table[i].me_key = NULL; + mp->ma_table[i].me_value = NULL; + } + mp->ma_used = 0; +} + +int +mappinggetnext(op, ppos, pkey, pvalue) + object *op; + int *ppos; + object **pkey; + object **pvalue; +{ + int i; + register mappingobject *mp; + if (!is_dictobject(op)) + return 0; + mp = (mappingobject *)op; + i = *ppos; + if (i < 0) + return 0; + while (i < mp->ma_size && mp->ma_table[i].me_value == NULL) + i++; + *ppos = i+1; + if (i >= mp->ma_size) + return 0; + if (pkey) + *pkey = mp->ma_table[i].me_key; + if (pvalue) + *pvalue = mp->ma_table[i].me_value; + return 1; +} + +/* Methods */ + +static void +mapping_dealloc(mp) + register mappingobject *mp; +{ + register int i; + register mappingentry *ep; + for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) { + if (ep->me_key != NULL) + DECREF(ep->me_key); + if (ep->me_value != NULL) + DECREF(ep->me_value); + } + if (mp->ma_table != NULL) + DEL(mp->ma_table); + DEL(mp); +} + +static int +mapping_print(mp, fp, flags) + register mappingobject *mp; + register FILE *fp; + register int flags; +{ + register int i; + register int any; + register mappingentry *ep; + fprintf(fp, "{"); + any = 0; + for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) { + if (ep->me_value != NULL) { + if (any++ > 0) + fprintf(fp, ", "); + if (printobject((object *)ep->me_key, fp, flags) != 0) + return -1; + fprintf(fp, ": "); + if (printobject(ep->me_value, fp, flags) != 0) + return -1; + } + } + fprintf(fp, "}"); + return 0; +} + +static void +js(pv, w) + object **pv; + object *w; +{ + joinstring(pv, w); + XDECREF(w); +} + +static object * +mapping_repr(mp) + mappingobject *mp; +{ + auto object *v; + object *sepa, *colon; + register int i; + register int any; + register mappingentry *ep; + v = newstringobject("{"); + sepa = newstringobject(", "); + colon = newstringobject(": "); + any = 0; + for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) { + if (ep->me_value != NULL) { + if (any++) + joinstring(&v, sepa); + js(&v, reprobject(ep->me_key)); + joinstring(&v, colon); + js(&v, reprobject(ep->me_value)); + } + } + js(&v, newstringobject("}")); + XDECREF(sepa); + XDECREF(colon); + return v; +} + +static int +mapping_length(mp) + mappingobject *mp; +{ + return mp->ma_used; +} + +static object * +mapping_subscript(mp, key) + mappingobject *mp; + register object *key; +{ + object *v; + long hash = hashobject(key); + if (hash == -1) + return NULL; + v = lookmapping(mp, key, hash) -> me_value; + if (v == NULL) + err_setval(KeyError, key); + else + INCREF(v); + return v; +} + +static int +mapping_ass_sub(mp, v, w) + mappingobject *mp; + object *v, *w; +{ + if (w == NULL) + return mappingremove((object *)mp, v); + else + return mappinginsert((object *)mp, v, w); +} + +static mapping_methods mapping_as_mapping = { + mapping_length, /*mp_length*/ + mapping_subscript, /*mp_subscript*/ + mapping_ass_sub, /*mp_ass_subscript*/ +}; + +static object * +mapping_keys(mp, args) + register mappingobject *mp; + object *args; +{ + register object *v; + register int i, j; + if (!getnoarg(args)) + return NULL; + v = newlistobject(mp->ma_used); + if (v == NULL) + return NULL; + for (i = 0, j = 0; i < mp->ma_size; i++) { + if (mp->ma_table[i].me_value != NULL) { + object *key = mp->ma_table[i].me_key; + INCREF(key); + setlistitem(v, j, key); + j++; + } + } + return v; +} + +static object * +mapping_values(mp, args) + register mappingobject *mp; + object *args; +{ + register object *v; + register int i, j; + if (!getnoarg(args)) + return NULL; + v = newlistobject(mp->ma_used); + if (v == NULL) + return NULL; + for (i = 0, j = 0; i < mp->ma_size; i++) { + if (mp->ma_table[i].me_value != NULL) { + object *value = mp->ma_table[i].me_value; + INCREF(value); + setlistitem(v, j, value); + j++; + } + } + return v; +} + +static object * +mapping_items(mp, args) + register mappingobject *mp; + object *args; +{ + register object *v; + register int i, j; + if (!getnoarg(args)) + return NULL; + v = newlistobject(mp->ma_used); + if (v == NULL) + return NULL; + for (i = 0, j = 0; i < mp->ma_size; i++) { + if (mp->ma_table[i].me_value != NULL) { + object *key = mp->ma_table[i].me_key; + object *value = mp->ma_table[i].me_value; + object *item = newtupleobject(2); + if (item == NULL) { + DECREF(v); + return NULL; + } + INCREF(key); + settupleitem(item, 0, key); + INCREF(value); + settupleitem(item, 1, value); + setlistitem(v, j, item); + j++; + } + } + return v; +} + +object * +getmappingkeys(mp) + object *mp; +{ + if (mp == NULL || !is_mappingobject(mp)) { + err_badcall(); + return NULL; + } + return mapping_keys((mappingobject *)mp, (object *)NULL); +} + +object * +getmappingvalues(mp) + object *mp; +{ + if (mp == NULL || !is_mappingobject(mp)) { + err_badcall(); + return NULL; + } + return mapping_values((mappingobject *)mp, (object *)NULL); +} + +object * +getmappingitems(mp) + object *mp; +{ + if (mp == NULL || !is_mappingobject(mp)) { + err_badcall(); + return NULL; + } + return mapping_values((mappingobject *)mp, (object *)NULL); +} + +static int +mapping_compare(a, b) + mappingobject *a, *b; +{ + object *akeys, *bkeys; + int i, n, res; + if (a == b) + return 0; + if (a->ma_used == 0) { + if (b->ma_used != 0) + return -1; + else + return 0; + } + else { + if (b->ma_used == 0) + return 1; + } + akeys = mapping_keys(a, (object *)NULL); + bkeys = mapping_keys(b, (object *)NULL); + if (akeys == NULL || bkeys == NULL) { + /* Oops, out of memory -- what to do? */ + /* For now, sort on address! */ + XDECREF(akeys); + XDECREF(bkeys); + if (a < b) + return -1; + else + return 1; + } + sortlist(akeys); + sortlist(bkeys); + n = a->ma_used < b->ma_used ? a->ma_used : b->ma_used; /* smallest */ + res = 0; + for (i = 0; i < n; i++) { + object *akey, *bkey, *aval, *bval; + long ahash, bhash; + akey = getlistitem(akeys, i); + bkey = getlistitem(bkeys, i); + res = cmpobject(akey, bkey); + if (res != 0) + break; + ahash = hashobject(akey); + if (ahash == -1) + err_clear(); /* Don't want errors here */ + bhash = hashobject(bkey); + if (bhash == -1) + err_clear(); /* Don't want errors here */ + aval = lookmapping(a, akey, ahash) -> me_value; + bval = lookmapping(b, bkey, bhash) -> me_value; + res = cmpobject(aval, bval); + if (res != 0) + break; + } + if (res == 0) { + if (a->ma_used < b->ma_used) + res = -1; + else if (a->ma_used > b->ma_used) + res = 1; + } + DECREF(akeys); + DECREF(bkeys); + return res; +} + +static object * +mapping_has_key(mp, args) + register mappingobject *mp; + object *args; +{ + object *key; + long hash; + register long ok; + if (!getargs(args, "O", &key)) + return NULL; + hash = hashobject(key); + if (hash == -1) + return NULL; + ok = lookmapping(mp, key, hash)->me_value != NULL; + return newintobject(ok); +} + +static struct methodlist mapp_methods[] = { + {"has_key", mapping_has_key}, + {"items", mapping_items}, + {"keys", mapping_keys}, + {"values", mapping_values}, + {NULL, NULL} /* sentinel */ +}; + +static object * +mapping_getattr(mp, name) + mappingobject *mp; + char *name; +{ + return findmethod(mapp_methods, (object *)mp, name); +} + +typeobject Mappingtype = { + OB_HEAD_INIT(&Typetype) + 0, + "dictionary", + sizeof(mappingobject), + 0, + mapping_dealloc, /*tp_dealloc*/ + mapping_print, /*tp_print*/ + mapping_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + mapping_compare, /*tp_compare*/ + mapping_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &mapping_as_mapping, /*tp_as_mapping*/ +}; + +/* For backward compatibility with old dictionary interface */ + +static object *last_name_object; +static char *last_name_char; + +object * +getattro(v, name) + object *v; + object *name; +{ + if (name != last_name_object) { + XDECREF(last_name_object); + INCREF(name); + last_name_object = name; + last_name_char = getstringvalue(name); + } + return getattr(v, last_name_char); +} + +int +setattro(v, name, value) + object *v; + object *name; + object *value; +{ + if (name != last_name_object) { + XDECREF(last_name_object); + INCREF(name); + last_name_object = name; + last_name_char = getstringvalue(name); + } + return setattr(v, last_name_char, value); +} + +object * +dictlookup(v, key) + object *v; + char *key; +{ + if (key != last_name_char) { + XDECREF(last_name_object); + last_name_object = newstringobject(key); + if (last_name_object == NULL) { + last_name_char = NULL; + return NULL; + } + last_name_char = key; + } + return mappinglookup(v, last_name_object); +} + +int +dictinsert(v, key, item) + object *v; + char *key; + object *item; +{ + if (key != last_name_char) { + XDECREF(last_name_object); + last_name_object = newstringobject(key); + if (last_name_object == NULL) { + last_name_char = NULL; + return NULL; + } + last_name_char = key; + } + return mappinginsert(v, last_name_object, item); +} + +int +dictremove(v, key) + object *v; + char *key; +{ + if (key != last_name_char) { + XDECREF(last_name_object); + last_name_object = newstringobject(key); + if (last_name_object == NULL) { + last_name_char = NULL; + return NULL; + } + last_name_char = key; + } + return mappingremove(v, last_name_object); +} diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_516999_437567_Modules#posixmodule.c b/src/main/resource/testFiles/cpython/prevFiles/prev_516999_437567_Modules#posixmodule.c new file mode 100644 index 0000000..3c28424 --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_516999_437567_Modules#posixmodule.c @@ -0,0 +1,8162 @@ + +/* POSIX module implementation */ + +/* This file is also used for Windows NT/MS-Win and OS/2. In that case the + module actually calls itself 'nt' or 'os2', not 'posix', and a few + functions are either unimplemented or implemented differently. The source + assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent + of the compiler used. Different compilers define their own feature + test macro, e.g. '__BORLANDC__' or '_MSC_VER'. For OS/2, the compiler + independent macro PYOS_OS2 should be defined. On OS/2 the default + compiler is assumed to be IBM's VisualAge C++ (VACPP). PYCC_GCC is used + as the compiler specific macro for the EMX port of gcc to OS/2. */ + +/* See also ../Dos/dosmodule.c */ + +#define PY_SSIZE_T_CLEAN + +#include "Python.h" +#include "structseq.h" + +#if defined(__VMS) +# include +#endif /* defined(__VMS) */ + +PyDoc_STRVAR(posix__doc__, +"This module provides access to operating system functionality that is\n\ +standardized by the C Standard and the POSIX standard (a thinly\n\ +disguised Unix interface). Refer to the library manual and\n\ +corresponding Unix manual entries for more information on calls."); + +#ifndef Py_USING_UNICODE +/* This is used in signatures of functions. */ +#define Py_UNICODE void +#endif + +#if defined(PYOS_OS2) +#define INCL_DOS +#define INCL_DOSERRORS +#define INCL_DOSPROCESS +#define INCL_NOPMAPI +#include +#if defined(PYCC_GCC) +#include +#include +#include +#include +#endif +#include "osdefs.h" +#endif + +#include +#include + +#ifdef HAVE_SYS_WAIT_H +#include /* For WNOHANG */ +#endif + +#include + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#ifdef HAVE_GRP_H +#include +#endif + +#ifdef HAVE_SYSEXITS_H +#include +#endif /* HAVE_SYSEXITS_H */ + +#ifdef HAVE_SYS_LOADAVG_H +#include +#endif + +/* Various compilers have only certain posix functions */ +/* XXX Gosh I wish these were all moved into pyconfig.h */ +#if defined(PYCC_VACPP) && defined(PYOS_OS2) +#include +#else +#if defined(__WATCOMC__) && !defined(__QNX__) /* Watcom compiler */ +#define HAVE_GETCWD 1 +#define HAVE_OPENDIR 1 +#define HAVE_SYSTEM 1 +#if defined(__OS2__) +#define HAVE_EXECV 1 +#define HAVE_WAIT 1 +#endif +#include +#else +#ifdef __BORLANDC__ /* Borland compiler */ +#define HAVE_EXECV 1 +#define HAVE_GETCWD 1 +#define HAVE_OPENDIR 1 +#define HAVE_PIPE 1 +#define HAVE_POPEN 1 +#define HAVE_SYSTEM 1 +#define HAVE_WAIT 1 +#else +#ifdef _MSC_VER /* Microsoft compiler */ +#define HAVE_GETCWD 1 +#define HAVE_SPAWNV 1 +#define HAVE_EXECV 1 +#define HAVE_PIPE 1 +#define HAVE_POPEN 1 +#define HAVE_SYSTEM 1 +#define HAVE_CWAIT 1 +#define HAVE_FSYNC 1 +#define fsync _commit +#else +#if defined(PYOS_OS2) && defined(PYCC_GCC) || defined(__VMS) +/* Everything needed is defined in PC/os2emx/pyconfig.h or vms/pyconfig.h */ +#else /* all other compilers */ +/* Unix functions that the configure script doesn't check for */ +#define HAVE_EXECV 1 +#define HAVE_FORK 1 +#if defined(__USLC__) && defined(__SCO_VERSION__) /* SCO UDK Compiler */ +#define HAVE_FORK1 1 +#endif +#define HAVE_GETCWD 1 +#define HAVE_GETEGID 1 +#define HAVE_GETEUID 1 +#define HAVE_GETGID 1 +#define HAVE_GETPPID 1 +#define HAVE_GETUID 1 +#define HAVE_KILL 1 +#define HAVE_OPENDIR 1 +#define HAVE_PIPE 1 +#ifndef __rtems__ +#define HAVE_POPEN 1 +#endif +#define HAVE_SYSTEM 1 +#define HAVE_WAIT 1 +#define HAVE_TTYNAME 1 +#endif /* PYOS_OS2 && PYCC_GCC && __VMS */ +#endif /* _MSC_VER */ +#endif /* __BORLANDC__ */ +#endif /* ! __WATCOMC__ || __QNX__ */ +#endif /* ! __IBMC__ */ + +#ifndef _MSC_VER + +#if defined(__sgi)&&_COMPILER_VERSION>=700 +/* declare ctermid_r if compiling with MIPSPro 7.x in ANSI C mode + (default) */ +extern char *ctermid_r(char *); +#endif + +#ifndef HAVE_UNISTD_H +#if defined(PYCC_VACPP) +extern int mkdir(char *); +#else +#if ( defined(__WATCOMC__) || defined(_MSC_VER) ) && !defined(__QNX__) +extern int mkdir(const char *); +#else +extern int mkdir(const char *, mode_t); +#endif +#endif +#if defined(__IBMC__) || defined(__IBMCPP__) +extern int chdir(char *); +extern int rmdir(char *); +#else +extern int chdir(const char *); +extern int rmdir(const char *); +#endif +#ifdef __BORLANDC__ +extern int chmod(const char *, int); +#else +extern int chmod(const char *, mode_t); +#endif +extern int chown(const char *, uid_t, gid_t); +extern char *getcwd(char *, int); +extern char *strerror(int); +extern int link(const char *, const char *); +extern int rename(const char *, const char *); +extern int stat(const char *, struct stat *); +extern int unlink(const char *); +extern int pclose(FILE *); +#ifdef HAVE_SYMLINK +extern int symlink(const char *, const char *); +#endif /* HAVE_SYMLINK */ +#ifdef HAVE_LSTAT +extern int lstat(const char *, struct stat *); +#endif /* HAVE_LSTAT */ +#endif /* !HAVE_UNISTD_H */ + +#endif /* !_MSC_VER */ + +#ifdef HAVE_UTIME_H +#include +#endif /* HAVE_UTIME_H */ + +#ifdef HAVE_SYS_UTIME_H +#include +#define HAVE_UTIME_H /* pretend we do for the rest of this file */ +#endif /* HAVE_SYS_UTIME_H */ + +#ifdef HAVE_SYS_TIMES_H +#include +#endif /* HAVE_SYS_TIMES_H */ + +#ifdef HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#ifdef HAVE_SYS_UTSNAME_H +#include +#endif /* HAVE_SYS_UTSNAME_H */ + +#ifdef HAVE_DIRENT_H +#include +#define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#if defined(__WATCOMC__) && !defined(__QNX__) +#include +#define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#define dirent direct +#define NAMLEN(dirent) (dirent)->d_namlen +#endif +#ifdef HAVE_SYS_NDIR_H +#include +#endif +#ifdef HAVE_SYS_DIR_H +#include +#endif +#ifdef HAVE_NDIR_H +#include +#endif +#endif + +#ifdef _MSC_VER +#include +#include +#include +#include "osdefs.h" +#define _WIN32_WINNT 0x0400 /* Needed for CryptoAPI on some systems */ +#include +#include /* for ShellExecute() */ +#define popen _popen +#define pclose _pclose +#endif /* _MSC_VER */ + +#if defined(PYCC_VACPP) && defined(PYOS_OS2) +#include +#endif /* OS2 */ + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif /* MAXPATHLEN */ + +#ifdef UNION_WAIT +/* Emulate some macros on systems that have a union instead of macros */ + +#ifndef WIFEXITED +#define WIFEXITED(u_wait) (!(u_wait).w_termsig && !(u_wait).w_coredump) +#endif + +#ifndef WEXITSTATUS +#define WEXITSTATUS(u_wait) (WIFEXITED(u_wait)?((u_wait).w_retcode):-1) +#endif + +#ifndef WTERMSIG +#define WTERMSIG(u_wait) ((u_wait).w_termsig) +#endif + +#endif /* UNION_WAIT */ + +/* Don't use the "_r" form if we don't need it (also, won't have a + prototype for it, at least on Solaris -- maybe others as well?). */ +#if defined(HAVE_CTERMID_R) && defined(WITH_THREAD) +#define USE_CTERMID_R +#endif + +#if defined(HAVE_TMPNAM_R) && defined(WITH_THREAD) +#define USE_TMPNAM_R +#endif + +/* choose the appropriate stat and fstat functions and return structs */ +#undef STAT +#if defined(MS_WIN64) || defined(MS_WINDOWS) +# define STAT win32_stat +# define FSTAT win32_fstat +# define STRUCT_STAT struct win32_stat +#else +# define STAT stat +# define FSTAT fstat +# define STRUCT_STAT struct stat +#endif + +#if defined(MAJOR_IN_MKDEV) +#include +#else +#if defined(MAJOR_IN_SYSMACROS) +#include +#endif +#if defined(HAVE_MKNOD) && defined(HAVE_SYS_MKDEV_H) +#include +#endif +#endif + +/* Return a dictionary corresponding to the POSIX environment table */ +#ifdef WITH_NEXT_FRAMEWORK +/* On Darwin/MacOSX a shared library or framework has no access to +** environ directly, we must obtain it with _NSGetEnviron(). +*/ +#include +static char **environ; +#elif !defined(_MSC_VER) && ( !defined(__WATCOMC__) || defined(__QNX__) ) +extern char **environ; +#endif /* !_MSC_VER */ + +static PyObject * +convertenviron(void) +{ + PyObject *d; + char **e; + d = PyDict_New(); + if (d == NULL) + return NULL; +#ifdef WITH_NEXT_FRAMEWORK + if (environ == NULL) + environ = *_NSGetEnviron(); +#endif + if (environ == NULL) + return d; + /* This part ignores errors */ + for (e = environ; *e != NULL; e++) { + PyObject *k; + PyObject *v; + char *p = strchr(*e, '='); + if (p == NULL) + continue; + k = PyString_FromStringAndSize(*e, (int)(p-*e)); + if (k == NULL) { + PyErr_Clear(); + continue; + } + v = PyString_FromString(p+1); + if (v == NULL) { + PyErr_Clear(); + Py_DECREF(k); + continue; + } + if (PyDict_GetItem(d, k) == NULL) { + if (PyDict_SetItem(d, k, v) != 0) + PyErr_Clear(); + } + Py_DECREF(k); + Py_DECREF(v); + } +#if defined(PYOS_OS2) + { + APIRET rc; + char buffer[1024]; /* OS/2 Provides a Documented Max of 1024 Chars */ + + rc = DosQueryExtLIBPATH(buffer, BEGIN_LIBPATH); + if (rc == NO_ERROR) { /* (not a type, envname is NOT 'BEGIN_LIBPATH') */ + PyObject *v = PyString_FromString(buffer); + PyDict_SetItemString(d, "BEGINLIBPATH", v); + Py_DECREF(v); + } + rc = DosQueryExtLIBPATH(buffer, END_LIBPATH); + if (rc == NO_ERROR) { /* (not a typo, envname is NOT 'END_LIBPATH') */ + PyObject *v = PyString_FromString(buffer); + PyDict_SetItemString(d, "ENDLIBPATH", v); + Py_DECREF(v); + } + } +#endif + return d; +} + + +/* Set a POSIX-specific error from errno, and return NULL */ + +static PyObject * +posix_error(void) +{ + return PyErr_SetFromErrno(PyExc_OSError); +} +static PyObject * +posix_error_with_filename(char* name) +{ + return PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); +} + +#ifdef Py_WIN_WIDE_FILENAMES +static PyObject * +posix_error_with_unicode_filename(Py_UNICODE* name) +{ + return PyErr_SetFromErrnoWithUnicodeFilename(PyExc_OSError, name); +} +#endif /* Py_WIN_WIDE_FILENAMES */ + + +static PyObject * +posix_error_with_allocated_filename(char* name) +{ + PyObject *rc = PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); + PyMem_Free(name); + return rc; +} + +#ifdef MS_WINDOWS +static PyObject * +win32_error(char* function, char* filename) +{ + /* XXX We should pass the function name along in the future. + (_winreg.c also wants to pass the function name.) + This would however require an additional param to the + Windows error object, which is non-trivial. + */ + errno = GetLastError(); + if (filename) + return PyErr_SetFromWindowsErrWithFilename(errno, filename); + else + return PyErr_SetFromWindowsErr(errno); +} + +#ifdef Py_WIN_WIDE_FILENAMES +static PyObject * +win32_error_unicode(char* function, Py_UNICODE* filename) +{ + /* XXX - see win32_error for comments on 'function' */ + errno = GetLastError(); + if (filename) + return PyErr_SetFromWindowsErrWithUnicodeFilename(errno, filename); + else + return PyErr_SetFromWindowsErr(errno); +} + +static PyObject *_PyUnicode_FromFileSystemEncodedObject(register PyObject *obj) +{ + /* XXX Perhaps we should make this API an alias of + PyObject_Unicode() instead ?! */ + if (PyUnicode_CheckExact(obj)) { + Py_INCREF(obj); + return obj; + } + if (PyUnicode_Check(obj)) { + /* For a Unicode subtype that's not a Unicode object, + return a true Unicode object with the same data. */ + return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(obj), + PyUnicode_GET_SIZE(obj)); + } + return PyUnicode_FromEncodedObject(obj, + Py_FileSystemDefaultEncoding, + "strict"); +} + +#endif /* Py_WIN_WIDE_FILENAMES */ + +#endif + +#if defined(PYOS_OS2) +/********************************************************************** + * Helper Function to Trim and Format OS/2 Messages + **********************************************************************/ + static void +os2_formatmsg(char *msgbuf, int msglen, char *reason) +{ + msgbuf[msglen] = '\0'; /* OS/2 Doesn't Guarantee a Terminator */ + + if (strlen(msgbuf) > 0) { /* If Non-Empty Msg, Trim CRLF */ + char *lastc = &msgbuf[ strlen(msgbuf)-1 ]; + + while (lastc > msgbuf && isspace(Py_CHARMASK(*lastc))) + *lastc-- = '\0'; /* Trim Trailing Whitespace (CRLF) */ + } + + /* Add Optional Reason Text */ + if (reason) { + strcat(msgbuf, " : "); + strcat(msgbuf, reason); + } +} + +/********************************************************************** + * Decode an OS/2 Operating System Error Code + * + * A convenience function to lookup an OS/2 error code and return a + * text message we can use to raise a Python exception. + * + * Notes: + * The messages for errors returned from the OS/2 kernel reside in + * the file OSO001.MSG in the \OS2 directory hierarchy. + * + **********************************************************************/ + static char * +os2_strerror(char *msgbuf, int msgbuflen, int errorcode, char *reason) +{ + APIRET rc; + ULONG msglen; + + /* Retrieve Kernel-Related Error Message from OSO001.MSG File */ + Py_BEGIN_ALLOW_THREADS + rc = DosGetMessage(NULL, 0, msgbuf, msgbuflen, + errorcode, "oso001.msg", &msglen); + Py_END_ALLOW_THREADS + + if (rc == NO_ERROR) + os2_formatmsg(msgbuf, msglen, reason); + else + PyOS_snprintf(msgbuf, msgbuflen, + "unknown OS error #%d", errorcode); + + return msgbuf; +} + +/* Set an OS/2-specific error and return NULL. OS/2 kernel + errors are not in a global variable e.g. 'errno' nor are + they congruent with posix error numbers. */ + +static PyObject * os2_error(int code) +{ + char text[1024]; + PyObject *v; + + os2_strerror(text, sizeof(text), code, ""); + + v = Py_BuildValue("(is)", code, text); + if (v != NULL) { + PyErr_SetObject(PyExc_OSError, v); + Py_DECREF(v); + } + return NULL; /* Signal to Python that an Exception is Pending */ +} + +#endif /* OS2 */ + +/* POSIX generic methods */ + +static PyObject * +posix_fildes(PyObject *fdobj, int (*func)(int)) +{ + int fd; + int res; + fd = PyObject_AsFileDescriptor(fdobj); + if (fd < 0) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = (*func)(fd); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef Py_WIN_WIDE_FILENAMES +static int +unicode_file_names(void) +{ + static int canusewide = -1; + if (canusewide == -1) { + /* As per doc for ::GetVersion(), this is the correct test for + the Windows NT family. */ + canusewide = (GetVersion() < 0x80000000) ? 1 : 0; + } + return canusewide; +} +#endif + +static PyObject * +posix_1str(PyObject *args, char *format, int (*func)(const char*), + char *wformat, int (*wfunc)(const Py_UNICODE*)) +{ + char *path1 = NULL; + int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, wformat, &po)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + res = (*wfunc)(PyUnicode_AS_UNICODE(po)); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_unicode_filename(PyUnicode_AS_UNICODE(po)); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow + strings are also valid. */ + PyErr_Clear(); + } +#else + /* Platforms that don't support Unicode filenames + shouldn't be passing these extra params */ + assert(wformat==NULL && wfunc == NULL); +#endif + + if (!PyArg_ParseTuple(args, format, + Py_FileSystemDefaultEncoding, &path1)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = (*func)(path1); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path1); + PyMem_Free(path1); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +posix_2str(PyObject *args, + char *format, + int (*func)(const char *, const char *), + char *wformat, + int (*wfunc)(const Py_UNICODE *, const Py_UNICODE *)) +{ + char *path1 = NULL, *path2 = NULL; + int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyObject *po1; + PyObject *po2; + if (PyArg_ParseTuple(args, wformat, &po1, &po2)) { + if (PyUnicode_Check(po1) || PyUnicode_Check(po2)) { + PyObject *wpath1; + PyObject *wpath2; + wpath1 = _PyUnicode_FromFileSystemEncodedObject(po1); + wpath2 = _PyUnicode_FromFileSystemEncodedObject(po2); + if (!wpath1 || !wpath2) { + Py_XDECREF(wpath1); + Py_XDECREF(wpath2); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + res = (*wfunc)(PyUnicode_AS_UNICODE(wpath1), + PyUnicode_AS_UNICODE(wpath2)); + Py_END_ALLOW_THREADS + Py_XDECREF(wpath1); + Py_XDECREF(wpath2); + if (res != 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; + } + /* Else flow through as neither is Unicode. */ + } + /* Drop the argument parsing error as narrow + strings are also valid. */ + PyErr_Clear(); + } +#else + /* Platforms that don't support Unicode filenames + shouldn't be passing these extra params */ + assert(wformat==NULL && wfunc == NULL); +#endif + + if (!PyArg_ParseTuple(args, format, + Py_FileSystemDefaultEncoding, &path1, + Py_FileSystemDefaultEncoding, &path2)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = (*func)(path1, path2); + Py_END_ALLOW_THREADS + PyMem_Free(path1); + PyMem_Free(path2); + if (res != 0) + /* XXX how to report both path1 and path2??? */ + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef MS_WINDOWS +/* The CRT of Windows has a number of flaws wrt. its stat() implementation: + - time stamps are restricted to second resolution + - file modification times suffer from forth-and-back conversions between + UTC and local time + Therefore, we implement our own stat, based on the Win32 API directly. +*/ +#define HAVE_STAT_NSEC 1 + +struct win32_stat{ + int st_dev; + __int64 st_ino; + unsigned short st_mode; + int st_nlink; + int st_uid; + int st_gid; + int st_rdev; + __int64 st_size; + int st_atime; + int st_atime_nsec; + int st_mtime; + int st_mtime_nsec; + int st_ctime; + int st_ctime_nsec; +}; + +static __int64 secs_between_epochs = 11644473600; /* Seconds between 1.1.1601 and 1.1.1970 */ + +static void +FILE_TIME_to_time_t_nsec(FILETIME *in_ptr, int *time_out, int* nsec_out) +{ + /* XXX endianness */ + __int64 in = *(__int64*)in_ptr; + *nsec_out = (int)(in % 10000000) * 100; /* FILETIME is in units of 100 nsec. */ + /* XXX Win32 supports time stamps past 2038; we currently don't */ + *time_out = Py_SAFE_DOWNCAST((in / 10000000) - secs_between_epochs, __int64, int); +} + +/* Below, we *know* that ugo+r is 0444 */ +#if _S_IREAD != 0400 +#error Unsupported C library +#endif +static int +attributes_to_mode(DWORD attr) +{ + int m = 0; + if (attr & FILE_ATTRIBUTE_DIRECTORY) + m |= _S_IFDIR | 0111; /* IFEXEC for user,group,other */ + else + m |= _S_IFREG; + if (attr & FILE_ATTRIBUTE_READONLY) + m |= 0444; + else + m |= 0666; + return m; +} + +static int +attribute_data_to_stat(WIN32_FILE_ATTRIBUTE_DATA *info, struct win32_stat *result) +{ + memset(result, 0, sizeof(*result)); + result->st_mode = attributes_to_mode(info->dwFileAttributes); + result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow; + FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_ctime, &result->st_ctime_nsec); + FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec); + FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec); + + return 0; +} + +static int +win32_stat(const char* path, struct win32_stat *result) +{ + WIN32_FILE_ATTRIBUTE_DATA info; + int code; + char *dot; + /* XXX not supported on Win95 and NT 3.x */ + if (!GetFileAttributesExA(path, GetFileExInfoStandard, &info)) { + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + errno = 0; + return -1; + } + code = attribute_data_to_stat(&info, result); + if (code != 0) + return code; + /* Set S_IFEXEC if it is an .exe, .bat, ... */ + dot = strrchr(path, '.'); + if (dot) { + if (stricmp(dot, ".bat") == 0 || + stricmp(dot, ".cmd") == 0 || + stricmp(dot, ".exe") == 0 || + stricmp(dot, ".com") == 0) + result->st_mode |= 0111; + } + return code; +} + +static int +win32_wstat(const wchar_t* path, struct win32_stat *result) +{ + int code; + const wchar_t *dot; + WIN32_FILE_ATTRIBUTE_DATA info; + /* XXX not supported on Win95 and NT 3.x */ + if (!GetFileAttributesExW(path, GetFileExInfoStandard, &info)) { + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + errno = 0; + return -1; + } + code = attribute_data_to_stat(&info, result); + if (code < 0) + return code; + /* Set IFEXEC if it is an .exe, .bat, ... */ + dot = wcsrchr(path, '.'); + if (dot) { + if (_wcsicmp(dot, L".bat") == 0 || + _wcsicmp(dot, L".cmd") == 0 || + _wcsicmp(dot, L".exe") == 0 || + _wcsicmp(dot, L".com") == 0) + result->st_mode |= 0111; + } + return code; +} + +static int +win32_fstat(int file_number, struct win32_stat *result) +{ + BY_HANDLE_FILE_INFORMATION info; + HANDLE h; + int type; + + h = (HANDLE)_get_osfhandle(file_number); + + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + errno = 0; + + if (h == INVALID_HANDLE_VALUE) { + /* This is really a C library error (invalid file handle). + We set the Win32 error to the closes one matching. */ + SetLastError(ERROR_INVALID_HANDLE); + return -1; + } + memset(result, 0, sizeof(*result)); + + type = GetFileType(h); + if (type == FILE_TYPE_UNKNOWN) { + DWORD error = GetLastError(); + if (error != 0) { + return -1; + } + /* else: valid but unknown file */ + } + + if (type != FILE_TYPE_DISK) { + if (type == FILE_TYPE_CHAR) + result->st_mode = _S_IFCHR; + else if (type == FILE_TYPE_PIPE) + result->st_mode = _S_IFIFO; + return 0; + } + + if (!GetFileInformationByHandle(h, &info)) { + return -1; + } + + /* similar to stat() */ + result->st_mode = attributes_to_mode(info.dwFileAttributes); + result->st_size = (((__int64)info.nFileSizeHigh)<<32) + info.nFileSizeLow; + FILE_TIME_to_time_t_nsec(&info.ftCreationTime, &result->st_ctime, &result->st_ctime_nsec); + FILE_TIME_to_time_t_nsec(&info.ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec); + FILE_TIME_to_time_t_nsec(&info.ftLastAccessTime, &result->st_atime, &result->st_atime_nsec); + /* specific to fstat() */ + result->st_nlink = info.nNumberOfLinks; + result->st_ino = (((__int64)info.nFileIndexHigh)<<32) + info.nFileIndexLow; + return 0; +} + +#endif /* MS_WINDOWS */ + +PyDoc_STRVAR(stat_result__doc__, +"stat_result: Result from stat or lstat.\n\n\ +This object may be accessed either as a tuple of\n\ + (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\ +or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\ +\n\ +Posix/windows: If your platform supports st_blksize, st_blocks, st_rdev,\n\ +or st_flags, they are available as attributes only.\n\ +\n\ +See os.stat for more information."); + +static PyStructSequence_Field stat_result_fields[] = { + {"st_mode", "protection bits"}, + {"st_ino", "inode"}, + {"st_dev", "device"}, + {"st_nlink", "number of hard links"}, + {"st_uid", "user ID of owner"}, + {"st_gid", "group ID of owner"}, + {"st_size", "total size, in bytes"}, + /* The NULL is replaced with PyStructSequence_UnnamedField later. */ + {NULL, "integer time of last access"}, + {NULL, "integer time of last modification"}, + {NULL, "integer time of last change"}, + {"st_atime", "time of last access"}, + {"st_mtime", "time of last modification"}, + {"st_ctime", "time of last change"}, +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + {"st_blksize", "blocksize for filesystem I/O"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + {"st_blocks", "number of blocks allocated"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + {"st_rdev", "device type (if inode device)"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_FLAGS + {"st_flags", "user defined flags for file"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_GEN + {"st_gen", "generation number"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME + {"st_birthtime", "time of creation"}, +#endif + {0} +}; + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE +#define ST_BLKSIZE_IDX 13 +#else +#define ST_BLKSIZE_IDX 12 +#endif + +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS +#define ST_BLOCKS_IDX (ST_BLKSIZE_IDX+1) +#else +#define ST_BLOCKS_IDX ST_BLKSIZE_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_RDEV +#define ST_RDEV_IDX (ST_BLOCKS_IDX+1) +#else +#define ST_RDEV_IDX ST_BLOCKS_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_FLAGS +#define ST_FLAGS_IDX (ST_RDEV_IDX+1) +#else +#define ST_FLAGS_IDX ST_RDEV_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_GEN +#define ST_GEN_IDX (ST_FLAGS_IDX+1) +#else +#define ST_GEN_IDX ST_FLAGS_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME +#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1) +#else +#define ST_BIRTHTIME_IDX ST_GEN_IDX +#endif + +static PyStructSequence_Desc stat_result_desc = { + "stat_result", /* name */ + stat_result__doc__, /* doc */ + stat_result_fields, + 10 +}; + +PyDoc_STRVAR(statvfs_result__doc__, +"statvfs_result: Result from statvfs or fstatvfs.\n\n\ +This object may be accessed either as a tuple of\n\ + (bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flag, namemax),\n\ +or via the attributes f_bsize, f_frsize, f_blocks, f_bfree, and so on.\n\ +\n\ +See os.statvfs for more information."); + +static PyStructSequence_Field statvfs_result_fields[] = { + {"f_bsize", }, + {"f_frsize", }, + {"f_blocks", }, + {"f_bfree", }, + {"f_bavail", }, + {"f_files", }, + {"f_ffree", }, + {"f_favail", }, + {"f_flag", }, + {"f_namemax",}, + {0} +}; + +static PyStructSequence_Desc statvfs_result_desc = { + "statvfs_result", /* name */ + statvfs_result__doc__, /* doc */ + statvfs_result_fields, + 10 +}; + +static PyTypeObject StatResultType; +static PyTypeObject StatVFSResultType; +static newfunc structseq_new; + +static PyObject * +statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyStructSequence *result; + int i; + + result = (PyStructSequence*)structseq_new(type, args, kwds); + if (!result) + return NULL; + /* If we have been initialized from a tuple, + st_?time might be set to None. Initialize it + from the int slots. */ + for (i = 7; i <= 9; i++) { + if (result->ob_item[i+3] == Py_None) { + Py_DECREF(Py_None); + Py_INCREF(result->ob_item[i]); + result->ob_item[i+3] = result->ob_item[i]; + } + } + return (PyObject*)result; +} + + + +/* If true, st_?time is float. */ +static int _stat_float_times = 1; + +PyDoc_STRVAR(stat_float_times__doc__, +"stat_float_times([newval]) -> oldval\n\n\ +Determine whether os.[lf]stat represents time stamps as float objects.\n\ +If newval is True, future calls to stat() return floats, if it is False,\n\ +future calls return ints. \n\ +If newval is omitted, return the current setting.\n"); + +static PyObject* +stat_float_times(PyObject* self, PyObject *args) +{ + int newval = -1; + if (!PyArg_ParseTuple(args, "|i:stat_float_times", &newval)) + return NULL; + if (newval == -1) + /* Return old value */ + return PyBool_FromLong(_stat_float_times); + _stat_float_times = newval; + Py_INCREF(Py_None); + return Py_None; +} + +static void +fill_time(PyObject *v, int index, time_t sec, unsigned long nsec) +{ + PyObject *fval,*ival; +#if SIZEOF_TIME_T > SIZEOF_LONG + ival = PyLong_FromLongLong((PY_LONG_LONG)sec); +#else + ival = PyInt_FromLong((long)sec); +#endif + if (_stat_float_times) { + fval = PyFloat_FromDouble(sec + 1e-9*nsec); + } else { + fval = ival; + Py_INCREF(fval); + } + PyStructSequence_SET_ITEM(v, index, ival); + PyStructSequence_SET_ITEM(v, index+3, fval); +} + +/* pack a system stat C structure into the Python stat tuple + (used by posix_stat() and posix_fstat()) */ +static PyObject* +_pystat_fromstructstat(STRUCT_STAT *st) +{ + unsigned long ansec, mnsec, cnsec; + PyObject *v = PyStructSequence_New(&StatResultType); + if (v == NULL) + return NULL; + + PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long)st->st_mode)); +#ifdef HAVE_LARGEFILE_SUPPORT + PyStructSequence_SET_ITEM(v, 1, + PyLong_FromLongLong((PY_LONG_LONG)st->st_ino)); +#else + PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long)st->st_ino)); +#endif +#if defined(HAVE_LONG_LONG) && !defined(MS_WINDOWS) + PyStructSequence_SET_ITEM(v, 2, + PyLong_FromLongLong((PY_LONG_LONG)st->st_dev)); +#else + PyStructSequence_SET_ITEM(v, 2, PyInt_FromLong((long)st->st_dev)); +#endif + PyStructSequence_SET_ITEM(v, 3, PyInt_FromLong((long)st->st_nlink)); + PyStructSequence_SET_ITEM(v, 4, PyInt_FromLong((long)st->st_uid)); + PyStructSequence_SET_ITEM(v, 5, PyInt_FromLong((long)st->st_gid)); +#ifdef HAVE_LARGEFILE_SUPPORT + PyStructSequence_SET_ITEM(v, 6, + PyLong_FromLongLong((PY_LONG_LONG)st->st_size)); +#else + PyStructSequence_SET_ITEM(v, 6, PyInt_FromLong(st->st_size)); +#endif + +#if defined(HAVE_STAT_TV_NSEC) + ansec = st->st_atim.tv_nsec; + mnsec = st->st_mtim.tv_nsec; + cnsec = st->st_ctim.tv_nsec; +#elif defined(HAVE_STAT_TV_NSEC2) + ansec = st->st_atimespec.tv_nsec; + mnsec = st->st_mtimespec.tv_nsec; + cnsec = st->st_ctimespec.tv_nsec; +#elif defined(HAVE_STAT_NSEC) + ansec = st->st_atime_nsec; + mnsec = st->st_mtime_nsec; + cnsec = st->st_ctime_nsec; +#else + ansec = mnsec = cnsec = 0; +#endif + fill_time(v, 7, st->st_atime, ansec); + fill_time(v, 8, st->st_mtime, mnsec); + fill_time(v, 9, st->st_ctime, cnsec); + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX, + PyInt_FromLong((long)st->st_blksize)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + PyStructSequence_SET_ITEM(v, ST_BLOCKS_IDX, + PyInt_FromLong((long)st->st_blocks)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + PyStructSequence_SET_ITEM(v, ST_RDEV_IDX, + PyInt_FromLong((long)st->st_rdev)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_GEN + PyStructSequence_SET_ITEM(v, ST_GEN_IDX, + PyInt_FromLong((long)st->st_gen)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME + { + PyObject *val; + unsigned long bsec,bnsec; + bsec = (long)st->st_birthtime; +#ifdef HAVE_STAT_TV_NSEC2 + bnsec = st->st_birthtimespec.tv_nsec; +#else + bnsec = 0; +#endif + if (_stat_float_times) { + val = PyFloat_FromDouble(bsec + 1e-9*bnsec); + } else { + val = PyInt_FromLong((long)bsec); + } + PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX, + val); + } +#endif +#ifdef HAVE_STRUCT_STAT_ST_FLAGS + PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX, + PyInt_FromLong((long)st->st_flags)); +#endif + + if (PyErr_Occurred()) { + Py_DECREF(v); + return NULL; + } + + return v; +} + +#ifdef MS_WINDOWS + +/* IsUNCRoot -- test whether the supplied path is of the form \\SERVER\SHARE\, + where / can be used in place of \ and the trailing slash is optional. + Both SERVER and SHARE must have at least one character. +*/ + +#define ISSLASHA(c) ((c) == '\\' || (c) == '/') +#define ISSLASHW(c) ((c) == L'\\' || (c) == L'/') +#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0])) + +static BOOL +IsUNCRootA(char *path, int pathlen) +{ + #define ISSLASH ISSLASHA + + int i, share; + + if (pathlen < 5 || !ISSLASH(path[0]) || !ISSLASH(path[1])) + /* minimum UNCRoot is \\x\y */ + return FALSE; + for (i = 2; i < pathlen ; i++) + if (ISSLASH(path[i])) break; + if (i == 2 || i == pathlen) + /* do not allow \\\SHARE or \\SERVER */ + return FALSE; + share = i+1; + for (i = share; i < pathlen; i++) + if (ISSLASH(path[i])) break; + return (i != share && (i == pathlen || i == pathlen-1)); + + #undef ISSLASH +} + +#ifdef Py_WIN_WIDE_FILENAMES +static BOOL +IsUNCRootW(Py_UNICODE *path, int pathlen) +{ + #define ISSLASH ISSLASHW + + int i, share; + + if (pathlen < 5 || !ISSLASH(path[0]) || !ISSLASH(path[1])) + /* minimum UNCRoot is \\x\y */ + return FALSE; + for (i = 2; i < pathlen ; i++) + if (ISSLASH(path[i])) break; + if (i == 2 || i == pathlen) + /* do not allow \\\SHARE or \\SERVER */ + return FALSE; + share = i+1; + for (i = share; i < pathlen; i++) + if (ISSLASH(path[i])) break; + return (i != share && (i == pathlen || i == pathlen-1)); + + #undef ISSLASH +} +#endif /* Py_WIN_WIDE_FILENAMES */ +#endif /* MS_WINDOWS */ + +static PyObject * +posix_do_stat(PyObject *self, PyObject *args, + char *format, +#ifdef __VMS + int (*statfunc)(const char *, STRUCT_STAT *, ...), +#else + int (*statfunc)(const char *, STRUCT_STAT *), +#endif + char *wformat, + int (*wstatfunc)(const Py_UNICODE *, STRUCT_STAT *)) +{ + STRUCT_STAT st; + char *path = NULL; /* pass this to stat; do not free() it */ + char *pathfree = NULL; /* this memory must be free'd */ + int res; + PyObject *result; + +#ifdef Py_WIN_WIDE_FILENAMES + /* If on wide-character-capable OS see if argument + is Unicode and if so use wide API. */ + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, wformat, &po)) { + Py_UNICODE *wpath = PyUnicode_AS_UNICODE(po); + + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE result OK without + thread lock as it is a simple dereference. */ + res = wstatfunc(wpath, &st); + Py_END_ALLOW_THREADS + + if (res != 0) + return win32_error_unicode("stat", wpath); + return _pystat_fromstructstat(&st); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, format, + Py_FileSystemDefaultEncoding, &path)) + return NULL; + pathfree = path; + + Py_BEGIN_ALLOW_THREADS + res = (*statfunc)(path, &st); + Py_END_ALLOW_THREADS + + if (res != 0) { +#ifdef MS_WINDOWS + result = win32_error("stat", pathfree); +#else + result = posix_error_with_filename(pathfree); +#endif + } + else + result = _pystat_fromstructstat(&st); + + PyMem_Free(pathfree); + return result; +} + +/* POSIX methods */ + +PyDoc_STRVAR(posix_access__doc__, +"access(path, mode) -> 1 if granted, 0 otherwise\n\n\ +Use the real uid/gid to test for access to a path. Note that most\n\ +operations will use the effective uid/gid, therefore this routine can\n\ +be used in a suid/sgid environment to test if the invoking user has the\n\ +specified access to the path. The mode argument can be F_OK to test\n\ +existence, or the inclusive-OR of R_OK, W_OK, and X_OK."); + +static PyObject * +posix_access(PyObject *self, PyObject *args) +{ + char *path; + int mode; + int res; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "Ui:access", &po, &mode)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread lock as + it is a simple dereference. */ + res = _waccess(PyUnicode_AS_UNICODE(po), mode); + Py_END_ALLOW_THREADS + return PyBool_FromLong(res == 0); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + if (!PyArg_ParseTuple(args, "eti:access", + Py_FileSystemDefaultEncoding, &path, &mode)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = access(path, mode); + Py_END_ALLOW_THREADS + PyMem_Free(path); + return PyBool_FromLong(res == 0); +} + +#ifndef F_OK +#define F_OK 0 +#endif +#ifndef R_OK +#define R_OK 4 +#endif +#ifndef W_OK +#define W_OK 2 +#endif +#ifndef X_OK +#define X_OK 1 +#endif + +#ifdef HAVE_TTYNAME +PyDoc_STRVAR(posix_ttyname__doc__, +"ttyname(fd) -> string\n\n\ +Return the name of the terminal device connected to 'fd'."); + +static PyObject * +posix_ttyname(PyObject *self, PyObject *args) +{ + int id; + char *ret; + + if (!PyArg_ParseTuple(args, "i:ttyname", &id)) + return NULL; + +#if defined(__VMS) + /* file descriptor 0 only, the default input device (stdin) */ + if (id == 0) { + ret = ttyname(); + } + else { + ret = NULL; + } +#else + ret = ttyname(id); +#endif + if (ret == NULL) + return posix_error(); + return PyString_FromString(ret); +} +#endif + +#ifdef HAVE_CTERMID +PyDoc_STRVAR(posix_ctermid__doc__, +"ctermid() -> string\n\n\ +Return the name of the controlling terminal for this process."); + +static PyObject * +posix_ctermid(PyObject *self, PyObject *noargs) +{ + char *ret; + char buffer[L_ctermid]; + +#ifdef USE_CTERMID_R + ret = ctermid_r(buffer); +#else + ret = ctermid(buffer); +#endif + if (ret == NULL) + return posix_error(); + return PyString_FromString(buffer); +} +#endif + +PyDoc_STRVAR(posix_chdir__doc__, +"chdir(path)\n\n\ +Change the current working directory to the specified path."); + +static PyObject * +posix_chdir(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_1str(args, "et:chdir", chdir, "U:chdir", _wchdir); +#elif defined(PYOS_OS2) && defined(PYCC_GCC) + return posix_1str(args, "et:chdir", _chdir2, NULL, NULL); +#elif defined(__VMS) + return posix_1str(args, "et:chdir", (int (*)(const char *))chdir, + NULL, NULL); +#else + return posix_1str(args, "et:chdir", chdir, NULL, NULL); +#endif +} + +#ifdef HAVE_FCHDIR +PyDoc_STRVAR(posix_fchdir__doc__, +"fchdir(fildes)\n\n\ +Change to the directory of the given file descriptor. fildes must be\n\ +opened on a directory, not a file."); + +static PyObject * +posix_fchdir(PyObject *self, PyObject *fdobj) +{ + return posix_fildes(fdobj, fchdir); +} +#endif /* HAVE_FCHDIR */ + + +PyDoc_STRVAR(posix_chmod__doc__, +"chmod(path, mode)\n\n\ +Change the access permissions of a file."); + +static PyObject * +posix_chmod(PyObject *self, PyObject *args) +{ + char *path = NULL; + int i; + int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "Ui|:chmod", &po, &i)) { + Py_BEGIN_ALLOW_THREADS + res = _wchmod(PyUnicode_AS_UNICODE(po), i); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_unicode_filename( + PyUnicode_AS_UNICODE(po)); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif /* Py_WIN_WIDE_FILENAMES */ + if (!PyArg_ParseTuple(args, "eti:chmod", Py_FileSystemDefaultEncoding, + &path, &i)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = chmod(path, i); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} + + +#ifdef HAVE_CHROOT +PyDoc_STRVAR(posix_chroot__doc__, +"chroot(path)\n\n\ +Change root directory to path."); + +static PyObject * +posix_chroot(PyObject *self, PyObject *args) +{ + return posix_1str(args, "et:chroot", chroot, NULL, NULL); +} +#endif + +#ifdef HAVE_FSYNC +PyDoc_STRVAR(posix_fsync__doc__, +"fsync(fildes)\n\n\ +force write of file with filedescriptor to disk."); + +static PyObject * +posix_fsync(PyObject *self, PyObject *fdobj) +{ + return posix_fildes(fdobj, fsync); +} +#endif /* HAVE_FSYNC */ + +#ifdef HAVE_FDATASYNC + +#ifdef __hpux +extern int fdatasync(int); /* On HP-UX, in libc but not in unistd.h */ +#endif + +PyDoc_STRVAR(posix_fdatasync__doc__, +"fdatasync(fildes)\n\n\ +force write of file with filedescriptor to disk.\n\ + does not force update of metadata."); + +static PyObject * +posix_fdatasync(PyObject *self, PyObject *fdobj) +{ + return posix_fildes(fdobj, fdatasync); +} +#endif /* HAVE_FDATASYNC */ + + +#ifdef HAVE_CHOWN +PyDoc_STRVAR(posix_chown__doc__, +"chown(path, uid, gid)\n\n\ +Change the owner and group id of path to the numeric uid and gid."); + +static PyObject * +posix_chown(PyObject *self, PyObject *args) +{ + char *path = NULL; + int uid, gid; + int res; + if (!PyArg_ParseTuple(args, "etii:chown", + Py_FileSystemDefaultEncoding, &path, + &uid, &gid)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = chown(path, (uid_t) uid, (gid_t) gid); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_CHOWN */ + +#ifdef HAVE_LCHOWN +PyDoc_STRVAR(posix_lchown__doc__, +"lchown(path, uid, gid)\n\n\ +Change the owner and group id of path to the numeric uid and gid.\n\ +This function will not follow symbolic links."); + +static PyObject * +posix_lchown(PyObject *self, PyObject *args) +{ + char *path = NULL; + int uid, gid; + int res; + if (!PyArg_ParseTuple(args, "etii:lchown", + Py_FileSystemDefaultEncoding, &path, + &uid, &gid)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = lchown(path, (uid_t) uid, (gid_t) gid); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_LCHOWN */ + + +#ifdef HAVE_GETCWD +PyDoc_STRVAR(posix_getcwd__doc__, +"getcwd() -> path\n\n\ +Return a string representing the current working directory."); + +static PyObject * +posix_getcwd(PyObject *self, PyObject *noargs) +{ + char buf[1026]; + char *res; + + Py_BEGIN_ALLOW_THREADS +#if defined(PYOS_OS2) && defined(PYCC_GCC) + res = _getcwd2(buf, sizeof buf); +#else + res = getcwd(buf, sizeof buf); +#endif + Py_END_ALLOW_THREADS + if (res == NULL) + return posix_error(); + return PyString_FromString(buf); +} + +#ifdef Py_USING_UNICODE +PyDoc_STRVAR(posix_getcwdu__doc__, +"getcwdu() -> path\n\n\ +Return a unicode string representing the current working directory."); + +static PyObject * +posix_getcwdu(PyObject *self, PyObject *noargs) +{ + char buf[1026]; + char *res; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + wchar_t *wres; + wchar_t wbuf[1026]; + Py_BEGIN_ALLOW_THREADS + wres = _wgetcwd(wbuf, sizeof wbuf/ sizeof wbuf[0]); + Py_END_ALLOW_THREADS + if (wres == NULL) + return posix_error(); + return PyUnicode_FromWideChar(wbuf, wcslen(wbuf)); + } +#endif + + Py_BEGIN_ALLOW_THREADS +#if defined(PYOS_OS2) && defined(PYCC_GCC) + res = _getcwd2(buf, sizeof buf); +#else + res = getcwd(buf, sizeof buf); +#endif + Py_END_ALLOW_THREADS + if (res == NULL) + return posix_error(); + return PyUnicode_Decode(buf, strlen(buf), Py_FileSystemDefaultEncoding,"strict"); +} +#endif +#endif + + +#ifdef HAVE_LINK +PyDoc_STRVAR(posix_link__doc__, +"link(src, dst)\n\n\ +Create a hard link to a file."); + +static PyObject * +posix_link(PyObject *self, PyObject *args) +{ + return posix_2str(args, "etet:link", link, NULL, NULL); +} +#endif /* HAVE_LINK */ + + +PyDoc_STRVAR(posix_listdir__doc__, +"listdir(path) -> list_of_strings\n\n\ +Return a list containing the names of the entries in the directory.\n\ +\n\ + path: path of directory to list\n\ +\n\ +The list is in arbitrary order. It does not include the special\n\ +entries '.' and '..' even if they are present in the directory."); + +static PyObject * +posix_listdir(PyObject *self, PyObject *args) +{ + /* XXX Should redo this putting the (now four) versions of opendir + in separate files instead of having them all here... */ +#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR) + + PyObject *d, *v; + HANDLE hFindFile; + BOOL result; + WIN32_FIND_DATA FileData; + /* MAX_PATH characters could mean a bigger encoded string */ + char namebuf[MAX_PATH*2+5]; + char *bufptr = namebuf; + Py_ssize_t len = sizeof(namebuf)/sizeof(namebuf[0]); + +#ifdef Py_WIN_WIDE_FILENAMES + /* If on wide-character-capable OS see if argument + is Unicode and if so use wide API. */ + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U:listdir", &po)) { + WIN32_FIND_DATAW wFileData; + Py_UNICODE wnamebuf[MAX_PATH*2+5]; + Py_UNICODE wch; + wcsncpy(wnamebuf, PyUnicode_AS_UNICODE(po), MAX_PATH); + wnamebuf[MAX_PATH] = L'\0'; + len = wcslen(wnamebuf); + wch = (len > 0) ? wnamebuf[len-1] : L'\0'; + if (wch != L'/' && wch != L'\\' && wch != L':') + wnamebuf[len++] = L'/'; + wcscpy(wnamebuf + len, L"*.*"); + if ((d = PyList_New(0)) == NULL) + return NULL; + hFindFile = FindFirstFileW(wnamebuf, &wFileData); + if (hFindFile == INVALID_HANDLE_VALUE) { + errno = GetLastError(); + if (errno == ERROR_FILE_NOT_FOUND) { + return d; + } + Py_DECREF(d); + return win32_error_unicode("FindFirstFileW", wnamebuf); + } + do { + if (wFileData.cFileName[0] == L'.' && + (wFileData.cFileName[1] == L'\0' || + wFileData.cFileName[1] == L'.' && + wFileData.cFileName[2] == L'\0')) + continue; + v = PyUnicode_FromUnicode(wFileData.cFileName, wcslen(wFileData.cFileName)); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + Py_BEGIN_ALLOW_THREADS + result = FindNextFileW(hFindFile, &wFileData); + Py_END_ALLOW_THREADS + } while (result == TRUE); + + if (FindClose(hFindFile) == FALSE) { + Py_DECREF(d); + return win32_error_unicode("FindClose", wnamebuf); + } + return d; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, "et#:listdir", + Py_FileSystemDefaultEncoding, &bufptr, &len)) + return NULL; + if (len > 0) { + char ch = namebuf[len-1]; + if (ch != SEP && ch != ALTSEP && ch != ':') + namebuf[len++] = '/'; + } + strcpy(namebuf + len, "*.*"); + + if ((d = PyList_New(0)) == NULL) + return NULL; + + hFindFile = FindFirstFile(namebuf, &FileData); + if (hFindFile == INVALID_HANDLE_VALUE) { + errno = GetLastError(); + if (errno == ERROR_FILE_NOT_FOUND) + return d; + Py_DECREF(d); + return win32_error("FindFirstFile", namebuf); + } + do { + if (FileData.cFileName[0] == '.' && + (FileData.cFileName[1] == '\0' || + FileData.cFileName[1] == '.' && + FileData.cFileName[2] == '\0')) + continue; + v = PyString_FromString(FileData.cFileName); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + Py_BEGIN_ALLOW_THREADS + result = FindNextFile(hFindFile, &FileData); + Py_END_ALLOW_THREADS + } while (result == TRUE); + + if (FindClose(hFindFile) == FALSE) { + Py_DECREF(d); + return win32_error("FindClose", namebuf); + } + + return d; + +#elif defined(PYOS_OS2) + +#ifndef MAX_PATH +#define MAX_PATH CCHMAXPATH +#endif + char *name, *pt; + Py_ssize_t len; + PyObject *d, *v; + char namebuf[MAX_PATH+5]; + HDIR hdir = 1; + ULONG srchcnt = 1; + FILEFINDBUF3 ep; + APIRET rc; + + if (!PyArg_ParseTuple(args, "t#:listdir", &name, &len)) + return NULL; + if (len >= MAX_PATH) { + PyErr_SetString(PyExc_ValueError, "path too long"); + return NULL; + } + strcpy(namebuf, name); + for (pt = namebuf; *pt; pt++) + if (*pt == ALTSEP) + *pt = SEP; + if (namebuf[len-1] != SEP) + namebuf[len++] = SEP; + strcpy(namebuf + len, "*.*"); + + if ((d = PyList_New(0)) == NULL) + return NULL; + + rc = DosFindFirst(namebuf, /* Wildcard Pattern to Match */ + &hdir, /* Handle to Use While Search Directory */ + FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_DIRECTORY, + &ep, sizeof(ep), /* Structure to Receive Directory Entry */ + &srchcnt, /* Max and Actual Count of Entries Per Iteration */ + FIL_STANDARD); /* Format of Entry (EAs or Not) */ + + if (rc != NO_ERROR) { + errno = ENOENT; + return posix_error_with_filename(name); + } + + if (srchcnt > 0) { /* If Directory is NOT Totally Empty, */ + do { + if (ep.achName[0] == '.' + && (ep.achName[1] == '\0' || (ep.achName[1] == '.' && ep.achName[2] == '\0'))) + continue; /* Skip Over "." and ".." Names */ + + strcpy(namebuf, ep.achName); + + /* Leave Case of Name Alone -- In Native Form */ + /* (Removed Forced Lowercasing Code) */ + + v = PyString_FromString(namebuf); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + } while (DosFindNext(hdir, &ep, sizeof(ep), &srchcnt) == NO_ERROR && srchcnt > 0); + } + + return d; +#else + + char *name = NULL; + PyObject *d, *v; + DIR *dirp; + struct dirent *ep; + int arg_is_unicode = 1; + + if (!PyArg_ParseTuple(args, "U:listdir", &v)) { + arg_is_unicode = 0; + PyErr_Clear(); + } + if (!PyArg_ParseTuple(args, "et:listdir", Py_FileSystemDefaultEncoding, &name)) + return NULL; + if ((dirp = opendir(name)) == NULL) { + return posix_error_with_allocated_filename(name); + } + if ((d = PyList_New(0)) == NULL) { + closedir(dirp); + PyMem_Free(name); + return NULL; + } + for (;;) { + Py_BEGIN_ALLOW_THREADS + ep = readdir(dirp); + Py_END_ALLOW_THREADS + if (ep == NULL) + break; + if (ep->d_name[0] == '.' && + (NAMLEN(ep) == 1 || + (ep->d_name[1] == '.' && NAMLEN(ep) == 2))) + continue; + v = PyString_FromStringAndSize(ep->d_name, NAMLEN(ep)); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } +#ifdef Py_USING_UNICODE + if (arg_is_unicode) { + PyObject *w; + + w = PyUnicode_FromEncodedObject(v, + Py_FileSystemDefaultEncoding, + "strict"); + if (w != NULL) { + Py_DECREF(v); + v = w; + } + else { + /* fall back to the original byte string, as + discussed in patch #683592 */ + PyErr_Clear(); + } + } +#endif + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + } + closedir(dirp); + PyMem_Free(name); + + return d; + +#endif /* which OS */ +} /* end of posix_listdir */ + +#ifdef MS_WINDOWS +/* A helper function for abspath on win32 */ +static PyObject * +posix__getfullpathname(PyObject *self, PyObject *args) +{ + /* assume encoded strings wont more than double no of chars */ + char inbuf[MAX_PATH*2]; + char *inbufp = inbuf; + Py_ssize_t insize = sizeof(inbuf); + char outbuf[MAX_PATH*2]; + char *temp; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U|:_getfullpathname", &po)) { + Py_UNICODE woutbuf[MAX_PATH*2]; + Py_UNICODE *wtemp; + if (!GetFullPathNameW(PyUnicode_AS_UNICODE(po), + sizeof(woutbuf)/sizeof(woutbuf[0]), + woutbuf, &wtemp)) + return win32_error("GetFullPathName", ""); + return PyUnicode_FromUnicode(woutbuf, wcslen(woutbuf)); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + if (!PyArg_ParseTuple (args, "et#:_getfullpathname", + Py_FileSystemDefaultEncoding, &inbufp, + &insize)) + return NULL; + if (!GetFullPathName(inbuf, sizeof(outbuf)/sizeof(outbuf[0]), + outbuf, &temp)) + return win32_error("GetFullPathName", inbuf); + if (PyUnicode_Check(PyTuple_GetItem(args, 0))) { + return PyUnicode_Decode(outbuf, strlen(outbuf), + Py_FileSystemDefaultEncoding, NULL); + } + return PyString_FromString(outbuf); +} /* end of posix__getfullpathname */ +#endif /* MS_WINDOWS */ + +PyDoc_STRVAR(posix_mkdir__doc__, +"mkdir(path [, mode=0777])\n\n\ +Create a directory."); + +static PyObject * +posix_mkdir(PyObject *self, PyObject *args) +{ + int res; + char *path = NULL; + int mode = 0777; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U|i:mkdir", &po, &mode)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread lock as + it is a simple dereference. */ + res = _wmkdir(PyUnicode_AS_UNICODE(po)); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, "et|i:mkdir", + Py_FileSystemDefaultEncoding, &path, &mode)) + return NULL; + Py_BEGIN_ALLOW_THREADS +#if ( defined(__WATCOMC__) || defined(_MSC_VER) || defined(PYCC_VACPP) ) && !defined(__QNX__) + res = mkdir(path); +#else + res = mkdir(path, mode); +#endif + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} + + +#ifdef HAVE_NICE +#if defined(HAVE_BROKEN_NICE) && defined(HAVE_SYS_RESOURCE_H) +#if defined(HAVE_GETPRIORITY) && !defined(PRIO_PROCESS) +#include +#endif +#endif + +PyDoc_STRVAR(posix_nice__doc__, +"nice(inc) -> new_priority\n\n\ +Decrease the priority of process by inc and return the new priority."); + +static PyObject * +posix_nice(PyObject *self, PyObject *args) +{ + int increment, value; + + if (!PyArg_ParseTuple(args, "i:nice", &increment)) + return NULL; + + /* There are two flavours of 'nice': one that returns the new + priority (as required by almost all standards out there) and the + Linux/FreeBSD/BSDI one, which returns '0' on success and advices + the use of getpriority() to get the new priority. + + If we are of the nice family that returns the new priority, we + need to clear errno before the call, and check if errno is filled + before calling posix_error() on a returnvalue of -1, because the + -1 may be the actual new priority! */ + + errno = 0; + value = nice(increment); +#if defined(HAVE_BROKEN_NICE) && defined(HAVE_GETPRIORITY) + if (value == 0) + value = getpriority(PRIO_PROCESS, 0); +#endif + if (value == -1 && errno != 0) + /* either nice() or getpriority() returned an error */ + return posix_error(); + return PyInt_FromLong((long) value); +} +#endif /* HAVE_NICE */ + + +PyDoc_STRVAR(posix_rename__doc__, +"rename(old, new)\n\n\ +Rename a file or directory."); + +static PyObject * +posix_rename(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_2str(args, "etet:rename", rename, "OO:rename", _wrename); +#else + return posix_2str(args, "etet:rename", rename, NULL, NULL); +#endif +} + + +PyDoc_STRVAR(posix_rmdir__doc__, +"rmdir(path)\n\n\ +Remove a directory."); + +static PyObject * +posix_rmdir(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_1str(args, "et:rmdir", rmdir, "U:rmdir", _wrmdir); +#else + return posix_1str(args, "et:rmdir", rmdir, NULL, NULL); +#endif +} + + +PyDoc_STRVAR(posix_stat__doc__, +"stat(path) -> stat result\n\n\ +Perform a stat system call on the given path."); + +static PyObject * +posix_stat(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_do_stat(self, args, "et:stat", STAT, "U:stat", win32_wstat); +#else + return posix_do_stat(self, args, "et:stat", STAT, NULL, NULL); +#endif +} + + +#ifdef HAVE_SYSTEM +PyDoc_STRVAR(posix_system__doc__, +"system(command) -> exit_status\n\n\ +Execute the command (a string) in a subshell."); + +static PyObject * +posix_system(PyObject *self, PyObject *args) +{ + char *command; + long sts; + if (!PyArg_ParseTuple(args, "s:system", &command)) + return NULL; + Py_BEGIN_ALLOW_THREADS + sts = system(command); + Py_END_ALLOW_THREADS + return PyInt_FromLong(sts); +} +#endif + + +PyDoc_STRVAR(posix_umask__doc__, +"umask(new_mask) -> old_mask\n\n\ +Set the current numeric umask and return the previous umask."); + +static PyObject * +posix_umask(PyObject *self, PyObject *args) +{ + int i; + if (!PyArg_ParseTuple(args, "i:umask", &i)) + return NULL; + i = (int)umask(i); + if (i < 0) + return posix_error(); + return PyInt_FromLong((long)i); +} + + +PyDoc_STRVAR(posix_unlink__doc__, +"unlink(path)\n\n\ +Remove a file (same as remove(path))."); + +PyDoc_STRVAR(posix_remove__doc__, +"remove(path)\n\n\ +Remove a file (same as unlink(path))."); + +static PyObject * +posix_unlink(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_1str(args, "et:remove", unlink, "U:remove", _wunlink); +#else + return posix_1str(args, "et:remove", unlink, NULL, NULL); +#endif +} + + +#ifdef HAVE_UNAME +PyDoc_STRVAR(posix_uname__doc__, +"uname() -> (sysname, nodename, release, version, machine)\n\n\ +Return a tuple identifying the current operating system."); + +static PyObject * +posix_uname(PyObject *self, PyObject *noargs) +{ + struct utsname u; + int res; + + Py_BEGIN_ALLOW_THREADS + res = uname(&u); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + return Py_BuildValue("(sssss)", + u.sysname, + u.nodename, + u.release, + u.version, + u.machine); +} +#endif /* HAVE_UNAME */ + +static int +extract_time(PyObject *t, long* sec, long* usec) +{ + long intval; + if (PyFloat_Check(t)) { + double tval = PyFloat_AsDouble(t); + PyObject *intobj = t->ob_type->tp_as_number->nb_int(t); + if (!intobj) + return -1; + intval = PyInt_AsLong(intobj); + Py_DECREF(intobj); + if (intval == -1 && PyErr_Occurred()) + return -1; + *sec = intval; + *usec = (long)((tval - intval) * 1e6); /* can't exceed 1000000 */ + if (*usec < 0) + /* If rounding gave us a negative number, + truncate. */ + *usec = 0; + return 0; + } + intval = PyInt_AsLong(t); + if (intval == -1 && PyErr_Occurred()) + return -1; + *sec = intval; + *usec = 0; + return 0; +} + +PyDoc_STRVAR(posix_utime__doc__, +"utime(path, (atime, utime))\n\ +utime(path, None)\n\n\ +Set the access and modified time of the file to the given values. If the\n\ +second form is used, set the access and modified times to the current time."); + +static PyObject * +posix_utime(PyObject *self, PyObject *args) +{ + char *path = NULL; + long atime, mtime, ausec, musec; + int res; + PyObject* arg; + +#if defined(HAVE_UTIMES) + struct timeval buf[2]; +#define ATIME buf[0].tv_sec +#define MTIME buf[1].tv_sec +#elif defined(HAVE_UTIME_H) +/* XXX should define struct utimbuf instead, above */ + struct utimbuf buf; +#define ATIME buf.actime +#define MTIME buf.modtime +#define UTIME_ARG &buf +#else /* HAVE_UTIMES */ + time_t buf[2]; +#define ATIME buf[0] +#define MTIME buf[1] +#define UTIME_ARG buf +#endif /* HAVE_UTIMES */ + + int have_unicode_filename = 0; +#ifdef Py_WIN_WIDE_FILENAMES + PyUnicodeObject *obwpath; + wchar_t *wpath; + if (unicode_file_names()) { + if (PyArg_ParseTuple(args, "UO|:utime", &obwpath, &arg)) { + wpath = PyUnicode_AS_UNICODE(obwpath); + have_unicode_filename = 1; + } else + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif /* Py_WIN_WIDE_FILENAMES */ + + if (!have_unicode_filename && \ + !PyArg_ParseTuple(args, "etO:utime", + Py_FileSystemDefaultEncoding, &path, &arg)) + return NULL; + if (arg == Py_None) { + /* optional time values not given */ + Py_BEGIN_ALLOW_THREADS +#ifdef Py_WIN_WIDE_FILENAMES + if (have_unicode_filename) + res = _wutime(wpath, NULL); + else +#endif /* Py_WIN_WIDE_FILENAMES */ + res = utime(path, NULL); + Py_END_ALLOW_THREADS + } + else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { + PyErr_SetString(PyExc_TypeError, + "utime() arg 2 must be a tuple (atime, mtime)"); + PyMem_Free(path); + return NULL; + } + else { + if (extract_time(PyTuple_GET_ITEM(arg, 0), + &atime, &ausec) == -1) { + PyMem_Free(path); + return NULL; + } + if (extract_time(PyTuple_GET_ITEM(arg, 1), + &mtime, &musec) == -1) { + PyMem_Free(path); + return NULL; + } + ATIME = atime; + MTIME = mtime; +#ifdef HAVE_UTIMES + buf[0].tv_usec = ausec; + buf[1].tv_usec = musec; + Py_BEGIN_ALLOW_THREADS + res = utimes(path, buf); + Py_END_ALLOW_THREADS +#else + Py_BEGIN_ALLOW_THREADS +#ifdef Py_WIN_WIDE_FILENAMES + if (have_unicode_filename) + /* utime is OK with utimbuf, but _wutime insists + on _utimbuf (the msvc headers assert the + underscore version is ansi) */ + res = _wutime(wpath, (struct _utimbuf *)UTIME_ARG); + else +#endif /* Py_WIN_WIDE_FILENAMES */ + res = utime(path, UTIME_ARG); + Py_END_ALLOW_THREADS +#endif /* HAVE_UTIMES */ + } + if (res < 0) { +#ifdef Py_WIN_WIDE_FILENAMES + if (have_unicode_filename) + return posix_error_with_unicode_filename(wpath); +#endif /* Py_WIN_WIDE_FILENAMES */ + return posix_error_with_allocated_filename(path); + } + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +#undef UTIME_ARG +#undef ATIME +#undef MTIME +} + + +/* Process operations */ + +PyDoc_STRVAR(posix__exit__doc__, +"_exit(status)\n\n\ +Exit to the system with specified status, without normal exit processing."); + +static PyObject * +posix__exit(PyObject *self, PyObject *args) +{ + int sts; + if (!PyArg_ParseTuple(args, "i:_exit", &sts)) + return NULL; + _exit(sts); + return NULL; /* Make gcc -Wall happy */ +} + +#if defined(HAVE_EXECV) || defined(HAVE_SPAWNV) +static void +free_string_array(char **array, Py_ssize_t count) +{ + Py_ssize_t i; + for (i = 0; i < count; i++) + PyMem_Free(array[i]); + PyMem_DEL(array); +} +#endif + + +#ifdef HAVE_EXECV +PyDoc_STRVAR(posix_execv__doc__, +"execv(path, args)\n\n\ +Execute an executable path with arguments, replacing current process.\n\ +\n\ + path: path of executable file\n\ + args: tuple or list of strings"); + +static PyObject * +posix_execv(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv; + char **argvlist; + Py_ssize_t i, argc; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + + /* execv has two arguments: (path, argv), where + argv is a list or tuple of strings. */ + + if (!PyArg_ParseTuple(args, "etO:execv", + Py_FileSystemDefaultEncoding, + &path, &argv)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, "execv() arg 2 must be a tuple or list"); + PyMem_Free(path); + return NULL; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyMem_Free(path); + return PyErr_NoMemory(); + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), "et", + Py_FileSystemDefaultEncoding, + &argvlist[i])) { + free_string_array(argvlist, i); + PyErr_SetString(PyExc_TypeError, + "execv() arg 2 must contain only strings"); + PyMem_Free(path); + return NULL; + + } + } + argvlist[argc] = NULL; + + execv(path, argvlist); + + /* If we get here it's definitely an error */ + + free_string_array(argvlist, argc); + PyMem_Free(path); + return posix_error(); +} + + +PyDoc_STRVAR(posix_execve__doc__, +"execve(path, args, env)\n\n\ +Execute a path with arguments and environment, replacing current process.\n\ +\n\ + path: path of executable file\n\ + args: tuple or list of arguments\n\ + env: dictionary of strings mapping to strings"); + +static PyObject * +posix_execve(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv, *env; + char **argvlist; + char **envlist; + PyObject *key, *val, *keys=NULL, *vals=NULL; + Py_ssize_t i, pos, argc, envc; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + int lastarg = 0; + + /* execve has three arguments: (path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (!PyArg_ParseTuple(args, "etOO:execve", + Py_FileSystemDefaultEncoding, + &path, &argv, &env)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "execve() arg 2 must be a tuple or list"); + goto fail_0; + } + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "execve() arg 3 must be a mapping object"); + goto fail_0; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + goto fail_0; + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), + "et;execve() arg 2 must contain only strings", + Py_FileSystemDefaultEncoding, + &argvlist[i])) + { + lastarg = i; + goto fail_1; + } + } + lastarg = argc; + argvlist[argc] = NULL; + + i = PyMapping_Size(env); + if (i < 0) + goto fail_1; + envlist = PyMem_NEW(char *, i + 1); + if (envlist == NULL) { + PyErr_NoMemory(); + goto fail_1; + } + envc = 0; + keys = PyMapping_Keys(env); + vals = PyMapping_Values(env); + if (!keys || !vals) + goto fail_2; + if (!PyList_Check(keys) || !PyList_Check(vals)) { + PyErr_SetString(PyExc_TypeError, + "execve(): env.keys() or env.values() is not a list"); + goto fail_2; + } + + for (pos = 0; pos < i; pos++) { + char *p, *k, *v; + size_t len; + + key = PyList_GetItem(keys, pos); + val = PyList_GetItem(vals, pos); + if (!key || !val) + goto fail_2; + + if (!PyArg_Parse( + key, + "s;execve() arg 3 contains a non-string key", + &k) || + !PyArg_Parse( + val, + "s;execve() arg 3 contains a non-string value", + &v)) + { + goto fail_2; + } + +#if defined(PYOS_OS2) + /* Omit Pseudo-Env Vars that Would Confuse Programs if Passed On */ + if (stricmp(k, "BEGINLIBPATH") != 0 && stricmp(k, "ENDLIBPATH") != 0) { +#endif + len = PyString_Size(key) + PyString_Size(val) + 2; + p = PyMem_NEW(char, len); + if (p == NULL) { + PyErr_NoMemory(); + goto fail_2; + } + PyOS_snprintf(p, len, "%s=%s", k, v); + envlist[envc++] = p; +#if defined(PYOS_OS2) + } +#endif + } + envlist[envc] = 0; + + execve(path, argvlist, envlist); + + /* If we get here it's definitely an error */ + + (void) posix_error(); + + fail_2: + while (--envc >= 0) + PyMem_DEL(envlist[envc]); + PyMem_DEL(envlist); + fail_1: + free_string_array(argvlist, lastarg); + Py_XDECREF(vals); + Py_XDECREF(keys); + fail_0: + PyMem_Free(path); + return NULL; +} +#endif /* HAVE_EXECV */ + + +#ifdef HAVE_SPAWNV +PyDoc_STRVAR(posix_spawnv__doc__, +"spawnv(mode, path, args)\n\n\ +Execute the program 'path' in a new process.\n\ +\n\ + mode: mode of process creation\n\ + path: path of executable file\n\ + args: tuple or list of strings"); + +static PyObject * +posix_spawnv(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv; + char **argvlist; + int mode, i, argc; + Py_intptr_t spawnval; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + + /* spawnv has three arguments: (mode, path, argv), where + argv is a list or tuple of strings. */ + + if (!PyArg_ParseTuple(args, "ietO:spawnv", &mode, + Py_FileSystemDefaultEncoding, + &path, &argv)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnv() arg 2 must be a tuple or list"); + PyMem_Free(path); + return NULL; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyMem_Free(path); + return PyErr_NoMemory(); + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), "et", + Py_FileSystemDefaultEncoding, + &argvlist[i])) { + free_string_array(argvlist, i); + PyErr_SetString( + PyExc_TypeError, + "spawnv() arg 2 must contain only strings"); + PyMem_Free(path); + return NULL; + } + } + argvlist[argc] = NULL; + +#if defined(PYOS_OS2) && defined(PYCC_GCC) + Py_BEGIN_ALLOW_THREADS + spawnval = spawnv(mode, path, argvlist); + Py_END_ALLOW_THREADS +#else + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; + + Py_BEGIN_ALLOW_THREADS + spawnval = _spawnv(mode, path, argvlist); + Py_END_ALLOW_THREADS +#endif + + free_string_array(argvlist, argc); + PyMem_Free(path); + + if (spawnval == -1) + return posix_error(); + else +#if SIZEOF_LONG == SIZEOF_VOID_P + return Py_BuildValue("l", (long) spawnval); +#else + return Py_BuildValue("L", (PY_LONG_LONG) spawnval); +#endif +} + + +PyDoc_STRVAR(posix_spawnve__doc__, +"spawnve(mode, path, args, env)\n\n\ +Execute the program 'path' in a new process.\n\ +\n\ + mode: mode of process creation\n\ + path: path of executable file\n\ + args: tuple or list of arguments\n\ + env: dictionary of strings mapping to strings"); + +static PyObject * +posix_spawnve(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv, *env; + char **argvlist; + char **envlist; + PyObject *key, *val, *keys=NULL, *vals=NULL, *res=NULL; + int mode, i, pos, argc, envc; + Py_intptr_t spawnval; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + int lastarg = 0; + + /* spawnve has four arguments: (mode, path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (!PyArg_ParseTuple(args, "ietOO:spawnve", &mode, + Py_FileSystemDefaultEncoding, + &path, &argv, &env)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnve() arg 2 must be a tuple or list"); + goto fail_0; + } + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "spawnve() arg 3 must be a mapping object"); + goto fail_0; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + goto fail_0; + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), + "et;spawnve() arg 2 must contain only strings", + Py_FileSystemDefaultEncoding, + &argvlist[i])) + { + lastarg = i; + goto fail_1; + } + } + lastarg = argc; + argvlist[argc] = NULL; + + i = PyMapping_Size(env); + if (i < 0) + goto fail_1; + envlist = PyMem_NEW(char *, i + 1); + if (envlist == NULL) { + PyErr_NoMemory(); + goto fail_1; + } + envc = 0; + keys = PyMapping_Keys(env); + vals = PyMapping_Values(env); + if (!keys || !vals) + goto fail_2; + if (!PyList_Check(keys) || !PyList_Check(vals)) { + PyErr_SetString(PyExc_TypeError, + "spawnve(): env.keys() or env.values() is not a list"); + goto fail_2; + } + + for (pos = 0; pos < i; pos++) { + char *p, *k, *v; + size_t len; + + key = PyList_GetItem(keys, pos); + val = PyList_GetItem(vals, pos); + if (!key || !val) + goto fail_2; + + if (!PyArg_Parse( + key, + "s;spawnve() arg 3 contains a non-string key", + &k) || + !PyArg_Parse( + val, + "s;spawnve() arg 3 contains a non-string value", + &v)) + { + goto fail_2; + } + len = PyString_Size(key) + PyString_Size(val) + 2; + p = PyMem_NEW(char, len); + if (p == NULL) { + PyErr_NoMemory(); + goto fail_2; + } + PyOS_snprintf(p, len, "%s=%s", k, v); + envlist[envc++] = p; + } + envlist[envc] = 0; + +#if defined(PYOS_OS2) && defined(PYCC_GCC) + Py_BEGIN_ALLOW_THREADS + spawnval = spawnve(mode, path, argvlist, envlist); + Py_END_ALLOW_THREADS +#else + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; + + Py_BEGIN_ALLOW_THREADS + spawnval = _spawnve(mode, path, argvlist, envlist); + Py_END_ALLOW_THREADS +#endif + + if (spawnval == -1) + (void) posix_error(); + else +#if SIZEOF_LONG == SIZEOF_VOID_P + res = Py_BuildValue("l", (long) spawnval); +#else + res = Py_BuildValue("L", (PY_LONG_LONG) spawnval); +#endif + + fail_2: + while (--envc >= 0) + PyMem_DEL(envlist[envc]); + PyMem_DEL(envlist); + fail_1: + free_string_array(argvlist, lastarg); + Py_XDECREF(vals); + Py_XDECREF(keys); + fail_0: + PyMem_Free(path); + return res; +} + +/* OS/2 supports spawnvp & spawnvpe natively */ +#if defined(PYOS_OS2) +PyDoc_STRVAR(posix_spawnvp__doc__, +"spawnvp(mode, file, args)\n\n\ +Execute the program 'file' in a new process, using the environment\n\ +search path to find the file.\n\ +\n\ + mode: mode of process creation\n\ + file: executable file name\n\ + args: tuple or list of strings"); + +static PyObject * +posix_spawnvp(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv; + char **argvlist; + int mode, i, argc; + Py_intptr_t spawnval; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + + /* spawnvp has three arguments: (mode, path, argv), where + argv is a list or tuple of strings. */ + + if (!PyArg_ParseTuple(args, "ietO:spawnvp", &mode, + Py_FileSystemDefaultEncoding, + &path, &argv)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnvp() arg 2 must be a tuple or list"); + PyMem_Free(path); + return NULL; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyMem_Free(path); + return PyErr_NoMemory(); + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), "et", + Py_FileSystemDefaultEncoding, + &argvlist[i])) { + free_string_array(argvlist, i); + PyErr_SetString( + PyExc_TypeError, + "spawnvp() arg 2 must contain only strings"); + PyMem_Free(path); + return NULL; + } + } + argvlist[argc] = NULL; + + Py_BEGIN_ALLOW_THREADS +#if defined(PYCC_GCC) + spawnval = spawnvp(mode, path, argvlist); +#else + spawnval = _spawnvp(mode, path, argvlist); +#endif + Py_END_ALLOW_THREADS + + free_string_array(argvlist, argc); + PyMem_Free(path); + + if (spawnval == -1) + return posix_error(); + else + return Py_BuildValue("l", (long) spawnval); +} + + +PyDoc_STRVAR(posix_spawnvpe__doc__, +"spawnvpe(mode, file, args, env)\n\n\ +Execute the program 'file' in a new process, using the environment\n\ +search path to find the file.\n\ +\n\ + mode: mode of process creation\n\ + file: executable file name\n\ + args: tuple or list of arguments\n\ + env: dictionary of strings mapping to strings"); + +static PyObject * +posix_spawnvpe(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv, *env; + char **argvlist; + char **envlist; + PyObject *key, *val, *keys=NULL, *vals=NULL, *res=NULL; + int mode, i, pos, argc, envc; + Py_intptr_t spawnval; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + int lastarg = 0; + + /* spawnvpe has four arguments: (mode, path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (!PyArg_ParseTuple(args, "ietOO:spawnvpe", &mode, + Py_FileSystemDefaultEncoding, + &path, &argv, &env)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnvpe() arg 2 must be a tuple or list"); + goto fail_0; + } + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "spawnvpe() arg 3 must be a mapping object"); + goto fail_0; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + goto fail_0; + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), + "et;spawnvpe() arg 2 must contain only strings", + Py_FileSystemDefaultEncoding, + &argvlist[i])) + { + lastarg = i; + goto fail_1; + } + } + lastarg = argc; + argvlist[argc] = NULL; + + i = PyMapping_Size(env); + if (i < 0) + goto fail_1; + envlist = PyMem_NEW(char *, i + 1); + if (envlist == NULL) { + PyErr_NoMemory(); + goto fail_1; + } + envc = 0; + keys = PyMapping_Keys(env); + vals = PyMapping_Values(env); + if (!keys || !vals) + goto fail_2; + if (!PyList_Check(keys) || !PyList_Check(vals)) { + PyErr_SetString(PyExc_TypeError, + "spawnvpe(): env.keys() or env.values() is not a list"); + goto fail_2; + } + + for (pos = 0; pos < i; pos++) { + char *p, *k, *v; + size_t len; + + key = PyList_GetItem(keys, pos); + val = PyList_GetItem(vals, pos); + if (!key || !val) + goto fail_2; + + if (!PyArg_Parse( + key, + "s;spawnvpe() arg 3 contains a non-string key", + &k) || + !PyArg_Parse( + val, + "s;spawnvpe() arg 3 contains a non-string value", + &v)) + { + goto fail_2; + } + len = PyString_Size(key) + PyString_Size(val) + 2; + p = PyMem_NEW(char, len); + if (p == NULL) { + PyErr_NoMemory(); + goto fail_2; + } + PyOS_snprintf(p, len, "%s=%s", k, v); + envlist[envc++] = p; + } + envlist[envc] = 0; + + Py_BEGIN_ALLOW_THREADS +#if defined(PYCC_GCC) + spawnval = spawnve(mode, path, argvlist, envlist); +#else + spawnval = _spawnve(mode, path, argvlist, envlist); +#endif + Py_END_ALLOW_THREADS + + if (spawnval == -1) + (void) posix_error(); + else + res = Py_BuildValue("l", (long) spawnval); + + fail_2: + while (--envc >= 0) + PyMem_DEL(envlist[envc]); + PyMem_DEL(envlist); + fail_1: + free_string_array(argvlist, lastarg); + Py_XDECREF(vals); + Py_XDECREF(keys); + fail_0: + PyMem_Free(path); + return res; +} +#endif /* PYOS_OS2 */ +#endif /* HAVE_SPAWNV */ + + +#ifdef HAVE_FORK1 +PyDoc_STRVAR(posix_fork1__doc__, +"fork1() -> pid\n\n\ +Fork a child process with a single multiplexed (i.e., not bound) thread.\n\ +\n\ +Return 0 to child process and PID of child to parent process."); + +static PyObject * +posix_fork1(PyObject *self, PyObject *noargs) +{ + int pid = fork1(); + if (pid == -1) + return posix_error(); + PyOS_AfterFork(); + return PyInt_FromLong((long)pid); +} +#endif + + +#ifdef HAVE_FORK +PyDoc_STRVAR(posix_fork__doc__, +"fork() -> pid\n\n\ +Fork a child process.\n\ +Return 0 to child process and PID of child to parent process."); + +static PyObject * +posix_fork(PyObject *self, PyObject *noargs) +{ + int pid = fork(); + if (pid == -1) + return posix_error(); + if (pid == 0) + PyOS_AfterFork(); + return PyInt_FromLong((long)pid); +} +#endif + +/* AIX uses /dev/ptc but is otherwise the same as /dev/ptmx */ +/* IRIX has both /dev/ptc and /dev/ptmx, use ptmx */ +#if defined(HAVE_DEV_PTC) && !defined(HAVE_DEV_PTMX) +#define DEV_PTY_FILE "/dev/ptc" +#define HAVE_DEV_PTMX +#else +#define DEV_PTY_FILE "/dev/ptmx" +#endif + +#if defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_DEV_PTMX) +#ifdef HAVE_PTY_H +#include +#else +#ifdef HAVE_LIBUTIL_H +#include +#endif /* HAVE_LIBUTIL_H */ +#endif /* HAVE_PTY_H */ +#ifdef HAVE_STROPTS_H +#include +#endif +#endif /* defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_DEV_PTMX */ + +#if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) +PyDoc_STRVAR(posix_openpty__doc__, +"openpty() -> (master_fd, slave_fd)\n\n\ +Open a pseudo-terminal, returning open fd's for both master and slave end.\n"); + +static PyObject * +posix_openpty(PyObject *self, PyObject *noargs) +{ + int master_fd, slave_fd; +#ifndef HAVE_OPENPTY + char * slave_name; +#endif +#if defined(HAVE_DEV_PTMX) && !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY) + PyOS_sighandler_t sig_saved; +#ifdef sun + extern char *ptsname(); +#endif +#endif + +#ifdef HAVE_OPENPTY + if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0) + return posix_error(); +#elif defined(HAVE__GETPTY) + slave_name = _getpty(&master_fd, O_RDWR, 0666, 0); + if (slave_name == NULL) + return posix_error(); + + slave_fd = open(slave_name, O_RDWR); + if (slave_fd < 0) + return posix_error(); +#else + master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */ + if (master_fd < 0) + return posix_error(); + sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL); + /* change permission of slave */ + if (grantpt(master_fd) < 0) { + PyOS_setsig(SIGCHLD, sig_saved); + return posix_error(); + } + /* unlock slave */ + if (unlockpt(master_fd) < 0) { + PyOS_setsig(SIGCHLD, sig_saved); + return posix_error(); + } + PyOS_setsig(SIGCHLD, sig_saved); + slave_name = ptsname(master_fd); /* get name of slave */ + if (slave_name == NULL) + return posix_error(); + slave_fd = open(slave_name, O_RDWR | O_NOCTTY); /* open slave */ + if (slave_fd < 0) + return posix_error(); +#if !defined(__CYGWIN__) && !defined(HAVE_DEV_PTC) + ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */ + ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */ +#ifndef __hpux + ioctl(slave_fd, I_PUSH, "ttcompat"); /* push ttcompat */ +#endif /* __hpux */ +#endif /* HAVE_CYGWIN */ +#endif /* HAVE_OPENPTY */ + + return Py_BuildValue("(ii)", master_fd, slave_fd); + +} +#endif /* defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) */ + +#ifdef HAVE_FORKPTY +PyDoc_STRVAR(posix_forkpty__doc__, +"forkpty() -> (pid, master_fd)\n\n\ +Fork a new process with a new pseudo-terminal as controlling tty.\n\n\ +Like fork(), return 0 as pid to child process, and PID of child to parent.\n\ +To both, return fd of newly opened pseudo-terminal.\n"); + +static PyObject * +posix_forkpty(PyObject *self, PyObject *noargs) +{ + int master_fd = -1, pid; + + pid = forkpty(&master_fd, NULL, NULL, NULL); + if (pid == -1) + return posix_error(); + if (pid == 0) + PyOS_AfterFork(); + return Py_BuildValue("(ii)", pid, master_fd); +} +#endif + +#ifdef HAVE_GETEGID +PyDoc_STRVAR(posix_getegid__doc__, +"getegid() -> egid\n\n\ +Return the current process's effective group id."); + +static PyObject * +posix_getegid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getegid()); +} +#endif + + +#ifdef HAVE_GETEUID +PyDoc_STRVAR(posix_geteuid__doc__, +"geteuid() -> euid\n\n\ +Return the current process's effective user id."); + +static PyObject * +posix_geteuid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)geteuid()); +} +#endif + + +#ifdef HAVE_GETGID +PyDoc_STRVAR(posix_getgid__doc__, +"getgid() -> gid\n\n\ +Return the current process's group id."); + +static PyObject * +posix_getgid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getgid()); +} +#endif + + +PyDoc_STRVAR(posix_getpid__doc__, +"getpid() -> pid\n\n\ +Return the current process id"); + +static PyObject * +posix_getpid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getpid()); +} + + +#ifdef HAVE_GETGROUPS +PyDoc_STRVAR(posix_getgroups__doc__, +"getgroups() -> list of group IDs\n\n\ +Return list of supplemental group IDs for the process."); + +static PyObject * +posix_getgroups(PyObject *self, PyObject *noargs) +{ + PyObject *result = NULL; + +#ifdef NGROUPS_MAX +#define MAX_GROUPS NGROUPS_MAX +#else + /* defined to be 16 on Solaris7, so this should be a small number */ +#define MAX_GROUPS 64 +#endif + gid_t grouplist[MAX_GROUPS]; + int n; + + n = getgroups(MAX_GROUPS, grouplist); + if (n < 0) + posix_error(); + else { + result = PyList_New(n); + if (result != NULL) { + int i; + for (i = 0; i < n; ++i) { + PyObject *o = PyInt_FromLong((long)grouplist[i]); + if (o == NULL) { + Py_DECREF(result); + result = NULL; + break; + } + PyList_SET_ITEM(result, i, o); + } + } + } + + return result; +} +#endif + +#ifdef HAVE_GETPGID +PyDoc_STRVAR(posix_getpgid__doc__, +"getpgid(pid) -> pgid\n\n\ +Call the system call getpgid()."); + +static PyObject * +posix_getpgid(PyObject *self, PyObject *args) +{ + int pid, pgid; + if (!PyArg_ParseTuple(args, "i:getpgid", &pid)) + return NULL; + pgid = getpgid(pid); + if (pgid < 0) + return posix_error(); + return PyInt_FromLong((long)pgid); +} +#endif /* HAVE_GETPGID */ + + +#ifdef HAVE_GETPGRP +PyDoc_STRVAR(posix_getpgrp__doc__, +"getpgrp() -> pgrp\n\n\ +Return the current process group id."); + +static PyObject * +posix_getpgrp(PyObject *self, PyObject *noargs) +{ +#ifdef GETPGRP_HAVE_ARG + return PyInt_FromLong((long)getpgrp(0)); +#else /* GETPGRP_HAVE_ARG */ + return PyInt_FromLong((long)getpgrp()); +#endif /* GETPGRP_HAVE_ARG */ +} +#endif /* HAVE_GETPGRP */ + + +#ifdef HAVE_SETPGRP +PyDoc_STRVAR(posix_setpgrp__doc__, +"setpgrp()\n\n\ +Make this process a session leader."); + +static PyObject * +posix_setpgrp(PyObject *self, PyObject *noargs) +{ +#ifdef SETPGRP_HAVE_ARG + if (setpgrp(0, 0) < 0) +#else /* SETPGRP_HAVE_ARG */ + if (setpgrp() < 0) +#endif /* SETPGRP_HAVE_ARG */ + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + +#endif /* HAVE_SETPGRP */ + +#ifdef HAVE_GETPPID +PyDoc_STRVAR(posix_getppid__doc__, +"getppid() -> ppid\n\n\ +Return the parent's process id."); + +static PyObject * +posix_getppid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getppid()); +} +#endif + + +#ifdef HAVE_GETLOGIN +PyDoc_STRVAR(posix_getlogin__doc__, +"getlogin() -> string\n\n\ +Return the actual login name."); + +static PyObject * +posix_getlogin(PyObject *self, PyObject *noargs) +{ + PyObject *result = NULL; + char *name; + int old_errno = errno; + + errno = 0; + name = getlogin(); + if (name == NULL) { + if (errno) + posix_error(); + else + PyErr_SetString(PyExc_OSError, + "unable to determine login name"); + } + else + result = PyString_FromString(name); + errno = old_errno; + + return result; +} +#endif + +#ifdef HAVE_GETUID +PyDoc_STRVAR(posix_getuid__doc__, +"getuid() -> uid\n\n\ +Return the current process's user id."); + +static PyObject * +posix_getuid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getuid()); +} +#endif + + +#ifdef HAVE_KILL +PyDoc_STRVAR(posix_kill__doc__, +"kill(pid, sig)\n\n\ +Kill a process with a signal."); + +static PyObject * +posix_kill(PyObject *self, PyObject *args) +{ + int pid, sig; + if (!PyArg_ParseTuple(args, "ii:kill", &pid, &sig)) + return NULL; +#if defined(PYOS_OS2) && !defined(PYCC_GCC) + if (sig == XCPT_SIGNAL_INTR || sig == XCPT_SIGNAL_BREAK) { + APIRET rc; + if ((rc = DosSendSignalException(pid, sig)) != NO_ERROR) + return os2_error(rc); + + } else if (sig == XCPT_SIGNAL_KILLPROC) { + APIRET rc; + if ((rc = DosKillProcess(DKP_PROCESS, pid)) != NO_ERROR) + return os2_error(rc); + + } else + return NULL; /* Unrecognized Signal Requested */ +#else + if (kill(pid, sig) == -1) + return posix_error(); +#endif + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_KILLPG +PyDoc_STRVAR(posix_killpg__doc__, +"killpg(pgid, sig)\n\n\ +Kill a process group with a signal."); + +static PyObject * +posix_killpg(PyObject *self, PyObject *args) +{ + int pgid, sig; + if (!PyArg_ParseTuple(args, "ii:killpg", &pgid, &sig)) + return NULL; + if (killpg(pgid, sig) == -1) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_PLOCK + +#ifdef HAVE_SYS_LOCK_H +#include +#endif + +PyDoc_STRVAR(posix_plock__doc__, +"plock(op)\n\n\ +Lock program segments into memory."); + +static PyObject * +posix_plock(PyObject *self, PyObject *args) +{ + int op; + if (!PyArg_ParseTuple(args, "i:plock", &op)) + return NULL; + if (plock(op) == -1) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + + +#ifdef HAVE_POPEN +PyDoc_STRVAR(posix_popen__doc__, +"popen(command [, mode='r' [, bufsize]]) -> pipe\n\n\ +Open a pipe to/from a command returning a file object."); + +#if defined(PYOS_OS2) +#if defined(PYCC_VACPP) +static int +async_system(const char *command) +{ + char errormsg[256], args[1024]; + RESULTCODES rcodes; + APIRET rc; + + char *shell = getenv("COMSPEC"); + if (!shell) + shell = "cmd"; + + /* avoid overflowing the argument buffer */ + if (strlen(shell) + 3 + strlen(command) >= 1024) + return ERROR_NOT_ENOUGH_MEMORY + + args[0] = '\0'; + strcat(args, shell); + strcat(args, "/c "); + strcat(args, command); + + /* execute asynchronously, inheriting the environment */ + rc = DosExecPgm(errormsg, + sizeof(errormsg), + EXEC_ASYNC, + args, + NULL, + &rcodes, + shell); + return rc; +} + +static FILE * +popen(const char *command, const char *mode, int pipesize, int *err) +{ + int oldfd, tgtfd; + HFILE pipeh[2]; + APIRET rc; + + /* mode determines which of stdin or stdout is reconnected to + * the pipe to the child + */ + if (strchr(mode, 'r') != NULL) { + tgt_fd = 1; /* stdout */ + } else if (strchr(mode, 'w')) { + tgt_fd = 0; /* stdin */ + } else { + *err = ERROR_INVALID_ACCESS; + return NULL; + } + + /* setup the pipe */ + if ((rc = DosCreatePipe(&pipeh[0], &pipeh[1], pipesize)) != NO_ERROR) { + *err = rc; + return NULL; + } + + /* prevent other threads accessing stdio */ + DosEnterCritSec(); + + /* reconnect stdio and execute child */ + oldfd = dup(tgtfd); + close(tgtfd); + if (dup2(pipeh[tgtfd], tgtfd) == 0) { + DosClose(pipeh[tgtfd]); + rc = async_system(command); + } + + /* restore stdio */ + dup2(oldfd, tgtfd); + close(oldfd); + + /* allow other threads access to stdio */ + DosExitCritSec(); + + /* if execution of child was successful return file stream */ + if (rc == NO_ERROR) + return fdopen(pipeh[1 - tgtfd], mode); + else { + DosClose(pipeh[1 - tgtfd]); + *err = rc; + return NULL; + } +} + +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + char *name; + char *mode = "r"; + int err, bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "s|si:popen", &name, &mode, &bufsize)) + return NULL; + Py_BEGIN_ALLOW_THREADS + fp = popen(name, mode, (bufsize > 0) ? bufsize : 4096, &err); + Py_END_ALLOW_THREADS + if (fp == NULL) + return os2_error(err); + + f = PyFile_FromFile(fp, name, mode, fclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +#elif defined(PYCC_GCC) + +/* standard posix version of popen() support */ +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + char *name; + char *mode = "r"; + int bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "s|si:popen", &name, &mode, &bufsize)) + return NULL; + Py_BEGIN_ALLOW_THREADS + fp = popen(name, mode); + Py_END_ALLOW_THREADS + if (fp == NULL) + return posix_error(); + f = PyFile_FromFile(fp, name, mode, pclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +/* fork() under OS/2 has lots'o'warts + * EMX supports pipe() and spawn*() so we can synthesize popen[234]() + * most of this code is a ripoff of the win32 code, but using the + * capabilities of EMX's C library routines + */ + +/* These tell _PyPopen() whether to return 1, 2, or 3 file objects. */ +#define POPEN_1 1 +#define POPEN_2 2 +#define POPEN_3 3 +#define POPEN_4 4 + +static PyObject *_PyPopen(char *, int, int, int); +static int _PyPclose(FILE *file); + +/* + * Internal dictionary mapping popen* file pointers to process handles, + * for use when retrieving the process exit code. See _PyPclose() below + * for more information on this dictionary's use. + */ +static PyObject *_PyPopenProcs = NULL; + +/* os2emx version of popen2() + * + * The result of this function is a pipe (file) connected to the + * process's stdin, and a pipe connected to the process's stdout. + */ + +static PyObject * +os2emx_popen2(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm=0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen2", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'"); + return NULL; + } else + tm = O_BINARY; + + f = _PyPopen(cmdstring, tm, POPEN_2, bufsize); + + return f; +} + +/* + * Variation on os2emx.popen2 + * + * The result of this function is 3 pipes - the process's stdin, + * stdout and stderr + */ + +static PyObject * +os2emx_popen3(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen3", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'"); + return NULL; + } else + tm = O_BINARY; + + f = _PyPopen(cmdstring, tm, POPEN_3, bufsize); + + return f; +} + +/* + * Variation on os2emx.popen2 + * + * The result of this function is 2 pipes - the processes stdin, + * and stdout+stderr combined as a single pipe. + */ + +static PyObject * +os2emx_popen4(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen4", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'"); + return NULL; + } else + tm = O_BINARY; + + f = _PyPopen(cmdstring, tm, POPEN_4, bufsize); + + return f; +} + +/* a couple of structures for convenient handling of multiple + * file handles and pipes + */ +struct file_ref +{ + int handle; + int flags; +}; + +struct pipe_ref +{ + int rd; + int wr; +}; + +/* The following code is derived from the win32 code */ + +static PyObject * +_PyPopen(char *cmdstring, int mode, int n, int bufsize) +{ + struct file_ref stdio[3]; + struct pipe_ref p_fd[3]; + FILE *p_s[3]; + int file_count, i, pipe_err, pipe_pid; + char *shell, *sh_name, *opt, *rd_mode, *wr_mode; + PyObject *f, *p_f[3]; + + /* file modes for subsequent fdopen's on pipe handles */ + if (mode == O_TEXT) + { + rd_mode = "rt"; + wr_mode = "wt"; + } + else + { + rd_mode = "rb"; + wr_mode = "wb"; + } + + /* prepare shell references */ + if ((shell = getenv("EMXSHELL")) == NULL) + if ((shell = getenv("COMSPEC")) == NULL) + { + errno = ENOENT; + return posix_error(); + } + + sh_name = _getname(shell); + if (stricmp(sh_name, "cmd.exe") == 0 || stricmp(sh_name, "4os2.exe") == 0) + opt = "/c"; + else + opt = "-c"; + + /* save current stdio fds + their flags, and set not inheritable */ + i = pipe_err = 0; + while (pipe_err >= 0 && i < 3) + { + pipe_err = stdio[i].handle = dup(i); + stdio[i].flags = fcntl(i, F_GETFD, 0); + fcntl(stdio[i].handle, F_SETFD, stdio[i].flags | FD_CLOEXEC); + i++; + } + if (pipe_err < 0) + { + /* didn't get them all saved - clean up and bail out */ + int saved_err = errno; + while (i-- > 0) + { + close(stdio[i].handle); + } + errno = saved_err; + return posix_error(); + } + + /* create pipe ends */ + file_count = 2; + if (n == POPEN_3) + file_count = 3; + i = pipe_err = 0; + while ((pipe_err == 0) && (i < file_count)) + pipe_err = pipe((int *)&p_fd[i++]); + if (pipe_err < 0) + { + /* didn't get them all made - clean up and bail out */ + while (i-- > 0) + { + close(p_fd[i].wr); + close(p_fd[i].rd); + } + errno = EPIPE; + return posix_error(); + } + + /* change the actual standard IO streams over temporarily, + * making the retained pipe ends non-inheritable + */ + pipe_err = 0; + + /* - stdin */ + if (dup2(p_fd[0].rd, 0) == 0) + { + close(p_fd[0].rd); + i = fcntl(p_fd[0].wr, F_GETFD, 0); + fcntl(p_fd[0].wr, F_SETFD, i | FD_CLOEXEC); + if ((p_s[0] = fdopen(p_fd[0].wr, wr_mode)) == NULL) + { + close(p_fd[0].wr); + pipe_err = -1; + } + } + else + { + pipe_err = -1; + } + + /* - stdout */ + if (pipe_err == 0) + { + if (dup2(p_fd[1].wr, 1) == 1) + { + close(p_fd[1].wr); + i = fcntl(p_fd[1].rd, F_GETFD, 0); + fcntl(p_fd[1].rd, F_SETFD, i | FD_CLOEXEC); + if ((p_s[1] = fdopen(p_fd[1].rd, rd_mode)) == NULL) + { + close(p_fd[1].rd); + pipe_err = -1; + } + } + else + { + pipe_err = -1; + } + } + + /* - stderr, as required */ + if (pipe_err == 0) + switch (n) + { + case POPEN_3: + { + if (dup2(p_fd[2].wr, 2) == 2) + { + close(p_fd[2].wr); + i = fcntl(p_fd[2].rd, F_GETFD, 0); + fcntl(p_fd[2].rd, F_SETFD, i | FD_CLOEXEC); + if ((p_s[2] = fdopen(p_fd[2].rd, rd_mode)) == NULL) + { + close(p_fd[2].rd); + pipe_err = -1; + } + } + else + { + pipe_err = -1; + } + break; + } + + case POPEN_4: + { + if (dup2(1, 2) != 2) + { + pipe_err = -1; + } + break; + } + } + + /* spawn the child process */ + if (pipe_err == 0) + { + pipe_pid = spawnlp(P_NOWAIT, shell, shell, opt, cmdstring, (char *)0); + if (pipe_pid == -1) + { + pipe_err = -1; + } + else + { + /* save the PID into the FILE structure + * NOTE: this implementation doesn't actually + * take advantage of this, but do it for + * completeness - AIM Apr01 + */ + for (i = 0; i < file_count; i++) + p_s[i]->_pid = pipe_pid; + } + } + + /* reset standard IO to normal */ + for (i = 0; i < 3; i++) + { + dup2(stdio[i].handle, i); + fcntl(i, F_SETFD, stdio[i].flags); + close(stdio[i].handle); + } + + /* if any remnant problems, clean up and bail out */ + if (pipe_err < 0) + { + for (i = 0; i < 3; i++) + { + close(p_fd[i].rd); + close(p_fd[i].wr); + } + errno = EPIPE; + return posix_error_with_filename(cmdstring); + } + + /* build tuple of file objects to return */ + if ((p_f[0] = PyFile_FromFile(p_s[0], cmdstring, wr_mode, _PyPclose)) != NULL) + PyFile_SetBufSize(p_f[0], bufsize); + if ((p_f[1] = PyFile_FromFile(p_s[1], cmdstring, rd_mode, _PyPclose)) != NULL) + PyFile_SetBufSize(p_f[1], bufsize); + if (n == POPEN_3) + { + if ((p_f[2] = PyFile_FromFile(p_s[2], cmdstring, rd_mode, _PyPclose)) != NULL) + PyFile_SetBufSize(p_f[0], bufsize); + f = PyTuple_Pack(3, p_f[0], p_f[1], p_f[2]); + } + else + f = PyTuple_Pack(2, p_f[0], p_f[1]); + + /* + * Insert the files we've created into the process dictionary + * all referencing the list with the process handle and the + * initial number of files (see description below in _PyPclose). + * Since if _PyPclose later tried to wait on a process when all + * handles weren't closed, it could create a deadlock with the + * child, we spend some energy here to try to ensure that we + * either insert all file handles into the dictionary or none + * at all. It's a little clumsy with the various popen modes + * and variable number of files involved. + */ + if (!_PyPopenProcs) + { + _PyPopenProcs = PyDict_New(); + } + + if (_PyPopenProcs) + { + PyObject *procObj, *pidObj, *intObj, *fileObj[3]; + int ins_rc[3]; + + fileObj[0] = fileObj[1] = fileObj[2] = NULL; + ins_rc[0] = ins_rc[1] = ins_rc[2] = 0; + + procObj = PyList_New(2); + pidObj = PyInt_FromLong((long) pipe_pid); + intObj = PyInt_FromLong((long) file_count); + + if (procObj && pidObj && intObj) + { + PyList_SetItem(procObj, 0, pidObj); + PyList_SetItem(procObj, 1, intObj); + + fileObj[0] = PyLong_FromVoidPtr(p_s[0]); + if (fileObj[0]) + { + ins_rc[0] = PyDict_SetItem(_PyPopenProcs, + fileObj[0], + procObj); + } + fileObj[1] = PyLong_FromVoidPtr(p_s[1]); + if (fileObj[1]) + { + ins_rc[1] = PyDict_SetItem(_PyPopenProcs, + fileObj[1], + procObj); + } + if (file_count >= 3) + { + fileObj[2] = PyLong_FromVoidPtr(p_s[2]); + if (fileObj[2]) + { + ins_rc[2] = PyDict_SetItem(_PyPopenProcs, + fileObj[2], + procObj); + } + } + + if (ins_rc[0] < 0 || !fileObj[0] || + ins_rc[1] < 0 || (file_count > 1 && !fileObj[1]) || + ins_rc[2] < 0 || (file_count > 2 && !fileObj[2])) + { + /* Something failed - remove any dictionary + * entries that did make it. + */ + if (!ins_rc[0] && fileObj[0]) + { + PyDict_DelItem(_PyPopenProcs, + fileObj[0]); + } + if (!ins_rc[1] && fileObj[1]) + { + PyDict_DelItem(_PyPopenProcs, + fileObj[1]); + } + if (!ins_rc[2] && fileObj[2]) + { + PyDict_DelItem(_PyPopenProcs, + fileObj[2]); + } + } + } + + /* + * Clean up our localized references for the dictionary keys + * and value since PyDict_SetItem will Py_INCREF any copies + * that got placed in the dictionary. + */ + Py_XDECREF(procObj); + Py_XDECREF(fileObj[0]); + Py_XDECREF(fileObj[1]); + Py_XDECREF(fileObj[2]); + } + + /* Child is launched. */ + return f; +} + +/* + * Wrapper for fclose() to use for popen* files, so we can retrieve the + * exit code for the child process and return as a result of the close. + * + * This function uses the _PyPopenProcs dictionary in order to map the + * input file pointer to information about the process that was + * originally created by the popen* call that created the file pointer. + * The dictionary uses the file pointer as a key (with one entry + * inserted for each file returned by the original popen* call) and a + * single list object as the value for all files from a single call. + * The list object contains the Win32 process handle at [0], and a file + * count at [1], which is initialized to the total number of file + * handles using that list. + * + * This function closes whichever handle it is passed, and decrements + * the file count in the dictionary for the process handle pointed to + * by this file. On the last close (when the file count reaches zero), + * this function will wait for the child process and then return its + * exit code as the result of the close() operation. This permits the + * files to be closed in any order - it is always the close() of the + * final handle that will return the exit code. + * + * NOTE: This function is currently called with the GIL released. + * hence we use the GILState API to manage our state. + */ + +static int _PyPclose(FILE *file) +{ + int result; + int exit_code; + int pipe_pid; + PyObject *procObj, *pidObj, *intObj, *fileObj; + int file_count; +#ifdef WITH_THREAD + PyGILState_STATE state; +#endif + + /* Close the file handle first, to ensure it can't block the + * child from exiting if it's the last handle. + */ + result = fclose(file); + +#ifdef WITH_THREAD + state = PyGILState_Ensure(); +#endif + if (_PyPopenProcs) + { + if ((fileObj = PyLong_FromVoidPtr(file)) != NULL && + (procObj = PyDict_GetItem(_PyPopenProcs, + fileObj)) != NULL && + (pidObj = PyList_GetItem(procObj,0)) != NULL && + (intObj = PyList_GetItem(procObj,1)) != NULL) + { + pipe_pid = (int) PyInt_AsLong(pidObj); + file_count = (int) PyInt_AsLong(intObj); + + if (file_count > 1) + { + /* Still other files referencing process */ + file_count--; + PyList_SetItem(procObj,1, + PyInt_FromLong((long) file_count)); + } + else + { + /* Last file for this process */ + if (result != EOF && + waitpid(pipe_pid, &exit_code, 0) == pipe_pid) + { + /* extract exit status */ + if (WIFEXITED(exit_code)) + { + result = WEXITSTATUS(exit_code); + } + else + { + errno = EPIPE; + result = -1; + } + } + else + { + /* Indicate failure - this will cause the file object + * to raise an I/O error and translate the last + * error code from errno. We do have a problem with + * last errors that overlap the normal errno table, + * but that's a consistent problem with the file object. + */ + result = -1; + } + } + + /* Remove this file pointer from dictionary */ + PyDict_DelItem(_PyPopenProcs, fileObj); + + if (PyDict_Size(_PyPopenProcs) == 0) + { + Py_DECREF(_PyPopenProcs); + _PyPopenProcs = NULL; + } + + } /* if object retrieval ok */ + + Py_XDECREF(fileObj); + } /* if _PyPopenProcs */ + +#ifdef WITH_THREAD + PyGILState_Release(state); +#endif + return result; +} + +#endif /* PYCC_??? */ + +#elif defined(MS_WINDOWS) + +/* + * Portable 'popen' replacement for Win32. + * + * Written by Bill Tutt . Minor tweaks + * and 2.0 integration by Fredrik Lundh + * Return code handling by David Bolen . + */ + +#include +#include +#include + +/* These tell _PyPopen() wether to return 1, 2, or 3 file objects. */ +#define POPEN_1 1 +#define POPEN_2 2 +#define POPEN_3 3 +#define POPEN_4 4 + +static PyObject *_PyPopen(char *, int, int); +static int _PyPclose(FILE *file); + +/* + * Internal dictionary mapping popen* file pointers to process handles, + * for use when retrieving the process exit code. See _PyPclose() below + * for more information on this dictionary's use. + */ +static PyObject *_PyPopenProcs = NULL; + + +/* popen that works from a GUI. + * + * The result of this function is a pipe (file) connected to the + * processes stdin or stdout, depending on the requested mode. + */ + +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "r"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 'r') + tm = _O_RDONLY; + else if (*mode != 'w') { + PyErr_SetString(PyExc_ValueError, "popen() arg 2 must be 'r' or 'w'"); + return NULL; + } else + tm = _O_WRONLY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen() arg 3 must be -1"); + return NULL; + } + + if (*(mode+1) == 't') + f = _PyPopen(cmdstring, tm | _O_TEXT, POPEN_1); + else if (*(mode+1) == 'b') + f = _PyPopen(cmdstring, tm | _O_BINARY, POPEN_1); + else + f = _PyPopen(cmdstring, tm | _O_TEXT, POPEN_1); + + return f; +} + +/* Variation on win32pipe.popen + * + * The result of this function is a pipe (file) connected to the + * process's stdin, and a pipe connected to the process's stdout. + */ + +static PyObject * +win32_popen2(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm=0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen2", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = _O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "popen2() arg 2 must be 't' or 'b'"); + return NULL; + } else + tm = _O_BINARY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen2() arg 3 must be -1"); + return NULL; + } + + f = _PyPopen(cmdstring, tm, POPEN_2); + + return f; +} + +/* + * Variation on + * + * The result of this function is 3 pipes - the process's stdin, + * stdout and stderr + */ + +static PyObject * +win32_popen3(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen3", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = _O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "popen3() arg 2 must be 't' or 'b'"); + return NULL; + } else + tm = _O_BINARY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen3() arg 3 must be -1"); + return NULL; + } + + f = _PyPopen(cmdstring, tm, POPEN_3); + + return f; +} + +/* + * Variation on win32pipe.popen + * + * The result of this function is 2 pipes - the processes stdin, + * and stdout+stderr combined as a single pipe. + */ + +static PyObject * +win32_popen4(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen4", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = _O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "popen4() arg 2 must be 't' or 'b'"); + return NULL; + } else + tm = _O_BINARY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen4() arg 3 must be -1"); + return NULL; + } + + f = _PyPopen(cmdstring, tm, POPEN_4); + + return f; +} + +static BOOL +_PyPopenCreateProcess(char *cmdstring, + HANDLE hStdin, + HANDLE hStdout, + HANDLE hStderr, + HANDLE *hProcess) +{ + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + DWORD dwProcessFlags = 0; /* no NEW_CONSOLE by default for Ctrl+C handling */ + char *s1,*s2, *s3 = " /c "; + const char *szConsoleSpawn = "w9xpopen.exe"; + int i; + Py_ssize_t x; + + if (i = GetEnvironmentVariable("COMSPEC",NULL,0)) { + char *comshell; + + s1 = (char *)alloca(i); + if (!(x = GetEnvironmentVariable("COMSPEC", s1, i))) + /* x < i, so x fits into an integer */ + return (int)x; + + /* Explicitly check if we are using COMMAND.COM. If we are + * then use the w9xpopen hack. + */ + comshell = s1 + x; + while (comshell >= s1 && *comshell != '\\') + --comshell; + ++comshell; + + if (GetVersion() < 0x80000000 && + _stricmp(comshell, "command.com") != 0) { + /* NT/2000 and not using command.com. */ + x = i + strlen(s3) + strlen(cmdstring) + 1; + s2 = (char *)alloca(x); + ZeroMemory(s2, x); + PyOS_snprintf(s2, x, "%s%s%s", s1, s3, cmdstring); + } + else { + /* + * Oh gag, we're on Win9x or using COMMAND.COM. Use + * the workaround listed in KB: Q150956 + */ + char modulepath[_MAX_PATH]; + struct stat statinfo; + GetModuleFileName(NULL, modulepath, sizeof(modulepath)); + for (i = x = 0; modulepath[i]; i++) + if (modulepath[i] == SEP) + x = i+1; + modulepath[x] = '\0'; + /* Create the full-name to w9xpopen, so we can test it exists */ + strncat(modulepath, + szConsoleSpawn, + (sizeof(modulepath)/sizeof(modulepath[0])) + -strlen(modulepath)); + if (stat(modulepath, &statinfo) != 0) { + /* Eeek - file-not-found - possibly an embedding + situation - see if we can locate it in sys.prefix + */ + strncpy(modulepath, + Py_GetExecPrefix(), + sizeof(modulepath)/sizeof(modulepath[0])); + if (modulepath[strlen(modulepath)-1] != '\\') + strcat(modulepath, "\\"); + strncat(modulepath, + szConsoleSpawn, + (sizeof(modulepath)/sizeof(modulepath[0])) + -strlen(modulepath)); + /* No where else to look - raise an easily identifiable + error, rather than leaving Windows to report + "file not found" - as the user is probably blissfully + unaware this shim EXE is used, and it will confuse them. + (well, it confused me for a while ;-) + */ + if (stat(modulepath, &statinfo) != 0) { + PyErr_Format(PyExc_RuntimeError, + "Can not locate '%s' which is needed " + "for popen to work with your shell " + "or platform.", + szConsoleSpawn); + return FALSE; + } + } + x = i + strlen(s3) + strlen(cmdstring) + 1 + + strlen(modulepath) + + strlen(szConsoleSpawn) + 1; + + s2 = (char *)alloca(x); + ZeroMemory(s2, x); + /* To maintain correct argument passing semantics, + we pass the command-line as it stands, and allow + quoting to be applied. w9xpopen.exe will then + use its argv vector, and re-quote the necessary + args for the ultimate child process. + */ + PyOS_snprintf( + s2, x, + "\"%s\" %s%s%s", + modulepath, + s1, + s3, + cmdstring); + /* Not passing CREATE_NEW_CONSOLE has been known to + cause random failures on win9x. Specifically a + dialog: + "Your program accessed mem currently in use at xxx" + and a hopeful warning about the stability of your + system. + Cost is Ctrl+C wont kill children, but anyone + who cares can have a go! + */ + dwProcessFlags |= CREATE_NEW_CONSOLE; + } + } + + /* Could be an else here to try cmd.exe / command.com in the path + Now we'll just error out.. */ + else { + PyErr_SetString(PyExc_RuntimeError, + "Cannot locate a COMSPEC environment variable to " + "use as the shell"); + return FALSE; + } + + ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + siStartInfo.hStdInput = hStdin; + siStartInfo.hStdOutput = hStdout; + siStartInfo.hStdError = hStderr; + siStartInfo.wShowWindow = SW_HIDE; + + if (CreateProcess(NULL, + s2, + NULL, + NULL, + TRUE, + dwProcessFlags, + NULL, + NULL, + &siStartInfo, + &piProcInfo) ) { + /* Close the handles now so anyone waiting is woken. */ + CloseHandle(piProcInfo.hThread); + + /* Return process handle */ + *hProcess = piProcInfo.hProcess; + return TRUE; + } + win32_error("CreateProcess", s2); + return FALSE; +} + +/* The following code is based off of KB: Q190351 */ + +static PyObject * +_PyPopen(char *cmdstring, int mode, int n) +{ + HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr, + hChildStderrRd, hChildStderrWr, hChildStdinWrDup, hChildStdoutRdDup, + hChildStderrRdDup, hProcess; /* hChildStdoutWrDup; */ + + SECURITY_ATTRIBUTES saAttr; + BOOL fSuccess; + int fd1, fd2, fd3; + FILE *f1, *f2, *f3; + long file_count; + PyObject *f; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) + return win32_error("CreatePipe", NULL); + + /* Create new output read handle and the input write handle. Set + * the inheritance properties to FALSE. Otherwise, the child inherits + * these handles; resulting in non-closeable handles to the pipes + * being created. */ + fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr, + GetCurrentProcess(), &hChildStdinWrDup, 0, + FALSE, + DUPLICATE_SAME_ACCESS); + if (!fSuccess) + return win32_error("DuplicateHandle", NULL); + + /* Close the inheritable version of ChildStdin + that we're using. */ + CloseHandle(hChildStdinWr); + + if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) + return win32_error("CreatePipe", NULL); + + fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, + GetCurrentProcess(), &hChildStdoutRdDup, 0, + FALSE, DUPLICATE_SAME_ACCESS); + if (!fSuccess) + return win32_error("DuplicateHandle", NULL); + + /* Close the inheritable version of ChildStdout + that we're using. */ + CloseHandle(hChildStdoutRd); + + if (n != POPEN_4) { + if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0)) + return win32_error("CreatePipe", NULL); + fSuccess = DuplicateHandle(GetCurrentProcess(), + hChildStderrRd, + GetCurrentProcess(), + &hChildStderrRdDup, 0, + FALSE, DUPLICATE_SAME_ACCESS); + if (!fSuccess) + return win32_error("DuplicateHandle", NULL); + /* Close the inheritable version of ChildStdErr that we're using. */ + CloseHandle(hChildStderrRd); + } + + switch (n) { + case POPEN_1: + switch (mode & (_O_RDONLY | _O_TEXT | _O_BINARY | _O_WRONLY)) { + case _O_WRONLY | _O_TEXT: + /* Case for writing to child Stdin in text mode. */ + fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, "w"); + f = PyFile_FromFile(f1, cmdstring, "w", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdoutRdDup); + CloseHandle(hChildStderrRdDup); + break; + + case _O_RDONLY | _O_TEXT: + /* Case for reading from child Stdout in text mode. */ + fd1 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode); + f1 = _fdopen(fd1, "r"); + f = PyFile_FromFile(f1, cmdstring, "r", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdinWrDup); + CloseHandle(hChildStderrRdDup); + break; + + case _O_RDONLY | _O_BINARY: + /* Case for readinig from child Stdout in binary mode. */ + fd1 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode); + f1 = _fdopen(fd1, "rb"); + f = PyFile_FromFile(f1, cmdstring, "rb", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdinWrDup); + CloseHandle(hChildStderrRdDup); + break; + + case _O_WRONLY | _O_BINARY: + /* Case for writing to child Stdin in binary mode. */ + fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, "wb"); + f = PyFile_FromFile(f1, cmdstring, "wb", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdoutRdDup); + CloseHandle(hChildStderrRdDup); + break; + } + file_count = 1; + break; + + case POPEN_2: + case POPEN_4: + { + char *m1, *m2; + PyObject *p1, *p2; + + if (mode & _O_TEXT) { + m1 = "r"; + m2 = "w"; + } else { + m1 = "rb"; + m2 = "wb"; + } + + fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, m2); + fd2 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode); + f2 = _fdopen(fd2, m1); + p1 = PyFile_FromFile(f1, cmdstring, m2, _PyPclose); + PyFile_SetBufSize(p1, 0); + p2 = PyFile_FromFile(f2, cmdstring, m1, _PyPclose); + PyFile_SetBufSize(p2, 0); + + if (n != 4) + CloseHandle(hChildStderrRdDup); + + f = PyTuple_Pack(2,p1,p2); + Py_XDECREF(p1); + Py_XDECREF(p2); + file_count = 2; + break; + } + + case POPEN_3: + { + char *m1, *m2; + PyObject *p1, *p2, *p3; + + if (mode & _O_TEXT) { + m1 = "r"; + m2 = "w"; + } else { + m1 = "rb"; + m2 = "wb"; + } + + fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, m2); + fd2 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode); + f2 = _fdopen(fd2, m1); + fd3 = _open_osfhandle((intptr_t)hChildStderrRdDup, mode); + f3 = _fdopen(fd3, m1); + p1 = PyFile_FromFile(f1, cmdstring, m2, _PyPclose); + p2 = PyFile_FromFile(f2, cmdstring, m1, _PyPclose); + p3 = PyFile_FromFile(f3, cmdstring, m1, _PyPclose); + PyFile_SetBufSize(p1, 0); + PyFile_SetBufSize(p2, 0); + PyFile_SetBufSize(p3, 0); + f = PyTuple_Pack(3,p1,p2,p3); + Py_XDECREF(p1); + Py_XDECREF(p2); + Py_XDECREF(p3); + file_count = 3; + break; + } + } + + if (n == POPEN_4) { + if (!_PyPopenCreateProcess(cmdstring, + hChildStdinRd, + hChildStdoutWr, + hChildStdoutWr, + &hProcess)) + return NULL; + } + else { + if (!_PyPopenCreateProcess(cmdstring, + hChildStdinRd, + hChildStdoutWr, + hChildStderrWr, + &hProcess)) + return NULL; + } + + /* + * Insert the files we've created into the process dictionary + * all referencing the list with the process handle and the + * initial number of files (see description below in _PyPclose). + * Since if _PyPclose later tried to wait on a process when all + * handles weren't closed, it could create a deadlock with the + * child, we spend some energy here to try to ensure that we + * either insert all file handles into the dictionary or none + * at all. It's a little clumsy with the various popen modes + * and variable number of files involved. + */ + if (!_PyPopenProcs) { + _PyPopenProcs = PyDict_New(); + } + + if (_PyPopenProcs) { + PyObject *procObj, *hProcessObj, *intObj, *fileObj[3]; + int ins_rc[3]; + + fileObj[0] = fileObj[1] = fileObj[2] = NULL; + ins_rc[0] = ins_rc[1] = ins_rc[2] = 0; + + procObj = PyList_New(2); + hProcessObj = PyLong_FromVoidPtr(hProcess); + intObj = PyInt_FromLong(file_count); + + if (procObj && hProcessObj && intObj) { + PyList_SetItem(procObj,0,hProcessObj); + PyList_SetItem(procObj,1,intObj); + + fileObj[0] = PyLong_FromVoidPtr(f1); + if (fileObj[0]) { + ins_rc[0] = PyDict_SetItem(_PyPopenProcs, + fileObj[0], + procObj); + } + if (file_count >= 2) { + fileObj[1] = PyLong_FromVoidPtr(f2); + if (fileObj[1]) { + ins_rc[1] = PyDict_SetItem(_PyPopenProcs, + fileObj[1], + procObj); + } + } + if (file_count >= 3) { + fileObj[2] = PyLong_FromVoidPtr(f3); + if (fileObj[2]) { + ins_rc[2] = PyDict_SetItem(_PyPopenProcs, + fileObj[2], + procObj); + } + } + + if (ins_rc[0] < 0 || !fileObj[0] || + ins_rc[1] < 0 || (file_count > 1 && !fileObj[1]) || + ins_rc[2] < 0 || (file_count > 2 && !fileObj[2])) { + /* Something failed - remove any dictionary + * entries that did make it. + */ + if (!ins_rc[0] && fileObj[0]) { + PyDict_DelItem(_PyPopenProcs, + fileObj[0]); + } + if (!ins_rc[1] && fileObj[1]) { + PyDict_DelItem(_PyPopenProcs, + fileObj[1]); + } + if (!ins_rc[2] && fileObj[2]) { + PyDict_DelItem(_PyPopenProcs, + fileObj[2]); + } + } + } + + /* + * Clean up our localized references for the dictionary keys + * and value since PyDict_SetItem will Py_INCREF any copies + * that got placed in the dictionary. + */ + Py_XDECREF(procObj); + Py_XDECREF(fileObj[0]); + Py_XDECREF(fileObj[1]); + Py_XDECREF(fileObj[2]); + } + + /* Child is launched. Close the parents copy of those pipe + * handles that only the child should have open. You need to + * make sure that no handles to the write end of the output pipe + * are maintained in this process or else the pipe will not close + * when the child process exits and the ReadFile will hang. */ + + if (!CloseHandle(hChildStdinRd)) + return win32_error("CloseHandle", NULL); + + if (!CloseHandle(hChildStdoutWr)) + return win32_error("CloseHandle", NULL); + + if ((n != 4) && (!CloseHandle(hChildStderrWr))) + return win32_error("CloseHandle", NULL); + + return f; +} + +/* + * Wrapper for fclose() to use for popen* files, so we can retrieve the + * exit code for the child process and return as a result of the close. + * + * This function uses the _PyPopenProcs dictionary in order to map the + * input file pointer to information about the process that was + * originally created by the popen* call that created the file pointer. + * The dictionary uses the file pointer as a key (with one entry + * inserted for each file returned by the original popen* call) and a + * single list object as the value for all files from a single call. + * The list object contains the Win32 process handle at [0], and a file + * count at [1], which is initialized to the total number of file + * handles using that list. + * + * This function closes whichever handle it is passed, and decrements + * the file count in the dictionary for the process handle pointed to + * by this file. On the last close (when the file count reaches zero), + * this function will wait for the child process and then return its + * exit code as the result of the close() operation. This permits the + * files to be closed in any order - it is always the close() of the + * final handle that will return the exit code. + * + * NOTE: This function is currently called with the GIL released. + * hence we use the GILState API to manage our state. + */ + +static int _PyPclose(FILE *file) +{ + int result; + DWORD exit_code; + HANDLE hProcess; + PyObject *procObj, *hProcessObj, *intObj, *fileObj; + long file_count; +#ifdef WITH_THREAD + PyGILState_STATE state; +#endif + + /* Close the file handle first, to ensure it can't block the + * child from exiting if it's the last handle. + */ + result = fclose(file); +#ifdef WITH_THREAD + state = PyGILState_Ensure(); +#endif + if (_PyPopenProcs) { + if ((fileObj = PyLong_FromVoidPtr(file)) != NULL && + (procObj = PyDict_GetItem(_PyPopenProcs, + fileObj)) != NULL && + (hProcessObj = PyList_GetItem(procObj,0)) != NULL && + (intObj = PyList_GetItem(procObj,1)) != NULL) { + + hProcess = PyLong_AsVoidPtr(hProcessObj); + file_count = PyInt_AsLong(intObj); + + if (file_count > 1) { + /* Still other files referencing process */ + file_count--; + PyList_SetItem(procObj,1, + PyInt_FromLong(file_count)); + } else { + /* Last file for this process */ + if (result != EOF && + WaitForSingleObject(hProcess, INFINITE) != WAIT_FAILED && + GetExitCodeProcess(hProcess, &exit_code)) { + /* Possible truncation here in 16-bit environments, but + * real exit codes are just the lower byte in any event. + */ + result = exit_code; + } else { + /* Indicate failure - this will cause the file object + * to raise an I/O error and translate the last Win32 + * error code from errno. We do have a problem with + * last errors that overlap the normal errno table, + * but that's a consistent problem with the file object. + */ + if (result != EOF) { + /* If the error wasn't from the fclose(), then + * set errno for the file object error handling. + */ + errno = GetLastError(); + } + result = -1; + } + + /* Free up the native handle at this point */ + CloseHandle(hProcess); + } + + /* Remove this file pointer from dictionary */ + PyDict_DelItem(_PyPopenProcs, fileObj); + + if (PyDict_Size(_PyPopenProcs) == 0) { + Py_DECREF(_PyPopenProcs); + _PyPopenProcs = NULL; + } + + } /* if object retrieval ok */ + + Py_XDECREF(fileObj); + } /* if _PyPopenProcs */ + +#ifdef WITH_THREAD + PyGILState_Release(state); +#endif + return result; +} + +#else /* which OS? */ +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + char *name; + char *mode = "r"; + int bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "s|si:popen", &name, &mode, &bufsize)) + return NULL; + /* Strip mode of binary or text modifiers */ + if (strcmp(mode, "rb") == 0 || strcmp(mode, "rt") == 0) + mode = "r"; + else if (strcmp(mode, "wb") == 0 || strcmp(mode, "wt") == 0) + mode = "w"; + Py_BEGIN_ALLOW_THREADS + fp = popen(name, mode); + Py_END_ALLOW_THREADS + if (fp == NULL) + return posix_error(); + f = PyFile_FromFile(fp, name, mode, pclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +#endif /* PYOS_??? */ +#endif /* HAVE_POPEN */ + + +#ifdef HAVE_SETUID +PyDoc_STRVAR(posix_setuid__doc__, +"setuid(uid)\n\n\ +Set the current process's user id."); + +static PyObject * +posix_setuid(PyObject *self, PyObject *args) +{ + int uid; + if (!PyArg_ParseTuple(args, "i:setuid", &uid)) + return NULL; + if (setuid(uid) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETUID */ + + +#ifdef HAVE_SETEUID +PyDoc_STRVAR(posix_seteuid__doc__, +"seteuid(uid)\n\n\ +Set the current process's effective user id."); + +static PyObject * +posix_seteuid (PyObject *self, PyObject *args) +{ + int euid; + if (!PyArg_ParseTuple(args, "i", &euid)) { + return NULL; + } else if (seteuid(euid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETEUID */ + +#ifdef HAVE_SETEGID +PyDoc_STRVAR(posix_setegid__doc__, +"setegid(gid)\n\n\ +Set the current process's effective group id."); + +static PyObject * +posix_setegid (PyObject *self, PyObject *args) +{ + int egid; + if (!PyArg_ParseTuple(args, "i", &egid)) { + return NULL; + } else if (setegid(egid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETEGID */ + +#ifdef HAVE_SETREUID +PyDoc_STRVAR(posix_setreuid__doc__, +"setreuid(ruid, euid)\n\n\ +Set the current process's real and effective user ids."); + +static PyObject * +posix_setreuid (PyObject *self, PyObject *args) +{ + int ruid, euid; + if (!PyArg_ParseTuple(args, "ii", &ruid, &euid)) { + return NULL; + } else if (setreuid(ruid, euid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETREUID */ + +#ifdef HAVE_SETREGID +PyDoc_STRVAR(posix_setregid__doc__, +"setregid(rgid, egid)\n\n\ +Set the current process's real and effective group ids."); + +static PyObject * +posix_setregid (PyObject *self, PyObject *args) +{ + int rgid, egid; + if (!PyArg_ParseTuple(args, "ii", &rgid, &egid)) { + return NULL; + } else if (setregid(rgid, egid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETREGID */ + +#ifdef HAVE_SETGID +PyDoc_STRVAR(posix_setgid__doc__, +"setgid(gid)\n\n\ +Set the current process's group id."); + +static PyObject * +posix_setgid(PyObject *self, PyObject *args) +{ + int gid; + if (!PyArg_ParseTuple(args, "i:setgid", &gid)) + return NULL; + if (setgid(gid) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETGID */ + +#ifdef HAVE_SETGROUPS +PyDoc_STRVAR(posix_setgroups__doc__, +"setgroups(list)\n\n\ +Set the groups of the current process to list."); + +static PyObject * +posix_setgroups(PyObject *self, PyObject *args) +{ + PyObject *groups; + int i, len; + gid_t grouplist[MAX_GROUPS]; + + if (!PyArg_ParseTuple(args, "O:setgid", &groups)) + return NULL; + if (!PySequence_Check(groups)) { + PyErr_SetString(PyExc_TypeError, "setgroups argument must be a sequence"); + return NULL; + } + len = PySequence_Size(groups); + if (len > MAX_GROUPS) { + PyErr_SetString(PyExc_ValueError, "too many groups"); + return NULL; + } + for(i = 0; i < len; i++) { + PyObject *elem; + elem = PySequence_GetItem(groups, i); + if (!elem) + return NULL; + if (!PyInt_Check(elem)) { + if (!PyLong_Check(elem)) { + PyErr_SetString(PyExc_TypeError, + "groups must be integers"); + Py_DECREF(elem); + return NULL; + } else { + unsigned long x = PyLong_AsUnsignedLong(elem); + if (PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "group id too big"); + Py_DECREF(elem); + return NULL; + } + grouplist[i] = x; + /* read back the value to see if it fitted in gid_t */ + if (grouplist[i] != x) { + PyErr_SetString(PyExc_TypeError, + "group id too big"); + Py_DECREF(elem); + return NULL; + } + } + } else { + long x = PyInt_AsLong(elem); + grouplist[i] = x; + if (grouplist[i] != x) { + PyErr_SetString(PyExc_TypeError, + "group id too big"); + Py_DECREF(elem); + return NULL; + } + } + Py_DECREF(elem); + } + + if (setgroups(len, grouplist) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETGROUPS */ + +#ifdef HAVE_WAITPID +PyDoc_STRVAR(posix_waitpid__doc__, +"waitpid(pid, options) -> (pid, status)\n\n\ +Wait for completion of a given child process."); + +static PyObject * +posix_waitpid(PyObject *self, PyObject *args) +{ + int pid, options; +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "ii:waitpid", &pid, &options)) + return NULL; + Py_BEGIN_ALLOW_THREADS + pid = waitpid(pid, &status, options); + Py_END_ALLOW_THREADS + if (pid == -1) + return posix_error(); + else + return Py_BuildValue("ii", pid, status_i); +} + +#elif defined(HAVE_CWAIT) + +/* MS C has a variant of waitpid() that's usable for most purposes. */ +PyDoc_STRVAR(posix_waitpid__doc__, +"waitpid(pid, options) -> (pid, status << 8)\n\n" +"Wait for completion of a given process. options is ignored on Windows."); + +static PyObject * +posix_waitpid(PyObject *self, PyObject *args) +{ + intptr_t pid; + int status, options; + + if (!PyArg_ParseTuple(args, "ii:waitpid", &pid, &options)) + return NULL; + Py_BEGIN_ALLOW_THREADS + pid = _cwait(&status, pid, options); + Py_END_ALLOW_THREADS + if (pid == -1) + return posix_error(); + else + /* shift the status left a byte so this is more like the + POSIX waitpid */ + return Py_BuildValue("ii", pid, status << 8); +} +#endif /* HAVE_WAITPID || HAVE_CWAIT */ + +#ifdef HAVE_WAIT +PyDoc_STRVAR(posix_wait__doc__, +"wait() -> (pid, status)\n\n\ +Wait for completion of a child process."); + +static PyObject * +posix_wait(PyObject *self, PyObject *noargs) +{ + int pid; +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + + status_i = 0; + Py_BEGIN_ALLOW_THREADS + pid = wait(&status); + Py_END_ALLOW_THREADS + if (pid == -1) + return posix_error(); + else + return Py_BuildValue("ii", pid, status_i); +#undef status_i +} +#endif + + +PyDoc_STRVAR(posix_lstat__doc__, +"lstat(path) -> stat result\n\n\ +Like stat(path), but do not follow symbolic links."); + +static PyObject * +posix_lstat(PyObject *self, PyObject *args) +{ +#ifdef HAVE_LSTAT + return posix_do_stat(self, args, "et:lstat", lstat, NULL, NULL); +#else /* !HAVE_LSTAT */ +#ifdef MS_WINDOWS + return posix_do_stat(self, args, "et:lstat", STAT, "U:lstat", win32_wstat); +#else + return posix_do_stat(self, args, "et:lstat", STAT, NULL, NULL); +#endif +#endif /* !HAVE_LSTAT */ +} + + +#ifdef HAVE_READLINK +PyDoc_STRVAR(posix_readlink__doc__, +"readlink(path) -> path\n\n\ +Return a string representing the path to which the symbolic link points."); + +static PyObject * +posix_readlink(PyObject *self, PyObject *args) +{ + char buf[MAXPATHLEN]; + char *path; + int n; + if (!PyArg_ParseTuple(args, "s:readlink", &path)) + return NULL; + Py_BEGIN_ALLOW_THREADS + n = readlink(path, buf, (int) sizeof buf); + Py_END_ALLOW_THREADS + if (n < 0) + return posix_error_with_filename(path); + return PyString_FromStringAndSize(buf, n); +} +#endif /* HAVE_READLINK */ + + +#ifdef HAVE_SYMLINK +PyDoc_STRVAR(posix_symlink__doc__, +"symlink(src, dst)\n\n\ +Create a symbolic link pointing to src named dst."); + +static PyObject * +posix_symlink(PyObject *self, PyObject *args) +{ + return posix_2str(args, "etet:symlink", symlink, NULL, NULL); +} +#endif /* HAVE_SYMLINK */ + + +#ifdef HAVE_TIMES +#ifndef HZ +#define HZ 60 /* Universal constant :-) */ +#endif /* HZ */ + +#if defined(PYCC_VACPP) && defined(PYOS_OS2) +static long +system_uptime(void) +{ + ULONG value = 0; + + Py_BEGIN_ALLOW_THREADS + DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &value, sizeof(value)); + Py_END_ALLOW_THREADS + + return value; +} + +static PyObject * +posix_times(PyObject *self, PyObject *noargs) +{ + /* Currently Only Uptime is Provided -- Others Later */ + return Py_BuildValue("ddddd", + (double)0 /* t.tms_utime / HZ */, + (double)0 /* t.tms_stime / HZ */, + (double)0 /* t.tms_cutime / HZ */, + (double)0 /* t.tms_cstime / HZ */, + (double)system_uptime() / 1000); +} +#else /* not OS2 */ +static PyObject * +posix_times(PyObject *self, PyObject *noargs) +{ + struct tms t; + clock_t c; + errno = 0; + c = times(&t); + if (c == (clock_t) -1) + return posix_error(); + return Py_BuildValue("ddddd", + (double)t.tms_utime / HZ, + (double)t.tms_stime / HZ, + (double)t.tms_cutime / HZ, + (double)t.tms_cstime / HZ, + (double)c / HZ); +} +#endif /* not OS2 */ +#endif /* HAVE_TIMES */ + + +#ifdef MS_WINDOWS +#define HAVE_TIMES /* so the method table will pick it up */ +static PyObject * +posix_times(PyObject *self, PyObject *noargs) +{ + FILETIME create, exit, kernel, user; + HANDLE hProc; + hProc = GetCurrentProcess(); + GetProcessTimes(hProc, &create, &exit, &kernel, &user); + /* The fields of a FILETIME structure are the hi and lo part + of a 64-bit value expressed in 100 nanosecond units. + 1e7 is one second in such units; 1e-7 the inverse. + 429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7. + */ + return Py_BuildValue( + "ddddd", + (double)(kernel.dwHighDateTime*429.4967296 + + kernel.dwLowDateTime*1e-7), + (double)(user.dwHighDateTime*429.4967296 + + user.dwLowDateTime*1e-7), + (double)0, + (double)0, + (double)0); +} +#endif /* MS_WINDOWS */ + +#ifdef HAVE_TIMES +PyDoc_STRVAR(posix_times__doc__, +"times() -> (utime, stime, cutime, cstime, elapsed_time)\n\n\ +Return a tuple of floating point numbers indicating process times."); +#endif + + +#ifdef HAVE_GETSID +PyDoc_STRVAR(posix_getsid__doc__, +"getsid(pid) -> sid\n\n\ +Call the system call getsid()."); + +static PyObject * +posix_getsid(PyObject *self, PyObject *args) +{ + int pid, sid; + if (!PyArg_ParseTuple(args, "i:getsid", &pid)) + return NULL; + sid = getsid(pid); + if (sid < 0) + return posix_error(); + return PyInt_FromLong((long)sid); +} +#endif /* HAVE_GETSID */ + + +#ifdef HAVE_SETSID +PyDoc_STRVAR(posix_setsid__doc__, +"setsid()\n\n\ +Call the system call setsid()."); + +static PyObject * +posix_setsid(PyObject *self, PyObject *noargs) +{ + if (setsid() < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETSID */ + +#ifdef HAVE_SETPGID +PyDoc_STRVAR(posix_setpgid__doc__, +"setpgid(pid, pgrp)\n\n\ +Call the system call setpgid()."); + +static PyObject * +posix_setpgid(PyObject *self, PyObject *args) +{ + int pid, pgrp; + if (!PyArg_ParseTuple(args, "ii:setpgid", &pid, &pgrp)) + return NULL; + if (setpgid(pid, pgrp) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETPGID */ + + +#ifdef HAVE_TCGETPGRP +PyDoc_STRVAR(posix_tcgetpgrp__doc__, +"tcgetpgrp(fd) -> pgid\n\n\ +Return the process group associated with the terminal given by a fd."); + +static PyObject * +posix_tcgetpgrp(PyObject *self, PyObject *args) +{ + int fd, pgid; + if (!PyArg_ParseTuple(args, "i:tcgetpgrp", &fd)) + return NULL; + pgid = tcgetpgrp(fd); + if (pgid < 0) + return posix_error(); + return PyInt_FromLong((long)pgid); +} +#endif /* HAVE_TCGETPGRP */ + + +#ifdef HAVE_TCSETPGRP +PyDoc_STRVAR(posix_tcsetpgrp__doc__, +"tcsetpgrp(fd, pgid)\n\n\ +Set the process group associated with the terminal given by a fd."); + +static PyObject * +posix_tcsetpgrp(PyObject *self, PyObject *args) +{ + int fd, pgid; + if (!PyArg_ParseTuple(args, "ii:tcsetpgrp", &fd, &pgid)) + return NULL; + if (tcsetpgrp(fd, pgid) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_TCSETPGRP */ + +/* Functions acting on file descriptors */ + +PyDoc_STRVAR(posix_open__doc__, +"open(filename, flag [, mode=0777]) -> fd\n\n\ +Open a file (for low level IO)."); + +static PyObject * +posix_open(PyObject *self, PyObject *args) +{ + char *file = NULL; + int flag; + int mode = 0777; + int fd; + +#ifdef MS_WINDOWS + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "Ui|i:mkdir", &po, &flag, &mode)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + fd = _wopen(PyUnicode_AS_UNICODE(po), flag, mode); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error(); + return PyInt_FromLong((long)fd); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, "eti|i", + Py_FileSystemDefaultEncoding, &file, + &flag, &mode)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + fd = open(file, flag, mode); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error_with_allocated_filename(file); + PyMem_Free(file); + return PyInt_FromLong((long)fd); +} + + +PyDoc_STRVAR(posix_close__doc__, +"close(fd)\n\n\ +Close a file descriptor (for low level IO)."); + +static PyObject * +posix_close(PyObject *self, PyObject *args) +{ + int fd, res; + if (!PyArg_ParseTuple(args, "i:close", &fd)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = close(fd); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(posix_dup__doc__, +"dup(fd) -> fd2\n\n\ +Return a duplicate of a file descriptor."); + +static PyObject * +posix_dup(PyObject *self, PyObject *args) +{ + int fd; + if (!PyArg_ParseTuple(args, "i:dup", &fd)) + return NULL; + Py_BEGIN_ALLOW_THREADS + fd = dup(fd); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error(); + return PyInt_FromLong((long)fd); +} + + +PyDoc_STRVAR(posix_dup2__doc__, +"dup2(old_fd, new_fd)\n\n\ +Duplicate file descriptor."); + +static PyObject * +posix_dup2(PyObject *self, PyObject *args) +{ + int fd, fd2, res; + if (!PyArg_ParseTuple(args, "ii:dup2", &fd, &fd2)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = dup2(fd, fd2); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(posix_lseek__doc__, +"lseek(fd, pos, how) -> newpos\n\n\ +Set the current position of a file descriptor."); + +static PyObject * +posix_lseek(PyObject *self, PyObject *args) +{ + int fd, how; +#if defined(MS_WIN64) || defined(MS_WINDOWS) + PY_LONG_LONG pos, res; +#else + off_t pos, res; +#endif + PyObject *posobj; + if (!PyArg_ParseTuple(args, "iOi:lseek", &fd, &posobj, &how)) + return NULL; +#ifdef SEEK_SET + /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */ + switch (how) { + case 0: how = SEEK_SET; break; + case 1: how = SEEK_CUR; break; + case 2: how = SEEK_END; break; + } +#endif /* SEEK_END */ + +#if !defined(HAVE_LARGEFILE_SUPPORT) + pos = PyInt_AsLong(posobj); +#else + pos = PyLong_Check(posobj) ? + PyLong_AsLongLong(posobj) : PyInt_AsLong(posobj); +#endif + if (PyErr_Occurred()) + return NULL; + + Py_BEGIN_ALLOW_THREADS +#if defined(MS_WIN64) || defined(MS_WINDOWS) + res = _lseeki64(fd, pos, how); +#else + res = lseek(fd, pos, how); +#endif + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + +#if !defined(HAVE_LARGEFILE_SUPPORT) + return PyInt_FromLong(res); +#else + return PyLong_FromLongLong(res); +#endif +} + + +PyDoc_STRVAR(posix_read__doc__, +"read(fd, buffersize) -> string\n\n\ +Read a file descriptor."); + +static PyObject * +posix_read(PyObject *self, PyObject *args) +{ + int fd, size, n; + PyObject *buffer; + if (!PyArg_ParseTuple(args, "ii:read", &fd, &size)) + return NULL; + if (size < 0) { + errno = EINVAL; + return posix_error(); + } + buffer = PyString_FromStringAndSize((char *)NULL, size); + if (buffer == NULL) + return NULL; + Py_BEGIN_ALLOW_THREADS + n = read(fd, PyString_AsString(buffer), size); + Py_END_ALLOW_THREADS + if (n < 0) { + Py_DECREF(buffer); + return posix_error(); + } + if (n != size) + _PyString_Resize(&buffer, n); + return buffer; +} + + +PyDoc_STRVAR(posix_write__doc__, +"write(fd, string) -> byteswritten\n\n\ +Write a string to a file descriptor."); + +static PyObject * +posix_write(PyObject *self, PyObject *args) +{ + int fd; + Py_ssize_t size; + char *buffer; + + if (!PyArg_ParseTuple(args, "is#:write", &fd, &buffer, &size)) + return NULL; + Py_BEGIN_ALLOW_THREADS + size = write(fd, buffer, (size_t)size); + Py_END_ALLOW_THREADS + if (size < 0) + return posix_error(); + return PyInt_FromSsize_t(size); +} + + +PyDoc_STRVAR(posix_fstat__doc__, +"fstat(fd) -> stat result\n\n\ +Like stat(), but for an open file descriptor."); + +static PyObject * +posix_fstat(PyObject *self, PyObject *args) +{ + int fd; + STRUCT_STAT st; + int res; + if (!PyArg_ParseTuple(args, "i:fstat", &fd)) + return NULL; +#ifdef __VMS + /* on OpenVMS we must ensure that all bytes are written to the file */ + fsync(fd); +#endif + Py_BEGIN_ALLOW_THREADS + res = FSTAT(fd, &st); + Py_END_ALLOW_THREADS + if (res != 0) { +#ifdef MS_WINDOWS + return win32_error("fstat", NULL); +#else + return posix_error(); +#endif + } + + return _pystat_fromstructstat(&st); +} + + +PyDoc_STRVAR(posix_fdopen__doc__, +"fdopen(fd [, mode='r' [, bufsize]]) -> file_object\n\n\ +Return an open file object connected to a file descriptor."); + +static PyObject * +posix_fdopen(PyObject *self, PyObject *args) +{ + int fd; + char *mode = "r"; + int bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "i|si", &fd, &mode, &bufsize)) + return NULL; + + if (mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a') { + PyErr_Format(PyExc_ValueError, + "invalid file mode '%s'", mode); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + fp = fdopen(fd, mode); + Py_END_ALLOW_THREADS + if (fp == NULL) + return posix_error(); + f = PyFile_FromFile(fp, "", mode, fclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +PyDoc_STRVAR(posix_isatty__doc__, +"isatty(fd) -> bool\n\n\ +Return True if the file descriptor 'fd' is an open file descriptor\n\ +connected to the slave end of a terminal."); + +static PyObject * +posix_isatty(PyObject *self, PyObject *args) +{ + int fd; + if (!PyArg_ParseTuple(args, "i:isatty", &fd)) + return NULL; + return PyBool_FromLong(isatty(fd)); +} + +#ifdef HAVE_PIPE +PyDoc_STRVAR(posix_pipe__doc__, +"pipe() -> (read_end, write_end)\n\n\ +Create a pipe."); + +static PyObject * +posix_pipe(PyObject *self, PyObject *noargs) +{ +#if defined(PYOS_OS2) + HFILE read, write; + APIRET rc; + + Py_BEGIN_ALLOW_THREADS + rc = DosCreatePipe( &read, &write, 4096); + Py_END_ALLOW_THREADS + if (rc != NO_ERROR) + return os2_error(rc); + + return Py_BuildValue("(ii)", read, write); +#else +#if !defined(MS_WINDOWS) + int fds[2]; + int res; + Py_BEGIN_ALLOW_THREADS + res = pipe(fds); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error(); + return Py_BuildValue("(ii)", fds[0], fds[1]); +#else /* MS_WINDOWS */ + HANDLE read, write; + int read_fd, write_fd; + BOOL ok; + Py_BEGIN_ALLOW_THREADS + ok = CreatePipe(&read, &write, NULL, 0); + Py_END_ALLOW_THREADS + if (!ok) + return win32_error("CreatePipe", NULL); + read_fd = _open_osfhandle((Py_intptr_t)read, 0); + write_fd = _open_osfhandle((Py_intptr_t)write, 1); + return Py_BuildValue("(ii)", read_fd, write_fd); +#endif /* MS_WINDOWS */ +#endif +} +#endif /* HAVE_PIPE */ + + +#ifdef HAVE_MKFIFO +PyDoc_STRVAR(posix_mkfifo__doc__, +"mkfifo(filename [, mode=0666])\n\n\ +Create a FIFO (a POSIX named pipe)."); + +static PyObject * +posix_mkfifo(PyObject *self, PyObject *args) +{ + char *filename; + int mode = 0666; + int res; + if (!PyArg_ParseTuple(args, "s|i:mkfifo", &filename, &mode)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = mkfifo(filename, mode); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + + +#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) +PyDoc_STRVAR(posix_mknod__doc__, +"mknod(filename [, mode=0600, device])\n\n\ +Create a filesystem node (file, device special file or named pipe)\n\ +named filename. mode specifies both the permissions to use and the\n\ +type of node to be created, being combined (bitwise OR) with one of\n\ +S_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO. For S_IFCHR and S_IFBLK,\n\ +device defines the newly created device special file (probably using\n\ +os.makedev()), otherwise it is ignored."); + + +static PyObject * +posix_mknod(PyObject *self, PyObject *args) +{ + char *filename; + int mode = 0600; + int device = 0; + int res; + if (!PyArg_ParseTuple(args, "s|ii:mknod", &filename, &mode, &device)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = mknod(filename, mode, device); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_DEVICE_MACROS +PyDoc_STRVAR(posix_major__doc__, +"major(device) -> major number\n\ +Extracts a device major number from a raw device number."); + +static PyObject * +posix_major(PyObject *self, PyObject *args) +{ + int device; + if (!PyArg_ParseTuple(args, "i:major", &device)) + return NULL; + return PyInt_FromLong((long)major(device)); +} + +PyDoc_STRVAR(posix_minor__doc__, +"minor(device) -> minor number\n\ +Extracts a device minor number from a raw device number."); + +static PyObject * +posix_minor(PyObject *self, PyObject *args) +{ + int device; + if (!PyArg_ParseTuple(args, "i:minor", &device)) + return NULL; + return PyInt_FromLong((long)minor(device)); +} + +PyDoc_STRVAR(posix_makedev__doc__, +"makedev(major, minor) -> device number\n\ +Composes a raw device number from the major and minor device numbers."); + +static PyObject * +posix_makedev(PyObject *self, PyObject *args) +{ + int major, minor; + if (!PyArg_ParseTuple(args, "ii:makedev", &major, &minor)) + return NULL; + return PyInt_FromLong((long)makedev(major, minor)); +} +#endif /* device macros */ + + +#ifdef HAVE_FTRUNCATE +PyDoc_STRVAR(posix_ftruncate__doc__, +"ftruncate(fd, length)\n\n\ +Truncate a file to a specified length."); + +static PyObject * +posix_ftruncate(PyObject *self, PyObject *args) +{ + int fd; + off_t length; + int res; + PyObject *lenobj; + + if (!PyArg_ParseTuple(args, "iO:ftruncate", &fd, &lenobj)) + return NULL; + +#if !defined(HAVE_LARGEFILE_SUPPORT) + length = PyInt_AsLong(lenobj); +#else + length = PyLong_Check(lenobj) ? + PyLong_AsLongLong(lenobj) : PyInt_AsLong(lenobj); +#endif + if (PyErr_Occurred()) + return NULL; + + Py_BEGIN_ALLOW_THREADS + res = ftruncate(fd, length); + Py_END_ALLOW_THREADS + if (res < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_PUTENV +PyDoc_STRVAR(posix_putenv__doc__, +"putenv(key, value)\n\n\ +Change or add an environment variable."); + +/* Save putenv() parameters as values here, so we can collect them when they + * get re-set with another call for the same key. */ +static PyObject *posix_putenv_garbage; + +static PyObject * +posix_putenv(PyObject *self, PyObject *args) +{ + char *s1, *s2; + char *new; + PyObject *newstr; + size_t len; + + if (!PyArg_ParseTuple(args, "ss:putenv", &s1, &s2)) + return NULL; + +#if defined(PYOS_OS2) + if (stricmp(s1, "BEGINLIBPATH") == 0) { + APIRET rc; + + rc = DosSetExtLIBPATH(s2, BEGIN_LIBPATH); + if (rc != NO_ERROR) + return os2_error(rc); + + } else if (stricmp(s1, "ENDLIBPATH") == 0) { + APIRET rc; + + rc = DosSetExtLIBPATH(s2, END_LIBPATH); + if (rc != NO_ERROR) + return os2_error(rc); + } else { +#endif + + /* XXX This can leak memory -- not easy to fix :-( */ + len = strlen(s1) + strlen(s2) + 2; + /* len includes space for a trailing \0; the size arg to + PyString_FromStringAndSize does not count that */ + newstr = PyString_FromStringAndSize(NULL, (int)len - 1); + if (newstr == NULL) + return PyErr_NoMemory(); + new = PyString_AS_STRING(newstr); + PyOS_snprintf(new, len, "%s=%s", s1, s2); + if (putenv(new)) { + Py_DECREF(newstr); + posix_error(); + return NULL; + } + /* Install the first arg and newstr in posix_putenv_garbage; + * this will cause previous value to be collected. This has to + * happen after the real putenv() call because the old value + * was still accessible until then. */ + if (PyDict_SetItem(posix_putenv_garbage, + PyTuple_GET_ITEM(args, 0), newstr)) { + /* really not much we can do; just leak */ + PyErr_Clear(); + } + else { + Py_DECREF(newstr); + } + +#if defined(PYOS_OS2) + } +#endif + Py_INCREF(Py_None); + return Py_None; +} +#endif /* putenv */ + +#ifdef HAVE_UNSETENV +PyDoc_STRVAR(posix_unsetenv__doc__, +"unsetenv(key)\n\n\ +Delete an environment variable."); + +static PyObject * +posix_unsetenv(PyObject *self, PyObject *args) +{ + char *s1; + + if (!PyArg_ParseTuple(args, "s:unsetenv", &s1)) + return NULL; + + unsetenv(s1); + + /* Remove the key from posix_putenv_garbage; + * this will cause it to be collected. This has to + * happen after the real unsetenv() call because the + * old value was still accessible until then. + */ + if (PyDict_DelItem(posix_putenv_garbage, + PyTuple_GET_ITEM(args, 0))) { + /* really not much we can do; just leak */ + PyErr_Clear(); + } + + Py_INCREF(Py_None); + return Py_None; +} +#endif /* unsetenv */ + +#ifdef HAVE_STRERROR +PyDoc_STRVAR(posix_strerror__doc__, +"strerror(code) -> string\n\n\ +Translate an error code to a message string."); + +static PyObject * +posix_strerror(PyObject *self, PyObject *args) +{ + int code; + char *message; + if (!PyArg_ParseTuple(args, "i:strerror", &code)) + return NULL; + message = strerror(code); + if (message == NULL) { + PyErr_SetString(PyExc_ValueError, + "strerror() argument out of range"); + return NULL; + } + return PyString_FromString(message); +} +#endif /* strerror */ + + +#ifdef HAVE_SYS_WAIT_H + +#ifdef WCOREDUMP +PyDoc_STRVAR(posix_WCOREDUMP__doc__, +"WCOREDUMP(status) -> bool\n\n\ +Return True if the process returning 'status' was dumped to a core file."); + +static PyObject * +posix_WCOREDUMP(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WCOREDUMP", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WCOREDUMP(status)); +#undef status_i +} +#endif /* WCOREDUMP */ + +#ifdef WIFCONTINUED +PyDoc_STRVAR(posix_WIFCONTINUED__doc__, +"WIFCONTINUED(status) -> bool\n\n\ +Return True if the process returning 'status' was continued from a\n\ +job control stop."); + +static PyObject * +posix_WIFCONTINUED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WCONTINUED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFCONTINUED(status)); +#undef status_i +} +#endif /* WIFCONTINUED */ + +#ifdef WIFSTOPPED +PyDoc_STRVAR(posix_WIFSTOPPED__doc__, +"WIFSTOPPED(status) -> bool\n\n\ +Return True if the process returning 'status' was stopped."); + +static PyObject * +posix_WIFSTOPPED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WIFSTOPPED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFSTOPPED(status)); +#undef status_i +} +#endif /* WIFSTOPPED */ + +#ifdef WIFSIGNALED +PyDoc_STRVAR(posix_WIFSIGNALED__doc__, +"WIFSIGNALED(status) -> bool\n\n\ +Return True if the process returning 'status' was terminated by a signal."); + +static PyObject * +posix_WIFSIGNALED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WIFSIGNALED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFSIGNALED(status)); +#undef status_i +} +#endif /* WIFSIGNALED */ + +#ifdef WIFEXITED +PyDoc_STRVAR(posix_WIFEXITED__doc__, +"WIFEXITED(status) -> bool\n\n\ +Return true if the process returning 'status' exited using the exit()\n\ +system call."); + +static PyObject * +posix_WIFEXITED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WIFEXITED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFEXITED(status)); +#undef status_i +} +#endif /* WIFEXITED */ + +#ifdef WEXITSTATUS +PyDoc_STRVAR(posix_WEXITSTATUS__doc__, +"WEXITSTATUS(status) -> integer\n\n\ +Return the process return code from 'status'."); + +static PyObject * +posix_WEXITSTATUS(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WEXITSTATUS", &status_i)) + { + return NULL; + } + + return Py_BuildValue("i", WEXITSTATUS(status)); +#undef status_i +} +#endif /* WEXITSTATUS */ + +#ifdef WTERMSIG +PyDoc_STRVAR(posix_WTERMSIG__doc__, +"WTERMSIG(status) -> integer\n\n\ +Return the signal that terminated the process that provided the 'status'\n\ +value."); + +static PyObject * +posix_WTERMSIG(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WTERMSIG", &status_i)) + { + return NULL; + } + + return Py_BuildValue("i", WTERMSIG(status)); +#undef status_i +} +#endif /* WTERMSIG */ + +#ifdef WSTOPSIG +PyDoc_STRVAR(posix_WSTOPSIG__doc__, +"WSTOPSIG(status) -> integer\n\n\ +Return the signal that stopped the process that provided\n\ +the 'status' value."); + +static PyObject * +posix_WSTOPSIG(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WSTOPSIG", &status_i)) + { + return NULL; + } + + return Py_BuildValue("i", WSTOPSIG(status)); +#undef status_i +} +#endif /* WSTOPSIG */ + +#endif /* HAVE_SYS_WAIT_H */ + + +#if defined(HAVE_FSTATVFS) +#ifdef _SCO_DS +/* SCO OpenServer 5.0 and later requires _SVID3 before it reveals the + needed definitions in sys/statvfs.h */ +#define _SVID3 +#endif +#include + +static PyObject* +_pystatvfs_fromstructstatvfs(struct statvfs st) { + PyObject *v = PyStructSequence_New(&StatVFSResultType); + if (v == NULL) + return NULL; + +#if !defined(HAVE_LARGEFILE_SUPPORT) + PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long) st.f_bsize)); + PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long) st.f_frsize)); + PyStructSequence_SET_ITEM(v, 2, PyInt_FromLong((long) st.f_blocks)); + PyStructSequence_SET_ITEM(v, 3, PyInt_FromLong((long) st.f_bfree)); + PyStructSequence_SET_ITEM(v, 4, PyInt_FromLong((long) st.f_bavail)); + PyStructSequence_SET_ITEM(v, 5, PyInt_FromLong((long) st.f_files)); + PyStructSequence_SET_ITEM(v, 6, PyInt_FromLong((long) st.f_ffree)); + PyStructSequence_SET_ITEM(v, 7, PyInt_FromLong((long) st.f_favail)); + PyStructSequence_SET_ITEM(v, 8, PyInt_FromLong((long) st.f_flag)); + PyStructSequence_SET_ITEM(v, 9, PyInt_FromLong((long) st.f_namemax)); +#else + PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long) st.f_bsize)); + PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long) st.f_frsize)); + PyStructSequence_SET_ITEM(v, 2, + PyLong_FromLongLong((PY_LONG_LONG) st.f_blocks)); + PyStructSequence_SET_ITEM(v, 3, + PyLong_FromLongLong((PY_LONG_LONG) st.f_bfree)); + PyStructSequence_SET_ITEM(v, 4, + PyLong_FromLongLong((PY_LONG_LONG) st.f_bavail)); + PyStructSequence_SET_ITEM(v, 5, + PyLong_FromLongLong((PY_LONG_LONG) st.f_files)); + PyStructSequence_SET_ITEM(v, 6, + PyLong_FromLongLong((PY_LONG_LONG) st.f_ffree)); + PyStructSequence_SET_ITEM(v, 7, + PyLong_FromLongLong((PY_LONG_LONG) st.f_favail)); + PyStructSequence_SET_ITEM(v, 8, PyInt_FromLong((long) st.f_flag)); + PyStructSequence_SET_ITEM(v, 9, PyInt_FromLong((long) st.f_namemax)); +#endif + + return v; +} + +PyDoc_STRVAR(posix_fstatvfs__doc__, +"fstatvfs(fd) -> statvfs result\n\n\ +Perform an fstatvfs system call on the given fd."); + +static PyObject * +posix_fstatvfs(PyObject *self, PyObject *args) +{ + int fd, res; + struct statvfs st; + + if (!PyArg_ParseTuple(args, "i:fstatvfs", &fd)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = fstatvfs(fd, &st); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error(); + + return _pystatvfs_fromstructstatvfs(st); +} +#endif /* HAVE_FSTATVFS */ + + +#if defined(HAVE_STATVFS) +#include + +PyDoc_STRVAR(posix_statvfs__doc__, +"statvfs(path) -> statvfs result\n\n\ +Perform a statvfs system call on the given path."); + +static PyObject * +posix_statvfs(PyObject *self, PyObject *args) +{ + char *path; + int res; + struct statvfs st; + if (!PyArg_ParseTuple(args, "s:statvfs", &path)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = statvfs(path, &st); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error_with_filename(path); + + return _pystatvfs_fromstructstatvfs(st); +} +#endif /* HAVE_STATVFS */ + + +#ifdef HAVE_TEMPNAM +PyDoc_STRVAR(posix_tempnam__doc__, +"tempnam([dir[, prefix]]) -> string\n\n\ +Return a unique name for a temporary file.\n\ +The directory and a prefix may be specified as strings; they may be omitted\n\ +or None if not needed."); + +static PyObject * +posix_tempnam(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + char *dir = NULL; + char *pfx = NULL; + char *name; + + if (!PyArg_ParseTuple(args, "|zz:tempnam", &dir, &pfx)) + return NULL; + + if (PyErr_Warn(PyExc_RuntimeWarning, + "tempnam is a potential security risk to your program") < 0) + return NULL; + +#ifdef MS_WINDOWS + name = _tempnam(dir, pfx); +#else + name = tempnam(dir, pfx); +#endif + if (name == NULL) + return PyErr_NoMemory(); + result = PyString_FromString(name); + free(name); + return result; +} +#endif + + +#ifdef HAVE_TMPFILE +PyDoc_STRVAR(posix_tmpfile__doc__, +"tmpfile() -> file object\n\n\ +Create a temporary file with no directory entries."); + +static PyObject * +posix_tmpfile(PyObject *self, PyObject *noargs) +{ + FILE *fp; + + fp = tmpfile(); + if (fp == NULL) + return posix_error(); + return PyFile_FromFile(fp, "", "w+b", fclose); +} +#endif + + +#ifdef HAVE_TMPNAM +PyDoc_STRVAR(posix_tmpnam__doc__, +"tmpnam() -> string\n\n\ +Return a unique name for a temporary file."); + +static PyObject * +posix_tmpnam(PyObject *self, PyObject *noargs) +{ + char buffer[L_tmpnam]; + char *name; + + if (PyErr_Warn(PyExc_RuntimeWarning, + "tmpnam is a potential security risk to your program") < 0) + return NULL; + +#ifdef USE_TMPNAM_R + name = tmpnam_r(buffer); +#else + name = tmpnam(buffer); +#endif + if (name == NULL) { + PyErr_SetObject(PyExc_OSError, + Py_BuildValue("is", 0, +#ifdef USE_TMPNAM_R + "unexpected NULL from tmpnam_r" +#else + "unexpected NULL from tmpnam" +#endif + )); + return NULL; + } + return PyString_FromString(buffer); +} +#endif + + +/* This is used for fpathconf(), pathconf(), confstr() and sysconf(). + * It maps strings representing configuration variable names to + * integer values, allowing those functions to be called with the + * magic names instead of polluting the module's namespace with tons of + * rarely-used constants. There are three separate tables that use + * these definitions. + * + * This code is always included, even if none of the interfaces that + * need it are included. The #if hackery needed to avoid it would be + * sufficiently pervasive that it's not worth the loss of readability. + */ +struct constdef { + char *name; + long value; +}; + +static int +conv_confname(PyObject *arg, int *valuep, struct constdef *table, + size_t tablesize) +{ + if (PyInt_Check(arg)) { + *valuep = PyInt_AS_LONG(arg); + return 1; + } + if (PyString_Check(arg)) { + /* look up the value in the table using a binary search */ + size_t lo = 0; + size_t mid; + size_t hi = tablesize; + int cmp; + char *confname = PyString_AS_STRING(arg); + while (lo < hi) { + mid = (lo + hi) / 2; + cmp = strcmp(confname, table[mid].name); + if (cmp < 0) + hi = mid; + else if (cmp > 0) + lo = mid + 1; + else { + *valuep = table[mid].value; + return 1; + } + } + PyErr_SetString(PyExc_ValueError, "unrecognized configuration name"); + } + else + PyErr_SetString(PyExc_TypeError, + "configuration names must be strings or integers"); + return 0; +} + + +#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF) +static struct constdef posix_constants_pathconf[] = { +#ifdef _PC_ABI_AIO_XFER_MAX + {"PC_ABI_AIO_XFER_MAX", _PC_ABI_AIO_XFER_MAX}, +#endif +#ifdef _PC_ABI_ASYNC_IO + {"PC_ABI_ASYNC_IO", _PC_ABI_ASYNC_IO}, +#endif +#ifdef _PC_ASYNC_IO + {"PC_ASYNC_IO", _PC_ASYNC_IO}, +#endif +#ifdef _PC_CHOWN_RESTRICTED + {"PC_CHOWN_RESTRICTED", _PC_CHOWN_RESTRICTED}, +#endif +#ifdef _PC_FILESIZEBITS + {"PC_FILESIZEBITS", _PC_FILESIZEBITS}, +#endif +#ifdef _PC_LAST + {"PC_LAST", _PC_LAST}, +#endif +#ifdef _PC_LINK_MAX + {"PC_LINK_MAX", _PC_LINK_MAX}, +#endif +#ifdef _PC_MAX_CANON + {"PC_MAX_CANON", _PC_MAX_CANON}, +#endif +#ifdef _PC_MAX_INPUT + {"PC_MAX_INPUT", _PC_MAX_INPUT}, +#endif +#ifdef _PC_NAME_MAX + {"PC_NAME_MAX", _PC_NAME_MAX}, +#endif +#ifdef _PC_NO_TRUNC + {"PC_NO_TRUNC", _PC_NO_TRUNC}, +#endif +#ifdef _PC_PATH_MAX + {"PC_PATH_MAX", _PC_PATH_MAX}, +#endif +#ifdef _PC_PIPE_BUF + {"PC_PIPE_BUF", _PC_PIPE_BUF}, +#endif +#ifdef _PC_PRIO_IO + {"PC_PRIO_IO", _PC_PRIO_IO}, +#endif +#ifdef _PC_SOCK_MAXBUF + {"PC_SOCK_MAXBUF", _PC_SOCK_MAXBUF}, +#endif +#ifdef _PC_SYNC_IO + {"PC_SYNC_IO", _PC_SYNC_IO}, +#endif +#ifdef _PC_VDISABLE + {"PC_VDISABLE", _PC_VDISABLE}, +#endif +}; + +static int +conv_path_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_pathconf, + sizeof(posix_constants_pathconf) + / sizeof(struct constdef)); +} +#endif + +#ifdef HAVE_FPATHCONF +PyDoc_STRVAR(posix_fpathconf__doc__, +"fpathconf(fd, name) -> integer\n\n\ +Return the configuration limit name for the file descriptor fd.\n\ +If there is no limit, return -1."); + +static PyObject * +posix_fpathconf(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name, fd; + + if (PyArg_ParseTuple(args, "iO&:fpathconf", &fd, + conv_path_confname, &name)) { + long limit; + + errno = 0; + limit = fpathconf(fd, name); + if (limit == -1 && errno != 0) + posix_error(); + else + result = PyInt_FromLong(limit); + } + return result; +} +#endif + + +#ifdef HAVE_PATHCONF +PyDoc_STRVAR(posix_pathconf__doc__, +"pathconf(path, name) -> integer\n\n\ +Return the configuration limit name for the file or directory path.\n\ +If there is no limit, return -1."); + +static PyObject * +posix_pathconf(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name; + char *path; + + if (PyArg_ParseTuple(args, "sO&:pathconf", &path, + conv_path_confname, &name)) { + long limit; + + errno = 0; + limit = pathconf(path, name); + if (limit == -1 && errno != 0) { + if (errno == EINVAL) + /* could be a path or name problem */ + posix_error(); + else + posix_error_with_filename(path); + } + else + result = PyInt_FromLong(limit); + } + return result; +} +#endif + +#ifdef HAVE_CONFSTR +static struct constdef posix_constants_confstr[] = { +#ifdef _CS_ARCHITECTURE + {"CS_ARCHITECTURE", _CS_ARCHITECTURE}, +#endif +#ifdef _CS_HOSTNAME + {"CS_HOSTNAME", _CS_HOSTNAME}, +#endif +#ifdef _CS_HW_PROVIDER + {"CS_HW_PROVIDER", _CS_HW_PROVIDER}, +#endif +#ifdef _CS_HW_SERIAL + {"CS_HW_SERIAL", _CS_HW_SERIAL}, +#endif +#ifdef _CS_INITTAB_NAME + {"CS_INITTAB_NAME", _CS_INITTAB_NAME}, +#endif +#ifdef _CS_LFS64_CFLAGS + {"CS_LFS64_CFLAGS", _CS_LFS64_CFLAGS}, +#endif +#ifdef _CS_LFS64_LDFLAGS + {"CS_LFS64_LDFLAGS", _CS_LFS64_LDFLAGS}, +#endif +#ifdef _CS_LFS64_LIBS + {"CS_LFS64_LIBS", _CS_LFS64_LIBS}, +#endif +#ifdef _CS_LFS64_LINTFLAGS + {"CS_LFS64_LINTFLAGS", _CS_LFS64_LINTFLAGS}, +#endif +#ifdef _CS_LFS_CFLAGS + {"CS_LFS_CFLAGS", _CS_LFS_CFLAGS}, +#endif +#ifdef _CS_LFS_LDFLAGS + {"CS_LFS_LDFLAGS", _CS_LFS_LDFLAGS}, +#endif +#ifdef _CS_LFS_LIBS + {"CS_LFS_LIBS", _CS_LFS_LIBS}, +#endif +#ifdef _CS_LFS_LINTFLAGS + {"CS_LFS_LINTFLAGS", _CS_LFS_LINTFLAGS}, +#endif +#ifdef _CS_MACHINE + {"CS_MACHINE", _CS_MACHINE}, +#endif +#ifdef _CS_PATH + {"CS_PATH", _CS_PATH}, +#endif +#ifdef _CS_RELEASE + {"CS_RELEASE", _CS_RELEASE}, +#endif +#ifdef _CS_SRPC_DOMAIN + {"CS_SRPC_DOMAIN", _CS_SRPC_DOMAIN}, +#endif +#ifdef _CS_SYSNAME + {"CS_SYSNAME", _CS_SYSNAME}, +#endif +#ifdef _CS_VERSION + {"CS_VERSION", _CS_VERSION}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_CFLAGS + {"CS_XBS5_ILP32_OFF32_CFLAGS", _CS_XBS5_ILP32_OFF32_CFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LDFLAGS + {"CS_XBS5_ILP32_OFF32_LDFLAGS", _CS_XBS5_ILP32_OFF32_LDFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LIBS + {"CS_XBS5_ILP32_OFF32_LIBS", _CS_XBS5_ILP32_OFF32_LIBS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LINTFLAGS + {"CS_XBS5_ILP32_OFF32_LINTFLAGS", _CS_XBS5_ILP32_OFF32_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_CFLAGS + {"CS_XBS5_ILP32_OFFBIG_CFLAGS", _CS_XBS5_ILP32_OFFBIG_CFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LDFLAGS + {"CS_XBS5_ILP32_OFFBIG_LDFLAGS", _CS_XBS5_ILP32_OFFBIG_LDFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LIBS + {"CS_XBS5_ILP32_OFFBIG_LIBS", _CS_XBS5_ILP32_OFFBIG_LIBS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LINTFLAGS + {"CS_XBS5_ILP32_OFFBIG_LINTFLAGS", _CS_XBS5_ILP32_OFFBIG_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_CFLAGS + {"CS_XBS5_LP64_OFF64_CFLAGS", _CS_XBS5_LP64_OFF64_CFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LDFLAGS + {"CS_XBS5_LP64_OFF64_LDFLAGS", _CS_XBS5_LP64_OFF64_LDFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LIBS + {"CS_XBS5_LP64_OFF64_LIBS", _CS_XBS5_LP64_OFF64_LIBS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LINTFLAGS + {"CS_XBS5_LP64_OFF64_LINTFLAGS", _CS_XBS5_LP64_OFF64_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_CFLAGS + {"CS_XBS5_LPBIG_OFFBIG_CFLAGS", _CS_XBS5_LPBIG_OFFBIG_CFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LDFLAGS + {"CS_XBS5_LPBIG_OFFBIG_LDFLAGS", _CS_XBS5_LPBIG_OFFBIG_LDFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LIBS + {"CS_XBS5_LPBIG_OFFBIG_LIBS", _CS_XBS5_LPBIG_OFFBIG_LIBS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS + {"CS_XBS5_LPBIG_OFFBIG_LINTFLAGS", _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS}, +#endif +#ifdef _MIPS_CS_AVAIL_PROCESSORS + {"MIPS_CS_AVAIL_PROCESSORS", _MIPS_CS_AVAIL_PROCESSORS}, +#endif +#ifdef _MIPS_CS_BASE + {"MIPS_CS_BASE", _MIPS_CS_BASE}, +#endif +#ifdef _MIPS_CS_HOSTID + {"MIPS_CS_HOSTID", _MIPS_CS_HOSTID}, +#endif +#ifdef _MIPS_CS_HW_NAME + {"MIPS_CS_HW_NAME", _MIPS_CS_HW_NAME}, +#endif +#ifdef _MIPS_CS_NUM_PROCESSORS + {"MIPS_CS_NUM_PROCESSORS", _MIPS_CS_NUM_PROCESSORS}, +#endif +#ifdef _MIPS_CS_OSREL_MAJ + {"MIPS_CS_OSREL_MAJ", _MIPS_CS_OSREL_MAJ}, +#endif +#ifdef _MIPS_CS_OSREL_MIN + {"MIPS_CS_OSREL_MIN", _MIPS_CS_OSREL_MIN}, +#endif +#ifdef _MIPS_CS_OSREL_PATCH + {"MIPS_CS_OSREL_PATCH", _MIPS_CS_OSREL_PATCH}, +#endif +#ifdef _MIPS_CS_OS_NAME + {"MIPS_CS_OS_NAME", _MIPS_CS_OS_NAME}, +#endif +#ifdef _MIPS_CS_OS_PROVIDER + {"MIPS_CS_OS_PROVIDER", _MIPS_CS_OS_PROVIDER}, +#endif +#ifdef _MIPS_CS_PROCESSORS + {"MIPS_CS_PROCESSORS", _MIPS_CS_PROCESSORS}, +#endif +#ifdef _MIPS_CS_SERIAL + {"MIPS_CS_SERIAL", _MIPS_CS_SERIAL}, +#endif +#ifdef _MIPS_CS_VENDOR + {"MIPS_CS_VENDOR", _MIPS_CS_VENDOR}, +#endif +}; + +static int +conv_confstr_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_confstr, + sizeof(posix_constants_confstr) + / sizeof(struct constdef)); +} + +PyDoc_STRVAR(posix_confstr__doc__, +"confstr(name) -> string\n\n\ +Return a string-valued system configuration variable."); + +static PyObject * +posix_confstr(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name; + char buffer[64]; + + if (PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name)) { + int len = confstr(name, buffer, sizeof(buffer)); + + errno = 0; + if (len == 0) { + if (errno != 0) + posix_error(); + else + result = PyString_FromString(""); + } + else { + if (len >= sizeof(buffer)) { + result = PyString_FromStringAndSize(NULL, len); + if (result != NULL) + confstr(name, PyString_AS_STRING(result), len+1); + } + else + result = PyString_FromString(buffer); + } + } + return result; +} +#endif + + +#ifdef HAVE_SYSCONF +static struct constdef posix_constants_sysconf[] = { +#ifdef _SC_2_CHAR_TERM + {"SC_2_CHAR_TERM", _SC_2_CHAR_TERM}, +#endif +#ifdef _SC_2_C_BIND + {"SC_2_C_BIND", _SC_2_C_BIND}, +#endif +#ifdef _SC_2_C_DEV + {"SC_2_C_DEV", _SC_2_C_DEV}, +#endif +#ifdef _SC_2_C_VERSION + {"SC_2_C_VERSION", _SC_2_C_VERSION}, +#endif +#ifdef _SC_2_FORT_DEV + {"SC_2_FORT_DEV", _SC_2_FORT_DEV}, +#endif +#ifdef _SC_2_FORT_RUN + {"SC_2_FORT_RUN", _SC_2_FORT_RUN}, +#endif +#ifdef _SC_2_LOCALEDEF + {"SC_2_LOCALEDEF", _SC_2_LOCALEDEF}, +#endif +#ifdef _SC_2_SW_DEV + {"SC_2_SW_DEV", _SC_2_SW_DEV}, +#endif +#ifdef _SC_2_UPE + {"SC_2_UPE", _SC_2_UPE}, +#endif +#ifdef _SC_2_VERSION + {"SC_2_VERSION", _SC_2_VERSION}, +#endif +#ifdef _SC_ABI_ASYNCHRONOUS_IO + {"SC_ABI_ASYNCHRONOUS_IO", _SC_ABI_ASYNCHRONOUS_IO}, +#endif +#ifdef _SC_ACL + {"SC_ACL", _SC_ACL}, +#endif +#ifdef _SC_AIO_LISTIO_MAX + {"SC_AIO_LISTIO_MAX", _SC_AIO_LISTIO_MAX}, +#endif +#ifdef _SC_AIO_MAX + {"SC_AIO_MAX", _SC_AIO_MAX}, +#endif +#ifdef _SC_AIO_PRIO_DELTA_MAX + {"SC_AIO_PRIO_DELTA_MAX", _SC_AIO_PRIO_DELTA_MAX}, +#endif +#ifdef _SC_ARG_MAX + {"SC_ARG_MAX", _SC_ARG_MAX}, +#endif +#ifdef _SC_ASYNCHRONOUS_IO + {"SC_ASYNCHRONOUS_IO", _SC_ASYNCHRONOUS_IO}, +#endif +#ifdef _SC_ATEXIT_MAX + {"SC_ATEXIT_MAX", _SC_ATEXIT_MAX}, +#endif +#ifdef _SC_AUDIT + {"SC_AUDIT", _SC_AUDIT}, +#endif +#ifdef _SC_AVPHYS_PAGES + {"SC_AVPHYS_PAGES", _SC_AVPHYS_PAGES}, +#endif +#ifdef _SC_BC_BASE_MAX + {"SC_BC_BASE_MAX", _SC_BC_BASE_MAX}, +#endif +#ifdef _SC_BC_DIM_MAX + {"SC_BC_DIM_MAX", _SC_BC_DIM_MAX}, +#endif +#ifdef _SC_BC_SCALE_MAX + {"SC_BC_SCALE_MAX", _SC_BC_SCALE_MAX}, +#endif +#ifdef _SC_BC_STRING_MAX + {"SC_BC_STRING_MAX", _SC_BC_STRING_MAX}, +#endif +#ifdef _SC_CAP + {"SC_CAP", _SC_CAP}, +#endif +#ifdef _SC_CHARCLASS_NAME_MAX + {"SC_CHARCLASS_NAME_MAX", _SC_CHARCLASS_NAME_MAX}, +#endif +#ifdef _SC_CHAR_BIT + {"SC_CHAR_BIT", _SC_CHAR_BIT}, +#endif +#ifdef _SC_CHAR_MAX + {"SC_CHAR_MAX", _SC_CHAR_MAX}, +#endif +#ifdef _SC_CHAR_MIN + {"SC_CHAR_MIN", _SC_CHAR_MIN}, +#endif +#ifdef _SC_CHILD_MAX + {"SC_CHILD_MAX", _SC_CHILD_MAX}, +#endif +#ifdef _SC_CLK_TCK + {"SC_CLK_TCK", _SC_CLK_TCK}, +#endif +#ifdef _SC_COHER_BLKSZ + {"SC_COHER_BLKSZ", _SC_COHER_BLKSZ}, +#endif +#ifdef _SC_COLL_WEIGHTS_MAX + {"SC_COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX}, +#endif +#ifdef _SC_DCACHE_ASSOC + {"SC_DCACHE_ASSOC", _SC_DCACHE_ASSOC}, +#endif +#ifdef _SC_DCACHE_BLKSZ + {"SC_DCACHE_BLKSZ", _SC_DCACHE_BLKSZ}, +#endif +#ifdef _SC_DCACHE_LINESZ + {"SC_DCACHE_LINESZ", _SC_DCACHE_LINESZ}, +#endif +#ifdef _SC_DCACHE_SZ + {"SC_DCACHE_SZ", _SC_DCACHE_SZ}, +#endif +#ifdef _SC_DCACHE_TBLKSZ + {"SC_DCACHE_TBLKSZ", _SC_DCACHE_TBLKSZ}, +#endif +#ifdef _SC_DELAYTIMER_MAX + {"SC_DELAYTIMER_MAX", _SC_DELAYTIMER_MAX}, +#endif +#ifdef _SC_EQUIV_CLASS_MAX + {"SC_EQUIV_CLASS_MAX", _SC_EQUIV_CLASS_MAX}, +#endif +#ifdef _SC_EXPR_NEST_MAX + {"SC_EXPR_NEST_MAX", _SC_EXPR_NEST_MAX}, +#endif +#ifdef _SC_FSYNC + {"SC_FSYNC", _SC_FSYNC}, +#endif +#ifdef _SC_GETGR_R_SIZE_MAX + {"SC_GETGR_R_SIZE_MAX", _SC_GETGR_R_SIZE_MAX}, +#endif +#ifdef _SC_GETPW_R_SIZE_MAX + {"SC_GETPW_R_SIZE_MAX", _SC_GETPW_R_SIZE_MAX}, +#endif +#ifdef _SC_ICACHE_ASSOC + {"SC_ICACHE_ASSOC", _SC_ICACHE_ASSOC}, +#endif +#ifdef _SC_ICACHE_BLKSZ + {"SC_ICACHE_BLKSZ", _SC_ICACHE_BLKSZ}, +#endif +#ifdef _SC_ICACHE_LINESZ + {"SC_ICACHE_LINESZ", _SC_ICACHE_LINESZ}, +#endif +#ifdef _SC_ICACHE_SZ + {"SC_ICACHE_SZ", _SC_ICACHE_SZ}, +#endif +#ifdef _SC_INF + {"SC_INF", _SC_INF}, +#endif +#ifdef _SC_INT_MAX + {"SC_INT_MAX", _SC_INT_MAX}, +#endif +#ifdef _SC_INT_MIN + {"SC_INT_MIN", _SC_INT_MIN}, +#endif +#ifdef _SC_IOV_MAX + {"SC_IOV_MAX", _SC_IOV_MAX}, +#endif +#ifdef _SC_IP_SECOPTS + {"SC_IP_SECOPTS", _SC_IP_SECOPTS}, +#endif +#ifdef _SC_JOB_CONTROL + {"SC_JOB_CONTROL", _SC_JOB_CONTROL}, +#endif +#ifdef _SC_KERN_POINTERS + {"SC_KERN_POINTERS", _SC_KERN_POINTERS}, +#endif +#ifdef _SC_KERN_SIM + {"SC_KERN_SIM", _SC_KERN_SIM}, +#endif +#ifdef _SC_LINE_MAX + {"SC_LINE_MAX", _SC_LINE_MAX}, +#endif +#ifdef _SC_LOGIN_NAME_MAX + {"SC_LOGIN_NAME_MAX", _SC_LOGIN_NAME_MAX}, +#endif +#ifdef _SC_LOGNAME_MAX + {"SC_LOGNAME_MAX", _SC_LOGNAME_MAX}, +#endif +#ifdef _SC_LONG_BIT + {"SC_LONG_BIT", _SC_LONG_BIT}, +#endif +#ifdef _SC_MAC + {"SC_MAC", _SC_MAC}, +#endif +#ifdef _SC_MAPPED_FILES + {"SC_MAPPED_FILES", _SC_MAPPED_FILES}, +#endif +#ifdef _SC_MAXPID + {"SC_MAXPID", _SC_MAXPID}, +#endif +#ifdef _SC_MB_LEN_MAX + {"SC_MB_LEN_MAX", _SC_MB_LEN_MAX}, +#endif +#ifdef _SC_MEMLOCK + {"SC_MEMLOCK", _SC_MEMLOCK}, +#endif +#ifdef _SC_MEMLOCK_RANGE + {"SC_MEMLOCK_RANGE", _SC_MEMLOCK_RANGE}, +#endif +#ifdef _SC_MEMORY_PROTECTION + {"SC_MEMORY_PROTECTION", _SC_MEMORY_PROTECTION}, +#endif +#ifdef _SC_MESSAGE_PASSING + {"SC_MESSAGE_PASSING", _SC_MESSAGE_PASSING}, +#endif +#ifdef _SC_MMAP_FIXED_ALIGNMENT + {"SC_MMAP_FIXED_ALIGNMENT", _SC_MMAP_FIXED_ALIGNMENT}, +#endif +#ifdef _SC_MQ_OPEN_MAX + {"SC_MQ_OPEN_MAX", _SC_MQ_OPEN_MAX}, +#endif +#ifdef _SC_MQ_PRIO_MAX + {"SC_MQ_PRIO_MAX", _SC_MQ_PRIO_MAX}, +#endif +#ifdef _SC_NACLS_MAX + {"SC_NACLS_MAX", _SC_NACLS_MAX}, +#endif +#ifdef _SC_NGROUPS_MAX + {"SC_NGROUPS_MAX", _SC_NGROUPS_MAX}, +#endif +#ifdef _SC_NL_ARGMAX + {"SC_NL_ARGMAX", _SC_NL_ARGMAX}, +#endif +#ifdef _SC_NL_LANGMAX + {"SC_NL_LANGMAX", _SC_NL_LANGMAX}, +#endif +#ifdef _SC_NL_MSGMAX + {"SC_NL_MSGMAX", _SC_NL_MSGMAX}, +#endif +#ifdef _SC_NL_NMAX + {"SC_NL_NMAX", _SC_NL_NMAX}, +#endif +#ifdef _SC_NL_SETMAX + {"SC_NL_SETMAX", _SC_NL_SETMAX}, +#endif +#ifdef _SC_NL_TEXTMAX + {"SC_NL_TEXTMAX", _SC_NL_TEXTMAX}, +#endif +#ifdef _SC_NPROCESSORS_CONF + {"SC_NPROCESSORS_CONF", _SC_NPROCESSORS_CONF}, +#endif +#ifdef _SC_NPROCESSORS_ONLN + {"SC_NPROCESSORS_ONLN", _SC_NPROCESSORS_ONLN}, +#endif +#ifdef _SC_NPROC_CONF + {"SC_NPROC_CONF", _SC_NPROC_CONF}, +#endif +#ifdef _SC_NPROC_ONLN + {"SC_NPROC_ONLN", _SC_NPROC_ONLN}, +#endif +#ifdef _SC_NZERO + {"SC_NZERO", _SC_NZERO}, +#endif +#ifdef _SC_OPEN_MAX + {"SC_OPEN_MAX", _SC_OPEN_MAX}, +#endif +#ifdef _SC_PAGESIZE + {"SC_PAGESIZE", _SC_PAGESIZE}, +#endif +#ifdef _SC_PAGE_SIZE + {"SC_PAGE_SIZE", _SC_PAGE_SIZE}, +#endif +#ifdef _SC_PASS_MAX + {"SC_PASS_MAX", _SC_PASS_MAX}, +#endif +#ifdef _SC_PHYS_PAGES + {"SC_PHYS_PAGES", _SC_PHYS_PAGES}, +#endif +#ifdef _SC_PII + {"SC_PII", _SC_PII}, +#endif +#ifdef _SC_PII_INTERNET + {"SC_PII_INTERNET", _SC_PII_INTERNET}, +#endif +#ifdef _SC_PII_INTERNET_DGRAM + {"SC_PII_INTERNET_DGRAM", _SC_PII_INTERNET_DGRAM}, +#endif +#ifdef _SC_PII_INTERNET_STREAM + {"SC_PII_INTERNET_STREAM", _SC_PII_INTERNET_STREAM}, +#endif +#ifdef _SC_PII_OSI + {"SC_PII_OSI", _SC_PII_OSI}, +#endif +#ifdef _SC_PII_OSI_CLTS + {"SC_PII_OSI_CLTS", _SC_PII_OSI_CLTS}, +#endif +#ifdef _SC_PII_OSI_COTS + {"SC_PII_OSI_COTS", _SC_PII_OSI_COTS}, +#endif +#ifdef _SC_PII_OSI_M + {"SC_PII_OSI_M", _SC_PII_OSI_M}, +#endif +#ifdef _SC_PII_SOCKET + {"SC_PII_SOCKET", _SC_PII_SOCKET}, +#endif +#ifdef _SC_PII_XTI + {"SC_PII_XTI", _SC_PII_XTI}, +#endif +#ifdef _SC_POLL + {"SC_POLL", _SC_POLL}, +#endif +#ifdef _SC_PRIORITIZED_IO + {"SC_PRIORITIZED_IO", _SC_PRIORITIZED_IO}, +#endif +#ifdef _SC_PRIORITY_SCHEDULING + {"SC_PRIORITY_SCHEDULING", _SC_PRIORITY_SCHEDULING}, +#endif +#ifdef _SC_REALTIME_SIGNALS + {"SC_REALTIME_SIGNALS", _SC_REALTIME_SIGNALS}, +#endif +#ifdef _SC_RE_DUP_MAX + {"SC_RE_DUP_MAX", _SC_RE_DUP_MAX}, +#endif +#ifdef _SC_RTSIG_MAX + {"SC_RTSIG_MAX", _SC_RTSIG_MAX}, +#endif +#ifdef _SC_SAVED_IDS + {"SC_SAVED_IDS", _SC_SAVED_IDS}, +#endif +#ifdef _SC_SCHAR_MAX + {"SC_SCHAR_MAX", _SC_SCHAR_MAX}, +#endif +#ifdef _SC_SCHAR_MIN + {"SC_SCHAR_MIN", _SC_SCHAR_MIN}, +#endif +#ifdef _SC_SELECT + {"SC_SELECT", _SC_SELECT}, +#endif +#ifdef _SC_SEMAPHORES + {"SC_SEMAPHORES", _SC_SEMAPHORES}, +#endif +#ifdef _SC_SEM_NSEMS_MAX + {"SC_SEM_NSEMS_MAX", _SC_SEM_NSEMS_MAX}, +#endif +#ifdef _SC_SEM_VALUE_MAX + {"SC_SEM_VALUE_MAX", _SC_SEM_VALUE_MAX}, +#endif +#ifdef _SC_SHARED_MEMORY_OBJECTS + {"SC_SHARED_MEMORY_OBJECTS", _SC_SHARED_MEMORY_OBJECTS}, +#endif +#ifdef _SC_SHRT_MAX + {"SC_SHRT_MAX", _SC_SHRT_MAX}, +#endif +#ifdef _SC_SHRT_MIN + {"SC_SHRT_MIN", _SC_SHRT_MIN}, +#endif +#ifdef _SC_SIGQUEUE_MAX + {"SC_SIGQUEUE_MAX", _SC_SIGQUEUE_MAX}, +#endif +#ifdef _SC_SIGRT_MAX + {"SC_SIGRT_MAX", _SC_SIGRT_MAX}, +#endif +#ifdef _SC_SIGRT_MIN + {"SC_SIGRT_MIN", _SC_SIGRT_MIN}, +#endif +#ifdef _SC_SOFTPOWER + {"SC_SOFTPOWER", _SC_SOFTPOWER}, +#endif +#ifdef _SC_SPLIT_CACHE + {"SC_SPLIT_CACHE", _SC_SPLIT_CACHE}, +#endif +#ifdef _SC_SSIZE_MAX + {"SC_SSIZE_MAX", _SC_SSIZE_MAX}, +#endif +#ifdef _SC_STACK_PROT + {"SC_STACK_PROT", _SC_STACK_PROT}, +#endif +#ifdef _SC_STREAM_MAX + {"SC_STREAM_MAX", _SC_STREAM_MAX}, +#endif +#ifdef _SC_SYNCHRONIZED_IO + {"SC_SYNCHRONIZED_IO", _SC_SYNCHRONIZED_IO}, +#endif +#ifdef _SC_THREADS + {"SC_THREADS", _SC_THREADS}, +#endif +#ifdef _SC_THREAD_ATTR_STACKADDR + {"SC_THREAD_ATTR_STACKADDR", _SC_THREAD_ATTR_STACKADDR}, +#endif +#ifdef _SC_THREAD_ATTR_STACKSIZE + {"SC_THREAD_ATTR_STACKSIZE", _SC_THREAD_ATTR_STACKSIZE}, +#endif +#ifdef _SC_THREAD_DESTRUCTOR_ITERATIONS + {"SC_THREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS}, +#endif +#ifdef _SC_THREAD_KEYS_MAX + {"SC_THREAD_KEYS_MAX", _SC_THREAD_KEYS_MAX}, +#endif +#ifdef _SC_THREAD_PRIORITY_SCHEDULING + {"SC_THREAD_PRIORITY_SCHEDULING", _SC_THREAD_PRIORITY_SCHEDULING}, +#endif +#ifdef _SC_THREAD_PRIO_INHERIT + {"SC_THREAD_PRIO_INHERIT", _SC_THREAD_PRIO_INHERIT}, +#endif +#ifdef _SC_THREAD_PRIO_PROTECT + {"SC_THREAD_PRIO_PROTECT", _SC_THREAD_PRIO_PROTECT}, +#endif +#ifdef _SC_THREAD_PROCESS_SHARED + {"SC_THREAD_PROCESS_SHARED", _SC_THREAD_PROCESS_SHARED}, +#endif +#ifdef _SC_THREAD_SAFE_FUNCTIONS + {"SC_THREAD_SAFE_FUNCTIONS", _SC_THREAD_SAFE_FUNCTIONS}, +#endif +#ifdef _SC_THREAD_STACK_MIN + {"SC_THREAD_STACK_MIN", _SC_THREAD_STACK_MIN}, +#endif +#ifdef _SC_THREAD_THREADS_MAX + {"SC_THREAD_THREADS_MAX", _SC_THREAD_THREADS_MAX}, +#endif +#ifdef _SC_TIMERS + {"SC_TIMERS", _SC_TIMERS}, +#endif +#ifdef _SC_TIMER_MAX + {"SC_TIMER_MAX", _SC_TIMER_MAX}, +#endif +#ifdef _SC_TTY_NAME_MAX + {"SC_TTY_NAME_MAX", _SC_TTY_NAME_MAX}, +#endif +#ifdef _SC_TZNAME_MAX + {"SC_TZNAME_MAX", _SC_TZNAME_MAX}, +#endif +#ifdef _SC_T_IOV_MAX + {"SC_T_IOV_MAX", _SC_T_IOV_MAX}, +#endif +#ifdef _SC_UCHAR_MAX + {"SC_UCHAR_MAX", _SC_UCHAR_MAX}, +#endif +#ifdef _SC_UINT_MAX + {"SC_UINT_MAX", _SC_UINT_MAX}, +#endif +#ifdef _SC_UIO_MAXIOV + {"SC_UIO_MAXIOV", _SC_UIO_MAXIOV}, +#endif +#ifdef _SC_ULONG_MAX + {"SC_ULONG_MAX", _SC_ULONG_MAX}, +#endif +#ifdef _SC_USHRT_MAX + {"SC_USHRT_MAX", _SC_USHRT_MAX}, +#endif +#ifdef _SC_VERSION + {"SC_VERSION", _SC_VERSION}, +#endif +#ifdef _SC_WORD_BIT + {"SC_WORD_BIT", _SC_WORD_BIT}, +#endif +#ifdef _SC_XBS5_ILP32_OFF32 + {"SC_XBS5_ILP32_OFF32", _SC_XBS5_ILP32_OFF32}, +#endif +#ifdef _SC_XBS5_ILP32_OFFBIG + {"SC_XBS5_ILP32_OFFBIG", _SC_XBS5_ILP32_OFFBIG}, +#endif +#ifdef _SC_XBS5_LP64_OFF64 + {"SC_XBS5_LP64_OFF64", _SC_XBS5_LP64_OFF64}, +#endif +#ifdef _SC_XBS5_LPBIG_OFFBIG + {"SC_XBS5_LPBIG_OFFBIG", _SC_XBS5_LPBIG_OFFBIG}, +#endif +#ifdef _SC_XOPEN_CRYPT + {"SC_XOPEN_CRYPT", _SC_XOPEN_CRYPT}, +#endif +#ifdef _SC_XOPEN_ENH_I18N + {"SC_XOPEN_ENH_I18N", _SC_XOPEN_ENH_I18N}, +#endif +#ifdef _SC_XOPEN_LEGACY + {"SC_XOPEN_LEGACY", _SC_XOPEN_LEGACY}, +#endif +#ifdef _SC_XOPEN_REALTIME + {"SC_XOPEN_REALTIME", _SC_XOPEN_REALTIME}, +#endif +#ifdef _SC_XOPEN_REALTIME_THREADS + {"SC_XOPEN_REALTIME_THREADS", _SC_XOPEN_REALTIME_THREADS}, +#endif +#ifdef _SC_XOPEN_SHM + {"SC_XOPEN_SHM", _SC_XOPEN_SHM}, +#endif +#ifdef _SC_XOPEN_UNIX + {"SC_XOPEN_UNIX", _SC_XOPEN_UNIX}, +#endif +#ifdef _SC_XOPEN_VERSION + {"SC_XOPEN_VERSION", _SC_XOPEN_VERSION}, +#endif +#ifdef _SC_XOPEN_XCU_VERSION + {"SC_XOPEN_XCU_VERSION", _SC_XOPEN_XCU_VERSION}, +#endif +#ifdef _SC_XOPEN_XPG2 + {"SC_XOPEN_XPG2", _SC_XOPEN_XPG2}, +#endif +#ifdef _SC_XOPEN_XPG3 + {"SC_XOPEN_XPG3", _SC_XOPEN_XPG3}, +#endif +#ifdef _SC_XOPEN_XPG4 + {"SC_XOPEN_XPG4", _SC_XOPEN_XPG4}, +#endif +}; + +static int +conv_sysconf_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_sysconf, + sizeof(posix_constants_sysconf) + / sizeof(struct constdef)); +} + +PyDoc_STRVAR(posix_sysconf__doc__, +"sysconf(name) -> integer\n\n\ +Return an integer-valued system configuration variable."); + +static PyObject * +posix_sysconf(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name; + + if (PyArg_ParseTuple(args, "O&:sysconf", conv_sysconf_confname, &name)) { + int value; + + errno = 0; + value = sysconf(name); + if (value == -1 && errno != 0) + posix_error(); + else + result = PyInt_FromLong(value); + } + return result; +} +#endif + + +/* This code is used to ensure that the tables of configuration value names + * are in sorted order as required by conv_confname(), and also to build the + * the exported dictionaries that are used to publish information about the + * names available on the host platform. + * + * Sorting the table at runtime ensures that the table is properly ordered + * when used, even for platforms we're not able to test on. It also makes + * it easier to add additional entries to the tables. + */ + +static int +cmp_constdefs(const void *v1, const void *v2) +{ + const struct constdef *c1 = + (const struct constdef *) v1; + const struct constdef *c2 = + (const struct constdef *) v2; + + return strcmp(c1->name, c2->name); +} + +static int +setup_confname_table(struct constdef *table, size_t tablesize, + char *tablename, PyObject *module) +{ + PyObject *d = NULL; + size_t i; + + qsort(table, tablesize, sizeof(struct constdef), cmp_constdefs); + d = PyDict_New(); + if (d == NULL) + return -1; + + for (i=0; i < tablesize; ++i) { + PyObject *o = PyInt_FromLong(table[i].value); + if (o == NULL || PyDict_SetItemString(d, table[i].name, o) == -1) { + Py_XDECREF(o); + Py_DECREF(d); + return -1; + } + Py_DECREF(o); + } + return PyModule_AddObject(module, tablename, d); +} + +/* Return -1 on failure, 0 on success. */ +static int +setup_confname_tables(PyObject *module) +{ +#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF) + if (setup_confname_table(posix_constants_pathconf, + sizeof(posix_constants_pathconf) + / sizeof(struct constdef), + "pathconf_names", module)) + return -1; +#endif +#ifdef HAVE_CONFSTR + if (setup_confname_table(posix_constants_confstr, + sizeof(posix_constants_confstr) + / sizeof(struct constdef), + "confstr_names", module)) + return -1; +#endif +#ifdef HAVE_SYSCONF + if (setup_confname_table(posix_constants_sysconf, + sizeof(posix_constants_sysconf) + / sizeof(struct constdef), + "sysconf_names", module)) + return -1; +#endif + return 0; +} + + +PyDoc_STRVAR(posix_abort__doc__, +"abort() -> does not return!\n\n\ +Abort the interpreter immediately. This 'dumps core' or otherwise fails\n\ +in the hardest way possible on the hosting operating system."); + +static PyObject * +posix_abort(PyObject *self, PyObject *noargs) +{ + abort(); + /*NOTREACHED*/ + Py_FatalError("abort() called from Python code didn't abort!"); + return NULL; +} + +#ifdef MS_WINDOWS +PyDoc_STRVAR(win32_startfile__doc__, +"startfile(filepath [, operation]) - Start a file with its associated\n\ +application.\n\ +\n\ +When \"operation\" is not specified or \"open\", this acts like\n\ +double-clicking the file in Explorer, or giving the file name as an\n\ +argument to the DOS \"start\" command: the file is opened with whatever\n\ +application (if any) its extension is associated.\n\ +When another \"operation\" is given, it specifies what should be done with\n\ +the file. A typical operation is \"print\".\n\ +\n\ +startfile returns as soon as the associated application is launched.\n\ +There is no option to wait for the application to close, and no way\n\ +to retrieve the application's exit status.\n\ +\n\ +The filepath is relative to the current directory. If you want to use\n\ +an absolute path, make sure the first character is not a slash (\"/\");\n\ +the underlying Win32 ShellExecute function doesn't work if it is."); + +static PyObject * +win32_startfile(PyObject *self, PyObject *args) +{ + char *filepath; + char *operation = NULL; + HINSTANCE rc; + if (!PyArg_ParseTuple(args, "et|s:startfile", + Py_FileSystemDefaultEncoding, &filepath, + &operation)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rc = ShellExecute((HWND)0, operation, filepath, + NULL, NULL, SW_SHOWNORMAL); + Py_END_ALLOW_THREADS + if (rc <= (HINSTANCE)32) { + PyObject *errval = win32_error("startfile", filepath); + PyMem_Free(filepath); + return errval; + } + PyMem_Free(filepath); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_GETLOADAVG +PyDoc_STRVAR(posix_getloadavg__doc__, +"getloadavg() -> (float, float, float)\n\n\ +Return the number of processes in the system run queue averaged over\n\ +the last 1, 5, and 15 minutes or raises OSError if the load average\n\ +was unobtainable"); + +static PyObject * +posix_getloadavg(PyObject *self, PyObject *noargs) +{ + double loadavg[3]; + if (getloadavg(loadavg, 3)!=3) { + PyErr_SetString(PyExc_OSError, "Load averages are unobtainable"); + return NULL; + } else + return Py_BuildValue("ddd", loadavg[0], loadavg[1], loadavg[2]); +} +#endif + +#ifdef MS_WINDOWS + +PyDoc_STRVAR(win32_urandom__doc__, +"urandom(n) -> str\n\n\ +Return a string of n random bytes suitable for cryptographic use."); + +typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\ + LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\ + DWORD dwFlags ); +typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\ + BYTE *pbBuffer ); + +static CRYPTGENRANDOM pCryptGenRandom = NULL; +static HCRYPTPROV hCryptProv = 0; + +static PyObject* +win32_urandom(PyObject *self, PyObject *args) +{ + int howMany; + PyObject* result; + + /* Read arguments */ + if (! PyArg_ParseTuple(args, "i:urandom", &howMany)) + return NULL; + if (howMany < 0) + return PyErr_Format(PyExc_ValueError, + "negative argument not allowed"); + + if (hCryptProv == 0) { + HINSTANCE hAdvAPI32 = NULL; + CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL; + + /* Obtain handle to the DLL containing CryptoAPI + This should not fail */ + hAdvAPI32 = GetModuleHandle("advapi32.dll"); + if(hAdvAPI32 == NULL) + return win32_error("GetModuleHandle", NULL); + + /* Obtain pointers to the CryptoAPI functions + This will fail on some early versions of Win95 */ + pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress( + hAdvAPI32, + "CryptAcquireContextA"); + if (pCryptAcquireContext == NULL) + return PyErr_Format(PyExc_NotImplementedError, + "CryptAcquireContextA not found"); + + pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress( + hAdvAPI32, "CryptGenRandom"); + if (pCryptAcquireContext == NULL) + return PyErr_Format(PyExc_NotImplementedError, + "CryptGenRandom not found"); + + /* Acquire context */ + if (! pCryptAcquireContext(&hCryptProv, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + return win32_error("CryptAcquireContext", NULL); + } + + /* Allocate bytes */ + result = PyString_FromStringAndSize(NULL, howMany); + if (result != NULL) { + /* Get random data */ + if (! pCryptGenRandom(hCryptProv, howMany, (unsigned char*) + PyString_AS_STRING(result))) { + Py_DECREF(result); + return win32_error("CryptGenRandom", NULL); + } + } + return result; +} +#endif + +static PyMethodDef posix_methods[] = { + {"access", posix_access, METH_VARARGS, posix_access__doc__}, +#ifdef HAVE_TTYNAME + {"ttyname", posix_ttyname, METH_VARARGS, posix_ttyname__doc__}, +#endif + {"chdir", posix_chdir, METH_VARARGS, posix_chdir__doc__}, + {"chmod", posix_chmod, METH_VARARGS, posix_chmod__doc__}, +#ifdef HAVE_CHOWN + {"chown", posix_chown, METH_VARARGS, posix_chown__doc__}, +#endif /* HAVE_CHOWN */ +#ifdef HAVE_LCHOWN + {"lchown", posix_lchown, METH_VARARGS, posix_lchown__doc__}, +#endif /* HAVE_LCHOWN */ +#ifdef HAVE_CHROOT + {"chroot", posix_chroot, METH_VARARGS, posix_chroot__doc__}, +#endif +#ifdef HAVE_CTERMID + {"ctermid", posix_ctermid, METH_NOARGS, posix_ctermid__doc__}, +#endif +#ifdef HAVE_GETCWD + {"getcwd", posix_getcwd, METH_NOARGS, posix_getcwd__doc__}, +#ifdef Py_USING_UNICODE + {"getcwdu", posix_getcwdu, METH_NOARGS, posix_getcwdu__doc__}, +#endif +#endif +#ifdef HAVE_LINK + {"link", posix_link, METH_VARARGS, posix_link__doc__}, +#endif /* HAVE_LINK */ + {"listdir", posix_listdir, METH_VARARGS, posix_listdir__doc__}, + {"lstat", posix_lstat, METH_VARARGS, posix_lstat__doc__}, + {"mkdir", posix_mkdir, METH_VARARGS, posix_mkdir__doc__}, +#ifdef HAVE_NICE + {"nice", posix_nice, METH_VARARGS, posix_nice__doc__}, +#endif /* HAVE_NICE */ +#ifdef HAVE_READLINK + {"readlink", posix_readlink, METH_VARARGS, posix_readlink__doc__}, +#endif /* HAVE_READLINK */ + {"rename", posix_rename, METH_VARARGS, posix_rename__doc__}, + {"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__}, + {"stat", posix_stat, METH_VARARGS, posix_stat__doc__}, + {"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__}, +#ifdef HAVE_SYMLINK + {"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__}, +#endif /* HAVE_SYMLINK */ +#ifdef HAVE_SYSTEM + {"system", posix_system, METH_VARARGS, posix_system__doc__}, +#endif + {"umask", posix_umask, METH_VARARGS, posix_umask__doc__}, +#ifdef HAVE_UNAME + {"uname", posix_uname, METH_NOARGS, posix_uname__doc__}, +#endif /* HAVE_UNAME */ + {"unlink", posix_unlink, METH_VARARGS, posix_unlink__doc__}, + {"remove", posix_unlink, METH_VARARGS, posix_remove__doc__}, + {"utime", posix_utime, METH_VARARGS, posix_utime__doc__}, +#ifdef HAVE_TIMES + {"times", posix_times, METH_NOARGS, posix_times__doc__}, +#endif /* HAVE_TIMES */ + {"_exit", posix__exit, METH_VARARGS, posix__exit__doc__}, +#ifdef HAVE_EXECV + {"execv", posix_execv, METH_VARARGS, posix_execv__doc__}, + {"execve", posix_execve, METH_VARARGS, posix_execve__doc__}, +#endif /* HAVE_EXECV */ +#ifdef HAVE_SPAWNV + {"spawnv", posix_spawnv, METH_VARARGS, posix_spawnv__doc__}, + {"spawnve", posix_spawnve, METH_VARARGS, posix_spawnve__doc__}, +#if defined(PYOS_OS2) + {"spawnvp", posix_spawnvp, METH_VARARGS, posix_spawnvp__doc__}, + {"spawnvpe", posix_spawnvpe, METH_VARARGS, posix_spawnvpe__doc__}, +#endif /* PYOS_OS2 */ +#endif /* HAVE_SPAWNV */ +#ifdef HAVE_FORK1 + {"fork1", posix_fork1, METH_NOARGS, posix_fork1__doc__}, +#endif /* HAVE_FORK1 */ +#ifdef HAVE_FORK + {"fork", posix_fork, METH_NOARGS, posix_fork__doc__}, +#endif /* HAVE_FORK */ +#if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) + {"openpty", posix_openpty, METH_NOARGS, posix_openpty__doc__}, +#endif /* HAVE_OPENPTY || HAVE__GETPTY || HAVE_DEV_PTMX */ +#ifdef HAVE_FORKPTY + {"forkpty", posix_forkpty, METH_NOARGS, posix_forkpty__doc__}, +#endif /* HAVE_FORKPTY */ +#ifdef HAVE_GETEGID + {"getegid", posix_getegid, METH_NOARGS, posix_getegid__doc__}, +#endif /* HAVE_GETEGID */ +#ifdef HAVE_GETEUID + {"geteuid", posix_geteuid, METH_NOARGS, posix_geteuid__doc__}, +#endif /* HAVE_GETEUID */ +#ifdef HAVE_GETGID + {"getgid", posix_getgid, METH_NOARGS, posix_getgid__doc__}, +#endif /* HAVE_GETGID */ +#ifdef HAVE_GETGROUPS + {"getgroups", posix_getgroups, METH_NOARGS, posix_getgroups__doc__}, +#endif + {"getpid", posix_getpid, METH_NOARGS, posix_getpid__doc__}, +#ifdef HAVE_GETPGRP + {"getpgrp", posix_getpgrp, METH_NOARGS, posix_getpgrp__doc__}, +#endif /* HAVE_GETPGRP */ +#ifdef HAVE_GETPPID + {"getppid", posix_getppid, METH_NOARGS, posix_getppid__doc__}, +#endif /* HAVE_GETPPID */ +#ifdef HAVE_GETUID + {"getuid", posix_getuid, METH_NOARGS, posix_getuid__doc__}, +#endif /* HAVE_GETUID */ +#ifdef HAVE_GETLOGIN + {"getlogin", posix_getlogin, METH_NOARGS, posix_getlogin__doc__}, +#endif +#ifdef HAVE_KILL + {"kill", posix_kill, METH_VARARGS, posix_kill__doc__}, +#endif /* HAVE_KILL */ +#ifdef HAVE_KILLPG + {"killpg", posix_killpg, METH_VARARGS, posix_killpg__doc__}, +#endif /* HAVE_KILLPG */ +#ifdef HAVE_PLOCK + {"plock", posix_plock, METH_VARARGS, posix_plock__doc__}, +#endif /* HAVE_PLOCK */ +#ifdef HAVE_POPEN + {"popen", posix_popen, METH_VARARGS, posix_popen__doc__}, +#ifdef MS_WINDOWS + {"popen2", win32_popen2, METH_VARARGS}, + {"popen3", win32_popen3, METH_VARARGS}, + {"popen4", win32_popen4, METH_VARARGS}, + {"startfile", win32_startfile, METH_VARARGS, win32_startfile__doc__}, +#else +#if defined(PYOS_OS2) && defined(PYCC_GCC) + {"popen2", os2emx_popen2, METH_VARARGS}, + {"popen3", os2emx_popen3, METH_VARARGS}, + {"popen4", os2emx_popen4, METH_VARARGS}, +#endif +#endif +#endif /* HAVE_POPEN */ +#ifdef HAVE_SETUID + {"setuid", posix_setuid, METH_VARARGS, posix_setuid__doc__}, +#endif /* HAVE_SETUID */ +#ifdef HAVE_SETEUID + {"seteuid", posix_seteuid, METH_VARARGS, posix_seteuid__doc__}, +#endif /* HAVE_SETEUID */ +#ifdef HAVE_SETEGID + {"setegid", posix_setegid, METH_VARARGS, posix_setegid__doc__}, +#endif /* HAVE_SETEGID */ +#ifdef HAVE_SETREUID + {"setreuid", posix_setreuid, METH_VARARGS, posix_setreuid__doc__}, +#endif /* HAVE_SETREUID */ +#ifdef HAVE_SETREGID + {"setregid", posix_setregid, METH_VARARGS, posix_setregid__doc__}, +#endif /* HAVE_SETREGID */ +#ifdef HAVE_SETGID + {"setgid", posix_setgid, METH_VARARGS, posix_setgid__doc__}, +#endif /* HAVE_SETGID */ +#ifdef HAVE_SETGROUPS + {"setgroups", posix_setgroups, METH_VARARGS, posix_setgroups__doc__}, +#endif /* HAVE_SETGROUPS */ +#ifdef HAVE_GETPGID + {"getpgid", posix_getpgid, METH_VARARGS, posix_getpgid__doc__}, +#endif /* HAVE_GETPGID */ +#ifdef HAVE_SETPGRP + {"setpgrp", posix_setpgrp, METH_NOARGS, posix_setpgrp__doc__}, +#endif /* HAVE_SETPGRP */ +#ifdef HAVE_WAIT + {"wait", posix_wait, METH_NOARGS, posix_wait__doc__}, +#endif /* HAVE_WAIT */ +#if defined(HAVE_WAITPID) || defined(HAVE_CWAIT) + {"waitpid", posix_waitpid, METH_VARARGS, posix_waitpid__doc__}, +#endif /* HAVE_WAITPID */ +#ifdef HAVE_GETSID + {"getsid", posix_getsid, METH_VARARGS, posix_getsid__doc__}, +#endif /* HAVE_GETSID */ +#ifdef HAVE_SETSID + {"setsid", posix_setsid, METH_NOARGS, posix_setsid__doc__}, +#endif /* HAVE_SETSID */ +#ifdef HAVE_SETPGID + {"setpgid", posix_setpgid, METH_VARARGS, posix_setpgid__doc__}, +#endif /* HAVE_SETPGID */ +#ifdef HAVE_TCGETPGRP + {"tcgetpgrp", posix_tcgetpgrp, METH_VARARGS, posix_tcgetpgrp__doc__}, +#endif /* HAVE_TCGETPGRP */ +#ifdef HAVE_TCSETPGRP + {"tcsetpgrp", posix_tcsetpgrp, METH_VARARGS, posix_tcsetpgrp__doc__}, +#endif /* HAVE_TCSETPGRP */ + {"open", posix_open, METH_VARARGS, posix_open__doc__}, + {"close", posix_close, METH_VARARGS, posix_close__doc__}, + {"dup", posix_dup, METH_VARARGS, posix_dup__doc__}, + {"dup2", posix_dup2, METH_VARARGS, posix_dup2__doc__}, + {"lseek", posix_lseek, METH_VARARGS, posix_lseek__doc__}, + {"read", posix_read, METH_VARARGS, posix_read__doc__}, + {"write", posix_write, METH_VARARGS, posix_write__doc__}, + {"fstat", posix_fstat, METH_VARARGS, posix_fstat__doc__}, + {"fdopen", posix_fdopen, METH_VARARGS, posix_fdopen__doc__}, + {"isatty", posix_isatty, METH_VARARGS, posix_isatty__doc__}, +#ifdef HAVE_PIPE + {"pipe", posix_pipe, METH_NOARGS, posix_pipe__doc__}, +#endif +#ifdef HAVE_MKFIFO + {"mkfifo", posix_mkfifo, METH_VARARGS, posix_mkfifo__doc__}, +#endif +#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) + {"mknod", posix_mknod, METH_VARARGS, posix_mknod__doc__}, +#endif +#ifdef HAVE_DEVICE_MACROS + {"major", posix_major, METH_VARARGS, posix_major__doc__}, + {"minor", posix_minor, METH_VARARGS, posix_minor__doc__}, + {"makedev", posix_makedev, METH_VARARGS, posix_makedev__doc__}, +#endif +#ifdef HAVE_FTRUNCATE + {"ftruncate", posix_ftruncate, METH_VARARGS, posix_ftruncate__doc__}, +#endif +#ifdef HAVE_PUTENV + {"putenv", posix_putenv, METH_VARARGS, posix_putenv__doc__}, +#endif +#ifdef HAVE_UNSETENV + {"unsetenv", posix_unsetenv, METH_VARARGS, posix_unsetenv__doc__}, +#endif +#ifdef HAVE_STRERROR + {"strerror", posix_strerror, METH_VARARGS, posix_strerror__doc__}, +#endif +#ifdef HAVE_FCHDIR + {"fchdir", posix_fchdir, METH_O, posix_fchdir__doc__}, +#endif +#ifdef HAVE_FSYNC + {"fsync", posix_fsync, METH_O, posix_fsync__doc__}, +#endif +#ifdef HAVE_FDATASYNC + {"fdatasync", posix_fdatasync, METH_O, posix_fdatasync__doc__}, +#endif +#ifdef HAVE_SYS_WAIT_H +#ifdef WCOREDUMP + {"WCOREDUMP", posix_WCOREDUMP, METH_VARARGS, posix_WCOREDUMP__doc__}, +#endif /* WCOREDUMP */ +#ifdef WIFCONTINUED + {"WIFCONTINUED",posix_WIFCONTINUED, METH_VARARGS, posix_WIFCONTINUED__doc__}, +#endif /* WIFCONTINUED */ +#ifdef WIFSTOPPED + {"WIFSTOPPED", posix_WIFSTOPPED, METH_VARARGS, posix_WIFSTOPPED__doc__}, +#endif /* WIFSTOPPED */ +#ifdef WIFSIGNALED + {"WIFSIGNALED", posix_WIFSIGNALED, METH_VARARGS, posix_WIFSIGNALED__doc__}, +#endif /* WIFSIGNALED */ +#ifdef WIFEXITED + {"WIFEXITED", posix_WIFEXITED, METH_VARARGS, posix_WIFEXITED__doc__}, +#endif /* WIFEXITED */ +#ifdef WEXITSTATUS + {"WEXITSTATUS", posix_WEXITSTATUS, METH_VARARGS, posix_WEXITSTATUS__doc__}, +#endif /* WEXITSTATUS */ +#ifdef WTERMSIG + {"WTERMSIG", posix_WTERMSIG, METH_VARARGS, posix_WTERMSIG__doc__}, +#endif /* WTERMSIG */ +#ifdef WSTOPSIG + {"WSTOPSIG", posix_WSTOPSIG, METH_VARARGS, posix_WSTOPSIG__doc__}, +#endif /* WSTOPSIG */ +#endif /* HAVE_SYS_WAIT_H */ +#ifdef HAVE_FSTATVFS + {"fstatvfs", posix_fstatvfs, METH_VARARGS, posix_fstatvfs__doc__}, +#endif +#ifdef HAVE_STATVFS + {"statvfs", posix_statvfs, METH_VARARGS, posix_statvfs__doc__}, +#endif +#ifdef HAVE_TMPFILE + {"tmpfile", posix_tmpfile, METH_NOARGS, posix_tmpfile__doc__}, +#endif +#ifdef HAVE_TEMPNAM + {"tempnam", posix_tempnam, METH_VARARGS, posix_tempnam__doc__}, +#endif +#ifdef HAVE_TMPNAM + {"tmpnam", posix_tmpnam, METH_NOARGS, posix_tmpnam__doc__}, +#endif +#ifdef HAVE_CONFSTR + {"confstr", posix_confstr, METH_VARARGS, posix_confstr__doc__}, +#endif +#ifdef HAVE_SYSCONF + {"sysconf", posix_sysconf, METH_VARARGS, posix_sysconf__doc__}, +#endif +#ifdef HAVE_FPATHCONF + {"fpathconf", posix_fpathconf, METH_VARARGS, posix_fpathconf__doc__}, +#endif +#ifdef HAVE_PATHCONF + {"pathconf", posix_pathconf, METH_VARARGS, posix_pathconf__doc__}, +#endif + {"abort", posix_abort, METH_NOARGS, posix_abort__doc__}, +#ifdef MS_WINDOWS + {"_getfullpathname", posix__getfullpathname, METH_VARARGS, NULL}, +#endif +#ifdef HAVE_GETLOADAVG + {"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__}, +#endif + #ifdef MS_WINDOWS + {"urandom", win32_urandom, METH_VARARGS, win32_urandom__doc__}, + #endif + {NULL, NULL} /* Sentinel */ +}; + + +static int +ins(PyObject *module, char *symbol, long value) +{ + return PyModule_AddIntConstant(module, symbol, value); +} + +#if defined(PYOS_OS2) +/* Insert Platform-Specific Constant Values (Strings & Numbers) of Common Use */ +static int insertvalues(PyObject *module) +{ + APIRET rc; + ULONG values[QSV_MAX+1]; + PyObject *v; + char *ver, tmp[50]; + + Py_BEGIN_ALLOW_THREADS + rc = DosQuerySysInfo(1L, QSV_MAX, &values[1], sizeof(ULONG) * QSV_MAX); + Py_END_ALLOW_THREADS + + if (rc != NO_ERROR) { + os2_error(rc); + return -1; + } + + if (ins(module, "meminstalled", values[QSV_TOTPHYSMEM])) return -1; + if (ins(module, "memkernel", values[QSV_TOTRESMEM])) return -1; + if (ins(module, "memvirtual", values[QSV_TOTAVAILMEM])) return -1; + if (ins(module, "maxpathlen", values[QSV_MAX_PATH_LENGTH])) return -1; + if (ins(module, "maxnamelen", values[QSV_MAX_COMP_LENGTH])) return -1; + if (ins(module, "revision", values[QSV_VERSION_REVISION])) return -1; + if (ins(module, "timeslice", values[QSV_MIN_SLICE])) return -1; + + switch (values[QSV_VERSION_MINOR]) { + case 0: ver = "2.00"; break; + case 10: ver = "2.10"; break; + case 11: ver = "2.11"; break; + case 30: ver = "3.00"; break; + case 40: ver = "4.00"; break; + case 50: ver = "5.00"; break; + default: + PyOS_snprintf(tmp, sizeof(tmp), + "%d-%d", values[QSV_VERSION_MAJOR], + values[QSV_VERSION_MINOR]); + ver = &tmp[0]; + } + + /* Add Indicator of the Version of the Operating System */ + if (PyModule_AddStringConstant(module, "version", tmp) < 0) + return -1; + + /* Add Indicator of Which Drive was Used to Boot the System */ + tmp[0] = 'A' + values[QSV_BOOT_DRIVE] - 1; + tmp[1] = ':'; + tmp[2] = '\0'; + + return PyModule_AddStringConstant(module, "bootdrive", tmp); +} +#endif + +static int +all_ins(PyObject *d) +{ +#ifdef F_OK + if (ins(d, "F_OK", (long)F_OK)) return -1; +#endif +#ifdef R_OK + if (ins(d, "R_OK", (long)R_OK)) return -1; +#endif +#ifdef W_OK + if (ins(d, "W_OK", (long)W_OK)) return -1; +#endif +#ifdef X_OK + if (ins(d, "X_OK", (long)X_OK)) return -1; +#endif +#ifdef NGROUPS_MAX + if (ins(d, "NGROUPS_MAX", (long)NGROUPS_MAX)) return -1; +#endif +#ifdef TMP_MAX + if (ins(d, "TMP_MAX", (long)TMP_MAX)) return -1; +#endif +#ifdef WCONTINUED + if (ins(d, "WCONTINUED", (long)WCONTINUED)) return -1; +#endif +#ifdef WNOHANG + if (ins(d, "WNOHANG", (long)WNOHANG)) return -1; +#endif +#ifdef WUNTRACED + if (ins(d, "WUNTRACED", (long)WUNTRACED)) return -1; +#endif +#ifdef O_RDONLY + if (ins(d, "O_RDONLY", (long)O_RDONLY)) return -1; +#endif +#ifdef O_WRONLY + if (ins(d, "O_WRONLY", (long)O_WRONLY)) return -1; +#endif +#ifdef O_RDWR + if (ins(d, "O_RDWR", (long)O_RDWR)) return -1; +#endif +#ifdef O_NDELAY + if (ins(d, "O_NDELAY", (long)O_NDELAY)) return -1; +#endif +#ifdef O_NONBLOCK + if (ins(d, "O_NONBLOCK", (long)O_NONBLOCK)) return -1; +#endif +#ifdef O_APPEND + if (ins(d, "O_APPEND", (long)O_APPEND)) return -1; +#endif +#ifdef O_DSYNC + if (ins(d, "O_DSYNC", (long)O_DSYNC)) return -1; +#endif +#ifdef O_RSYNC + if (ins(d, "O_RSYNC", (long)O_RSYNC)) return -1; +#endif +#ifdef O_SYNC + if (ins(d, "O_SYNC", (long)O_SYNC)) return -1; +#endif +#ifdef O_NOCTTY + if (ins(d, "O_NOCTTY", (long)O_NOCTTY)) return -1; +#endif +#ifdef O_CREAT + if (ins(d, "O_CREAT", (long)O_CREAT)) return -1; +#endif +#ifdef O_EXCL + if (ins(d, "O_EXCL", (long)O_EXCL)) return -1; +#endif +#ifdef O_TRUNC + if (ins(d, "O_TRUNC", (long)O_TRUNC)) return -1; +#endif +#ifdef O_BINARY + if (ins(d, "O_BINARY", (long)O_BINARY)) return -1; +#endif +#ifdef O_TEXT + if (ins(d, "O_TEXT", (long)O_TEXT)) return -1; +#endif +#ifdef O_LARGEFILE + if (ins(d, "O_LARGEFILE", (long)O_LARGEFILE)) return -1; +#endif +#ifdef O_SHLOCK + if (ins(d, "O_SHLOCK", (long)O_SHLOCK)) return -1; +#endif +#ifdef O_EXLOCK + if (ins(d, "O_EXLOCK", (long)O_EXLOCK)) return -1; +#endif + +/* MS Windows */ +#ifdef O_NOINHERIT + /* Don't inherit in child processes. */ + if (ins(d, "O_NOINHERIT", (long)O_NOINHERIT)) return -1; +#endif +#ifdef _O_SHORT_LIVED + /* Optimize for short life (keep in memory). */ + /* MS forgot to define this one with a non-underscore form too. */ + if (ins(d, "O_SHORT_LIVED", (long)_O_SHORT_LIVED)) return -1; +#endif +#ifdef O_TEMPORARY + /* Automatically delete when last handle is closed. */ + if (ins(d, "O_TEMPORARY", (long)O_TEMPORARY)) return -1; +#endif +#ifdef O_RANDOM + /* Optimize for random access. */ + if (ins(d, "O_RANDOM", (long)O_RANDOM)) return -1; +#endif +#ifdef O_SEQUENTIAL + /* Optimize for sequential access. */ + if (ins(d, "O_SEQUENTIAL", (long)O_SEQUENTIAL)) return -1; +#endif + +/* GNU extensions. */ +#ifdef O_DIRECT + /* Direct disk access. */ + if (ins(d, "O_DIRECT", (long)O_DIRECT)) return -1; +#endif +#ifdef O_DIRECTORY + /* Must be a directory. */ + if (ins(d, "O_DIRECTORY", (long)O_DIRECTORY)) return -1; +#endif +#ifdef O_NOFOLLOW + /* Do not follow links. */ + if (ins(d, "O_NOFOLLOW", (long)O_NOFOLLOW)) return -1; +#endif + + /* These come from sysexits.h */ +#ifdef EX_OK + if (ins(d, "EX_OK", (long)EX_OK)) return -1; +#endif /* EX_OK */ +#ifdef EX_USAGE + if (ins(d, "EX_USAGE", (long)EX_USAGE)) return -1; +#endif /* EX_USAGE */ +#ifdef EX_DATAERR + if (ins(d, "EX_DATAERR", (long)EX_DATAERR)) return -1; +#endif /* EX_DATAERR */ +#ifdef EX_NOINPUT + if (ins(d, "EX_NOINPUT", (long)EX_NOINPUT)) return -1; +#endif /* EX_NOINPUT */ +#ifdef EX_NOUSER + if (ins(d, "EX_NOUSER", (long)EX_NOUSER)) return -1; +#endif /* EX_NOUSER */ +#ifdef EX_NOHOST + if (ins(d, "EX_NOHOST", (long)EX_NOHOST)) return -1; +#endif /* EX_NOHOST */ +#ifdef EX_UNAVAILABLE + if (ins(d, "EX_UNAVAILABLE", (long)EX_UNAVAILABLE)) return -1; +#endif /* EX_UNAVAILABLE */ +#ifdef EX_SOFTWARE + if (ins(d, "EX_SOFTWARE", (long)EX_SOFTWARE)) return -1; +#endif /* EX_SOFTWARE */ +#ifdef EX_OSERR + if (ins(d, "EX_OSERR", (long)EX_OSERR)) return -1; +#endif /* EX_OSERR */ +#ifdef EX_OSFILE + if (ins(d, "EX_OSFILE", (long)EX_OSFILE)) return -1; +#endif /* EX_OSFILE */ +#ifdef EX_CANTCREAT + if (ins(d, "EX_CANTCREAT", (long)EX_CANTCREAT)) return -1; +#endif /* EX_CANTCREAT */ +#ifdef EX_IOERR + if (ins(d, "EX_IOERR", (long)EX_IOERR)) return -1; +#endif /* EX_IOERR */ +#ifdef EX_TEMPFAIL + if (ins(d, "EX_TEMPFAIL", (long)EX_TEMPFAIL)) return -1; +#endif /* EX_TEMPFAIL */ +#ifdef EX_PROTOCOL + if (ins(d, "EX_PROTOCOL", (long)EX_PROTOCOL)) return -1; +#endif /* EX_PROTOCOL */ +#ifdef EX_NOPERM + if (ins(d, "EX_NOPERM", (long)EX_NOPERM)) return -1; +#endif /* EX_NOPERM */ +#ifdef EX_CONFIG + if (ins(d, "EX_CONFIG", (long)EX_CONFIG)) return -1; +#endif /* EX_CONFIG */ +#ifdef EX_NOTFOUND + if (ins(d, "EX_NOTFOUND", (long)EX_NOTFOUND)) return -1; +#endif /* EX_NOTFOUND */ + +#ifdef HAVE_SPAWNV +#if defined(PYOS_OS2) && defined(PYCC_GCC) + if (ins(d, "P_WAIT", (long)P_WAIT)) return -1; + if (ins(d, "P_NOWAIT", (long)P_NOWAIT)) return -1; + if (ins(d, "P_OVERLAY", (long)P_OVERLAY)) return -1; + if (ins(d, "P_DEBUG", (long)P_DEBUG)) return -1; + if (ins(d, "P_SESSION", (long)P_SESSION)) return -1; + if (ins(d, "P_DETACH", (long)P_DETACH)) return -1; + if (ins(d, "P_PM", (long)P_PM)) return -1; + if (ins(d, "P_DEFAULT", (long)P_DEFAULT)) return -1; + if (ins(d, "P_MINIMIZE", (long)P_MINIMIZE)) return -1; + if (ins(d, "P_MAXIMIZE", (long)P_MAXIMIZE)) return -1; + if (ins(d, "P_FULLSCREEN", (long)P_FULLSCREEN)) return -1; + if (ins(d, "P_WINDOWED", (long)P_WINDOWED)) return -1; + if (ins(d, "P_FOREGROUND", (long)P_FOREGROUND)) return -1; + if (ins(d, "P_BACKGROUND", (long)P_BACKGROUND)) return -1; + if (ins(d, "P_NOCLOSE", (long)P_NOCLOSE)) return -1; + if (ins(d, "P_NOSESSION", (long)P_NOSESSION)) return -1; + if (ins(d, "P_QUOTE", (long)P_QUOTE)) return -1; + if (ins(d, "P_TILDE", (long)P_TILDE)) return -1; + if (ins(d, "P_UNRELATED", (long)P_UNRELATED)) return -1; + if (ins(d, "P_DEBUGDESC", (long)P_DEBUGDESC)) return -1; +#else + if (ins(d, "P_WAIT", (long)_P_WAIT)) return -1; + if (ins(d, "P_NOWAIT", (long)_P_NOWAIT)) return -1; + if (ins(d, "P_OVERLAY", (long)_OLD_P_OVERLAY)) return -1; + if (ins(d, "P_NOWAITO", (long)_P_NOWAITO)) return -1; + if (ins(d, "P_DETACH", (long)_P_DETACH)) return -1; +#endif +#endif + +#if defined(PYOS_OS2) + if (insertvalues(d)) return -1; +#endif + return 0; +} + + +#if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__)) && !defined(__QNX__) +#define INITFUNC initnt +#define MODNAME "nt" + +#elif defined(PYOS_OS2) +#define INITFUNC initos2 +#define MODNAME "os2" + +#else +#define INITFUNC initposix +#define MODNAME "posix" +#endif + +PyMODINIT_FUNC +INITFUNC(void) +{ + PyObject *m, *v; + + m = Py_InitModule3(MODNAME, + posix_methods, + posix__doc__); + if (m == NULL) + return; + + /* Initialize environ dictionary */ + v = convertenviron(); + Py_XINCREF(v); + if (v == NULL || PyModule_AddObject(m, "environ", v) != 0) + return; + Py_DECREF(v); + + if (all_ins(m)) + return; + + if (setup_confname_tables(m)) + return; + + Py_INCREF(PyExc_OSError); + PyModule_AddObject(m, "error", PyExc_OSError); + +#ifdef HAVE_PUTENV + if (posix_putenv_garbage == NULL) + posix_putenv_garbage = PyDict_New(); +#endif + + stat_result_desc.name = MODNAME ".stat_result"; + stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; + stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; + stat_result_desc.fields[9].name = PyStructSequence_UnnamedField; + PyStructSequence_InitType(&StatResultType, &stat_result_desc); + structseq_new = StatResultType.tp_new; + StatResultType.tp_new = statresult_new; + Py_INCREF((PyObject*) &StatResultType); + PyModule_AddObject(m, "stat_result", (PyObject*) &StatResultType); + + statvfs_result_desc.name = MODNAME ".statvfs_result"; + PyStructSequence_InitType(&StatVFSResultType, &statvfs_result_desc); + Py_INCREF((PyObject*) &StatVFSResultType); + PyModule_AddObject(m, "statvfs_result", + (PyObject*) &StatVFSResultType); +} diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_671582_f8e322_PC#_msi.c b/src/main/resource/testFiles/cpython/prevFiles/prev_671582_f8e322_PC#_msi.c new file mode 100644 index 0000000..eb69014 --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_671582_f8e322_PC#_msi.c @@ -0,0 +1,1073 @@ +/* Helper library for MSI creation with Python. + * Copyright (C) 2005 Martin v. Löwis + * Licensed to PSF under a contributor agreement. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static PyObject *MSIError; + +static PyObject* +uuidcreate(PyObject* obj, PyObject*args) +{ + UUID result; + wchar_t *cresult; + PyObject *oresult; + + /* May return ok, local only, and no address. + For local only, the documentation says we still get a uuid. + For RPC_S_UUID_NO_ADDRESS, it's not clear whether we can + use the result. */ + if (UuidCreate(&result) == RPC_S_UUID_NO_ADDRESS) { + PyErr_SetString(PyExc_NotImplementedError, "processing 'no address' result"); + return NULL; + } + + if (UuidToStringW(&result, &cresult) == RPC_S_OUT_OF_MEMORY) { + PyErr_SetString(PyExc_MemoryError, "out of memory in uuidgen"); + return NULL; + } + + oresult = PyUnicode_FromWideChar(cresult, wcslen(cresult)); + RpcStringFreeW(&cresult); + return oresult; + +} + +/* FCI callback functions */ + +static FNFCIALLOC(cb_alloc) +{ + return malloc(cb); +} + +static FNFCIFREE(cb_free) +{ + free(memory); +} + +static FNFCIOPEN(cb_open) +{ + int result = _open(pszFile, oflag | O_NOINHERIT, pmode); + if (result == -1) + *err = errno; + return result; +} + +static FNFCIREAD(cb_read) +{ + UINT result = (UINT)_read(hf, memory, cb); + if (result != cb) + *err = errno; + return result; +} + +static FNFCIWRITE(cb_write) +{ + UINT result = (UINT)_write(hf, memory, cb); + if (result != cb) + *err = errno; + return result; +} + +static FNFCICLOSE(cb_close) +{ + int result = _close(hf); + if (result != 0) + *err = errno; + return result; +} + +static FNFCISEEK(cb_seek) +{ + long result = (long)_lseek(hf, dist, seektype); + if (result == -1) + *err = errno; + return result; +} + +static FNFCIDELETE(cb_delete) +{ + int result = remove(pszFile); + if (result != 0) + *err = errno; + return result; +} + +static FNFCIFILEPLACED(cb_fileplaced) +{ + return 0; +} + +static FNFCIGETTEMPFILE(cb_gettempfile) +{ + char *name = _tempnam("", "tmp"); + if ((name != NULL) && ((int)strlen(name) < cbTempName)) { + strcpy(pszTempName, name); + free(name); + return TRUE; + } + + if (name) free(name); + return FALSE; +} + +static FNFCISTATUS(cb_status) +{ + if (pv) { + _Py_IDENTIFIER(status); + + PyObject *result = _PyObject_CallMethodId(pv, &PyId_status, "iii", typeStatus, cb1, cb2); + if (result == NULL) + return -1; + Py_DECREF(result); + } + return 0; +} + +static FNFCIGETNEXTCABINET(cb_getnextcabinet) +{ + if (pv) { + _Py_IDENTIFIER(getnextcabinet); + + PyObject *result = _PyObject_CallMethodId(pv, &PyId_getnextcabinet, "i", pccab->iCab); + if (result == NULL) + return -1; + if (!PyBytes_Check(result)) { + PyErr_Format(PyExc_TypeError, + "Incorrect return type %s from getnextcabinet", + result->ob_type->tp_name); + Py_DECREF(result); + return FALSE; + } + strncpy(pccab->szCab, PyBytes_AsString(result), sizeof(pccab->szCab)); + return TRUE; + } + return FALSE; +} + +static FNFCIGETOPENINFO(cb_getopeninfo) +{ + BY_HANDLE_FILE_INFORMATION bhfi; + FILETIME filetime; + HANDLE handle; + + /* Need Win32 handle to get time stamps */ + handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) + return -1; + + if (GetFileInformationByHandle(handle, &bhfi) == FALSE) + { + CloseHandle(handle); + return -1; + } + + FileTimeToLocalFileTime(&bhfi.ftLastWriteTime, &filetime); + FileTimeToDosDateTime(&filetime, pdate, ptime); + + *pattribs = (int)(bhfi.dwFileAttributes & + (_A_RDONLY | _A_SYSTEM | _A_HIDDEN | _A_ARCH)); + + CloseHandle(handle); + + return _open(pszName, _O_RDONLY | _O_BINARY | O_NOINHERIT); +} + +static PyObject* fcicreate(PyObject* obj, PyObject* args) +{ + char *cabname, *p; + PyObject *files; + CCAB ccab; + HFCI hfci; + ERF erf; + Py_ssize_t i; + + + if (!PyArg_ParseTuple(args, "sO:FCICreate", &cabname, &files)) + return NULL; + + if (!PyList_Check(files)) { + PyErr_SetString(PyExc_TypeError, "FCICreate expects a list"); + return NULL; + } + + ccab.cb = INT_MAX; /* no need to split CAB into multiple media */ + ccab.cbFolderThresh = 1000000; /* flush directory after this many bytes */ + ccab.cbReserveCFData = 0; + ccab.cbReserveCFFolder = 0; + ccab.cbReserveCFHeader = 0; + + ccab.iCab = 1; + ccab.iDisk = 1; + + ccab.setID = 0; + ccab.szDisk[0] = '\0'; + + for (i = 0, p = cabname; *p; p = CharNext(p)) + if (*p == '\\' || *p == '/') + i = p - cabname + 1; + + if (i >= sizeof(ccab.szCabPath) || + strlen(cabname+i) >= sizeof(ccab.szCab)) { + PyErr_SetString(PyExc_ValueError, "path name too long"); + return 0; + } + + if (i > 0) { + memcpy(ccab.szCabPath, cabname, i); + ccab.szCabPath[i] = '\0'; + strcpy(ccab.szCab, cabname+i); + } else { + strcpy(ccab.szCabPath, ".\\"); + strcpy(ccab.szCab, cabname); + } + + hfci = FCICreate(&erf, cb_fileplaced, cb_alloc, cb_free, + cb_open, cb_read, cb_write, cb_close, cb_seek, cb_delete, + cb_gettempfile, &ccab, NULL); + + if (hfci == NULL) { + PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper); + return NULL; + } + + for (i=0; i < PyList_GET_SIZE(files); i++) { + PyObject *item = PyList_GET_ITEM(files, i); + char *filename, *cabname; + if (!PyArg_ParseTuple(item, "ss", &filename, &cabname)) + goto err; + if (!FCIAddFile(hfci, filename, cabname, FALSE, + cb_getnextcabinet, cb_status, cb_getopeninfo, + tcompTYPE_MSZIP)) + goto err; + } + + if (!FCIFlushCabinet(hfci, FALSE, cb_getnextcabinet, cb_status)) + goto err; + + if (!FCIDestroy(hfci)) + goto err; + + Py_INCREF(Py_None); + return Py_None; +err: + PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper); /* XXX better error type */ + FCIDestroy(hfci); + return NULL; +} + +typedef struct msiobj{ + PyObject_HEAD + MSIHANDLE h; +}msiobj; + +static void +msiobj_dealloc(msiobj* msidb) +{ + MsiCloseHandle(msidb->h); + msidb->h = 0; +} + +static PyObject* +msiobj_close(msiobj* msidb, PyObject *args) +{ + MsiCloseHandle(msidb->h); + msidb->h = 0; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +msierror(int status) +{ + int code; + char buf[2000]; + char *res = buf; + DWORD size = sizeof(buf); + MSIHANDLE err = MsiGetLastErrorRecord(); + + if (err == 0) { + switch(status) { + case ERROR_ACCESS_DENIED: + PyErr_SetString(MSIError, "access denied"); + return NULL; + case ERROR_FUNCTION_FAILED: + PyErr_SetString(MSIError, "function failed"); + return NULL; + case ERROR_INVALID_DATA: + PyErr_SetString(MSIError, "invalid data"); + return NULL; + case ERROR_INVALID_HANDLE: + PyErr_SetString(MSIError, "invalid handle"); + return NULL; + case ERROR_INVALID_STATE: + PyErr_SetString(MSIError, "invalid state"); + return NULL; + case ERROR_INVALID_PARAMETER: + PyErr_SetString(MSIError, "invalid parameter"); + return NULL; + default: + PyErr_Format(MSIError, "unknown error %x", status); + return NULL; + } + } + + code = MsiRecordGetInteger(err, 1); /* XXX code */ + if (MsiFormatRecord(0, err, res, &size) == ERROR_MORE_DATA) { + res = malloc(size+1); + MsiFormatRecord(0, err, res, &size); + res[size]='\0'; + } + MsiCloseHandle(err); + PyErr_SetString(MSIError, res); + if (res != buf) + free(res); + return NULL; +} + +/*************************** Record objects **********************/ + +static PyObject* +record_getfieldcount(msiobj* record, PyObject* args) +{ + return PyLong_FromLong(MsiRecordGetFieldCount(record->h)); +} + +static PyObject* +record_getinteger(msiobj* record, PyObject* args) +{ + unsigned int field; + int status; + + if (!PyArg_ParseTuple(args, "I:GetInteger", &field)) + return NULL; + status = MsiRecordGetInteger(record->h, field); + if (status == MSI_NULL_INTEGER){ + PyErr_SetString(MSIError, "could not convert record field to integer"); + return NULL; + } + return PyLong_FromLong((long) status); +} + +static PyObject* +record_getstring(msiobj* record, PyObject* args) +{ + unsigned int field; + unsigned int status; + WCHAR buf[2000]; + WCHAR *res = buf; + DWORD size = sizeof(buf); + PyObject* string; + + if (!PyArg_ParseTuple(args, "I:GetString", &field)) + return NULL; + status = MsiRecordGetStringW(record->h, field, res, &size); + if (status == ERROR_MORE_DATA) { + res = (WCHAR*) malloc((size + 1)*sizeof(WCHAR)); + if (res == NULL) + return PyErr_NoMemory(); + status = MsiRecordGetStringW(record->h, field, res, &size); + } + if (status != ERROR_SUCCESS) + return msierror((int) status); + string = PyUnicode_FromWideChar(res, size); + if (buf != res) + free(res); + return string; +} + +static PyObject* +record_cleardata(msiobj* record, PyObject *args) +{ + int status = MsiRecordClearData(record->h); + if (status != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +record_setstring(msiobj* record, PyObject *args) +{ + int status; + int field; + wchar_t *data; + + if (!PyArg_ParseTuple(args, "iu:SetString", &field, &data)) + return NULL; + + if ((status = MsiRecordSetStringW(record->h, field, data)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +record_setstream(msiobj* record, PyObject *args) +{ + int status; + int field; + wchar_t *data; + + if (!PyArg_ParseTuple(args, "iu:SetStream", &field, &data)) + return NULL; + + if ((status = MsiRecordSetStreamW(record->h, field, data)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +record_setinteger(msiobj* record, PyObject *args) +{ + int status; + int field; + int data; + + if (!PyArg_ParseTuple(args, "ii:SetInteger", &field, &data)) + return NULL; + + if ((status = MsiRecordSetInteger(record->h, field, data)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + + + +static PyMethodDef record_methods[] = { + { "GetFieldCount", (PyCFunction)record_getfieldcount, METH_NOARGS, + PyDoc_STR("GetFieldCount() -> int\nWraps MsiRecordGetFieldCount")}, + { "GetInteger", (PyCFunction)record_getinteger, METH_VARARGS, + PyDoc_STR("GetInteger(field) -> int\nWraps MsiRecordGetInteger")}, + { "GetString", (PyCFunction)record_getstring, METH_VARARGS, + PyDoc_STR("GetString(field) -> string\nWraps MsiRecordGetString")}, + { "SetString", (PyCFunction)record_setstring, METH_VARARGS, + PyDoc_STR("SetString(field,str) -> None\nWraps MsiRecordSetString")}, + { "SetStream", (PyCFunction)record_setstream, METH_VARARGS, + PyDoc_STR("SetStream(field,filename) -> None\nWraps MsiRecordSetInteger")}, + { "SetInteger", (PyCFunction)record_setinteger, METH_VARARGS, + PyDoc_STR("SetInteger(field,int) -> None\nWraps MsiRecordSetInteger")}, + { "ClearData", (PyCFunction)record_cleardata, METH_NOARGS, + PyDoc_STR("ClearData() -> int\nWraps MsiRecordGClearData")}, + { NULL, NULL } +}; + +static PyTypeObject record_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_msi.Record", /*tp_name*/ + sizeof(msiobj), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)msiobj_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + record_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +static PyObject* +record_new(MSIHANDLE h) +{ + msiobj *result = PyObject_NEW(struct msiobj, &record_Type); + + if (!result) { + MsiCloseHandle(h); + return NULL; + } + + result->h = h; + return (PyObject*)result; +} + +/*************************** SummaryInformation objects **************/ + +static PyObject* +summary_getproperty(msiobj* si, PyObject *args) +{ + int status; + int field; + PyObject *result; + UINT type; + INT ival; + FILETIME fval; + char sbuf[1000]; + char *sval = sbuf; + DWORD ssize = sizeof(sval); + + if (!PyArg_ParseTuple(args, "i:GetProperty", &field)) + return NULL; + + status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival, + &fval, sval, &ssize); + if (status == ERROR_MORE_DATA) { + sval = malloc(ssize); + status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival, + &fval, sval, &ssize); + } + + switch(type) { + case VT_I2: case VT_I4: + return PyLong_FromLong(ival); + case VT_FILETIME: + PyErr_SetString(PyExc_NotImplementedError, "FILETIME result"); + return NULL; + case VT_LPSTR: + result = PyBytes_FromStringAndSize(sval, ssize); + if (sval != sbuf) + free(sval); + return result; + } + PyErr_Format(PyExc_NotImplementedError, "result of type %d", type); + return NULL; +} + +static PyObject* +summary_getpropertycount(msiobj* si, PyObject *args) +{ + int status; + UINT result; + + status = MsiSummaryInfoGetPropertyCount(si->h, &result); + if (status != ERROR_SUCCESS) + return msierror(status); + + return PyLong_FromLong(result); +} + +static PyObject* +summary_setproperty(msiobj* si, PyObject *args) +{ + int status; + int field; + PyObject* data; + + if (!PyArg_ParseTuple(args, "iO:SetProperty", &field, &data)) + return NULL; + + if (PyUnicode_Check(data)) { + status = MsiSummaryInfoSetPropertyW(si->h, field, VT_LPSTR, + 0, NULL, PyUnicode_AsUnicode(data)); + } else if (PyLong_CheckExact(data)) { + long value = PyLong_AsLong(data); + if (value == -1 && PyErr_Occurred()) { + return NULL; + } + status = MsiSummaryInfoSetProperty(si->h, field, VT_I4, + value, NULL, NULL); + } else { + PyErr_SetString(PyExc_TypeError, "unsupported type"); + return NULL; + } + + if (status != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject* +summary_persist(msiobj* si, PyObject *args) +{ + int status; + + status = MsiSummaryInfoPersist(si->h); + if (status != ERROR_SUCCESS) + return msierror(status); + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef summary_methods[] = { + { "GetProperty", (PyCFunction)summary_getproperty, METH_VARARGS, + PyDoc_STR("GetProperty(propid) -> value\nWraps MsiSummaryInfoGetProperty")}, + { "GetPropertyCount", (PyCFunction)summary_getpropertycount, METH_NOARGS, + PyDoc_STR("GetProperty() -> int\nWraps MsiSummaryInfoGetPropertyCount")}, + { "SetProperty", (PyCFunction)summary_setproperty, METH_VARARGS, + PyDoc_STR("SetProperty(value) -> None\nWraps MsiSummaryInfoProperty")}, + { "Persist", (PyCFunction)summary_persist, METH_NOARGS, + PyDoc_STR("Persist() -> None\nWraps MsiSummaryInfoPersist")}, + { NULL, NULL } +}; + +static PyTypeObject summary_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_msi.SummaryInformation", /*tp_name*/ + sizeof(msiobj), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)msiobj_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + summary_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +/*************************** View objects **************/ + +static PyObject* +view_execute(msiobj *view, PyObject*args) +{ + int status; + MSIHANDLE params = 0; + PyObject *oparams = Py_None; + + if (!PyArg_ParseTuple(args, "O:Execute", &oparams)) + return NULL; + + if (oparams != Py_None) { + if (oparams->ob_type != &record_Type) { + PyErr_SetString(PyExc_TypeError, "Execute argument must be a record"); + return NULL; + } + params = ((msiobj*)oparams)->h; + } + + status = MsiViewExecute(view->h, params); + if (status != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +view_fetch(msiobj *view, PyObject*args) +{ + int status; + MSIHANDLE result; + + if ((status = MsiViewFetch(view->h, &result)) != ERROR_SUCCESS) + return msierror(status); + + return record_new(result); +} + +static PyObject* +view_getcolumninfo(msiobj *view, PyObject *args) +{ + int status; + int kind; + MSIHANDLE result; + + if (!PyArg_ParseTuple(args, "i:GetColumnInfo", &kind)) + return NULL; + + if ((status = MsiViewGetColumnInfo(view->h, kind, &result)) != ERROR_SUCCESS) + return msierror(status); + + return record_new(result); +} + +static PyObject* +view_modify(msiobj *view, PyObject *args) +{ + int kind; + PyObject *data; + int status; + + if (!PyArg_ParseTuple(args, "iO:Modify", &kind, &data)) + return NULL; + + if (data->ob_type != &record_Type) { + PyErr_SetString(PyExc_TypeError, "Modify expects a record object"); + return NULL; + } + + if ((status = MsiViewModify(view->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +view_close(msiobj *view, PyObject*args) +{ + int status; + + if ((status = MsiViewClose(view->h)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef view_methods[] = { + { "Execute", (PyCFunction)view_execute, METH_VARARGS, + PyDoc_STR("Execute(params=None) -> None\nWraps MsiViewExecute")}, + { "GetColumnInfo", (PyCFunction)view_getcolumninfo, METH_VARARGS, + PyDoc_STR("GetColumnInfo() -> result\nWraps MsiGetColumnInfo")}, + { "Fetch", (PyCFunction)view_fetch, METH_NOARGS, + PyDoc_STR("Fetch() -> result\nWraps MsiViewFetch")}, + { "Modify", (PyCFunction)view_modify, METH_VARARGS, + PyDoc_STR("Modify(mode,record) -> None\nWraps MsiViewModify")}, + { "Close", (PyCFunction)view_close, METH_NOARGS, + PyDoc_STR("Close() -> result\nWraps MsiViewClose")}, + { NULL, NULL } +}; + +static PyTypeObject msiview_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_msi.View", /*tp_name*/ + sizeof(msiobj), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)msiobj_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + view_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +/*************************** Database objects **************/ + +static PyObject* +msidb_openview(msiobj *msidb, PyObject *args) +{ + int status; + char *sql; + MSIHANDLE hView; + msiobj *result; + + if (!PyArg_ParseTuple(args, "s:OpenView", &sql)) + return NULL; + + if ((status = MsiDatabaseOpenView(msidb->h, sql, &hView)) != ERROR_SUCCESS) + return msierror(status); + + result = PyObject_NEW(struct msiobj, &msiview_Type); + if (!result) { + MsiCloseHandle(hView); + return NULL; + } + + result->h = hView; + return (PyObject*)result; +} + +static PyObject* +msidb_commit(msiobj *msidb, PyObject *args) +{ + int status; + + if ((status = MsiDatabaseCommit(msidb->h)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +msidb_getsummaryinformation(msiobj *db, PyObject *args) +{ + int status; + int count; + MSIHANDLE result; + msiobj *oresult; + + if (!PyArg_ParseTuple(args, "i:GetSummaryInformation", &count)) + return NULL; + + status = MsiGetSummaryInformation(db->h, NULL, count, &result); + if (status != ERROR_SUCCESS) + return msierror(status); + + oresult = PyObject_NEW(struct msiobj, &summary_Type); + if (!result) { + MsiCloseHandle(result); + return NULL; + } + + oresult->h = result; + return (PyObject*)oresult; +} + +static PyMethodDef db_methods[] = { + { "OpenView", (PyCFunction)msidb_openview, METH_VARARGS, + PyDoc_STR("OpenView(sql) -> viewobj\nWraps MsiDatabaseOpenView")}, + { "Commit", (PyCFunction)msidb_commit, METH_NOARGS, + PyDoc_STR("Commit() -> None\nWraps MsiDatabaseCommit")}, + { "GetSummaryInformation", (PyCFunction)msidb_getsummaryinformation, METH_VARARGS, + PyDoc_STR("GetSummaryInformation(updateCount) -> viewobj\nWraps MsiGetSummaryInformation")}, + { NULL, NULL } +}; + +static PyTypeObject msidb_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_msi.Database", /*tp_name*/ + sizeof(msiobj), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)msiobj_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + db_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +static PyObject* msiopendb(PyObject *obj, PyObject *args) +{ + int status; + char *path; + int persist; + MSIHANDLE h; + msiobj *result; + + if (!PyArg_ParseTuple(args, "si:MSIOpenDatabase", &path, &persist)) + return NULL; + + status = MsiOpenDatabase(path, (LPCSTR)persist, &h); + if (status != ERROR_SUCCESS) + return msierror(status); + + result = PyObject_NEW(struct msiobj, &msidb_Type); + if (!result) { + MsiCloseHandle(h); + return NULL; + } + result->h = h; + return (PyObject*)result; +} + +static PyObject* +createrecord(PyObject *o, PyObject *args) +{ + int count; + MSIHANDLE h; + + if (!PyArg_ParseTuple(args, "i:CreateRecord", &count)) + return NULL; + + h = MsiCreateRecord(count); + if (h == 0) + return msierror(0); + + return record_new(h); +} + + +static PyMethodDef msi_methods[] = { + {"UuidCreate", (PyCFunction)uuidcreate, METH_NOARGS, + PyDoc_STR("UuidCreate() -> string")}, + {"FCICreate", (PyCFunction)fcicreate, METH_VARARGS, + PyDoc_STR("fcicreate(cabname,files) -> None")}, + {"OpenDatabase", (PyCFunction)msiopendb, METH_VARARGS, + PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiOpenDatabase")}, + {"CreateRecord", (PyCFunction)createrecord, METH_VARARGS, + PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiCreateRecord")}, + {NULL, NULL} /* sentinel */ +}; + +static char msi_doc[] = "Documentation"; + + +static struct PyModuleDef _msimodule = { + PyModuleDef_HEAD_INIT, + "_msi", + msi_doc, + -1, + msi_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__msi(void) +{ + PyObject *m; + + m = PyModule_Create(&_msimodule); + if (m == NULL) + return NULL; + + PyModule_AddIntConstant(m, "MSIDBOPEN_CREATEDIRECT", (int)MSIDBOPEN_CREATEDIRECT); + PyModule_AddIntConstant(m, "MSIDBOPEN_CREATE", (int)MSIDBOPEN_CREATE); + PyModule_AddIntConstant(m, "MSIDBOPEN_DIRECT", (int)MSIDBOPEN_DIRECT); + PyModule_AddIntConstant(m, "MSIDBOPEN_READONLY", (int)MSIDBOPEN_READONLY); + PyModule_AddIntConstant(m, "MSIDBOPEN_TRANSACT", (int)MSIDBOPEN_TRANSACT); + PyModule_AddIntConstant(m, "MSIDBOPEN_PATCHFILE", (int)MSIDBOPEN_PATCHFILE); + + PyModule_AddIntMacro(m, MSICOLINFO_NAMES); + PyModule_AddIntMacro(m, MSICOLINFO_TYPES); + + PyModule_AddIntMacro(m, MSIMODIFY_SEEK); + PyModule_AddIntMacro(m, MSIMODIFY_REFRESH); + PyModule_AddIntMacro(m, MSIMODIFY_INSERT); + PyModule_AddIntMacro(m, MSIMODIFY_UPDATE); + PyModule_AddIntMacro(m, MSIMODIFY_ASSIGN); + PyModule_AddIntMacro(m, MSIMODIFY_REPLACE); + PyModule_AddIntMacro(m, MSIMODIFY_MERGE); + PyModule_AddIntMacro(m, MSIMODIFY_DELETE); + PyModule_AddIntMacro(m, MSIMODIFY_INSERT_TEMPORARY); + PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE); + PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_NEW); + PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_FIELD); + PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_DELETE); + + PyModule_AddIntMacro(m, PID_CODEPAGE); + PyModule_AddIntMacro(m, PID_TITLE); + PyModule_AddIntMacro(m, PID_SUBJECT); + PyModule_AddIntMacro(m, PID_AUTHOR); + PyModule_AddIntMacro(m, PID_KEYWORDS); + PyModule_AddIntMacro(m, PID_COMMENTS); + PyModule_AddIntMacro(m, PID_TEMPLATE); + PyModule_AddIntMacro(m, PID_LASTAUTHOR); + PyModule_AddIntMacro(m, PID_REVNUMBER); + PyModule_AddIntMacro(m, PID_LASTPRINTED); + PyModule_AddIntMacro(m, PID_CREATE_DTM); + PyModule_AddIntMacro(m, PID_LASTSAVE_DTM); + PyModule_AddIntMacro(m, PID_PAGECOUNT); + PyModule_AddIntMacro(m, PID_WORDCOUNT); + PyModule_AddIntMacro(m, PID_CHARCOUNT); + PyModule_AddIntMacro(m, PID_APPNAME); + PyModule_AddIntMacro(m, PID_SECURITY); + + MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL); + if (!MSIError) + return NULL; + PyModule_AddObject(m, "MSIError", MSIError); + return m; +} diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_719f5f_dcc6ef_Objects#intobject.c b/src/main/resource/testFiles/cpython/prevFiles/prev_719f5f_dcc6ef_Objects#intobject.c new file mode 100644 index 0000000..6b983f1 --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_719f5f_dcc6ef_Objects#intobject.c @@ -0,0 +1,458 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Integer object implementation */ + +#include "allobjects.h" + +/* Standard Booleans */ + +intobject FalseObject = { + OB_HEAD_INIT(&Inttype) + 0 +}; + +intobject TrueObject = { + OB_HEAD_INIT(&Inttype) + 1 +}; + +static object * +err_ovf(msg) + char *msg; +{ + err_setstr(OverflowError, msg); + return NULL; +} + +/* Integers are quite normal objects, to make object handling uniform. + (Using odd pointers to represent integers would save much space + but require extra checks for this special case throughout the code.) + Since, a typical Python program spends much of its time allocating + and deallocating integers, these operations should be very fast. + Therefore we use a dedicated allocation scheme with a much lower + overhead (in space and time) than straight malloc(): a simple + dedicated free list, filled when necessary with memory from malloc(). +*/ + +#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */ +#define N_INTOBJECTS (BLOCK_SIZE / sizeof(intobject)) + +static intobject * +fill_free_list() +{ + intobject *p, *q; + p = NEW(intobject, N_INTOBJECTS); + if (p == NULL) + return (intobject *)err_nomem(); + q = p + N_INTOBJECTS; + while (--q > p) + *(intobject **)q = q-1; + *(intobject **)q = NULL; + return p + N_INTOBJECTS - 1; +} + +static intobject *free_list = NULL; + +object * +newintobject(ival) + long ival; +{ + register intobject *v; + if (free_list == NULL) { + if ((free_list = fill_free_list()) == NULL) + return NULL; + } + v = free_list; + free_list = *(intobject **)free_list; + NEWREF(v); + v->ob_type = &Inttype; + v->ob_ival = ival; + return (object *) v; +} + +static void +int_dealloc(v) + intobject *v; +{ + *(intobject **)v = free_list; + free_list = v; +} + +long +getintvalue(op) + register object *op; +{ + if (!is_intobject(op)) { + err_badcall(); + return -1; + } + else + return ((intobject *)op) -> ob_ival; +} + +/* Methods */ + +static int +int_print(v, fp, flags) + intobject *v; + FILE *fp; + int flags; +{ + fprintf(fp, "%ld", v->ob_ival); + return 0; +} + +static object * +int_repr(v) + intobject *v; +{ + char buf[20]; + sprintf(buf, "%ld", v->ob_ival); + return newstringobject(buf); +} + +static int +int_compare(v, w) + intobject *v, *w; +{ + register long i = v->ob_ival; + register long j = w->ob_ival; + return (i < j) ? -1 : (i > j) ? 1 : 0; +} + +static object * +int_add(v, w) + intobject *v; + intobject *w; +{ + register long a, b, x; + a = v->ob_ival; + b = w->ob_ival; + x = a + b; + if ((x^a) < 0 && (x^b) < 0) + return err_ovf("integer addition"); + return newintobject(x); +} + +static object * +int_sub(v, w) + intobject *v; + intobject *w; +{ + register long a, b, x; + a = v->ob_ival; + b = w->ob_ival; + x = a - b; + if ((x^a) < 0 && (x^~b) < 0) + return err_ovf("integer subtraction"); + return newintobject(x); +} + +static object * +int_mul(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + double x; + a = v->ob_ival; + b = w->ob_ival; + x = (double)a * (double)b; + if (x > 0x7fffffff || x < (double) (long) 0x80000000) + return err_ovf("integer multiplication"); + return newintobject(a * b); +} + +static int +i_divmod(x, y, p_xdivy, p_xmody) + register intobject *x, *y; + long *p_xdivy, *p_xmody; +{ + long xi = x->ob_ival; + long yi = y->ob_ival; + long xdivy, xmody; + + if (yi == 0) { + err_setstr(ZeroDivisionError, "integer division or modulo"); + return -1; + } + if (yi < 0) { + if (xi < 0) + xdivy = -xi / -yi; + else + xdivy = - (xi / -yi); + } + else { + if (xi < 0) + xdivy = - (-xi / yi); + else + xdivy = xi / yi; + } + xmody = xi - xdivy*yi; + if (xmody < 0 && yi > 0 || xmody > 0 && yi < 0) { + xmody += yi; + xdivy -= 1; + } + *p_xdivy = xdivy; + *p_xmody = xmody; + return 0; +} + +static object * +int_div(x, y) + intobject *x; + intobject *y; +{ + long d, m; + if (i_divmod(x, y, &d, &m) < 0) + return NULL; + return newintobject(d); +} + +static object * +int_mod(x, y) + intobject *x; + intobject *y; +{ + long d, m; + if (i_divmod(x, y, &d, &m) < 0) + return NULL; + newintobject(m); +} + +static object * +int_divmod(x, y) + intobject *x; + intobject *y; +{ + object *v, *v0, *v1; + long d, m; + if (i_divmod(x, y, &d, &m) < 0) + return NULL; + v = newtupleobject(2); + v0 = newintobject(d); + v1 = newintobject(m); + if (v == NULL || v0 == NULL || v1 == NULL || + settupleitem(v, 0, v0) != 0 || + settupleitem(v, 1, v1) != 0) { + XDECREF(v); + XDECREF(v0); + XDECREF(v1); + v = NULL; + } + return v; +} + +static object * +int_pow(v, w) + intobject *v; + intobject *w; +{ + register long iv, iw, ix; + iv = v->ob_ival; + iw = w->ob_ival; + if (iw < 0) { + err_setstr(ValueError, "integer to the negative power"); + return NULL; + } + ix = 1; + while (--iw >= 0) { + long prev = ix; + ix = ix * iv; + if (iv == 0) + break; /* 0 to some power -- avoid ix / 0 */ + if (ix / iv != prev) + return err_ovf("integer pow()"); + } + return newintobject(ix); +} + +static object * +int_neg(v) + intobject *v; +{ + register long a, x; + a = v->ob_ival; + x = -a; + if (a < 0 && x < 0) + return err_ovf("integer negation"); + return newintobject(x); +} + +static object * +int_pos(v) + intobject *v; +{ + INCREF(v); + return (object *)v; +} + +static object * +int_abs(v) + intobject *v; +{ + if (v->ob_ival >= 0) + return int_pos(v); + else + return int_neg(v); +} + +static int +int_nonzero(v) + intobject *v; +{ + return v->ob_ival != 0; +} + +static object * +int_invert(v) + intobject *v; +{ + return newintobject(~v->ob_ival); +} + +static object * +int_lshift(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + a = v->ob_ival; + b = w->ob_ival; + if (b < 0) { + err_setstr(ValueError, "negative shift count"); + return NULL; + } + if (a == 0 || b == 0) { + INCREF(v); + return (object *) v; + } + if (b >= 32) { + return newintobject(0L); + } + a = (unsigned long)a << b; + return newintobject(a); +} + +static object * +int_rshift(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + a = v->ob_ival; + b = w->ob_ival; + if (b < 0) { + err_setstr(ValueError, "negative shift count"); + return NULL; + } + if (a == 0 || b == 0) { + INCREF(v); + return (object *) v; + } + if (b >= 32) { + if (a < 0) + a = -1; + else + a = 0; + } + else { + if (a < 0) + a = ~( ~(unsigned long)a >> b ); + else + a = (unsigned long)a >> b; + } + return newintobject(a); +} + +static object * +int_and(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + a = v->ob_ival; + b = w->ob_ival; + return newintobject(a & b); +} + +static object * +int_xor(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + a = v->ob_ival; + b = w->ob_ival; + return newintobject(a ^ b); +} + +static object * +int_or(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + a = v->ob_ival; + b = w->ob_ival; + return newintobject(a | b); +} + +static number_methods int_as_number = { + int_add, /*nb_add*/ + int_sub, /*nb_subtract*/ + int_mul, /*nb_multiply*/ + int_div, /*nb_divide*/ + int_mod, /*nb_remainder*/ + int_divmod, /*nb_divmod*/ + int_pow, /*nb_power*/ + int_neg, /*nb_negative*/ + int_pos, /*nb_positive*/ + int_abs, /*nb_absolute*/ + int_nonzero, /*nb_nonzero*/ + int_invert, /*nb_invert*/ + int_lshift, /*nb_lshift*/ + int_rshift, /*nb_rshift*/ + int_and, /*nb_and*/ + int_xor, /*nb_xor*/ + int_or, /*nb_or*/ +}; + +typeobject Inttype = { + OB_HEAD_INIT(&Typetype) + 0, + "int", + sizeof(intobject), + 0, + int_dealloc, /*tp_dealloc*/ + int_print, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + int_compare, /*tp_compare*/ + int_repr, /*tp_repr*/ + &int_as_number, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ +}; diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_b110da_5abaa2_Modules#_cursesmodule.c b/src/main/resource/testFiles/cpython/prevFiles/prev_b110da_5abaa2_Modules#_cursesmodule.c new file mode 100644 index 0000000..3bf2ca7 --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_b110da_5abaa2_Modules#_cursesmodule.c @@ -0,0 +1,3486 @@ +/* + * This is a curses module for Python. + * + * Based on prior work by Lance Ellinghaus and Oliver Andrich + * Version 1.2 of this module: Copyright 1994 by Lance Ellinghouse, + * Cathedral City, California Republic, United States of America. + * + * Version 1.5b1, heavily extended for ncurses by Oliver Andrich: + * Copyright 1996,1997 by Oliver Andrich, Koblenz, Germany. + * + * Tidied for Python 1.6, and currently maintained by . + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this source file to use, copy, modify, merge, or publish it + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or in any new file that contains a substantial portion of + * this file. + * + * THE AUTHOR MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF + * THE SOFTWARE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT + * EXPRESS OR IMPLIED WARRANTY. THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE, STRICT LIABILITY OR + * ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + + A number of SysV or ncurses functions don't have wrappers yet; if you + need a given function, add it and send a patch. See + http://www.python.org/dev/patches/ for instructions on how to submit + patches to Python. + + Here's a list of currently unsupported functions: + + addchnstr addchstr color_set define_key + del_curterm delscreen dupwin inchnstr inchstr innstr keyok + mcprint mvaddchnstr mvaddchstr mvcur mvinchnstr + mvinchstr mvinnstr mmvwaddchnstr mvwaddchstr + mvwinchnstr mvwinchstr mvwinnstr newterm + restartterm ripoffline scr_dump + scr_init scr_restore scr_set scrl set_curterm set_term setterm + tgetent tgetflag tgetnum tgetstr tgoto timeout tputs + vidattr vidputs waddchnstr waddchstr + wcolor_set winchnstr winchstr winnstr wmouse_trafo wscrl + + Low-priority: + slk_attr slk_attr_off slk_attr_on slk_attr_set slk_attroff + slk_attron slk_attrset slk_clear slk_color slk_init slk_label + slk_noutrefresh slk_refresh slk_restore slk_set slk_touch + + Menu extension (ncurses and probably SYSV): + current_item free_item free_menu item_count item_description + item_index item_init item_name item_opts item_opts_off + item_opts_on item_term item_userptr item_value item_visible + menu_back menu_driver menu_fore menu_format menu_grey + menu_init menu_items menu_mark menu_opts menu_opts_off + menu_opts_on menu_pad menu_pattern menu_request_by_name + menu_request_name menu_spacing menu_sub menu_term menu_userptr + menu_win new_item new_menu pos_menu_cursor post_menu + scale_menu set_current_item set_item_init set_item_opts + set_item_term set_item_userptr set_item_value set_menu_back + set_menu_fore set_menu_format set_menu_grey set_menu_init + set_menu_items set_menu_mark set_menu_opts set_menu_pad + set_menu_pattern set_menu_spacing set_menu_sub set_menu_term + set_menu_userptr set_menu_win set_top_row top_row unpost_menu + + Form extension (ncurses and probably SYSV): + current_field data_ahead data_behind dup_field + dynamic_fieldinfo field_arg field_back field_buffer + field_count field_fore field_index field_info field_init + field_just field_opts field_opts_off field_opts_on field_pad + field_status field_term field_type field_userptr form_driver + form_fields form_init form_opts form_opts_off form_opts_on + form_page form_request_by_name form_request_name form_sub + form_term form_userptr form_win free_field free_form + link_field link_fieldtype move_field new_field new_form + new_page pos_form_cursor post_form scale_form + set_current_field set_field_back set_field_buffer + set_field_fore set_field_init set_field_just set_field_opts + set_field_pad set_field_status set_field_term set_field_type + set_field_userptr set_fieldtype_arg set_fieldtype_choice + set_form_fields set_form_init set_form_opts set_form_page + set_form_sub set_form_term set_form_userptr set_form_win + set_max_field set_new_page unpost_form + + +*/ + +/* Release Number */ + +char *PyCursesVersion = "2.2"; + +/* Includes */ + +#define PY_SSIZE_T_CLEAN + +#include "Python.h" + + +#ifdef __hpux +#define STRICT_SYSV_CURSES +#endif + +#define CURSES_MODULE +#include "py_curses.h" + +/* These prototypes are in , but including this header + #defines many common symbols (such as "lines") which breaks the + curses module in other ways. So the code will just specify + explicit prototypes here. */ +extern int setupterm(char *,int,int *); +#ifdef __sgi +#include +#endif + +#ifdef HAVE_LANGINFO_H +#include +#endif + +#if !defined(HAVE_NCURSES_H) && (defined(sgi) || defined(__sun) || defined(SCO5)) +#define STRICT_SYSV_CURSES /* Don't use ncurses extensions */ +typedef chtype attr_t; /* No attr_t type is available */ +#endif + +#if defined(_AIX) +#define STRICT_SYSV_CURSES +#endif + +/*[clinic input] +module curses +class curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=88c860abdbb50e0c]*/ + +#include "clinic/_cursesmodule.c.h" + +/* Definition of exception curses.error */ + +static PyObject *PyCursesError; + +/* Tells whether setupterm() has been called to initialise terminfo. */ +static int initialised_setupterm = FALSE; + +/* Tells whether initscr() has been called to initialise curses. */ +static int initialised = FALSE; + +/* Tells whether start_color() has been called to initialise color usage. */ +static int initialisedcolors = FALSE; + +static char *screen_encoding = NULL; + +/* Utility Macros */ +#define PyCursesSetupTermCalled \ + if (initialised_setupterm != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call (at least) setupterm() first"); \ + return 0; } + +#define PyCursesInitialised \ + if (initialised != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call initscr() first"); \ + return 0; } + +#define PyCursesInitialisedColor \ + if (initialisedcolors != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call start_color() first"); \ + return 0; } + +/* Utility Functions */ + +/* + * Check the return code from a curses function and return None + * or raise an exception as appropriate. These are exported using the + * capsule API. + */ + +static PyObject * +PyCursesCheckERR(int code, const char *fname) +{ + if (code != ERR) { + Py_INCREF(Py_None); + return Py_None; + } else { + if (fname == NULL) { + PyErr_SetString(PyCursesError, catchall_ERR); + } else { + PyErr_Format(PyCursesError, "%s() returned ERR", fname); + } + return NULL; + } +} + +/* Convert an object to a byte (an integer of type chtype): + + - int + - bytes of length 1 + - str of length 1 + + Return 1 on success, 0 on error (invalid type or integer overflow). */ +static int +PyCurses_ConvertToChtype(PyCursesWindowObject *win, PyObject *obj, chtype *ch) +{ + long value; + if(PyBytes_Check(obj) && PyBytes_Size(obj) == 1) { + value = (unsigned char)PyBytes_AsString(obj)[0]; + } + else if (PyUnicode_Check(obj)) { + if (PyUnicode_GetLength(obj) != 1) { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, " + "got a str of length %zi", + PyUnicode_GET_LENGTH(obj)); + return 0; + } + value = PyUnicode_READ_CHAR(obj, 0); + if (128 < value) { + PyObject *bytes; + const char *encoding; + if (win) + encoding = win->encoding; + else + encoding = screen_encoding; + bytes = PyUnicode_AsEncodedString(obj, encoding, NULL); + if (bytes == NULL) + return 0; + if (PyBytes_GET_SIZE(bytes) == 1) + value = (unsigned char)PyBytes_AS_STRING(bytes)[0]; + else + value = -1; + Py_DECREF(bytes); + if (value < 0) + goto overflow; + } + } + else if (PyLong_CheckExact(obj)) { + int long_overflow; + value = PyLong_AsLongAndOverflow(obj, &long_overflow); + if (long_overflow) + goto overflow; + } + else { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, got %s", + Py_TYPE(obj)->tp_name); + return 0; + } + *ch = (chtype)value; + if ((long)*ch != value) + goto overflow; + return 1; + +overflow: + PyErr_SetString(PyExc_OverflowError, + "byte doesn't fit in chtype"); + return 0; +} + +/* Convert an object to a byte (chtype) or a character (cchar_t): + + - int + - bytes of length 1 + - str of length 1 + + Return: + + - 2 if obj is a character (written into *wch) + - 1 if obj is a byte (written into *ch) + - 0 on error: raise an exception */ +static int +PyCurses_ConvertToCchar_t(PyCursesWindowObject *win, PyObject *obj, + chtype *ch +#ifdef HAVE_NCURSESW + , wchar_t *wch +#endif + ) +{ + long value; +#ifdef HAVE_NCURSESW + wchar_t buffer[2]; +#endif + + if (PyUnicode_Check(obj)) { +#ifdef HAVE_NCURSESW + if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, " + "got a str of length %zi", + PyUnicode_GET_LENGTH(obj)); + return 0; + } + *wch = buffer[0]; + return 2; +#else + return PyCurses_ConvertToChtype(win, obj, ch); +#endif + } + else if(PyBytes_Check(obj) && PyBytes_Size(obj) == 1) { + value = (unsigned char)PyBytes_AsString(obj)[0]; + } + else if (PyLong_CheckExact(obj)) { + int overflow; + value = PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow) { + PyErr_SetString(PyExc_OverflowError, + "int doesn't fit in long"); + return 0; + } + } + else { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, got %s", + Py_TYPE(obj)->tp_name); + return 0; + } + + *ch = (chtype)value; + if ((long)*ch != value) { + PyErr_Format(PyExc_OverflowError, + "byte doesn't fit in chtype"); + return 0; + } + return 1; +} + +/* Convert an object to a byte string (char*) or a wide character string + (wchar_t*). Return: + + - 2 if obj is a character string (written into *wch) + - 1 if obj is a byte string (written into *bytes) + - 0 on error: raise an exception */ +static int +PyCurses_ConvertToString(PyCursesWindowObject *win, PyObject *obj, + PyObject **bytes, wchar_t **wstr) +{ + if (PyUnicode_Check(obj)) { +#ifdef HAVE_NCURSESW + assert (wstr != NULL); + *wstr = PyUnicode_AsWideCharString(obj, NULL); + if (*wstr == NULL) + return 0; + return 2; +#else + assert (wstr == NULL); + *bytes = PyUnicode_AsEncodedString(obj, win->encoding, NULL); + if (*bytes == NULL) + return 0; + return 1; +#endif + } + else if (PyBytes_Check(obj)) { + Py_INCREF(obj); + *bytes = obj; + return 1; + } + + PyErr_Format(PyExc_TypeError, "expect bytes or str, got %s", + Py_TYPE(obj)->tp_name); + return 0; +} + +/* Function versions of the 3 functions for testing whether curses has been + initialised or not. */ + +static int func_PyCursesSetupTermCalled(void) +{ + PyCursesSetupTermCalled; + return 1; +} + +static int func_PyCursesInitialised(void) +{ + PyCursesInitialised; + return 1; +} + +static int func_PyCursesInitialisedColor(void) +{ + PyCursesInitialisedColor; + return 1; +} + +/***************************************************************************** + The Window Object +******************************************************************************/ + +/* Definition of the window type */ + +PyTypeObject PyCursesWindow_Type; + +/* Function prototype macros for Window object + + X - function name + TYPE - parameter Type + ERGSTR - format string for construction of the return value + PARSESTR - format string for argument parsing +*/ + +#define Window_NoArgNoReturnFunction(X) \ + static PyObject *PyCursesWindow_ ## X \ + (PyCursesWindowObject *self, PyObject *args) \ + { return PyCursesCheckERR(X(self->win), # X); } + +#define Window_NoArgTrueFalseFunction(X) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self) \ + { \ + if (X (self->win) == FALSE) { Py_INCREF(Py_False); return Py_False; } \ + else { Py_INCREF(Py_True); return Py_True; } } + +#define Window_NoArgNoReturnVoidFunction(X) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self) \ + { \ + X(self->win); Py_INCREF(Py_None); return Py_None; } + +#define Window_NoArg2TupleReturnFunction(X, TYPE, ERGSTR) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self) \ + { \ + TYPE arg1, arg2; \ + X(self->win,arg1,arg2); return Py_BuildValue(ERGSTR, arg1, arg2); } + +#define Window_OneArgNoReturnVoidFunction(X, TYPE, PARSESTR) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self, PyObject *args) \ + { \ + TYPE arg1; \ + if (!PyArg_ParseTuple(args, PARSESTR, &arg1)) return NULL; \ + X(self->win,arg1); Py_INCREF(Py_None); return Py_None; } + +#define Window_OneArgNoReturnFunction(X, TYPE, PARSESTR) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self, PyObject *args) \ + { \ + TYPE arg1; \ + if (!PyArg_ParseTuple(args,PARSESTR, &arg1)) return NULL; \ + return PyCursesCheckERR(X(self->win, arg1), # X); } + +#define Window_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self, PyObject *args) \ + { \ + TYPE arg1, arg2; \ + if (!PyArg_ParseTuple(args,PARSESTR, &arg1, &arg2)) return NULL; \ + return PyCursesCheckERR(X(self->win, arg1, arg2), # X); } + +/* ------------- WINDOW routines --------------- */ + +Window_NoArgNoReturnFunction(untouchwin) +Window_NoArgNoReturnFunction(touchwin) +Window_NoArgNoReturnFunction(redrawwin) +Window_NoArgNoReturnFunction(winsertln) +Window_NoArgNoReturnFunction(werase) +Window_NoArgNoReturnFunction(wdeleteln) + +Window_NoArgTrueFalseFunction(is_wintouched) + +Window_NoArgNoReturnVoidFunction(wsyncup) +Window_NoArgNoReturnVoidFunction(wsyncdown) +Window_NoArgNoReturnVoidFunction(wstandend) +Window_NoArgNoReturnVoidFunction(wstandout) +Window_NoArgNoReturnVoidFunction(wcursyncup) +Window_NoArgNoReturnVoidFunction(wclrtoeol) +Window_NoArgNoReturnVoidFunction(wclrtobot) +Window_NoArgNoReturnVoidFunction(wclear) + +Window_OneArgNoReturnVoidFunction(idcok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnVoidFunction(immedok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnVoidFunction(wtimeout, int, "i;delay") + +Window_NoArg2TupleReturnFunction(getyx, int, "ii") +Window_NoArg2TupleReturnFunction(getbegyx, int, "ii") +Window_NoArg2TupleReturnFunction(getmaxyx, int, "ii") +Window_NoArg2TupleReturnFunction(getparyx, int, "ii") + +Window_OneArgNoReturnFunction(clearok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnFunction(idlok, int, "i;True(1) or False(0)") +#if defined(__NetBSD__) +Window_OneArgNoReturnVoidFunction(keypad, int, "i;True(1) or False(0)") +#else +Window_OneArgNoReturnFunction(keypad, int, "i;True(1) or False(0)") +#endif +Window_OneArgNoReturnFunction(leaveok, int, "i;True(1) or False(0)") +#if defined(__NetBSD__) +Window_OneArgNoReturnVoidFunction(nodelay, int, "i;True(1) or False(0)") +#else +Window_OneArgNoReturnFunction(nodelay, int, "i;True(1) or False(0)") +#endif +Window_OneArgNoReturnFunction(notimeout, int, "i;True(1) or False(0)") +Window_OneArgNoReturnFunction(scrollok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnFunction(winsdelln, int, "i;nlines") +Window_OneArgNoReturnFunction(syncok, int, "i;True(1) or False(0)") + +Window_TwoArgNoReturnFunction(mvwin, int, "ii;y,x") +Window_TwoArgNoReturnFunction(mvderwin, int, "ii;y,x") +Window_TwoArgNoReturnFunction(wmove, int, "ii;y,x") +#ifndef STRICT_SYSV_CURSES +Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns") +#endif + +/* Allocation and deallocation of Window Objects */ + +static PyObject * +PyCursesWindow_New(WINDOW *win, const char *encoding) +{ + PyCursesWindowObject *wo; + + if (encoding == NULL) { +#if defined(MS_WINDOWS) + char *buffer[100]; + UINT cp; + cp = GetConsoleOutputCP(); + if (cp != 0) { + PyOS_snprintf(buffer, sizeof(buffer), "cp%u", cp); + encoding = buffer; + } +#elif defined(CODESET) + const char *codeset = nl_langinfo(CODESET); + if (codeset != NULL && codeset[0] != 0) + encoding = codeset; +#endif + if (encoding == NULL) + encoding = "utf-8"; + } + + wo = PyObject_NEW(PyCursesWindowObject, &PyCursesWindow_Type); + if (wo == NULL) return NULL; + wo->win = win; + wo->encoding = _PyMem_Strdup(encoding); + if (wo->encoding == NULL) { + Py_DECREF(wo); + PyErr_NoMemory(); + return NULL; + } + return (PyObject *)wo; +} + +static void +PyCursesWindow_Dealloc(PyCursesWindowObject *wo) +{ + if (wo->win != stdscr) delwin(wo->win); + if (wo->encoding != NULL) + PyMem_Free(wo->encoding); + PyObject_DEL(wo); +} + +/* Addch, Addstr, Addnstr */ + +/*[clinic input] + +curses.window.addch + + [ + y: int + Y-coordinate. + x: int + X-coordinate. + ] + + ch: object + Character to add. + + [ + attr: long + Attributes for the character. + ] + / + +Paint character ch at (y, x) with attributes attr. + +Paint character ch at (y, x) with attributes attr, +overwriting any character previously painted at that location. +By default, the character position and attributes are the +current settings for the window object. +[clinic start generated code]*/ + +static PyObject * +curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1, int y, + int x, PyObject *ch, int group_right_1, long attr) +/*[clinic end generated code: output=99f7f85078ec06c3 input=5a41efb34a2de338]*/ +{ + PyCursesWindowObject *cwself = (PyCursesWindowObject *)self; + int coordinates_group = group_left_1; + int attr_group = group_right_1; + int rtn; + int type; + chtype cch; +#ifdef HAVE_NCURSESW + wchar_t wstr[2]; + cchar_t wcval; +#endif + const char *funcname; + + if (!attr_group) + attr = A_NORMAL; + +#ifdef HAVE_NCURSESW + type = PyCurses_ConvertToCchar_t(cwself, ch, &cch, wstr); + if (type == 2) { + funcname = "add_wch"; + wstr[1] = L'\0'; + setcchar(&wcval, wstr, attr, 0, NULL); + if (coordinates_group) + rtn = mvwadd_wch(cwself->win,y,x, &wcval); + else { + rtn = wadd_wch(cwself->win, &wcval); + } + } + else +#else + type = PyCurses_ConvertToCchar_t(cwself, ch, &cch); +#endif + if (type == 1) { + funcname = "addch"; + if (coordinates_group) + rtn = mvwaddch(cwself->win,y,x, cch | attr); + else { + rtn = waddch(cwself->win, cch | attr); + } + } + else { + return NULL; + } + return PyCursesCheckERR(rtn, funcname); +} + +static PyObject * +PyCursesWindow_AddStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + long lattr; + int use_xy = FALSE, use_attr = FALSE; + const char *funcname; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"O;str", &strobj)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;str,attr", &strobj, &lattr)) + return NULL; + attr = lattr; + use_attr = TRUE; + break; + case 3: + if (!PyArg_ParseTuple(args,"iiO;int,int,str", &y, &x, &strobj)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOl;int,int,str,attr", &y, &x, &strobj, &lattr)) + return NULL; + attr = lattr; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "addstr requires 1 to 4 arguments"); + return NULL; + } +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + (void)wattrset(self->win,attr); + } +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "addwstr"; + if (use_xy == TRUE) + rtn = mvwaddwstr(self->win,y,x,wstr); + else + rtn = waddwstr(self->win,wstr); + PyMem_Free(wstr); + } + else +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "addstr"; + if (use_xy == TRUE) + rtn = mvwaddstr(self->win,y,x,str); + else + rtn = waddstr(self->win,str); + Py_DECREF(bytesobj); + } + if (use_attr == TRUE) + (void)wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, funcname); +} + +static PyObject * +PyCursesWindow_AddNStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn, x, y, n; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + long lattr; + int use_xy = FALSE, use_attr = FALSE; + const char *funcname; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"Oi;str,n", &strobj, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args,"Oil;str,n,attr", &strobj, &n, &lattr)) + return NULL; + attr = lattr; + use_attr = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOi;y,x,str,n", &y, &x, &strobj, &n)) + return NULL; + use_xy = TRUE; + break; + case 5: + if (!PyArg_ParseTuple(args,"iiOil;y,x,str,n,attr", &y, &x, &strobj, &n, &lattr)) + return NULL; + attr = lattr; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "addnstr requires 2 to 5 arguments"); + return NULL; + } +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; + + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + (void)wattrset(self->win,attr); + } +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "addnwstr"; + if (use_xy == TRUE) + rtn = mvwaddnwstr(self->win,y,x,wstr,n); + else + rtn = waddnwstr(self->win,wstr,n); + PyMem_Free(wstr); + } + else +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "addnstr"; + if (use_xy == TRUE) + rtn = mvwaddnstr(self->win,y,x,str,n); + else + rtn = waddnstr(self->win,str,n); + Py_DECREF(bytesobj); + } + if (use_attr == TRUE) + (void)wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, funcname); +} + +static PyObject * +PyCursesWindow_Bkgd(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype bkgd; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;ch or int,attr", &temp, &lattr)) + return NULL; + attr = lattr; + break; + default: + PyErr_SetString(PyExc_TypeError, "bkgd requires 1 or 2 arguments"); + return NULL; + } + + if (!PyCurses_ConvertToChtype(self, temp, &bkgd)) + return NULL; + + return PyCursesCheckERR(wbkgd(self->win, bkgd | attr), "bkgd"); +} + +static PyObject * +PyCursesWindow_AttrOff(PyCursesWindowObject *self, PyObject *args) +{ + long lattr; + if (!PyArg_ParseTuple(args,"l;attr", &lattr)) + return NULL; + return PyCursesCheckERR(wattroff(self->win, (attr_t)lattr), "attroff"); +} + +static PyObject * +PyCursesWindow_AttrOn(PyCursesWindowObject *self, PyObject *args) +{ + long lattr; + if (!PyArg_ParseTuple(args,"l;attr", &lattr)) + return NULL; + return PyCursesCheckERR(wattron(self->win, (attr_t)lattr), "attron"); +} + +static PyObject * +PyCursesWindow_AttrSet(PyCursesWindowObject *self, PyObject *args) +{ + long lattr; + if (!PyArg_ParseTuple(args,"l;attr", &lattr)) + return NULL; + return PyCursesCheckERR(wattrset(self->win, (attr_t)lattr), "attrset"); +} + +static PyObject * +PyCursesWindow_BkgdSet(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype bkgd; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;ch or int,attr", &temp, &lattr)) + return NULL; + attr = lattr; + break; + default: + PyErr_SetString(PyExc_TypeError, "bkgdset requires 1 or 2 arguments"); + return NULL; + } + + if (!PyCurses_ConvertToChtype(self, temp, &bkgd)) + return NULL; + + wbkgdset(self->win, bkgd | attr); + return PyCursesCheckERR(0, "bkgdset"); +} + +static PyObject * +PyCursesWindow_Border(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp[8]; + chtype ch[8]; + int i; + + /* Clear the array of parameters */ + for(i=0; i<8; i++) { + temp[i] = NULL; + ch[i] = 0; + } + + if (!PyArg_ParseTuple(args,"|OOOOOOOO;ls,rs,ts,bs,tl,tr,bl,br", + &temp[0], &temp[1], &temp[2], &temp[3], + &temp[4], &temp[5], &temp[6], &temp[7])) + return NULL; + + for(i=0; i<8; i++) { + if (temp[i] != NULL && !PyCurses_ConvertToChtype(self, temp[i], &ch[i])) + return NULL; + } + + wborder(self->win, + ch[0], ch[1], ch[2], ch[3], + ch[4], ch[5], ch[6], ch[7]); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCursesWindow_Box(PyCursesWindowObject *self, PyObject *args) +{ + chtype ch1=0,ch2=0; + switch(PyTuple_Size(args)){ + case 0: break; + default: + if (!PyArg_ParseTuple(args,"ll;vertint,horint", &ch1, &ch2)) + return NULL; + } + box(self->win,ch1,ch2); + Py_INCREF(Py_None); + return Py_None; +} + +#if defined(HAVE_NCURSES_H) || defined(MVWDELCH_IS_EXPRESSION) +#define py_mvwdelch mvwdelch +#else +int py_mvwdelch(WINDOW *w, int y, int x) +{ + mvwdelch(w,y,x); + /* On HP/UX, mvwdelch already returns. On other systems, + we may well run into this return statement. */ + return 0; +} +#endif + +/* chgat, added by Fabian Kreutz */ + +static PyObject * +PyCursesWindow_ChgAt(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + int num = -1; + short color; + attr_t attr = A_NORMAL; + long lattr; + int use_xy = FALSE; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"l;attr", &lattr)) + return NULL; + attr = lattr; + break; + case 2: + if (!PyArg_ParseTuple(args,"il;n,attr", &num, &lattr)) + return NULL; + attr = lattr; + break; + case 3: + if (!PyArg_ParseTuple(args,"iil;int,int,attr", &y, &x, &lattr)) + return NULL; + attr = lattr; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiil;int,int,n,attr", &y, &x, &num, &lattr)) + return NULL; + attr = lattr; + use_xy = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "chgat requires 1 to 4 arguments"); + return NULL; + } + + color = (short)((attr >> 8) & 0xff); + attr = attr - (color << 8); + + if (use_xy == TRUE) { + rtn = mvwchgat(self->win,y,x,num,attr,color,NULL); + touchline(self->win,y,1); + } else { + getyx(self->win,y,x); + rtn = wchgat(self->win,num,attr,color,NULL); + touchline(self->win,y,1); + } + return PyCursesCheckERR(rtn, "chgat"); +} + + +static PyObject * +PyCursesWindow_DelCh(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + + switch (PyTuple_Size(args)) { + case 0: + rtn = wdelch(self->win); + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x", &y, &x)) + return NULL; + rtn = py_mvwdelch(self->win,y,x); + break; + default: + PyErr_SetString(PyExc_TypeError, "delch requires 0 or 2 arguments"); + return NULL; + } + return PyCursesCheckERR(rtn, "[mv]wdelch"); +} + +static PyObject * +PyCursesWindow_DerWin(PyCursesWindowObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols, begin_y, begin_x; + + nlines = 0; + ncols = 0; + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;begin_y,begin_x",&begin_y,&begin_x)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiii;nlines,ncols,begin_y,begin_x", + &nlines,&ncols,&begin_y,&begin_x)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "derwin requires 2 or 4 arguments"); + return NULL; + } + + win = derwin(self->win,nlines,ncols,begin_y,begin_x); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win, NULL); +} + +static PyObject * +PyCursesWindow_EchoChar(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;ch or int,attr", &temp, &lattr)) + return NULL; + attr = lattr; + break; + default: + PyErr_SetString(PyExc_TypeError, "echochar requires 1 or 2 arguments"); + + + return NULL; + } + + if (!PyCurses_ConvertToChtype(self, temp, &ch)) + return NULL; + +#ifdef WINDOW_HAS_FLAGS + if (self->win->_flags & _ISPAD) + return PyCursesCheckERR(pechochar(self->win, ch | attr), + "echochar"); + else +#endif + return PyCursesCheckERR(wechochar(self->win, ch | attr), + "echochar"); +} + +#ifdef NCURSES_MOUSE_VERSION +static PyObject * +PyCursesWindow_Enclose(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + if (!PyArg_ParseTuple(args,"ii;y,x", &y, &x)) + return NULL; + + return PyLong_FromLong( wenclose(self->win,y,x) ); +} +#endif + +static PyObject * +PyCursesWindow_GetBkgd(PyCursesWindowObject *self) +{ + return PyLong_FromLong((long) getbkgd(self->win)); +} + +static PyObject * +PyCursesWindow_GetCh(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + int rtn; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + rtn = wgetch(self->win); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = mvwgetch(self->win,y,x); + Py_END_ALLOW_THREADS + break; + default: + PyErr_SetString(PyExc_TypeError, "getch requires 0 or 2 arguments"); + return NULL; + } + return PyLong_FromLong((long)rtn); +} + +static PyObject * +PyCursesWindow_GetKey(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + int rtn; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + rtn = wgetch(self->win); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = mvwgetch(self->win,y,x); + Py_END_ALLOW_THREADS + break; + default: + PyErr_SetString(PyExc_TypeError, "getkey requires 0 or 2 arguments"); + return NULL; + } + if (rtn == ERR) { + /* getch() returns ERR in nodelay mode */ + PyErr_CheckSignals(); + if (!PyErr_Occurred()) + PyErr_SetString(PyCursesError, "no input"); + return NULL; + } else if (rtn<=255) { + return Py_BuildValue("C", rtn); + } else { + const char *knp; +#if defined(__NetBSD__) + knp = unctrl(rtn); +#else + knp = keyname(rtn); +#endif + return PyUnicode_FromString((knp == NULL) ? "" : knp); + } +} + +#ifdef HAVE_NCURSESW +static PyObject * +PyCursesWindow_Get_WCh(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + int ct; + wint_t rtn; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + ct = wget_wch(self->win,&rtn); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS + ct = mvwget_wch(self->win,y,x,&rtn); + Py_END_ALLOW_THREADS + break; + default: + PyErr_SetString(PyExc_TypeError, "get_wch requires 0 or 2 arguments"); + return NULL; + } + if (ct == ERR) { + if (PyErr_CheckSignals()) + return NULL; + + /* get_wch() returns ERR in nodelay mode */ + PyErr_SetString(PyCursesError, "no input"); + return NULL; + } + if (ct == KEY_CODE_YES) + return PyLong_FromLong(rtn); + else + return PyUnicode_FromOrdinal(rtn); +} +#endif + +static PyObject * +PyCursesWindow_GetStr(PyCursesWindowObject *self, PyObject *args) +{ + int x, y, n; + char rtn[1024]; /* This should be big enough.. I hope */ + int rtn2; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + rtn2 = wgetnstr(self->win,rtn, 1023); + Py_END_ALLOW_THREADS + break; + case 1: + if (!PyArg_ParseTuple(args,"i;n", &n)) + return NULL; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + rtn2 = wgetnstr(self->win, rtn, Py_MIN(n, 1023)); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS +#ifdef STRICT_SYSV_CURSES + rtn2 = wmove(self->win,y,x)==ERR ? ERR : wgetnstr(self->win, rtn, 1023); +#else + rtn2 = mvwgetnstr(self->win,y,x,rtn, 1023); +#endif + Py_END_ALLOW_THREADS + break; + case 3: + if (!PyArg_ParseTuple(args,"iii;y,x,n", &y, &x, &n)) + return NULL; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); + return NULL; + } +#ifdef STRICT_SYSV_CURSES + Py_BEGIN_ALLOW_THREADS + rtn2 = wmove(self->win,y,x)==ERR ? ERR : + wgetnstr(self->win, rtn, Py_MIN(n, 1023)); + Py_END_ALLOW_THREADS +#else + Py_BEGIN_ALLOW_THREADS + rtn2 = mvwgetnstr(self->win, y, x, rtn, Py_MIN(n, 1023)); + Py_END_ALLOW_THREADS +#endif + break; + default: + PyErr_SetString(PyExc_TypeError, "getstr requires 0 to 3 arguments"); + return NULL; + } + if (rtn2 == ERR) + rtn[0] = 0; + return PyBytes_FromString(rtn); +} + +static PyObject * +PyCursesWindow_Hline(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + int n, x, y, code = OK; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args, "Oi;ch or int,n", &temp, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args, "Oil;ch or int,n,attr", &temp, &n, &lattr)) + return NULL; + attr = lattr; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiOi;y,x,ch or int,n", &y, &x, &temp, &n)) + return NULL; + code = wmove(self->win, y, x); + break; + case 5: + if (!PyArg_ParseTuple(args, "iiOil; y,x,ch or int,n,attr", + &y, &x, &temp, &n, &lattr)) + return NULL; + attr = lattr; + code = wmove(self->win, y, x); + break; + default: + PyErr_SetString(PyExc_TypeError, "hline requires 2 to 5 arguments"); + return NULL; + } + + if (code != ERR) { + if (!PyCurses_ConvertToChtype(self, temp, &ch)) + return NULL; + return PyCursesCheckERR(whline(self->win, ch | attr, n), "hline"); + } else + return PyCursesCheckERR(code, "wmove"); +} + +static PyObject * +PyCursesWindow_InsCh(PyCursesWindowObject *self, PyObject *args) +{ + int rtn, x, y, use_xy = FALSE; + PyObject *temp; + chtype ch = 0; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args, "Ol;ch or int,attr", &temp, &lattr)) + return NULL; + attr = lattr; + break; + case 3: + if (!PyArg_ParseTuple(args,"iiO;y,x,ch or int", &y, &x, &temp)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOl;y,x,ch or int, attr", &y, &x, &temp, &lattr)) + return NULL; + attr = lattr; + use_xy = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "insch requires 1 or 4 arguments"); + return NULL; + } + + if (!PyCurses_ConvertToChtype(self, temp, &ch)) + return NULL; + + if (use_xy == TRUE) + rtn = mvwinsch(self->win,y,x, ch | attr); + else { + rtn = winsch(self->win, ch | attr); + } + return PyCursesCheckERR(rtn, "insch"); +} + +static PyObject * +PyCursesWindow_InCh(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + unsigned long rtn; + + switch (PyTuple_Size(args)) { + case 0: + rtn = winch(self->win); + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + rtn = mvwinch(self->win,y,x); + break; + default: + PyErr_SetString(PyExc_TypeError, "inch requires 0 or 2 arguments"); + return NULL; + } + return PyLong_FromUnsignedLong(rtn); +} + +static PyObject * +PyCursesWindow_InStr(PyCursesWindowObject *self, PyObject *args) +{ + int x, y, n; + char rtn[1024]; /* This should be big enough.. I hope */ + int rtn2; + + switch (PyTuple_Size(args)) { + case 0: + rtn2 = winnstr(self->win,rtn, 1023); + break; + case 1: + if (!PyArg_ParseTuple(args,"i;n", &n)) + return NULL; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); + return NULL; + } + rtn2 = winnstr(self->win, rtn, Py_MIN(n, 1023)); + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + rtn2 = mvwinnstr(self->win,y,x,rtn,1023); + break; + case 3: + if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n)) + return NULL; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); + return NULL; + } + rtn2 = mvwinnstr(self->win, y, x, rtn, Py_MIN(n,1023)); + break; + default: + PyErr_SetString(PyExc_TypeError, "instr requires 0 or 3 arguments"); + return NULL; + } + if (rtn2 == ERR) + rtn[0] = 0; + return PyBytes_FromString(rtn); +} + +static PyObject * +PyCursesWindow_InsStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + long lattr; + int use_xy = FALSE, use_attr = FALSE; + const char *funcname; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"O;str", &strobj)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;str,attr", &strobj, &lattr)) + return NULL; + attr = lattr; + use_attr = TRUE; + break; + case 3: + if (!PyArg_ParseTuple(args,"iiO;y,x,str", &y, &x, &strobj)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOl;y,x,str,attr", &y, &x, &strobj, &lattr)) + return NULL; + attr = lattr; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "insstr requires 1 to 4 arguments"); + return NULL; + } + +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; + + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + (void)wattrset(self->win,attr); + } +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "inswstr"; + if (use_xy == TRUE) + rtn = mvwins_wstr(self->win,y,x,wstr); + else + rtn = wins_wstr(self->win,wstr); + PyMem_Free(wstr); + } + else +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "insstr"; + if (use_xy == TRUE) + rtn = mvwinsstr(self->win,y,x,str); + else + rtn = winsstr(self->win,str); + Py_DECREF(bytesobj); + } + if (use_attr == TRUE) + (void)wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, funcname); +} + +static PyObject * +PyCursesWindow_InsNStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn, x, y, n; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + long lattr; + int use_xy = FALSE, use_attr = FALSE; + const char *funcname; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"Oi;str,n", &strobj, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args,"Oil;str,n,attr", &strobj, &n, &lattr)) + return NULL; + attr = lattr; + use_attr = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOi;y,x,str,n", &y, &x, &strobj, &n)) + return NULL; + use_xy = TRUE; + break; + case 5: + if (!PyArg_ParseTuple(args,"iiOil;y,x,str,n,attr", &y, &x, &strobj, &n, &lattr)) + return NULL; + attr = lattr; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "insnstr requires 2 to 5 arguments"); + return NULL; + } + +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; + + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + (void)wattrset(self->win,attr); + } +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "insn_wstr"; + if (use_xy == TRUE) + rtn = mvwins_nwstr(self->win,y,x,wstr,n); + else + rtn = wins_nwstr(self->win,wstr,n); + PyMem_Free(wstr); + } + else +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "insnstr"; + if (use_xy == TRUE) + rtn = mvwinsnstr(self->win,y,x,str,n); + else + rtn = winsnstr(self->win,str,n); + Py_DECREF(bytesobj); + } + if (use_attr == TRUE) + (void)wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, funcname); +} + +static PyObject * +PyCursesWindow_Is_LineTouched(PyCursesWindowObject *self, PyObject *args) +{ + int line, erg; + if (!PyArg_ParseTuple(args,"i;line", &line)) + return NULL; + erg = is_linetouched(self->win, line); + if (erg == ERR) { + PyErr_SetString(PyExc_TypeError, + "is_linetouched: line number outside of boundaries"); + return NULL; + } else + if (erg == FALSE) { + Py_INCREF(Py_False); + return Py_False; + } else { + Py_INCREF(Py_True); + return Py_True; + } +} + +static PyObject * +PyCursesWindow_NoOutRefresh(PyCursesWindowObject *self, PyObject *args) +{ + int pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol; + int rtn; + +#ifndef WINDOW_HAS_FLAGS + if (0) +#else + if (self->win->_flags & _ISPAD) +#endif + { + switch(PyTuple_Size(args)) { + case 6: + if (!PyArg_ParseTuple(args, + "iiiiii;" \ + "pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol", + &pminrow, &pmincol, &sminrow, + &smincol, &smaxrow, &smaxcol)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = pnoutrefresh(self->win, + pminrow, pmincol, sminrow, + smincol, smaxrow, smaxcol); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "pnoutrefresh"); + default: + PyErr_SetString(PyCursesError, + "noutrefresh() called for a pad " + "requires 6 arguments"); + return NULL; + } + } else { + if (!PyArg_ParseTuple(args, ":noutrefresh")) + return NULL; + + Py_BEGIN_ALLOW_THREADS + rtn = wnoutrefresh(self->win); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "wnoutrefresh"); + } +} + +static PyObject * +PyCursesWindow_Overlay(PyCursesWindowObject *self, PyObject *args) +{ + PyCursesWindowObject *temp; + int use_copywin = FALSE; + int sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol; + int rtn; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O!;window object", + &PyCursesWindow_Type, &temp)) + return NULL; + break; + case 7: + if (!PyArg_ParseTuple(args, "O!iiiiii;window object, int, int, int, int, int, int", + &PyCursesWindow_Type, &temp, &sminrow, &smincol, + &dminrow, &dmincol, &dmaxrow, &dmaxcol)) + return NULL; + use_copywin = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, + "overlay requires one or seven arguments"); + return NULL; + } + + if (use_copywin == TRUE) { + rtn = copywin(self->win, temp->win, sminrow, smincol, + dminrow, dmincol, dmaxrow, dmaxcol, TRUE); + return PyCursesCheckERR(rtn, "copywin"); + } + else { + rtn = overlay(self->win, temp->win); + return PyCursesCheckERR(rtn, "overlay"); + } +} + +static PyObject * +PyCursesWindow_Overwrite(PyCursesWindowObject *self, PyObject *args) +{ + PyCursesWindowObject *temp; + int use_copywin = FALSE; + int sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol; + int rtn; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O!;window object", + &PyCursesWindow_Type, &temp)) + return NULL; + break; + case 7: + if (!PyArg_ParseTuple(args, "O!iiiiii;window object, int, int, int, int, int, int", + &PyCursesWindow_Type, &temp, &sminrow, &smincol, + &dminrow, &dmincol, &dmaxrow, &dmaxcol)) + return NULL; + use_copywin = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, + "overwrite requires one or seven arguments"); + return NULL; + } + + if (use_copywin == TRUE) { + rtn = copywin(self->win, temp->win, sminrow, smincol, + dminrow, dmincol, dmaxrow, dmaxcol, FALSE); + return PyCursesCheckERR(rtn, "copywin"); + } + else { + rtn = overwrite(self->win, temp->win); + return PyCursesCheckERR(rtn, "overwrite"); + } +} + +static PyObject * +PyCursesWindow_PutWin(PyCursesWindowObject *self, PyObject *stream) +{ + /* We have to simulate this by writing to a temporary FILE*, + then reading back, then writing to the argument stream. */ + char fn[100]; + int fd = -1; + FILE *fp = NULL; + PyObject *res = NULL; + + strcpy(fn, "/tmp/py.curses.putwin.XXXXXX"); + fd = mkstemp(fn); + if (fd < 0) + return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + if (_Py_set_inheritable(fd, 0, NULL) < 0) + goto exit; + fp = fdopen(fd, "wb+"); + if (fp == NULL) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + goto exit; + } + res = PyCursesCheckERR(putwin(self->win, fp), "putwin"); + if (res == NULL) + goto exit; + fseek(fp, 0, 0); + while (1) { + char buf[BUFSIZ]; + Py_ssize_t n = fread(buf, 1, BUFSIZ, fp); + _Py_IDENTIFIER(write); + + if (n <= 0) + break; + Py_DECREF(res); + res = _PyObject_CallMethodId(stream, &PyId_write, "y#", buf, n); + if (res == NULL) + break; + } + +exit: + if (fp != NULL) + fclose(fp); + else if (fd != -1) + close(fd); + remove(fn); + return res; +} + +static PyObject * +PyCursesWindow_RedrawLine(PyCursesWindowObject *self, PyObject *args) +{ + int beg, num; + if (!PyArg_ParseTuple(args, "ii;beg,num", &beg, &num)) + return NULL; + return PyCursesCheckERR(wredrawln(self->win,beg,num), "redrawln"); +} + +static PyObject * +PyCursesWindow_Refresh(PyCursesWindowObject *self, PyObject *args) +{ + int pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol; + int rtn; + +#ifndef WINDOW_HAS_FLAGS + if (0) +#else + if (self->win->_flags & _ISPAD) +#endif + { + switch(PyTuple_Size(args)) { + case 6: + if (!PyArg_ParseTuple(args, + "iiiiii;" \ + "pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol", + &pminrow, &pmincol, &sminrow, + &smincol, &smaxrow, &smaxcol)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + rtn = prefresh(self->win, + pminrow, pmincol, sminrow, + smincol, smaxrow, smaxcol); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "prefresh"); + default: + PyErr_SetString(PyCursesError, + "refresh() for a pad requires 6 arguments"); + return NULL; + } + } else { + if (!PyArg_ParseTuple(args, ":refresh")) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = wrefresh(self->win); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "prefresh"); + } +} + +static PyObject * +PyCursesWindow_SetScrollRegion(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + if (!PyArg_ParseTuple(args,"ii;top, bottom",&y,&x)) + return NULL; + return PyCursesCheckERR(wsetscrreg(self->win,y,x), "wsetscrreg"); +} + +static PyObject * +PyCursesWindow_SubWin(PyCursesWindowObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols, begin_y, begin_x; + + nlines = 0; + ncols = 0; + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;begin_y,begin_x",&begin_y,&begin_x)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiii;nlines,ncols,begin_y,begin_x", + &nlines,&ncols,&begin_y,&begin_x)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "subwin requires 2 or 4 arguments"); + return NULL; + } + + /* printf("Subwin: %i %i %i %i \n", nlines, ncols, begin_y, begin_x); */ +#ifdef WINDOW_HAS_FLAGS + if (self->win->_flags & _ISPAD) + win = subpad(self->win, nlines, ncols, begin_y, begin_x); + else +#endif + win = subwin(self->win, nlines, ncols, begin_y, begin_x); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win, self->encoding); +} + +static PyObject * +PyCursesWindow_Scroll(PyCursesWindowObject *self, PyObject *args) +{ + int nlines; + switch(PyTuple_Size(args)) { + case 0: + return PyCursesCheckERR(scroll(self->win), "scroll"); + case 1: + if (!PyArg_ParseTuple(args, "i;nlines", &nlines)) + return NULL; + return PyCursesCheckERR(wscrl(self->win, nlines), "scroll"); + default: + PyErr_SetString(PyExc_TypeError, "scroll requires 0 or 1 arguments"); + return NULL; + } +} + +static PyObject * +PyCursesWindow_TouchLine(PyCursesWindowObject *self, PyObject *args) +{ + int st, cnt, val; + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;start,count",&st,&cnt)) + return NULL; + return PyCursesCheckERR(touchline(self->win,st,cnt), "touchline"); + case 3: + if (!PyArg_ParseTuple(args, "iii;start,count,val", &st, &cnt, &val)) + return NULL; + return PyCursesCheckERR(wtouchln(self->win, st, cnt, val), "touchline"); + default: + PyErr_SetString(PyExc_TypeError, "touchline requires 2 or 3 arguments"); + return NULL; + } +} + +static PyObject * +PyCursesWindow_Vline(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + int n, x, y, code = OK; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args, "Oi;ch or int,n", &temp, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args, "Oil;ch or int,n,attr", &temp, &n, &lattr)) + return NULL; + attr = lattr; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiOi;y,x,ch or int,n", &y, &x, &temp, &n)) + return NULL; + code = wmove(self->win, y, x); + break; + case 5: + if (!PyArg_ParseTuple(args, "iiOil; y,x,ch or int,n,attr", + &y, &x, &temp, &n, &lattr)) + return NULL; + attr = lattr; + code = wmove(self->win, y, x); + break; + default: + PyErr_SetString(PyExc_TypeError, "vline requires 2 to 5 arguments"); + return NULL; + } + + if (code != ERR) { + if (!PyCurses_ConvertToChtype(self, temp, &ch)) + return NULL; + return PyCursesCheckERR(wvline(self->win, ch | attr, n), "vline"); + } else + return PyCursesCheckERR(code, "wmove"); +} + +static PyObject * +PyCursesWindow_get_encoding(PyCursesWindowObject *self, void *closure) +{ + return PyUnicode_FromString(self->encoding); +} + +static int +PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value) +{ + PyObject *ascii; + char *encoding; + + /* It is illegal to del win.encoding */ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "encoding may not be deleted"); + return -1; + } + + if (!PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "setting encoding to a non-string"); + return -1; + } + ascii = PyUnicode_AsASCIIString(value); + if (ascii == NULL) + return -1; + encoding = _PyMem_Strdup(PyBytes_AS_STRING(ascii)); + Py_DECREF(ascii); + if (encoding == NULL) { + PyErr_NoMemory(); + return -1; + } + PyMem_Free(self->encoding); + self->encoding = encoding; + return 0; +} + + +static PyMethodDef PyCursesWindow_Methods[] = { + CURSES_WINDOW_ADDCH_METHODDEF + {"addnstr", (PyCFunction)PyCursesWindow_AddNStr, METH_VARARGS}, + {"addstr", (PyCFunction)PyCursesWindow_AddStr, METH_VARARGS}, + {"attroff", (PyCFunction)PyCursesWindow_AttrOff, METH_VARARGS}, + {"attron", (PyCFunction)PyCursesWindow_AttrOn, METH_VARARGS}, + {"attrset", (PyCFunction)PyCursesWindow_AttrSet, METH_VARARGS}, + {"bkgd", (PyCFunction)PyCursesWindow_Bkgd, METH_VARARGS}, + {"chgat", (PyCFunction)PyCursesWindow_ChgAt, METH_VARARGS}, + {"bkgdset", (PyCFunction)PyCursesWindow_BkgdSet, METH_VARARGS}, + {"border", (PyCFunction)PyCursesWindow_Border, METH_VARARGS}, + {"box", (PyCFunction)PyCursesWindow_Box, METH_VARARGS}, + {"clear", (PyCFunction)PyCursesWindow_wclear, METH_NOARGS}, + {"clearok", (PyCFunction)PyCursesWindow_clearok, METH_VARARGS}, + {"clrtobot", (PyCFunction)PyCursesWindow_wclrtobot, METH_NOARGS}, + {"clrtoeol", (PyCFunction)PyCursesWindow_wclrtoeol, METH_NOARGS}, + {"cursyncup", (PyCFunction)PyCursesWindow_wcursyncup, METH_NOARGS}, + {"delch", (PyCFunction)PyCursesWindow_DelCh, METH_VARARGS}, + {"deleteln", (PyCFunction)PyCursesWindow_wdeleteln, METH_NOARGS}, + {"derwin", (PyCFunction)PyCursesWindow_DerWin, METH_VARARGS}, + {"echochar", (PyCFunction)PyCursesWindow_EchoChar, METH_VARARGS}, +#ifdef NCURSES_MOUSE_VERSION + {"enclose", (PyCFunction)PyCursesWindow_Enclose, METH_VARARGS}, +#endif + {"erase", (PyCFunction)PyCursesWindow_werase, METH_NOARGS}, + {"getbegyx", (PyCFunction)PyCursesWindow_getbegyx, METH_NOARGS}, + {"getbkgd", (PyCFunction)PyCursesWindow_GetBkgd, METH_NOARGS}, + {"getch", (PyCFunction)PyCursesWindow_GetCh, METH_VARARGS}, + {"getkey", (PyCFunction)PyCursesWindow_GetKey, METH_VARARGS}, +#ifdef HAVE_NCURSESW + {"get_wch", (PyCFunction)PyCursesWindow_Get_WCh, METH_VARARGS}, +#endif + {"getmaxyx", (PyCFunction)PyCursesWindow_getmaxyx, METH_NOARGS}, + {"getparyx", (PyCFunction)PyCursesWindow_getparyx, METH_NOARGS}, + {"getstr", (PyCFunction)PyCursesWindow_GetStr, METH_VARARGS}, + {"getyx", (PyCFunction)PyCursesWindow_getyx, METH_NOARGS}, + {"hline", (PyCFunction)PyCursesWindow_Hline, METH_VARARGS}, + {"idcok", (PyCFunction)PyCursesWindow_idcok, METH_VARARGS}, + {"idlok", (PyCFunction)PyCursesWindow_idlok, METH_VARARGS}, + {"immedok", (PyCFunction)PyCursesWindow_immedok, METH_VARARGS}, + {"inch", (PyCFunction)PyCursesWindow_InCh, METH_VARARGS}, + {"insch", (PyCFunction)PyCursesWindow_InsCh, METH_VARARGS}, + {"insdelln", (PyCFunction)PyCursesWindow_winsdelln, METH_VARARGS}, + {"insertln", (PyCFunction)PyCursesWindow_winsertln, METH_NOARGS}, + {"insnstr", (PyCFunction)PyCursesWindow_InsNStr, METH_VARARGS}, + {"insstr", (PyCFunction)PyCursesWindow_InsStr, METH_VARARGS}, + {"instr", (PyCFunction)PyCursesWindow_InStr, METH_VARARGS}, + {"is_linetouched", (PyCFunction)PyCursesWindow_Is_LineTouched, METH_VARARGS}, + {"is_wintouched", (PyCFunction)PyCursesWindow_is_wintouched, METH_NOARGS}, + {"keypad", (PyCFunction)PyCursesWindow_keypad, METH_VARARGS}, + {"leaveok", (PyCFunction)PyCursesWindow_leaveok, METH_VARARGS}, + {"move", (PyCFunction)PyCursesWindow_wmove, METH_VARARGS}, + {"mvderwin", (PyCFunction)PyCursesWindow_mvderwin, METH_VARARGS}, + {"mvwin", (PyCFunction)PyCursesWindow_mvwin, METH_VARARGS}, + {"nodelay", (PyCFunction)PyCursesWindow_nodelay, METH_VARARGS}, + {"notimeout", (PyCFunction)PyCursesWindow_notimeout, METH_VARARGS}, + {"noutrefresh", (PyCFunction)PyCursesWindow_NoOutRefresh, METH_VARARGS}, + {"overlay", (PyCFunction)PyCursesWindow_Overlay, METH_VARARGS}, + {"overwrite", (PyCFunction)PyCursesWindow_Overwrite, + METH_VARARGS}, + {"putwin", (PyCFunction)PyCursesWindow_PutWin, METH_O}, + {"redrawln", (PyCFunction)PyCursesWindow_RedrawLine, METH_VARARGS}, + {"redrawwin", (PyCFunction)PyCursesWindow_redrawwin, METH_NOARGS}, + {"refresh", (PyCFunction)PyCursesWindow_Refresh, METH_VARARGS}, +#ifndef STRICT_SYSV_CURSES + {"resize", (PyCFunction)PyCursesWindow_wresize, METH_VARARGS}, +#endif + {"scroll", (PyCFunction)PyCursesWindow_Scroll, METH_VARARGS}, + {"scrollok", (PyCFunction)PyCursesWindow_scrollok, METH_VARARGS}, + {"setscrreg", (PyCFunction)PyCursesWindow_SetScrollRegion, METH_VARARGS}, + {"standend", (PyCFunction)PyCursesWindow_wstandend, METH_NOARGS}, + {"standout", (PyCFunction)PyCursesWindow_wstandout, METH_NOARGS}, + {"subpad", (PyCFunction)PyCursesWindow_SubWin, METH_VARARGS}, + {"subwin", (PyCFunction)PyCursesWindow_SubWin, METH_VARARGS}, + {"syncdown", (PyCFunction)PyCursesWindow_wsyncdown, METH_NOARGS}, + {"syncok", (PyCFunction)PyCursesWindow_syncok, METH_VARARGS}, + {"syncup", (PyCFunction)PyCursesWindow_wsyncup, METH_NOARGS}, + {"timeout", (PyCFunction)PyCursesWindow_wtimeout, METH_VARARGS}, + {"touchline", (PyCFunction)PyCursesWindow_TouchLine, METH_VARARGS}, + {"touchwin", (PyCFunction)PyCursesWindow_touchwin, METH_NOARGS}, + {"untouchwin", (PyCFunction)PyCursesWindow_untouchwin, METH_NOARGS}, + {"vline", (PyCFunction)PyCursesWindow_Vline, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyGetSetDef PyCursesWindow_getsets[] = { + {"encoding", + (getter)PyCursesWindow_get_encoding, + (setter)PyCursesWindow_set_encoding, + "the typecode character used to create the array"}, + {NULL, NULL, NULL, NULL } /* sentinel */ +}; + +/* -------------------------------------------------------*/ + +PyTypeObject PyCursesWindow_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_curses.curses window", /*tp_name*/ + sizeof(PyCursesWindowObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyCursesWindow_Dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)0, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + PyCursesWindow_Methods, /*tp_methods*/ + 0, /* tp_members */ + PyCursesWindow_getsets, /* tp_getset */ +}; + +/********************************************************************* + Global Functions +**********************************************************************/ + +NoArgNoReturnFunction(beep) +NoArgNoReturnFunction(def_prog_mode) +NoArgNoReturnFunction(def_shell_mode) +NoArgNoReturnFunction(doupdate) +NoArgNoReturnFunction(endwin) +NoArgNoReturnFunction(flash) +NoArgNoReturnFunction(nocbreak) +NoArgNoReturnFunction(noecho) +NoArgNoReturnFunction(nonl) +NoArgNoReturnFunction(noraw) +NoArgNoReturnFunction(reset_prog_mode) +NoArgNoReturnFunction(reset_shell_mode) +NoArgNoReturnFunction(resetty) +NoArgNoReturnFunction(savetty) + +NoArgOrFlagNoReturnFunction(cbreak) +NoArgOrFlagNoReturnFunction(echo) +NoArgOrFlagNoReturnFunction(nl) +NoArgOrFlagNoReturnFunction(raw) + +NoArgReturnIntFunction(baudrate) +NoArgReturnIntFunction(termattrs) + +NoArgReturnStringFunction(termname) +NoArgReturnStringFunction(longname) + +NoArgTrueFalseFunction(can_change_color) +NoArgTrueFalseFunction(has_colors) +NoArgTrueFalseFunction(has_ic) +NoArgTrueFalseFunction(has_il) +NoArgTrueFalseFunction(isendwin) +NoArgNoReturnVoidFunction(flushinp) +NoArgNoReturnVoidFunction(noqiflush) + +static PyObject * +PyCurses_filter(PyObject *self) +{ + /* not checking for PyCursesInitialised here since filter() must + be called before initscr() */ + filter(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCurses_Color_Content(PyObject *self, PyObject *args) +{ + short color,r,g,b; + + PyCursesInitialised; + PyCursesInitialisedColor; + + if (!PyArg_ParseTuple(args, "h:color_content", &color)) return NULL; + + if (color_content(color, &r, &g, &b) != ERR) + return Py_BuildValue("(iii)", r, g, b); + else { + PyErr_SetString(PyCursesError, + "Argument 1 was out of range. Check value of COLORS."); + return NULL; + } +} + +static PyObject * +PyCurses_color_pair(PyObject *self, PyObject *args) +{ + int n; + + PyCursesInitialised; + PyCursesInitialisedColor; + + if (!PyArg_ParseTuple(args, "i:color_pair", &n)) return NULL; + return PyLong_FromLong((long) (n << 8)); +} + +static PyObject * +PyCurses_Curs_Set(PyObject *self, PyObject *args) +{ + int vis,erg; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args, "i:curs_set", &vis)) return NULL; + + erg = curs_set(vis); + if (erg == ERR) return PyCursesCheckERR(erg, "curs_set"); + + return PyLong_FromLong((long) erg); +} + +static PyObject * +PyCurses_Delay_Output(PyObject *self, PyObject *args) +{ + int ms; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args, "i:delay_output", &ms)) return NULL; + + return PyCursesCheckERR(delay_output(ms), "delay_output"); +} + +static PyObject * +PyCurses_EraseChar(PyObject *self) +{ + char ch; + + PyCursesInitialised; + + ch = erasechar(); + + return PyBytes_FromStringAndSize(&ch, 1); +} + +static PyObject * +PyCurses_getsyx(PyObject *self) +{ + int x = 0; + int y = 0; + + PyCursesInitialised; + + getsyx(y, x); + + return Py_BuildValue("(ii)", y, x); +} + +#ifdef NCURSES_MOUSE_VERSION +static PyObject * +PyCurses_GetMouse(PyObject *self) +{ + int rtn; + MEVENT event; + + PyCursesInitialised; + + rtn = getmouse( &event ); + if (rtn == ERR) { + PyErr_SetString(PyCursesError, "getmouse() returned ERR"); + return NULL; + } + return Py_BuildValue("(hiiil)", + (short)event.id, + event.x, event.y, event.z, + (long) event.bstate); +} + +static PyObject * +PyCurses_UngetMouse(PyObject *self, PyObject *args) +{ + MEVENT event; + + PyCursesInitialised; + if (!PyArg_ParseTuple(args, "hiiil", + &event.id, + &event.x, &event.y, &event.z, + (int *) &event.bstate)) + return NULL; + + return PyCursesCheckERR(ungetmouse(&event), "ungetmouse"); +} +#endif + +static PyObject * +PyCurses_GetWin(PyCursesWindowObject *self, PyObject *stream) +{ + char fn[100]; + int fd = -1; + FILE *fp = NULL; + PyObject *data; + size_t datalen; + WINDOW *win; + _Py_IDENTIFIER(read); + PyObject *res = NULL; + + PyCursesInitialised; + + strcpy(fn, "/tmp/py.curses.getwin.XXXXXX"); + fd = mkstemp(fn); + if (fd < 0) + return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + if (_Py_set_inheritable(fd, 0, NULL) < 0) + goto error; + fp = fdopen(fd, "wb+"); + if (fp == NULL) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + goto error; + } + + data = _PyObject_CallMethodId(stream, &PyId_read, NULL); + if (data == NULL) + goto error; + if (!PyBytes_Check(data)) { + PyErr_Format(PyExc_TypeError, + "f.read() returned %.100s instead of bytes", + data->ob_type->tp_name); + Py_DECREF(data); + goto error; + } + datalen = PyBytes_GET_SIZE(data); + if (fwrite(PyBytes_AS_STRING(data), 1, datalen, fp) != datalen) { + Py_DECREF(data); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + goto error; + } + Py_DECREF(data); + + fseek(fp, 0, 0); + win = getwin(fp); + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + goto error; + } + res = PyCursesWindow_New(win, NULL); + +error: + if (fp != NULL) + fclose(fp); + else if (fd != -1) + close(fd); + remove(fn); + return res; +} + +static PyObject * +PyCurses_HalfDelay(PyObject *self, PyObject *args) +{ + unsigned char tenths; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args, "b:halfdelay", &tenths)) return NULL; + + return PyCursesCheckERR(halfdelay(tenths), "halfdelay"); +} + +#ifndef STRICT_SYSV_CURSES +/* No has_key! */ +static PyObject * PyCurses_has_key(PyObject *self, PyObject *args) +{ + int ch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"i",&ch)) return NULL; + + if (has_key(ch) == FALSE) { + Py_INCREF(Py_False); + return Py_False; + } + Py_INCREF(Py_True); + return Py_True; +} +#endif /* STRICT_SYSV_CURSES */ + +static PyObject * +PyCurses_Init_Color(PyObject *self, PyObject *args) +{ + short color, r, g, b; + + PyCursesInitialised; + PyCursesInitialisedColor; + + switch(PyTuple_Size(args)) { + case 4: + if (!PyArg_ParseTuple(args, "hhhh;color,r,g,b", &color, &r, &g, &b)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "init_color requires 4 arguments"); + return NULL; + } + + return PyCursesCheckERR(init_color(color, r, g, b), "init_color"); +} + +static PyObject * +PyCurses_Init_Pair(PyObject *self, PyObject *args) +{ + short pair, f, b; + + PyCursesInitialised; + PyCursesInitialisedColor; + + if (PyTuple_Size(args) != 3) { + PyErr_SetString(PyExc_TypeError, "init_pair requires 3 arguments"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "hhh;pair, f, b", &pair, &f, &b)) return NULL; + + return PyCursesCheckERR(init_pair(pair, f, b), "init_pair"); +} + +static PyObject *ModDict; + +static PyObject * +PyCurses_InitScr(PyObject *self) +{ + WINDOW *win; + PyCursesWindowObject *winobj; + + if (initialised == TRUE) { + wrefresh(stdscr); + return (PyObject *)PyCursesWindow_New(stdscr, NULL); + } + + win = initscr(); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + initialised = initialised_setupterm = TRUE; + +/* This was moved from initcurses() because it core dumped on SGI, + where they're not defined until you've called initscr() */ +#define SetDictInt(string,ch) \ + do { \ + PyObject *o = PyLong_FromLong((long) (ch)); \ + if (o && PyDict_SetItemString(ModDict, string, o) == 0) { \ + Py_DECREF(o); \ + } \ + } while (0) + + /* Here are some graphic symbols you can use */ + SetDictInt("ACS_ULCORNER", (ACS_ULCORNER)); + SetDictInt("ACS_LLCORNER", (ACS_LLCORNER)); + SetDictInt("ACS_URCORNER", (ACS_URCORNER)); + SetDictInt("ACS_LRCORNER", (ACS_LRCORNER)); + SetDictInt("ACS_LTEE", (ACS_LTEE)); + SetDictInt("ACS_RTEE", (ACS_RTEE)); + SetDictInt("ACS_BTEE", (ACS_BTEE)); + SetDictInt("ACS_TTEE", (ACS_TTEE)); + SetDictInt("ACS_HLINE", (ACS_HLINE)); + SetDictInt("ACS_VLINE", (ACS_VLINE)); + SetDictInt("ACS_PLUS", (ACS_PLUS)); +#if !defined(__hpux) || defined(HAVE_NCURSES_H) + /* On HP/UX 11, these are of type cchar_t, which is not an + integral type. If this is a problem on more platforms, a + configure test should be added to determine whether ACS_S1 + is of integral type. */ + SetDictInt("ACS_S1", (ACS_S1)); + SetDictInt("ACS_S9", (ACS_S9)); + SetDictInt("ACS_DIAMOND", (ACS_DIAMOND)); + SetDictInt("ACS_CKBOARD", (ACS_CKBOARD)); + SetDictInt("ACS_DEGREE", (ACS_DEGREE)); + SetDictInt("ACS_PLMINUS", (ACS_PLMINUS)); + SetDictInt("ACS_BULLET", (ACS_BULLET)); + SetDictInt("ACS_LARROW", (ACS_LARROW)); + SetDictInt("ACS_RARROW", (ACS_RARROW)); + SetDictInt("ACS_DARROW", (ACS_DARROW)); + SetDictInt("ACS_UARROW", (ACS_UARROW)); + SetDictInt("ACS_BOARD", (ACS_BOARD)); + SetDictInt("ACS_LANTERN", (ACS_LANTERN)); + SetDictInt("ACS_BLOCK", (ACS_BLOCK)); +#endif + SetDictInt("ACS_BSSB", (ACS_ULCORNER)); + SetDictInt("ACS_SSBB", (ACS_LLCORNER)); + SetDictInt("ACS_BBSS", (ACS_URCORNER)); + SetDictInt("ACS_SBBS", (ACS_LRCORNER)); + SetDictInt("ACS_SBSS", (ACS_RTEE)); + SetDictInt("ACS_SSSB", (ACS_LTEE)); + SetDictInt("ACS_SSBS", (ACS_BTEE)); + SetDictInt("ACS_BSSS", (ACS_TTEE)); + SetDictInt("ACS_BSBS", (ACS_HLINE)); + SetDictInt("ACS_SBSB", (ACS_VLINE)); + SetDictInt("ACS_SSSS", (ACS_PLUS)); + + /* The following are never available with strict SYSV curses */ +#ifdef ACS_S3 + SetDictInt("ACS_S3", (ACS_S3)); +#endif +#ifdef ACS_S7 + SetDictInt("ACS_S7", (ACS_S7)); +#endif +#ifdef ACS_LEQUAL + SetDictInt("ACS_LEQUAL", (ACS_LEQUAL)); +#endif +#ifdef ACS_GEQUAL + SetDictInt("ACS_GEQUAL", (ACS_GEQUAL)); +#endif +#ifdef ACS_PI + SetDictInt("ACS_PI", (ACS_PI)); +#endif +#ifdef ACS_NEQUAL + SetDictInt("ACS_NEQUAL", (ACS_NEQUAL)); +#endif +#ifdef ACS_STERLING + SetDictInt("ACS_STERLING", (ACS_STERLING)); +#endif + + SetDictInt("LINES", LINES); + SetDictInt("COLS", COLS); + + winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL); + screen_encoding = winobj->encoding; + return (PyObject *)winobj; +} + +static PyObject * +PyCurses_setupterm(PyObject* self, PyObject *args, PyObject* keywds) +{ + int fd = -1; + int err; + char* termstr = NULL; + + static char *kwlist[] = {"term", "fd", NULL}; + + if (!PyArg_ParseTupleAndKeywords( + args, keywds, "|zi:setupterm", kwlist, &termstr, &fd)) { + return NULL; + } + + if (fd == -1) { + PyObject* sys_stdout; + + sys_stdout = PySys_GetObject("stdout"); + + if (sys_stdout == NULL || sys_stdout == Py_None) { + PyErr_SetString( + PyCursesError, + "lost sys.stdout"); + return NULL; + } + + fd = PyObject_AsFileDescriptor(sys_stdout); + + if (fd == -1) { + return NULL; + } + } + + if (!initialised_setupterm && setupterm(termstr,fd,&err) == ERR) { + char* s = "setupterm: unknown error"; + + if (err == 0) { + s = "setupterm: could not find terminal"; + } else if (err == -1) { + s = "setupterm: could not find terminfo database"; + } + + PyErr_SetString(PyCursesError,s); + return NULL; + } + + initialised_setupterm = TRUE; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCurses_IntrFlush(PyObject *self, PyObject *args) +{ + int ch; + + PyCursesInitialised; + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"i;True(1), False(0)",&ch)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "intrflush requires 1 argument"); + return NULL; + } + + return PyCursesCheckERR(intrflush(NULL,ch), "intrflush"); +} + +#ifdef HAVE_CURSES_IS_TERM_RESIZED +static PyObject * +PyCurses_Is_Term_Resized(PyObject *self, PyObject *args) +{ + int lines; + int columns; + int result; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"ii:is_term_resized", &lines, &columns)) + return NULL; + result = is_term_resized(lines, columns); + if (result == TRUE) { + Py_INCREF(Py_True); + return Py_True; + } else { + Py_INCREF(Py_False); + return Py_False; + } +} +#endif /* HAVE_CURSES_IS_TERM_RESIZED */ + +#if !defined(__NetBSD__) +static PyObject * +PyCurses_KeyName(PyObject *self, PyObject *args) +{ + const char *knp; + int ch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"i",&ch)) return NULL; + + if (ch < 0) { + PyErr_SetString(PyExc_ValueError, "invalid key number"); + return NULL; + } + knp = keyname(ch); + + return PyBytes_FromString((knp == NULL) ? "" : knp); +} +#endif + +static PyObject * +PyCurses_KillChar(PyObject *self) +{ + char ch; + + ch = killchar(); + + return PyBytes_FromStringAndSize(&ch, 1); +} + +static PyObject * +PyCurses_Meta(PyObject *self, PyObject *args) +{ + int ch; + + PyCursesInitialised; + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"i;True(1), False(0)",&ch)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "meta requires 1 argument"); + return NULL; + } + + return PyCursesCheckERR(meta(stdscr, ch), "meta"); +} + +#ifdef NCURSES_MOUSE_VERSION +static PyObject * +PyCurses_MouseInterval(PyObject *self, PyObject *args) +{ + int interval; + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"i;interval",&interval)) + return NULL; + return PyCursesCheckERR(mouseinterval(interval), "mouseinterval"); +} + +static PyObject * +PyCurses_MouseMask(PyObject *self, PyObject *args) +{ + int newmask; + mmask_t oldmask, availmask; + + PyCursesInitialised; + if (!PyArg_ParseTuple(args,"i;mousemask",&newmask)) + return NULL; + availmask = mousemask(newmask, &oldmask); + return Py_BuildValue("(ll)", (long)availmask, (long)oldmask); +} +#endif + +static PyObject * +PyCurses_Napms(PyObject *self, PyObject *args) +{ + int ms; + + PyCursesInitialised; + if (!PyArg_ParseTuple(args, "i;ms", &ms)) return NULL; + + return Py_BuildValue("i", napms(ms)); +} + + +static PyObject * +PyCurses_NewPad(PyObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"ii;nlines,ncols",&nlines,&ncols)) return NULL; + + win = newpad(nlines, ncols); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win, NULL); +} + +static PyObject * +PyCurses_NewWindow(PyObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols, begin_y=0, begin_x=0; + + PyCursesInitialised; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;nlines,ncols",&nlines,&ncols)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiii;nlines,ncols,begin_y,begin_x", + &nlines,&ncols,&begin_y,&begin_x)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "newwin requires 2 or 4 arguments"); + return NULL; + } + + win = newwin(nlines,ncols,begin_y,begin_x); + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win, NULL); +} + +static PyObject * +PyCurses_Pair_Content(PyObject *self, PyObject *args) +{ + short pair,f,b; + + PyCursesInitialised; + PyCursesInitialisedColor; + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "h;pair", &pair)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "pair_content requires 1 argument"); + return NULL; + } + + if (pair_content(pair, &f, &b)==ERR) { + PyErr_SetString(PyCursesError, + "Argument 1 was out of range. (1..COLOR_PAIRS-1)"); + return NULL; + } + + return Py_BuildValue("(ii)", f, b); +} + +static PyObject * +PyCurses_pair_number(PyObject *self, PyObject *args) +{ + int n; + + PyCursesInitialised; + PyCursesInitialisedColor; + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "i;pairvalue", &n)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, + "pair_number requires 1 argument"); + return NULL; + } + + return PyLong_FromLong((long) ((n & A_COLOR) >> 8)); +} + +static PyObject * +PyCurses_Putp(PyObject *self, PyObject *args) +{ + char *str; + + if (!PyArg_ParseTuple(args,"y;str", &str)) + return NULL; + return PyCursesCheckERR(putp(str), "putp"); +} + +static PyObject * +PyCurses_QiFlush(PyObject *self, PyObject *args) +{ + int flag = 0; + + PyCursesInitialised; + + switch(PyTuple_Size(args)) { + case 0: + qiflush(); + Py_INCREF(Py_None); + return Py_None; + case 1: + if (!PyArg_ParseTuple(args, "i;True(1) or False(0)", &flag)) return NULL; + if (flag) qiflush(); + else noqiflush(); + Py_INCREF(Py_None); + return Py_None; + default: + PyErr_SetString(PyExc_TypeError, "qiflush requires 0 or 1 arguments"); + return NULL; + } +} + +/* Internal helper used for updating curses.LINES, curses.COLS, _curses.LINES + * and _curses.COLS */ +#if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM) +static int +update_lines_cols(void) +{ + PyObject *o; + PyObject *m = PyImport_ImportModuleNoBlock("curses"); + _Py_IDENTIFIER(LINES); + _Py_IDENTIFIER(COLS); + + if (!m) + return 0; + + o = PyLong_FromLong(LINES); + if (!o) { + Py_DECREF(m); + return 0; + } + if (_PyObject_SetAttrId(m, &PyId_LINES, o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + /* PyId_LINES.object will be initialized here. */ + if (PyDict_SetItem(ModDict, PyId_LINES.object, o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + Py_DECREF(o); + o = PyLong_FromLong(COLS); + if (!o) { + Py_DECREF(m); + return 0; + } + if (_PyObject_SetAttrId(m, &PyId_COLS, o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + if (PyDict_SetItem(ModDict, PyId_COLS.object, o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + Py_DECREF(o); + Py_DECREF(m); + return 1; +} + +static PyObject * +PyCurses_update_lines_cols(PyObject *self) +{ + return PyLong_FromLong((long) update_lines_cols()); +} + +#endif + +#ifdef HAVE_CURSES_RESIZETERM +static PyObject * +PyCurses_ResizeTerm(PyObject *self, PyObject *args) +{ + int lines; + int columns; + PyObject *result; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"ii:resizeterm", &lines, &columns)) + return NULL; + + result = PyCursesCheckERR(resizeterm(lines, columns), "resizeterm"); + if (!result) + return NULL; + if (!update_lines_cols()) + return NULL; + return result; +} + +#endif + +#ifdef HAVE_CURSES_RESIZE_TERM +static PyObject * +PyCurses_Resize_Term(PyObject *self, PyObject *args) +{ + int lines; + int columns; + + PyObject *result; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"ii:resize_term", &lines, &columns)) + return NULL; + + result = PyCursesCheckERR(resize_term(lines, columns), "resize_term"); + if (!result) + return NULL; + if (!update_lines_cols()) + return NULL; + return result; +} +#endif /* HAVE_CURSES_RESIZE_TERM */ + +static PyObject * +PyCurses_setsyx(PyObject *self, PyObject *args) +{ + int y,x; + + PyCursesInitialised; + + if (PyTuple_Size(args)!=2) { + PyErr_SetString(PyExc_TypeError, "setsyx requires 2 arguments"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "ii;y, x", &y, &x)) return NULL; + + setsyx(y,x); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCurses_Start_Color(PyObject *self) +{ + int code; + PyObject *c, *cp; + + PyCursesInitialised; + + code = start_color(); + if (code != ERR) { + initialisedcolors = TRUE; + c = PyLong_FromLong((long) COLORS); + if (c == NULL) + return NULL; + PyDict_SetItemString(ModDict, "COLORS", c); + Py_DECREF(c); + cp = PyLong_FromLong((long) COLOR_PAIRS); + if (cp == NULL) + return NULL; + PyDict_SetItemString(ModDict, "COLOR_PAIRS", cp); + Py_DECREF(cp); + Py_INCREF(Py_None); + return Py_None; + } else { + PyErr_SetString(PyCursesError, "start_color() returned ERR"); + return NULL; + } +} + +static PyObject * +PyCurses_tigetflag(PyObject *self, PyObject *args) +{ + char *capname; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "s", &capname)) + return NULL; + + return PyLong_FromLong( (long) tigetflag( capname ) ); +} + +static PyObject * +PyCurses_tigetnum(PyObject *self, PyObject *args) +{ + char *capname; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "s", &capname)) + return NULL; + + return PyLong_FromLong( (long) tigetnum( capname ) ); +} + +static PyObject * +PyCurses_tigetstr(PyObject *self, PyObject *args) +{ + char *capname; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "s", &capname)) + return NULL; + + capname = tigetstr( capname ); + if (capname == 0 || capname == (char*) -1) { + Py_INCREF(Py_None); + return Py_None; + } + return PyBytes_FromString( capname ); +} + +static PyObject * +PyCurses_tparm(PyObject *self, PyObject *args) +{ + char* fmt; + char* result = NULL; + int i1=0,i2=0,i3=0,i4=0,i5=0,i6=0,i7=0,i8=0,i9=0; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "y|iiiiiiiii:tparm", + &fmt, &i1, &i2, &i3, &i4, + &i5, &i6, &i7, &i8, &i9)) { + return NULL; + } + + result = tparm(fmt,i1,i2,i3,i4,i5,i6,i7,i8,i9); + if (!result) { + PyErr_SetString(PyCursesError, "tparm() returned NULL"); + return NULL; + } + + return PyBytes_FromString(result); +} + +static PyObject * +PyCurses_TypeAhead(PyObject *self, PyObject *args) +{ + int fd; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"i;fd",&fd)) return NULL; + + return PyCursesCheckERR(typeahead( fd ), "typeahead"); +} + +static PyObject * +PyCurses_UnCtrl(PyObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"O;ch or int",&temp)) return NULL; + + if (!PyCurses_ConvertToChtype(NULL, temp, &ch)) + return NULL; + + return PyBytes_FromString(unctrl(ch)); +} + +static PyObject * +PyCurses_UngetCh(PyObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"O;ch or int",&temp)) + return NULL; + + if (!PyCurses_ConvertToChtype(NULL, temp, &ch)) + return NULL; + + return PyCursesCheckERR(ungetch(ch), "ungetch"); +} + +#ifdef HAVE_NCURSESW +/* Convert an object to a character (wchar_t): + + - int + - str of length 1 + + Return 1 on success, 0 on error. */ +static int +PyCurses_ConvertToWchar_t(PyObject *obj, + wchar_t *wch) +{ + if (PyUnicode_Check(obj)) { + wchar_t buffer[2]; + if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, " + "got a str of length %zi", + PyUnicode_GET_LENGTH(obj)); + return 0; + } + *wch = buffer[0]; + return 2; + } + else if (PyLong_CheckExact(obj)) { + long value; + int overflow; + value = PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow) { + PyErr_SetString(PyExc_OverflowError, + "int doesn't fit in long"); + return 0; + } + *wch = (wchar_t)value; + if ((long)*wch != value) { + PyErr_Format(PyExc_OverflowError, + "character doesn't fit in wchar_t"); + return 0; + } + return 1; + } + else { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, got %s", + Py_TYPE(obj)->tp_name); + return 0; + } +} + +static PyObject * +PyCurses_Unget_Wch(PyObject *self, PyObject *args) +{ + PyObject *obj; + wchar_t wch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"O", &obj)) + return NULL; + + if (!PyCurses_ConvertToWchar_t(obj, &wch)) + return NULL; + return PyCursesCheckERR(unget_wch(wch), "unget_wch"); +} +#endif + +static PyObject * +PyCurses_Use_Env(PyObject *self, PyObject *args) +{ + int flag; + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"i;True(1), False(0)",&flag)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "use_env requires 1 argument"); + return NULL; + } + use_env(flag); + Py_INCREF(Py_None); + return Py_None; +} + +#ifndef STRICT_SYSV_CURSES +static PyObject * +PyCurses_Use_Default_Colors(PyObject *self) +{ + int code; + + PyCursesInitialised; + PyCursesInitialisedColor; + + code = use_default_colors(); + if (code != ERR) { + Py_INCREF(Py_None); + return Py_None; + } else { + PyErr_SetString(PyCursesError, "use_default_colors() returned ERR"); + return NULL; + } +} +#endif /* STRICT_SYSV_CURSES */ + +/* List of functions defined in the module */ + +static PyMethodDef PyCurses_methods[] = { + {"baudrate", (PyCFunction)PyCurses_baudrate, METH_NOARGS}, + {"beep", (PyCFunction)PyCurses_beep, METH_NOARGS}, + {"can_change_color", (PyCFunction)PyCurses_can_change_color, METH_NOARGS}, + {"cbreak", (PyCFunction)PyCurses_cbreak, METH_VARARGS}, + {"color_content", (PyCFunction)PyCurses_Color_Content, METH_VARARGS}, + {"color_pair", (PyCFunction)PyCurses_color_pair, METH_VARARGS}, + {"curs_set", (PyCFunction)PyCurses_Curs_Set, METH_VARARGS}, + {"def_prog_mode", (PyCFunction)PyCurses_def_prog_mode, METH_NOARGS}, + {"def_shell_mode", (PyCFunction)PyCurses_def_shell_mode, METH_NOARGS}, + {"delay_output", (PyCFunction)PyCurses_Delay_Output, METH_VARARGS}, + {"doupdate", (PyCFunction)PyCurses_doupdate, METH_NOARGS}, + {"echo", (PyCFunction)PyCurses_echo, METH_VARARGS}, + {"endwin", (PyCFunction)PyCurses_endwin, METH_NOARGS}, + {"erasechar", (PyCFunction)PyCurses_EraseChar, METH_NOARGS}, + {"filter", (PyCFunction)PyCurses_filter, METH_NOARGS}, + {"flash", (PyCFunction)PyCurses_flash, METH_NOARGS}, + {"flushinp", (PyCFunction)PyCurses_flushinp, METH_NOARGS}, +#ifdef NCURSES_MOUSE_VERSION + {"getmouse", (PyCFunction)PyCurses_GetMouse, METH_NOARGS}, + {"ungetmouse", (PyCFunction)PyCurses_UngetMouse, METH_VARARGS}, +#endif + {"getsyx", (PyCFunction)PyCurses_getsyx, METH_NOARGS}, + {"getwin", (PyCFunction)PyCurses_GetWin, METH_O}, + {"has_colors", (PyCFunction)PyCurses_has_colors, METH_NOARGS}, + {"has_ic", (PyCFunction)PyCurses_has_ic, METH_NOARGS}, + {"has_il", (PyCFunction)PyCurses_has_il, METH_NOARGS}, +#ifndef STRICT_SYSV_CURSES + {"has_key", (PyCFunction)PyCurses_has_key, METH_VARARGS}, +#endif + {"halfdelay", (PyCFunction)PyCurses_HalfDelay, METH_VARARGS}, + {"init_color", (PyCFunction)PyCurses_Init_Color, METH_VARARGS}, + {"init_pair", (PyCFunction)PyCurses_Init_Pair, METH_VARARGS}, + {"initscr", (PyCFunction)PyCurses_InitScr, METH_NOARGS}, + {"intrflush", (PyCFunction)PyCurses_IntrFlush, METH_VARARGS}, + {"isendwin", (PyCFunction)PyCurses_isendwin, METH_NOARGS}, +#ifdef HAVE_CURSES_IS_TERM_RESIZED + {"is_term_resized", (PyCFunction)PyCurses_Is_Term_Resized, METH_VARARGS}, +#endif +#if !defined(__NetBSD__) + {"keyname", (PyCFunction)PyCurses_KeyName, METH_VARARGS}, +#endif + {"killchar", (PyCFunction)PyCurses_KillChar, METH_NOARGS}, + {"longname", (PyCFunction)PyCurses_longname, METH_NOARGS}, + {"meta", (PyCFunction)PyCurses_Meta, METH_VARARGS}, +#ifdef NCURSES_MOUSE_VERSION + {"mouseinterval", (PyCFunction)PyCurses_MouseInterval, METH_VARARGS}, + {"mousemask", (PyCFunction)PyCurses_MouseMask, METH_VARARGS}, +#endif + {"napms", (PyCFunction)PyCurses_Napms, METH_VARARGS}, + {"newpad", (PyCFunction)PyCurses_NewPad, METH_VARARGS}, + {"newwin", (PyCFunction)PyCurses_NewWindow, METH_VARARGS}, + {"nl", (PyCFunction)PyCurses_nl, METH_VARARGS}, + {"nocbreak", (PyCFunction)PyCurses_nocbreak, METH_NOARGS}, + {"noecho", (PyCFunction)PyCurses_noecho, METH_NOARGS}, + {"nonl", (PyCFunction)PyCurses_nonl, METH_NOARGS}, + {"noqiflush", (PyCFunction)PyCurses_noqiflush, METH_NOARGS}, + {"noraw", (PyCFunction)PyCurses_noraw, METH_NOARGS}, + {"pair_content", (PyCFunction)PyCurses_Pair_Content, METH_VARARGS}, + {"pair_number", (PyCFunction)PyCurses_pair_number, METH_VARARGS}, + {"putp", (PyCFunction)PyCurses_Putp, METH_VARARGS}, + {"qiflush", (PyCFunction)PyCurses_QiFlush, METH_VARARGS}, + {"raw", (PyCFunction)PyCurses_raw, METH_VARARGS}, + {"reset_prog_mode", (PyCFunction)PyCurses_reset_prog_mode, METH_NOARGS}, + {"reset_shell_mode", (PyCFunction)PyCurses_reset_shell_mode, METH_NOARGS}, + {"resetty", (PyCFunction)PyCurses_resetty, METH_NOARGS}, +#ifdef HAVE_CURSES_RESIZETERM + {"resizeterm", (PyCFunction)PyCurses_ResizeTerm, METH_VARARGS}, +#endif +#ifdef HAVE_CURSES_RESIZE_TERM + {"resize_term", (PyCFunction)PyCurses_Resize_Term, METH_VARARGS}, +#endif + {"savetty", (PyCFunction)PyCurses_savetty, METH_NOARGS}, + {"setsyx", (PyCFunction)PyCurses_setsyx, METH_VARARGS}, + {"setupterm", (PyCFunction)PyCurses_setupterm, + METH_VARARGS|METH_KEYWORDS}, + {"start_color", (PyCFunction)PyCurses_Start_Color, METH_NOARGS}, + {"termattrs", (PyCFunction)PyCurses_termattrs, METH_NOARGS}, + {"termname", (PyCFunction)PyCurses_termname, METH_NOARGS}, + {"tigetflag", (PyCFunction)PyCurses_tigetflag, METH_VARARGS}, + {"tigetnum", (PyCFunction)PyCurses_tigetnum, METH_VARARGS}, + {"tigetstr", (PyCFunction)PyCurses_tigetstr, METH_VARARGS}, + {"tparm", (PyCFunction)PyCurses_tparm, METH_VARARGS}, + {"typeahead", (PyCFunction)PyCurses_TypeAhead, METH_VARARGS}, + {"unctrl", (PyCFunction)PyCurses_UnCtrl, METH_VARARGS}, + {"ungetch", (PyCFunction)PyCurses_UngetCh, METH_VARARGS}, +#if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM) + {"update_lines_cols", (PyCFunction)PyCurses_update_lines_cols, METH_NOARGS}, +#endif +#ifdef HAVE_NCURSESW + {"unget_wch", (PyCFunction)PyCurses_Unget_Wch, METH_VARARGS}, +#endif + {"use_env", (PyCFunction)PyCurses_Use_Env, METH_VARARGS}, +#ifndef STRICT_SYSV_CURSES + {"use_default_colors", (PyCFunction)PyCurses_Use_Default_Colors, METH_NOARGS}, +#endif + {NULL, NULL} /* sentinel */ +}; + +/* Initialization function for the module */ + + +static struct PyModuleDef _cursesmodule = { + PyModuleDef_HEAD_INIT, + "_curses", + NULL, + -1, + PyCurses_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__curses(void) +{ + PyObject *m, *d, *v, *c_api_object; + static void *PyCurses_API[PyCurses_API_pointers]; + + /* Initialize object type */ + if (PyType_Ready(&PyCursesWindow_Type) < 0) + return NULL; + + /* Initialize the C API pointer array */ + PyCurses_API[0] = (void *)&PyCursesWindow_Type; + PyCurses_API[1] = (void *)func_PyCursesSetupTermCalled; + PyCurses_API[2] = (void *)func_PyCursesInitialised; + PyCurses_API[3] = (void *)func_PyCursesInitialisedColor; + + /* Create the module and add the functions */ + m = PyModule_Create(&_cursesmodule); + if (m == NULL) + return NULL; + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + if (d == NULL) + return NULL; + ModDict = d; /* For PyCurses_InitScr to use later */ + + /* Add a capsule for the C API */ + c_api_object = PyCapsule_New(PyCurses_API, PyCurses_CAPSULE_NAME, NULL); + PyDict_SetItemString(d, "_C_API", c_api_object); + Py_DECREF(c_api_object); + + /* For exception curses.error */ + PyCursesError = PyErr_NewException("_curses.error", NULL, NULL); + PyDict_SetItemString(d, "error", PyCursesError); + + /* Make the version available */ + v = PyBytes_FromString(PyCursesVersion); + PyDict_SetItemString(d, "version", v); + PyDict_SetItemString(d, "__version__", v); + Py_DECREF(v); + + SetDictInt("ERR", ERR); + SetDictInt("OK", OK); + + /* Here are some attributes you can add to chars to print */ + + SetDictInt("A_ATTRIBUTES", A_ATTRIBUTES); + SetDictInt("A_NORMAL", A_NORMAL); + SetDictInt("A_STANDOUT", A_STANDOUT); + SetDictInt("A_UNDERLINE", A_UNDERLINE); + SetDictInt("A_REVERSE", A_REVERSE); + SetDictInt("A_BLINK", A_BLINK); + SetDictInt("A_DIM", A_DIM); + SetDictInt("A_BOLD", A_BOLD); + SetDictInt("A_ALTCHARSET", A_ALTCHARSET); +#if !defined(__NetBSD__) + SetDictInt("A_INVIS", A_INVIS); +#endif + SetDictInt("A_PROTECT", A_PROTECT); + SetDictInt("A_CHARTEXT", A_CHARTEXT); + SetDictInt("A_COLOR", A_COLOR); + + /* The following are never available with strict SYSV curses */ +#ifdef A_HORIZONTAL + SetDictInt("A_HORIZONTAL", A_HORIZONTAL); +#endif +#ifdef A_LEFT + SetDictInt("A_LEFT", A_LEFT); +#endif +#ifdef A_LOW + SetDictInt("A_LOW", A_LOW); +#endif +#ifdef A_RIGHT + SetDictInt("A_RIGHT", A_RIGHT); +#endif +#ifdef A_TOP + SetDictInt("A_TOP", A_TOP); +#endif +#ifdef A_VERTICAL + SetDictInt("A_VERTICAL", A_VERTICAL); +#endif + + SetDictInt("COLOR_BLACK", COLOR_BLACK); + SetDictInt("COLOR_RED", COLOR_RED); + SetDictInt("COLOR_GREEN", COLOR_GREEN); + SetDictInt("COLOR_YELLOW", COLOR_YELLOW); + SetDictInt("COLOR_BLUE", COLOR_BLUE); + SetDictInt("COLOR_MAGENTA", COLOR_MAGENTA); + SetDictInt("COLOR_CYAN", COLOR_CYAN); + SetDictInt("COLOR_WHITE", COLOR_WHITE); + +#ifdef NCURSES_MOUSE_VERSION + /* Mouse-related constants */ + SetDictInt("BUTTON1_PRESSED", BUTTON1_PRESSED); + SetDictInt("BUTTON1_RELEASED", BUTTON1_RELEASED); + SetDictInt("BUTTON1_CLICKED", BUTTON1_CLICKED); + SetDictInt("BUTTON1_DOUBLE_CLICKED", BUTTON1_DOUBLE_CLICKED); + SetDictInt("BUTTON1_TRIPLE_CLICKED", BUTTON1_TRIPLE_CLICKED); + + SetDictInt("BUTTON2_PRESSED", BUTTON2_PRESSED); + SetDictInt("BUTTON2_RELEASED", BUTTON2_RELEASED); + SetDictInt("BUTTON2_CLICKED", BUTTON2_CLICKED); + SetDictInt("BUTTON2_DOUBLE_CLICKED", BUTTON2_DOUBLE_CLICKED); + SetDictInt("BUTTON2_TRIPLE_CLICKED", BUTTON2_TRIPLE_CLICKED); + + SetDictInt("BUTTON3_PRESSED", BUTTON3_PRESSED); + SetDictInt("BUTTON3_RELEASED", BUTTON3_RELEASED); + SetDictInt("BUTTON3_CLICKED", BUTTON3_CLICKED); + SetDictInt("BUTTON3_DOUBLE_CLICKED", BUTTON3_DOUBLE_CLICKED); + SetDictInt("BUTTON3_TRIPLE_CLICKED", BUTTON3_TRIPLE_CLICKED); + + SetDictInt("BUTTON4_PRESSED", BUTTON4_PRESSED); + SetDictInt("BUTTON4_RELEASED", BUTTON4_RELEASED); + SetDictInt("BUTTON4_CLICKED", BUTTON4_CLICKED); + SetDictInt("BUTTON4_DOUBLE_CLICKED", BUTTON4_DOUBLE_CLICKED); + SetDictInt("BUTTON4_TRIPLE_CLICKED", BUTTON4_TRIPLE_CLICKED); + + SetDictInt("BUTTON_SHIFT", BUTTON_SHIFT); + SetDictInt("BUTTON_CTRL", BUTTON_CTRL); + SetDictInt("BUTTON_ALT", BUTTON_ALT); + + SetDictInt("ALL_MOUSE_EVENTS", ALL_MOUSE_EVENTS); + SetDictInt("REPORT_MOUSE_POSITION", REPORT_MOUSE_POSITION); +#endif + /* Now set everything up for KEY_ variables */ + { + int key; + char *key_n; + char *key_n2; +#if !defined(__NetBSD__) + for (key=KEY_MIN;key < KEY_MAX; key++) { + key_n = (char *)keyname(key); + if (key_n == NULL || strcmp(key_n,"UNKNOWN KEY")==0) + continue; + if (strncmp(key_n,"KEY_F(",6)==0) { + char *p1, *p2; + key_n2 = PyMem_Malloc(strlen(key_n)+1); + if (!key_n2) { + PyErr_NoMemory(); + break; + } + p1 = key_n; + p2 = key_n2; + while (*p1) { + if (*p1 != '(' && *p1 != ')') { + *p2 = *p1; + p2++; + } + p1++; + } + *p2 = (char)0; + } else + key_n2 = key_n; + SetDictInt(key_n2,key); + if (key_n2 != key_n) + PyMem_Free(key_n2); + } +#endif + SetDictInt("KEY_MIN", KEY_MIN); + SetDictInt("KEY_MAX", KEY_MAX); + } + return m; +} diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_ed6219_939667_Objects#abstract.c b/src/main/resource/testFiles/cpython/prevFiles/prev_ed6219_939667_Objects#abstract.c new file mode 100644 index 0000000..502bd82 --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_ed6219_939667_Objects#abstract.c @@ -0,0 +1,1467 @@ +/*********************************************************** +Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, +The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI or Corporation for National Research Initiatives or +CNRI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +While CWI is the initial source for this software, a modified version +is made available by the Corporation for National Research Initiatives +(CNRI) at the Internet address ftp://ftp.python.org. + +STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH +CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Abstract Object Interface (many thanks to Jim Fulton) */ + +#include "Python.h" +#include + +/* Shorthands to return certain errors */ + +static PyObject * +type_error(msg) + char *msg; +{ + PyErr_SetString(PyExc_TypeError, msg); + return NULL; +} + +static PyObject * +null_error() +{ + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_SystemError, + "null argument to internal routine"); + return NULL; +} + +/* Copied with modifications from stropmodule.c: atoi, atof, atol */ + +static PyObject * +int_from_string(v) + PyObject *v; +{ + extern long PyOS_strtol Py_PROTO((const char *, char **, int)); + char *s, *end; + long x; + char buffer[256]; /* For errors */ + + s = PyString_AS_STRING(v); + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "empty string for int()"); + return NULL; + } + errno = 0; + x = PyOS_strtol(s, &end, 10); + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + sprintf(buffer, "invalid literal for int(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + else if (end-s != PyString_GET_SIZE(v)) { + PyErr_SetString(PyExc_ValueError, + "null byte in argument for int()"); + return NULL; + } + else if (errno != 0) { + sprintf(buffer, "int() literal too large: %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + return PyInt_FromLong(x); +} + +static PyObject * +long_from_string(v) + PyObject *v; +{ + char *s, *end; + PyObject *x; + char buffer[256]; /* For errors */ + + s = PyString_AS_STRING(v); + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "empty string for long()"); + return NULL; + } + x = PyLong_FromString(s, &end, 10); + if (x == NULL) + return NULL; + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + sprintf(buffer, "invalid literal for long(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + Py_DECREF(x); + return NULL; + } + else if (end-s != PyString_GET_SIZE(v)) { + PyErr_SetString(PyExc_ValueError, + "null byte in argument for float()"); + return NULL; + } + return x; +} + +static PyObject * +float_from_string(v) + PyObject *v; +{ + extern double strtod Py_PROTO((const char *, char **)); + char *s, *end; + double x; + char buffer[256]; /* For errors */ + + s = PyString_AS_STRING(v); + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "empty string for float()"); + return NULL; + } + errno = 0; + PyFPE_START_PROTECT("float_from_string", return 0) + x = strtod(s, &end); + PyFPE_END_PROTECT(x) + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + sprintf(buffer, "invalid literal for float(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + else if (end-s != PyString_GET_SIZE(v)) { + PyErr_SetString(PyExc_ValueError, + "null byte in argument for float()"); + return NULL; + } + else if (errno != 0) { + sprintf(buffer, "float() literal too large: %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + return PyFloat_FromDouble(x); +} + +/* Operations on any object */ + +int +PyObject_Cmp(o1, o2, result) + PyObject *o1; + PyObject *o2; + int *result; +{ + int r; + + if (o1 == NULL || o2 == NULL) { + null_error(); + return -1; + } + r = PyObject_Compare(o1, o2); + if (PyErr_Occurred()) + return -1; + *result = r; + return 0; +} + +PyObject * +PyObject_Type(o) + PyObject *o; +{ + PyObject *v; + + if (o == NULL) + return null_error(); + v = (PyObject *)o->ob_type; + Py_INCREF(v); + return v; +} + +int +PyObject_Length(o) + PyObject *o; +{ + PySequenceMethods *m; + + if (o == NULL) { + null_error(); + return -1; + } + + m = o->ob_type->tp_as_sequence; + if (m && m->sq_length) + return m->sq_length(o); + + return PyMapping_Length(o); +} + +PyObject * +PyObject_GetItem(o, key) + PyObject *o; + PyObject *key; +{ + PyMappingMethods *m; + + if (o == NULL || key == NULL) + return null_error(); + + m = o->ob_type->tp_as_mapping; + if (m && m->mp_subscript) + return m->mp_subscript(o, key); + + if (PyInt_Check(key)) + return PySequence_GetItem(o, PyInt_AsLong(key)); + + return type_error("unsubscriptable object"); +} + +int +PyObject_SetItem(o, key, value) + PyObject *o; + PyObject *key; + PyObject *value; +{ + PyMappingMethods *m; + + if (o == NULL || key == NULL || value == NULL) { + null_error(); + return -1; + } + m = o->ob_type->tp_as_mapping; + if (m && m->mp_ass_subscript) + return m->mp_ass_subscript(o, key, value); + + if (PyInt_Check(key)) + return PySequence_SetItem(o, PyInt_AsLong(key), value); + + type_error("object does not support item assignment"); + return -1; +} + +int +PyObject_DelItem(o, key) + PyObject *o; + PyObject *key; +{ + PyMappingMethods *m; + + if (o == NULL || key == NULL) { + null_error(); + return -1; + } + m = o->ob_type->tp_as_mapping; + if (m && m->mp_ass_subscript) + return m->mp_ass_subscript(o, key, (PyObject*)NULL); + + if (PyInt_Check(key)) + return PySequence_DelItem(o, PyInt_AsLong(key)); + + type_error("object does not support item deletion"); + return -1; +} + +/* Operations on numbers */ + +int +PyNumber_Check(o) + PyObject *o; +{ + return o && o->ob_type->tp_as_number; +} + +/* Binary operators */ + +#define BINOP(v, w, opname, ropname, thisfunc) \ + if (PyInstance_Check(v) || PyInstance_Check(w)) \ + return PyInstance_DoBinOp(v, w, opname, ropname, thisfunc) + +PyObject * +PyNumber_Or(v, w) + PyObject *v, *w; +{ + extern int PyNumber_Coerce(); + + BINOP(v, w, "__or__", "__ror__", PyNumber_Or); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_or) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for |"); +} + +PyObject * +PyNumber_Xor(v, w) + PyObject *v, *w; +{ + extern int PyNumber_Coerce(); + + BINOP(v, w, "__xor__", "__rxor__", PyNumber_Xor); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_xor) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for ^"); +} + +PyObject * +PyNumber_And(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__and__", "__rand__", PyNumber_And); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_and) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for &"); +} + +PyObject * +PyNumber_Lshift(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__lshift__", "__rlshift__", PyNumber_Lshift); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_lshift) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for <<"); +} + +PyObject * +PyNumber_Rshift(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__rshift__", "__rrshift__", PyNumber_Rshift); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_rshift) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for >>"); +} + +PyObject * +PyNumber_Add(v, w) + PyObject *v, *w; +{ + PySequenceMethods *m; + + BINOP(v, w, "__add__", "__radd__", PyNumber_Add); + m = v->ob_type->tp_as_sequence; + if (m && m->sq_concat) + return (*m->sq_concat)(v, w); + else if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_add) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for +"); +} + +PyObject * +PyNumber_Subtract(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__sub__", "__rsub__", PyNumber_Subtract); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_subtract) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for -"); +} + +PyObject * +PyNumber_Multiply(v, w) + PyObject *v, *w; +{ + PyTypeObject *tp = v->ob_type; + PySequenceMethods *m; + + BINOP(v, w, "__mul__", "__rmul__", PyNumber_Multiply); + if (tp->tp_as_number != NULL && + w->ob_type->tp_as_sequence != NULL && + !PyInstance_Check(v)) { + /* number*sequence -- swap v and w */ + PyObject *tmp = v; + v = w; + w = tmp; + tp = v->ob_type; + } + if (tp->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyInstance_Check(v)) { + /* Instances of user-defined classes get their + other argument uncoerced, so they may + implement sequence*number as well as + number*number. */ + Py_INCREF(v); + Py_INCREF(w); + } + else if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_multiply) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + m = tp->tp_as_sequence; + if (m && m->sq_repeat) { + if (!PyInt_Check(w)) + return type_error( + "can't multiply sequence with non-int"); + return (*m->sq_repeat)(v, (int)PyInt_AsLong(w)); + } + return type_error("bad operand type(s) for *"); +} + +PyObject * +PyNumber_Divide(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__div__", "__rdiv__", PyNumber_Divide); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_divide) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for /"); +} + +PyObject * +PyNumber_Remainder(v, w) + PyObject *v, *w; +{ + if (PyString_Check(v)) + return PyString_Format(v, w); + BINOP(v, w, "__mod__", "__rmod__", PyNumber_Remainder); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_remainder) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for %"); +} + +PyObject * +PyNumber_Divmod(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__divmod__", "__rdivmod__", PyNumber_Divmod); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_divmod) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for divmod()"); +} + +/* Power (binary or ternary) */ + +static PyObject * +do_pow(v, w) + PyObject *v, *w; +{ + PyObject *res; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *, PyObject *)); + BINOP(v, w, "__pow__", "__rpow__", do_pow); + if (v->ob_type->tp_as_number == NULL || + w->ob_type->tp_as_number == NULL) { + PyErr_SetString(PyExc_TypeError, + "pow(x, y) requires numeric arguments"); + return NULL; + } + if ( +#ifndef WITHOUT_COMPLEX + !PyComplex_Check(v) && +#endif + PyFloat_Check(w) && PyFloat_AsDouble(v) < 0.0) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_ValueError, + "negative number to float power"); + return NULL; + } + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_power) != NULL) + res = (*f)(v, w, Py_None); + else + res = type_error("pow(x, y) not defined for these operands"); + Py_DECREF(v); + Py_DECREF(w); + return res; +} + +PyObject * +PyNumber_Power(v, w, z) + PyObject *v, *w, *z; +{ + PyObject *res; + PyObject *v1, *z1, *w2, *z2; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *, PyObject *)); + + if (z == Py_None) + return do_pow(v, w); + /* XXX The ternary version doesn't do class instance coercions */ + if (PyInstance_Check(v)) + return v->ob_type->tp_as_number->nb_power(v, w, z); + if (v->ob_type->tp_as_number == NULL || + z->ob_type->tp_as_number == NULL || + w->ob_type->tp_as_number == NULL) { + return type_error("pow(x, y, z) requires numeric arguments"); + } + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + res = NULL; + v1 = v; + z1 = z; + if (PyNumber_Coerce(&v1, &z1) != 0) + goto error2; + w2 = w; + z2 = z1; + if (PyNumber_Coerce(&w2, &z2) != 0) + goto error1; + if ((f = v1->ob_type->tp_as_number->nb_power) != NULL) + res = (*f)(v1, w2, z2); + else + res = type_error( + "pow(x, y, z) not defined for these operands"); + Py_DECREF(w2); + Py_DECREF(z2); + error1: + Py_DECREF(v1); + Py_DECREF(z1); + error2: + Py_DECREF(v); + Py_DECREF(w); + return res; +} + +/* Unary operators and functions */ + +PyObject * +PyNumber_Negative(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_negative) + return (*m->nb_negative)(o); + + return type_error("bad operand type for unary -"); +} + +PyObject * +PyNumber_Positive(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_positive) + return (*m->nb_positive)(o); + + return type_error("bad operand type for unary +"); +} + +PyObject * +PyNumber_Invert(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_invert) + return (*m->nb_invert)(o); + + return type_error("bad operand type for unary ~"); +} + +PyObject * +PyNumber_Absolute(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_absolute) + return m->nb_absolute(o); + + return type_error("bad operand type for abs()"); +} + +PyObject * +PyNumber_Int(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + if (PyString_Check(o)) + return int_from_string(o); + m = o->ob_type->tp_as_number; + if (m && m->nb_int) + return m->nb_int(o); + + return type_error("object can't be converted to int"); +} + +PyObject * +PyNumber_Long(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + if (PyString_Check(o)) + return long_from_string(o); + m = o->ob_type->tp_as_number; + if (m && m->nb_long) + return m->nb_long(o); + + return type_error("object can't be converted to long"); +} + +PyObject * +PyNumber_Float(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + if (PyString_Check(o)) + return float_from_string(o); + m = o->ob_type->tp_as_number; + if (m && m->nb_float) + return m->nb_float(o); + + return type_error("object can't be converted to float"); +} + +/* Operations on sequences */ + +int +PySequence_Check(s) + PyObject *s; +{ + return s != NULL && s->ob_type->tp_as_sequence; +} + +int +PySequence_Length(s) + PyObject *s; +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_length) + return m->sq_length(s); + + type_error("len() of unsized object"); + return -1; +} + +PyObject * +PySequence_Concat(s, o) + PyObject *s; + PyObject *o; +{ + PySequenceMethods *m; + + if (s == NULL || o == NULL) + return null_error(); + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_concat) + return m->sq_concat(s, o); + + return type_error("object can't be concatenated"); +} + +PyObject * +PySequence_Repeat(o, count) + PyObject *o; + int count; +{ + PySequenceMethods *m; + + if (o == NULL) + return null_error(); + + m = o->ob_type->tp_as_sequence; + if (m && m->sq_repeat) + return m->sq_repeat(o, count); + + return type_error("object can't be repeated"); +} + +PyObject * +PySequence_GetItem(s, i) + PyObject *s; + int i; +{ + PySequenceMethods *m; + + if (s == NULL) + return null_error(); + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_item) { + if (i < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return NULL; + i += l; + } + } + return m->sq_item(s, i); + } + + return type_error("unindexable object"); +} + +PyObject * +PySequence_GetSlice(s, i1, i2) + PyObject *s; + int i1; + int i2; +{ + PySequenceMethods *m; + + if (!s) return null_error(); + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_slice) { + if (i1 < 0 || i2 < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return NULL; + if (i1 < 0) + i1 += l; + if (i2 < 0) + i2 += l; + } + } + return m->sq_slice(s, i1, i2); + } + + return type_error("unsliceable object"); +} + +int +PySequence_SetItem(s, i, o) + PyObject *s; + int i; + PyObject *o; +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_item) { + if (i < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return NULL; + i += l; + } + } + return m->sq_ass_item(s, i, o); + } + + type_error("object doesn't support item assignment"); + return -1; +} + +int +PySequence_DelItem(s, i) + PyObject *s; + int i; +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_item) { + if (i < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return NULL; + i += l; + } + } + return m->sq_ass_item(s, i, (PyObject *)NULL); + } + + type_error("object doesn't support item deletion"); + return -1; +} + +int +PySequence_SetSlice(s, i1, i2, o) + PyObject *s; + int i1; + int i2; + PyObject *o; +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_slice) { + if (i1 < 0 || i2 < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return NULL; + if (i1 < 0) + i1 += l; + if (i2 < 0) + i2 += l; + } + } + return m->sq_ass_slice(s, i1, i2, o); + } + type_error("object doesn't support slice assignment"); + return -1; +} + +int +PySequence_DelSlice(s, i1, i2) + PyObject *s; + int i1; + int i2; +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_slice) { + if (i1 < 0 || i2 < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return NULL; + if (i1 < 0) + i1 += l; + if (i2 < 0) + i2 += l; + } + } + return m->sq_ass_slice(s, i1, i2, (PyObject *)NULL); + } + type_error("object doesn't support slice deletion"); + return -1; +} + +PyObject * +PySequence_Tuple(v) + PyObject *v; +{ + PySequenceMethods *m; + + if (v == NULL) + return null_error(); + + if (PyTuple_Check(v)) { + Py_INCREF(v); + return v; + } + + if (PyList_Check(v)) + return PyList_AsTuple(v); + + if (PyString_Check(v)) { + int n = PyString_Size(v); + PyObject *t = PyTuple_New(n); + if (t != NULL) { + int i; + char *p = PyString_AsString(v); + for (i = 0; i < n; i++) { + PyObject *item = + PyString_FromStringAndSize(p+i, 1); + if (item == NULL) { + Py_DECREF(t); + t = NULL; + break; + } + PyTuple_SetItem(t, i, item); + } + } + return t; + } + + /* Generic sequence object */ + m = v->ob_type->tp_as_sequence; + if (m && m->sq_item) { + /* XXX Should support indefinite-length sequences */ + int i; + PyObject *t; + int n = PySequence_Length(v); + if (n < 0) + return NULL; + t = PyTuple_New(n); + if (t == NULL) + return NULL; + for (i = 0; i < n; i++) { + PyObject *item = (*m->sq_item)(v, i); + if (item == NULL) { + Py_DECREF(t); + t = NULL; + break; + } + PyTuple_SetItem(t, i, item); + } + return t; + } + + /* None of the above */ + return type_error("tuple() argument must be a sequence"); +} + +PyObject * +PySequence_List(v) + PyObject *v; +{ + PySequenceMethods *m; + + m = v->ob_type->tp_as_sequence; + if (m && m->sq_item) { + /* XXX Should support indefinite-length sequences */ + int i; + PyObject *l; + int n = PySequence_Length(v); + if (n < 0) + return NULL; + l = PyList_New(n); + if (l == NULL) + return NULL; + for (i = 0; i < n; i++) { + PyObject *item = (*m->sq_item)(v, i); + if (item == NULL) { + Py_DECREF(l); + l = NULL; + break; + } + PyList_SetItem(l, i, item); + } + return l; + } + return type_error("list() argument must be a sequence"); +} + +int +PySequence_Count(s, o) + PyObject *s; + PyObject *o; +{ + int l, i, n, cmp, err; + PyObject *item; + + if (s == NULL || o == NULL) { + null_error(); + return -1; + } + + l = PySequence_Length(s); + if (l < 0) + return -1; + + n = 0; + for (i = 0; i < l; i++) { + item = PySequence_GetItem(s, i); + if (item == NULL) + return -1; + err = PyObject_Cmp(item, o, &cmp); + Py_DECREF(item); + if (err < 0) + return err; + if (cmp == 0) + n++; + } + return n; +} + +int +PySequence_Contains(w, v) /* v in w */ + PyObject *w; + PyObject *v; +{ + int i, cmp; + PyObject *x; + PySequenceMethods *sq; + + /* Special case for char in string */ + if (PyString_Check(w)) { + register char *s, *end; + register char c; + if (!PyString_Check(v) || PyString_Size(v) != 1) { + PyErr_SetString(PyExc_TypeError, + "string member test needs char left operand"); + return -1; + } + c = PyString_AsString(v)[0]; + s = PyString_AsString(w); + end = s + PyString_Size(w); + while (s < end) { + if (c == *s++) + return 1; + } + return 0; + } + + sq = w->ob_type->tp_as_sequence; + if (sq == NULL || sq->sq_item == NULL) { + PyErr_SetString(PyExc_TypeError, + "'in' or 'not in' needs sequence right argument"); + return -1; + } + + for (i = 0; ; i++) { + x = (*sq->sq_item)(w, i); + if (x == NULL) { + if (PyErr_ExceptionMatches(PyExc_IndexError)) { + PyErr_Clear(); + break; + } + return -1; + } + cmp = PyObject_Compare(v, x); + Py_XDECREF(x); + if (cmp == 0) + return 1; + if (PyErr_Occurred()) + return -1; + } + + return 0; +} + +/* Backwards compatibility */ +#undef PySequence_In +int +PySequence_In(w, v) + PyObject *w; + PyObject *v; +{ + return PySequence_Contains(w, v); +} + +int +PySequence_Index(s, o) + PyObject *s; + PyObject *o; +{ + int l, i, cmp, err; + PyObject *item; + + if (s == NULL || o == NULL) { + null_error(); + return -1; + } + + l = PySequence_Length(s); + if (l < 0) + return -1; + + for (i = 0; i < l; i++) { + item = PySequence_GetItem(s, i); + if (item == NULL) + return -1; + err = PyObject_Cmp(item, o, &cmp); + Py_DECREF(item); + if (err < 0) + return err; + if (cmp == 0) + return i; + } + + PyErr_SetString(PyExc_ValueError, "sequence.index(x): x not in list"); + return -1; +} + +/* Operations on mappings */ + +int +PyMapping_Check(o) + PyObject *o; +{ + return o && o->ob_type->tp_as_mapping; +} + +int +PyMapping_Length(o) + PyObject *o; +{ + PyMappingMethods *m; + + if (o == NULL) { + null_error(); + return -1; + } + + m = o->ob_type->tp_as_mapping; + if (m && m->mp_length) + return m->mp_length(o); + + type_error("len() of unsized object"); + return -1; +} + +PyObject * +PyMapping_GetItemString(o, key) + PyObject *o; + char *key; +{ + PyObject *okey, *r; + + if (key == NULL) + return null_error(); + + okey = PyString_FromString(key); + if (okey == NULL) + return NULL; + r = PyObject_GetItem(o, okey); + Py_DECREF(okey); + return r; +} + +int +PyMapping_SetItemString(o, key, value) + PyObject *o; + char *key; + PyObject *value; +{ + PyObject *okey; + int r; + + if (key == NULL) { + null_error(); + return -1; + } + + okey = PyString_FromString(key); + if (okey == NULL) + return NULL; + r = PyObject_SetItem(o, okey, value); + Py_DECREF(okey); + return r; +} + +int +PyMapping_HasKeyString(o, key) + PyObject *o; + char *key; +{ + PyObject *v; + + v = PyMapping_GetItemString(o, key); + if (v) { + Py_DECREF(v); + return 1; + } + PyErr_Clear(); + return 0; +} + +int +PyMapping_HasKey(o, key) + PyObject *o; + PyObject *key; +{ + PyObject *v; + + v = PyObject_GetItem(o, key); + if (v) { + Py_DECREF(v); + return 1; + } + PyErr_Clear(); + return 0; +} + +/* Operations on callable objects */ + +/* XXX PyCallable_Check() is in object.c */ + +PyObject * +PyObject_CallObject(o, a) + PyObject *o, *a; +{ + PyObject *r; + PyObject *args = a; + + if (args == NULL) { + args = PyTuple_New(0); + if (args == NULL) + return NULL; + } + + r = PyEval_CallObject(o, args); + + if (args != a) { + Py_DECREF(args); + } + + return r; +} + +PyObject * +#ifdef HAVE_STDARG_PROTOTYPES +/* VARARGS 2 */ +PyObject_CallFunction(PyObject *callable, char *format, ...) +#else +/* VARARGS */ + PyObject_CallFunction(va_alist) va_dcl +#endif +{ + va_list va; + PyObject *args, *retval; +#ifdef HAVE_STDARG_PROTOTYPES + va_start(va, format); +#else + PyObject *callable; + char *format; + va_start(va); + callable = va_arg(va, PyObject *); + format = va_arg(va, char *); +#endif + + if (callable == NULL) { + va_end(va); + return null_error(); + } + + if (format) + args = Py_VaBuildValue(format, va); + else + args = PyTuple_New(0); + + va_end(va); + + if (args == NULL) + return NULL; + + if (!PyTuple_Check(args)) { + PyObject *a; + + a = PyTuple_New(1); + if (a == NULL) + return NULL; + if (PyTuple_SetItem(a, 0, args) < 0) + return NULL; + args = a; + } + retval = PyObject_CallObject(callable, args); + + Py_DECREF(args); + + return retval; +} + +PyObject * +#ifdef HAVE_STDARG_PROTOTYPES +/* VARARGS 2 */ +PyObject_CallMethod(PyObject *o, char *name, char *format, ...) +#else +/* VARARGS */ + PyObject_CallMethod(va_alist) va_dcl +#endif +{ + va_list va; + PyObject *args, *func = 0, *retval; +#ifdef HAVE_STDARG_PROTOTYPES + va_start(va, format); +#else + PyObject *o; + char *name; + char *format; + va_start(va); + o = va_arg(va, PyObject *); + name = va_arg(va, char *); + format = va_arg(va, char *); +#endif + + if (o == NULL || name == NULL) { + va_end(va); + return null_error(); + } + + func = PyObject_GetAttrString(o, name); + if (func == NULL) { + va_end(va); + PyErr_SetString(PyExc_AttributeError, name); + return 0; + } + + if (!PyCallable_Check(func)) { + va_end(va); + return type_error("call of non-callable attribute"); + } + + if (format && *format) + args = Py_VaBuildValue(format, va); + else + args = PyTuple_New(0); + + va_end(va); + + if (!args) + return NULL; + + if (!PyTuple_Check(args)) { + PyObject *a; + + a = PyTuple_New(1); + if (a == NULL) + return NULL; + if (PyTuple_SetItem(a, 0, args) < 0) + return NULL; + args = a; + } + + retval = PyObject_CallObject(func, args); + + Py_DECREF(args); + Py_DECREF(func); + + return retval; +} diff --git a/src/main/resource/testFiles/cpython/revFiles/03897e_8fcc8e_Python#sysmodule.c b/src/main/resource/testFiles/cpython/revFiles/03897e_8fcc8e_Python#sysmodule.c new file mode 100644 index 0000000..4ae207b --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/03897e_8fcc8e_Python#sysmodule.c @@ -0,0 +1,1061 @@ + +/* System module */ + +/* +Various bits of information used by the interpreter are collected in +module 'sys'. +Function member: +- exit(sts): raise SystemExit +Data members: +- stdin, stdout, stderr: standard file objects +- modules: the table of modules (dictionary) +- path: module search path (list of strings) +- argv: script arguments (list of strings) +- ps1, ps2: optional primary and secondary prompts (strings) +*/ + +#include "Python.h" +#include "compile.h" +#include "frameobject.h" + +#include "osdefs.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef MS_COREDLL +extern void *PyWin_DLLhModule; +/* A string loaded from the DLL at startup: */ +extern const char *PyWin_DLLVersionString; +#endif + +PyObject * +PySys_GetObject(char *name) +{ + PyThreadState *tstate = PyThreadState_Get(); + PyObject *sd = tstate->interp->sysdict; + if (sd == NULL) + return NULL; + return PyDict_GetItemString(sd, name); +} + +FILE * +PySys_GetFile(char *name, FILE *def) +{ + FILE *fp = NULL; + PyObject *v = PySys_GetObject(name); + if (v != NULL && PyFile_Check(v)) + fp = PyFile_AsFile(v); + if (fp == NULL) + fp = def; + return fp; +} + +int +PySys_SetObject(char *name, PyObject *v) +{ + PyThreadState *tstate = PyThreadState_Get(); + PyObject *sd = tstate->interp->sysdict; + if (v == NULL) { + if (PyDict_GetItemString(sd, name) == NULL) + return 0; + else + return PyDict_DelItemString(sd, name); + } + else + return PyDict_SetItemString(sd, name, v); +} + +static PyObject * +sys_displayhook(PyObject *self, PyObject *args) +{ + PyObject *o, *outf; + PyInterpreterState *interp = PyThreadState_Get()->interp; + PyObject *modules = interp->modules; + PyObject *builtins = PyDict_GetItemString(modules, "__builtin__"); + + if (builtins == NULL) { + PyErr_SetString(PyExc_RuntimeError, "lost __builtin__"); + return NULL; + } + + /* parse arguments */ + if (!PyArg_ParseTuple(args, "O:displayhook", &o)) + return NULL; + + /* Print value except if None */ + /* After printing, also assign to '_' */ + /* Before, set '_' to None to avoid recursion */ + if (o == Py_None) { + Py_INCREF(Py_None); + return Py_None; + } + if (PyObject_SetAttrString(builtins, "_", Py_None) != 0) + return NULL; + if (Py_FlushLine() != 0) + return NULL; + outf = PySys_GetObject("stdout"); + if (outf == NULL) { + PyErr_SetString(PyExc_RuntimeError, "lost sys.stdout"); + return NULL; + } + if (PyFile_WriteObject(o, outf, 0) != 0) + return NULL; + PyFile_SoftSpace(outf, 1); + if (Py_FlushLine() != 0) + return NULL; + if (PyObject_SetAttrString(builtins, "_", o) != 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static char displayhook_doc[] = +"displayhook(object) -> None\n" +"\n" +"Print an object to sys.stdout and also save it in __builtin__._\n"; + +static PyObject * +sys_excepthook(PyObject* self, PyObject* args) +{ + PyObject *exc, *value, *tb; + if (!PyArg_ParseTuple(args, "OOO:excepthook", &exc, &value, &tb)) + return NULL; + PyErr_Display(exc, value, tb); + Py_INCREF(Py_None); + return Py_None; +} + +static char excepthook_doc[] = +"excepthook(exctype, value, traceback) -> None\n" +"\n" +"Handle an exception by displaying it with a traceback on sys.stderr.\n"; + +static PyObject * +sys_exc_info(PyObject *self, PyObject *args) +{ + PyThreadState *tstate; + if (!PyArg_ParseTuple(args, ":exc_info")) + return NULL; + tstate = PyThreadState_Get(); + return Py_BuildValue( + "(OOO)", + tstate->exc_type != NULL ? tstate->exc_type : Py_None, + tstate->exc_value != NULL ? tstate->exc_value : Py_None, + tstate->exc_traceback != NULL ? + tstate->exc_traceback : Py_None); +} + +static char exc_info_doc[] = +"exc_info() -> (type, value, traceback)\n\ +\n\ +Return information about the exception that is currently being handled.\n\ +This should be called from inside an except clause only."; + +static PyObject * +sys_exit(PyObject *self, PyObject *args) +{ + /* Raise SystemExit so callers may catch it or clean up. */ + PyErr_SetObject(PyExc_SystemExit, args); + return NULL; +} + +static char exit_doc[] = +"exit([status])\n\ +\n\ +Exit the interpreter by raising SystemExit(status).\n\ +If the status is omitted or None, it defaults to zero (i.e., success).\n\ +If the status numeric, it will be used as the system exit status.\n\ +If it is another kind of object, it will be printed and the system\n\ +exit status will be one (i.e., failure)."; + +static PyObject * +sys_getdefaultencoding(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":getdefaultencoding")) + return NULL; + return PyString_FromString(PyUnicode_GetDefaultEncoding()); +} + +static char getdefaultencoding_doc[] = +"getdefaultencoding() -> string\n\ +\n\ +Return the current default string encoding used by the Unicode \n\ +implementation."; + +static PyObject * +sys_setdefaultencoding(PyObject *self, PyObject *args) +{ + char *encoding; + if (!PyArg_ParseTuple(args, "s:setdefaultencoding", &encoding)) + return NULL; + if (PyUnicode_SetDefaultEncoding(encoding)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static char setdefaultencoding_doc[] = +"setdefaultencoding(encoding)\n\ +\n\ +Set the current default string encoding used by the Unicode implementation."; + +/* + * Cached interned string objects used for calling the profile and + * trace functions. Initialized by trace_init(). + */ +static PyObject *whatstrings[4] = {NULL, NULL, NULL, NULL}; + +static int +trace_init(void) +{ + static char *whatnames[4] = {"call", "exception", "line", "return"}; + PyObject *name; + int i; + for (i = 0; i < 4; ++i) { + if (whatstrings[i] == NULL) { + name = PyString_InternFromString(whatnames[i]); + if (name == NULL) + return -1; + whatstrings[i] = name; + } + } + return 0; +} + + +static PyObject * +call_trampoline(PyThreadState *tstate, PyObject* callback, + PyFrameObject *frame, int what, PyObject *arg) +{ + PyObject *args = PyTuple_New(3); + PyObject *whatstr; + PyObject *result; + + if (args == NULL) + return NULL; + Py_INCREF(frame); + whatstr = whatstrings[what]; + Py_INCREF(whatstr); + if (arg == NULL) + arg = Py_None; + Py_INCREF(arg); + PyTuple_SET_ITEM(args, 0, (PyObject *)frame); + PyTuple_SET_ITEM(args, 1, whatstr); + PyTuple_SET_ITEM(args, 2, arg); + + /* call the Python-level function */ + PyFrame_FastToLocals(frame); + result = PyEval_CallObject(callback, args); + PyFrame_LocalsToFast(frame, 1); + if (result == NULL) + PyTraceBack_Here(frame); + + /* cleanup */ + Py_DECREF(args); + return result; +} + +static int +profile_trampoline(PyObject *self, PyFrameObject *frame, + int what, PyObject *arg) +{ + PyThreadState *tstate = frame->f_tstate; + PyObject *result; + + result = call_trampoline(tstate, self, frame, what, arg); + if (result == NULL) { + PyEval_SetProfile(NULL, NULL); + return -1; + } + Py_DECREF(result); + return 0; +} + +static int +trace_trampoline(PyObject *self, PyFrameObject *frame, + int what, PyObject *arg) +{ + PyThreadState *tstate = frame->f_tstate; + PyObject *callback; + PyObject *result; + + if (what == PyTrace_CALL) + callback = self; + else + callback = frame->f_trace; + if (callback == NULL) + return 0; + result = call_trampoline(tstate, callback, frame, what, arg); + if (result == NULL) { + PyEval_SetTrace(NULL, NULL); + Py_XDECREF(frame->f_trace); + frame->f_trace = NULL; + return -1; + } + if (result != Py_None) { + PyObject *temp = frame->f_trace; + frame->f_trace = NULL; + Py_XDECREF(temp); + frame->f_trace = result; + } + else { + Py_DECREF(result); + } + return 0; +} + +static PyObject * +sys_settrace(PyObject *self, PyObject *args) +{ + if (trace_init() == -1) + return NULL; + if (args == Py_None) + PyEval_SetTrace(NULL, NULL); + else + PyEval_SetTrace(trace_trampoline, args); + Py_INCREF(Py_None); + return Py_None; +} + +static char settrace_doc[] = +"settrace(function)\n\ +\n\ +Set the global debug tracing function. It will be called on each\n\ +function call. See the debugger chapter in the library manual."; + +static PyObject * +sys_setprofile(PyObject *self, PyObject *args) +{ + if (trace_init() == -1) + return NULL; + if (args == Py_None) + PyEval_SetProfile(NULL, NULL); + else + PyEval_SetProfile(profile_trampoline, args); + Py_INCREF(Py_None); + return Py_None; +} + +static char setprofile_doc[] = +"setprofile(function)\n\ +\n\ +Set the profiling function. It will be called on each function call\n\ +and return. See the profiler chapter in the library manual."; + +static PyObject * +sys_setcheckinterval(PyObject *self, PyObject *args) +{ + PyThreadState *tstate = PyThreadState_Get(); + if (!PyArg_ParseTuple(args, "i:setcheckinterval", &tstate->interp->checkinterval)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static char setcheckinterval_doc[] = +"setcheckinterval(n)\n\ +\n\ +Tell the Python interpreter to check for asynchronous events every\n\ +n instructions. This also affects how often thread switches occur."; + +static PyObject * +sys_setrecursionlimit(PyObject *self, PyObject *args) +{ + int new_limit; + if (!PyArg_ParseTuple(args, "i:setrecursionlimit", &new_limit)) + return NULL; + if (new_limit <= 0) { + PyErr_SetString(PyExc_ValueError, + "recursion limit must be positive"); + return NULL; + } + Py_SetRecursionLimit(new_limit); + Py_INCREF(Py_None); + return Py_None; +} + +static char setrecursionlimit_doc[] = +"setrecursionlimit(n)\n\ +\n\ +Set the maximum depth of the Python interpreter stack to n. This\n\ +limit prevents infinite recursion from causing an overflow of the C\n\ +stack and crashing Python. The highest possible limit is platform-\n\ +dependent."; + +static PyObject * +sys_getrecursionlimit(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":getrecursionlimit")) + return NULL; + return PyInt_FromLong(Py_GetRecursionLimit()); +} + +static char getrecursionlimit_doc[] = +"getrecursionlimit()\n\ +\n\ +Return the current value of the recursion limit, the maximum depth\n\ +of the Python interpreter stack. This limit prevents infinite\n\ +recursion from causing an overflow of the C stack and crashing Python."; + +#ifdef HAVE_DLOPEN +static PyObject * +sys_setdlopenflags(PyObject *self, PyObject *args) +{ + int new_val; + PyThreadState *tstate = PyThreadState_Get(); + if (!PyArg_ParseTuple(args, "i:setdlopenflags", &new_val)) + return NULL; + if (!tstate) + return NULL; + tstate->interp->dlopenflags = new_val; + Py_INCREF(Py_None); + return Py_None; +} + +static char setdlopenflags_doc[] = +"setdlopenflags(n) -> None\n\ +\n\ +Set the flags that will be used for dlopen() calls. Among other\n\ +things, this will enable a lazy resolving of symbols when imporing\n\ +a module, if called as sys.setdlopenflags(0)\n\ +To share symols across extension modules, call as\n\ +sys.setdlopenflags(dl.RTLD_NOW|dl.RTLD_GLOBAL)"; + +static PyObject * +sys_getdlopenflags(PyObject *self, PyObject *args) +{ + PyThreadState *tstate = PyThreadState_Get(); + if (!PyArg_ParseTuple(args, ":getdlopenflags")) + return NULL; + if (!tstate) + return NULL; + return PyInt_FromLong(tstate->interp->dlopenflags); +} + +static char getdlopenflags_doc[] = +"getdlopenflags() -> int\n\ +\n\ +Return the current value of the flags that are used for dlopen()\n\ +calls. The flag constants are defined in the dl module."; +#endif + +#ifdef USE_MALLOPT +/* Link with -lmalloc (or -lmpc) on an SGI */ +#include + +static PyObject * +sys_mdebug(PyObject *self, PyObject *args) +{ + int flag; + if (!PyArg_ParseTuple(args, "i:mdebug", &flag)) + return NULL; + mallopt(M_DEBUG, flag); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* USE_MALLOPT */ + +static PyObject * +sys_getrefcount(PyObject *self, PyObject *args) +{ + PyObject *arg; + if (!PyArg_ParseTuple(args, "O:getrefcount", &arg)) + return NULL; + return PyInt_FromLong(arg->ob_refcnt); +} + +#ifdef Py_TRACE_REFS +static PyObject * +sys_gettotalrefcount(PyObject *self, PyObject *args) +{ + extern long _Py_RefTotal; + if (!PyArg_ParseTuple(args, ":gettotalrefcount")) + return NULL; + return PyInt_FromLong(_Py_RefTotal); +} + +#endif /* Py_TRACE_REFS */ + +static char getrefcount_doc[] = +"getrefcount(object) -> integer\n\ +\n\ +Return the current reference count for the object. This includes the\n\ +temporary reference in the argument list, so it is at least 2."; + +#ifdef COUNT_ALLOCS +static PyObject * +sys_getcounts(PyObject *self, PyObject *args) +{ + extern PyObject *get_counts(void); + + if (!PyArg_ParseTuple(args, ":getcounts")) + return NULL; + return get_counts(); +} +#endif + +static char getframe_doc[] = +"_getframe([depth]) -> frameobject\n\ +\n\ +Return a frame object from the call stack. If optional integer depth is\n\ +given, return the frame object that many calls below the top of the stack.\n\ +If that is deeper than the call stack, ValueError is raised. The default\n\ +for depth is zero, returning the frame at the top of the call stack.\n\ +\n\ +This function should be used for internal and specialized\n\ +purposes only."; + +static PyObject * +sys_getframe(PyObject *self, PyObject *args) +{ + PyFrameObject *f = PyThreadState_Get()->frame; + int depth = -1; + + if (!PyArg_ParseTuple(args, "|i:_getframe", &depth)) + return NULL; + + while (depth > 0 && f != NULL) { + f = f->f_back; + --depth; + } + if (f == NULL) { + PyErr_SetString(PyExc_ValueError, + "call stack is not deep enough"); + return NULL; + } + Py_INCREF(f); + return (PyObject*)f; +} + + +#ifdef Py_TRACE_REFS +/* Defined in objects.c because it uses static globals if that file */ +extern PyObject *_Py_GetObjects(PyObject *, PyObject *); +#endif + +#ifdef DYNAMIC_EXECUTION_PROFILE +/* Defined in ceval.c because it uses static globals if that file */ +extern PyObject *_Py_GetDXProfile(PyObject *, PyObject *); +#endif + +static PyMethodDef sys_methods[] = { + /* Might as well keep this in alphabetic order */ + {"displayhook", sys_displayhook, 1, displayhook_doc}, + {"exc_info", sys_exc_info, 1, exc_info_doc}, + {"excepthook", sys_excepthook, 1, excepthook_doc}, + {"exit", sys_exit, 0, exit_doc}, + {"getdefaultencoding", sys_getdefaultencoding, 1, + getdefaultencoding_doc}, +#ifdef HAVE_DLOPEN + {"getdlopenflags", sys_getdlopenflags, 1, + getdlopenflags_doc}, +#endif +#ifdef COUNT_ALLOCS + {"getcounts", sys_getcounts, 1}, +#endif +#ifdef DYNAMIC_EXECUTION_PROFILE + {"getdxp", _Py_GetDXProfile, 1}, +#endif +#ifdef Py_TRACE_REFS + {"getobjects", _Py_GetObjects, 1}, + {"gettotalrefcount", sys_gettotalrefcount, 1}, +#endif + {"getrefcount", sys_getrefcount, 1, getrefcount_doc}, + {"getrecursionlimit", sys_getrecursionlimit, 1, + getrecursionlimit_doc}, + {"_getframe", sys_getframe, 1, getframe_doc}, +#ifdef USE_MALLOPT + {"mdebug", sys_mdebug, 1}, +#endif + {"setdefaultencoding", sys_setdefaultencoding, 1, + setdefaultencoding_doc}, + {"setcheckinterval", sys_setcheckinterval, 1, + setcheckinterval_doc}, +#ifdef HAVE_DLOPEN + {"setdlopenflags", sys_setdlopenflags, 1, + setdlopenflags_doc}, +#endif + {"setprofile", sys_setprofile, 0, setprofile_doc}, + {"setrecursionlimit", sys_setrecursionlimit, 1, + setrecursionlimit_doc}, + {"settrace", sys_settrace, 0, settrace_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +list_builtin_module_names(void) +{ + PyObject *list = PyList_New(0); + int i; + if (list == NULL) + return NULL; + for (i = 0; PyImport_Inittab[i].name != NULL; i++) { + PyObject *name = PyString_FromString( + PyImport_Inittab[i].name); + if (name == NULL) + break; + PyList_Append(list, name); + Py_DECREF(name); + } + if (PyList_Sort(list) != 0) { + Py_DECREF(list); + list = NULL; + } + if (list) { + PyObject *v = PyList_AsTuple(list); + Py_DECREF(list); + list = v; + } + return list; +} + +static PyObject *warnoptions = NULL; + +void +PySys_ResetWarnOptions(void) +{ + if (warnoptions == NULL || !PyList_Check(warnoptions)) + return; + PyList_SetSlice(warnoptions, 0, PyList_GET_SIZE(warnoptions), NULL); +} + +void +PySys_AddWarnOption(char *s) +{ + PyObject *str; + + if (warnoptions == NULL || !PyList_Check(warnoptions)) { + Py_XDECREF(warnoptions); + warnoptions = PyList_New(0); + if (warnoptions == NULL) + return; + } + str = PyString_FromString(s); + if (str != NULL) { + PyList_Append(warnoptions, str); + Py_DECREF(str); + } +} + +/* XXX This doc string is too long to be a single string literal in VC++ 5.0. + Two literals concatenated works just fine. If you have a K&R compiler + or other abomination that however *does* understand longer strings, + get rid of the !!! comment in the middle and the quotes that surround it. */ +static char sys_doc[] = +"This module provides access to some objects used or maintained by the\n\ +interpreter and to functions that interact strongly with the interpreter.\n\ +\n\ +Dynamic objects:\n\ +\n\ +argv -- command line arguments; argv[0] is the script pathname if known\n\ +path -- module search path; path[0] is the script directory, else ''\n\ +modules -- dictionary of loaded modules\n\ +\n\ +displayhook -- called to show results in an interactive session\n\ +excepthook -- called to handle any uncaught exception other than SystemExit\n\ + To customize printing in an interactive session or to install a custom\n\ + top-level exception handler, assign other functions to replace these.\n\ +\n\ +exitfunc -- if sys.exitfunc exists, this routine is called when Python exits\n\ + Assigning to sys.exitfunc is deprecated; use the atexit module instead.\n\ +\n\ +stdin -- standard input file object; used by raw_input() and input()\n\ +stdout -- standard output file object; used by the print statement\n\ +stderr -- standard error object; used for error messages\n\ + By assigning other file objects (or objects that behave like files)\n\ + to these, it is possible to redirect all of the interpreter's I/O.\n\ +\n\ +last_type -- type of last uncaught exception\n\ +last_value -- value of last uncaught exception\n\ +last_traceback -- traceback of last uncaught exception\n\ + These three are only available in an interactive session after a\n\ + traceback has been printed.\n\ +\n\ +exc_type -- type of exception currently being handled\n\ +exc_value -- value of exception currently being handled\n\ +exc_traceback -- traceback of exception currently being handled\n\ + The function exc_info() should be used instead of these three,\n\ + because it is thread-safe.\n\ +" +#ifndef MS_WIN16 +/* concatenating string here */ +"\n\ +Static objects:\n\ +\n\ +maxint -- the largest supported integer (the smallest is -maxint-1)\n\ +maxunicode -- the largest supported character\n\ +builtin_module_names -- tuple of module names built into this intepreter\n\ +version -- the version of this interpreter as a string\n\ +version_info -- version information as a tuple\n\ +hexversion -- version information encoded as a single integer\n\ +copyright -- copyright notice pertaining to this interpreter\n\ +platform -- platform identifier\n\ +executable -- pathname of this Python interpreter\n\ +prefix -- prefix used to find the Python library\n\ +exec_prefix -- prefix used to find the machine-specific Python library\n\ +" +#ifdef MS_WINDOWS +/* concatenating string here */ +"dllhandle -- [Windows only] integer handle of the Python DLL\n\ +winver -- [Windows only] version number of the Python DLL\n\ +" +#endif /* MS_WINDOWS */ +"__stdin__ -- the original stdin; don't touch!\n\ +__stdout__ -- the original stdout; don't touch!\n\ +__stderr__ -- the original stderr; don't touch!\n\ +__displayhook__ -- the original displayhook; don't touch!\n\ +__excepthook__ -- the original excepthook; don't touch!\n\ +\n\ +Functions:\n\ +\n\ +displayhook() -- print an object to the screen, and save it in __builtin__._\n\ +excepthook() -- print an exception and its traceback to sys.stderr\n\ +exc_info() -- return thread-safe information about the current exception\n\ +exit() -- exit the interpreter by raising SystemExit\n\ +getdlopenflags() -- returns flags to be used for dlopen() calls\n\ +getrefcount() -- return the reference count for an object (plus one :-)\n\ +getrecursionlimit() -- return the max recursion depth for the interpreter\n\ +setcheckinterval() -- control how often the interpreter checks for events\n\ +setdlopenflags() -- set the flags to be used for dlopen() calls\n\ +setprofile() -- set the global profiling function\n\ +setrecursionlimit() -- set the max recursion depth for the interpreter\n\ +settrace() -- set the global debug tracing function\n\ +" +#endif /* MS_WIN16 */ +/* end of sys_doc */ ; + +PyObject * +_PySys_Init(void) +{ + PyObject *m, *v, *sysdict; + PyObject *sysin, *sysout, *syserr; + char *s; + + m = Py_InitModule3("sys", sys_methods, sys_doc); + sysdict = PyModule_GetDict(m); + + sysin = PyFile_FromFile(stdin, "", "r", NULL); + sysout = PyFile_FromFile(stdout, "", "w", NULL); + syserr = PyFile_FromFile(stderr, "", "w", NULL); + if (PyErr_Occurred()) + return NULL; + PyDict_SetItemString(sysdict, "stdin", sysin); + PyDict_SetItemString(sysdict, "stdout", sysout); + PyDict_SetItemString(sysdict, "stderr", syserr); + /* Make backup copies for cleanup */ + PyDict_SetItemString(sysdict, "__stdin__", sysin); + PyDict_SetItemString(sysdict, "__stdout__", sysout); + PyDict_SetItemString(sysdict, "__stderr__", syserr); + PyDict_SetItemString(sysdict, "__displayhook__", + PyDict_GetItemString(sysdict, "displayhook")); + PyDict_SetItemString(sysdict, "__excepthook__", + PyDict_GetItemString(sysdict, "excepthook")); + Py_XDECREF(sysin); + Py_XDECREF(sysout); + Py_XDECREF(syserr); + PyDict_SetItemString(sysdict, "version", + v = PyString_FromString(Py_GetVersion())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "hexversion", + v = PyInt_FromLong(PY_VERSION_HEX)); + Py_XDECREF(v); + /* + * These release level checks are mutually exclusive and cover + * the field, so don't get too fancy with the pre-processor! + */ +#if PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_ALPHA + s = "alpha"; +#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_BETA + s = "beta"; +#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_GAMMA + s = "candidate"; +#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_FINAL + s = "final"; +#endif + PyDict_SetItemString(sysdict, "version_info", + v = Py_BuildValue("iiisi", PY_MAJOR_VERSION, + PY_MINOR_VERSION, + PY_MICRO_VERSION, s, + PY_RELEASE_SERIAL)); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "copyright", + v = PyString_FromString(Py_GetCopyright())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "platform", + v = PyString_FromString(Py_GetPlatform())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "executable", + v = PyString_FromString(Py_GetProgramFullPath())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "prefix", + v = PyString_FromString(Py_GetPrefix())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "exec_prefix", + v = PyString_FromString(Py_GetExecPrefix())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "maxint", + v = PyInt_FromLong(PyInt_GetMax())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "maxunicode", + v = PyInt_FromLong(PyUnicode_GetMax())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "builtin_module_names", + v = list_builtin_module_names()); + Py_XDECREF(v); + { + /* Assumes that longs are at least 2 bytes long. + Should be safe! */ + unsigned long number = 1; + char *value; + + s = (char *) &number; + if (s[0] == 0) + value = "big"; + else + value = "little"; + PyDict_SetItemString(sysdict, "byteorder", + v = PyString_FromString(value)); + Py_XDECREF(v); + } +#ifdef MS_COREDLL + PyDict_SetItemString(sysdict, "dllhandle", + v = PyLong_FromVoidPtr(PyWin_DLLhModule)); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "winver", + v = PyString_FromString(PyWin_DLLVersionString)); + Py_XDECREF(v); +#endif + if (warnoptions == NULL) { + warnoptions = PyList_New(0); + } + else { + Py_INCREF(warnoptions); + } + if (warnoptions != NULL) { + PyDict_SetItemString(sysdict, "warnoptions", warnoptions); + } + + if (PyErr_Occurred()) + return NULL; + return m; +} + +static PyObject * +makepathobject(char *path, int delim) +{ + int i, n; + char *p; + PyObject *v, *w; + + n = 1; + p = path; + while ((p = strchr(p, delim)) != NULL) { + n++; + p++; + } + v = PyList_New(n); + if (v == NULL) + return NULL; + for (i = 0; ; i++) { + p = strchr(path, delim); + if (p == NULL) + p = strchr(path, '\0'); /* End of string */ + w = PyString_FromStringAndSize(path, (int) (p - path)); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(v, i, w); + if (*p == '\0') + break; + path = p+1; + } + return v; +} + +void +PySys_SetPath(char *path) +{ + PyObject *v; + if ((v = makepathobject(path, DELIM)) == NULL) + Py_FatalError("can't create sys.path"); + if (PySys_SetObject("path", v) != 0) + Py_FatalError("can't assign sys.path"); + Py_DECREF(v); +} + +static PyObject * +makeargvobject(int argc, char **argv) +{ + PyObject *av; + if (argc <= 0 || argv == NULL) { + /* Ensure at least one (empty) argument is seen */ + static char *empty_argv[1] = {""}; + argv = empty_argv; + argc = 1; + } + av = PyList_New(argc); + if (av != NULL) { + int i; + for (i = 0; i < argc; i++) { + PyObject *v = PyString_FromString(argv[i]); + if (v == NULL) { + Py_DECREF(av); + av = NULL; + break; + } + PyList_SetItem(av, i, v); + } + } + return av; +} + +void +PySys_SetArgv(int argc, char **argv) +{ + PyObject *av = makeargvobject(argc, argv); + PyObject *path = PySys_GetObject("path"); + if (av == NULL) + Py_FatalError("no mem for sys.argv"); + if (PySys_SetObject("argv", av) != 0) + Py_FatalError("can't assign sys.argv"); + if (path != NULL) { + char *argv0 = argv[0]; + char *p = NULL; + int n = 0; + PyObject *a; +#ifdef HAVE_READLINK + char link[MAXPATHLEN+1]; + char argv0copy[2*MAXPATHLEN+1]; + int nr = 0; + if (argc > 0 && argv0 != NULL) + nr = readlink(argv0, link, MAXPATHLEN); + if (nr > 0) { + /* It's a symlink */ + link[nr] = '\0'; + if (link[0] == SEP) + argv0 = link; /* Link to absolute path */ + else if (strchr(link, SEP) == NULL) + ; /* Link without path */ + else { + /* Must join(dirname(argv0), link) */ + char *q = strrchr(argv0, SEP); + if (q == NULL) + argv0 = link; /* argv0 without path */ + else { + /* Must make a copy */ + strcpy(argv0copy, argv0); + q = strrchr(argv0copy, SEP); + strcpy(q+1, link); + argv0 = argv0copy; + } + } + } +#endif /* HAVE_READLINK */ +#if SEP == '\\' /* Special case for MS filename syntax */ + if (argc > 0 && argv0 != NULL) { + char *q; + p = strrchr(argv0, SEP); + /* Test for alternate separator */ + q = strrchr(p ? p : argv0, '/'); + if (q != NULL) + p = q; + if (p != NULL) { + n = p + 1 - argv0; + if (n > 1 && p[-1] != ':') + n--; /* Drop trailing separator */ + } + } +#else /* All other filename syntaxes */ + if (argc > 0 && argv0 != NULL) + p = strrchr(argv0, SEP); + if (p != NULL) { +#ifndef RISCOS + n = p + 1 - argv0; +#else /* don't include trailing separator */ + n = p - argv0; +#endif /* RISCOS */ +#if SEP == '/' /* Special case for Unix filename syntax */ + if (n > 1) + n--; /* Drop trailing separator */ +#endif /* Unix */ + } +#endif /* All others */ + a = PyString_FromStringAndSize(argv0, n); + if (a == NULL) + Py_FatalError("no mem for sys.path insertion"); + if (PyList_Insert(path, 0, a) < 0) + Py_FatalError("sys.path.insert(0) failed"); + Py_DECREF(a); + } + Py_DECREF(av); +} + + +/* APIs to write to sys.stdout or sys.stderr using a printf-like interface. + Adapted from code submitted by Just van Rossum. + + PySys_WriteStdout(format, ...) + PySys_WriteStderr(format, ...) + + The first function writes to sys.stdout; the second to sys.stderr. When + there is a problem, they write to the real (C level) stdout or stderr; + no exceptions are raised. + + Both take a printf-style format string as their first argument followed + by a variable length argument list determined by the format string. + + *** WARNING *** + + The format should limit the total size of the formatted output string to + 1000 bytes. In particular, this means that no unrestricted "%s" formats + should occur; these should be limited using "%.s where is a + decimal number calculated so that plus the maximum size of other + formatted text does not exceed 1000 bytes. Also watch out for "%f", + which can print hundreds of digits for very large numbers. + + */ + +static void +mywrite(char *name, FILE *fp, const char *format, va_list va) +{ + PyObject *file; + PyObject *error_type, *error_value, *error_traceback; + + PyErr_Fetch(&error_type, &error_value, &error_traceback); + file = PySys_GetObject(name); + if (file == NULL || PyFile_AsFile(file) == fp) + vfprintf(fp, format, va); + else { + char buffer[1001]; + if (vsprintf(buffer, format, va) >= sizeof(buffer)) + Py_FatalError("PySys_WriteStdout/err: buffer overrun"); + if (PyFile_WriteString(buffer, file) != 0) { + PyErr_Clear(); + fputs(buffer, fp); + } + } + PyErr_Restore(error_type, error_value, error_traceback); +} + +void +PySys_WriteStdout(const char *format, ...) +{ + va_list va; + + va_start(va, format); + mywrite("stdout", stdout, format, va); + va_end(va); +} + +void +PySys_WriteStderr(const char *format, ...) +{ + va_list va; + + va_start(va, format); + mywrite("stderr", stderr, format, va); + va_end(va); +} diff --git a/src/main/resource/testFiles/cpython/revFiles/09705f_db2a0f_Modules#_sre.c b/src/main/resource/testFiles/cpython/revFiles/09705f_db2a0f_Modules#_sre.c new file mode 100644 index 0000000..619d39b --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/09705f_db2a0f_Modules#_sre.c @@ -0,0 +1,2995 @@ +/* + * Secret Labs' Regular Expression Engine + * + * regular expression matching engine + * + * partial history: + * 1999-10-24 fl created (based on existing template matcher code) + * 2000-03-06 fl first alpha, sort of + * 2000-08-01 fl fixes for 1.6b1 + * 2000-08-07 fl use PyOS_CheckStack() if available + * 2000-09-20 fl added expand method + * 2001-03-20 fl lots of fixes for 2.1b2 + * 2001-04-15 fl export copyright as Python attribute, not global + * 2001-04-28 fl added __copy__ methods (work in progress) + * 2001-05-14 fl fixes for 1.5.2 compatibility + * 2001-07-01 fl added BIGCHARSET support (from Martin von Loewis) + * 2001-10-18 fl fixed group reset issue (from Matthew Mueller) + * 2001-10-20 fl added split primitive; reenable unicode for 1.6/2.0/2.1 + * 2001-10-21 fl added sub/subn primitive + * 2001-10-24 fl added finditer primitive (for 2.2 only) + * 2001-12-07 fl fixed memory leak in sub/subn (Guido van Rossum) + * 2002-11-09 fl fixed empty sub/subn return type + * + * Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved. + * + * This version of the SRE library can be redistributed under CNRI's + * Python 1.6 license. For any other use, please contact Secret Labs + * AB (info@pythonware.com). + * + * Portions of this engine have been developed in cooperation with + * CNRI. Hewlett-Packard provided funding for 1.6 integration and + * other compatibility work. + */ + +#ifndef SRE_RECURSIVE + +static char copyright[] = + " SRE 2.2.2 Copyright (c) 1997-2002 by Secret Labs AB "; + +#include "Python.h" +#include "structmember.h" /* offsetof */ + +#include "sre.h" + +#include + +/* name of this module, minus the leading underscore */ +#if !defined(SRE_MODULE) +#define SRE_MODULE "sre" +#endif + +/* defining this one enables tracing */ +#undef VERBOSE + +#if PY_VERSION_HEX >= 0x01060000 +#if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE) +/* defining this enables unicode support (default under 1.6a1 and later) */ +#define HAVE_UNICODE +#endif +#endif + +/* -------------------------------------------------------------------- */ +/* optional features */ + +/* prevent run-away recursion (bad patterns on long strings) */ + +#if !defined(USE_STACKCHECK) +#if defined(MS_WIN64) || defined(__LP64__) || defined(_LP64) +/* require smaller recursion limit for a number of 64-bit platforms: + Win64 (MS_WIN64), Linux64 (__LP64__), Monterey (64-bit AIX) (_LP64) */ +/* FIXME: maybe the limit should be 40000 / sizeof(void*) ? */ +#define USE_RECURSION_LIMIT 7500 +#else +#define USE_RECURSION_LIMIT 10000 +#endif +#endif + +/* enables fast searching */ +#define USE_FAST_SEARCH + +/* enables aggressive inlining (always on for Visual C) */ +#undef USE_INLINE + +/* enables copy/deepcopy handling (work in progress) */ +#undef USE_BUILTIN_COPY + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_DEL(op) PyMem_DEL((op)) +#endif + +/* -------------------------------------------------------------------- */ + +#if defined(_MSC_VER) +#pragma optimize("agtw", on) /* doesn't seem to make much difference... */ +#pragma warning(disable: 4710) /* who cares if functions are not inlined ;-) */ +/* fastest possible local call under MSVC */ +#define LOCAL(type) static __inline type __fastcall +#elif defined(USE_INLINE) +#define LOCAL(type) static inline type +#else +#define LOCAL(type) static type +#endif + +/* error codes */ +#define SRE_ERROR_ILLEGAL -1 /* illegal opcode */ +#define SRE_ERROR_STATE -2 /* illegal state */ +#define SRE_ERROR_RECURSION_LIMIT -3 /* runaway recursion */ +#define SRE_ERROR_MEMORY -9 /* out of memory */ + +#if defined(VERBOSE) +#define TRACE(v) printf v +#else +#define TRACE(v) +#endif + +/* -------------------------------------------------------------------- */ +/* search engine state */ + +/* default character predicates (run sre_chars.py to regenerate tables) */ + +#define SRE_DIGIT_MASK 1 +#define SRE_SPACE_MASK 2 +#define SRE_LINEBREAK_MASK 4 +#define SRE_ALNUM_MASK 8 +#define SRE_WORD_MASK 16 + +/* FIXME: this assumes ASCII. create tables in init_sre() instead */ + +static char sre_char_info[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 2, +2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, +0, 0, 16, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0 }; + +static char sre_char_lower[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, +27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, +44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, +61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, +108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, +122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, +106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, +120, 121, 122, 123, 124, 125, 126, 127 }; + +#define SRE_IS_DIGIT(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_DIGIT_MASK) : 0) +#define SRE_IS_SPACE(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_SPACE_MASK) : 0) +#define SRE_IS_LINEBREAK(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_LINEBREAK_MASK) : 0) +#define SRE_IS_ALNUM(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_ALNUM_MASK) : 0) +#define SRE_IS_WORD(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_WORD_MASK) : 0) + +static unsigned int sre_lower(unsigned int ch) +{ + return ((ch) < 128 ? sre_char_lower[ch] : ch); +} + +/* locale-specific character predicates */ + +#define SRE_LOC_IS_DIGIT(ch) ((ch) < 256 ? isdigit((ch)) : 0) +#define SRE_LOC_IS_SPACE(ch) ((ch) < 256 ? isspace((ch)) : 0) +#define SRE_LOC_IS_LINEBREAK(ch) ((ch) == '\n') +#define SRE_LOC_IS_ALNUM(ch) ((ch) < 256 ? isalnum((ch)) : 0) +#define SRE_LOC_IS_WORD(ch) (SRE_LOC_IS_ALNUM((ch)) || (ch) == '_') + +static unsigned int sre_lower_locale(unsigned int ch) +{ + return ((ch) < 256 ? tolower((ch)) : ch); +} + +/* unicode-specific character predicates */ + +#if defined(HAVE_UNICODE) + +#define SRE_UNI_IS_DIGIT(ch) Py_UNICODE_ISDIGIT((Py_UNICODE)(ch)) +#define SRE_UNI_IS_SPACE(ch) Py_UNICODE_ISSPACE((Py_UNICODE)(ch)) +#define SRE_UNI_IS_LINEBREAK(ch) Py_UNICODE_ISLINEBREAK((Py_UNICODE)(ch)) +#define SRE_UNI_IS_ALNUM(ch) Py_UNICODE_ISALNUM((Py_UNICODE)(ch)) +#define SRE_UNI_IS_WORD(ch) (SRE_UNI_IS_ALNUM((ch)) || (ch) == '_') + +static unsigned int sre_lower_unicode(unsigned int ch) +{ + return (unsigned int) Py_UNICODE_TOLOWER((Py_UNICODE)(ch)); +} + +#endif + +LOCAL(int) +sre_category(SRE_CODE category, unsigned int ch) +{ + switch (category) { + + case SRE_CATEGORY_DIGIT: + return SRE_IS_DIGIT(ch); + case SRE_CATEGORY_NOT_DIGIT: + return !SRE_IS_DIGIT(ch); + case SRE_CATEGORY_SPACE: + return SRE_IS_SPACE(ch); + case SRE_CATEGORY_NOT_SPACE: + return !SRE_IS_SPACE(ch); + case SRE_CATEGORY_WORD: + return SRE_IS_WORD(ch); + case SRE_CATEGORY_NOT_WORD: + return !SRE_IS_WORD(ch); + case SRE_CATEGORY_LINEBREAK: + return SRE_IS_LINEBREAK(ch); + case SRE_CATEGORY_NOT_LINEBREAK: + return !SRE_IS_LINEBREAK(ch); + + case SRE_CATEGORY_LOC_WORD: + return SRE_LOC_IS_WORD(ch); + case SRE_CATEGORY_LOC_NOT_WORD: + return !SRE_LOC_IS_WORD(ch); + +#if defined(HAVE_UNICODE) + case SRE_CATEGORY_UNI_DIGIT: + return SRE_UNI_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_NOT_DIGIT: + return !SRE_UNI_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_SPACE: + return SRE_UNI_IS_SPACE(ch); + case SRE_CATEGORY_UNI_NOT_SPACE: + return !SRE_UNI_IS_SPACE(ch); + case SRE_CATEGORY_UNI_WORD: + return SRE_UNI_IS_WORD(ch); + case SRE_CATEGORY_UNI_NOT_WORD: + return !SRE_UNI_IS_WORD(ch); + case SRE_CATEGORY_UNI_LINEBREAK: + return SRE_UNI_IS_LINEBREAK(ch); + case SRE_CATEGORY_UNI_NOT_LINEBREAK: + return !SRE_UNI_IS_LINEBREAK(ch); +#else + case SRE_CATEGORY_UNI_DIGIT: + return SRE_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_NOT_DIGIT: + return !SRE_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_SPACE: + return SRE_IS_SPACE(ch); + case SRE_CATEGORY_UNI_NOT_SPACE: + return !SRE_IS_SPACE(ch); + case SRE_CATEGORY_UNI_WORD: + return SRE_LOC_IS_WORD(ch); + case SRE_CATEGORY_UNI_NOT_WORD: + return !SRE_LOC_IS_WORD(ch); + case SRE_CATEGORY_UNI_LINEBREAK: + return SRE_IS_LINEBREAK(ch); + case SRE_CATEGORY_UNI_NOT_LINEBREAK: + return !SRE_IS_LINEBREAK(ch); +#endif + } + return 0; +} + +/* helpers */ + +static void +mark_fini(SRE_STATE* state) +{ + if (state->mark_stack) { + free(state->mark_stack); + state->mark_stack = NULL; + } + state->mark_stack_size = state->mark_stack_base = 0; +} + +static int +mark_save(SRE_STATE* state, int lo, int hi) +{ + void* stack; + int size; + int minsize, newsize; + + if (hi <= lo) + return 0; + + size = (hi - lo) + 1; + + newsize = state->mark_stack_size; + minsize = state->mark_stack_base + size; + + if (newsize < minsize) { + /* create new stack */ + if (!newsize) { + newsize = 512; + if (newsize < minsize) + newsize = minsize; + TRACE(("allocate stack %d\n", newsize)); + stack = malloc(sizeof(void*) * newsize); + } else { + /* grow the stack */ + while (newsize < minsize) + newsize += newsize; + TRACE(("grow stack to %d\n", newsize)); + stack = realloc(state->mark_stack, sizeof(void*) * newsize); + } + if (!stack) { + mark_fini(state); + return SRE_ERROR_MEMORY; + } + state->mark_stack = stack; + state->mark_stack_size = newsize; + } + + TRACE(("copy %d:%d to %d (%d)\n", lo, hi, state->mark_stack_base, size)); + + memcpy(state->mark_stack + state->mark_stack_base, state->mark + lo, + size * sizeof(void*)); + + state->mark_stack_base += size; + + return 0; +} + +static int +mark_restore(SRE_STATE* state, int lo, int hi) +{ + int size; + + if (hi <= lo) + return 0; + + size = (hi - lo) + 1; + + state->mark_stack_base -= size; + + TRACE(("copy %d:%d from %d\n", lo, hi, state->mark_stack_base)); + + memcpy(state->mark + lo, state->mark_stack + state->mark_stack_base, + size * sizeof(void*)); + + return 0; +} + +static void +lastmark_restore(SRE_STATE *state, int lastmark) +{ + if (state->lastmark > lastmark) { + memset( + state->mark + lastmark + 1, 0, + (state->lastmark - lastmark) * sizeof(void*) + ); + state->lastmark = lastmark; + state->lastindex = (lastmark == 0) ? -1 : (lastmark-1)/2+1; + } +} + +/* generate 8-bit version */ + +#define SRE_CHAR unsigned char +#define SRE_AT sre_at +#define SRE_COUNT sre_count +#define SRE_CHARSET sre_charset +#define SRE_INFO sre_info +#define SRE_MATCH sre_match +#define SRE_SEARCH sre_search +#define SRE_LITERAL_TEMPLATE sre_literal_template + +#if defined(HAVE_UNICODE) + +#define SRE_RECURSIVE +#include "_sre.c" +#undef SRE_RECURSIVE + +#undef SRE_LITERAL_TEMPLATE +#undef SRE_SEARCH +#undef SRE_MATCH +#undef SRE_INFO +#undef SRE_CHARSET +#undef SRE_COUNT +#undef SRE_AT +#undef SRE_CHAR + +/* generate 16-bit unicode version */ + +#define SRE_CHAR Py_UNICODE +#define SRE_AT sre_uat +#define SRE_COUNT sre_ucount +#define SRE_CHARSET sre_ucharset +#define SRE_INFO sre_uinfo +#define SRE_MATCH sre_umatch +#define SRE_SEARCH sre_usearch +#define SRE_LITERAL_TEMPLATE sre_uliteral_template +#endif + +#endif /* SRE_RECURSIVE */ + +/* -------------------------------------------------------------------- */ +/* String matching engine */ + +/* the following section is compiled twice, with different character + settings */ + +LOCAL(int) +SRE_AT(SRE_STATE* state, SRE_CHAR* ptr, SRE_CODE at) +{ + /* check if pointer is at given position */ + + int this, that; + + switch (at) { + + case SRE_AT_BEGINNING: + case SRE_AT_BEGINNING_STRING: + return ((void*) ptr == state->beginning); + + case SRE_AT_BEGINNING_LINE: + return ((void*) ptr == state->beginning || + SRE_IS_LINEBREAK((int) ptr[-1])); + + case SRE_AT_END: + return (((void*) (ptr+1) == state->end && + SRE_IS_LINEBREAK((int) ptr[0])) || + ((void*) ptr == state->end)); + + case SRE_AT_END_LINE: + return ((void*) ptr == state->end || + SRE_IS_LINEBREAK((int) ptr[0])); + + case SRE_AT_END_STRING: + return ((void*) ptr == state->end); + + case SRE_AT_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_IS_WORD((int) ptr[0]) : 0; + return this != that; + + case SRE_AT_NON_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_IS_WORD((int) ptr[0]) : 0; + return this == that; + + case SRE_AT_LOC_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_LOC_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_LOC_IS_WORD((int) ptr[0]) : 0; + return this != that; + + case SRE_AT_LOC_NON_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_LOC_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_LOC_IS_WORD((int) ptr[0]) : 0; + return this == that; + +#if defined(HAVE_UNICODE) + case SRE_AT_UNI_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_UNI_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_UNI_IS_WORD((int) ptr[0]) : 0; + return this != that; + + case SRE_AT_UNI_NON_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_UNI_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_UNI_IS_WORD((int) ptr[0]) : 0; + return this == that; +#endif + + } + + return 0; +} + +LOCAL(int) +SRE_CHARSET(SRE_CODE* set, SRE_CODE ch) +{ + /* check if character is a member of the given set */ + + int ok = 1; + + for (;;) { + switch (*set++) { + + case SRE_OP_LITERAL: + /* */ + if (ch == set[0]) + return ok; + set++; + break; + + case SRE_OP_RANGE: + /* */ + if (set[0] <= ch && ch <= set[1]) + return ok; + set += 2; + break; + + case SRE_OP_CHARSET: + /* (16 bits per code word) */ + if (ch < 256 && (set[ch >> 4] & (1 << (ch & 15)))) + return ok; + set += 16; + break; + + case SRE_OP_BIGCHARSET: + /* <256 blockindices> */ + { + int count, block; + count = *(set++); + block = ((unsigned char*)set)[ch >> 8]; + set += 128; + if (set[block*16 + ((ch & 255)>>4)] & (1 << (ch & 15))) + return ok; + set += count*16; + break; + } + + case SRE_OP_CATEGORY: + /* */ + if (sre_category(set[0], (int) ch)) + return ok; + set += 1; + break; + + case SRE_OP_NEGATE: + ok = !ok; + break; + + case SRE_OP_FAILURE: + return !ok; + + default: + /* internal error -- there's not much we can do about it + here, so let's just pretend it didn't match... */ + return 0; + } + } +} + +LOCAL(int) SRE_MATCH(SRE_STATE* state, SRE_CODE* pattern, int level); + +LOCAL(int) +SRE_COUNT(SRE_STATE* state, SRE_CODE* pattern, int maxcount, int level) +{ + SRE_CODE chr; + SRE_CHAR* ptr = state->ptr; + SRE_CHAR* end = state->end; + int i; + + /* adjust end */ + if (maxcount < end - ptr && maxcount != 65535) + end = ptr + maxcount; + + switch (pattern[0]) { + + case SRE_OP_ANY: + /* repeated dot wildcard. */ + TRACE(("|%p|%p|COUNT ANY\n", pattern, ptr)); + while (ptr < end && !SRE_IS_LINEBREAK(*ptr)) + ptr++; + break; + + case SRE_OP_ANY_ALL: + /* repeated dot wildcare. skip to the end of the target + string, and backtrack from there */ + TRACE(("|%p|%p|COUNT ANY_ALL\n", pattern, ptr)); + ptr = end; + break; + + case SRE_OP_LITERAL: + /* repeated literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT LITERAL %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) *ptr == chr) + ptr++; + break; + + case SRE_OP_LITERAL_IGNORE: + /* repeated literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT LITERAL_IGNORE %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) state->lower(*ptr) == chr) + ptr++; + break; + + case SRE_OP_NOT_LITERAL: + /* repeated non-literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT NOT_LITERAL %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) *ptr != chr) + ptr++; + break; + + case SRE_OP_NOT_LITERAL_IGNORE: + /* repeated non-literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT NOT_LITERAL_IGNORE %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) state->lower(*ptr) != chr) + ptr++; + break; + + case SRE_OP_IN: + /* repeated set */ + TRACE(("|%p|%p|COUNT IN\n", pattern, ptr)); + while (ptr < end && SRE_CHARSET(pattern + 2, *ptr)) + ptr++; + break; + + default: + /* repeated single character pattern */ + TRACE(("|%p|%p|COUNT SUBPATTERN\n", pattern, ptr)); + while ((SRE_CHAR*) state->ptr < end) { + i = SRE_MATCH(state, pattern, level); + if (i < 0) + return i; + if (!i) + break; + } + TRACE(("|%p|%p|COUNT %d\n", pattern, ptr, + (SRE_CHAR*) state->ptr - ptr)); + return (SRE_CHAR*) state->ptr - ptr; + } + + TRACE(("|%p|%p|COUNT %d\n", pattern, ptr, ptr - (SRE_CHAR*) state->ptr)); + return ptr - (SRE_CHAR*) state->ptr; +} + +#if 0 /* not used in this release */ +LOCAL(int) +SRE_INFO(SRE_STATE* state, SRE_CODE* pattern) +{ + /* check if an SRE_OP_INFO block matches at the current position. + returns the number of SRE_CODE objects to skip if successful, 0 + if no match */ + + SRE_CHAR* end = state->end; + SRE_CHAR* ptr = state->ptr; + int i; + + /* check minimal length */ + if (pattern[3] && (end - ptr) < pattern[3]) + return 0; + + /* check known prefix */ + if (pattern[2] & SRE_INFO_PREFIX && pattern[5] > 1) { + /* */ + for (i = 0; i < pattern[5]; i++) + if ((SRE_CODE) ptr[i] != pattern[7 + i]) + return 0; + return pattern[0] + 2 * pattern[6]; + } + return pattern[0]; +} +#endif + +LOCAL(int) +SRE_MATCH(SRE_STATE* state, SRE_CODE* pattern, int level) +{ + /* check if string matches the given pattern. returns <0 for + error, 0 for failure, and 1 for success */ + + SRE_CHAR* end = state->end; + SRE_CHAR* ptr = state->ptr; + int i, count; + SRE_REPEAT* rp; + int lastmark; + SRE_CODE chr; + + SRE_REPEAT rep; /* FIXME: allocate in STATE instead */ + + TRACE(("|%p|%p|ENTER %d\n", pattern, ptr, level)); + +#if defined(USE_STACKCHECK) + if (level % 10 == 0 && PyOS_CheckStack()) + return SRE_ERROR_RECURSION_LIMIT; +#endif + +#if defined(USE_RECURSION_LIMIT) + if (level > USE_RECURSION_LIMIT) + return SRE_ERROR_RECURSION_LIMIT; +#endif + + if (pattern[0] == SRE_OP_INFO) { + /* optimization info block */ + /* <1=skip> <2=flags> <3=min> ... */ + if (pattern[3] && (end - ptr) < pattern[3]) { + TRACE(("reject (got %d chars, need %d)\n", + (end - ptr), pattern[3])); + return 0; + } + pattern += pattern[1] + 1; + } + + for (;;) { + + switch (*pattern++) { + + case SRE_OP_FAILURE: + /* immediate failure */ + TRACE(("|%p|%p|FAILURE\n", pattern, ptr)); + return 0; + + case SRE_OP_SUCCESS: + /* end of pattern */ + TRACE(("|%p|%p|SUCCESS\n", pattern, ptr)); + state->ptr = ptr; + return 1; + + case SRE_OP_AT: + /* match at given position */ + /* */ + TRACE(("|%p|%p|AT %d\n", pattern, ptr, *pattern)); + if (!SRE_AT(state, ptr, *pattern)) + return 0; + pattern++; + break; + + case SRE_OP_CATEGORY: + /* match at given category */ + /* */ + TRACE(("|%p|%p|CATEGORY %d\n", pattern, ptr, *pattern)); + if (ptr >= end || !sre_category(pattern[0], ptr[0])) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_LITERAL: + /* match literal string */ + /* */ + TRACE(("|%p|%p|LITERAL %d\n", pattern, ptr, *pattern)); + if (ptr >= end || (SRE_CODE) ptr[0] != pattern[0]) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_NOT_LITERAL: + /* match anything that is not literal character */ + /* */ + TRACE(("|%p|%p|NOT_LITERAL %d\n", pattern, ptr, *pattern)); + if (ptr >= end || (SRE_CODE) ptr[0] == pattern[0]) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_ANY: + /* match anything (except a newline) */ + /* */ + TRACE(("|%p|%p|ANY\n", pattern, ptr)); + if (ptr >= end || SRE_IS_LINEBREAK(ptr[0])) + return 0; + ptr++; + break; + + case SRE_OP_ANY_ALL: + /* match anything */ + /* */ + TRACE(("|%p|%p|ANY_ALL\n", pattern, ptr)); + if (ptr >= end) + return 0; + ptr++; + break; + + case SRE_OP_IN: + /* match set member (or non_member) */ + /* */ + TRACE(("|%p|%p|IN\n", pattern, ptr)); + if (ptr >= end || !SRE_CHARSET(pattern + 1, *ptr)) + return 0; + pattern += pattern[0]; + ptr++; + break; + + case SRE_OP_GROUPREF: + /* match backreference */ + TRACE(("|%p|%p|GROUPREF %d\n", pattern, ptr, pattern[0])); + i = pattern[0]; + { + SRE_CHAR* p = (SRE_CHAR*) state->mark[i+i]; + SRE_CHAR* e = (SRE_CHAR*) state->mark[i+i+1]; + if (!p || !e || e < p) + return 0; + while (p < e) { + if (ptr >= end || *ptr != *p) + return 0; + p++; ptr++; + } + } + pattern++; + break; + + case SRE_OP_GROUPREF_IGNORE: + /* match backreference */ + TRACE(("|%p|%p|GROUPREF_IGNORE %d\n", pattern, ptr, pattern[0])); + i = pattern[0]; + { + SRE_CHAR* p = (SRE_CHAR*) state->mark[i+i]; + SRE_CHAR* e = (SRE_CHAR*) state->mark[i+i+1]; + if (!p || !e || e < p) + return 0; + while (p < e) { + if (ptr >= end || + state->lower(*ptr) != state->lower(*p)) + return 0; + p++; ptr++; + } + } + pattern++; + break; + + case SRE_OP_LITERAL_IGNORE: + TRACE(("|%p|%p|LITERAL_IGNORE %d\n", pattern, ptr, pattern[0])); + if (ptr >= end || + state->lower(*ptr) != state->lower(*pattern)) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_NOT_LITERAL_IGNORE: + TRACE(("|%p|%p|NOT_LITERAL_IGNORE %d\n", pattern, ptr, *pattern)); + if (ptr >= end || + state->lower(*ptr) == state->lower(*pattern)) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_IN_IGNORE: + TRACE(("|%p|%p|IN_IGNORE\n", pattern, ptr)); + if (ptr >= end + || !SRE_CHARSET(pattern + 1, (SRE_CODE) state->lower(*ptr))) + return 0; + pattern += pattern[0]; + ptr++; + break; + + case SRE_OP_MARK: + /* set mark */ + /* */ + TRACE(("|%p|%p|MARK %d\n", pattern, ptr, pattern[0])); + i = pattern[0]; + if (i > state->lastmark) { + state->lastmark = i; + if (i & 1) + state->lastindex = i/2 + 1; + } + state->mark[i] = ptr; + pattern++; + break; + + case SRE_OP_JUMP: + case SRE_OP_INFO: + /* jump forward */ + /* */ + TRACE(("|%p|%p|JUMP %d\n", pattern, ptr, pattern[0])); + pattern += pattern[0]; + break; + + case SRE_OP_ASSERT: + /* assert subpattern */ + /* */ + TRACE(("|%p|%p|ASSERT %d\n", pattern, ptr, pattern[1])); + state->ptr = ptr - pattern[1]; + if (state->ptr < state->beginning) + return 0; + i = SRE_MATCH(state, pattern + 2, level + 1); + if (i <= 0) + return i; + pattern += pattern[0]; + break; + + case SRE_OP_ASSERT_NOT: + /* assert not subpattern */ + /* */ + TRACE(("|%p|%p|ASSERT_NOT %d\n", pattern, ptr, pattern[1])); + state->ptr = ptr - pattern[1]; + if (state->ptr >= state->beginning) { + i = SRE_MATCH(state, pattern + 2, level + 1); + if (i < 0) + return i; + if (i) + return 0; + } + pattern += pattern[0]; + break; + + case SRE_OP_BRANCH: + /* alternation */ + /* <0=skip> code ... */ + TRACE(("|%p|%p|BRANCH\n", pattern, ptr)); + lastmark = state->lastmark; + for (; pattern[0]; pattern += pattern[0]) { + if (pattern[1] == SRE_OP_LITERAL && + (ptr >= end || (SRE_CODE) *ptr != pattern[2])) + continue; + if (pattern[1] == SRE_OP_IN && + (ptr >= end || !SRE_CHARSET(pattern + 3, (SRE_CODE) *ptr))) + continue; + state->ptr = ptr; + i = SRE_MATCH(state, pattern + 1, level + 1); + if (i) + return i; + lastmark_restore(state, lastmark); + } + return 0; + + case SRE_OP_REPEAT_ONE: + /* match repeated sequence (maximizing regexp) */ + + /* this operator only works if the repeated item is + exactly one character wide, and we're not already + collecting backtracking points. for other cases, + use the MAX_REPEAT operator */ + + /* <1=min> <2=max> item tail */ + + TRACE(("|%p|%p|REPEAT_ONE %d %d\n", pattern, ptr, + pattern[1], pattern[2])); + + if (ptr + pattern[1] > end) + return 0; /* cannot match */ + + state->ptr = ptr; + + count = SRE_COUNT(state, pattern + 3, pattern[2], level + 1); + if (count < 0) + return count; + + ptr += count; + + /* when we arrive here, count contains the number of + matches, and ptr points to the tail of the target + string. check if the rest of the pattern matches, + and backtrack if not. */ + + if (count < (int) pattern[1]) + return 0; + + if (pattern[pattern[0]] == SRE_OP_SUCCESS) { + /* tail is empty. we're finished */ + state->ptr = ptr; + return 1; + + } else if (pattern[pattern[0]] == SRE_OP_LITERAL) { + /* tail starts with a literal. skip positions where + the rest of the pattern cannot possibly match */ + chr = pattern[pattern[0]+1]; + for (;;) { + while (count >= (int) pattern[1] && + (ptr >= end || *ptr != chr)) { + ptr--; + count--; + } + if (count < (int) pattern[1]) + break; + state->ptr = ptr; + i = SRE_MATCH(state, pattern + pattern[0], level + 1); + if (i) + return i; + ptr--; + count--; + } + + } else { + /* general case */ + lastmark = state->lastmark; + while (count >= (int) pattern[1]) { + state->ptr = ptr; + i = SRE_MATCH(state, pattern + pattern[0], level + 1); + if (i) + return i; + ptr--; + count--; + lastmark_restore(state, lastmark); + } + } + return 0; + + case SRE_OP_REPEAT: + /* create repeat context. all the hard work is done + by the UNTIL operator (MAX_UNTIL, MIN_UNTIL) */ + /* <1=min> <2=max> item tail */ + TRACE(("|%p|%p|REPEAT %d %d\n", pattern, ptr, + pattern[1], pattern[2])); + + rep.count = -1; + rep.pattern = pattern; + + /* install new repeat context */ + rep.prev = state->repeat; + state->repeat = &rep; + + state->ptr = ptr; + i = SRE_MATCH(state, pattern + pattern[0], level + 1); + + state->repeat = rep.prev; + + return i; + + case SRE_OP_MAX_UNTIL: + /* maximizing repeat */ + /* <1=min> <2=max> item tail */ + + /* FIXME: we probably need to deal with zero-width + matches in here... */ + + rp = state->repeat; + if (!rp) + return SRE_ERROR_STATE; + + state->ptr = ptr; + + count = rp->count + 1; + + TRACE(("|%p|%p|MAX_UNTIL %d\n", pattern, ptr, count)); + + if (count < rp->pattern[1]) { + /* not enough matches */ + rp->count = count; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + rp->count = count - 1; + state->ptr = ptr; + return 0; + } + + if (count < rp->pattern[2] || rp->pattern[2] == 65535) { + /* we may have enough matches, but if we can + match another item, do so */ + rp->count = count; + lastmark = state->lastmark; + i = mark_save(state, 0, lastmark); + if (i < 0) + return i; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + i = mark_restore(state, 0, lastmark); + if (i < 0) + return i; + lastmark_restore(state, lastmark); + rp->count = count - 1; + state->ptr = ptr; + } + + /* cannot match more repeated items here. make sure the + tail matches */ + state->repeat = rp->prev; + i = SRE_MATCH(state, pattern, level + 1); + if (i) + return i; + state->repeat = rp; + state->ptr = ptr; + return 0; + + case SRE_OP_MIN_UNTIL: + /* minimizing repeat */ + /* <1=min> <2=max> item tail */ + + rp = state->repeat; + if (!rp) + return SRE_ERROR_STATE; + + count = rp->count + 1; + + TRACE(("|%p|%p|MIN_UNTIL %d %p\n", pattern, ptr, count, + rp->pattern)); + + state->ptr = ptr; + + if (count < rp->pattern[1]) { + /* not enough matches */ + rp->count = count; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + rp->count = count-1; + state->ptr = ptr; + return 0; + } + + /* see if the tail matches */ + state->repeat = rp->prev; + i = SRE_MATCH(state, pattern, level + 1); + if (i) + return i; + + state->ptr = ptr; + state->repeat = rp; + + if (count >= rp->pattern[2] && rp->pattern[2] != 65535) + return 0; + + rp->count = count; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + rp->count = count - 1; + state->ptr = ptr; + return 0; + + default: + TRACE(("|%p|%p|UNKNOWN %d\n", pattern, ptr, pattern[-1])); + return SRE_ERROR_ILLEGAL; + } + } + + /* can't end up here */ + /* return SRE_ERROR_ILLEGAL; -- see python-dev discussion */ +} + +LOCAL(int) +SRE_SEARCH(SRE_STATE* state, SRE_CODE* pattern) +{ + SRE_CHAR* ptr = state->start; + SRE_CHAR* end = state->end; + int status = 0; + int prefix_len = 0; + int prefix_skip = 0; + SRE_CODE* prefix = NULL; + SRE_CODE* charset = NULL; + SRE_CODE* overlap = NULL; + int flags = 0; + + if (pattern[0] == SRE_OP_INFO) { + /* optimization info block */ + /* <1=skip> <2=flags> <3=min> <4=max> <5=prefix info> */ + + flags = pattern[2]; + + if (pattern[3] > 0) { + /* adjust end point (but make sure we leave at least one + character in there, so literal search will work) */ + end -= pattern[3]-1; + if (end <= ptr) + end = ptr+1; + } + + if (flags & SRE_INFO_PREFIX) { + /* pattern starts with a known prefix */ + /* */ + prefix_len = pattern[5]; + prefix_skip = pattern[6]; + prefix = pattern + 7; + overlap = prefix + prefix_len - 1; + } else if (flags & SRE_INFO_CHARSET) + /* pattern starts with a character from a known set */ + /* */ + charset = pattern + 5; + + pattern += 1 + pattern[1]; + } + + TRACE(("prefix = %p %d %d\n", prefix, prefix_len, prefix_skip)); + TRACE(("charset = %p\n", charset)); + +#if defined(USE_FAST_SEARCH) + if (prefix_len > 1) { + /* pattern starts with a known prefix. use the overlap + table to skip forward as fast as we possibly can */ + int i = 0; + end = state->end; + while (ptr < end) { + for (;;) { + if ((SRE_CODE) ptr[0] != prefix[i]) { + if (!i) + break; + else + i = overlap[i]; + } else { + if (++i == prefix_len) { + /* found a potential match */ + TRACE(("|%p|%p|SEARCH SCAN\n", pattern, ptr)); + state->start = ptr + 1 - prefix_len; + state->ptr = ptr + 1 - prefix_len + prefix_skip; + if (flags & SRE_INFO_LITERAL) + return 1; /* we got all of it */ + status = SRE_MATCH(state, pattern + 2*prefix_skip, 1); + if (status != 0) + return status; + /* close but no cigar -- try again */ + i = overlap[i]; + } + break; + } + + } + ptr++; + } + return 0; + } +#endif + + if (pattern[0] == SRE_OP_LITERAL) { + /* pattern starts with a literal character. this is used + for short prefixes, and if fast search is disabled */ + SRE_CODE chr = pattern[1]; + end = state->end; + for (;;) { + while (ptr < end && (SRE_CODE) ptr[0] != chr) + ptr++; + if (ptr >= end) + return 0; + TRACE(("|%p|%p|SEARCH LITERAL\n", pattern, ptr)); + state->start = ptr; + state->ptr = ++ptr; + if (flags & SRE_INFO_LITERAL) + return 1; /* we got all of it */ + status = SRE_MATCH(state, pattern + 2, 1); + if (status != 0) + break; + } + } else if (charset) { + /* pattern starts with a character from a known set */ + end = state->end; + for (;;) { + while (ptr < end && !SRE_CHARSET(charset, ptr[0])) + ptr++; + if (ptr >= end) + return 0; + TRACE(("|%p|%p|SEARCH CHARSET\n", pattern, ptr)); + state->start = ptr; + state->ptr = ptr; + status = SRE_MATCH(state, pattern, 1); + if (status != 0) + break; + ptr++; + } + } else + /* general case */ + while (ptr <= end) { + TRACE(("|%p|%p|SEARCH\n", pattern, ptr)); + state->start = state->ptr = ptr++; + status = SRE_MATCH(state, pattern, 1); + if (status != 0) + break; + } + + return status; +} + +LOCAL(int) +SRE_LITERAL_TEMPLATE(SRE_CHAR* ptr, int len) +{ + /* check if given string is a literal template (i.e. no escapes) */ + while (len-- > 0) + if (*ptr++ == '\\') + return 0; + return 1; +} + +#if !defined(SRE_RECURSIVE) + +/* -------------------------------------------------------------------- */ +/* factories and destructors */ + +/* see sre.h for object declarations */ + +static PyTypeObject Pattern_Type; +static PyTypeObject Match_Type; +static PyTypeObject Scanner_Type; + +static PyObject * +_compile(PyObject* self_, PyObject* args) +{ + /* "compile" pattern descriptor to pattern object */ + + PatternObject* self; + int i, n; + + PyObject* pattern; + int flags = 0; + PyObject* code; + int groups = 0; + PyObject* groupindex = NULL; + PyObject* indexgroup = NULL; + if (!PyArg_ParseTuple(args, "OiO!|iOO", &pattern, &flags, + &PyList_Type, &code, &groups, + &groupindex, &indexgroup)) + return NULL; + + n = PyList_GET_SIZE(code); + + self = PyObject_NEW_VAR(PatternObject, &Pattern_Type, n); + if (!self) + return NULL; + + self->codesize = n; + + for (i = 0; i < n; i++) { + PyObject *o = PyList_GET_ITEM(code, i); + self->code[i] = (SRE_CODE) PyInt_AsLong(o); + } + + if (PyErr_Occurred()) { + PyObject_DEL(self); + return NULL; + } + + Py_INCREF(pattern); + self->pattern = pattern; + + self->flags = flags; + + self->groups = groups; + + Py_XINCREF(groupindex); + self->groupindex = groupindex; + + Py_XINCREF(indexgroup); + self->indexgroup = indexgroup; + + return (PyObject*) self; +} + +static PyObject * +sre_codesize(PyObject* self, PyObject* args) +{ + return Py_BuildValue("i", sizeof(SRE_CODE)); +} + +static PyObject * +sre_getlower(PyObject* self, PyObject* args) +{ + int character, flags; + if (!PyArg_ParseTuple(args, "ii", &character, &flags)) + return NULL; + if (flags & SRE_FLAG_LOCALE) + return Py_BuildValue("i", sre_lower_locale(character)); + if (flags & SRE_FLAG_UNICODE) +#if defined(HAVE_UNICODE) + return Py_BuildValue("i", sre_lower_unicode(character)); +#else + return Py_BuildValue("i", sre_lower_locale(character)); +#endif + return Py_BuildValue("i", sre_lower(character)); +} + +LOCAL(void) +state_reset(SRE_STATE* state) +{ + state->lastmark = 0; + + /* FIXME: dynamic! */ + memset(state->mark, 0, sizeof(*state->mark) * SRE_MARK_SIZE); + + state->lastindex = -1; + + state->repeat = NULL; + + mark_fini(state); +} + +static void* +getstring(PyObject* string, int* p_length, int* p_charsize) +{ + /* given a python object, return a data pointer, a length (in + characters), and a character size. return NULL if the object + is not a string (or not compatible) */ + + PyBufferProcs *buffer; + int size, bytes, charsize; + void* ptr; + +#if defined(HAVE_UNICODE) + if (PyUnicode_Check(string)) { + /* unicode strings doesn't always support the buffer interface */ + ptr = (void*) PyUnicode_AS_DATA(string); + bytes = PyUnicode_GET_DATA_SIZE(string); + size = PyUnicode_GET_SIZE(string); + charsize = sizeof(Py_UNICODE); + + } else { +#endif + + /* get pointer to string buffer */ + buffer = string->ob_type->tp_as_buffer; + if (!buffer || !buffer->bf_getreadbuffer || !buffer->bf_getsegcount || + buffer->bf_getsegcount(string, NULL) != 1) { + PyErr_SetString(PyExc_TypeError, "expected string or buffer"); + return NULL; + } + + /* determine buffer size */ + bytes = buffer->bf_getreadbuffer(string, 0, &ptr); + if (bytes < 0) { + PyErr_SetString(PyExc_TypeError, "buffer has negative size"); + return NULL; + } + + /* determine character size */ +#if PY_VERSION_HEX >= 0x01060000 + size = PyObject_Size(string); +#else + size = PyObject_Length(string); +#endif + + if (PyString_Check(string) || bytes == size) + charsize = 1; +#if defined(HAVE_UNICODE) + else if (bytes == (int) (size * sizeof(Py_UNICODE))) + charsize = sizeof(Py_UNICODE); +#endif + else { + PyErr_SetString(PyExc_TypeError, "buffer size mismatch"); + return NULL; + } + +#if defined(HAVE_UNICODE) + } +#endif + + *p_length = size; + *p_charsize = charsize; + + return ptr; +} + +LOCAL(PyObject*) +state_init(SRE_STATE* state, PatternObject* pattern, PyObject* string, + int start, int end) +{ + /* prepare state object */ + + int length; + int charsize; + void* ptr; + + memset(state, 0, sizeof(SRE_STATE)); + + state->lastindex = -1; + + ptr = getstring(string, &length, &charsize); + if (!ptr) + return NULL; + + /* adjust boundaries */ + if (start < 0) + start = 0; + else if (start > length) + start = length; + + if (end < 0) + end = 0; + else if (end > length) + end = length; + + state->charsize = charsize; + + state->beginning = ptr; + + state->start = (void*) ((char*) ptr + start * state->charsize); + state->end = (void*) ((char*) ptr + end * state->charsize); + + Py_INCREF(string); + state->string = string; + state->pos = start; + state->endpos = end; + + if (pattern->flags & SRE_FLAG_LOCALE) + state->lower = sre_lower_locale; + else if (pattern->flags & SRE_FLAG_UNICODE) +#if defined(HAVE_UNICODE) + state->lower = sre_lower_unicode; +#else + state->lower = sre_lower_locale; +#endif + else + state->lower = sre_lower; + + return string; +} + +LOCAL(void) +state_fini(SRE_STATE* state) +{ + Py_XDECREF(state->string); + mark_fini(state); +} + +/* calculate offset from start of string */ +#define STATE_OFFSET(state, member)\ + (((char*)(member) - (char*)(state)->beginning) / (state)->charsize) + +LOCAL(PyObject*) +state_getslice(SRE_STATE* state, int index, PyObject* string, int empty) +{ + int i, j; + + index = (index - 1) * 2; + + if (string == Py_None || !state->mark[index] || !state->mark[index+1]) { + if (empty) + /* want empty string */ + i = j = 0; + else { + Py_INCREF(Py_None); + return Py_None; + } + } else { + i = STATE_OFFSET(state, state->mark[index]); + j = STATE_OFFSET(state, state->mark[index+1]); + } + + return PySequence_GetSlice(string, i, j); +} + +static void +pattern_error(int status) +{ + switch (status) { + case SRE_ERROR_RECURSION_LIMIT: + PyErr_SetString( + PyExc_RuntimeError, + "maximum recursion limit exceeded" + ); + break; + case SRE_ERROR_MEMORY: + PyErr_NoMemory(); + break; + default: + /* other error codes indicate compiler/engine bugs */ + PyErr_SetString( + PyExc_RuntimeError, + "internal error in regular expression engine" + ); + } +} + +static PyObject* +pattern_new_match(PatternObject* pattern, SRE_STATE* state, int status) +{ + /* create match object (from state object) */ + + MatchObject* match; + int i, j; + char* base; + int n; + + if (status > 0) { + + /* create match object (with room for extra group marks) */ + match = PyObject_NEW_VAR(MatchObject, &Match_Type, + 2*(pattern->groups+1)); + if (!match) + return NULL; + + Py_INCREF(pattern); + match->pattern = pattern; + + Py_INCREF(state->string); + match->string = state->string; + + match->regs = NULL; + match->groups = pattern->groups+1; + + /* fill in group slices */ + + base = (char*) state->beginning; + n = state->charsize; + + match->mark[0] = ((char*) state->start - base) / n; + match->mark[1] = ((char*) state->ptr - base) / n; + + for (i = j = 0; i < pattern->groups; i++, j+=2) + if (j+1 <= state->lastmark && state->mark[j] && state->mark[j+1]) { + match->mark[j+2] = ((char*) state->mark[j] - base) / n; + match->mark[j+3] = ((char*) state->mark[j+1] - base) / n; + } else + match->mark[j+2] = match->mark[j+3] = -1; /* undefined */ + + match->pos = state->pos; + match->endpos = state->endpos; + + match->lastindex = state->lastindex; + + return (PyObject*) match; + + } else if (status == 0) { + + /* no match */ + Py_INCREF(Py_None); + return Py_None; + + } + + /* internal error */ + pattern_error(status); + return NULL; +} + +static PyObject* +pattern_scanner(PatternObject* pattern, PyObject* args) +{ + /* create search state object */ + + ScannerObject* self; + + PyObject* string; + int start = 0; + int end = INT_MAX; + if (!PyArg_ParseTuple(args, "O|ii:scanner", &string, &start, &end)) + return NULL; + + /* create scanner object */ + self = PyObject_NEW(ScannerObject, &Scanner_Type); + if (!self) + return NULL; + + string = state_init(&self->state, pattern, string, start, end); + if (!string) { + PyObject_DEL(self); + return NULL; + } + + Py_INCREF(pattern); + self->pattern = (PyObject*) pattern; + + return (PyObject*) self; +} + +static void +pattern_dealloc(PatternObject* self) +{ + Py_XDECREF(self->pattern); + Py_XDECREF(self->groupindex); + Py_XDECREF(self->indexgroup); + PyObject_DEL(self); +} + +static PyObject* +pattern_match(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + int status; + + PyObject* string; + int start = 0; + int end = INT_MAX; + static char* kwlist[] = { "pattern", "pos", "endpos", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:match", kwlist, + &string, &start, &end)) + return NULL; + + string = state_init(&state, self, string, start, end); + if (!string) + return NULL; + + state.ptr = state.start; + + TRACE(("|%p|%p|MATCH\n", PatternObject_GetCode(self), state.ptr)); + + if (state.charsize == 1) { + status = sre_match(&state, PatternObject_GetCode(self), 1); + } else { +#if defined(HAVE_UNICODE) + status = sre_umatch(&state, PatternObject_GetCode(self), 1); +#endif + } + + TRACE(("|%p|%p|END\n", PatternObject_GetCode(self), state.ptr)); + + state_fini(&state); + + return pattern_new_match(self, &state, status); +} + +static PyObject* +pattern_search(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + int status; + + PyObject* string; + int start = 0; + int end = INT_MAX; + static char* kwlist[] = { "pattern", "pos", "endpos", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:search", kwlist, + &string, &start, &end)) + return NULL; + + string = state_init(&state, self, string, start, end); + if (!string) + return NULL; + + TRACE(("|%p|%p|SEARCH\n", PatternObject_GetCode(self), state.ptr)); + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + TRACE(("|%p|%p|END\n", PatternObject_GetCode(self), state.ptr)); + + state_fini(&state); + + return pattern_new_match(self, &state, status); +} + +static PyObject* +call(char* module, char* function, PyObject* args) +{ + PyObject* name; + PyObject* mod; + PyObject* func; + PyObject* result; + + if (!args) + return NULL; + name = PyString_FromString(module); + if (!name) + return NULL; + mod = PyImport_Import(name); + Py_DECREF(name); + if (!mod) + return NULL; + func = PyObject_GetAttrString(mod, function); + Py_DECREF(mod); + if (!func) + return NULL; + result = PyObject_CallObject(func, args); + Py_DECREF(func); + Py_DECREF(args); + return result; +} + +#ifdef USE_BUILTIN_COPY +static int +deepcopy(PyObject** object, PyObject* memo) +{ + PyObject* copy; + + copy = call( + "copy", "deepcopy", + Py_BuildValue("OO", *object, memo) + ); + if (!copy) + return 0; + + Py_DECREF(*object); + *object = copy; + + return 1; /* success */ +} +#endif + +static PyObject* +join_list(PyObject* list, PyObject* pattern) +{ + /* join list elements */ + + PyObject* joiner; +#if PY_VERSION_HEX >= 0x01060000 + PyObject* function; + PyObject* args; +#endif + PyObject* result; + + switch (PyList_GET_SIZE(list)) { + case 0: + Py_DECREF(list); + return PySequence_GetSlice(pattern, 0, 0); + case 1: + result = PyList_GET_ITEM(list, 0); + Py_INCREF(result); + Py_DECREF(list); + return result; + } + + /* two or more elements: slice out a suitable separator from the + first member, and use that to join the entire list */ + + joiner = PySequence_GetSlice(pattern, 0, 0); + if (!joiner) + return NULL; + +#if PY_VERSION_HEX >= 0x01060000 + function = PyObject_GetAttrString(joiner, "join"); + if (!function) { + Py_DECREF(joiner); + return NULL; + } + args = PyTuple_New(1); + if (!args) { + Py_DECREF(function); + Py_DECREF(joiner); + return NULL; + } + PyTuple_SET_ITEM(args, 0, list); + result = PyObject_CallObject(function, args); + Py_DECREF(args); /* also removes list */ + Py_DECREF(function); +#else + result = call( + "string", "join", + Py_BuildValue("OO", list, joiner) + ); +#endif + Py_DECREF(joiner); + + return result; +} + +static PyObject* +pattern_findall(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + PyObject* list; + int status; + int i, b, e; + + PyObject* string; + int start = 0; + int end = INT_MAX; + static char* kwlist[] = { "source", "pos", "endpos", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:findall", kwlist, + &string, &start, &end)) + return NULL; + + string = state_init(&state, self, string, start, end); + if (!string) + return NULL; + + list = PyList_New(0); + if (!list) { + state_fini(&state); + return NULL; + } + + while (state.start <= state.end) { + + PyObject* item; + + state_reset(&state); + + state.ptr = state.start; + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + if (status <= 0) { + if (status == 0) + break; + pattern_error(status); + goto error; + } + + /* don't bother to build a match object */ + switch (self->groups) { + case 0: + b = STATE_OFFSET(&state, state.start); + e = STATE_OFFSET(&state, state.ptr); + item = PySequence_GetSlice(string, b, e); + if (!item) + goto error; + break; + case 1: + item = state_getslice(&state, 1, string, 1); + if (!item) + goto error; + break; + default: + item = PyTuple_New(self->groups); + if (!item) + goto error; + for (i = 0; i < self->groups; i++) { + PyObject* o = state_getslice(&state, i+1, string, 1); + if (!o) { + Py_DECREF(item); + goto error; + } + PyTuple_SET_ITEM(item, i, o); + } + break; + } + + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + if (state.ptr == state.start) + state.start = (void*) ((char*) state.ptr + state.charsize); + else + state.start = state.ptr; + + } + + state_fini(&state); + return list; + +error: + Py_DECREF(list); + state_fini(&state); + return NULL; + +} + +#if PY_VERSION_HEX >= 0x02020000 +static PyObject* +pattern_finditer(PatternObject* pattern, PyObject* args) +{ + PyObject* scanner; + PyObject* search; + PyObject* iterator; + + scanner = pattern_scanner(pattern, args); + if (!scanner) + return NULL; + + search = PyObject_GetAttrString(scanner, "search"); + Py_DECREF(scanner); + if (!search) + return NULL; + + iterator = PyCallIter_New(search, Py_None); + Py_DECREF(search); + + return iterator; +} +#endif + +static PyObject* +pattern_split(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + PyObject* list; + PyObject* item; + int status; + int n; + int i; + void* last; + + PyObject* string; + int maxsplit = 0; + static char* kwlist[] = { "source", "maxsplit", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|i:split", kwlist, + &string, &maxsplit)) + return NULL; + + string = state_init(&state, self, string, 0, INT_MAX); + if (!string) + return NULL; + + list = PyList_New(0); + if (!list) { + state_fini(&state); + return NULL; + } + + n = 0; + last = state.start; + + while (!maxsplit || n < maxsplit) { + + state_reset(&state); + + state.ptr = state.start; + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + if (status <= 0) { + if (status == 0) + break; + pattern_error(status); + goto error; + } + + if (state.start == state.ptr) { + if (last == state.end) + break; + /* skip one character */ + state.start = (void*) ((char*) state.ptr + state.charsize); + continue; + } + + /* get segment before this match */ + item = PySequence_GetSlice( + string, STATE_OFFSET(&state, last), + STATE_OFFSET(&state, state.start) + ); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + /* add groups (if any) */ + for (i = 0; i < self->groups; i++) { + item = state_getslice(&state, i+1, string, 0); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + n = n + 1; + + last = state.start = state.ptr; + + } + + /* get segment following last match (even if empty) */ + item = PySequence_GetSlice( + string, STATE_OFFSET(&state, last), state.endpos + ); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + state_fini(&state); + return list; + +error: + Py_DECREF(list); + state_fini(&state); + return NULL; + +} + +static PyObject* +pattern_subx(PatternObject* self, PyObject* template, PyObject* string, + int count, int subn) +{ + SRE_STATE state; + PyObject* list; + PyObject* item; + PyObject* filter; + PyObject* args; + PyObject* match; + void* ptr; + int status; + int n; + int i, b, e; + int filter_is_callable; + + if (PyCallable_Check(template)) { + /* sub/subn takes either a function or a template */ + filter = template; + Py_INCREF(filter); + filter_is_callable = 1; + } else { + /* if not callable, check if it's a literal string */ + int literal; + ptr = getstring(template, &n, &b); + if (ptr) { + if (b == 1) { + literal = sre_literal_template(ptr, n); + } else { +#if defined(HAVE_UNICODE) + literal = sre_uliteral_template(ptr, n); +#endif + } + } else { + PyErr_Clear(); + literal = 0; + } + if (literal) { + filter = template; + Py_INCREF(filter); + filter_is_callable = 0; + } else { + /* not a literal; hand it over to the template compiler */ + filter = call( + SRE_MODULE, "_subx", + Py_BuildValue("OO", self, template) + ); + if (!filter) + return NULL; + filter_is_callable = PyCallable_Check(filter); + } + } + + string = state_init(&state, self, string, 0, INT_MAX); + if (!string) { + Py_DECREF(filter); + return NULL; + } + + list = PyList_New(0); + if (!list) { + Py_DECREF(filter); + state_fini(&state); + return NULL; + } + + n = i = 0; + + while (!count || n < count) { + + state_reset(&state); + + state.ptr = state.start; + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + if (status <= 0) { + if (status == 0) + break; + pattern_error(status); + goto error; + } + + b = STATE_OFFSET(&state, state.start); + e = STATE_OFFSET(&state, state.ptr); + + if (i < b) { + /* get segment before this match */ + item = PySequence_GetSlice(string, i, b); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + } else if (i == b && i == e && n > 0) + /* ignore empty match on latest position */ + goto next; + + if (filter_is_callable) { + /* pass match object through filter */ + match = pattern_new_match(self, &state, 1); + if (!match) + goto error; + args = Py_BuildValue("(O)", match); + if (!args) { + Py_DECREF(match); + goto error; + } + item = PyObject_CallObject(filter, args); + Py_DECREF(args); + Py_DECREF(match); + if (!item) + goto error; + } else { + /* filter is literal string */ + item = filter; + Py_INCREF(item); + } + + /* add to list */ + if (item != Py_None) { + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + i = e; + n = n + 1; + +next: + /* move on */ + if (state.ptr == state.start) + state.start = (void*) ((char*) state.ptr + state.charsize); + else + state.start = state.ptr; + + } + + /* get segment following last match */ + if (i < state.endpos) { + item = PySequence_GetSlice(string, i, state.endpos); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + state_fini(&state); + + Py_DECREF(filter); + + /* convert list to single string (also removes list) */ + item = join_list(list, self->pattern); + + if (!item) + return NULL; + + if (subn) + return Py_BuildValue("Ni", item, n); + + return item; + +error: + Py_DECREF(list); + state_fini(&state); + Py_DECREF(filter); + return NULL; + +} + +static PyObject* +pattern_sub(PatternObject* self, PyObject* args, PyObject* kw) +{ + PyObject* template; + PyObject* string; + int count = 0; + static char* kwlist[] = { "repl", "string", "count", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|i:sub", kwlist, + &template, &string, &count)) + return NULL; + + return pattern_subx(self, template, string, count, 0); +} + +static PyObject* +pattern_subn(PatternObject* self, PyObject* args, PyObject* kw) +{ + PyObject* template; + PyObject* string; + int count = 0; + static char* kwlist[] = { "repl", "string", "count", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|i:subn", kwlist, + &template, &string, &count)) + return NULL; + + return pattern_subx(self, template, string, count, 1); +} + +static PyObject* +pattern_copy(PatternObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + PatternObject* copy; + int offset; + + if (args != Py_None && !PyArg_ParseTuple(args, ":__copy__")) + return NULL; + + copy = PyObject_NEW_VAR(PatternObject, &Pattern_Type, self->codesize); + if (!copy) + return NULL; + + offset = offsetof(PatternObject, groups); + + Py_XINCREF(self->groupindex); + Py_XINCREF(self->indexgroup); + Py_XINCREF(self->pattern); + + memcpy((char*) copy + offset, (char*) self + offset, + sizeof(PatternObject) + self->codesize * sizeof(SRE_CODE) - offset); + + return (PyObject*) copy; +#else + PyErr_SetString(PyExc_TypeError, "cannot copy this pattern object"); + return NULL; +#endif +} + +static PyObject* +pattern_deepcopy(PatternObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + PatternObject* copy; + + PyObject* memo; + if (!PyArg_ParseTuple(args, "O:__deepcopy__", &memo)) + return NULL; + + copy = (PatternObject*) pattern_copy(self, Py_None); + if (!copy) + return NULL; + + if (!deepcopy(©->groupindex, memo) || + !deepcopy(©->indexgroup, memo) || + !deepcopy(©->pattern, memo)) { + Py_DECREF(copy); + return NULL; + } + +#else + PyErr_SetString(PyExc_TypeError, "cannot deepcopy this pattern object"); + return NULL; +#endif +} + +static PyMethodDef pattern_methods[] = { + {"match", (PyCFunction) pattern_match, METH_VARARGS|METH_KEYWORDS}, + {"search", (PyCFunction) pattern_search, METH_VARARGS|METH_KEYWORDS}, + {"sub", (PyCFunction) pattern_sub, METH_VARARGS|METH_KEYWORDS}, + {"subn", (PyCFunction) pattern_subn, METH_VARARGS|METH_KEYWORDS}, + {"split", (PyCFunction) pattern_split, METH_VARARGS|METH_KEYWORDS}, + {"findall", (PyCFunction) pattern_findall, METH_VARARGS|METH_KEYWORDS}, +#if PY_VERSION_HEX >= 0x02020000 + {"finditer", (PyCFunction) pattern_finditer, METH_VARARGS}, +#endif + {"scanner", (PyCFunction) pattern_scanner, METH_VARARGS}, + {"__copy__", (PyCFunction) pattern_copy, METH_VARARGS}, + {"__deepcopy__", (PyCFunction) pattern_deepcopy, METH_VARARGS}, + {NULL, NULL} +}; + +static PyObject* +pattern_getattr(PatternObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(pattern_methods, (PyObject*) self, name); + + if (res) + return res; + + PyErr_Clear(); + + /* attributes */ + if (!strcmp(name, "pattern")) { + Py_INCREF(self->pattern); + return self->pattern; + } + + if (!strcmp(name, "flags")) + return Py_BuildValue("i", self->flags); + + if (!strcmp(name, "groups")) + return Py_BuildValue("i", self->groups); + + if (!strcmp(name, "groupindex") && self->groupindex) { + Py_INCREF(self->groupindex); + return self->groupindex; + } + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +statichere PyTypeObject Pattern_Type = { + PyObject_HEAD_INIT(NULL) + 0, "_" SRE_MODULE ".SRE_Pattern", + sizeof(PatternObject), sizeof(SRE_CODE), + (destructor)pattern_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)pattern_getattr /*tp_getattr*/ +}; + +/* -------------------------------------------------------------------- */ +/* match methods */ + +static void +match_dealloc(MatchObject* self) +{ + Py_XDECREF(self->regs); + Py_XDECREF(self->string); + Py_DECREF(self->pattern); + PyObject_DEL(self); +} + +static PyObject* +match_getslice_by_index(MatchObject* self, int index, PyObject* def) +{ + if (index < 0 || index >= self->groups) { + /* raise IndexError if we were given a bad group number */ + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + index *= 2; + + if (self->string == Py_None || self->mark[index] < 0) { + /* return default value if the string or group is undefined */ + Py_INCREF(def); + return def; + } + + return PySequence_GetSlice( + self->string, self->mark[index], self->mark[index+1] + ); +} + +static int +match_getindex(MatchObject* self, PyObject* index) +{ + int i; + + if (PyInt_Check(index)) + return (int) PyInt_AS_LONG(index); + + i = -1; + + if (self->pattern->groupindex) { + index = PyObject_GetItem(self->pattern->groupindex, index); + if (index) { + if (PyInt_Check(index)) + i = (int) PyInt_AS_LONG(index); + Py_DECREF(index); + } else + PyErr_Clear(); + } + + return i; +} + +static PyObject* +match_getslice(MatchObject* self, PyObject* index, PyObject* def) +{ + return match_getslice_by_index(self, match_getindex(self, index), def); +} + +static PyObject* +match_expand(MatchObject* self, PyObject* args) +{ + PyObject* template; + if (!PyArg_ParseTuple(args, "O:expand", &template)) + return NULL; + + /* delegate to Python code */ + return call( + SRE_MODULE, "_expand", + Py_BuildValue("OOO", self->pattern, self, template) + ); +} + +static PyObject* +match_group(MatchObject* self, PyObject* args) +{ + PyObject* result; + int i, size; + + size = PyTuple_GET_SIZE(args); + + switch (size) { + case 0: + result = match_getslice(self, Py_False, Py_None); + break; + case 1: + result = match_getslice(self, PyTuple_GET_ITEM(args, 0), Py_None); + break; + default: + /* fetch multiple items */ + result = PyTuple_New(size); + if (!result) + return NULL; + for (i = 0; i < size; i++) { + PyObject* item = match_getslice( + self, PyTuple_GET_ITEM(args, i), Py_None + ); + if (!item) { + Py_DECREF(result); + return NULL; + } + PyTuple_SET_ITEM(result, i, item); + } + break; + } + return result; +} + +static PyObject* +match_groups(MatchObject* self, PyObject* args, PyObject* kw) +{ + PyObject* result; + int index; + + PyObject* def = Py_None; + static char* kwlist[] = { "default", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:groups", kwlist, &def)) + return NULL; + + result = PyTuple_New(self->groups-1); + if (!result) + return NULL; + + for (index = 1; index < self->groups; index++) { + PyObject* item; + item = match_getslice_by_index(self, index, def); + if (!item) { + Py_DECREF(result); + return NULL; + } + PyTuple_SET_ITEM(result, index-1, item); + } + + return result; +} + +static PyObject* +match_groupdict(MatchObject* self, PyObject* args, PyObject* kw) +{ + PyObject* result; + PyObject* keys; + int index; + + PyObject* def = Py_None; + static char* kwlist[] = { "default", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:groupdict", kwlist, &def)) + return NULL; + + result = PyDict_New(); + if (!result || !self->pattern->groupindex) + return result; + + keys = PyMapping_Keys(self->pattern->groupindex); + if (!keys) + goto failed; + + for (index = 0; index < PyList_GET_SIZE(keys); index++) { + int status; + PyObject* key; + PyObject* value; + key = PyList_GET_ITEM(keys, index); + if (!key) + goto failed; + value = match_getslice(self, key, def); + if (!value) { + Py_DECREF(key); + goto failed; + } + status = PyDict_SetItem(result, key, value); + Py_DECREF(value); + if (status < 0) + goto failed; + } + + Py_DECREF(keys); + + return result; + +failed: + Py_DECREF(keys); + Py_DECREF(result); + return NULL; +} + +static PyObject* +match_start(MatchObject* self, PyObject* args) +{ + int index; + + PyObject* index_ = Py_False; /* zero */ + if (!PyArg_ParseTuple(args, "|O:start", &index_)) + return NULL; + + index = match_getindex(self, index_); + + if (index < 0 || index >= self->groups) { + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + /* mark is -1 if group is undefined */ + return Py_BuildValue("i", self->mark[index*2]); +} + +static PyObject* +match_end(MatchObject* self, PyObject* args) +{ + int index; + + PyObject* index_ = Py_False; /* zero */ + if (!PyArg_ParseTuple(args, "|O:end", &index_)) + return NULL; + + index = match_getindex(self, index_); + + if (index < 0 || index >= self->groups) { + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + /* mark is -1 if group is undefined */ + return Py_BuildValue("i", self->mark[index*2+1]); +} + +LOCAL(PyObject*) +_pair(int i1, int i2) +{ + PyObject* pair; + PyObject* item; + + pair = PyTuple_New(2); + if (!pair) + return NULL; + + item = PyInt_FromLong(i1); + if (!item) + goto error; + PyTuple_SET_ITEM(pair, 0, item); + + item = PyInt_FromLong(i2); + if (!item) + goto error; + PyTuple_SET_ITEM(pair, 1, item); + + return pair; + + error: + Py_DECREF(pair); + return NULL; +} + +static PyObject* +match_span(MatchObject* self, PyObject* args) +{ + int index; + + PyObject* index_ = Py_False; /* zero */ + if (!PyArg_ParseTuple(args, "|O:span", &index_)) + return NULL; + + index = match_getindex(self, index_); + + if (index < 0 || index >= self->groups) { + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + /* marks are -1 if group is undefined */ + return _pair(self->mark[index*2], self->mark[index*2+1]); +} + +static PyObject* +match_regs(MatchObject* self) +{ + PyObject* regs; + PyObject* item; + int index; + + regs = PyTuple_New(self->groups); + if (!regs) + return NULL; + + for (index = 0; index < self->groups; index++) { + item = _pair(self->mark[index*2], self->mark[index*2+1]); + if (!item) { + Py_DECREF(regs); + return NULL; + } + PyTuple_SET_ITEM(regs, index, item); + } + + Py_INCREF(regs); + self->regs = regs; + + return regs; +} + +static PyObject* +match_copy(MatchObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + MatchObject* copy; + int slots, offset; + + if (args != Py_None && !PyArg_ParseTuple(args, ":__copy__")) + return NULL; + + slots = 2 * (self->pattern->groups+1); + + copy = PyObject_NEW_VAR(MatchObject, &Match_Type, slots); + if (!copy) + return NULL; + + /* this value a constant, but any compiler should be able to + figure that out all by itself */ + offset = offsetof(MatchObject, string); + + Py_XINCREF(self->pattern); + Py_XINCREF(self->string); + Py_XINCREF(self->regs); + + memcpy((char*) copy + offset, (char*) self + offset, + sizeof(MatchObject) + slots * sizeof(int) - offset); + + return (PyObject*) copy; +#else + PyErr_SetString(PyExc_TypeError, "cannot copy this match object"); + return NULL; +#endif +} + +static PyObject* +match_deepcopy(MatchObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + MatchObject* copy; + + PyObject* memo; + if (!PyArg_ParseTuple(args, "O:__deepcopy__", &memo)) + return NULL; + + copy = (MatchObject*) match_copy(self, Py_None); + if (!copy) + return NULL; + + if (!deepcopy((PyObject**) ©->pattern, memo) || + !deepcopy(©->string, memo) || + !deepcopy(©->regs, memo)) { + Py_DECREF(copy); + return NULL; + } + +#else + PyErr_SetString(PyExc_TypeError, "cannot deepcopy this match object"); + return NULL; +#endif +} + +static PyMethodDef match_methods[] = { + {"group", (PyCFunction) match_group, METH_VARARGS}, + {"start", (PyCFunction) match_start, METH_VARARGS}, + {"end", (PyCFunction) match_end, METH_VARARGS}, + {"span", (PyCFunction) match_span, METH_VARARGS}, + {"groups", (PyCFunction) match_groups, METH_VARARGS|METH_KEYWORDS}, + {"groupdict", (PyCFunction) match_groupdict, METH_VARARGS|METH_KEYWORDS}, + {"expand", (PyCFunction) match_expand, METH_VARARGS}, + {"__copy__", (PyCFunction) match_copy, METH_VARARGS}, + {"__deepcopy__", (PyCFunction) match_deepcopy, METH_VARARGS}, + {NULL, NULL} +}; + +static PyObject* +match_getattr(MatchObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(match_methods, (PyObject*) self, name); + if (res) + return res; + + PyErr_Clear(); + + if (!strcmp(name, "lastindex")) { + if (self->lastindex >= 0) + return Py_BuildValue("i", self->lastindex); + Py_INCREF(Py_None); + return Py_None; + } + + if (!strcmp(name, "lastgroup")) { + if (self->pattern->indexgroup && self->lastindex >= 0) { + PyObject* result = PySequence_GetItem( + self->pattern->indexgroup, self->lastindex + ); + if (result) + return result; + PyErr_Clear(); + } + Py_INCREF(Py_None); + return Py_None; + } + + if (!strcmp(name, "string")) { + if (self->string) { + Py_INCREF(self->string); + return self->string; + } else { + Py_INCREF(Py_None); + return Py_None; + } + } + + if (!strcmp(name, "regs")) { + if (self->regs) { + Py_INCREF(self->regs); + return self->regs; + } else + return match_regs(self); + } + + if (!strcmp(name, "re")) { + Py_INCREF(self->pattern); + return (PyObject*) self->pattern; + } + + if (!strcmp(name, "pos")) + return Py_BuildValue("i", self->pos); + + if (!strcmp(name, "endpos")) + return Py_BuildValue("i", self->endpos); + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +/* FIXME: implement setattr("string", None) as a special case (to + detach the associated string, if any */ + +statichere PyTypeObject Match_Type = { + PyObject_HEAD_INIT(NULL) + 0, "_" SRE_MODULE ".SRE_Match", + sizeof(MatchObject), sizeof(int), + (destructor)match_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)match_getattr /*tp_getattr*/ +}; + +/* -------------------------------------------------------------------- */ +/* scanner methods (experimental) */ + +static void +scanner_dealloc(ScannerObject* self) +{ + state_fini(&self->state); + Py_DECREF(self->pattern); + PyObject_DEL(self); +} + +static PyObject* +scanner_match(ScannerObject* self, PyObject* args) +{ + SRE_STATE* state = &self->state; + PyObject* match; + int status; + + state_reset(state); + + state->ptr = state->start; + + if (state->charsize == 1) { + status = sre_match(state, PatternObject_GetCode(self->pattern), 1); + } else { +#if defined(HAVE_UNICODE) + status = sre_umatch(state, PatternObject_GetCode(self->pattern), 1); +#endif + } + + match = pattern_new_match((PatternObject*) self->pattern, + state, status); + + if ((status == 0 || state->ptr == state->start) && + state->ptr < state->end) + state->start = (void*) ((char*) state->ptr + state->charsize); + else + state->start = state->ptr; + + return match; +} + + +static PyObject* +scanner_search(ScannerObject* self, PyObject* args) +{ + SRE_STATE* state = &self->state; + PyObject* match; + int status; + + state_reset(state); + + state->ptr = state->start; + + if (state->charsize == 1) { + status = sre_search(state, PatternObject_GetCode(self->pattern)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(state, PatternObject_GetCode(self->pattern)); +#endif + } + + match = pattern_new_match((PatternObject*) self->pattern, + state, status); + + if ((status == 0 || state->ptr == state->start) && + state->ptr < state->end) + state->start = (void*) ((char*) state->ptr + state->charsize); + else + state->start = state->ptr; + + return match; +} + +static PyMethodDef scanner_methods[] = { + /* FIXME: use METH_OLDARGS instead of 0 or fix to use METH_VARARGS */ + /* METH_OLDARGS is not in Python 1.5.2 */ + {"match", (PyCFunction) scanner_match, 0}, + {"search", (PyCFunction) scanner_search, 0}, + {NULL, NULL} +}; + +static PyObject* +scanner_getattr(ScannerObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(scanner_methods, (PyObject*) self, name); + if (res) + return res; + + PyErr_Clear(); + + /* attributes */ + if (!strcmp(name, "pattern")) { + Py_INCREF(self->pattern); + return self->pattern; + } + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +statichere PyTypeObject Scanner_Type = { + PyObject_HEAD_INIT(NULL) + 0, "_" SRE_MODULE ".SRE_Scanner", + sizeof(ScannerObject), 0, + (destructor)scanner_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)scanner_getattr, /*tp_getattr*/ +}; + +static PyMethodDef _functions[] = { + {"compile", _compile, METH_VARARGS}, + {"getcodesize", sre_codesize, METH_VARARGS}, + {"getlower", sre_getlower, METH_VARARGS}, + {NULL, NULL} +}; + +PyMODINIT_FUNC init_sre(void) +{ + PyObject* m; + PyObject* d; + PyObject* x; + + /* Patch object types */ + Pattern_Type.ob_type = Match_Type.ob_type = + Scanner_Type.ob_type = &PyType_Type; + + m = Py_InitModule("_" SRE_MODULE, _functions); + d = PyModule_GetDict(m); + + x = PyInt_FromLong(SRE_MAGIC); + if (x) { + PyDict_SetItemString(d, "MAGIC", x); + Py_DECREF(x); + } + + x = PyString_FromString(copyright); + if (x) { + PyDict_SetItemString(d, "copyright", x); + Py_DECREF(x); + } +} + +#endif /* !defined(SRE_RECURSIVE) */ diff --git a/src/main/resource/testFiles/cpython/revFiles/0aed3a4_404cdc_Objects#obmalloc.c b/src/main/resource/testFiles/cpython/revFiles/0aed3a4_404cdc_Objects#obmalloc.c new file mode 100644 index 0000000..40c9fcd --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/0aed3a4_404cdc_Objects#obmalloc.c @@ -0,0 +1,2449 @@ +#include "Python.h" + + +/* Defined in tracemalloc.c */ +extern void _PyMem_DumpTraceback(int fd, const void *ptr); + + +/* Python's malloc wrappers (see pymem.h) */ + +/* + * Basic types + * I don't care if these are defined in or elsewhere. Axiom. + */ +#undef uchar +#define uchar unsigned char /* assuming == 8 bits */ + +#undef uint +#define uint unsigned int /* assuming >= 16 bits */ + +#undef uptr +#define uptr Py_uintptr_t + +/* Forward declaration */ +static void* _PyMem_DebugRawMalloc(void *ctx, size_t size); +static void* _PyMem_DebugRawCalloc(void *ctx, size_t nelem, size_t elsize); +static void* _PyMem_DebugRawRealloc(void *ctx, void *ptr, size_t size); +static void _PyMem_DebugRawFree(void *ctx, void *p); + +static void* _PyMem_DebugMalloc(void *ctx, size_t size); +static void* _PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize); +static void* _PyMem_DebugRealloc(void *ctx, void *ptr, size_t size); +static void _PyMem_DebugFree(void *ctx, void *p); + +static void _PyObject_DebugDumpAddress(const void *p); +static void _PyMem_DebugCheckAddress(char api_id, const void *p); + +#if defined(__has_feature) /* Clang */ + #if __has_feature(address_sanitizer) /* is ASAN enabled? */ + #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \ + __attribute__((no_address_safety_analysis)) \ + __attribute__ ((noinline)) + #else + #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS + #endif +#else + #if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */ + #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \ + __attribute__((no_address_safety_analysis)) \ + __attribute__ ((noinline)) + #else + #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS + #endif +#endif + +#ifdef WITH_PYMALLOC + +#ifdef MS_WINDOWS +# include +#elif defined(HAVE_MMAP) +# include +# ifdef MAP_ANONYMOUS +# define ARENAS_USE_MMAP +# endif +#endif + +/* Forward declaration */ +static void* _PyObject_Malloc(void *ctx, size_t size); +static void* _PyObject_Calloc(void *ctx, size_t nelem, size_t elsize); +static void _PyObject_Free(void *ctx, void *p); +static void* _PyObject_Realloc(void *ctx, void *ptr, size_t size); +#endif + + +static void * +_PyMem_RawMalloc(void *ctx, size_t size) +{ + /* PyMem_RawMalloc(0) means malloc(1). Some systems would return NULL + for malloc(0), which would be treated as an error. Some platforms would + return a pointer with no memory behind it, which would break pymalloc. + To solve these problems, allocate an extra byte. */ + if (size == 0) + size = 1; + return malloc(size); +} + +static void * +_PyMem_RawCalloc(void *ctx, size_t nelem, size_t elsize) +{ + /* PyMem_RawCalloc(0, 0) means calloc(1, 1). Some systems would return NULL + for calloc(0, 0), which would be treated as an error. Some platforms + would return a pointer with no memory behind it, which would break + pymalloc. To solve these problems, allocate an extra byte. */ + if (nelem == 0 || elsize == 0) { + nelem = 1; + elsize = 1; + } + return calloc(nelem, elsize); +} + +static void * +_PyMem_RawRealloc(void *ctx, void *ptr, size_t size) +{ + if (size == 0) + size = 1; + return realloc(ptr, size); +} + +static void +_PyMem_RawFree(void *ctx, void *ptr) +{ + free(ptr); +} + + +#ifdef MS_WINDOWS +static void * +_PyObject_ArenaVirtualAlloc(void *ctx, size_t size) +{ + return VirtualAlloc(NULL, size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +} + +static void +_PyObject_ArenaVirtualFree(void *ctx, void *ptr, size_t size) +{ + VirtualFree(ptr, 0, MEM_RELEASE); +} + +#elif defined(ARENAS_USE_MMAP) +static void * +_PyObject_ArenaMmap(void *ctx, size_t size) +{ + void *ptr; + ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (ptr == MAP_FAILED) + return NULL; + assert(ptr != NULL); + return ptr; +} + +static void +_PyObject_ArenaMunmap(void *ctx, void *ptr, size_t size) +{ + munmap(ptr, size); +} + +#else +static void * +_PyObject_ArenaMalloc(void *ctx, size_t size) +{ + return malloc(size); +} + +static void +_PyObject_ArenaFree(void *ctx, void *ptr, size_t size) +{ + free(ptr); +} +#endif + + +#define PYRAW_FUNCS _PyMem_RawMalloc, _PyMem_RawCalloc, _PyMem_RawRealloc, _PyMem_RawFree +#ifdef WITH_PYMALLOC +# define PYOBJ_FUNCS _PyObject_Malloc, _PyObject_Calloc, _PyObject_Realloc, _PyObject_Free +#else +# define PYOBJ_FUNCS PYRAW_FUNCS +#endif +#define PYMEM_FUNCS PYRAW_FUNCS + +typedef struct { + /* We tag each block with an API ID in order to tag API violations */ + char api_id; + PyMemAllocatorEx alloc; +} debug_alloc_api_t; +static struct { + debug_alloc_api_t raw; + debug_alloc_api_t mem; + debug_alloc_api_t obj; +} _PyMem_Debug = { + {'r', {NULL, PYRAW_FUNCS}}, + {'m', {NULL, PYMEM_FUNCS}}, + {'o', {NULL, PYOBJ_FUNCS}} + }; + +#define PYRAWDBG_FUNCS \ + _PyMem_DebugRawMalloc, _PyMem_DebugRawCalloc, _PyMem_DebugRawRealloc, _PyMem_DebugRawFree +#define PYDBG_FUNCS \ + _PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree + +static PyMemAllocatorEx _PyMem_Raw = { +#ifdef Py_DEBUG + &_PyMem_Debug.raw, PYRAWDBG_FUNCS +#else + NULL, PYRAW_FUNCS +#endif + }; + +static PyMemAllocatorEx _PyMem = { +#ifdef Py_DEBUG + &_PyMem_Debug.mem, PYDBG_FUNCS +#else + NULL, PYMEM_FUNCS +#endif + }; + +static PyMemAllocatorEx _PyObject = { +#ifdef Py_DEBUG + &_PyMem_Debug.obj, PYDBG_FUNCS +#else + NULL, PYOBJ_FUNCS +#endif + }; + +int +_PyMem_SetupAllocators(const char *opt) +{ + if (opt == NULL || *opt == '\0') { + /* PYTHONMALLOC is empty or is not set or ignored (-E/-I command line + options): use default allocators */ +#ifdef Py_DEBUG +# ifdef WITH_PYMALLOC + opt = "pymalloc_debug"; +# else + opt = "malloc_debug"; +# endif +#else + /* !Py_DEBUG */ +# ifdef WITH_PYMALLOC + opt = "pymalloc"; +# else + opt = "malloc"; +# endif +#endif + } + + if (strcmp(opt, "debug") == 0) { + PyMem_SetupDebugHooks(); + } + else if (strcmp(opt, "malloc") == 0 || strcmp(opt, "malloc_debug") == 0) + { + PyMemAllocatorEx alloc = {NULL, PYRAW_FUNCS}; + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc); + + if (strcmp(opt, "malloc_debug") == 0) + PyMem_SetupDebugHooks(); + } +#ifdef WITH_PYMALLOC + else if (strcmp(opt, "pymalloc") == 0 + || strcmp(opt, "pymalloc_debug") == 0) + { + PyMemAllocatorEx mem_alloc = {NULL, PYRAW_FUNCS}; + PyMemAllocatorEx obj_alloc = {NULL, PYOBJ_FUNCS}; + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &mem_alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &mem_alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &obj_alloc); + + if (strcmp(opt, "pymalloc_debug") == 0) + PyMem_SetupDebugHooks(); + } +#endif + else { + /* unknown allocator */ + return -1; + } + return 0; +} + +#undef PYRAW_FUNCS +#undef PYMEM_FUNCS +#undef PYOBJ_FUNCS +#undef PYRAWDBG_FUNCS +#undef PYDBG_FUNCS + +static PyObjectArenaAllocator _PyObject_Arena = {NULL, +#ifdef MS_WINDOWS + _PyObject_ArenaVirtualAlloc, _PyObject_ArenaVirtualFree +#elif defined(ARENAS_USE_MMAP) + _PyObject_ArenaMmap, _PyObject_ArenaMunmap +#else + _PyObject_ArenaMalloc, _PyObject_ArenaFree +#endif + }; + +static int +_PyMem_DebugEnabled(void) +{ + return (_PyObject.malloc == _PyMem_DebugMalloc); +} + +#ifdef WITH_PYMALLOC +int +_PyMem_PymallocEnabled(void) +{ + if (_PyMem_DebugEnabled()) { + return (_PyMem_Debug.obj.alloc.malloc == _PyObject_Malloc); + } + else { + return (_PyObject.malloc == _PyObject_Malloc); + } +} +#endif + +void +PyMem_SetupDebugHooks(void) +{ + PyMemAllocatorEx alloc; + + alloc.malloc = _PyMem_DebugRawMalloc; + alloc.calloc = _PyMem_DebugRawCalloc; + alloc.realloc = _PyMem_DebugRawRealloc; + alloc.free = _PyMem_DebugRawFree; + + if (_PyMem_Raw.malloc != _PyMem_DebugRawMalloc) { + alloc.ctx = &_PyMem_Debug.raw; + PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &_PyMem_Debug.raw.alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc); + } + + alloc.malloc = _PyMem_DebugMalloc; + alloc.calloc = _PyMem_DebugCalloc; + alloc.realloc = _PyMem_DebugRealloc; + alloc.free = _PyMem_DebugFree; + + if (_PyMem.malloc != _PyMem_DebugMalloc) { + alloc.ctx = &_PyMem_Debug.mem; + PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &_PyMem_Debug.mem.alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc); + } + + if (_PyObject.malloc != _PyMem_DebugMalloc) { + alloc.ctx = &_PyMem_Debug.obj; + PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &_PyMem_Debug.obj.alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc); + } +} + +void +PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) +{ + switch(domain) + { + case PYMEM_DOMAIN_RAW: *allocator = _PyMem_Raw; break; + case PYMEM_DOMAIN_MEM: *allocator = _PyMem; break; + case PYMEM_DOMAIN_OBJ: *allocator = _PyObject; break; + default: + /* unknown domain: set all attributes to NULL */ + allocator->ctx = NULL; + allocator->malloc = NULL; + allocator->calloc = NULL; + allocator->realloc = NULL; + allocator->free = NULL; + } +} + +void +PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) +{ + switch(domain) + { + case PYMEM_DOMAIN_RAW: _PyMem_Raw = *allocator; break; + case PYMEM_DOMAIN_MEM: _PyMem = *allocator; break; + case PYMEM_DOMAIN_OBJ: _PyObject = *allocator; break; + /* ignore unknown domain */ + } +} + +void +PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator) +{ + *allocator = _PyObject_Arena; +} + +void +PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator) +{ + _PyObject_Arena = *allocator; +} + +void * +PyMem_RawMalloc(size_t size) +{ + /* + * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for size < 0 is not required. + */ + if (size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyMem_Raw.malloc(_PyMem_Raw.ctx, size); +} + +void * +PyMem_RawCalloc(size_t nelem, size_t elsize) +{ + /* see PyMem_RawMalloc() */ + if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize) + return NULL; + return _PyMem_Raw.calloc(_PyMem_Raw.ctx, nelem, elsize); +} + +void* +PyMem_RawRealloc(void *ptr, size_t new_size) +{ + /* see PyMem_RawMalloc() */ + if (new_size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyMem_Raw.realloc(_PyMem_Raw.ctx, ptr, new_size); +} + +void PyMem_RawFree(void *ptr) +{ + _PyMem_Raw.free(_PyMem_Raw.ctx, ptr); +} + +void * +PyMem_Malloc(size_t size) +{ + /* see PyMem_RawMalloc() */ + if (size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyMem.malloc(_PyMem.ctx, size); +} + +void * +PyMem_Calloc(size_t nelem, size_t elsize) +{ + /* see PyMem_RawMalloc() */ + if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize) + return NULL; + return _PyMem.calloc(_PyMem.ctx, nelem, elsize); +} + +void * +PyMem_Realloc(void *ptr, size_t new_size) +{ + /* see PyMem_RawMalloc() */ + if (new_size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyMem.realloc(_PyMem.ctx, ptr, new_size); +} + +void +PyMem_Free(void *ptr) +{ + _PyMem.free(_PyMem.ctx, ptr); +} + +char * +_PyMem_RawStrdup(const char *str) +{ + size_t size; + char *copy; + + size = strlen(str) + 1; + copy = PyMem_RawMalloc(size); + if (copy == NULL) + return NULL; + memcpy(copy, str, size); + return copy; +} + +char * +_PyMem_Strdup(const char *str) +{ + size_t size; + char *copy; + + size = strlen(str) + 1; + copy = PyMem_Malloc(size); + if (copy == NULL) + return NULL; + memcpy(copy, str, size); + return copy; +} + +void * +PyObject_Malloc(size_t size) +{ + /* see PyMem_RawMalloc() */ + if (size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyObject.malloc(_PyObject.ctx, size); +} + +void * +PyObject_Calloc(size_t nelem, size_t elsize) +{ + /* see PyMem_RawMalloc() */ + if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize) + return NULL; + return _PyObject.calloc(_PyObject.ctx, nelem, elsize); +} + +void * +PyObject_Realloc(void *ptr, size_t new_size) +{ + /* see PyMem_RawMalloc() */ + if (new_size > (size_t)PY_SSIZE_T_MAX) + return NULL; + return _PyObject.realloc(_PyObject.ctx, ptr, new_size); +} + +void +PyObject_Free(void *ptr) +{ + _PyObject.free(_PyObject.ctx, ptr); +} + + +#ifdef WITH_PYMALLOC + +#ifdef WITH_VALGRIND +#include + +/* If we're using GCC, use __builtin_expect() to reduce overhead of + the valgrind checks */ +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +# define UNLIKELY(value) __builtin_expect((value), 0) +#else +# define UNLIKELY(value) (value) +#endif + +/* -1 indicates that we haven't checked that we're running on valgrind yet. */ +static int running_on_valgrind = -1; +#endif + +/* An object allocator for Python. + + Here is an introduction to the layers of the Python memory architecture, + showing where the object allocator is actually used (layer +2), It is + called for every object allocation and deallocation (PyObject_New/Del), + unless the object-specific allocators implement a proprietary allocation + scheme (ex.: ints use a simple free list). This is also the place where + the cyclic garbage collector operates selectively on container objects. + + + Object-specific allocators + _____ ______ ______ ________ + [ int ] [ dict ] [ list ] ... [ string ] Python core | ++3 | <----- Object-specific memory -----> | <-- Non-object memory --> | + _______________________________ | | + [ Python's object allocator ] | | ++2 | ####### Object memory ####### | <------ Internal buffers ------> | + ______________________________________________________________ | + [ Python's raw memory allocator (PyMem_ API) ] | ++1 | <----- Python memory (under PyMem manager's control) ------> | | + __________________________________________________________________ + [ Underlying general-purpose allocator (ex: C library malloc) ] + 0 | <------ Virtual memory allocated for the python process -------> | + + ========================================================================= + _______________________________________________________________________ + [ OS-specific Virtual Memory Manager (VMM) ] +-1 | <--- Kernel dynamic storage allocation & management (page-based) ---> | + __________________________________ __________________________________ + [ ] [ ] +-2 | <-- Physical memory: ROM/RAM --> | | <-- Secondary storage (swap) --> | + +*/ +/*==========================================================================*/ + +/* A fast, special-purpose memory allocator for small blocks, to be used + on top of a general-purpose malloc -- heavily based on previous art. */ + +/* Vladimir Marangozov -- August 2000 */ + +/* + * "Memory management is where the rubber meets the road -- if we do the wrong + * thing at any level, the results will not be good. And if we don't make the + * levels work well together, we are in serious trouble." (1) + * + * (1) Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles, + * "Dynamic Storage Allocation: A Survey and Critical Review", + * in Proc. 1995 Int'l. Workshop on Memory Management, September 1995. + */ + +/* #undef WITH_MEMORY_LIMITS */ /* disable mem limit checks */ + +/*==========================================================================*/ + +/* + * Allocation strategy abstract: + * + * For small requests, the allocator sub-allocates blocks of memory. + * Requests greater than SMALL_REQUEST_THRESHOLD bytes are routed to the + * system's allocator. + * + * Small requests are grouped in size classes spaced 8 bytes apart, due + * to the required valid alignment of the returned address. Requests of + * a particular size are serviced from memory pools of 4K (one VMM page). + * Pools are fragmented on demand and contain free lists of blocks of one + * particular size class. In other words, there is a fixed-size allocator + * for each size class. Free pools are shared by the different allocators + * thus minimizing the space reserved for a particular size class. + * + * This allocation strategy is a variant of what is known as "simple + * segregated storage based on array of free lists". The main drawback of + * simple segregated storage is that we might end up with lot of reserved + * memory for the different free lists, which degenerate in time. To avoid + * this, we partition each free list in pools and we share dynamically the + * reserved space between all free lists. This technique is quite efficient + * for memory intensive programs which allocate mainly small-sized blocks. + * + * For small requests we have the following table: + * + * Request in bytes Size of allocated block Size class idx + * ---------------------------------------------------------------- + * 1-8 8 0 + * 9-16 16 1 + * 17-24 24 2 + * 25-32 32 3 + * 33-40 40 4 + * 41-48 48 5 + * 49-56 56 6 + * 57-64 64 7 + * 65-72 72 8 + * ... ... ... + * 497-504 504 62 + * 505-512 512 63 + * + * 0, SMALL_REQUEST_THRESHOLD + 1 and up: routed to the underlying + * allocator. + */ + +/*==========================================================================*/ + +/* + * -- Main tunable settings section -- + */ + +/* + * Alignment of addresses returned to the user. 8-bytes alignment works + * on most current architectures (with 32-bit or 64-bit address busses). + * The alignment value is also used for grouping small requests in size + * classes spaced ALIGNMENT bytes apart. + * + * You shouldn't change this unless you know what you are doing. + */ +#define ALIGNMENT 8 /* must be 2^N */ +#define ALIGNMENT_SHIFT 3 + +/* Return the number of bytes in size class I, as a uint. */ +#define INDEX2SIZE(I) (((uint)(I) + 1) << ALIGNMENT_SHIFT) + +/* + * Max size threshold below which malloc requests are considered to be + * small enough in order to use preallocated memory pools. You can tune + * this value according to your application behaviour and memory needs. + * + * Note: a size threshold of 512 guarantees that newly created dictionaries + * will be allocated from preallocated memory pools on 64-bit. + * + * The following invariants must hold: + * 1) ALIGNMENT <= SMALL_REQUEST_THRESHOLD <= 512 + * 2) SMALL_REQUEST_THRESHOLD is evenly divisible by ALIGNMENT + * + * Although not required, for better performance and space efficiency, + * it is recommended that SMALL_REQUEST_THRESHOLD is set to a power of 2. + */ +#define SMALL_REQUEST_THRESHOLD 512 +#define NB_SMALL_SIZE_CLASSES (SMALL_REQUEST_THRESHOLD / ALIGNMENT) + +/* + * The system's VMM page size can be obtained on most unices with a + * getpagesize() call or deduced from various header files. To make + * things simpler, we assume that it is 4K, which is OK for most systems. + * It is probably better if this is the native page size, but it doesn't + * have to be. In theory, if SYSTEM_PAGE_SIZE is larger than the native page + * size, then `POOL_ADDR(p)->arenaindex' could rarely cause a segmentation + * violation fault. 4K is apparently OK for all the platforms that python + * currently targets. + */ +#define SYSTEM_PAGE_SIZE (4 * 1024) +#define SYSTEM_PAGE_SIZE_MASK (SYSTEM_PAGE_SIZE - 1) + +/* + * Maximum amount of memory managed by the allocator for small requests. + */ +#ifdef WITH_MEMORY_LIMITS +#ifndef SMALL_MEMORY_LIMIT +#define SMALL_MEMORY_LIMIT (64 * 1024 * 1024) /* 64 MB -- more? */ +#endif +#endif + +/* + * The allocator sub-allocates blocks of memory (called arenas) aligned + * on a page boundary. This is a reserved virtual address space for the + * current process (obtained through a malloc()/mmap() call). In no way this + * means that the memory arenas will be used entirely. A malloc() is + * usually an address range reservation for bytes, unless all pages within + * this space are referenced subsequently. So malloc'ing big blocks and not + * using them does not mean "wasting memory". It's an addressable range + * wastage... + * + * Arenas are allocated with mmap() on systems supporting anonymous memory + * mappings to reduce heap fragmentation. + */ +#define ARENA_SIZE (256 << 10) /* 256KB */ + +#ifdef WITH_MEMORY_LIMITS +#define MAX_ARENAS (SMALL_MEMORY_LIMIT / ARENA_SIZE) +#endif + +/* + * Size of the pools used for small blocks. Should be a power of 2, + * between 1K and SYSTEM_PAGE_SIZE, that is: 1k, 2k, 4k. + */ +#define POOL_SIZE SYSTEM_PAGE_SIZE /* must be 2^N */ +#define POOL_SIZE_MASK SYSTEM_PAGE_SIZE_MASK + +/* + * -- End of tunable settings section -- + */ + +/*==========================================================================*/ + +/* + * Locking + * + * To reduce lock contention, it would probably be better to refine the + * crude function locking with per size class locking. I'm not positive + * however, whether it's worth switching to such locking policy because + * of the performance penalty it might introduce. + * + * The following macros describe the simplest (should also be the fastest) + * lock object on a particular platform and the init/fini/lock/unlock + * operations on it. The locks defined here are not expected to be recursive + * because it is assumed that they will always be called in the order: + * INIT, [LOCK, UNLOCK]*, FINI. + */ + +/* + * Python's threads are serialized, so object malloc locking is disabled. + */ +#define SIMPLELOCK_DECL(lock) /* simple lock declaration */ +#define SIMPLELOCK_INIT(lock) /* allocate (if needed) and initialize */ +#define SIMPLELOCK_FINI(lock) /* free/destroy an existing lock */ +#define SIMPLELOCK_LOCK(lock) /* acquire released lock */ +#define SIMPLELOCK_UNLOCK(lock) /* release acquired lock */ + +/* When you say memory, my mind reasons in terms of (pointers to) blocks */ +typedef uchar block; + +/* Pool for small blocks. */ +struct pool_header { + union { block *_padding; + uint count; } ref; /* number of allocated blocks */ + block *freeblock; /* pool's free list head */ + struct pool_header *nextpool; /* next pool of this size class */ + struct pool_header *prevpool; /* previous pool "" */ + uint arenaindex; /* index into arenas of base adr */ + uint szidx; /* block size class index */ + uint nextoffset; /* bytes to virgin block */ + uint maxnextoffset; /* largest valid nextoffset */ +}; + +typedef struct pool_header *poolp; + +/* Record keeping for arenas. */ +struct arena_object { + /* The address of the arena, as returned by malloc. Note that 0 + * will never be returned by a successful malloc, and is used + * here to mark an arena_object that doesn't correspond to an + * allocated arena. + */ + uptr address; + + /* Pool-aligned pointer to the next pool to be carved off. */ + block* pool_address; + + /* The number of available pools in the arena: free pools + never- + * allocated pools. + */ + uint nfreepools; + + /* The total number of pools in the arena, whether or not available. */ + uint ntotalpools; + + /* Singly-linked list of available pools. */ + struct pool_header* freepools; + + /* Whenever this arena_object is not associated with an allocated + * arena, the nextarena member is used to link all unassociated + * arena_objects in the singly-linked `unused_arena_objects` list. + * The prevarena member is unused in this case. + * + * When this arena_object is associated with an allocated arena + * with at least one available pool, both members are used in the + * doubly-linked `usable_arenas` list, which is maintained in + * increasing order of `nfreepools` values. + * + * Else this arena_object is associated with an allocated arena + * all of whose pools are in use. `nextarena` and `prevarena` + * are both meaningless in this case. + */ + struct arena_object* nextarena; + struct arena_object* prevarena; +}; + +#define POOL_OVERHEAD _Py_SIZE_ROUND_UP(sizeof(struct pool_header), ALIGNMENT) + +#define DUMMY_SIZE_IDX 0xffff /* size class of newly cached pools */ + +/* Round pointer P down to the closest pool-aligned address <= P, as a poolp */ +#define POOL_ADDR(P) ((poolp)_Py_ALIGN_DOWN((P), POOL_SIZE)) + +/* Return total number of blocks in pool of size index I, as a uint. */ +#define NUMBLOCKS(I) ((uint)(POOL_SIZE - POOL_OVERHEAD) / INDEX2SIZE(I)) + +/*==========================================================================*/ + +/* + * This malloc lock + */ +SIMPLELOCK_DECL(_malloc_lock) +#define LOCK() SIMPLELOCK_LOCK(_malloc_lock) +#define UNLOCK() SIMPLELOCK_UNLOCK(_malloc_lock) +#define LOCK_INIT() SIMPLELOCK_INIT(_malloc_lock) +#define LOCK_FINI() SIMPLELOCK_FINI(_malloc_lock) + +/* + * Pool table -- headed, circular, doubly-linked lists of partially used pools. + +This is involved. For an index i, usedpools[i+i] is the header for a list of +all partially used pools holding small blocks with "size class idx" i. So +usedpools[0] corresponds to blocks of size 8, usedpools[2] to blocks of size +16, and so on: index 2*i <-> blocks of size (i+1)<freeblock points to +the start of a singly-linked list of free blocks within the pool. When a +block is freed, it's inserted at the front of its pool's freeblock list. Note +that the available blocks in a pool are *not* linked all together when a pool +is initialized. Instead only "the first two" (lowest addresses) blocks are +set up, returning the first such block, and setting pool->freeblock to a +one-block list holding the second such block. This is consistent with that +pymalloc strives at all levels (arena, pool, and block) never to touch a piece +of memory until it's actually needed. + +So long as a pool is in the used state, we're certain there *is* a block +available for allocating, and pool->freeblock is not NULL. If pool->freeblock +points to the end of the free list before we've carved the entire pool into +blocks, that means we simply haven't yet gotten to one of the higher-address +blocks. The offset from the pool_header to the start of "the next" virgin +block is stored in the pool_header nextoffset member, and the largest value +of nextoffset that makes sense is stored in the maxnextoffset member when a +pool is initialized. All the blocks in a pool have been passed out at least +once when and only when nextoffset > maxnextoffset. + + +Major obscurity: While the usedpools vector is declared to have poolp +entries, it doesn't really. It really contains two pointers per (conceptual) +poolp entry, the nextpool and prevpool members of a pool_header. The +excruciating initialization code below fools C so that + + usedpool[i+i] + +"acts like" a genuine poolp, but only so long as you only reference its +nextpool and prevpool members. The "- 2*sizeof(block *)" gibberish is +compensating for that a pool_header's nextpool and prevpool members +immediately follow a pool_header's first two members: + + union { block *_padding; + uint count; } ref; + block *freeblock; + +each of which consume sizeof(block *) bytes. So what usedpools[i+i] really +contains is a fudged-up pointer p such that *if* C believes it's a poolp +pointer, then p->nextpool and p->prevpool are both p (meaning that the headed +circular list is empty). + +It's unclear why the usedpools setup is so convoluted. It could be to +minimize the amount of cache required to hold this heavily-referenced table +(which only *needs* the two interpool pointer members of a pool_header). OTOH, +referencing code has to remember to "double the index" and doing so isn't +free, usedpools[0] isn't a strictly legal pointer, and we're crucially relying +on that C doesn't insert any padding anywhere in a pool_header at or before +the prevpool member. +**************************************************************************** */ + +#define PTA(x) ((poolp )((uchar *)&(usedpools[2*(x)]) - 2*sizeof(block *))) +#define PT(x) PTA(x), PTA(x) + +static poolp usedpools[2 * ((NB_SMALL_SIZE_CLASSES + 7) / 8) * 8] = { + PT(0), PT(1), PT(2), PT(3), PT(4), PT(5), PT(6), PT(7) +#if NB_SMALL_SIZE_CLASSES > 8 + , PT(8), PT(9), PT(10), PT(11), PT(12), PT(13), PT(14), PT(15) +#if NB_SMALL_SIZE_CLASSES > 16 + , PT(16), PT(17), PT(18), PT(19), PT(20), PT(21), PT(22), PT(23) +#if NB_SMALL_SIZE_CLASSES > 24 + , PT(24), PT(25), PT(26), PT(27), PT(28), PT(29), PT(30), PT(31) +#if NB_SMALL_SIZE_CLASSES > 32 + , PT(32), PT(33), PT(34), PT(35), PT(36), PT(37), PT(38), PT(39) +#if NB_SMALL_SIZE_CLASSES > 40 + , PT(40), PT(41), PT(42), PT(43), PT(44), PT(45), PT(46), PT(47) +#if NB_SMALL_SIZE_CLASSES > 48 + , PT(48), PT(49), PT(50), PT(51), PT(52), PT(53), PT(54), PT(55) +#if NB_SMALL_SIZE_CLASSES > 56 + , PT(56), PT(57), PT(58), PT(59), PT(60), PT(61), PT(62), PT(63) +#if NB_SMALL_SIZE_CLASSES > 64 +#error "NB_SMALL_SIZE_CLASSES should be less than 64" +#endif /* NB_SMALL_SIZE_CLASSES > 64 */ +#endif /* NB_SMALL_SIZE_CLASSES > 56 */ +#endif /* NB_SMALL_SIZE_CLASSES > 48 */ +#endif /* NB_SMALL_SIZE_CLASSES > 40 */ +#endif /* NB_SMALL_SIZE_CLASSES > 32 */ +#endif /* NB_SMALL_SIZE_CLASSES > 24 */ +#endif /* NB_SMALL_SIZE_CLASSES > 16 */ +#endif /* NB_SMALL_SIZE_CLASSES > 8 */ +}; + +/*========================================================================== +Arena management. + +`arenas` is a vector of arena_objects. It contains maxarenas entries, some of +which may not be currently used (== they're arena_objects that aren't +currently associated with an allocated arena). Note that arenas proper are +separately malloc'ed. + +Prior to Python 2.5, arenas were never free()'ed. Starting with Python 2.5, +we do try to free() arenas, and use some mild heuristic strategies to increase +the likelihood that arenas eventually can be freed. + +unused_arena_objects + + This is a singly-linked list of the arena_objects that are currently not + being used (no arena is associated with them). Objects are taken off the + head of the list in new_arena(), and are pushed on the head of the list in + PyObject_Free() when the arena is empty. Key invariant: an arena_object + is on this list if and only if its .address member is 0. + +usable_arenas + + This is a doubly-linked list of the arena_objects associated with arenas + that have pools available. These pools are either waiting to be reused, + or have not been used before. The list is sorted to have the most- + allocated arenas first (ascending order based on the nfreepools member). + This means that the next allocation will come from a heavily used arena, + which gives the nearly empty arenas a chance to be returned to the system. + In my unscientific tests this dramatically improved the number of arenas + that could be freed. + +Note that an arena_object associated with an arena all of whose pools are +currently in use isn't on either list. +*/ + +/* Array of objects used to track chunks of memory (arenas). */ +static struct arena_object* arenas = NULL; +/* Number of slots currently allocated in the `arenas` vector. */ +static uint maxarenas = 0; + +/* The head of the singly-linked, NULL-terminated list of available + * arena_objects. + */ +static struct arena_object* unused_arena_objects = NULL; + +/* The head of the doubly-linked, NULL-terminated at each end, list of + * arena_objects associated with arenas that have pools available. + */ +static struct arena_object* usable_arenas = NULL; + +/* How many arena_objects do we initially allocate? + * 16 = can allocate 16 arenas = 16 * ARENA_SIZE = 4MB before growing the + * `arenas` vector. + */ +#define INITIAL_ARENA_OBJECTS 16 + +/* Number of arenas allocated that haven't been free()'d. */ +static size_t narenas_currently_allocated = 0; + +/* Total number of times malloc() called to allocate an arena. */ +static size_t ntimes_arena_allocated = 0; +/* High water mark (max value ever seen) for narenas_currently_allocated. */ +static size_t narenas_highwater = 0; + +static Py_ssize_t _Py_AllocatedBlocks = 0; + +Py_ssize_t +_Py_GetAllocatedBlocks(void) +{ + return _Py_AllocatedBlocks; +} + + +/* Allocate a new arena. If we run out of memory, return NULL. Else + * allocate a new arena, and return the address of an arena_object + * describing the new arena. It's expected that the caller will set + * `usable_arenas` to the return value. + */ +static struct arena_object* +new_arena(void) +{ + struct arena_object* arenaobj; + uint excess; /* number of bytes above pool alignment */ + void *address; + static int debug_stats = -1; + + if (debug_stats == -1) { + char *opt = Py_GETENV("PYTHONMALLOCSTATS"); + debug_stats = (opt != NULL && *opt != '\0'); + } + if (debug_stats) + _PyObject_DebugMallocStats(stderr); + + if (unused_arena_objects == NULL) { + uint i; + uint numarenas; + size_t nbytes; + + /* Double the number of arena objects on each allocation. + * Note that it's possible for `numarenas` to overflow. + */ + numarenas = maxarenas ? maxarenas << 1 : INITIAL_ARENA_OBJECTS; + if (numarenas <= maxarenas) + return NULL; /* overflow */ +#if SIZEOF_SIZE_T <= SIZEOF_INT + if (numarenas > PY_SIZE_MAX / sizeof(*arenas)) + return NULL; /* overflow */ +#endif + nbytes = numarenas * sizeof(*arenas); + arenaobj = (struct arena_object *)PyMem_RawRealloc(arenas, nbytes); + if (arenaobj == NULL) + return NULL; + arenas = arenaobj; + + /* We might need to fix pointers that were copied. However, + * new_arena only gets called when all the pages in the + * previous arenas are full. Thus, there are *no* pointers + * into the old array. Thus, we don't have to worry about + * invalid pointers. Just to be sure, some asserts: + */ + assert(usable_arenas == NULL); + assert(unused_arena_objects == NULL); + + /* Put the new arenas on the unused_arena_objects list. */ + for (i = maxarenas; i < numarenas; ++i) { + arenas[i].address = 0; /* mark as unassociated */ + arenas[i].nextarena = i < numarenas - 1 ? + &arenas[i+1] : NULL; + } + + /* Update globals. */ + unused_arena_objects = &arenas[maxarenas]; + maxarenas = numarenas; + } + + /* Take the next available arena object off the head of the list. */ + assert(unused_arena_objects != NULL); + arenaobj = unused_arena_objects; + unused_arena_objects = arenaobj->nextarena; + assert(arenaobj->address == 0); + address = _PyObject_Arena.alloc(_PyObject_Arena.ctx, ARENA_SIZE); + if (address == NULL) { + /* The allocation failed: return NULL after putting the + * arenaobj back. + */ + arenaobj->nextarena = unused_arena_objects; + unused_arena_objects = arenaobj; + return NULL; + } + arenaobj->address = (uptr)address; + + ++narenas_currently_allocated; + ++ntimes_arena_allocated; + if (narenas_currently_allocated > narenas_highwater) + narenas_highwater = narenas_currently_allocated; + arenaobj->freepools = NULL; + /* pool_address <- first pool-aligned address in the arena + nfreepools <- number of whole pools that fit after alignment */ + arenaobj->pool_address = (block*)arenaobj->address; + arenaobj->nfreepools = ARENA_SIZE / POOL_SIZE; + assert(POOL_SIZE * arenaobj->nfreepools == ARENA_SIZE); + excess = (uint)(arenaobj->address & POOL_SIZE_MASK); + if (excess != 0) { + --arenaobj->nfreepools; + arenaobj->pool_address += POOL_SIZE - excess; + } + arenaobj->ntotalpools = arenaobj->nfreepools; + + return arenaobj; +} + +/* +Py_ADDRESS_IN_RANGE(P, POOL) + +Return true if and only if P is an address that was allocated by pymalloc. +POOL must be the pool address associated with P, i.e., POOL = POOL_ADDR(P) +(the caller is asked to compute this because the macro expands POOL more than +once, and for efficiency it's best for the caller to assign POOL_ADDR(P) to a +variable and pass the latter to the macro; because Py_ADDRESS_IN_RANGE is +called on every alloc/realloc/free, micro-efficiency is important here). + +Tricky: Let B be the arena base address associated with the pool, B = +arenas[(POOL)->arenaindex].address. Then P belongs to the arena if and only if + + B <= P < B + ARENA_SIZE + +Subtracting B throughout, this is true iff + + 0 <= P-B < ARENA_SIZE + +By using unsigned arithmetic, the "0 <=" half of the test can be skipped. + +Obscure: A PyMem "free memory" function can call the pymalloc free or realloc +before the first arena has been allocated. `arenas` is still NULL in that +case. We're relying on that maxarenas is also 0 in that case, so that +(POOL)->arenaindex < maxarenas must be false, saving us from trying to index +into a NULL arenas. + +Details: given P and POOL, the arena_object corresponding to P is AO = +arenas[(POOL)->arenaindex]. Suppose obmalloc controls P. Then (barring wild +stores, etc), POOL is the correct address of P's pool, AO.address is the +correct base address of the pool's arena, and P must be within ARENA_SIZE of +AO.address. In addition, AO.address is not 0 (no arena can start at address 0 +(NULL)). Therefore Py_ADDRESS_IN_RANGE correctly reports that obmalloc +controls P. + +Now suppose obmalloc does not control P (e.g., P was obtained via a direct +call to the system malloc() or realloc()). (POOL)->arenaindex may be anything +in this case -- it may even be uninitialized trash. If the trash arenaindex +is >= maxarenas, the macro correctly concludes at once that obmalloc doesn't +control P. + +Else arenaindex is < maxarena, and AO is read up. If AO corresponds to an +allocated arena, obmalloc controls all the memory in slice AO.address : +AO.address+ARENA_SIZE. By case assumption, P is not controlled by obmalloc, +so P doesn't lie in that slice, so the macro correctly reports that P is not +controlled by obmalloc. + +Finally, if P is not controlled by obmalloc and AO corresponds to an unused +arena_object (one not currently associated with an allocated arena), +AO.address is 0, and the second test in the macro reduces to: + + P < ARENA_SIZE + +If P >= ARENA_SIZE (extremely likely), the macro again correctly concludes +that P is not controlled by obmalloc. However, if P < ARENA_SIZE, this part +of the test still passes, and the third clause (AO.address != 0) is necessary +to get the correct result: AO.address is 0 in this case, so the macro +correctly reports that P is not controlled by obmalloc (despite that P lies in +slice AO.address : AO.address + ARENA_SIZE). + +Note: The third (AO.address != 0) clause was added in Python 2.5. Before +2.5, arenas were never free()'ed, and an arenaindex < maxarena always +corresponded to a currently-allocated arena, so the "P is not controlled by +obmalloc, AO corresponds to an unused arena_object, and P < ARENA_SIZE" case +was impossible. + +Note that the logic is excruciating, and reading up possibly uninitialized +memory when P is not controlled by obmalloc (to get at (POOL)->arenaindex) +creates problems for some memory debuggers. The overwhelming advantage is +that this test determines whether an arbitrary address is controlled by +obmalloc in a small constant time, independent of the number of arenas +obmalloc controls. Since this test is needed at every entry point, it's +extremely desirable that it be this fast. + +Since Py_ADDRESS_IN_RANGE may be reading from memory which was not allocated +by Python, it is important that (POOL)->arenaindex is read only once, as +another thread may be concurrently modifying the value without holding the +GIL. To accomplish this, the arenaindex_temp variable is used to store +(POOL)->arenaindex for the duration of the Py_ADDRESS_IN_RANGE macro's +execution. The caller of the macro is responsible for declaring this +variable. +*/ +#define Py_ADDRESS_IN_RANGE(P, POOL) \ + ((arenaindex_temp = (POOL)->arenaindex) < maxarenas && \ + (uptr)(P) - arenas[arenaindex_temp].address < (uptr)ARENA_SIZE && \ + arenas[arenaindex_temp].address != 0) + + +/* This is only useful when running memory debuggers such as + * Purify or Valgrind. Uncomment to use. + * +#define Py_USING_MEMORY_DEBUGGER + */ + +#ifdef Py_USING_MEMORY_DEBUGGER + +/* Py_ADDRESS_IN_RANGE may access uninitialized memory by design + * This leads to thousands of spurious warnings when using + * Purify or Valgrind. By making a function, we can easily + * suppress the uninitialized memory reads in this one function. + * So we won't ignore real errors elsewhere. + * + * Disable the macro and use a function. + */ + +#undef Py_ADDRESS_IN_RANGE + +#if defined(__GNUC__) && ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) || \ + (__GNUC__ >= 4)) +#define Py_NO_INLINE __attribute__((__noinline__)) +#else +#define Py_NO_INLINE +#endif + +/* Don't make static, to try to ensure this isn't inlined. */ +int Py_ADDRESS_IN_RANGE(void *P, poolp pool) Py_NO_INLINE; +#undef Py_NO_INLINE +#endif + +/*==========================================================================*/ + +/* malloc. Note that nbytes==0 tries to return a non-NULL pointer, distinct + * from all other currently live pointers. This may not be possible. + */ + +/* + * The basic blocks are ordered by decreasing execution frequency, + * which minimizes the number of jumps in the most common cases, + * improves branching prediction and instruction scheduling (small + * block allocations typically result in a couple of instructions). + * Unless the optimizer reorders everything, being too smart... + */ + +static void * +_PyObject_Alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize) +{ + size_t nbytes; + block *bp; + poolp pool; + poolp next; + uint size; + + _Py_AllocatedBlocks++; + + assert(nelem <= PY_SSIZE_T_MAX / elsize); + nbytes = nelem * elsize; + +#ifdef WITH_VALGRIND + if (UNLIKELY(running_on_valgrind == -1)) + running_on_valgrind = RUNNING_ON_VALGRIND; + if (UNLIKELY(running_on_valgrind)) + goto redirect; +#endif + + if (nelem == 0 || elsize == 0) + goto redirect; + + if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { + LOCK(); + /* + * Most frequent paths first + */ + size = (uint)(nbytes - 1) >> ALIGNMENT_SHIFT; + pool = usedpools[size + size]; + if (pool != pool->nextpool) { + /* + * There is a used pool for this size class. + * Pick up the head block of its free list. + */ + ++pool->ref.count; + bp = pool->freeblock; + assert(bp != NULL); + if ((pool->freeblock = *(block **)bp) != NULL) { + UNLOCK(); + if (use_calloc) + memset(bp, 0, nbytes); + return (void *)bp; + } + /* + * Reached the end of the free list, try to extend it. + */ + if (pool->nextoffset <= pool->maxnextoffset) { + /* There is room for another block. */ + pool->freeblock = (block*)pool + + pool->nextoffset; + pool->nextoffset += INDEX2SIZE(size); + *(block **)(pool->freeblock) = NULL; + UNLOCK(); + if (use_calloc) + memset(bp, 0, nbytes); + return (void *)bp; + } + /* Pool is full, unlink from used pools. */ + next = pool->nextpool; + pool = pool->prevpool; + next->prevpool = pool; + pool->nextpool = next; + UNLOCK(); + if (use_calloc) + memset(bp, 0, nbytes); + return (void *)bp; + } + + /* There isn't a pool of the right size class immediately + * available: use a free pool. + */ + if (usable_arenas == NULL) { + /* No arena has a free pool: allocate a new arena. */ +#ifdef WITH_MEMORY_LIMITS + if (narenas_currently_allocated >= MAX_ARENAS) { + UNLOCK(); + goto redirect; + } +#endif + usable_arenas = new_arena(); + if (usable_arenas == NULL) { + UNLOCK(); + goto redirect; + } + usable_arenas->nextarena = + usable_arenas->prevarena = NULL; + } + assert(usable_arenas->address != 0); + + /* Try to get a cached free pool. */ + pool = usable_arenas->freepools; + if (pool != NULL) { + /* Unlink from cached pools. */ + usable_arenas->freepools = pool->nextpool; + + /* This arena already had the smallest nfreepools + * value, so decreasing nfreepools doesn't change + * that, and we don't need to rearrange the + * usable_arenas list. However, if the arena has + * become wholly allocated, we need to remove its + * arena_object from usable_arenas. + */ + --usable_arenas->nfreepools; + if (usable_arenas->nfreepools == 0) { + /* Wholly allocated: remove. */ + assert(usable_arenas->freepools == NULL); + assert(usable_arenas->nextarena == NULL || + usable_arenas->nextarena->prevarena == + usable_arenas); + + usable_arenas = usable_arenas->nextarena; + if (usable_arenas != NULL) { + usable_arenas->prevarena = NULL; + assert(usable_arenas->address != 0); + } + } + else { + /* nfreepools > 0: it must be that freepools + * isn't NULL, or that we haven't yet carved + * off all the arena's pools for the first + * time. + */ + assert(usable_arenas->freepools != NULL || + usable_arenas->pool_address <= + (block*)usable_arenas->address + + ARENA_SIZE - POOL_SIZE); + } + init_pool: + /* Frontlink to used pools. */ + next = usedpools[size + size]; /* == prev */ + pool->nextpool = next; + pool->prevpool = next; + next->nextpool = pool; + next->prevpool = pool; + pool->ref.count = 1; + if (pool->szidx == size) { + /* Luckily, this pool last contained blocks + * of the same size class, so its header + * and free list are already initialized. + */ + bp = pool->freeblock; + assert(bp != NULL); + pool->freeblock = *(block **)bp; + UNLOCK(); + if (use_calloc) + memset(bp, 0, nbytes); + return (void *)bp; + } + /* + * Initialize the pool header, set up the free list to + * contain just the second block, and return the first + * block. + */ + pool->szidx = size; + size = INDEX2SIZE(size); + bp = (block *)pool + POOL_OVERHEAD; + pool->nextoffset = POOL_OVERHEAD + (size << 1); + pool->maxnextoffset = POOL_SIZE - size; + pool->freeblock = bp + size; + *(block **)(pool->freeblock) = NULL; + UNLOCK(); + if (use_calloc) + memset(bp, 0, nbytes); + return (void *)bp; + } + + /* Carve off a new pool. */ + assert(usable_arenas->nfreepools > 0); + assert(usable_arenas->freepools == NULL); + pool = (poolp)usable_arenas->pool_address; + assert((block*)pool <= (block*)usable_arenas->address + + ARENA_SIZE - POOL_SIZE); + pool->arenaindex = (uint)(usable_arenas - arenas); + assert(&arenas[pool->arenaindex] == usable_arenas); + pool->szidx = DUMMY_SIZE_IDX; + usable_arenas->pool_address += POOL_SIZE; + --usable_arenas->nfreepools; + + if (usable_arenas->nfreepools == 0) { + assert(usable_arenas->nextarena == NULL || + usable_arenas->nextarena->prevarena == + usable_arenas); + /* Unlink the arena: it is completely allocated. */ + usable_arenas = usable_arenas->nextarena; + if (usable_arenas != NULL) { + usable_arenas->prevarena = NULL; + assert(usable_arenas->address != 0); + } + } + + goto init_pool; + } + + /* The small block allocator ends here. */ + +redirect: + /* Redirect the original request to the underlying (libc) allocator. + * We jump here on bigger requests, on error in the code above (as a + * last chance to serve the request) or when the max memory limit + * has been reached. + */ + { + void *result; + if (use_calloc) + result = PyMem_RawCalloc(nelem, elsize); + else + result = PyMem_RawMalloc(nbytes); + if (!result) + _Py_AllocatedBlocks--; + return result; + } +} + +static void * +_PyObject_Malloc(void *ctx, size_t nbytes) +{ + return _PyObject_Alloc(0, ctx, 1, nbytes); +} + +static void * +_PyObject_Calloc(void *ctx, size_t nelem, size_t elsize) +{ + return _PyObject_Alloc(1, ctx, nelem, elsize); +} + +/* free */ + +ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS +static void +_PyObject_Free(void *ctx, void *p) +{ + poolp pool; + block *lastfree; + poolp next, prev; + uint size; +#ifndef Py_USING_MEMORY_DEBUGGER + uint arenaindex_temp; +#endif + + if (p == NULL) /* free(NULL) has no effect */ + return; + + _Py_AllocatedBlocks--; + +#ifdef WITH_VALGRIND + if (UNLIKELY(running_on_valgrind > 0)) + goto redirect; +#endif + + pool = POOL_ADDR(p); + if (Py_ADDRESS_IN_RANGE(p, pool)) { + /* We allocated this address. */ + LOCK(); + /* Link p to the start of the pool's freeblock list. Since + * the pool had at least the p block outstanding, the pool + * wasn't empty (so it's already in a usedpools[] list, or + * was full and is in no list -- it's not in the freeblocks + * list in any case). + */ + assert(pool->ref.count > 0); /* else it was empty */ + *(block **)p = lastfree = pool->freeblock; + pool->freeblock = (block *)p; + if (lastfree) { + struct arena_object* ao; + uint nf; /* ao->nfreepools */ + + /* freeblock wasn't NULL, so the pool wasn't full, + * and the pool is in a usedpools[] list. + */ + if (--pool->ref.count != 0) { + /* pool isn't empty: leave it in usedpools */ + UNLOCK(); + return; + } + /* Pool is now empty: unlink from usedpools, and + * link to the front of freepools. This ensures that + * previously freed pools will be allocated later + * (being not referenced, they are perhaps paged out). + */ + next = pool->nextpool; + prev = pool->prevpool; + next->prevpool = prev; + prev->nextpool = next; + + /* Link the pool to freepools. This is a singly-linked + * list, and pool->prevpool isn't used there. + */ + ao = &arenas[pool->arenaindex]; + pool->nextpool = ao->freepools; + ao->freepools = pool; + nf = ++ao->nfreepools; + + /* All the rest is arena management. We just freed + * a pool, and there are 4 cases for arena mgmt: + * 1. If all the pools are free, return the arena to + * the system free(). + * 2. If this is the only free pool in the arena, + * add the arena back to the `usable_arenas` list. + * 3. If the "next" arena has a smaller count of free + * pools, we have to "slide this arena right" to + * restore that usable_arenas is sorted in order of + * nfreepools. + * 4. Else there's nothing more to do. + */ + if (nf == ao->ntotalpools) { + /* Case 1. First unlink ao from usable_arenas. + */ + assert(ao->prevarena == NULL || + ao->prevarena->address != 0); + assert(ao ->nextarena == NULL || + ao->nextarena->address != 0); + + /* Fix the pointer in the prevarena, or the + * usable_arenas pointer. + */ + if (ao->prevarena == NULL) { + usable_arenas = ao->nextarena; + assert(usable_arenas == NULL || + usable_arenas->address != 0); + } + else { + assert(ao->prevarena->nextarena == ao); + ao->prevarena->nextarena = + ao->nextarena; + } + /* Fix the pointer in the nextarena. */ + if (ao->nextarena != NULL) { + assert(ao->nextarena->prevarena == ao); + ao->nextarena->prevarena = + ao->prevarena; + } + /* Record that this arena_object slot is + * available to be reused. + */ + ao->nextarena = unused_arena_objects; + unused_arena_objects = ao; + + /* Free the entire arena. */ + _PyObject_Arena.free(_PyObject_Arena.ctx, + (void *)ao->address, ARENA_SIZE); + ao->address = 0; /* mark unassociated */ + --narenas_currently_allocated; + + UNLOCK(); + return; + } + if (nf == 1) { + /* Case 2. Put ao at the head of + * usable_arenas. Note that because + * ao->nfreepools was 0 before, ao isn't + * currently on the usable_arenas list. + */ + ao->nextarena = usable_arenas; + ao->prevarena = NULL; + if (usable_arenas) + usable_arenas->prevarena = ao; + usable_arenas = ao; + assert(usable_arenas->address != 0); + + UNLOCK(); + return; + } + /* If this arena is now out of order, we need to keep + * the list sorted. The list is kept sorted so that + * the "most full" arenas are used first, which allows + * the nearly empty arenas to be completely freed. In + * a few un-scientific tests, it seems like this + * approach allowed a lot more memory to be freed. + */ + if (ao->nextarena == NULL || + nf <= ao->nextarena->nfreepools) { + /* Case 4. Nothing to do. */ + UNLOCK(); + return; + } + /* Case 3: We have to move the arena towards the end + * of the list, because it has more free pools than + * the arena to its right. + * First unlink ao from usable_arenas. + */ + if (ao->prevarena != NULL) { + /* ao isn't at the head of the list */ + assert(ao->prevarena->nextarena == ao); + ao->prevarena->nextarena = ao->nextarena; + } + else { + /* ao is at the head of the list */ + assert(usable_arenas == ao); + usable_arenas = ao->nextarena; + } + ao->nextarena->prevarena = ao->prevarena; + + /* Locate the new insertion point by iterating over + * the list, using our nextarena pointer. + */ + while (ao->nextarena != NULL && + nf > ao->nextarena->nfreepools) { + ao->prevarena = ao->nextarena; + ao->nextarena = ao->nextarena->nextarena; + } + + /* Insert ao at this point. */ + assert(ao->nextarena == NULL || + ao->prevarena == ao->nextarena->prevarena); + assert(ao->prevarena->nextarena == ao->nextarena); + + ao->prevarena->nextarena = ao; + if (ao->nextarena != NULL) + ao->nextarena->prevarena = ao; + + /* Verify that the swaps worked. */ + assert(ao->nextarena == NULL || + nf <= ao->nextarena->nfreepools); + assert(ao->prevarena == NULL || + nf > ao->prevarena->nfreepools); + assert(ao->nextarena == NULL || + ao->nextarena->prevarena == ao); + assert((usable_arenas == ao && + ao->prevarena == NULL) || + ao->prevarena->nextarena == ao); + + UNLOCK(); + return; + } + /* Pool was full, so doesn't currently live in any list: + * link it to the front of the appropriate usedpools[] list. + * This mimics LRU pool usage for new allocations and + * targets optimal filling when several pools contain + * blocks of the same size class. + */ + --pool->ref.count; + assert(pool->ref.count > 0); /* else the pool is empty */ + size = pool->szidx; + next = usedpools[size + size]; + prev = next->prevpool; + /* insert pool before next: prev <-> pool <-> next */ + pool->nextpool = next; + pool->prevpool = prev; + next->prevpool = pool; + prev->nextpool = pool; + UNLOCK(); + return; + } + +#ifdef WITH_VALGRIND +redirect: +#endif + /* We didn't allocate this address. */ + PyMem_RawFree(p); +} + +/* realloc. If p is NULL, this acts like malloc(nbytes). Else if nbytes==0, + * then as the Python docs promise, we do not treat this like free(p), and + * return a non-NULL result. + */ + +ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS +static void * +_PyObject_Realloc(void *ctx, void *p, size_t nbytes) +{ + void *bp; + poolp pool; + size_t size; +#ifndef Py_USING_MEMORY_DEBUGGER + uint arenaindex_temp; +#endif + + if (p == NULL) + return _PyObject_Alloc(0, ctx, 1, nbytes); + +#ifdef WITH_VALGRIND + /* Treat running_on_valgrind == -1 the same as 0 */ + if (UNLIKELY(running_on_valgrind > 0)) + goto redirect; +#endif + + pool = POOL_ADDR(p); + if (Py_ADDRESS_IN_RANGE(p, pool)) { + /* We're in charge of this block */ + size = INDEX2SIZE(pool->szidx); + if (nbytes <= size) { + /* The block is staying the same or shrinking. If + * it's shrinking, there's a tradeoff: it costs + * cycles to copy the block to a smaller size class, + * but it wastes memory not to copy it. The + * compromise here is to copy on shrink only if at + * least 25% of size can be shaved off. + */ + if (4 * nbytes > 3 * size) { + /* It's the same, + * or shrinking and new/old > 3/4. + */ + return p; + } + size = nbytes; + } + bp = _PyObject_Alloc(0, ctx, 1, nbytes); + if (bp != NULL) { + memcpy(bp, p, size); + _PyObject_Free(ctx, p); + } + return bp; + } +#ifdef WITH_VALGRIND + redirect: +#endif + /* We're not managing this block. If nbytes <= + * SMALL_REQUEST_THRESHOLD, it's tempting to try to take over this + * block. However, if we do, we need to copy the valid data from + * the C-managed block to one of our blocks, and there's no portable + * way to know how much of the memory space starting at p is valid. + * As bug 1185883 pointed out the hard way, it's possible that the + * C-managed block is "at the end" of allocated VM space, so that + * a memory fault can occur if we try to copy nbytes bytes starting + * at p. Instead we punt: let C continue to manage this block. + */ + if (nbytes) + return PyMem_RawRealloc(p, nbytes); + /* C doesn't define the result of realloc(p, 0) (it may or may not + * return NULL then), but Python's docs promise that nbytes==0 never + * returns NULL. We don't pass 0 to realloc(), to avoid that endcase + * to begin with. Even then, we can't be sure that realloc() won't + * return NULL. + */ + bp = PyMem_RawRealloc(p, 1); + return bp ? bp : p; +} + +#else /* ! WITH_PYMALLOC */ + +/*==========================================================================*/ +/* pymalloc not enabled: Redirect the entry points to malloc. These will + * only be used by extensions that are compiled with pymalloc enabled. */ + +Py_ssize_t +_Py_GetAllocatedBlocks(void) +{ + return 0; +} + +#endif /* WITH_PYMALLOC */ + + +/*==========================================================================*/ +/* A x-platform debugging allocator. This doesn't manage memory directly, + * it wraps a real allocator, adding extra debugging info to the memory blocks. + */ + +/* Special bytes broadcast into debug memory blocks at appropriate times. + * Strings of these are unlikely to be valid addresses, floats, ints or + * 7-bit ASCII. + */ +#undef CLEANBYTE +#undef DEADBYTE +#undef FORBIDDENBYTE +#define CLEANBYTE 0xCB /* clean (newly allocated) memory */ +#define DEADBYTE 0xDB /* dead (newly freed) memory */ +#define FORBIDDENBYTE 0xFB /* untouchable bytes at each end of a block */ + +static size_t serialno = 0; /* incremented on each debug {m,re}alloc */ + +/* serialno is always incremented via calling this routine. The point is + * to supply a single place to set a breakpoint. + */ +static void +bumpserialno(void) +{ + ++serialno; +} + +#define SST SIZEOF_SIZE_T + +/* Read sizeof(size_t) bytes at p as a big-endian size_t. */ +static size_t +read_size_t(const void *p) +{ + const uchar *q = (const uchar *)p; + size_t result = *q++; + int i; + + for (i = SST; --i > 0; ++q) + result = (result << 8) | *q; + return result; +} + +/* Write n as a big-endian size_t, MSB at address p, LSB at + * p + sizeof(size_t) - 1. + */ +static void +write_size_t(void *p, size_t n) +{ + uchar *q = (uchar *)p + SST - 1; + int i; + + for (i = SST; --i >= 0; --q) { + *q = (uchar)(n & 0xff); + n >>= 8; + } +} + +/* Let S = sizeof(size_t). The debug malloc asks for 4*S extra bytes and + fills them with useful stuff, here calling the underlying malloc's result p: + +p[0: S] + Number of bytes originally asked for. This is a size_t, big-endian (easier + to read in a memory dump). +p[S] + API ID. See PEP 445. This is a character, but seems undocumented. +p[S+1: 2*S] + Copies of FORBIDDENBYTE. Used to catch under- writes and reads. +p[2*S: 2*S+n] + The requested memory, filled with copies of CLEANBYTE. + Used to catch reference to uninitialized memory. + &p[2*S] is returned. Note that this is 8-byte aligned if pymalloc + handled the request itself. +p[2*S+n: 2*S+n+S] + Copies of FORBIDDENBYTE. Used to catch over- writes and reads. +p[2*S+n+S: 2*S+n+2*S] + A serial number, incremented by 1 on each call to _PyMem_DebugMalloc + and _PyMem_DebugRealloc. + This is a big-endian size_t. + If "bad memory" is detected later, the serial number gives an + excellent way to set a breakpoint on the next run, to capture the + instant at which this block was passed out. +*/ + +static void * +_PyMem_DebugRawAlloc(int use_calloc, void *ctx, size_t nbytes) +{ + debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; + uchar *p; /* base address of malloc'ed block */ + uchar *tail; /* p + 2*SST + nbytes == pointer to tail pad bytes */ + size_t total; /* nbytes + 4*SST */ + + bumpserialno(); + total = nbytes + 4*SST; + if (nbytes > PY_SSIZE_T_MAX - 4*SST) + /* overflow: can't represent total as a Py_ssize_t */ + return NULL; + + if (use_calloc) + p = (uchar *)api->alloc.calloc(api->alloc.ctx, 1, total); + else + p = (uchar *)api->alloc.malloc(api->alloc.ctx, total); + if (p == NULL) + return NULL; + + /* at p, write size (SST bytes), id (1 byte), pad (SST-1 bytes) */ + write_size_t(p, nbytes); + p[SST] = (uchar)api->api_id; + memset(p + SST + 1, FORBIDDENBYTE, SST-1); + + if (nbytes > 0 && !use_calloc) + memset(p + 2*SST, CLEANBYTE, nbytes); + + /* at tail, write pad (SST bytes) and serialno (SST bytes) */ + tail = p + 2*SST + nbytes; + memset(tail, FORBIDDENBYTE, SST); + write_size_t(tail + SST, serialno); + + return p + 2*SST; +} + +static void * +_PyMem_DebugRawMalloc(void *ctx, size_t nbytes) +{ + return _PyMem_DebugRawAlloc(0, ctx, nbytes); +} + +static void * +_PyMem_DebugRawCalloc(void *ctx, size_t nelem, size_t elsize) +{ + size_t nbytes; + assert(elsize == 0 || nelem <= PY_SSIZE_T_MAX / elsize); + nbytes = nelem * elsize; + return _PyMem_DebugRawAlloc(1, ctx, nbytes); +} + +/* The debug free first checks the 2*SST bytes on each end for sanity (in + particular, that the FORBIDDENBYTEs with the api ID are still intact). + Then fills the original bytes with DEADBYTE. + Then calls the underlying free. +*/ +static void +_PyMem_DebugRawFree(void *ctx, void *p) +{ + debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; + uchar *q = (uchar *)p - 2*SST; /* address returned from malloc */ + size_t nbytes; + + if (p == NULL) + return; + _PyMem_DebugCheckAddress(api->api_id, p); + nbytes = read_size_t(q); + nbytes += 4*SST; + if (nbytes > 0) + memset(q, DEADBYTE, nbytes); + api->alloc.free(api->alloc.ctx, q); +} + +static void * +_PyMem_DebugRawRealloc(void *ctx, void *p, size_t nbytes) +{ + debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; + uchar *q = (uchar *)p, *oldq; + uchar *tail; + size_t total; /* nbytes + 4*SST */ + size_t original_nbytes; + int i; + + if (p == NULL) + return _PyMem_DebugRawAlloc(0, ctx, nbytes); + + _PyMem_DebugCheckAddress(api->api_id, p); + bumpserialno(); + original_nbytes = read_size_t(q - 2*SST); + total = nbytes + 4*SST; + if (nbytes > PY_SSIZE_T_MAX - 4*SST) + /* overflow: can't represent total as a Py_ssize_t */ + return NULL; + + /* Resize and add decorations. We may get a new pointer here, in which + * case we didn't get the chance to mark the old memory with DEADBYTE, + * but we live with that. + */ + oldq = q; + q = (uchar *)api->alloc.realloc(api->alloc.ctx, q - 2*SST, total); + if (q == NULL) + return NULL; + + if (q == oldq && nbytes < original_nbytes) { + /* shrinking: mark old extra memory dead */ + memset(q + nbytes, DEADBYTE, original_nbytes - nbytes); + } + + write_size_t(q, nbytes); + assert(q[SST] == (uchar)api->api_id); + for (i = 1; i < SST; ++i) + assert(q[SST + i] == FORBIDDENBYTE); + q += 2*SST; + + tail = q + nbytes; + memset(tail, FORBIDDENBYTE, SST); + write_size_t(tail + SST, serialno); + + if (nbytes > original_nbytes) { + /* growing: mark new extra memory clean */ + memset(q + original_nbytes, CLEANBYTE, + nbytes - original_nbytes); + } + + return q; +} + +static void +_PyMem_DebugCheckGIL(void) +{ +#ifdef WITH_THREAD + if (!PyGILState_Check()) + Py_FatalError("Python memory allocator called " + "without holding the GIL"); +#endif +} + +static void * +_PyMem_DebugMalloc(void *ctx, size_t nbytes) +{ + _PyMem_DebugCheckGIL(); + return _PyMem_DebugRawMalloc(ctx, nbytes); +} + +static void * +_PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize) +{ + _PyMem_DebugCheckGIL(); + return _PyMem_DebugRawCalloc(ctx, nelem, elsize); +} + +static void +_PyMem_DebugFree(void *ctx, void *ptr) +{ + _PyMem_DebugCheckGIL(); + _PyMem_DebugRawFree(ctx, ptr); +} + +static void * +_PyMem_DebugRealloc(void *ctx, void *ptr, size_t nbytes) +{ + _PyMem_DebugCheckGIL(); + return _PyMem_DebugRawRealloc(ctx, ptr, nbytes); +} + +/* Check the forbidden bytes on both ends of the memory allocated for p. + * If anything is wrong, print info to stderr via _PyObject_DebugDumpAddress, + * and call Py_FatalError to kill the program. + * The API id, is also checked. + */ +static void +_PyMem_DebugCheckAddress(char api, const void *p) +{ + const uchar *q = (const uchar *)p; + char msgbuf[64]; + char *msg; + size_t nbytes; + const uchar *tail; + int i; + char id; + + if (p == NULL) { + msg = "didn't expect a NULL pointer"; + goto error; + } + + /* Check the API id */ + id = (char)q[-SST]; + if (id != api) { + msg = msgbuf; + snprintf(msg, sizeof(msgbuf), "bad ID: Allocated using API '%c', verified using API '%c'", id, api); + msgbuf[sizeof(msgbuf)-1] = 0; + goto error; + } + + /* Check the stuff at the start of p first: if there's underwrite + * corruption, the number-of-bytes field may be nuts, and checking + * the tail could lead to a segfault then. + */ + for (i = SST-1; i >= 1; --i) { + if (*(q-i) != FORBIDDENBYTE) { + msg = "bad leading pad byte"; + goto error; + } + } + + nbytes = read_size_t(q - 2*SST); + tail = q + nbytes; + for (i = 0; i < SST; ++i) { + if (tail[i] != FORBIDDENBYTE) { + msg = "bad trailing pad byte"; + goto error; + } + } + + return; + +error: + _PyObject_DebugDumpAddress(p); + Py_FatalError(msg); +} + +/* Display info to stderr about the memory block at p. */ +static void +_PyObject_DebugDumpAddress(const void *p) +{ + const uchar *q = (const uchar *)p; + const uchar *tail; + size_t nbytes, serial; + int i; + int ok; + char id; + + fprintf(stderr, "Debug memory block at address p=%p:", p); + if (p == NULL) { + fprintf(stderr, "\n"); + return; + } + id = (char)q[-SST]; + fprintf(stderr, " API '%c'\n", id); + + nbytes = read_size_t(q - 2*SST); + fprintf(stderr, " %" PY_FORMAT_SIZE_T "u bytes originally " + "requested\n", nbytes); + + /* In case this is nuts, check the leading pad bytes first. */ + fprintf(stderr, " The %d pad bytes at p-%d are ", SST-1, SST-1); + ok = 1; + for (i = 1; i <= SST-1; ++i) { + if (*(q-i) != FORBIDDENBYTE) { + ok = 0; + break; + } + } + if (ok) + fputs("FORBIDDENBYTE, as expected.\n", stderr); + else { + fprintf(stderr, "not all FORBIDDENBYTE (0x%02x):\n", + FORBIDDENBYTE); + for (i = SST-1; i >= 1; --i) { + const uchar byte = *(q-i); + fprintf(stderr, " at p-%d: 0x%02x", i, byte); + if (byte != FORBIDDENBYTE) + fputs(" *** OUCH", stderr); + fputc('\n', stderr); + } + + fputs(" Because memory is corrupted at the start, the " + "count of bytes requested\n" + " may be bogus, and checking the trailing pad " + "bytes may segfault.\n", stderr); + } + + tail = q + nbytes; + fprintf(stderr, " The %d pad bytes at tail=%p are ", SST, tail); + ok = 1; + for (i = 0; i < SST; ++i) { + if (tail[i] != FORBIDDENBYTE) { + ok = 0; + break; + } + } + if (ok) + fputs("FORBIDDENBYTE, as expected.\n", stderr); + else { + fprintf(stderr, "not all FORBIDDENBYTE (0x%02x):\n", + FORBIDDENBYTE); + for (i = 0; i < SST; ++i) { + const uchar byte = tail[i]; + fprintf(stderr, " at tail+%d: 0x%02x", + i, byte); + if (byte != FORBIDDENBYTE) + fputs(" *** OUCH", stderr); + fputc('\n', stderr); + } + } + + serial = read_size_t(tail + SST); + fprintf(stderr, " The block was made by call #%" PY_FORMAT_SIZE_T + "u to debug malloc/realloc.\n", serial); + + if (nbytes > 0) { + i = 0; + fputs(" Data at p:", stderr); + /* print up to 8 bytes at the start */ + while (q < tail && i < 8) { + fprintf(stderr, " %02x", *q); + ++i; + ++q; + } + /* and up to 8 at the end */ + if (q < tail) { + if (tail - q > 8) { + fputs(" ...", stderr); + q = tail - 8; + } + while (q < tail) { + fprintf(stderr, " %02x", *q); + ++q; + } + } + fputc('\n', stderr); + } + fputc('\n', stderr); + + fflush(stderr); + _PyMem_DumpTraceback(fileno(stderr), p); +} + + +static size_t +printone(FILE *out, const char* msg, size_t value) +{ + int i, k; + char buf[100]; + size_t origvalue = value; + + fputs(msg, out); + for (i = (int)strlen(msg); i < 35; ++i) + fputc(' ', out); + fputc('=', out); + + /* Write the value with commas. */ + i = 22; + buf[i--] = '\0'; + buf[i--] = '\n'; + k = 3; + do { + size_t nextvalue = value / 10; + unsigned int digit = (unsigned int)(value - nextvalue * 10); + value = nextvalue; + buf[i--] = (char)(digit + '0'); + --k; + if (k == 0 && value && i >= 0) { + k = 3; + buf[i--] = ','; + } + } while (value && i >= 0); + + while (i >= 0) + buf[i--] = ' '; + fputs(buf, out); + + return origvalue; +} + +void +_PyDebugAllocatorStats(FILE *out, + const char *block_name, int num_blocks, size_t sizeof_block) +{ + char buf1[128]; + char buf2[128]; + PyOS_snprintf(buf1, sizeof(buf1), + "%d %ss * %" PY_FORMAT_SIZE_T "d bytes each", + num_blocks, block_name, sizeof_block); + PyOS_snprintf(buf2, sizeof(buf2), + "%48s ", buf1); + (void)printone(out, buf2, num_blocks * sizeof_block); +} + + +#ifdef WITH_PYMALLOC + +#ifdef Py_DEBUG +/* Is target in the list? The list is traversed via the nextpool pointers. + * The list may be NULL-terminated, or circular. Return 1 if target is in + * list, else 0. + */ +static int +pool_is_in_list(const poolp target, poolp list) +{ + poolp origlist = list; + assert(target != NULL); + if (list == NULL) + return 0; + do { + if (target == list) + return 1; + list = list->nextpool; + } while (list != NULL && list != origlist); + return 0; +} +#endif + +/* Print summary info to "out" about the state of pymalloc's structures. + * In Py_DEBUG mode, also perform some expensive internal consistency + * checks. + */ +void +_PyObject_DebugMallocStats(FILE *out) +{ + uint i; + const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT; + /* # of pools, allocated blocks, and free blocks per class index */ + size_t numpools[SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT]; + size_t numblocks[SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT]; + size_t numfreeblocks[SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT]; + /* total # of allocated bytes in used and full pools */ + size_t allocated_bytes = 0; + /* total # of available bytes in used pools */ + size_t available_bytes = 0; + /* # of free pools + pools not yet carved out of current arena */ + uint numfreepools = 0; + /* # of bytes for arena alignment padding */ + size_t arena_alignment = 0; + /* # of bytes in used and full pools used for pool_headers */ + size_t pool_header_bytes = 0; + /* # of bytes in used and full pools wasted due to quantization, + * i.e. the necessarily leftover space at the ends of used and + * full pools. + */ + size_t quantization = 0; + /* # of arenas actually allocated. */ + size_t narenas = 0; + /* running total -- should equal narenas * ARENA_SIZE */ + size_t total; + char buf[128]; + + fprintf(out, "Small block threshold = %d, in %u size classes.\n", + SMALL_REQUEST_THRESHOLD, numclasses); + + for (i = 0; i < numclasses; ++i) + numpools[i] = numblocks[i] = numfreeblocks[i] = 0; + + /* Because full pools aren't linked to from anything, it's easiest + * to march over all the arenas. If we're lucky, most of the memory + * will be living in full pools -- would be a shame to miss them. + */ + for (i = 0; i < maxarenas; ++i) { + uint j; + uptr base = arenas[i].address; + + /* Skip arenas which are not allocated. */ + if (arenas[i].address == (uptr)NULL) + continue; + narenas += 1; + + numfreepools += arenas[i].nfreepools; + + /* round up to pool alignment */ + if (base & (uptr)POOL_SIZE_MASK) { + arena_alignment += POOL_SIZE; + base &= ~(uptr)POOL_SIZE_MASK; + base += POOL_SIZE; + } + + /* visit every pool in the arena */ + assert(base <= (uptr) arenas[i].pool_address); + for (j = 0; + base < (uptr) arenas[i].pool_address; + ++j, base += POOL_SIZE) { + poolp p = (poolp)base; + const uint sz = p->szidx; + uint freeblocks; + + if (p->ref.count == 0) { + /* currently unused */ +#ifdef Py_DEBUG + assert(pool_is_in_list(p, arenas[i].freepools)); +#endif + continue; + } + ++numpools[sz]; + numblocks[sz] += p->ref.count; + freeblocks = NUMBLOCKS(sz) - p->ref.count; + numfreeblocks[sz] += freeblocks; +#ifdef Py_DEBUG + if (freeblocks > 0) + assert(pool_is_in_list(p, usedpools[sz + sz])); +#endif + } + } + assert(narenas == narenas_currently_allocated); + + fputc('\n', out); + fputs("class size num pools blocks in use avail blocks\n" + "----- ---- --------- ------------- ------------\n", + out); + + for (i = 0; i < numclasses; ++i) { + size_t p = numpools[i]; + size_t b = numblocks[i]; + size_t f = numfreeblocks[i]; + uint size = INDEX2SIZE(i); + if (p == 0) { + assert(b == 0 && f == 0); + continue; + } + fprintf(out, "%5u %6u " + "%11" PY_FORMAT_SIZE_T "u " + "%15" PY_FORMAT_SIZE_T "u " + "%13" PY_FORMAT_SIZE_T "u\n", + i, size, p, b, f); + allocated_bytes += b * size; + available_bytes += f * size; + pool_header_bytes += p * POOL_OVERHEAD; + quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size); + } + fputc('\n', out); + if (_PyMem_DebugEnabled()) + (void)printone(out, "# times object malloc called", serialno); + (void)printone(out, "# arenas allocated total", ntimes_arena_allocated); + (void)printone(out, "# arenas reclaimed", ntimes_arena_allocated - narenas); + (void)printone(out, "# arenas highwater mark", narenas_highwater); + (void)printone(out, "# arenas allocated current", narenas); + + PyOS_snprintf(buf, sizeof(buf), + "%" PY_FORMAT_SIZE_T "u arenas * %d bytes/arena", + narenas, ARENA_SIZE); + (void)printone(out, buf, narenas * ARENA_SIZE); + + fputc('\n', out); + + total = printone(out, "# bytes in allocated blocks", allocated_bytes); + total += printone(out, "# bytes in available blocks", available_bytes); + + PyOS_snprintf(buf, sizeof(buf), + "%u unused pools * %d bytes", numfreepools, POOL_SIZE); + total += printone(out, buf, (size_t)numfreepools * POOL_SIZE); + + total += printone(out, "# bytes lost to pool headers", pool_header_bytes); + total += printone(out, "# bytes lost to quantization", quantization); + total += printone(out, "# bytes lost to arena alignment", arena_alignment); + (void)printone(out, "Total", total); +} + +#endif /* #ifdef WITH_PYMALLOC */ + + +#ifdef Py_USING_MEMORY_DEBUGGER +/* Make this function last so gcc won't inline it since the definition is + * after the reference. + */ +int +Py_ADDRESS_IN_RANGE(void *P, poolp pool) +{ + uint arenaindex_temp = pool->arenaindex; + + return arenaindex_temp < maxarenas && + (uptr)P - arenas[arenaindex_temp].address < (uptr)ARENA_SIZE && + arenas[arenaindex_temp].address != 0; +} +#endif diff --git a/src/main/resource/testFiles/cpython/revFiles/1fc238a_a33099_Objects#dictobject.c b/src/main/resource/testFiles/cpython/revFiles/1fc238a_a33099_Objects#dictobject.c new file mode 100644 index 0000000..527b062 --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/1fc238a_a33099_Objects#dictobject.c @@ -0,0 +1,788 @@ +/*********************************************************** +Copyright 1991, 1992, 1993 by Stichting Mathematisch Centrum, +Amsterdam, The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Mapping object implementation; using a hash table */ + +/* This file should really be called "dictobject.c", since "mapping" + is the generic name for objects with an unorderred arbitrary key + set (just like lists are sequences), but since it improves (and was + originally derived from) a file by that name I had to change its + name. For the user these objects are still called "dictionaries". */ + +#include "allobjects.h" +#include "modsupport.h" + + +/* +Table of primes suitable as keys, in ascending order. +The first line are the largest primes less than some powers of two, +the second line is the largest prime less than 6000, +and the third line is a selection from Knuth, Vol. 3, Sec. 6.1, Table 1. +The final value is a sentinel and should cause the memory allocation +of that many entries to fail (if none of the earlier values cause such +failure already). +*/ +static unsigned int primes[] = { + 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2017, 4093, + 5987, + 9551, 15683, 19609, 31397, + 0xffffffff /* All bits set -- truncation OK */ +}; + +/* Object used as dummy key to fill deleted entries */ +static object *dummy; /* Initialized by first call to newmappingobject() */ + +/* +Invariant for entries: when in use, de_value is not NULL and de_key is +not NULL and not dummy; when not in use, de_value is NULL and de_key +is either NULL or dummy. A dummy key value cannot be replaced by +NULL, since otherwise other keys may be lost. +*/ +typedef struct { + long me_hash; + object *me_key; + object *me_value; +} mappingentry; + +/* +To ensure the lookup algorithm terminates, the table size must be a +prime number and there must be at least one NULL key in the table. +The value ma_fill is the number of non-NULL keys; ma_used is the number +of non-NULL, non-dummy keys. +To avoid slowing down lookups on a near-full table, we resize the table +when it is more than half filled. +*/ +typedef struct { + OB_HEAD + int ma_fill; + int ma_used; + int ma_size; + mappingentry *ma_table; +} mappingobject; + +object * +newmappingobject() +{ + register mappingobject *mp; + if (dummy == NULL) { /* Auto-initialize dummy */ + dummy = newstringobject(""); + if (dummy == NULL) + return NULL; + } + mp = NEWOBJ(mappingobject, &Mappingtype); + if (mp == NULL) + return NULL; + mp->ma_size = primes[0]; + mp->ma_table = (mappingentry *) calloc(sizeof(mappingentry), mp->ma_size); + if (mp->ma_table == NULL) { + DEL(mp); + return err_nomem(); + } + mp->ma_fill = 0; + mp->ma_used = 0; + return (object *)mp; +} + +/* +The basic lookup function used by all operations. +This is essentially Algorithm D from Knuth Vol. 3, Sec. 6.4. +Open addressing is preferred over chaining since the link overhead for +chaining would be substantial (100% with typical malloc overhead). + +First a 32-bit hash value, 'sum', is computed from the key string. +The first character is added an extra time shifted by 8 to avoid hashing +single-character keys (often heavily used variables) too close together. +All arithmetic on sum should ignore overflow. + +The initial probe index is then computed as sum mod the table size. +Subsequent probe indices are incr apart (mod table size), where incr +is also derived from sum, with the additional requirement that it is +relative prime to the table size (i.e., 1 <= incr < size, since the size +is a prime number). My choice for incr is somewhat arbitrary. +*/ +static mappingentry *lookmapping PROTO((mappingobject *, object *, long)); +static mappingentry * +lookmapping(mp, key, hash) + register mappingobject *mp; + object *key; + long hash; +{ + register int i, incr; + register unsigned long sum = (unsigned long) hash; + register mappingentry *freeslot = NULL; + /* We must come up with (i, incr) such that 0 <= i < ma_size + and 0 < incr < ma_size and both are a function of hash */ + i = sum % mp->ma_size; + do { + sum = sum + sum + sum + 1; + incr = sum % mp->ma_size; + } while (incr == 0); + for (;;) { + register mappingentry *ep = &mp->ma_table[i]; + if (ep->me_key == NULL) { + if (freeslot != NULL) + return freeslot; + else + return ep; + } + if (ep->me_key == dummy) { + if (freeslot != NULL) + freeslot = ep; + } + else if (ep->me_hash == hash && + cmpobject(ep->me_key, key) == 0) { + return ep; + } + i = (i + incr) % mp->ma_size; + } +} + +/* +Internal routine to insert a new item into the table. +Used both by the internal resize routine and by the public insert routine. +Eats a reference to key and one to value. +*/ +static void insertmapping PROTO((mappingobject *, object *, long, object *)); +static void +insertmapping(mp, key, hash, value) + register mappingobject *mp; + object *key; + long hash; + object *value; +{ + register mappingentry *ep; + ep = lookmapping(mp, key, hash); + if (ep->me_value != NULL) { + DECREF(ep->me_value); + DECREF(key); + } + else { + if (ep->me_key == NULL) + mp->ma_fill++; + else + DECREF(ep->me_key); + ep->me_key = key; + ep->me_hash = hash; + mp->ma_used++; + } + ep->me_value = value; +} + +/* +Restructure the table by allocating a new table and reinserting all +items again. When entries have been deleted, the new table may +actually be smaller than the old one. +*/ +static int mappingresize PROTO((mappingobject *)); +static int +mappingresize(mp) + mappingobject *mp; +{ + register int oldsize = mp->ma_size; + register int newsize; + register mappingentry *oldtable = mp->ma_table; + register mappingentry *newtable; + register mappingentry *ep; + register int i; + newsize = mp->ma_size; + for (i = 0; ; i++) { + if (primes[i] > mp->ma_used*2) { + newsize = primes[i]; + break; + } + } + newtable = (mappingentry *) calloc(sizeof(mappingentry), newsize); + if (newtable == NULL) { + err_nomem(); + return -1; + } + mp->ma_size = newsize; + mp->ma_table = newtable; + mp->ma_fill = 0; + mp->ma_used = 0; + for (i = 0, ep = oldtable; i < oldsize; i++, ep++) { + if (ep->me_value != NULL) + insertmapping(mp,ep->me_key,ep->me_hash,ep->me_value); + else { + XDECREF(ep->me_key); + } + } + DEL(oldtable); + return 0; +} + +object * +mappinglookup(op, key) + object *op; + object *key; +{ + long hash; + if (!is_mappingobject(op)) { + err_badcall(); + return NULL; + } + hash = hashobject(key); + if (hash == -1) + return NULL; + return lookmapping((mappingobject *)op, key, hash) -> me_value; +} + +int +mappinginsert(op, key, value) + register object *op; + object *key; + object *value; +{ + register mappingobject *mp; + register long hash; + if (!is_mappingobject(op)) { + err_badcall(); + return -1; + } + hash = hashobject(key); + if (hash == -1) + return -1; + mp = (mappingobject *)op; + /* if fill >= 2/3 size, resize */ + if (mp->ma_fill*3 >= mp->ma_size*2) { + if (mappingresize(mp) != 0) { + if (mp->ma_fill+1 > mp->ma_size) + return -1; + } + } + INCREF(value); + INCREF(key); + insertmapping(mp, key, hash, value); + return 0; +} + +int +mappingremove(op, key) + object *op; + object *key; +{ + register mappingobject *mp; + register long hash; + register mappingentry *ep; + if (!is_mappingobject(op)) { + err_badcall(); + return -1; + } + hash = hashobject(key); + if (hash == -1) + return -1; + mp = (mappingobject *)op; + ep = lookmapping(mp, key, hash); + if (ep->me_value == NULL) { + err_setval(KeyError, key); + return -1; + } + DECREF(ep->me_key); + INCREF(dummy); + ep->me_key = dummy; + DECREF(ep->me_value); + ep->me_value = NULL; + mp->ma_used--; + return 0; +} + +void +mappingclear(op) + object *op; +{ + int i; + register mappingobject *mp; + if (!is_mappingobject(op)) + return; + mp = (mappingobject *)op; + for (i = 0; i < mp->ma_size; i++) { + XDECREF(mp->ma_table[i].me_key); + XDECREF(mp->ma_table[i].me_value); + mp->ma_table[i].me_key = NULL; + mp->ma_table[i].me_value = NULL; + } + mp->ma_used = 0; +} + +int +mappinggetnext(op, ppos, pkey, pvalue) + object *op; + int *ppos; + object **pkey; + object **pvalue; +{ + int i; + register mappingobject *mp; + if (!is_dictobject(op)) + return 0; + mp = (mappingobject *)op; + i = *ppos; + if (i < 0) + return 0; + while (i < mp->ma_size && mp->ma_table[i].me_value == NULL) + i++; + *ppos = i+1; + if (i >= mp->ma_size) + return 0; + if (pkey) + *pkey = mp->ma_table[i].me_key; + if (pvalue) + *pvalue = mp->ma_table[i].me_value; + return 1; +} + +/* Methods */ + +static void +mapping_dealloc(mp) + register mappingobject *mp; +{ + register int i; + register mappingentry *ep; + for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) { + if (ep->me_key != NULL) + DECREF(ep->me_key); + if (ep->me_value != NULL) + DECREF(ep->me_value); + } + if (mp->ma_table != NULL) + DEL(mp->ma_table); + DEL(mp); +} + +static int +mapping_print(mp, fp, flags) + register mappingobject *mp; + register FILE *fp; + register int flags; +{ + register int i; + register int any; + register mappingentry *ep; + fprintf(fp, "{"); + any = 0; + for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) { + if (ep->me_value != NULL) { + if (any++ > 0) + fprintf(fp, ", "); + if (printobject((object *)ep->me_key, fp, flags) != 0) + return -1; + fprintf(fp, ": "); + if (printobject(ep->me_value, fp, flags) != 0) + return -1; + } + } + fprintf(fp, "}"); + return 0; +} + +static void +js(pv, w) + object **pv; + object *w; +{ + joinstring(pv, w); + XDECREF(w); +} + +static object * +mapping_repr(mp) + mappingobject *mp; +{ + auto object *v; + object *sepa, *colon; + register int i; + register int any; + register mappingentry *ep; + v = newstringobject("{"); + sepa = newstringobject(", "); + colon = newstringobject(": "); + any = 0; + for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) { + if (ep->me_value != NULL) { + if (any++) + joinstring(&v, sepa); + js(&v, reprobject(ep->me_key)); + joinstring(&v, colon); + js(&v, reprobject(ep->me_value)); + } + } + js(&v, newstringobject("}")); + XDECREF(sepa); + XDECREF(colon); + return v; +} + +static int +mapping_length(mp) + mappingobject *mp; +{ + return mp->ma_used; +} + +static object * +mapping_subscript(mp, key) + mappingobject *mp; + register object *key; +{ + object *v; + long hash = hashobject(key); + if (hash == -1) + return NULL; + v = lookmapping(mp, key, hash) -> me_value; + if (v == NULL) + err_setval(KeyError, key); + else + INCREF(v); + return v; +} + +static int +mapping_ass_sub(mp, v, w) + mappingobject *mp; + object *v, *w; +{ + if (w == NULL) + return mappingremove((object *)mp, v); + else + return mappinginsert((object *)mp, v, w); +} + +static mapping_methods mapping_as_mapping = { + mapping_length, /*mp_length*/ + mapping_subscript, /*mp_subscript*/ + mapping_ass_sub, /*mp_ass_subscript*/ +}; + +static object * +mapping_keys(mp, args) + register mappingobject *mp; + object *args; +{ + register object *v; + register int i, j; + if (!getnoarg(args)) + return NULL; + v = newlistobject(mp->ma_used); + if (v == NULL) + return NULL; + for (i = 0, j = 0; i < mp->ma_size; i++) { + if (mp->ma_table[i].me_value != NULL) { + object *key = mp->ma_table[i].me_key; + INCREF(key); + setlistitem(v, j, key); + j++; + } + } + return v; +} + +static object * +mapping_values(mp, args) + register mappingobject *mp; + object *args; +{ + register object *v; + register int i, j; + if (!getnoarg(args)) + return NULL; + v = newlistobject(mp->ma_used); + if (v == NULL) + return NULL; + for (i = 0, j = 0; i < mp->ma_size; i++) { + if (mp->ma_table[i].me_value != NULL) { + object *value = mp->ma_table[i].me_value; + INCREF(value); + setlistitem(v, j, value); + j++; + } + } + return v; +} + +static object * +mapping_items(mp, args) + register mappingobject *mp; + object *args; +{ + register object *v; + register int i, j; + if (!getnoarg(args)) + return NULL; + v = newlistobject(mp->ma_used); + if (v == NULL) + return NULL; + for (i = 0, j = 0; i < mp->ma_size; i++) { + if (mp->ma_table[i].me_value != NULL) { + object *key = mp->ma_table[i].me_key; + object *value = mp->ma_table[i].me_value; + object *item = newtupleobject(2); + if (item == NULL) { + DECREF(v); + return NULL; + } + INCREF(key); + settupleitem(item, 0, key); + INCREF(value); + settupleitem(item, 1, value); + setlistitem(v, j, item); + j++; + } + } + return v; +} + +object * +getmappingkeys(mp) + object *mp; +{ + if (mp == NULL || !is_mappingobject(mp)) { + err_badcall(); + return NULL; + } + return mapping_keys((mappingobject *)mp, (object *)NULL); +} + +object * +getmappingvalues(mp) + object *mp; +{ + if (mp == NULL || !is_mappingobject(mp)) { + err_badcall(); + return NULL; + } + return mapping_values((mappingobject *)mp, (object *)NULL); +} + +object * +getmappingitems(mp) + object *mp; +{ + if (mp == NULL || !is_mappingobject(mp)) { + err_badcall(); + return NULL; + } + return mapping_values((mappingobject *)mp, (object *)NULL); +} + +static int +mapping_compare(a, b) + mappingobject *a, *b; +{ + object *akeys, *bkeys; + int i, n, res; + if (a == b) + return 0; + if (a->ma_used == 0) { + if (b->ma_used != 0) + return -1; + else + return 0; + } + else { + if (b->ma_used == 0) + return 1; + } + akeys = mapping_keys(a, (object *)NULL); + bkeys = mapping_keys(b, (object *)NULL); + if (akeys == NULL || bkeys == NULL) { + /* Oops, out of memory -- what to do? */ + /* For now, sort on address! */ + XDECREF(akeys); + XDECREF(bkeys); + if (a < b) + return -1; + else + return 1; + } + sortlist(akeys); + sortlist(bkeys); + n = a->ma_used < b->ma_used ? a->ma_used : b->ma_used; /* smallest */ + res = 0; + for (i = 0; i < n; i++) { + object *akey, *bkey, *aval, *bval; + long ahash, bhash; + akey = getlistitem(akeys, i); + bkey = getlistitem(bkeys, i); + res = cmpobject(akey, bkey); + if (res != 0) + break; + ahash = hashobject(akey); + if (ahash == -1) + err_clear(); /* Don't want errors here */ + bhash = hashobject(bkey); + if (bhash == -1) + err_clear(); /* Don't want errors here */ + aval = lookmapping(a, akey, ahash) -> me_value; + bval = lookmapping(b, bkey, bhash) -> me_value; + res = cmpobject(aval, bval); + if (res != 0) + break; + } + if (res == 0) { + if (a->ma_used < b->ma_used) + res = -1; + else if (a->ma_used > b->ma_used) + res = 1; + } + DECREF(akeys); + DECREF(bkeys); + return res; +} + +static object * +mapping_has_key(mp, args) + register mappingobject *mp; + object *args; +{ + object *key; + long hash; + register long ok; + if (!getargs(args, "O", &key)) + return NULL; + hash = hashobject(key); + if (hash == -1) + return NULL; + ok = lookmapping(mp, key, hash)->me_value != NULL; + return newintobject(ok); +} + +static struct methodlist mapp_methods[] = { + {"has_key", mapping_has_key}, + {"items", mapping_items}, + {"keys", mapping_keys}, + {"values", mapping_values}, + {NULL, NULL} /* sentinel */ +}; + +static object * +mapping_getattr(mp, name) + mappingobject *mp; + char *name; +{ + return findmethod(mapp_methods, (object *)mp, name); +} + +typeobject Mappingtype = { + OB_HEAD_INIT(&Typetype) + 0, + "dictionary", + sizeof(mappingobject), + 0, + mapping_dealloc, /*tp_dealloc*/ + mapping_print, /*tp_print*/ + mapping_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + mapping_compare, /*tp_compare*/ + mapping_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &mapping_as_mapping, /*tp_as_mapping*/ +}; + +/* For backward compatibility with old dictionary interface */ + +static object *last_name_object; +static char *last_name_char; + +object * +getattro(v, name) + object *v; + object *name; +{ + if (name != last_name_object) { + XDECREF(last_name_object); + INCREF(name); + last_name_object = name; + last_name_char = getstringvalue(name); + } + return getattr(v, last_name_char); +} + +int +setattro(v, name, value) + object *v; + object *name; + object *value; +{ + if (name != last_name_object) { + XDECREF(last_name_object); + INCREF(name); + last_name_object = name; + last_name_char = getstringvalue(name); + } + return setattr(v, last_name_char, value); +} + +object * +dictlookup(v, key) + object *v; + char *key; +{ + if (key != last_name_char) { + XDECREF(last_name_object); + last_name_object = newstringobject(key); + if (last_name_object == NULL) { + last_name_char = NULL; + return NULL; + } + last_name_char = key; + } + return mappinglookup(v, last_name_object); +} + +int +dictinsert(v, key, item) + object *v; + char *key; + object *item; +{ + if (key != last_name_char) { + XDECREF(last_name_object); + last_name_object = newstringobject(key); + if (last_name_object == NULL) { + last_name_char = NULL; + return -1; + } + last_name_char = key; + } + return mappinginsert(v, last_name_object, item); +} + +int +dictremove(v, key) + object *v; + char *key; +{ + if (key != last_name_char) { + XDECREF(last_name_object); + last_name_object = newstringobject(key); + if (last_name_object == NULL) { + last_name_char = NULL; + return -1; + } + last_name_char = key; + } + return mappingremove(v, last_name_object); +} diff --git a/src/main/resource/testFiles/cpython/revFiles/516999_437567_Modules#posixmodule.c b/src/main/resource/testFiles/cpython/revFiles/516999_437567_Modules#posixmodule.c new file mode 100644 index 0000000..f982275 --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/516999_437567_Modules#posixmodule.c @@ -0,0 +1,8164 @@ + +/* POSIX module implementation */ + +/* This file is also used for Windows NT/MS-Win and OS/2. In that case the + module actually calls itself 'nt' or 'os2', not 'posix', and a few + functions are either unimplemented or implemented differently. The source + assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent + of the compiler used. Different compilers define their own feature + test macro, e.g. '__BORLANDC__' or '_MSC_VER'. For OS/2, the compiler + independent macro PYOS_OS2 should be defined. On OS/2 the default + compiler is assumed to be IBM's VisualAge C++ (VACPP). PYCC_GCC is used + as the compiler specific macro for the EMX port of gcc to OS/2. */ + +/* See also ../Dos/dosmodule.c */ + +#define PY_SSIZE_T_CLEAN + +#include "Python.h" +#include "structseq.h" + +#if defined(__VMS) +# include +#endif /* defined(__VMS) */ + +PyDoc_STRVAR(posix__doc__, +"This module provides access to operating system functionality that is\n\ +standardized by the C Standard and the POSIX standard (a thinly\n\ +disguised Unix interface). Refer to the library manual and\n\ +corresponding Unix manual entries for more information on calls."); + +#ifndef Py_USING_UNICODE +/* This is used in signatures of functions. */ +#define Py_UNICODE void +#endif + +#if defined(PYOS_OS2) +#define INCL_DOS +#define INCL_DOSERRORS +#define INCL_DOSPROCESS +#define INCL_NOPMAPI +#include +#if defined(PYCC_GCC) +#include +#include +#include +#include +#endif +#include "osdefs.h" +#endif + +#include +#include + +#ifdef HAVE_SYS_WAIT_H +#include /* For WNOHANG */ +#endif + +#include + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#ifdef HAVE_GRP_H +#include +#endif + +#ifdef HAVE_SYSEXITS_H +#include +#endif /* HAVE_SYSEXITS_H */ + +#ifdef HAVE_SYS_LOADAVG_H +#include +#endif + +/* Various compilers have only certain posix functions */ +/* XXX Gosh I wish these were all moved into pyconfig.h */ +#if defined(PYCC_VACPP) && defined(PYOS_OS2) +#include +#else +#if defined(__WATCOMC__) && !defined(__QNX__) /* Watcom compiler */ +#define HAVE_GETCWD 1 +#define HAVE_OPENDIR 1 +#define HAVE_SYSTEM 1 +#if defined(__OS2__) +#define HAVE_EXECV 1 +#define HAVE_WAIT 1 +#endif +#include +#else +#ifdef __BORLANDC__ /* Borland compiler */ +#define HAVE_EXECV 1 +#define HAVE_GETCWD 1 +#define HAVE_OPENDIR 1 +#define HAVE_PIPE 1 +#define HAVE_POPEN 1 +#define HAVE_SYSTEM 1 +#define HAVE_WAIT 1 +#else +#ifdef _MSC_VER /* Microsoft compiler */ +#define HAVE_GETCWD 1 +#define HAVE_SPAWNV 1 +#define HAVE_EXECV 1 +#define HAVE_PIPE 1 +#define HAVE_POPEN 1 +#define HAVE_SYSTEM 1 +#define HAVE_CWAIT 1 +#define HAVE_FSYNC 1 +#define fsync _commit +#else +#if defined(PYOS_OS2) && defined(PYCC_GCC) || defined(__VMS) +/* Everything needed is defined in PC/os2emx/pyconfig.h or vms/pyconfig.h */ +#else /* all other compilers */ +/* Unix functions that the configure script doesn't check for */ +#define HAVE_EXECV 1 +#define HAVE_FORK 1 +#if defined(__USLC__) && defined(__SCO_VERSION__) /* SCO UDK Compiler */ +#define HAVE_FORK1 1 +#endif +#define HAVE_GETCWD 1 +#define HAVE_GETEGID 1 +#define HAVE_GETEUID 1 +#define HAVE_GETGID 1 +#define HAVE_GETPPID 1 +#define HAVE_GETUID 1 +#define HAVE_KILL 1 +#define HAVE_OPENDIR 1 +#define HAVE_PIPE 1 +#ifndef __rtems__ +#define HAVE_POPEN 1 +#endif +#define HAVE_SYSTEM 1 +#define HAVE_WAIT 1 +#define HAVE_TTYNAME 1 +#endif /* PYOS_OS2 && PYCC_GCC && __VMS */ +#endif /* _MSC_VER */ +#endif /* __BORLANDC__ */ +#endif /* ! __WATCOMC__ || __QNX__ */ +#endif /* ! __IBMC__ */ + +#ifndef _MSC_VER + +#if defined(__sgi)&&_COMPILER_VERSION>=700 +/* declare ctermid_r if compiling with MIPSPro 7.x in ANSI C mode + (default) */ +extern char *ctermid_r(char *); +#endif + +#ifndef HAVE_UNISTD_H +#if defined(PYCC_VACPP) +extern int mkdir(char *); +#else +#if ( defined(__WATCOMC__) || defined(_MSC_VER) ) && !defined(__QNX__) +extern int mkdir(const char *); +#else +extern int mkdir(const char *, mode_t); +#endif +#endif +#if defined(__IBMC__) || defined(__IBMCPP__) +extern int chdir(char *); +extern int rmdir(char *); +#else +extern int chdir(const char *); +extern int rmdir(const char *); +#endif +#ifdef __BORLANDC__ +extern int chmod(const char *, int); +#else +extern int chmod(const char *, mode_t); +#endif +extern int chown(const char *, uid_t, gid_t); +extern char *getcwd(char *, int); +extern char *strerror(int); +extern int link(const char *, const char *); +extern int rename(const char *, const char *); +extern int stat(const char *, struct stat *); +extern int unlink(const char *); +extern int pclose(FILE *); +#ifdef HAVE_SYMLINK +extern int symlink(const char *, const char *); +#endif /* HAVE_SYMLINK */ +#ifdef HAVE_LSTAT +extern int lstat(const char *, struct stat *); +#endif /* HAVE_LSTAT */ +#endif /* !HAVE_UNISTD_H */ + +#endif /* !_MSC_VER */ + +#ifdef HAVE_UTIME_H +#include +#endif /* HAVE_UTIME_H */ + +#ifdef HAVE_SYS_UTIME_H +#include +#define HAVE_UTIME_H /* pretend we do for the rest of this file */ +#endif /* HAVE_SYS_UTIME_H */ + +#ifdef HAVE_SYS_TIMES_H +#include +#endif /* HAVE_SYS_TIMES_H */ + +#ifdef HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#ifdef HAVE_SYS_UTSNAME_H +#include +#endif /* HAVE_SYS_UTSNAME_H */ + +#ifdef HAVE_DIRENT_H +#include +#define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#if defined(__WATCOMC__) && !defined(__QNX__) +#include +#define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#define dirent direct +#define NAMLEN(dirent) (dirent)->d_namlen +#endif +#ifdef HAVE_SYS_NDIR_H +#include +#endif +#ifdef HAVE_SYS_DIR_H +#include +#endif +#ifdef HAVE_NDIR_H +#include +#endif +#endif + +#ifdef _MSC_VER +#include +#include +#include +#include "osdefs.h" +#define _WIN32_WINNT 0x0400 /* Needed for CryptoAPI on some systems */ +#include +#include /* for ShellExecute() */ +#define popen _popen +#define pclose _pclose +#endif /* _MSC_VER */ + +#if defined(PYCC_VACPP) && defined(PYOS_OS2) +#include +#endif /* OS2 */ + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif /* MAXPATHLEN */ + +#ifdef UNION_WAIT +/* Emulate some macros on systems that have a union instead of macros */ + +#ifndef WIFEXITED +#define WIFEXITED(u_wait) (!(u_wait).w_termsig && !(u_wait).w_coredump) +#endif + +#ifndef WEXITSTATUS +#define WEXITSTATUS(u_wait) (WIFEXITED(u_wait)?((u_wait).w_retcode):-1) +#endif + +#ifndef WTERMSIG +#define WTERMSIG(u_wait) ((u_wait).w_termsig) +#endif + +#endif /* UNION_WAIT */ + +/* Don't use the "_r" form if we don't need it (also, won't have a + prototype for it, at least on Solaris -- maybe others as well?). */ +#if defined(HAVE_CTERMID_R) && defined(WITH_THREAD) +#define USE_CTERMID_R +#endif + +#if defined(HAVE_TMPNAM_R) && defined(WITH_THREAD) +#define USE_TMPNAM_R +#endif + +/* choose the appropriate stat and fstat functions and return structs */ +#undef STAT +#if defined(MS_WIN64) || defined(MS_WINDOWS) +# define STAT win32_stat +# define FSTAT win32_fstat +# define STRUCT_STAT struct win32_stat +#else +# define STAT stat +# define FSTAT fstat +# define STRUCT_STAT struct stat +#endif + +#if defined(MAJOR_IN_MKDEV) +#include +#else +#if defined(MAJOR_IN_SYSMACROS) +#include +#endif +#if defined(HAVE_MKNOD) && defined(HAVE_SYS_MKDEV_H) +#include +#endif +#endif + +/* Return a dictionary corresponding to the POSIX environment table */ +#ifdef WITH_NEXT_FRAMEWORK +/* On Darwin/MacOSX a shared library or framework has no access to +** environ directly, we must obtain it with _NSGetEnviron(). +*/ +#include +static char **environ; +#elif !defined(_MSC_VER) && ( !defined(__WATCOMC__) || defined(__QNX__) ) +extern char **environ; +#endif /* !_MSC_VER */ + +static PyObject * +convertenviron(void) +{ + PyObject *d; + char **e; + d = PyDict_New(); + if (d == NULL) + return NULL; +#ifdef WITH_NEXT_FRAMEWORK + if (environ == NULL) + environ = *_NSGetEnviron(); +#endif + if (environ == NULL) + return d; + /* This part ignores errors */ + for (e = environ; *e != NULL; e++) { + PyObject *k; + PyObject *v; + char *p = strchr(*e, '='); + if (p == NULL) + continue; + k = PyString_FromStringAndSize(*e, (int)(p-*e)); + if (k == NULL) { + PyErr_Clear(); + continue; + } + v = PyString_FromString(p+1); + if (v == NULL) { + PyErr_Clear(); + Py_DECREF(k); + continue; + } + if (PyDict_GetItem(d, k) == NULL) { + if (PyDict_SetItem(d, k, v) != 0) + PyErr_Clear(); + } + Py_DECREF(k); + Py_DECREF(v); + } +#if defined(PYOS_OS2) + { + APIRET rc; + char buffer[1024]; /* OS/2 Provides a Documented Max of 1024 Chars */ + + rc = DosQueryExtLIBPATH(buffer, BEGIN_LIBPATH); + if (rc == NO_ERROR) { /* (not a type, envname is NOT 'BEGIN_LIBPATH') */ + PyObject *v = PyString_FromString(buffer); + PyDict_SetItemString(d, "BEGINLIBPATH", v); + Py_DECREF(v); + } + rc = DosQueryExtLIBPATH(buffer, END_LIBPATH); + if (rc == NO_ERROR) { /* (not a typo, envname is NOT 'END_LIBPATH') */ + PyObject *v = PyString_FromString(buffer); + PyDict_SetItemString(d, "ENDLIBPATH", v); + Py_DECREF(v); + } + } +#endif + return d; +} + + +/* Set a POSIX-specific error from errno, and return NULL */ + +static PyObject * +posix_error(void) +{ + return PyErr_SetFromErrno(PyExc_OSError); +} +static PyObject * +posix_error_with_filename(char* name) +{ + return PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); +} + +#ifdef Py_WIN_WIDE_FILENAMES +static PyObject * +posix_error_with_unicode_filename(Py_UNICODE* name) +{ + return PyErr_SetFromErrnoWithUnicodeFilename(PyExc_OSError, name); +} +#endif /* Py_WIN_WIDE_FILENAMES */ + + +static PyObject * +posix_error_with_allocated_filename(char* name) +{ + PyObject *rc = PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); + PyMem_Free(name); + return rc; +} + +#ifdef MS_WINDOWS +static PyObject * +win32_error(char* function, char* filename) +{ + /* XXX We should pass the function name along in the future. + (_winreg.c also wants to pass the function name.) + This would however require an additional param to the + Windows error object, which is non-trivial. + */ + errno = GetLastError(); + if (filename) + return PyErr_SetFromWindowsErrWithFilename(errno, filename); + else + return PyErr_SetFromWindowsErr(errno); +} + +#ifdef Py_WIN_WIDE_FILENAMES +static PyObject * +win32_error_unicode(char* function, Py_UNICODE* filename) +{ + /* XXX - see win32_error for comments on 'function' */ + errno = GetLastError(); + if (filename) + return PyErr_SetFromWindowsErrWithUnicodeFilename(errno, filename); + else + return PyErr_SetFromWindowsErr(errno); +} + +static PyObject *_PyUnicode_FromFileSystemEncodedObject(register PyObject *obj) +{ + /* XXX Perhaps we should make this API an alias of + PyObject_Unicode() instead ?! */ + if (PyUnicode_CheckExact(obj)) { + Py_INCREF(obj); + return obj; + } + if (PyUnicode_Check(obj)) { + /* For a Unicode subtype that's not a Unicode object, + return a true Unicode object with the same data. */ + return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(obj), + PyUnicode_GET_SIZE(obj)); + } + return PyUnicode_FromEncodedObject(obj, + Py_FileSystemDefaultEncoding, + "strict"); +} + +#endif /* Py_WIN_WIDE_FILENAMES */ + +#endif + +#if defined(PYOS_OS2) +/********************************************************************** + * Helper Function to Trim and Format OS/2 Messages + **********************************************************************/ + static void +os2_formatmsg(char *msgbuf, int msglen, char *reason) +{ + msgbuf[msglen] = '\0'; /* OS/2 Doesn't Guarantee a Terminator */ + + if (strlen(msgbuf) > 0) { /* If Non-Empty Msg, Trim CRLF */ + char *lastc = &msgbuf[ strlen(msgbuf)-1 ]; + + while (lastc > msgbuf && isspace(Py_CHARMASK(*lastc))) + *lastc-- = '\0'; /* Trim Trailing Whitespace (CRLF) */ + } + + /* Add Optional Reason Text */ + if (reason) { + strcat(msgbuf, " : "); + strcat(msgbuf, reason); + } +} + +/********************************************************************** + * Decode an OS/2 Operating System Error Code + * + * A convenience function to lookup an OS/2 error code and return a + * text message we can use to raise a Python exception. + * + * Notes: + * The messages for errors returned from the OS/2 kernel reside in + * the file OSO001.MSG in the \OS2 directory hierarchy. + * + **********************************************************************/ + static char * +os2_strerror(char *msgbuf, int msgbuflen, int errorcode, char *reason) +{ + APIRET rc; + ULONG msglen; + + /* Retrieve Kernel-Related Error Message from OSO001.MSG File */ + Py_BEGIN_ALLOW_THREADS + rc = DosGetMessage(NULL, 0, msgbuf, msgbuflen, + errorcode, "oso001.msg", &msglen); + Py_END_ALLOW_THREADS + + if (rc == NO_ERROR) + os2_formatmsg(msgbuf, msglen, reason); + else + PyOS_snprintf(msgbuf, msgbuflen, + "unknown OS error #%d", errorcode); + + return msgbuf; +} + +/* Set an OS/2-specific error and return NULL. OS/2 kernel + errors are not in a global variable e.g. 'errno' nor are + they congruent with posix error numbers. */ + +static PyObject * os2_error(int code) +{ + char text[1024]; + PyObject *v; + + os2_strerror(text, sizeof(text), code, ""); + + v = Py_BuildValue("(is)", code, text); + if (v != NULL) { + PyErr_SetObject(PyExc_OSError, v); + Py_DECREF(v); + } + return NULL; /* Signal to Python that an Exception is Pending */ +} + +#endif /* OS2 */ + +/* POSIX generic methods */ + +static PyObject * +posix_fildes(PyObject *fdobj, int (*func)(int)) +{ + int fd; + int res; + fd = PyObject_AsFileDescriptor(fdobj); + if (fd < 0) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = (*func)(fd); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef Py_WIN_WIDE_FILENAMES +static int +unicode_file_names(void) +{ + static int canusewide = -1; + if (canusewide == -1) { + /* As per doc for ::GetVersion(), this is the correct test for + the Windows NT family. */ + canusewide = (GetVersion() < 0x80000000) ? 1 : 0; + } + return canusewide; +} +#endif + +static PyObject * +posix_1str(PyObject *args, char *format, int (*func)(const char*), + char *wformat, int (*wfunc)(const Py_UNICODE*)) +{ + char *path1 = NULL; + int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, wformat, &po)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + res = (*wfunc)(PyUnicode_AS_UNICODE(po)); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_unicode_filename(PyUnicode_AS_UNICODE(po)); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow + strings are also valid. */ + PyErr_Clear(); + } +#else + /* Platforms that don't support Unicode filenames + shouldn't be passing these extra params */ + assert(wformat==NULL && wfunc == NULL); +#endif + + if (!PyArg_ParseTuple(args, format, + Py_FileSystemDefaultEncoding, &path1)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = (*func)(path1); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path1); + PyMem_Free(path1); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +posix_2str(PyObject *args, + char *format, + int (*func)(const char *, const char *), + char *wformat, + int (*wfunc)(const Py_UNICODE *, const Py_UNICODE *)) +{ + char *path1 = NULL, *path2 = NULL; + int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyObject *po1; + PyObject *po2; + if (PyArg_ParseTuple(args, wformat, &po1, &po2)) { + if (PyUnicode_Check(po1) || PyUnicode_Check(po2)) { + PyObject *wpath1; + PyObject *wpath2; + wpath1 = _PyUnicode_FromFileSystemEncodedObject(po1); + wpath2 = _PyUnicode_FromFileSystemEncodedObject(po2); + if (!wpath1 || !wpath2) { + Py_XDECREF(wpath1); + Py_XDECREF(wpath2); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + res = (*wfunc)(PyUnicode_AS_UNICODE(wpath1), + PyUnicode_AS_UNICODE(wpath2)); + Py_END_ALLOW_THREADS + Py_XDECREF(wpath1); + Py_XDECREF(wpath2); + if (res != 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; + } + /* Else flow through as neither is Unicode. */ + } + /* Drop the argument parsing error as narrow + strings are also valid. */ + PyErr_Clear(); + } +#else + /* Platforms that don't support Unicode filenames + shouldn't be passing these extra params */ + assert(wformat==NULL && wfunc == NULL); +#endif + + if (!PyArg_ParseTuple(args, format, + Py_FileSystemDefaultEncoding, &path1, + Py_FileSystemDefaultEncoding, &path2)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = (*func)(path1, path2); + Py_END_ALLOW_THREADS + PyMem_Free(path1); + PyMem_Free(path2); + if (res != 0) + /* XXX how to report both path1 and path2??? */ + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef MS_WINDOWS +/* The CRT of Windows has a number of flaws wrt. its stat() implementation: + - time stamps are restricted to second resolution + - file modification times suffer from forth-and-back conversions between + UTC and local time + Therefore, we implement our own stat, based on the Win32 API directly. +*/ +#define HAVE_STAT_NSEC 1 + +struct win32_stat{ + int st_dev; + __int64 st_ino; + unsigned short st_mode; + int st_nlink; + int st_uid; + int st_gid; + int st_rdev; + __int64 st_size; + int st_atime; + int st_atime_nsec; + int st_mtime; + int st_mtime_nsec; + int st_ctime; + int st_ctime_nsec; +}; + +static __int64 secs_between_epochs = 11644473600; /* Seconds between 1.1.1601 and 1.1.1970 */ + +static void +FILE_TIME_to_time_t_nsec(FILETIME *in_ptr, int *time_out, int* nsec_out) +{ + /* XXX endianness */ + __int64 in = *(__int64*)in_ptr; + *nsec_out = (int)(in % 10000000) * 100; /* FILETIME is in units of 100 nsec. */ + /* XXX Win32 supports time stamps past 2038; we currently don't */ + *time_out = Py_SAFE_DOWNCAST((in / 10000000) - secs_between_epochs, __int64, int); +} + +/* Below, we *know* that ugo+r is 0444 */ +#if _S_IREAD != 0400 +#error Unsupported C library +#endif +static int +attributes_to_mode(DWORD attr) +{ + int m = 0; + if (attr & FILE_ATTRIBUTE_DIRECTORY) + m |= _S_IFDIR | 0111; /* IFEXEC for user,group,other */ + else + m |= _S_IFREG; + if (attr & FILE_ATTRIBUTE_READONLY) + m |= 0444; + else + m |= 0666; + return m; +} + +static int +attribute_data_to_stat(WIN32_FILE_ATTRIBUTE_DATA *info, struct win32_stat *result) +{ + memset(result, 0, sizeof(*result)); + result->st_mode = attributes_to_mode(info->dwFileAttributes); + result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow; + FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_ctime, &result->st_ctime_nsec); + FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec); + FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec); + + return 0; +} + +static int +win32_stat(const char* path, struct win32_stat *result) +{ + WIN32_FILE_ATTRIBUTE_DATA info; + int code; + char *dot; + /* XXX not supported on Win95 and NT 3.x */ + if (!GetFileAttributesExA(path, GetFileExInfoStandard, &info)) { + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + errno = 0; + return -1; + } + code = attribute_data_to_stat(&info, result); + if (code != 0) + return code; + /* Set S_IFEXEC if it is an .exe, .bat, ... */ + dot = strrchr(path, '.'); + if (dot) { + if (stricmp(dot, ".bat") == 0 || + stricmp(dot, ".cmd") == 0 || + stricmp(dot, ".exe") == 0 || + stricmp(dot, ".com") == 0) + result->st_mode |= 0111; + } + return code; +} + +static int +win32_wstat(const wchar_t* path, struct win32_stat *result) +{ + int code; + const wchar_t *dot; + WIN32_FILE_ATTRIBUTE_DATA info; + /* XXX not supported on Win95 and NT 3.x */ + if (!GetFileAttributesExW(path, GetFileExInfoStandard, &info)) { + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + errno = 0; + return -1; + } + code = attribute_data_to_stat(&info, result); + if (code < 0) + return code; + /* Set IFEXEC if it is an .exe, .bat, ... */ + dot = wcsrchr(path, '.'); + if (dot) { + if (_wcsicmp(dot, L".bat") == 0 || + _wcsicmp(dot, L".cmd") == 0 || + _wcsicmp(dot, L".exe") == 0 || + _wcsicmp(dot, L".com") == 0) + result->st_mode |= 0111; + } + return code; +} + +static int +win32_fstat(int file_number, struct win32_stat *result) +{ + BY_HANDLE_FILE_INFORMATION info; + HANDLE h; + int type; + + h = (HANDLE)_get_osfhandle(file_number); + + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + errno = 0; + + if (h == INVALID_HANDLE_VALUE) { + /* This is really a C library error (invalid file handle). + We set the Win32 error to the closes one matching. */ + SetLastError(ERROR_INVALID_HANDLE); + return -1; + } + memset(result, 0, sizeof(*result)); + + type = GetFileType(h); + if (type == FILE_TYPE_UNKNOWN) { + DWORD error = GetLastError(); + if (error != 0) { + return -1; + } + /* else: valid but unknown file */ + } + + if (type != FILE_TYPE_DISK) { + if (type == FILE_TYPE_CHAR) + result->st_mode = _S_IFCHR; + else if (type == FILE_TYPE_PIPE) + result->st_mode = _S_IFIFO; + return 0; + } + + if (!GetFileInformationByHandle(h, &info)) { + return -1; + } + + /* similar to stat() */ + result->st_mode = attributes_to_mode(info.dwFileAttributes); + result->st_size = (((__int64)info.nFileSizeHigh)<<32) + info.nFileSizeLow; + FILE_TIME_to_time_t_nsec(&info.ftCreationTime, &result->st_ctime, &result->st_ctime_nsec); + FILE_TIME_to_time_t_nsec(&info.ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec); + FILE_TIME_to_time_t_nsec(&info.ftLastAccessTime, &result->st_atime, &result->st_atime_nsec); + /* specific to fstat() */ + result->st_nlink = info.nNumberOfLinks; + result->st_ino = (((__int64)info.nFileIndexHigh)<<32) + info.nFileIndexLow; + return 0; +} + +#endif /* MS_WINDOWS */ + +PyDoc_STRVAR(stat_result__doc__, +"stat_result: Result from stat or lstat.\n\n\ +This object may be accessed either as a tuple of\n\ + (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\ +or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\ +\n\ +Posix/windows: If your platform supports st_blksize, st_blocks, st_rdev,\n\ +or st_flags, they are available as attributes only.\n\ +\n\ +See os.stat for more information."); + +static PyStructSequence_Field stat_result_fields[] = { + {"st_mode", "protection bits"}, + {"st_ino", "inode"}, + {"st_dev", "device"}, + {"st_nlink", "number of hard links"}, + {"st_uid", "user ID of owner"}, + {"st_gid", "group ID of owner"}, + {"st_size", "total size, in bytes"}, + /* The NULL is replaced with PyStructSequence_UnnamedField later. */ + {NULL, "integer time of last access"}, + {NULL, "integer time of last modification"}, + {NULL, "integer time of last change"}, + {"st_atime", "time of last access"}, + {"st_mtime", "time of last modification"}, + {"st_ctime", "time of last change"}, +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + {"st_blksize", "blocksize for filesystem I/O"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + {"st_blocks", "number of blocks allocated"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + {"st_rdev", "device type (if inode device)"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_FLAGS + {"st_flags", "user defined flags for file"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_GEN + {"st_gen", "generation number"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME + {"st_birthtime", "time of creation"}, +#endif + {0} +}; + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE +#define ST_BLKSIZE_IDX 13 +#else +#define ST_BLKSIZE_IDX 12 +#endif + +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS +#define ST_BLOCKS_IDX (ST_BLKSIZE_IDX+1) +#else +#define ST_BLOCKS_IDX ST_BLKSIZE_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_RDEV +#define ST_RDEV_IDX (ST_BLOCKS_IDX+1) +#else +#define ST_RDEV_IDX ST_BLOCKS_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_FLAGS +#define ST_FLAGS_IDX (ST_RDEV_IDX+1) +#else +#define ST_FLAGS_IDX ST_RDEV_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_GEN +#define ST_GEN_IDX (ST_FLAGS_IDX+1) +#else +#define ST_GEN_IDX ST_FLAGS_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME +#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1) +#else +#define ST_BIRTHTIME_IDX ST_GEN_IDX +#endif + +static PyStructSequence_Desc stat_result_desc = { + "stat_result", /* name */ + stat_result__doc__, /* doc */ + stat_result_fields, + 10 +}; + +PyDoc_STRVAR(statvfs_result__doc__, +"statvfs_result: Result from statvfs or fstatvfs.\n\n\ +This object may be accessed either as a tuple of\n\ + (bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flag, namemax),\n\ +or via the attributes f_bsize, f_frsize, f_blocks, f_bfree, and so on.\n\ +\n\ +See os.statvfs for more information."); + +static PyStructSequence_Field statvfs_result_fields[] = { + {"f_bsize", }, + {"f_frsize", }, + {"f_blocks", }, + {"f_bfree", }, + {"f_bavail", }, + {"f_files", }, + {"f_ffree", }, + {"f_favail", }, + {"f_flag", }, + {"f_namemax",}, + {0} +}; + +static PyStructSequence_Desc statvfs_result_desc = { + "statvfs_result", /* name */ + statvfs_result__doc__, /* doc */ + statvfs_result_fields, + 10 +}; + +static PyTypeObject StatResultType; +static PyTypeObject StatVFSResultType; +static newfunc structseq_new; + +static PyObject * +statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyStructSequence *result; + int i; + + result = (PyStructSequence*)structseq_new(type, args, kwds); + if (!result) + return NULL; + /* If we have been initialized from a tuple, + st_?time might be set to None. Initialize it + from the int slots. */ + for (i = 7; i <= 9; i++) { + if (result->ob_item[i+3] == Py_None) { + Py_DECREF(Py_None); + Py_INCREF(result->ob_item[i]); + result->ob_item[i+3] = result->ob_item[i]; + } + } + return (PyObject*)result; +} + + + +/* If true, st_?time is float. */ +static int _stat_float_times = 1; + +PyDoc_STRVAR(stat_float_times__doc__, +"stat_float_times([newval]) -> oldval\n\n\ +Determine whether os.[lf]stat represents time stamps as float objects.\n\ +If newval is True, future calls to stat() return floats, if it is False,\n\ +future calls return ints. \n\ +If newval is omitted, return the current setting.\n"); + +static PyObject* +stat_float_times(PyObject* self, PyObject *args) +{ + int newval = -1; + if (!PyArg_ParseTuple(args, "|i:stat_float_times", &newval)) + return NULL; + if (newval == -1) + /* Return old value */ + return PyBool_FromLong(_stat_float_times); + _stat_float_times = newval; + Py_INCREF(Py_None); + return Py_None; +} + +static void +fill_time(PyObject *v, int index, time_t sec, unsigned long nsec) +{ + PyObject *fval,*ival; +#if SIZEOF_TIME_T > SIZEOF_LONG + ival = PyLong_FromLongLong((PY_LONG_LONG)sec); +#else + ival = PyInt_FromLong((long)sec); +#endif + if (_stat_float_times) { + fval = PyFloat_FromDouble(sec + 1e-9*nsec); + } else { + fval = ival; + Py_INCREF(fval); + } + PyStructSequence_SET_ITEM(v, index, ival); + PyStructSequence_SET_ITEM(v, index+3, fval); +} + +/* pack a system stat C structure into the Python stat tuple + (used by posix_stat() and posix_fstat()) */ +static PyObject* +_pystat_fromstructstat(STRUCT_STAT *st) +{ + unsigned long ansec, mnsec, cnsec; + PyObject *v = PyStructSequence_New(&StatResultType); + if (v == NULL) + return NULL; + + PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long)st->st_mode)); +#ifdef HAVE_LARGEFILE_SUPPORT + PyStructSequence_SET_ITEM(v, 1, + PyLong_FromLongLong((PY_LONG_LONG)st->st_ino)); +#else + PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long)st->st_ino)); +#endif +#if defined(HAVE_LONG_LONG) && !defined(MS_WINDOWS) + PyStructSequence_SET_ITEM(v, 2, + PyLong_FromLongLong((PY_LONG_LONG)st->st_dev)); +#else + PyStructSequence_SET_ITEM(v, 2, PyInt_FromLong((long)st->st_dev)); +#endif + PyStructSequence_SET_ITEM(v, 3, PyInt_FromLong((long)st->st_nlink)); + PyStructSequence_SET_ITEM(v, 4, PyInt_FromLong((long)st->st_uid)); + PyStructSequence_SET_ITEM(v, 5, PyInt_FromLong((long)st->st_gid)); +#ifdef HAVE_LARGEFILE_SUPPORT + PyStructSequence_SET_ITEM(v, 6, + PyLong_FromLongLong((PY_LONG_LONG)st->st_size)); +#else + PyStructSequence_SET_ITEM(v, 6, PyInt_FromLong(st->st_size)); +#endif + +#if defined(HAVE_STAT_TV_NSEC) + ansec = st->st_atim.tv_nsec; + mnsec = st->st_mtim.tv_nsec; + cnsec = st->st_ctim.tv_nsec; +#elif defined(HAVE_STAT_TV_NSEC2) + ansec = st->st_atimespec.tv_nsec; + mnsec = st->st_mtimespec.tv_nsec; + cnsec = st->st_ctimespec.tv_nsec; +#elif defined(HAVE_STAT_NSEC) + ansec = st->st_atime_nsec; + mnsec = st->st_mtime_nsec; + cnsec = st->st_ctime_nsec; +#else + ansec = mnsec = cnsec = 0; +#endif + fill_time(v, 7, st->st_atime, ansec); + fill_time(v, 8, st->st_mtime, mnsec); + fill_time(v, 9, st->st_ctime, cnsec); + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX, + PyInt_FromLong((long)st->st_blksize)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + PyStructSequence_SET_ITEM(v, ST_BLOCKS_IDX, + PyInt_FromLong((long)st->st_blocks)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + PyStructSequence_SET_ITEM(v, ST_RDEV_IDX, + PyInt_FromLong((long)st->st_rdev)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_GEN + PyStructSequence_SET_ITEM(v, ST_GEN_IDX, + PyInt_FromLong((long)st->st_gen)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME + { + PyObject *val; + unsigned long bsec,bnsec; + bsec = (long)st->st_birthtime; +#ifdef HAVE_STAT_TV_NSEC2 + bnsec = st->st_birthtimespec.tv_nsec; +#else + bnsec = 0; +#endif + if (_stat_float_times) { + val = PyFloat_FromDouble(bsec + 1e-9*bnsec); + } else { + val = PyInt_FromLong((long)bsec); + } + PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX, + val); + } +#endif +#ifdef HAVE_STRUCT_STAT_ST_FLAGS + PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX, + PyInt_FromLong((long)st->st_flags)); +#endif + + if (PyErr_Occurred()) { + Py_DECREF(v); + return NULL; + } + + return v; +} + +#ifdef MS_WINDOWS + +/* IsUNCRoot -- test whether the supplied path is of the form \\SERVER\SHARE\, + where / can be used in place of \ and the trailing slash is optional. + Both SERVER and SHARE must have at least one character. +*/ + +#define ISSLASHA(c) ((c) == '\\' || (c) == '/') +#define ISSLASHW(c) ((c) == L'\\' || (c) == L'/') +#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0])) + +static BOOL +IsUNCRootA(char *path, int pathlen) +{ + #define ISSLASH ISSLASHA + + int i, share; + + if (pathlen < 5 || !ISSLASH(path[0]) || !ISSLASH(path[1])) + /* minimum UNCRoot is \\x\y */ + return FALSE; + for (i = 2; i < pathlen ; i++) + if (ISSLASH(path[i])) break; + if (i == 2 || i == pathlen) + /* do not allow \\\SHARE or \\SERVER */ + return FALSE; + share = i+1; + for (i = share; i < pathlen; i++) + if (ISSLASH(path[i])) break; + return (i != share && (i == pathlen || i == pathlen-1)); + + #undef ISSLASH +} + +#ifdef Py_WIN_WIDE_FILENAMES +static BOOL +IsUNCRootW(Py_UNICODE *path, int pathlen) +{ + #define ISSLASH ISSLASHW + + int i, share; + + if (pathlen < 5 || !ISSLASH(path[0]) || !ISSLASH(path[1])) + /* minimum UNCRoot is \\x\y */ + return FALSE; + for (i = 2; i < pathlen ; i++) + if (ISSLASH(path[i])) break; + if (i == 2 || i == pathlen) + /* do not allow \\\SHARE or \\SERVER */ + return FALSE; + share = i+1; + for (i = share; i < pathlen; i++) + if (ISSLASH(path[i])) break; + return (i != share && (i == pathlen || i == pathlen-1)); + + #undef ISSLASH +} +#endif /* Py_WIN_WIDE_FILENAMES */ +#endif /* MS_WINDOWS */ + +static PyObject * +posix_do_stat(PyObject *self, PyObject *args, + char *format, +#ifdef __VMS + int (*statfunc)(const char *, STRUCT_STAT *, ...), +#else + int (*statfunc)(const char *, STRUCT_STAT *), +#endif + char *wformat, + int (*wstatfunc)(const Py_UNICODE *, STRUCT_STAT *)) +{ + STRUCT_STAT st; + char *path = NULL; /* pass this to stat; do not free() it */ + char *pathfree = NULL; /* this memory must be free'd */ + int res; + PyObject *result; + +#ifdef Py_WIN_WIDE_FILENAMES + /* If on wide-character-capable OS see if argument + is Unicode and if so use wide API. */ + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, wformat, &po)) { + Py_UNICODE *wpath = PyUnicode_AS_UNICODE(po); + + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE result OK without + thread lock as it is a simple dereference. */ + res = wstatfunc(wpath, &st); + Py_END_ALLOW_THREADS + + if (res != 0) + return win32_error_unicode("stat", wpath); + return _pystat_fromstructstat(&st); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, format, + Py_FileSystemDefaultEncoding, &path)) + return NULL; + pathfree = path; + + Py_BEGIN_ALLOW_THREADS + res = (*statfunc)(path, &st); + Py_END_ALLOW_THREADS + + if (res != 0) { +#ifdef MS_WINDOWS + result = win32_error("stat", pathfree); +#else + result = posix_error_with_filename(pathfree); +#endif + } + else + result = _pystat_fromstructstat(&st); + + PyMem_Free(pathfree); + return result; +} + +/* POSIX methods */ + +PyDoc_STRVAR(posix_access__doc__, +"access(path, mode) -> 1 if granted, 0 otherwise\n\n\ +Use the real uid/gid to test for access to a path. Note that most\n\ +operations will use the effective uid/gid, therefore this routine can\n\ +be used in a suid/sgid environment to test if the invoking user has the\n\ +specified access to the path. The mode argument can be F_OK to test\n\ +existence, or the inclusive-OR of R_OK, W_OK, and X_OK."); + +static PyObject * +posix_access(PyObject *self, PyObject *args) +{ + char *path; + int mode; + int res; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "Ui:access", &po, &mode)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread lock as + it is a simple dereference. */ + res = _waccess(PyUnicode_AS_UNICODE(po), mode); + Py_END_ALLOW_THREADS + return PyBool_FromLong(res == 0); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + if (!PyArg_ParseTuple(args, "eti:access", + Py_FileSystemDefaultEncoding, &path, &mode)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = access(path, mode); + Py_END_ALLOW_THREADS + PyMem_Free(path); + return PyBool_FromLong(res == 0); +} + +#ifndef F_OK +#define F_OK 0 +#endif +#ifndef R_OK +#define R_OK 4 +#endif +#ifndef W_OK +#define W_OK 2 +#endif +#ifndef X_OK +#define X_OK 1 +#endif + +#ifdef HAVE_TTYNAME +PyDoc_STRVAR(posix_ttyname__doc__, +"ttyname(fd) -> string\n\n\ +Return the name of the terminal device connected to 'fd'."); + +static PyObject * +posix_ttyname(PyObject *self, PyObject *args) +{ + int id; + char *ret; + + if (!PyArg_ParseTuple(args, "i:ttyname", &id)) + return NULL; + +#if defined(__VMS) + /* file descriptor 0 only, the default input device (stdin) */ + if (id == 0) { + ret = ttyname(); + } + else { + ret = NULL; + } +#else + ret = ttyname(id); +#endif + if (ret == NULL) + return posix_error(); + return PyString_FromString(ret); +} +#endif + +#ifdef HAVE_CTERMID +PyDoc_STRVAR(posix_ctermid__doc__, +"ctermid() -> string\n\n\ +Return the name of the controlling terminal for this process."); + +static PyObject * +posix_ctermid(PyObject *self, PyObject *noargs) +{ + char *ret; + char buffer[L_ctermid]; + +#ifdef USE_CTERMID_R + ret = ctermid_r(buffer); +#else + ret = ctermid(buffer); +#endif + if (ret == NULL) + return posix_error(); + return PyString_FromString(buffer); +} +#endif + +PyDoc_STRVAR(posix_chdir__doc__, +"chdir(path)\n\n\ +Change the current working directory to the specified path."); + +static PyObject * +posix_chdir(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_1str(args, "et:chdir", chdir, "U:chdir", _wchdir); +#elif defined(PYOS_OS2) && defined(PYCC_GCC) + return posix_1str(args, "et:chdir", _chdir2, NULL, NULL); +#elif defined(__VMS) + return posix_1str(args, "et:chdir", (int (*)(const char *))chdir, + NULL, NULL); +#else + return posix_1str(args, "et:chdir", chdir, NULL, NULL); +#endif +} + +#ifdef HAVE_FCHDIR +PyDoc_STRVAR(posix_fchdir__doc__, +"fchdir(fildes)\n\n\ +Change to the directory of the given file descriptor. fildes must be\n\ +opened on a directory, not a file."); + +static PyObject * +posix_fchdir(PyObject *self, PyObject *fdobj) +{ + return posix_fildes(fdobj, fchdir); +} +#endif /* HAVE_FCHDIR */ + + +PyDoc_STRVAR(posix_chmod__doc__, +"chmod(path, mode)\n\n\ +Change the access permissions of a file."); + +static PyObject * +posix_chmod(PyObject *self, PyObject *args) +{ + char *path = NULL; + int i; + int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "Ui|:chmod", &po, &i)) { + Py_BEGIN_ALLOW_THREADS + res = _wchmod(PyUnicode_AS_UNICODE(po), i); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_unicode_filename( + PyUnicode_AS_UNICODE(po)); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif /* Py_WIN_WIDE_FILENAMES */ + if (!PyArg_ParseTuple(args, "eti:chmod", Py_FileSystemDefaultEncoding, + &path, &i)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = chmod(path, i); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} + + +#ifdef HAVE_CHROOT +PyDoc_STRVAR(posix_chroot__doc__, +"chroot(path)\n\n\ +Change root directory to path."); + +static PyObject * +posix_chroot(PyObject *self, PyObject *args) +{ + return posix_1str(args, "et:chroot", chroot, NULL, NULL); +} +#endif + +#ifdef HAVE_FSYNC +PyDoc_STRVAR(posix_fsync__doc__, +"fsync(fildes)\n\n\ +force write of file with filedescriptor to disk."); + +static PyObject * +posix_fsync(PyObject *self, PyObject *fdobj) +{ + return posix_fildes(fdobj, fsync); +} +#endif /* HAVE_FSYNC */ + +#ifdef HAVE_FDATASYNC + +#ifdef __hpux +extern int fdatasync(int); /* On HP-UX, in libc but not in unistd.h */ +#endif + +PyDoc_STRVAR(posix_fdatasync__doc__, +"fdatasync(fildes)\n\n\ +force write of file with filedescriptor to disk.\n\ + does not force update of metadata."); + +static PyObject * +posix_fdatasync(PyObject *self, PyObject *fdobj) +{ + return posix_fildes(fdobj, fdatasync); +} +#endif /* HAVE_FDATASYNC */ + + +#ifdef HAVE_CHOWN +PyDoc_STRVAR(posix_chown__doc__, +"chown(path, uid, gid)\n\n\ +Change the owner and group id of path to the numeric uid and gid."); + +static PyObject * +posix_chown(PyObject *self, PyObject *args) +{ + char *path = NULL; + int uid, gid; + int res; + if (!PyArg_ParseTuple(args, "etii:chown", + Py_FileSystemDefaultEncoding, &path, + &uid, &gid)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = chown(path, (uid_t) uid, (gid_t) gid); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_CHOWN */ + +#ifdef HAVE_LCHOWN +PyDoc_STRVAR(posix_lchown__doc__, +"lchown(path, uid, gid)\n\n\ +Change the owner and group id of path to the numeric uid and gid.\n\ +This function will not follow symbolic links."); + +static PyObject * +posix_lchown(PyObject *self, PyObject *args) +{ + char *path = NULL; + int uid, gid; + int res; + if (!PyArg_ParseTuple(args, "etii:lchown", + Py_FileSystemDefaultEncoding, &path, + &uid, &gid)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = lchown(path, (uid_t) uid, (gid_t) gid); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_LCHOWN */ + + +#ifdef HAVE_GETCWD +PyDoc_STRVAR(posix_getcwd__doc__, +"getcwd() -> path\n\n\ +Return a string representing the current working directory."); + +static PyObject * +posix_getcwd(PyObject *self, PyObject *noargs) +{ + char buf[1026]; + char *res; + + Py_BEGIN_ALLOW_THREADS +#if defined(PYOS_OS2) && defined(PYCC_GCC) + res = _getcwd2(buf, sizeof buf); +#else + res = getcwd(buf, sizeof buf); +#endif + Py_END_ALLOW_THREADS + if (res == NULL) + return posix_error(); + return PyString_FromString(buf); +} + +#ifdef Py_USING_UNICODE +PyDoc_STRVAR(posix_getcwdu__doc__, +"getcwdu() -> path\n\n\ +Return a unicode string representing the current working directory."); + +static PyObject * +posix_getcwdu(PyObject *self, PyObject *noargs) +{ + char buf[1026]; + char *res; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + wchar_t *wres; + wchar_t wbuf[1026]; + Py_BEGIN_ALLOW_THREADS + wres = _wgetcwd(wbuf, sizeof wbuf/ sizeof wbuf[0]); + Py_END_ALLOW_THREADS + if (wres == NULL) + return posix_error(); + return PyUnicode_FromWideChar(wbuf, wcslen(wbuf)); + } +#endif + + Py_BEGIN_ALLOW_THREADS +#if defined(PYOS_OS2) && defined(PYCC_GCC) + res = _getcwd2(buf, sizeof buf); +#else + res = getcwd(buf, sizeof buf); +#endif + Py_END_ALLOW_THREADS + if (res == NULL) + return posix_error(); + return PyUnicode_Decode(buf, strlen(buf), Py_FileSystemDefaultEncoding,"strict"); +} +#endif +#endif + + +#ifdef HAVE_LINK +PyDoc_STRVAR(posix_link__doc__, +"link(src, dst)\n\n\ +Create a hard link to a file."); + +static PyObject * +posix_link(PyObject *self, PyObject *args) +{ + return posix_2str(args, "etet:link", link, NULL, NULL); +} +#endif /* HAVE_LINK */ + + +PyDoc_STRVAR(posix_listdir__doc__, +"listdir(path) -> list_of_strings\n\n\ +Return a list containing the names of the entries in the directory.\n\ +\n\ + path: path of directory to list\n\ +\n\ +The list is in arbitrary order. It does not include the special\n\ +entries '.' and '..' even if they are present in the directory."); + +static PyObject * +posix_listdir(PyObject *self, PyObject *args) +{ + /* XXX Should redo this putting the (now four) versions of opendir + in separate files instead of having them all here... */ +#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR) + + PyObject *d, *v; + HANDLE hFindFile; + BOOL result = FALSE; + WIN32_FIND_DATA FileData; + /* MAX_PATH characters could mean a bigger encoded string */ + char namebuf[MAX_PATH*2+5]; + char *bufptr = namebuf; + Py_ssize_t len = sizeof(namebuf)/sizeof(namebuf[0]); + +#ifdef Py_WIN_WIDE_FILENAMES + /* If on wide-character-capable OS see if argument + is Unicode and if so use wide API. */ + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U:listdir", &po)) { + WIN32_FIND_DATAW wFileData; + Py_UNICODE wnamebuf[MAX_PATH*2+5]; + Py_UNICODE wch; + wcsncpy(wnamebuf, PyUnicode_AS_UNICODE(po), MAX_PATH); + wnamebuf[MAX_PATH] = L'\0'; + len = wcslen(wnamebuf); + wch = (len > 0) ? wnamebuf[len-1] : L'\0'; + if (wch != L'/' && wch != L'\\' && wch != L':') + wnamebuf[len++] = L'/'; + wcscpy(wnamebuf + len, L"*.*"); + if ((d = PyList_New(0)) == NULL) + return NULL; + hFindFile = FindFirstFileW(wnamebuf, &wFileData); + if (hFindFile == INVALID_HANDLE_VALUE) { + errno = GetLastError(); + if (errno == ERROR_FILE_NOT_FOUND) { + return d; + } + Py_DECREF(d); + return win32_error_unicode("FindFirstFileW", wnamebuf); + } + do { + if (wFileData.cFileName[0] == L'.' && + (wFileData.cFileName[1] == L'\0' || + wFileData.cFileName[1] == L'.' && + wFileData.cFileName[2] == L'\0')) + goto loop_w; + v = PyUnicode_FromUnicode(wFileData.cFileName, wcslen(wFileData.cFileName)); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); +loop_w: + Py_BEGIN_ALLOW_THREADS + result = FindNextFileW(hFindFile, &wFileData); + Py_END_ALLOW_THREADS + } while (result == TRUE); + + if (FindClose(hFindFile) == FALSE) { + Py_DECREF(d); + return win32_error_unicode("FindClose", wnamebuf); + } + return d; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, "et#:listdir", + Py_FileSystemDefaultEncoding, &bufptr, &len)) + return NULL; + if (len > 0) { + char ch = namebuf[len-1]; + if (ch != SEP && ch != ALTSEP && ch != ':') + namebuf[len++] = '/'; + } + strcpy(namebuf + len, "*.*"); + + if ((d = PyList_New(0)) == NULL) + return NULL; + + hFindFile = FindFirstFile(namebuf, &FileData); + if (hFindFile == INVALID_HANDLE_VALUE) { + errno = GetLastError(); + if (errno == ERROR_FILE_NOT_FOUND) + return d; + Py_DECREF(d); + return win32_error("FindFirstFile", namebuf); + } + do { + if (FileData.cFileName[0] == '.' && + (FileData.cFileName[1] == '\0' || + FileData.cFileName[1] == '.' && + FileData.cFileName[2] == '\0')) + goto loop_a; + v = PyString_FromString(FileData.cFileName); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); +loop_a: + Py_BEGIN_ALLOW_THREADS + result = FindNextFile(hFindFile, &FileData); + Py_END_ALLOW_THREADS + } while (result == TRUE); + + if (FindClose(hFindFile) == FALSE) { + Py_DECREF(d); + return win32_error("FindClose", namebuf); + } + + return d; + +#elif defined(PYOS_OS2) + +#ifndef MAX_PATH +#define MAX_PATH CCHMAXPATH +#endif + char *name, *pt; + Py_ssize_t len; + PyObject *d, *v; + char namebuf[MAX_PATH+5]; + HDIR hdir = 1; + ULONG srchcnt = 1; + FILEFINDBUF3 ep; + APIRET rc; + + if (!PyArg_ParseTuple(args, "t#:listdir", &name, &len)) + return NULL; + if (len >= MAX_PATH) { + PyErr_SetString(PyExc_ValueError, "path too long"); + return NULL; + } + strcpy(namebuf, name); + for (pt = namebuf; *pt; pt++) + if (*pt == ALTSEP) + *pt = SEP; + if (namebuf[len-1] != SEP) + namebuf[len++] = SEP; + strcpy(namebuf + len, "*.*"); + + if ((d = PyList_New(0)) == NULL) + return NULL; + + rc = DosFindFirst(namebuf, /* Wildcard Pattern to Match */ + &hdir, /* Handle to Use While Search Directory */ + FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_DIRECTORY, + &ep, sizeof(ep), /* Structure to Receive Directory Entry */ + &srchcnt, /* Max and Actual Count of Entries Per Iteration */ + FIL_STANDARD); /* Format of Entry (EAs or Not) */ + + if (rc != NO_ERROR) { + errno = ENOENT; + return posix_error_with_filename(name); + } + + if (srchcnt > 0) { /* If Directory is NOT Totally Empty, */ + do { + if (ep.achName[0] == '.' + && (ep.achName[1] == '\0' || (ep.achName[1] == '.' && ep.achName[2] == '\0'))) + continue; /* Skip Over "." and ".." Names */ + + strcpy(namebuf, ep.achName); + + /* Leave Case of Name Alone -- In Native Form */ + /* (Removed Forced Lowercasing Code) */ + + v = PyString_FromString(namebuf); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + } while (DosFindNext(hdir, &ep, sizeof(ep), &srchcnt) == NO_ERROR && srchcnt > 0); + } + + return d; +#else + + char *name = NULL; + PyObject *d, *v; + DIR *dirp; + struct dirent *ep; + int arg_is_unicode = 1; + + if (!PyArg_ParseTuple(args, "U:listdir", &v)) { + arg_is_unicode = 0; + PyErr_Clear(); + } + if (!PyArg_ParseTuple(args, "et:listdir", Py_FileSystemDefaultEncoding, &name)) + return NULL; + if ((dirp = opendir(name)) == NULL) { + return posix_error_with_allocated_filename(name); + } + if ((d = PyList_New(0)) == NULL) { + closedir(dirp); + PyMem_Free(name); + return NULL; + } + for (;;) { + Py_BEGIN_ALLOW_THREADS + ep = readdir(dirp); + Py_END_ALLOW_THREADS + if (ep == NULL) + break; + if (ep->d_name[0] == '.' && + (NAMLEN(ep) == 1 || + (ep->d_name[1] == '.' && NAMLEN(ep) == 2))) + continue; + v = PyString_FromStringAndSize(ep->d_name, NAMLEN(ep)); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } +#ifdef Py_USING_UNICODE + if (arg_is_unicode) { + PyObject *w; + + w = PyUnicode_FromEncodedObject(v, + Py_FileSystemDefaultEncoding, + "strict"); + if (w != NULL) { + Py_DECREF(v); + v = w; + } + else { + /* fall back to the original byte string, as + discussed in patch #683592 */ + PyErr_Clear(); + } + } +#endif + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + } + closedir(dirp); + PyMem_Free(name); + + return d; + +#endif /* which OS */ +} /* end of posix_listdir */ + +#ifdef MS_WINDOWS +/* A helper function for abspath on win32 */ +static PyObject * +posix__getfullpathname(PyObject *self, PyObject *args) +{ + /* assume encoded strings wont more than double no of chars */ + char inbuf[MAX_PATH*2]; + char *inbufp = inbuf; + Py_ssize_t insize = sizeof(inbuf); + char outbuf[MAX_PATH*2]; + char *temp; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U|:_getfullpathname", &po)) { + Py_UNICODE woutbuf[MAX_PATH*2]; + Py_UNICODE *wtemp; + if (!GetFullPathNameW(PyUnicode_AS_UNICODE(po), + sizeof(woutbuf)/sizeof(woutbuf[0]), + woutbuf, &wtemp)) + return win32_error("GetFullPathName", ""); + return PyUnicode_FromUnicode(woutbuf, wcslen(woutbuf)); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + if (!PyArg_ParseTuple (args, "et#:_getfullpathname", + Py_FileSystemDefaultEncoding, &inbufp, + &insize)) + return NULL; + if (!GetFullPathName(inbuf, sizeof(outbuf)/sizeof(outbuf[0]), + outbuf, &temp)) + return win32_error("GetFullPathName", inbuf); + if (PyUnicode_Check(PyTuple_GetItem(args, 0))) { + return PyUnicode_Decode(outbuf, strlen(outbuf), + Py_FileSystemDefaultEncoding, NULL); + } + return PyString_FromString(outbuf); +} /* end of posix__getfullpathname */ +#endif /* MS_WINDOWS */ + +PyDoc_STRVAR(posix_mkdir__doc__, +"mkdir(path [, mode=0777])\n\n\ +Create a directory."); + +static PyObject * +posix_mkdir(PyObject *self, PyObject *args) +{ + int res; + char *path = NULL; + int mode = 0777; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U|i:mkdir", &po, &mode)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread lock as + it is a simple dereference. */ + res = _wmkdir(PyUnicode_AS_UNICODE(po)); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, "et|i:mkdir", + Py_FileSystemDefaultEncoding, &path, &mode)) + return NULL; + Py_BEGIN_ALLOW_THREADS +#if ( defined(__WATCOMC__) || defined(_MSC_VER) || defined(PYCC_VACPP) ) && !defined(__QNX__) + res = mkdir(path); +#else + res = mkdir(path, mode); +#endif + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} + + +#ifdef HAVE_NICE +#if defined(HAVE_BROKEN_NICE) && defined(HAVE_SYS_RESOURCE_H) +#if defined(HAVE_GETPRIORITY) && !defined(PRIO_PROCESS) +#include +#endif +#endif + +PyDoc_STRVAR(posix_nice__doc__, +"nice(inc) -> new_priority\n\n\ +Decrease the priority of process by inc and return the new priority."); + +static PyObject * +posix_nice(PyObject *self, PyObject *args) +{ + int increment, value; + + if (!PyArg_ParseTuple(args, "i:nice", &increment)) + return NULL; + + /* There are two flavours of 'nice': one that returns the new + priority (as required by almost all standards out there) and the + Linux/FreeBSD/BSDI one, which returns '0' on success and advices + the use of getpriority() to get the new priority. + + If we are of the nice family that returns the new priority, we + need to clear errno before the call, and check if errno is filled + before calling posix_error() on a returnvalue of -1, because the + -1 may be the actual new priority! */ + + errno = 0; + value = nice(increment); +#if defined(HAVE_BROKEN_NICE) && defined(HAVE_GETPRIORITY) + if (value == 0) + value = getpriority(PRIO_PROCESS, 0); +#endif + if (value == -1 && errno != 0) + /* either nice() or getpriority() returned an error */ + return posix_error(); + return PyInt_FromLong((long) value); +} +#endif /* HAVE_NICE */ + + +PyDoc_STRVAR(posix_rename__doc__, +"rename(old, new)\n\n\ +Rename a file or directory."); + +static PyObject * +posix_rename(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_2str(args, "etet:rename", rename, "OO:rename", _wrename); +#else + return posix_2str(args, "etet:rename", rename, NULL, NULL); +#endif +} + + +PyDoc_STRVAR(posix_rmdir__doc__, +"rmdir(path)\n\n\ +Remove a directory."); + +static PyObject * +posix_rmdir(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_1str(args, "et:rmdir", rmdir, "U:rmdir", _wrmdir); +#else + return posix_1str(args, "et:rmdir", rmdir, NULL, NULL); +#endif +} + + +PyDoc_STRVAR(posix_stat__doc__, +"stat(path) -> stat result\n\n\ +Perform a stat system call on the given path."); + +static PyObject * +posix_stat(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_do_stat(self, args, "et:stat", STAT, "U:stat", win32_wstat); +#else + return posix_do_stat(self, args, "et:stat", STAT, NULL, NULL); +#endif +} + + +#ifdef HAVE_SYSTEM +PyDoc_STRVAR(posix_system__doc__, +"system(command) -> exit_status\n\n\ +Execute the command (a string) in a subshell."); + +static PyObject * +posix_system(PyObject *self, PyObject *args) +{ + char *command; + long sts; + if (!PyArg_ParseTuple(args, "s:system", &command)) + return NULL; + Py_BEGIN_ALLOW_THREADS + sts = system(command); + Py_END_ALLOW_THREADS + return PyInt_FromLong(sts); +} +#endif + + +PyDoc_STRVAR(posix_umask__doc__, +"umask(new_mask) -> old_mask\n\n\ +Set the current numeric umask and return the previous umask."); + +static PyObject * +posix_umask(PyObject *self, PyObject *args) +{ + int i; + if (!PyArg_ParseTuple(args, "i:umask", &i)) + return NULL; + i = (int)umask(i); + if (i < 0) + return posix_error(); + return PyInt_FromLong((long)i); +} + + +PyDoc_STRVAR(posix_unlink__doc__, +"unlink(path)\n\n\ +Remove a file (same as remove(path))."); + +PyDoc_STRVAR(posix_remove__doc__, +"remove(path)\n\n\ +Remove a file (same as unlink(path))."); + +static PyObject * +posix_unlink(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_1str(args, "et:remove", unlink, "U:remove", _wunlink); +#else + return posix_1str(args, "et:remove", unlink, NULL, NULL); +#endif +} + + +#ifdef HAVE_UNAME +PyDoc_STRVAR(posix_uname__doc__, +"uname() -> (sysname, nodename, release, version, machine)\n\n\ +Return a tuple identifying the current operating system."); + +static PyObject * +posix_uname(PyObject *self, PyObject *noargs) +{ + struct utsname u; + int res; + + Py_BEGIN_ALLOW_THREADS + res = uname(&u); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + return Py_BuildValue("(sssss)", + u.sysname, + u.nodename, + u.release, + u.version, + u.machine); +} +#endif /* HAVE_UNAME */ + +static int +extract_time(PyObject *t, long* sec, long* usec) +{ + long intval; + if (PyFloat_Check(t)) { + double tval = PyFloat_AsDouble(t); + PyObject *intobj = t->ob_type->tp_as_number->nb_int(t); + if (!intobj) + return -1; + intval = PyInt_AsLong(intobj); + Py_DECREF(intobj); + if (intval == -1 && PyErr_Occurred()) + return -1; + *sec = intval; + *usec = (long)((tval - intval) * 1e6); /* can't exceed 1000000 */ + if (*usec < 0) + /* If rounding gave us a negative number, + truncate. */ + *usec = 0; + return 0; + } + intval = PyInt_AsLong(t); + if (intval == -1 && PyErr_Occurred()) + return -1; + *sec = intval; + *usec = 0; + return 0; +} + +PyDoc_STRVAR(posix_utime__doc__, +"utime(path, (atime, utime))\n\ +utime(path, None)\n\n\ +Set the access and modified time of the file to the given values. If the\n\ +second form is used, set the access and modified times to the current time."); + +static PyObject * +posix_utime(PyObject *self, PyObject *args) +{ + char *path = NULL; + long atime, mtime, ausec, musec; + int res; + PyObject* arg; + +#if defined(HAVE_UTIMES) + struct timeval buf[2]; +#define ATIME buf[0].tv_sec +#define MTIME buf[1].tv_sec +#elif defined(HAVE_UTIME_H) +/* XXX should define struct utimbuf instead, above */ + struct utimbuf buf; +#define ATIME buf.actime +#define MTIME buf.modtime +#define UTIME_ARG &buf +#else /* HAVE_UTIMES */ + time_t buf[2]; +#define ATIME buf[0] +#define MTIME buf[1] +#define UTIME_ARG buf +#endif /* HAVE_UTIMES */ + + int have_unicode_filename = 0; +#ifdef Py_WIN_WIDE_FILENAMES + PyUnicodeObject *obwpath; + wchar_t *wpath; + if (unicode_file_names()) { + if (PyArg_ParseTuple(args, "UO|:utime", &obwpath, &arg)) { + wpath = PyUnicode_AS_UNICODE(obwpath); + have_unicode_filename = 1; + } else + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif /* Py_WIN_WIDE_FILENAMES */ + + if (!have_unicode_filename && \ + !PyArg_ParseTuple(args, "etO:utime", + Py_FileSystemDefaultEncoding, &path, &arg)) + return NULL; + if (arg == Py_None) { + /* optional time values not given */ + Py_BEGIN_ALLOW_THREADS +#ifdef Py_WIN_WIDE_FILENAMES + if (have_unicode_filename) + res = _wutime(wpath, NULL); + else +#endif /* Py_WIN_WIDE_FILENAMES */ + res = utime(path, NULL); + Py_END_ALLOW_THREADS + } + else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { + PyErr_SetString(PyExc_TypeError, + "utime() arg 2 must be a tuple (atime, mtime)"); + PyMem_Free(path); + return NULL; + } + else { + if (extract_time(PyTuple_GET_ITEM(arg, 0), + &atime, &ausec) == -1) { + PyMem_Free(path); + return NULL; + } + if (extract_time(PyTuple_GET_ITEM(arg, 1), + &mtime, &musec) == -1) { + PyMem_Free(path); + return NULL; + } + ATIME = atime; + MTIME = mtime; +#ifdef HAVE_UTIMES + buf[0].tv_usec = ausec; + buf[1].tv_usec = musec; + Py_BEGIN_ALLOW_THREADS + res = utimes(path, buf); + Py_END_ALLOW_THREADS +#else + Py_BEGIN_ALLOW_THREADS +#ifdef Py_WIN_WIDE_FILENAMES + if (have_unicode_filename) + /* utime is OK with utimbuf, but _wutime insists + on _utimbuf (the msvc headers assert the + underscore version is ansi) */ + res = _wutime(wpath, (struct _utimbuf *)UTIME_ARG); + else +#endif /* Py_WIN_WIDE_FILENAMES */ + res = utime(path, UTIME_ARG); + Py_END_ALLOW_THREADS +#endif /* HAVE_UTIMES */ + } + if (res < 0) { +#ifdef Py_WIN_WIDE_FILENAMES + if (have_unicode_filename) + return posix_error_with_unicode_filename(wpath); +#endif /* Py_WIN_WIDE_FILENAMES */ + return posix_error_with_allocated_filename(path); + } + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +#undef UTIME_ARG +#undef ATIME +#undef MTIME +} + + +/* Process operations */ + +PyDoc_STRVAR(posix__exit__doc__, +"_exit(status)\n\n\ +Exit to the system with specified status, without normal exit processing."); + +static PyObject * +posix__exit(PyObject *self, PyObject *args) +{ + int sts; + if (!PyArg_ParseTuple(args, "i:_exit", &sts)) + return NULL; + _exit(sts); + return NULL; /* Make gcc -Wall happy */ +} + +#if defined(HAVE_EXECV) || defined(HAVE_SPAWNV) +static void +free_string_array(char **array, Py_ssize_t count) +{ + Py_ssize_t i; + for (i = 0; i < count; i++) + PyMem_Free(array[i]); + PyMem_DEL(array); +} +#endif + + +#ifdef HAVE_EXECV +PyDoc_STRVAR(posix_execv__doc__, +"execv(path, args)\n\n\ +Execute an executable path with arguments, replacing current process.\n\ +\n\ + path: path of executable file\n\ + args: tuple or list of strings"); + +static PyObject * +posix_execv(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv; + char **argvlist; + Py_ssize_t i, argc; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + + /* execv has two arguments: (path, argv), where + argv is a list or tuple of strings. */ + + if (!PyArg_ParseTuple(args, "etO:execv", + Py_FileSystemDefaultEncoding, + &path, &argv)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, "execv() arg 2 must be a tuple or list"); + PyMem_Free(path); + return NULL; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyMem_Free(path); + return PyErr_NoMemory(); + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), "et", + Py_FileSystemDefaultEncoding, + &argvlist[i])) { + free_string_array(argvlist, i); + PyErr_SetString(PyExc_TypeError, + "execv() arg 2 must contain only strings"); + PyMem_Free(path); + return NULL; + + } + } + argvlist[argc] = NULL; + + execv(path, argvlist); + + /* If we get here it's definitely an error */ + + free_string_array(argvlist, argc); + PyMem_Free(path); + return posix_error(); +} + + +PyDoc_STRVAR(posix_execve__doc__, +"execve(path, args, env)\n\n\ +Execute a path with arguments and environment, replacing current process.\n\ +\n\ + path: path of executable file\n\ + args: tuple or list of arguments\n\ + env: dictionary of strings mapping to strings"); + +static PyObject * +posix_execve(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv, *env; + char **argvlist; + char **envlist; + PyObject *key, *val, *keys=NULL, *vals=NULL; + Py_ssize_t i, pos, argc, envc; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + int lastarg = 0; + + /* execve has three arguments: (path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (!PyArg_ParseTuple(args, "etOO:execve", + Py_FileSystemDefaultEncoding, + &path, &argv, &env)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "execve() arg 2 must be a tuple or list"); + goto fail_0; + } + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "execve() arg 3 must be a mapping object"); + goto fail_0; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + goto fail_0; + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), + "et;execve() arg 2 must contain only strings", + Py_FileSystemDefaultEncoding, + &argvlist[i])) + { + lastarg = i; + goto fail_1; + } + } + lastarg = argc; + argvlist[argc] = NULL; + + i = PyMapping_Size(env); + if (i < 0) + goto fail_1; + envlist = PyMem_NEW(char *, i + 1); + if (envlist == NULL) { + PyErr_NoMemory(); + goto fail_1; + } + envc = 0; + keys = PyMapping_Keys(env); + vals = PyMapping_Values(env); + if (!keys || !vals) + goto fail_2; + if (!PyList_Check(keys) || !PyList_Check(vals)) { + PyErr_SetString(PyExc_TypeError, + "execve(): env.keys() or env.values() is not a list"); + goto fail_2; + } + + for (pos = 0; pos < i; pos++) { + char *p, *k, *v; + size_t len; + + key = PyList_GetItem(keys, pos); + val = PyList_GetItem(vals, pos); + if (!key || !val) + goto fail_2; + + if (!PyArg_Parse( + key, + "s;execve() arg 3 contains a non-string key", + &k) || + !PyArg_Parse( + val, + "s;execve() arg 3 contains a non-string value", + &v)) + { + goto fail_2; + } + +#if defined(PYOS_OS2) + /* Omit Pseudo-Env Vars that Would Confuse Programs if Passed On */ + if (stricmp(k, "BEGINLIBPATH") != 0 && stricmp(k, "ENDLIBPATH") != 0) { +#endif + len = PyString_Size(key) + PyString_Size(val) + 2; + p = PyMem_NEW(char, len); + if (p == NULL) { + PyErr_NoMemory(); + goto fail_2; + } + PyOS_snprintf(p, len, "%s=%s", k, v); + envlist[envc++] = p; +#if defined(PYOS_OS2) + } +#endif + } + envlist[envc] = 0; + + execve(path, argvlist, envlist); + + /* If we get here it's definitely an error */ + + (void) posix_error(); + + fail_2: + while (--envc >= 0) + PyMem_DEL(envlist[envc]); + PyMem_DEL(envlist); + fail_1: + free_string_array(argvlist, lastarg); + Py_XDECREF(vals); + Py_XDECREF(keys); + fail_0: + PyMem_Free(path); + return NULL; +} +#endif /* HAVE_EXECV */ + + +#ifdef HAVE_SPAWNV +PyDoc_STRVAR(posix_spawnv__doc__, +"spawnv(mode, path, args)\n\n\ +Execute the program 'path' in a new process.\n\ +\n\ + mode: mode of process creation\n\ + path: path of executable file\n\ + args: tuple or list of strings"); + +static PyObject * +posix_spawnv(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv; + char **argvlist; + int mode, i, argc; + Py_intptr_t spawnval; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + + /* spawnv has three arguments: (mode, path, argv), where + argv is a list or tuple of strings. */ + + if (!PyArg_ParseTuple(args, "ietO:spawnv", &mode, + Py_FileSystemDefaultEncoding, + &path, &argv)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnv() arg 2 must be a tuple or list"); + PyMem_Free(path); + return NULL; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyMem_Free(path); + return PyErr_NoMemory(); + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), "et", + Py_FileSystemDefaultEncoding, + &argvlist[i])) { + free_string_array(argvlist, i); + PyErr_SetString( + PyExc_TypeError, + "spawnv() arg 2 must contain only strings"); + PyMem_Free(path); + return NULL; + } + } + argvlist[argc] = NULL; + +#if defined(PYOS_OS2) && defined(PYCC_GCC) + Py_BEGIN_ALLOW_THREADS + spawnval = spawnv(mode, path, argvlist); + Py_END_ALLOW_THREADS +#else + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; + + Py_BEGIN_ALLOW_THREADS + spawnval = _spawnv(mode, path, argvlist); + Py_END_ALLOW_THREADS +#endif + + free_string_array(argvlist, argc); + PyMem_Free(path); + + if (spawnval == -1) + return posix_error(); + else +#if SIZEOF_LONG == SIZEOF_VOID_P + return Py_BuildValue("l", (long) spawnval); +#else + return Py_BuildValue("L", (PY_LONG_LONG) spawnval); +#endif +} + + +PyDoc_STRVAR(posix_spawnve__doc__, +"spawnve(mode, path, args, env)\n\n\ +Execute the program 'path' in a new process.\n\ +\n\ + mode: mode of process creation\n\ + path: path of executable file\n\ + args: tuple or list of arguments\n\ + env: dictionary of strings mapping to strings"); + +static PyObject * +posix_spawnve(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv, *env; + char **argvlist; + char **envlist; + PyObject *key, *val, *keys=NULL, *vals=NULL, *res=NULL; + int mode, i, pos, argc, envc; + Py_intptr_t spawnval; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + int lastarg = 0; + + /* spawnve has four arguments: (mode, path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (!PyArg_ParseTuple(args, "ietOO:spawnve", &mode, + Py_FileSystemDefaultEncoding, + &path, &argv, &env)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnve() arg 2 must be a tuple or list"); + goto fail_0; + } + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "spawnve() arg 3 must be a mapping object"); + goto fail_0; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + goto fail_0; + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), + "et;spawnve() arg 2 must contain only strings", + Py_FileSystemDefaultEncoding, + &argvlist[i])) + { + lastarg = i; + goto fail_1; + } + } + lastarg = argc; + argvlist[argc] = NULL; + + i = PyMapping_Size(env); + if (i < 0) + goto fail_1; + envlist = PyMem_NEW(char *, i + 1); + if (envlist == NULL) { + PyErr_NoMemory(); + goto fail_1; + } + envc = 0; + keys = PyMapping_Keys(env); + vals = PyMapping_Values(env); + if (!keys || !vals) + goto fail_2; + if (!PyList_Check(keys) || !PyList_Check(vals)) { + PyErr_SetString(PyExc_TypeError, + "spawnve(): env.keys() or env.values() is not a list"); + goto fail_2; + } + + for (pos = 0; pos < i; pos++) { + char *p, *k, *v; + size_t len; + + key = PyList_GetItem(keys, pos); + val = PyList_GetItem(vals, pos); + if (!key || !val) + goto fail_2; + + if (!PyArg_Parse( + key, + "s;spawnve() arg 3 contains a non-string key", + &k) || + !PyArg_Parse( + val, + "s;spawnve() arg 3 contains a non-string value", + &v)) + { + goto fail_2; + } + len = PyString_Size(key) + PyString_Size(val) + 2; + p = PyMem_NEW(char, len); + if (p == NULL) { + PyErr_NoMemory(); + goto fail_2; + } + PyOS_snprintf(p, len, "%s=%s", k, v); + envlist[envc++] = p; + } + envlist[envc] = 0; + +#if defined(PYOS_OS2) && defined(PYCC_GCC) + Py_BEGIN_ALLOW_THREADS + spawnval = spawnve(mode, path, argvlist, envlist); + Py_END_ALLOW_THREADS +#else + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; + + Py_BEGIN_ALLOW_THREADS + spawnval = _spawnve(mode, path, argvlist, envlist); + Py_END_ALLOW_THREADS +#endif + + if (spawnval == -1) + (void) posix_error(); + else +#if SIZEOF_LONG == SIZEOF_VOID_P + res = Py_BuildValue("l", (long) spawnval); +#else + res = Py_BuildValue("L", (PY_LONG_LONG) spawnval); +#endif + + fail_2: + while (--envc >= 0) + PyMem_DEL(envlist[envc]); + PyMem_DEL(envlist); + fail_1: + free_string_array(argvlist, lastarg); + Py_XDECREF(vals); + Py_XDECREF(keys); + fail_0: + PyMem_Free(path); + return res; +} + +/* OS/2 supports spawnvp & spawnvpe natively */ +#if defined(PYOS_OS2) +PyDoc_STRVAR(posix_spawnvp__doc__, +"spawnvp(mode, file, args)\n\n\ +Execute the program 'file' in a new process, using the environment\n\ +search path to find the file.\n\ +\n\ + mode: mode of process creation\n\ + file: executable file name\n\ + args: tuple or list of strings"); + +static PyObject * +posix_spawnvp(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv; + char **argvlist; + int mode, i, argc; + Py_intptr_t spawnval; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + + /* spawnvp has three arguments: (mode, path, argv), where + argv is a list or tuple of strings. */ + + if (!PyArg_ParseTuple(args, "ietO:spawnvp", &mode, + Py_FileSystemDefaultEncoding, + &path, &argv)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnvp() arg 2 must be a tuple or list"); + PyMem_Free(path); + return NULL; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyMem_Free(path); + return PyErr_NoMemory(); + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), "et", + Py_FileSystemDefaultEncoding, + &argvlist[i])) { + free_string_array(argvlist, i); + PyErr_SetString( + PyExc_TypeError, + "spawnvp() arg 2 must contain only strings"); + PyMem_Free(path); + return NULL; + } + } + argvlist[argc] = NULL; + + Py_BEGIN_ALLOW_THREADS +#if defined(PYCC_GCC) + spawnval = spawnvp(mode, path, argvlist); +#else + spawnval = _spawnvp(mode, path, argvlist); +#endif + Py_END_ALLOW_THREADS + + free_string_array(argvlist, argc); + PyMem_Free(path); + + if (spawnval == -1) + return posix_error(); + else + return Py_BuildValue("l", (long) spawnval); +} + + +PyDoc_STRVAR(posix_spawnvpe__doc__, +"spawnvpe(mode, file, args, env)\n\n\ +Execute the program 'file' in a new process, using the environment\n\ +search path to find the file.\n\ +\n\ + mode: mode of process creation\n\ + file: executable file name\n\ + args: tuple or list of arguments\n\ + env: dictionary of strings mapping to strings"); + +static PyObject * +posix_spawnvpe(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv, *env; + char **argvlist; + char **envlist; + PyObject *key, *val, *keys=NULL, *vals=NULL, *res=NULL; + int mode, i, pos, argc, envc; + Py_intptr_t spawnval; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + int lastarg = 0; + + /* spawnvpe has four arguments: (mode, path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (!PyArg_ParseTuple(args, "ietOO:spawnvpe", &mode, + Py_FileSystemDefaultEncoding, + &path, &argv, &env)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnvpe() arg 2 must be a tuple or list"); + goto fail_0; + } + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "spawnvpe() arg 3 must be a mapping object"); + goto fail_0; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + goto fail_0; + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), + "et;spawnvpe() arg 2 must contain only strings", + Py_FileSystemDefaultEncoding, + &argvlist[i])) + { + lastarg = i; + goto fail_1; + } + } + lastarg = argc; + argvlist[argc] = NULL; + + i = PyMapping_Size(env); + if (i < 0) + goto fail_1; + envlist = PyMem_NEW(char *, i + 1); + if (envlist == NULL) { + PyErr_NoMemory(); + goto fail_1; + } + envc = 0; + keys = PyMapping_Keys(env); + vals = PyMapping_Values(env); + if (!keys || !vals) + goto fail_2; + if (!PyList_Check(keys) || !PyList_Check(vals)) { + PyErr_SetString(PyExc_TypeError, + "spawnvpe(): env.keys() or env.values() is not a list"); + goto fail_2; + } + + for (pos = 0; pos < i; pos++) { + char *p, *k, *v; + size_t len; + + key = PyList_GetItem(keys, pos); + val = PyList_GetItem(vals, pos); + if (!key || !val) + goto fail_2; + + if (!PyArg_Parse( + key, + "s;spawnvpe() arg 3 contains a non-string key", + &k) || + !PyArg_Parse( + val, + "s;spawnvpe() arg 3 contains a non-string value", + &v)) + { + goto fail_2; + } + len = PyString_Size(key) + PyString_Size(val) + 2; + p = PyMem_NEW(char, len); + if (p == NULL) { + PyErr_NoMemory(); + goto fail_2; + } + PyOS_snprintf(p, len, "%s=%s", k, v); + envlist[envc++] = p; + } + envlist[envc] = 0; + + Py_BEGIN_ALLOW_THREADS +#if defined(PYCC_GCC) + spawnval = spawnve(mode, path, argvlist, envlist); +#else + spawnval = _spawnve(mode, path, argvlist, envlist); +#endif + Py_END_ALLOW_THREADS + + if (spawnval == -1) + (void) posix_error(); + else + res = Py_BuildValue("l", (long) spawnval); + + fail_2: + while (--envc >= 0) + PyMem_DEL(envlist[envc]); + PyMem_DEL(envlist); + fail_1: + free_string_array(argvlist, lastarg); + Py_XDECREF(vals); + Py_XDECREF(keys); + fail_0: + PyMem_Free(path); + return res; +} +#endif /* PYOS_OS2 */ +#endif /* HAVE_SPAWNV */ + + +#ifdef HAVE_FORK1 +PyDoc_STRVAR(posix_fork1__doc__, +"fork1() -> pid\n\n\ +Fork a child process with a single multiplexed (i.e., not bound) thread.\n\ +\n\ +Return 0 to child process and PID of child to parent process."); + +static PyObject * +posix_fork1(PyObject *self, PyObject *noargs) +{ + int pid = fork1(); + if (pid == -1) + return posix_error(); + PyOS_AfterFork(); + return PyInt_FromLong((long)pid); +} +#endif + + +#ifdef HAVE_FORK +PyDoc_STRVAR(posix_fork__doc__, +"fork() -> pid\n\n\ +Fork a child process.\n\ +Return 0 to child process and PID of child to parent process."); + +static PyObject * +posix_fork(PyObject *self, PyObject *noargs) +{ + int pid = fork(); + if (pid == -1) + return posix_error(); + if (pid == 0) + PyOS_AfterFork(); + return PyInt_FromLong((long)pid); +} +#endif + +/* AIX uses /dev/ptc but is otherwise the same as /dev/ptmx */ +/* IRIX has both /dev/ptc and /dev/ptmx, use ptmx */ +#if defined(HAVE_DEV_PTC) && !defined(HAVE_DEV_PTMX) +#define DEV_PTY_FILE "/dev/ptc" +#define HAVE_DEV_PTMX +#else +#define DEV_PTY_FILE "/dev/ptmx" +#endif + +#if defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_DEV_PTMX) +#ifdef HAVE_PTY_H +#include +#else +#ifdef HAVE_LIBUTIL_H +#include +#endif /* HAVE_LIBUTIL_H */ +#endif /* HAVE_PTY_H */ +#ifdef HAVE_STROPTS_H +#include +#endif +#endif /* defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_DEV_PTMX */ + +#if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) +PyDoc_STRVAR(posix_openpty__doc__, +"openpty() -> (master_fd, slave_fd)\n\n\ +Open a pseudo-terminal, returning open fd's for both master and slave end.\n"); + +static PyObject * +posix_openpty(PyObject *self, PyObject *noargs) +{ + int master_fd, slave_fd; +#ifndef HAVE_OPENPTY + char * slave_name; +#endif +#if defined(HAVE_DEV_PTMX) && !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY) + PyOS_sighandler_t sig_saved; +#ifdef sun + extern char *ptsname(); +#endif +#endif + +#ifdef HAVE_OPENPTY + if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0) + return posix_error(); +#elif defined(HAVE__GETPTY) + slave_name = _getpty(&master_fd, O_RDWR, 0666, 0); + if (slave_name == NULL) + return posix_error(); + + slave_fd = open(slave_name, O_RDWR); + if (slave_fd < 0) + return posix_error(); +#else + master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */ + if (master_fd < 0) + return posix_error(); + sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL); + /* change permission of slave */ + if (grantpt(master_fd) < 0) { + PyOS_setsig(SIGCHLD, sig_saved); + return posix_error(); + } + /* unlock slave */ + if (unlockpt(master_fd) < 0) { + PyOS_setsig(SIGCHLD, sig_saved); + return posix_error(); + } + PyOS_setsig(SIGCHLD, sig_saved); + slave_name = ptsname(master_fd); /* get name of slave */ + if (slave_name == NULL) + return posix_error(); + slave_fd = open(slave_name, O_RDWR | O_NOCTTY); /* open slave */ + if (slave_fd < 0) + return posix_error(); +#if !defined(__CYGWIN__) && !defined(HAVE_DEV_PTC) + ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */ + ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */ +#ifndef __hpux + ioctl(slave_fd, I_PUSH, "ttcompat"); /* push ttcompat */ +#endif /* __hpux */ +#endif /* HAVE_CYGWIN */ +#endif /* HAVE_OPENPTY */ + + return Py_BuildValue("(ii)", master_fd, slave_fd); + +} +#endif /* defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) */ + +#ifdef HAVE_FORKPTY +PyDoc_STRVAR(posix_forkpty__doc__, +"forkpty() -> (pid, master_fd)\n\n\ +Fork a new process with a new pseudo-terminal as controlling tty.\n\n\ +Like fork(), return 0 as pid to child process, and PID of child to parent.\n\ +To both, return fd of newly opened pseudo-terminal.\n"); + +static PyObject * +posix_forkpty(PyObject *self, PyObject *noargs) +{ + int master_fd = -1, pid; + + pid = forkpty(&master_fd, NULL, NULL, NULL); + if (pid == -1) + return posix_error(); + if (pid == 0) + PyOS_AfterFork(); + return Py_BuildValue("(ii)", pid, master_fd); +} +#endif + +#ifdef HAVE_GETEGID +PyDoc_STRVAR(posix_getegid__doc__, +"getegid() -> egid\n\n\ +Return the current process's effective group id."); + +static PyObject * +posix_getegid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getegid()); +} +#endif + + +#ifdef HAVE_GETEUID +PyDoc_STRVAR(posix_geteuid__doc__, +"geteuid() -> euid\n\n\ +Return the current process's effective user id."); + +static PyObject * +posix_geteuid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)geteuid()); +} +#endif + + +#ifdef HAVE_GETGID +PyDoc_STRVAR(posix_getgid__doc__, +"getgid() -> gid\n\n\ +Return the current process's group id."); + +static PyObject * +posix_getgid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getgid()); +} +#endif + + +PyDoc_STRVAR(posix_getpid__doc__, +"getpid() -> pid\n\n\ +Return the current process id"); + +static PyObject * +posix_getpid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getpid()); +} + + +#ifdef HAVE_GETGROUPS +PyDoc_STRVAR(posix_getgroups__doc__, +"getgroups() -> list of group IDs\n\n\ +Return list of supplemental group IDs for the process."); + +static PyObject * +posix_getgroups(PyObject *self, PyObject *noargs) +{ + PyObject *result = NULL; + +#ifdef NGROUPS_MAX +#define MAX_GROUPS NGROUPS_MAX +#else + /* defined to be 16 on Solaris7, so this should be a small number */ +#define MAX_GROUPS 64 +#endif + gid_t grouplist[MAX_GROUPS]; + int n; + + n = getgroups(MAX_GROUPS, grouplist); + if (n < 0) + posix_error(); + else { + result = PyList_New(n); + if (result != NULL) { + int i; + for (i = 0; i < n; ++i) { + PyObject *o = PyInt_FromLong((long)grouplist[i]); + if (o == NULL) { + Py_DECREF(result); + result = NULL; + break; + } + PyList_SET_ITEM(result, i, o); + } + } + } + + return result; +} +#endif + +#ifdef HAVE_GETPGID +PyDoc_STRVAR(posix_getpgid__doc__, +"getpgid(pid) -> pgid\n\n\ +Call the system call getpgid()."); + +static PyObject * +posix_getpgid(PyObject *self, PyObject *args) +{ + int pid, pgid; + if (!PyArg_ParseTuple(args, "i:getpgid", &pid)) + return NULL; + pgid = getpgid(pid); + if (pgid < 0) + return posix_error(); + return PyInt_FromLong((long)pgid); +} +#endif /* HAVE_GETPGID */ + + +#ifdef HAVE_GETPGRP +PyDoc_STRVAR(posix_getpgrp__doc__, +"getpgrp() -> pgrp\n\n\ +Return the current process group id."); + +static PyObject * +posix_getpgrp(PyObject *self, PyObject *noargs) +{ +#ifdef GETPGRP_HAVE_ARG + return PyInt_FromLong((long)getpgrp(0)); +#else /* GETPGRP_HAVE_ARG */ + return PyInt_FromLong((long)getpgrp()); +#endif /* GETPGRP_HAVE_ARG */ +} +#endif /* HAVE_GETPGRP */ + + +#ifdef HAVE_SETPGRP +PyDoc_STRVAR(posix_setpgrp__doc__, +"setpgrp()\n\n\ +Make this process a session leader."); + +static PyObject * +posix_setpgrp(PyObject *self, PyObject *noargs) +{ +#ifdef SETPGRP_HAVE_ARG + if (setpgrp(0, 0) < 0) +#else /* SETPGRP_HAVE_ARG */ + if (setpgrp() < 0) +#endif /* SETPGRP_HAVE_ARG */ + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + +#endif /* HAVE_SETPGRP */ + +#ifdef HAVE_GETPPID +PyDoc_STRVAR(posix_getppid__doc__, +"getppid() -> ppid\n\n\ +Return the parent's process id."); + +static PyObject * +posix_getppid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getppid()); +} +#endif + + +#ifdef HAVE_GETLOGIN +PyDoc_STRVAR(posix_getlogin__doc__, +"getlogin() -> string\n\n\ +Return the actual login name."); + +static PyObject * +posix_getlogin(PyObject *self, PyObject *noargs) +{ + PyObject *result = NULL; + char *name; + int old_errno = errno; + + errno = 0; + name = getlogin(); + if (name == NULL) { + if (errno) + posix_error(); + else + PyErr_SetString(PyExc_OSError, + "unable to determine login name"); + } + else + result = PyString_FromString(name); + errno = old_errno; + + return result; +} +#endif + +#ifdef HAVE_GETUID +PyDoc_STRVAR(posix_getuid__doc__, +"getuid() -> uid\n\n\ +Return the current process's user id."); + +static PyObject * +posix_getuid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getuid()); +} +#endif + + +#ifdef HAVE_KILL +PyDoc_STRVAR(posix_kill__doc__, +"kill(pid, sig)\n\n\ +Kill a process with a signal."); + +static PyObject * +posix_kill(PyObject *self, PyObject *args) +{ + int pid, sig; + if (!PyArg_ParseTuple(args, "ii:kill", &pid, &sig)) + return NULL; +#if defined(PYOS_OS2) && !defined(PYCC_GCC) + if (sig == XCPT_SIGNAL_INTR || sig == XCPT_SIGNAL_BREAK) { + APIRET rc; + if ((rc = DosSendSignalException(pid, sig)) != NO_ERROR) + return os2_error(rc); + + } else if (sig == XCPT_SIGNAL_KILLPROC) { + APIRET rc; + if ((rc = DosKillProcess(DKP_PROCESS, pid)) != NO_ERROR) + return os2_error(rc); + + } else + return NULL; /* Unrecognized Signal Requested */ +#else + if (kill(pid, sig) == -1) + return posix_error(); +#endif + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_KILLPG +PyDoc_STRVAR(posix_killpg__doc__, +"killpg(pgid, sig)\n\n\ +Kill a process group with a signal."); + +static PyObject * +posix_killpg(PyObject *self, PyObject *args) +{ + int pgid, sig; + if (!PyArg_ParseTuple(args, "ii:killpg", &pgid, &sig)) + return NULL; + if (killpg(pgid, sig) == -1) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_PLOCK + +#ifdef HAVE_SYS_LOCK_H +#include +#endif + +PyDoc_STRVAR(posix_plock__doc__, +"plock(op)\n\n\ +Lock program segments into memory."); + +static PyObject * +posix_plock(PyObject *self, PyObject *args) +{ + int op; + if (!PyArg_ParseTuple(args, "i:plock", &op)) + return NULL; + if (plock(op) == -1) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + + +#ifdef HAVE_POPEN +PyDoc_STRVAR(posix_popen__doc__, +"popen(command [, mode='r' [, bufsize]]) -> pipe\n\n\ +Open a pipe to/from a command returning a file object."); + +#if defined(PYOS_OS2) +#if defined(PYCC_VACPP) +static int +async_system(const char *command) +{ + char errormsg[256], args[1024]; + RESULTCODES rcodes; + APIRET rc; + + char *shell = getenv("COMSPEC"); + if (!shell) + shell = "cmd"; + + /* avoid overflowing the argument buffer */ + if (strlen(shell) + 3 + strlen(command) >= 1024) + return ERROR_NOT_ENOUGH_MEMORY + + args[0] = '\0'; + strcat(args, shell); + strcat(args, "/c "); + strcat(args, command); + + /* execute asynchronously, inheriting the environment */ + rc = DosExecPgm(errormsg, + sizeof(errormsg), + EXEC_ASYNC, + args, + NULL, + &rcodes, + shell); + return rc; +} + +static FILE * +popen(const char *command, const char *mode, int pipesize, int *err) +{ + int oldfd, tgtfd; + HFILE pipeh[2]; + APIRET rc; + + /* mode determines which of stdin or stdout is reconnected to + * the pipe to the child + */ + if (strchr(mode, 'r') != NULL) { + tgt_fd = 1; /* stdout */ + } else if (strchr(mode, 'w')) { + tgt_fd = 0; /* stdin */ + } else { + *err = ERROR_INVALID_ACCESS; + return NULL; + } + + /* setup the pipe */ + if ((rc = DosCreatePipe(&pipeh[0], &pipeh[1], pipesize)) != NO_ERROR) { + *err = rc; + return NULL; + } + + /* prevent other threads accessing stdio */ + DosEnterCritSec(); + + /* reconnect stdio and execute child */ + oldfd = dup(tgtfd); + close(tgtfd); + if (dup2(pipeh[tgtfd], tgtfd) == 0) { + DosClose(pipeh[tgtfd]); + rc = async_system(command); + } + + /* restore stdio */ + dup2(oldfd, tgtfd); + close(oldfd); + + /* allow other threads access to stdio */ + DosExitCritSec(); + + /* if execution of child was successful return file stream */ + if (rc == NO_ERROR) + return fdopen(pipeh[1 - tgtfd], mode); + else { + DosClose(pipeh[1 - tgtfd]); + *err = rc; + return NULL; + } +} + +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + char *name; + char *mode = "r"; + int err, bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "s|si:popen", &name, &mode, &bufsize)) + return NULL; + Py_BEGIN_ALLOW_THREADS + fp = popen(name, mode, (bufsize > 0) ? bufsize : 4096, &err); + Py_END_ALLOW_THREADS + if (fp == NULL) + return os2_error(err); + + f = PyFile_FromFile(fp, name, mode, fclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +#elif defined(PYCC_GCC) + +/* standard posix version of popen() support */ +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + char *name; + char *mode = "r"; + int bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "s|si:popen", &name, &mode, &bufsize)) + return NULL; + Py_BEGIN_ALLOW_THREADS + fp = popen(name, mode); + Py_END_ALLOW_THREADS + if (fp == NULL) + return posix_error(); + f = PyFile_FromFile(fp, name, mode, pclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +/* fork() under OS/2 has lots'o'warts + * EMX supports pipe() and spawn*() so we can synthesize popen[234]() + * most of this code is a ripoff of the win32 code, but using the + * capabilities of EMX's C library routines + */ + +/* These tell _PyPopen() whether to return 1, 2, or 3 file objects. */ +#define POPEN_1 1 +#define POPEN_2 2 +#define POPEN_3 3 +#define POPEN_4 4 + +static PyObject *_PyPopen(char *, int, int, int); +static int _PyPclose(FILE *file); + +/* + * Internal dictionary mapping popen* file pointers to process handles, + * for use when retrieving the process exit code. See _PyPclose() below + * for more information on this dictionary's use. + */ +static PyObject *_PyPopenProcs = NULL; + +/* os2emx version of popen2() + * + * The result of this function is a pipe (file) connected to the + * process's stdin, and a pipe connected to the process's stdout. + */ + +static PyObject * +os2emx_popen2(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm=0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen2", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'"); + return NULL; + } else + tm = O_BINARY; + + f = _PyPopen(cmdstring, tm, POPEN_2, bufsize); + + return f; +} + +/* + * Variation on os2emx.popen2 + * + * The result of this function is 3 pipes - the process's stdin, + * stdout and stderr + */ + +static PyObject * +os2emx_popen3(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen3", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'"); + return NULL; + } else + tm = O_BINARY; + + f = _PyPopen(cmdstring, tm, POPEN_3, bufsize); + + return f; +} + +/* + * Variation on os2emx.popen2 + * + * The result of this function is 2 pipes - the processes stdin, + * and stdout+stderr combined as a single pipe. + */ + +static PyObject * +os2emx_popen4(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen4", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'"); + return NULL; + } else + tm = O_BINARY; + + f = _PyPopen(cmdstring, tm, POPEN_4, bufsize); + + return f; +} + +/* a couple of structures for convenient handling of multiple + * file handles and pipes + */ +struct file_ref +{ + int handle; + int flags; +}; + +struct pipe_ref +{ + int rd; + int wr; +}; + +/* The following code is derived from the win32 code */ + +static PyObject * +_PyPopen(char *cmdstring, int mode, int n, int bufsize) +{ + struct file_ref stdio[3]; + struct pipe_ref p_fd[3]; + FILE *p_s[3]; + int file_count, i, pipe_err, pipe_pid; + char *shell, *sh_name, *opt, *rd_mode, *wr_mode; + PyObject *f, *p_f[3]; + + /* file modes for subsequent fdopen's on pipe handles */ + if (mode == O_TEXT) + { + rd_mode = "rt"; + wr_mode = "wt"; + } + else + { + rd_mode = "rb"; + wr_mode = "wb"; + } + + /* prepare shell references */ + if ((shell = getenv("EMXSHELL")) == NULL) + if ((shell = getenv("COMSPEC")) == NULL) + { + errno = ENOENT; + return posix_error(); + } + + sh_name = _getname(shell); + if (stricmp(sh_name, "cmd.exe") == 0 || stricmp(sh_name, "4os2.exe") == 0) + opt = "/c"; + else + opt = "-c"; + + /* save current stdio fds + their flags, and set not inheritable */ + i = pipe_err = 0; + while (pipe_err >= 0 && i < 3) + { + pipe_err = stdio[i].handle = dup(i); + stdio[i].flags = fcntl(i, F_GETFD, 0); + fcntl(stdio[i].handle, F_SETFD, stdio[i].flags | FD_CLOEXEC); + i++; + } + if (pipe_err < 0) + { + /* didn't get them all saved - clean up and bail out */ + int saved_err = errno; + while (i-- > 0) + { + close(stdio[i].handle); + } + errno = saved_err; + return posix_error(); + } + + /* create pipe ends */ + file_count = 2; + if (n == POPEN_3) + file_count = 3; + i = pipe_err = 0; + while ((pipe_err == 0) && (i < file_count)) + pipe_err = pipe((int *)&p_fd[i++]); + if (pipe_err < 0) + { + /* didn't get them all made - clean up and bail out */ + while (i-- > 0) + { + close(p_fd[i].wr); + close(p_fd[i].rd); + } + errno = EPIPE; + return posix_error(); + } + + /* change the actual standard IO streams over temporarily, + * making the retained pipe ends non-inheritable + */ + pipe_err = 0; + + /* - stdin */ + if (dup2(p_fd[0].rd, 0) == 0) + { + close(p_fd[0].rd); + i = fcntl(p_fd[0].wr, F_GETFD, 0); + fcntl(p_fd[0].wr, F_SETFD, i | FD_CLOEXEC); + if ((p_s[0] = fdopen(p_fd[0].wr, wr_mode)) == NULL) + { + close(p_fd[0].wr); + pipe_err = -1; + } + } + else + { + pipe_err = -1; + } + + /* - stdout */ + if (pipe_err == 0) + { + if (dup2(p_fd[1].wr, 1) == 1) + { + close(p_fd[1].wr); + i = fcntl(p_fd[1].rd, F_GETFD, 0); + fcntl(p_fd[1].rd, F_SETFD, i | FD_CLOEXEC); + if ((p_s[1] = fdopen(p_fd[1].rd, rd_mode)) == NULL) + { + close(p_fd[1].rd); + pipe_err = -1; + } + } + else + { + pipe_err = -1; + } + } + + /* - stderr, as required */ + if (pipe_err == 0) + switch (n) + { + case POPEN_3: + { + if (dup2(p_fd[2].wr, 2) == 2) + { + close(p_fd[2].wr); + i = fcntl(p_fd[2].rd, F_GETFD, 0); + fcntl(p_fd[2].rd, F_SETFD, i | FD_CLOEXEC); + if ((p_s[2] = fdopen(p_fd[2].rd, rd_mode)) == NULL) + { + close(p_fd[2].rd); + pipe_err = -1; + } + } + else + { + pipe_err = -1; + } + break; + } + + case POPEN_4: + { + if (dup2(1, 2) != 2) + { + pipe_err = -1; + } + break; + } + } + + /* spawn the child process */ + if (pipe_err == 0) + { + pipe_pid = spawnlp(P_NOWAIT, shell, shell, opt, cmdstring, (char *)0); + if (pipe_pid == -1) + { + pipe_err = -1; + } + else + { + /* save the PID into the FILE structure + * NOTE: this implementation doesn't actually + * take advantage of this, but do it for + * completeness - AIM Apr01 + */ + for (i = 0; i < file_count; i++) + p_s[i]->_pid = pipe_pid; + } + } + + /* reset standard IO to normal */ + for (i = 0; i < 3; i++) + { + dup2(stdio[i].handle, i); + fcntl(i, F_SETFD, stdio[i].flags); + close(stdio[i].handle); + } + + /* if any remnant problems, clean up and bail out */ + if (pipe_err < 0) + { + for (i = 0; i < 3; i++) + { + close(p_fd[i].rd); + close(p_fd[i].wr); + } + errno = EPIPE; + return posix_error_with_filename(cmdstring); + } + + /* build tuple of file objects to return */ + if ((p_f[0] = PyFile_FromFile(p_s[0], cmdstring, wr_mode, _PyPclose)) != NULL) + PyFile_SetBufSize(p_f[0], bufsize); + if ((p_f[1] = PyFile_FromFile(p_s[1], cmdstring, rd_mode, _PyPclose)) != NULL) + PyFile_SetBufSize(p_f[1], bufsize); + if (n == POPEN_3) + { + if ((p_f[2] = PyFile_FromFile(p_s[2], cmdstring, rd_mode, _PyPclose)) != NULL) + PyFile_SetBufSize(p_f[0], bufsize); + f = PyTuple_Pack(3, p_f[0], p_f[1], p_f[2]); + } + else + f = PyTuple_Pack(2, p_f[0], p_f[1]); + + /* + * Insert the files we've created into the process dictionary + * all referencing the list with the process handle and the + * initial number of files (see description below in _PyPclose). + * Since if _PyPclose later tried to wait on a process when all + * handles weren't closed, it could create a deadlock with the + * child, we spend some energy here to try to ensure that we + * either insert all file handles into the dictionary or none + * at all. It's a little clumsy with the various popen modes + * and variable number of files involved. + */ + if (!_PyPopenProcs) + { + _PyPopenProcs = PyDict_New(); + } + + if (_PyPopenProcs) + { + PyObject *procObj, *pidObj, *intObj, *fileObj[3]; + int ins_rc[3]; + + fileObj[0] = fileObj[1] = fileObj[2] = NULL; + ins_rc[0] = ins_rc[1] = ins_rc[2] = 0; + + procObj = PyList_New(2); + pidObj = PyInt_FromLong((long) pipe_pid); + intObj = PyInt_FromLong((long) file_count); + + if (procObj && pidObj && intObj) + { + PyList_SetItem(procObj, 0, pidObj); + PyList_SetItem(procObj, 1, intObj); + + fileObj[0] = PyLong_FromVoidPtr(p_s[0]); + if (fileObj[0]) + { + ins_rc[0] = PyDict_SetItem(_PyPopenProcs, + fileObj[0], + procObj); + } + fileObj[1] = PyLong_FromVoidPtr(p_s[1]); + if (fileObj[1]) + { + ins_rc[1] = PyDict_SetItem(_PyPopenProcs, + fileObj[1], + procObj); + } + if (file_count >= 3) + { + fileObj[2] = PyLong_FromVoidPtr(p_s[2]); + if (fileObj[2]) + { + ins_rc[2] = PyDict_SetItem(_PyPopenProcs, + fileObj[2], + procObj); + } + } + + if (ins_rc[0] < 0 || !fileObj[0] || + ins_rc[1] < 0 || (file_count > 1 && !fileObj[1]) || + ins_rc[2] < 0 || (file_count > 2 && !fileObj[2])) + { + /* Something failed - remove any dictionary + * entries that did make it. + */ + if (!ins_rc[0] && fileObj[0]) + { + PyDict_DelItem(_PyPopenProcs, + fileObj[0]); + } + if (!ins_rc[1] && fileObj[1]) + { + PyDict_DelItem(_PyPopenProcs, + fileObj[1]); + } + if (!ins_rc[2] && fileObj[2]) + { + PyDict_DelItem(_PyPopenProcs, + fileObj[2]); + } + } + } + + /* + * Clean up our localized references for the dictionary keys + * and value since PyDict_SetItem will Py_INCREF any copies + * that got placed in the dictionary. + */ + Py_XDECREF(procObj); + Py_XDECREF(fileObj[0]); + Py_XDECREF(fileObj[1]); + Py_XDECREF(fileObj[2]); + } + + /* Child is launched. */ + return f; +} + +/* + * Wrapper for fclose() to use for popen* files, so we can retrieve the + * exit code for the child process and return as a result of the close. + * + * This function uses the _PyPopenProcs dictionary in order to map the + * input file pointer to information about the process that was + * originally created by the popen* call that created the file pointer. + * The dictionary uses the file pointer as a key (with one entry + * inserted for each file returned by the original popen* call) and a + * single list object as the value for all files from a single call. + * The list object contains the Win32 process handle at [0], and a file + * count at [1], which is initialized to the total number of file + * handles using that list. + * + * This function closes whichever handle it is passed, and decrements + * the file count in the dictionary for the process handle pointed to + * by this file. On the last close (when the file count reaches zero), + * this function will wait for the child process and then return its + * exit code as the result of the close() operation. This permits the + * files to be closed in any order - it is always the close() of the + * final handle that will return the exit code. + * + * NOTE: This function is currently called with the GIL released. + * hence we use the GILState API to manage our state. + */ + +static int _PyPclose(FILE *file) +{ + int result; + int exit_code; + int pipe_pid; + PyObject *procObj, *pidObj, *intObj, *fileObj; + int file_count; +#ifdef WITH_THREAD + PyGILState_STATE state; +#endif + + /* Close the file handle first, to ensure it can't block the + * child from exiting if it's the last handle. + */ + result = fclose(file); + +#ifdef WITH_THREAD + state = PyGILState_Ensure(); +#endif + if (_PyPopenProcs) + { + if ((fileObj = PyLong_FromVoidPtr(file)) != NULL && + (procObj = PyDict_GetItem(_PyPopenProcs, + fileObj)) != NULL && + (pidObj = PyList_GetItem(procObj,0)) != NULL && + (intObj = PyList_GetItem(procObj,1)) != NULL) + { + pipe_pid = (int) PyInt_AsLong(pidObj); + file_count = (int) PyInt_AsLong(intObj); + + if (file_count > 1) + { + /* Still other files referencing process */ + file_count--; + PyList_SetItem(procObj,1, + PyInt_FromLong((long) file_count)); + } + else + { + /* Last file for this process */ + if (result != EOF && + waitpid(pipe_pid, &exit_code, 0) == pipe_pid) + { + /* extract exit status */ + if (WIFEXITED(exit_code)) + { + result = WEXITSTATUS(exit_code); + } + else + { + errno = EPIPE; + result = -1; + } + } + else + { + /* Indicate failure - this will cause the file object + * to raise an I/O error and translate the last + * error code from errno. We do have a problem with + * last errors that overlap the normal errno table, + * but that's a consistent problem with the file object. + */ + result = -1; + } + } + + /* Remove this file pointer from dictionary */ + PyDict_DelItem(_PyPopenProcs, fileObj); + + if (PyDict_Size(_PyPopenProcs) == 0) + { + Py_DECREF(_PyPopenProcs); + _PyPopenProcs = NULL; + } + + } /* if object retrieval ok */ + + Py_XDECREF(fileObj); + } /* if _PyPopenProcs */ + +#ifdef WITH_THREAD + PyGILState_Release(state); +#endif + return result; +} + +#endif /* PYCC_??? */ + +#elif defined(MS_WINDOWS) + +/* + * Portable 'popen' replacement for Win32. + * + * Written by Bill Tutt . Minor tweaks + * and 2.0 integration by Fredrik Lundh + * Return code handling by David Bolen . + */ + +#include +#include +#include + +/* These tell _PyPopen() wether to return 1, 2, or 3 file objects. */ +#define POPEN_1 1 +#define POPEN_2 2 +#define POPEN_3 3 +#define POPEN_4 4 + +static PyObject *_PyPopen(char *, int, int); +static int _PyPclose(FILE *file); + +/* + * Internal dictionary mapping popen* file pointers to process handles, + * for use when retrieving the process exit code. See _PyPclose() below + * for more information on this dictionary's use. + */ +static PyObject *_PyPopenProcs = NULL; + + +/* popen that works from a GUI. + * + * The result of this function is a pipe (file) connected to the + * processes stdin or stdout, depending on the requested mode. + */ + +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "r"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 'r') + tm = _O_RDONLY; + else if (*mode != 'w') { + PyErr_SetString(PyExc_ValueError, "popen() arg 2 must be 'r' or 'w'"); + return NULL; + } else + tm = _O_WRONLY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen() arg 3 must be -1"); + return NULL; + } + + if (*(mode+1) == 't') + f = _PyPopen(cmdstring, tm | _O_TEXT, POPEN_1); + else if (*(mode+1) == 'b') + f = _PyPopen(cmdstring, tm | _O_BINARY, POPEN_1); + else + f = _PyPopen(cmdstring, tm | _O_TEXT, POPEN_1); + + return f; +} + +/* Variation on win32pipe.popen + * + * The result of this function is a pipe (file) connected to the + * process's stdin, and a pipe connected to the process's stdout. + */ + +static PyObject * +win32_popen2(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm=0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen2", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = _O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "popen2() arg 2 must be 't' or 'b'"); + return NULL; + } else + tm = _O_BINARY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen2() arg 3 must be -1"); + return NULL; + } + + f = _PyPopen(cmdstring, tm, POPEN_2); + + return f; +} + +/* + * Variation on + * + * The result of this function is 3 pipes - the process's stdin, + * stdout and stderr + */ + +static PyObject * +win32_popen3(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen3", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = _O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "popen3() arg 2 must be 't' or 'b'"); + return NULL; + } else + tm = _O_BINARY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen3() arg 3 must be -1"); + return NULL; + } + + f = _PyPopen(cmdstring, tm, POPEN_3); + + return f; +} + +/* + * Variation on win32pipe.popen + * + * The result of this function is 2 pipes - the processes stdin, + * and stdout+stderr combined as a single pipe. + */ + +static PyObject * +win32_popen4(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen4", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = _O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "popen4() arg 2 must be 't' or 'b'"); + return NULL; + } else + tm = _O_BINARY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen4() arg 3 must be -1"); + return NULL; + } + + f = _PyPopen(cmdstring, tm, POPEN_4); + + return f; +} + +static BOOL +_PyPopenCreateProcess(char *cmdstring, + HANDLE hStdin, + HANDLE hStdout, + HANDLE hStderr, + HANDLE *hProcess) +{ + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + DWORD dwProcessFlags = 0; /* no NEW_CONSOLE by default for Ctrl+C handling */ + char *s1,*s2, *s3 = " /c "; + const char *szConsoleSpawn = "w9xpopen.exe"; + int i; + Py_ssize_t x; + + if (i = GetEnvironmentVariable("COMSPEC",NULL,0)) { + char *comshell; + + s1 = (char *)alloca(i); + if (!(x = GetEnvironmentVariable("COMSPEC", s1, i))) + /* x < i, so x fits into an integer */ + return (int)x; + + /* Explicitly check if we are using COMMAND.COM. If we are + * then use the w9xpopen hack. + */ + comshell = s1 + x; + while (comshell >= s1 && *comshell != '\\') + --comshell; + ++comshell; + + if (GetVersion() < 0x80000000 && + _stricmp(comshell, "command.com") != 0) { + /* NT/2000 and not using command.com. */ + x = i + strlen(s3) + strlen(cmdstring) + 1; + s2 = (char *)alloca(x); + ZeroMemory(s2, x); + PyOS_snprintf(s2, x, "%s%s%s", s1, s3, cmdstring); + } + else { + /* + * Oh gag, we're on Win9x or using COMMAND.COM. Use + * the workaround listed in KB: Q150956 + */ + char modulepath[_MAX_PATH]; + struct stat statinfo; + GetModuleFileName(NULL, modulepath, sizeof(modulepath)); + for (i = x = 0; modulepath[i]; i++) + if (modulepath[i] == SEP) + x = i+1; + modulepath[x] = '\0'; + /* Create the full-name to w9xpopen, so we can test it exists */ + strncat(modulepath, + szConsoleSpawn, + (sizeof(modulepath)/sizeof(modulepath[0])) + -strlen(modulepath)); + if (stat(modulepath, &statinfo) != 0) { + /* Eeek - file-not-found - possibly an embedding + situation - see if we can locate it in sys.prefix + */ + strncpy(modulepath, + Py_GetExecPrefix(), + sizeof(modulepath)/sizeof(modulepath[0])); + if (modulepath[strlen(modulepath)-1] != '\\') + strcat(modulepath, "\\"); + strncat(modulepath, + szConsoleSpawn, + (sizeof(modulepath)/sizeof(modulepath[0])) + -strlen(modulepath)); + /* No where else to look - raise an easily identifiable + error, rather than leaving Windows to report + "file not found" - as the user is probably blissfully + unaware this shim EXE is used, and it will confuse them. + (well, it confused me for a while ;-) + */ + if (stat(modulepath, &statinfo) != 0) { + PyErr_Format(PyExc_RuntimeError, + "Can not locate '%s' which is needed " + "for popen to work with your shell " + "or platform.", + szConsoleSpawn); + return FALSE; + } + } + x = i + strlen(s3) + strlen(cmdstring) + 1 + + strlen(modulepath) + + strlen(szConsoleSpawn) + 1; + + s2 = (char *)alloca(x); + ZeroMemory(s2, x); + /* To maintain correct argument passing semantics, + we pass the command-line as it stands, and allow + quoting to be applied. w9xpopen.exe will then + use its argv vector, and re-quote the necessary + args for the ultimate child process. + */ + PyOS_snprintf( + s2, x, + "\"%s\" %s%s%s", + modulepath, + s1, + s3, + cmdstring); + /* Not passing CREATE_NEW_CONSOLE has been known to + cause random failures on win9x. Specifically a + dialog: + "Your program accessed mem currently in use at xxx" + and a hopeful warning about the stability of your + system. + Cost is Ctrl+C wont kill children, but anyone + who cares can have a go! + */ + dwProcessFlags |= CREATE_NEW_CONSOLE; + } + } + + /* Could be an else here to try cmd.exe / command.com in the path + Now we'll just error out.. */ + else { + PyErr_SetString(PyExc_RuntimeError, + "Cannot locate a COMSPEC environment variable to " + "use as the shell"); + return FALSE; + } + + ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + siStartInfo.hStdInput = hStdin; + siStartInfo.hStdOutput = hStdout; + siStartInfo.hStdError = hStderr; + siStartInfo.wShowWindow = SW_HIDE; + + if (CreateProcess(NULL, + s2, + NULL, + NULL, + TRUE, + dwProcessFlags, + NULL, + NULL, + &siStartInfo, + &piProcInfo) ) { + /* Close the handles now so anyone waiting is woken. */ + CloseHandle(piProcInfo.hThread); + + /* Return process handle */ + *hProcess = piProcInfo.hProcess; + return TRUE; + } + win32_error("CreateProcess", s2); + return FALSE; +} + +/* The following code is based off of KB: Q190351 */ + +static PyObject * +_PyPopen(char *cmdstring, int mode, int n) +{ + HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr, + hChildStderrRd, hChildStderrWr, hChildStdinWrDup, hChildStdoutRdDup, + hChildStderrRdDup, hProcess; /* hChildStdoutWrDup; */ + + SECURITY_ATTRIBUTES saAttr; + BOOL fSuccess; + int fd1, fd2, fd3; + FILE *f1, *f2, *f3; + long file_count; + PyObject *f; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) + return win32_error("CreatePipe", NULL); + + /* Create new output read handle and the input write handle. Set + * the inheritance properties to FALSE. Otherwise, the child inherits + * these handles; resulting in non-closeable handles to the pipes + * being created. */ + fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr, + GetCurrentProcess(), &hChildStdinWrDup, 0, + FALSE, + DUPLICATE_SAME_ACCESS); + if (!fSuccess) + return win32_error("DuplicateHandle", NULL); + + /* Close the inheritable version of ChildStdin + that we're using. */ + CloseHandle(hChildStdinWr); + + if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) + return win32_error("CreatePipe", NULL); + + fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, + GetCurrentProcess(), &hChildStdoutRdDup, 0, + FALSE, DUPLICATE_SAME_ACCESS); + if (!fSuccess) + return win32_error("DuplicateHandle", NULL); + + /* Close the inheritable version of ChildStdout + that we're using. */ + CloseHandle(hChildStdoutRd); + + if (n != POPEN_4) { + if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0)) + return win32_error("CreatePipe", NULL); + fSuccess = DuplicateHandle(GetCurrentProcess(), + hChildStderrRd, + GetCurrentProcess(), + &hChildStderrRdDup, 0, + FALSE, DUPLICATE_SAME_ACCESS); + if (!fSuccess) + return win32_error("DuplicateHandle", NULL); + /* Close the inheritable version of ChildStdErr that we're using. */ + CloseHandle(hChildStderrRd); + } + + switch (n) { + case POPEN_1: + switch (mode & (_O_RDONLY | _O_TEXT | _O_BINARY | _O_WRONLY)) { + case _O_WRONLY | _O_TEXT: + /* Case for writing to child Stdin in text mode. */ + fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, "w"); + f = PyFile_FromFile(f1, cmdstring, "w", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdoutRdDup); + CloseHandle(hChildStderrRdDup); + break; + + case _O_RDONLY | _O_TEXT: + /* Case for reading from child Stdout in text mode. */ + fd1 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode); + f1 = _fdopen(fd1, "r"); + f = PyFile_FromFile(f1, cmdstring, "r", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdinWrDup); + CloseHandle(hChildStderrRdDup); + break; + + case _O_RDONLY | _O_BINARY: + /* Case for readinig from child Stdout in binary mode. */ + fd1 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode); + f1 = _fdopen(fd1, "rb"); + f = PyFile_FromFile(f1, cmdstring, "rb", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdinWrDup); + CloseHandle(hChildStderrRdDup); + break; + + case _O_WRONLY | _O_BINARY: + /* Case for writing to child Stdin in binary mode. */ + fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, "wb"); + f = PyFile_FromFile(f1, cmdstring, "wb", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdoutRdDup); + CloseHandle(hChildStderrRdDup); + break; + } + file_count = 1; + break; + + case POPEN_2: + case POPEN_4: + { + char *m1, *m2; + PyObject *p1, *p2; + + if (mode & _O_TEXT) { + m1 = "r"; + m2 = "w"; + } else { + m1 = "rb"; + m2 = "wb"; + } + + fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, m2); + fd2 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode); + f2 = _fdopen(fd2, m1); + p1 = PyFile_FromFile(f1, cmdstring, m2, _PyPclose); + PyFile_SetBufSize(p1, 0); + p2 = PyFile_FromFile(f2, cmdstring, m1, _PyPclose); + PyFile_SetBufSize(p2, 0); + + if (n != 4) + CloseHandle(hChildStderrRdDup); + + f = PyTuple_Pack(2,p1,p2); + Py_XDECREF(p1); + Py_XDECREF(p2); + file_count = 2; + break; + } + + case POPEN_3: + { + char *m1, *m2; + PyObject *p1, *p2, *p3; + + if (mode & _O_TEXT) { + m1 = "r"; + m2 = "w"; + } else { + m1 = "rb"; + m2 = "wb"; + } + + fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, m2); + fd2 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode); + f2 = _fdopen(fd2, m1); + fd3 = _open_osfhandle((intptr_t)hChildStderrRdDup, mode); + f3 = _fdopen(fd3, m1); + p1 = PyFile_FromFile(f1, cmdstring, m2, _PyPclose); + p2 = PyFile_FromFile(f2, cmdstring, m1, _PyPclose); + p3 = PyFile_FromFile(f3, cmdstring, m1, _PyPclose); + PyFile_SetBufSize(p1, 0); + PyFile_SetBufSize(p2, 0); + PyFile_SetBufSize(p3, 0); + f = PyTuple_Pack(3,p1,p2,p3); + Py_XDECREF(p1); + Py_XDECREF(p2); + Py_XDECREF(p3); + file_count = 3; + break; + } + } + + if (n == POPEN_4) { + if (!_PyPopenCreateProcess(cmdstring, + hChildStdinRd, + hChildStdoutWr, + hChildStdoutWr, + &hProcess)) + return NULL; + } + else { + if (!_PyPopenCreateProcess(cmdstring, + hChildStdinRd, + hChildStdoutWr, + hChildStderrWr, + &hProcess)) + return NULL; + } + + /* + * Insert the files we've created into the process dictionary + * all referencing the list with the process handle and the + * initial number of files (see description below in _PyPclose). + * Since if _PyPclose later tried to wait on a process when all + * handles weren't closed, it could create a deadlock with the + * child, we spend some energy here to try to ensure that we + * either insert all file handles into the dictionary or none + * at all. It's a little clumsy with the various popen modes + * and variable number of files involved. + */ + if (!_PyPopenProcs) { + _PyPopenProcs = PyDict_New(); + } + + if (_PyPopenProcs) { + PyObject *procObj, *hProcessObj, *intObj, *fileObj[3]; + int ins_rc[3]; + + fileObj[0] = fileObj[1] = fileObj[2] = NULL; + ins_rc[0] = ins_rc[1] = ins_rc[2] = 0; + + procObj = PyList_New(2); + hProcessObj = PyLong_FromVoidPtr(hProcess); + intObj = PyInt_FromLong(file_count); + + if (procObj && hProcessObj && intObj) { + PyList_SetItem(procObj,0,hProcessObj); + PyList_SetItem(procObj,1,intObj); + + fileObj[0] = PyLong_FromVoidPtr(f1); + if (fileObj[0]) { + ins_rc[0] = PyDict_SetItem(_PyPopenProcs, + fileObj[0], + procObj); + } + if (file_count >= 2) { + fileObj[1] = PyLong_FromVoidPtr(f2); + if (fileObj[1]) { + ins_rc[1] = PyDict_SetItem(_PyPopenProcs, + fileObj[1], + procObj); + } + } + if (file_count >= 3) { + fileObj[2] = PyLong_FromVoidPtr(f3); + if (fileObj[2]) { + ins_rc[2] = PyDict_SetItem(_PyPopenProcs, + fileObj[2], + procObj); + } + } + + if (ins_rc[0] < 0 || !fileObj[0] || + ins_rc[1] < 0 || (file_count > 1 && !fileObj[1]) || + ins_rc[2] < 0 || (file_count > 2 && !fileObj[2])) { + /* Something failed - remove any dictionary + * entries that did make it. + */ + if (!ins_rc[0] && fileObj[0]) { + PyDict_DelItem(_PyPopenProcs, + fileObj[0]); + } + if (!ins_rc[1] && fileObj[1]) { + PyDict_DelItem(_PyPopenProcs, + fileObj[1]); + } + if (!ins_rc[2] && fileObj[2]) { + PyDict_DelItem(_PyPopenProcs, + fileObj[2]); + } + } + } + + /* + * Clean up our localized references for the dictionary keys + * and value since PyDict_SetItem will Py_INCREF any copies + * that got placed in the dictionary. + */ + Py_XDECREF(procObj); + Py_XDECREF(fileObj[0]); + Py_XDECREF(fileObj[1]); + Py_XDECREF(fileObj[2]); + } + + /* Child is launched. Close the parents copy of those pipe + * handles that only the child should have open. You need to + * make sure that no handles to the write end of the output pipe + * are maintained in this process or else the pipe will not close + * when the child process exits and the ReadFile will hang. */ + + if (!CloseHandle(hChildStdinRd)) + return win32_error("CloseHandle", NULL); + + if (!CloseHandle(hChildStdoutWr)) + return win32_error("CloseHandle", NULL); + + if ((n != 4) && (!CloseHandle(hChildStderrWr))) + return win32_error("CloseHandle", NULL); + + return f; +} + +/* + * Wrapper for fclose() to use for popen* files, so we can retrieve the + * exit code for the child process and return as a result of the close. + * + * This function uses the _PyPopenProcs dictionary in order to map the + * input file pointer to information about the process that was + * originally created by the popen* call that created the file pointer. + * The dictionary uses the file pointer as a key (with one entry + * inserted for each file returned by the original popen* call) and a + * single list object as the value for all files from a single call. + * The list object contains the Win32 process handle at [0], and a file + * count at [1], which is initialized to the total number of file + * handles using that list. + * + * This function closes whichever handle it is passed, and decrements + * the file count in the dictionary for the process handle pointed to + * by this file. On the last close (when the file count reaches zero), + * this function will wait for the child process and then return its + * exit code as the result of the close() operation. This permits the + * files to be closed in any order - it is always the close() of the + * final handle that will return the exit code. + * + * NOTE: This function is currently called with the GIL released. + * hence we use the GILState API to manage our state. + */ + +static int _PyPclose(FILE *file) +{ + int result; + DWORD exit_code; + HANDLE hProcess; + PyObject *procObj, *hProcessObj, *intObj, *fileObj; + long file_count; +#ifdef WITH_THREAD + PyGILState_STATE state; +#endif + + /* Close the file handle first, to ensure it can't block the + * child from exiting if it's the last handle. + */ + result = fclose(file); +#ifdef WITH_THREAD + state = PyGILState_Ensure(); +#endif + if (_PyPopenProcs) { + if ((fileObj = PyLong_FromVoidPtr(file)) != NULL && + (procObj = PyDict_GetItem(_PyPopenProcs, + fileObj)) != NULL && + (hProcessObj = PyList_GetItem(procObj,0)) != NULL && + (intObj = PyList_GetItem(procObj,1)) != NULL) { + + hProcess = PyLong_AsVoidPtr(hProcessObj); + file_count = PyInt_AsLong(intObj); + + if (file_count > 1) { + /* Still other files referencing process */ + file_count--; + PyList_SetItem(procObj,1, + PyInt_FromLong(file_count)); + } else { + /* Last file for this process */ + if (result != EOF && + WaitForSingleObject(hProcess, INFINITE) != WAIT_FAILED && + GetExitCodeProcess(hProcess, &exit_code)) { + /* Possible truncation here in 16-bit environments, but + * real exit codes are just the lower byte in any event. + */ + result = exit_code; + } else { + /* Indicate failure - this will cause the file object + * to raise an I/O error and translate the last Win32 + * error code from errno. We do have a problem with + * last errors that overlap the normal errno table, + * but that's a consistent problem with the file object. + */ + if (result != EOF) { + /* If the error wasn't from the fclose(), then + * set errno for the file object error handling. + */ + errno = GetLastError(); + } + result = -1; + } + + /* Free up the native handle at this point */ + CloseHandle(hProcess); + } + + /* Remove this file pointer from dictionary */ + PyDict_DelItem(_PyPopenProcs, fileObj); + + if (PyDict_Size(_PyPopenProcs) == 0) { + Py_DECREF(_PyPopenProcs); + _PyPopenProcs = NULL; + } + + } /* if object retrieval ok */ + + Py_XDECREF(fileObj); + } /* if _PyPopenProcs */ + +#ifdef WITH_THREAD + PyGILState_Release(state); +#endif + return result; +} + +#else /* which OS? */ +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + char *name; + char *mode = "r"; + int bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "s|si:popen", &name, &mode, &bufsize)) + return NULL; + /* Strip mode of binary or text modifiers */ + if (strcmp(mode, "rb") == 0 || strcmp(mode, "rt") == 0) + mode = "r"; + else if (strcmp(mode, "wb") == 0 || strcmp(mode, "wt") == 0) + mode = "w"; + Py_BEGIN_ALLOW_THREADS + fp = popen(name, mode); + Py_END_ALLOW_THREADS + if (fp == NULL) + return posix_error(); + f = PyFile_FromFile(fp, name, mode, pclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +#endif /* PYOS_??? */ +#endif /* HAVE_POPEN */ + + +#ifdef HAVE_SETUID +PyDoc_STRVAR(posix_setuid__doc__, +"setuid(uid)\n\n\ +Set the current process's user id."); + +static PyObject * +posix_setuid(PyObject *self, PyObject *args) +{ + int uid; + if (!PyArg_ParseTuple(args, "i:setuid", &uid)) + return NULL; + if (setuid(uid) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETUID */ + + +#ifdef HAVE_SETEUID +PyDoc_STRVAR(posix_seteuid__doc__, +"seteuid(uid)\n\n\ +Set the current process's effective user id."); + +static PyObject * +posix_seteuid (PyObject *self, PyObject *args) +{ + int euid; + if (!PyArg_ParseTuple(args, "i", &euid)) { + return NULL; + } else if (seteuid(euid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETEUID */ + +#ifdef HAVE_SETEGID +PyDoc_STRVAR(posix_setegid__doc__, +"setegid(gid)\n\n\ +Set the current process's effective group id."); + +static PyObject * +posix_setegid (PyObject *self, PyObject *args) +{ + int egid; + if (!PyArg_ParseTuple(args, "i", &egid)) { + return NULL; + } else if (setegid(egid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETEGID */ + +#ifdef HAVE_SETREUID +PyDoc_STRVAR(posix_setreuid__doc__, +"setreuid(ruid, euid)\n\n\ +Set the current process's real and effective user ids."); + +static PyObject * +posix_setreuid (PyObject *self, PyObject *args) +{ + int ruid, euid; + if (!PyArg_ParseTuple(args, "ii", &ruid, &euid)) { + return NULL; + } else if (setreuid(ruid, euid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETREUID */ + +#ifdef HAVE_SETREGID +PyDoc_STRVAR(posix_setregid__doc__, +"setregid(rgid, egid)\n\n\ +Set the current process's real and effective group ids."); + +static PyObject * +posix_setregid (PyObject *self, PyObject *args) +{ + int rgid, egid; + if (!PyArg_ParseTuple(args, "ii", &rgid, &egid)) { + return NULL; + } else if (setregid(rgid, egid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETREGID */ + +#ifdef HAVE_SETGID +PyDoc_STRVAR(posix_setgid__doc__, +"setgid(gid)\n\n\ +Set the current process's group id."); + +static PyObject * +posix_setgid(PyObject *self, PyObject *args) +{ + int gid; + if (!PyArg_ParseTuple(args, "i:setgid", &gid)) + return NULL; + if (setgid(gid) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETGID */ + +#ifdef HAVE_SETGROUPS +PyDoc_STRVAR(posix_setgroups__doc__, +"setgroups(list)\n\n\ +Set the groups of the current process to list."); + +static PyObject * +posix_setgroups(PyObject *self, PyObject *args) +{ + PyObject *groups; + int i, len; + gid_t grouplist[MAX_GROUPS]; + + if (!PyArg_ParseTuple(args, "O:setgid", &groups)) + return NULL; + if (!PySequence_Check(groups)) { + PyErr_SetString(PyExc_TypeError, "setgroups argument must be a sequence"); + return NULL; + } + len = PySequence_Size(groups); + if (len > MAX_GROUPS) { + PyErr_SetString(PyExc_ValueError, "too many groups"); + return NULL; + } + for(i = 0; i < len; i++) { + PyObject *elem; + elem = PySequence_GetItem(groups, i); + if (!elem) + return NULL; + if (!PyInt_Check(elem)) { + if (!PyLong_Check(elem)) { + PyErr_SetString(PyExc_TypeError, + "groups must be integers"); + Py_DECREF(elem); + return NULL; + } else { + unsigned long x = PyLong_AsUnsignedLong(elem); + if (PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "group id too big"); + Py_DECREF(elem); + return NULL; + } + grouplist[i] = x; + /* read back the value to see if it fitted in gid_t */ + if (grouplist[i] != x) { + PyErr_SetString(PyExc_TypeError, + "group id too big"); + Py_DECREF(elem); + return NULL; + } + } + } else { + long x = PyInt_AsLong(elem); + grouplist[i] = x; + if (grouplist[i] != x) { + PyErr_SetString(PyExc_TypeError, + "group id too big"); + Py_DECREF(elem); + return NULL; + } + } + Py_DECREF(elem); + } + + if (setgroups(len, grouplist) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETGROUPS */ + +#ifdef HAVE_WAITPID +PyDoc_STRVAR(posix_waitpid__doc__, +"waitpid(pid, options) -> (pid, status)\n\n\ +Wait for completion of a given child process."); + +static PyObject * +posix_waitpid(PyObject *self, PyObject *args) +{ + int pid, options; +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "ii:waitpid", &pid, &options)) + return NULL; + Py_BEGIN_ALLOW_THREADS + pid = waitpid(pid, &status, options); + Py_END_ALLOW_THREADS + if (pid == -1) + return posix_error(); + else + return Py_BuildValue("ii", pid, status_i); +} + +#elif defined(HAVE_CWAIT) + +/* MS C has a variant of waitpid() that's usable for most purposes. */ +PyDoc_STRVAR(posix_waitpid__doc__, +"waitpid(pid, options) -> (pid, status << 8)\n\n" +"Wait for completion of a given process. options is ignored on Windows."); + +static PyObject * +posix_waitpid(PyObject *self, PyObject *args) +{ + intptr_t pid; + int status, options; + + if (!PyArg_ParseTuple(args, "ii:waitpid", &pid, &options)) + return NULL; + Py_BEGIN_ALLOW_THREADS + pid = _cwait(&status, pid, options); + Py_END_ALLOW_THREADS + if (pid == -1) + return posix_error(); + else + /* shift the status left a byte so this is more like the + POSIX waitpid */ + return Py_BuildValue("ii", pid, status << 8); +} +#endif /* HAVE_WAITPID || HAVE_CWAIT */ + +#ifdef HAVE_WAIT +PyDoc_STRVAR(posix_wait__doc__, +"wait() -> (pid, status)\n\n\ +Wait for completion of a child process."); + +static PyObject * +posix_wait(PyObject *self, PyObject *noargs) +{ + int pid; +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + + status_i = 0; + Py_BEGIN_ALLOW_THREADS + pid = wait(&status); + Py_END_ALLOW_THREADS + if (pid == -1) + return posix_error(); + else + return Py_BuildValue("ii", pid, status_i); +#undef status_i +} +#endif + + +PyDoc_STRVAR(posix_lstat__doc__, +"lstat(path) -> stat result\n\n\ +Like stat(path), but do not follow symbolic links."); + +static PyObject * +posix_lstat(PyObject *self, PyObject *args) +{ +#ifdef HAVE_LSTAT + return posix_do_stat(self, args, "et:lstat", lstat, NULL, NULL); +#else /* !HAVE_LSTAT */ +#ifdef MS_WINDOWS + return posix_do_stat(self, args, "et:lstat", STAT, "U:lstat", win32_wstat); +#else + return posix_do_stat(self, args, "et:lstat", STAT, NULL, NULL); +#endif +#endif /* !HAVE_LSTAT */ +} + + +#ifdef HAVE_READLINK +PyDoc_STRVAR(posix_readlink__doc__, +"readlink(path) -> path\n\n\ +Return a string representing the path to which the symbolic link points."); + +static PyObject * +posix_readlink(PyObject *self, PyObject *args) +{ + char buf[MAXPATHLEN]; + char *path; + int n; + if (!PyArg_ParseTuple(args, "s:readlink", &path)) + return NULL; + Py_BEGIN_ALLOW_THREADS + n = readlink(path, buf, (int) sizeof buf); + Py_END_ALLOW_THREADS + if (n < 0) + return posix_error_with_filename(path); + return PyString_FromStringAndSize(buf, n); +} +#endif /* HAVE_READLINK */ + + +#ifdef HAVE_SYMLINK +PyDoc_STRVAR(posix_symlink__doc__, +"symlink(src, dst)\n\n\ +Create a symbolic link pointing to src named dst."); + +static PyObject * +posix_symlink(PyObject *self, PyObject *args) +{ + return posix_2str(args, "etet:symlink", symlink, NULL, NULL); +} +#endif /* HAVE_SYMLINK */ + + +#ifdef HAVE_TIMES +#ifndef HZ +#define HZ 60 /* Universal constant :-) */ +#endif /* HZ */ + +#if defined(PYCC_VACPP) && defined(PYOS_OS2) +static long +system_uptime(void) +{ + ULONG value = 0; + + Py_BEGIN_ALLOW_THREADS + DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &value, sizeof(value)); + Py_END_ALLOW_THREADS + + return value; +} + +static PyObject * +posix_times(PyObject *self, PyObject *noargs) +{ + /* Currently Only Uptime is Provided -- Others Later */ + return Py_BuildValue("ddddd", + (double)0 /* t.tms_utime / HZ */, + (double)0 /* t.tms_stime / HZ */, + (double)0 /* t.tms_cutime / HZ */, + (double)0 /* t.tms_cstime / HZ */, + (double)system_uptime() / 1000); +} +#else /* not OS2 */ +static PyObject * +posix_times(PyObject *self, PyObject *noargs) +{ + struct tms t; + clock_t c; + errno = 0; + c = times(&t); + if (c == (clock_t) -1) + return posix_error(); + return Py_BuildValue("ddddd", + (double)t.tms_utime / HZ, + (double)t.tms_stime / HZ, + (double)t.tms_cutime / HZ, + (double)t.tms_cstime / HZ, + (double)c / HZ); +} +#endif /* not OS2 */ +#endif /* HAVE_TIMES */ + + +#ifdef MS_WINDOWS +#define HAVE_TIMES /* so the method table will pick it up */ +static PyObject * +posix_times(PyObject *self, PyObject *noargs) +{ + FILETIME create, exit, kernel, user; + HANDLE hProc; + hProc = GetCurrentProcess(); + GetProcessTimes(hProc, &create, &exit, &kernel, &user); + /* The fields of a FILETIME structure are the hi and lo part + of a 64-bit value expressed in 100 nanosecond units. + 1e7 is one second in such units; 1e-7 the inverse. + 429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7. + */ + return Py_BuildValue( + "ddddd", + (double)(kernel.dwHighDateTime*429.4967296 + + kernel.dwLowDateTime*1e-7), + (double)(user.dwHighDateTime*429.4967296 + + user.dwLowDateTime*1e-7), + (double)0, + (double)0, + (double)0); +} +#endif /* MS_WINDOWS */ + +#ifdef HAVE_TIMES +PyDoc_STRVAR(posix_times__doc__, +"times() -> (utime, stime, cutime, cstime, elapsed_time)\n\n\ +Return a tuple of floating point numbers indicating process times."); +#endif + + +#ifdef HAVE_GETSID +PyDoc_STRVAR(posix_getsid__doc__, +"getsid(pid) -> sid\n\n\ +Call the system call getsid()."); + +static PyObject * +posix_getsid(PyObject *self, PyObject *args) +{ + int pid, sid; + if (!PyArg_ParseTuple(args, "i:getsid", &pid)) + return NULL; + sid = getsid(pid); + if (sid < 0) + return posix_error(); + return PyInt_FromLong((long)sid); +} +#endif /* HAVE_GETSID */ + + +#ifdef HAVE_SETSID +PyDoc_STRVAR(posix_setsid__doc__, +"setsid()\n\n\ +Call the system call setsid()."); + +static PyObject * +posix_setsid(PyObject *self, PyObject *noargs) +{ + if (setsid() < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETSID */ + +#ifdef HAVE_SETPGID +PyDoc_STRVAR(posix_setpgid__doc__, +"setpgid(pid, pgrp)\n\n\ +Call the system call setpgid()."); + +static PyObject * +posix_setpgid(PyObject *self, PyObject *args) +{ + int pid, pgrp; + if (!PyArg_ParseTuple(args, "ii:setpgid", &pid, &pgrp)) + return NULL; + if (setpgid(pid, pgrp) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETPGID */ + + +#ifdef HAVE_TCGETPGRP +PyDoc_STRVAR(posix_tcgetpgrp__doc__, +"tcgetpgrp(fd) -> pgid\n\n\ +Return the process group associated with the terminal given by a fd."); + +static PyObject * +posix_tcgetpgrp(PyObject *self, PyObject *args) +{ + int fd, pgid; + if (!PyArg_ParseTuple(args, "i:tcgetpgrp", &fd)) + return NULL; + pgid = tcgetpgrp(fd); + if (pgid < 0) + return posix_error(); + return PyInt_FromLong((long)pgid); +} +#endif /* HAVE_TCGETPGRP */ + + +#ifdef HAVE_TCSETPGRP +PyDoc_STRVAR(posix_tcsetpgrp__doc__, +"tcsetpgrp(fd, pgid)\n\n\ +Set the process group associated with the terminal given by a fd."); + +static PyObject * +posix_tcsetpgrp(PyObject *self, PyObject *args) +{ + int fd, pgid; + if (!PyArg_ParseTuple(args, "ii:tcsetpgrp", &fd, &pgid)) + return NULL; + if (tcsetpgrp(fd, pgid) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_TCSETPGRP */ + +/* Functions acting on file descriptors */ + +PyDoc_STRVAR(posix_open__doc__, +"open(filename, flag [, mode=0777]) -> fd\n\n\ +Open a file (for low level IO)."); + +static PyObject * +posix_open(PyObject *self, PyObject *args) +{ + char *file = NULL; + int flag; + int mode = 0777; + int fd; + +#ifdef MS_WINDOWS + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "Ui|i:mkdir", &po, &flag, &mode)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + fd = _wopen(PyUnicode_AS_UNICODE(po), flag, mode); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error(); + return PyInt_FromLong((long)fd); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, "eti|i", + Py_FileSystemDefaultEncoding, &file, + &flag, &mode)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + fd = open(file, flag, mode); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error_with_allocated_filename(file); + PyMem_Free(file); + return PyInt_FromLong((long)fd); +} + + +PyDoc_STRVAR(posix_close__doc__, +"close(fd)\n\n\ +Close a file descriptor (for low level IO)."); + +static PyObject * +posix_close(PyObject *self, PyObject *args) +{ + int fd, res; + if (!PyArg_ParseTuple(args, "i:close", &fd)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = close(fd); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(posix_dup__doc__, +"dup(fd) -> fd2\n\n\ +Return a duplicate of a file descriptor."); + +static PyObject * +posix_dup(PyObject *self, PyObject *args) +{ + int fd; + if (!PyArg_ParseTuple(args, "i:dup", &fd)) + return NULL; + Py_BEGIN_ALLOW_THREADS + fd = dup(fd); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error(); + return PyInt_FromLong((long)fd); +} + + +PyDoc_STRVAR(posix_dup2__doc__, +"dup2(old_fd, new_fd)\n\n\ +Duplicate file descriptor."); + +static PyObject * +posix_dup2(PyObject *self, PyObject *args) +{ + int fd, fd2, res; + if (!PyArg_ParseTuple(args, "ii:dup2", &fd, &fd2)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = dup2(fd, fd2); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(posix_lseek__doc__, +"lseek(fd, pos, how) -> newpos\n\n\ +Set the current position of a file descriptor."); + +static PyObject * +posix_lseek(PyObject *self, PyObject *args) +{ + int fd, how; +#if defined(MS_WIN64) || defined(MS_WINDOWS) + PY_LONG_LONG pos, res; +#else + off_t pos, res; +#endif + PyObject *posobj; + if (!PyArg_ParseTuple(args, "iOi:lseek", &fd, &posobj, &how)) + return NULL; +#ifdef SEEK_SET + /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */ + switch (how) { + case 0: how = SEEK_SET; break; + case 1: how = SEEK_CUR; break; + case 2: how = SEEK_END; break; + } +#endif /* SEEK_END */ + +#if !defined(HAVE_LARGEFILE_SUPPORT) + pos = PyInt_AsLong(posobj); +#else + pos = PyLong_Check(posobj) ? + PyLong_AsLongLong(posobj) : PyInt_AsLong(posobj); +#endif + if (PyErr_Occurred()) + return NULL; + + Py_BEGIN_ALLOW_THREADS +#if defined(MS_WIN64) || defined(MS_WINDOWS) + res = _lseeki64(fd, pos, how); +#else + res = lseek(fd, pos, how); +#endif + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + +#if !defined(HAVE_LARGEFILE_SUPPORT) + return PyInt_FromLong(res); +#else + return PyLong_FromLongLong(res); +#endif +} + + +PyDoc_STRVAR(posix_read__doc__, +"read(fd, buffersize) -> string\n\n\ +Read a file descriptor."); + +static PyObject * +posix_read(PyObject *self, PyObject *args) +{ + int fd, size, n; + PyObject *buffer; + if (!PyArg_ParseTuple(args, "ii:read", &fd, &size)) + return NULL; + if (size < 0) { + errno = EINVAL; + return posix_error(); + } + buffer = PyString_FromStringAndSize((char *)NULL, size); + if (buffer == NULL) + return NULL; + Py_BEGIN_ALLOW_THREADS + n = read(fd, PyString_AsString(buffer), size); + Py_END_ALLOW_THREADS + if (n < 0) { + Py_DECREF(buffer); + return posix_error(); + } + if (n != size) + _PyString_Resize(&buffer, n); + return buffer; +} + + +PyDoc_STRVAR(posix_write__doc__, +"write(fd, string) -> byteswritten\n\n\ +Write a string to a file descriptor."); + +static PyObject * +posix_write(PyObject *self, PyObject *args) +{ + int fd; + Py_ssize_t size; + char *buffer; + + if (!PyArg_ParseTuple(args, "is#:write", &fd, &buffer, &size)) + return NULL; + Py_BEGIN_ALLOW_THREADS + size = write(fd, buffer, (size_t)size); + Py_END_ALLOW_THREADS + if (size < 0) + return posix_error(); + return PyInt_FromSsize_t(size); +} + + +PyDoc_STRVAR(posix_fstat__doc__, +"fstat(fd) -> stat result\n\n\ +Like stat(), but for an open file descriptor."); + +static PyObject * +posix_fstat(PyObject *self, PyObject *args) +{ + int fd; + STRUCT_STAT st; + int res; + if (!PyArg_ParseTuple(args, "i:fstat", &fd)) + return NULL; +#ifdef __VMS + /* on OpenVMS we must ensure that all bytes are written to the file */ + fsync(fd); +#endif + Py_BEGIN_ALLOW_THREADS + res = FSTAT(fd, &st); + Py_END_ALLOW_THREADS + if (res != 0) { +#ifdef MS_WINDOWS + return win32_error("fstat", NULL); +#else + return posix_error(); +#endif + } + + return _pystat_fromstructstat(&st); +} + + +PyDoc_STRVAR(posix_fdopen__doc__, +"fdopen(fd [, mode='r' [, bufsize]]) -> file_object\n\n\ +Return an open file object connected to a file descriptor."); + +static PyObject * +posix_fdopen(PyObject *self, PyObject *args) +{ + int fd; + char *mode = "r"; + int bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "i|si", &fd, &mode, &bufsize)) + return NULL; + + if (mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a') { + PyErr_Format(PyExc_ValueError, + "invalid file mode '%s'", mode); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + fp = fdopen(fd, mode); + Py_END_ALLOW_THREADS + if (fp == NULL) + return posix_error(); + f = PyFile_FromFile(fp, "", mode, fclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +PyDoc_STRVAR(posix_isatty__doc__, +"isatty(fd) -> bool\n\n\ +Return True if the file descriptor 'fd' is an open file descriptor\n\ +connected to the slave end of a terminal."); + +static PyObject * +posix_isatty(PyObject *self, PyObject *args) +{ + int fd; + if (!PyArg_ParseTuple(args, "i:isatty", &fd)) + return NULL; + return PyBool_FromLong(isatty(fd)); +} + +#ifdef HAVE_PIPE +PyDoc_STRVAR(posix_pipe__doc__, +"pipe() -> (read_end, write_end)\n\n\ +Create a pipe."); + +static PyObject * +posix_pipe(PyObject *self, PyObject *noargs) +{ +#if defined(PYOS_OS2) + HFILE read, write; + APIRET rc; + + Py_BEGIN_ALLOW_THREADS + rc = DosCreatePipe( &read, &write, 4096); + Py_END_ALLOW_THREADS + if (rc != NO_ERROR) + return os2_error(rc); + + return Py_BuildValue("(ii)", read, write); +#else +#if !defined(MS_WINDOWS) + int fds[2]; + int res; + Py_BEGIN_ALLOW_THREADS + res = pipe(fds); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error(); + return Py_BuildValue("(ii)", fds[0], fds[1]); +#else /* MS_WINDOWS */ + HANDLE read, write; + int read_fd, write_fd; + BOOL ok; + Py_BEGIN_ALLOW_THREADS + ok = CreatePipe(&read, &write, NULL, 0); + Py_END_ALLOW_THREADS + if (!ok) + return win32_error("CreatePipe", NULL); + read_fd = _open_osfhandle((Py_intptr_t)read, 0); + write_fd = _open_osfhandle((Py_intptr_t)write, 1); + return Py_BuildValue("(ii)", read_fd, write_fd); +#endif /* MS_WINDOWS */ +#endif +} +#endif /* HAVE_PIPE */ + + +#ifdef HAVE_MKFIFO +PyDoc_STRVAR(posix_mkfifo__doc__, +"mkfifo(filename [, mode=0666])\n\n\ +Create a FIFO (a POSIX named pipe)."); + +static PyObject * +posix_mkfifo(PyObject *self, PyObject *args) +{ + char *filename; + int mode = 0666; + int res; + if (!PyArg_ParseTuple(args, "s|i:mkfifo", &filename, &mode)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = mkfifo(filename, mode); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + + +#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) +PyDoc_STRVAR(posix_mknod__doc__, +"mknod(filename [, mode=0600, device])\n\n\ +Create a filesystem node (file, device special file or named pipe)\n\ +named filename. mode specifies both the permissions to use and the\n\ +type of node to be created, being combined (bitwise OR) with one of\n\ +S_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO. For S_IFCHR and S_IFBLK,\n\ +device defines the newly created device special file (probably using\n\ +os.makedev()), otherwise it is ignored."); + + +static PyObject * +posix_mknod(PyObject *self, PyObject *args) +{ + char *filename; + int mode = 0600; + int device = 0; + int res; + if (!PyArg_ParseTuple(args, "s|ii:mknod", &filename, &mode, &device)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = mknod(filename, mode, device); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_DEVICE_MACROS +PyDoc_STRVAR(posix_major__doc__, +"major(device) -> major number\n\ +Extracts a device major number from a raw device number."); + +static PyObject * +posix_major(PyObject *self, PyObject *args) +{ + int device; + if (!PyArg_ParseTuple(args, "i:major", &device)) + return NULL; + return PyInt_FromLong((long)major(device)); +} + +PyDoc_STRVAR(posix_minor__doc__, +"minor(device) -> minor number\n\ +Extracts a device minor number from a raw device number."); + +static PyObject * +posix_minor(PyObject *self, PyObject *args) +{ + int device; + if (!PyArg_ParseTuple(args, "i:minor", &device)) + return NULL; + return PyInt_FromLong((long)minor(device)); +} + +PyDoc_STRVAR(posix_makedev__doc__, +"makedev(major, minor) -> device number\n\ +Composes a raw device number from the major and minor device numbers."); + +static PyObject * +posix_makedev(PyObject *self, PyObject *args) +{ + int major, minor; + if (!PyArg_ParseTuple(args, "ii:makedev", &major, &minor)) + return NULL; + return PyInt_FromLong((long)makedev(major, minor)); +} +#endif /* device macros */ + + +#ifdef HAVE_FTRUNCATE +PyDoc_STRVAR(posix_ftruncate__doc__, +"ftruncate(fd, length)\n\n\ +Truncate a file to a specified length."); + +static PyObject * +posix_ftruncate(PyObject *self, PyObject *args) +{ + int fd; + off_t length; + int res; + PyObject *lenobj; + + if (!PyArg_ParseTuple(args, "iO:ftruncate", &fd, &lenobj)) + return NULL; + +#if !defined(HAVE_LARGEFILE_SUPPORT) + length = PyInt_AsLong(lenobj); +#else + length = PyLong_Check(lenobj) ? + PyLong_AsLongLong(lenobj) : PyInt_AsLong(lenobj); +#endif + if (PyErr_Occurred()) + return NULL; + + Py_BEGIN_ALLOW_THREADS + res = ftruncate(fd, length); + Py_END_ALLOW_THREADS + if (res < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_PUTENV +PyDoc_STRVAR(posix_putenv__doc__, +"putenv(key, value)\n\n\ +Change or add an environment variable."); + +/* Save putenv() parameters as values here, so we can collect them when they + * get re-set with another call for the same key. */ +static PyObject *posix_putenv_garbage; + +static PyObject * +posix_putenv(PyObject *self, PyObject *args) +{ + char *s1, *s2; + char *new; + PyObject *newstr; + size_t len; + + if (!PyArg_ParseTuple(args, "ss:putenv", &s1, &s2)) + return NULL; + +#if defined(PYOS_OS2) + if (stricmp(s1, "BEGINLIBPATH") == 0) { + APIRET rc; + + rc = DosSetExtLIBPATH(s2, BEGIN_LIBPATH); + if (rc != NO_ERROR) + return os2_error(rc); + + } else if (stricmp(s1, "ENDLIBPATH") == 0) { + APIRET rc; + + rc = DosSetExtLIBPATH(s2, END_LIBPATH); + if (rc != NO_ERROR) + return os2_error(rc); + } else { +#endif + + /* XXX This can leak memory -- not easy to fix :-( */ + len = strlen(s1) + strlen(s2) + 2; + /* len includes space for a trailing \0; the size arg to + PyString_FromStringAndSize does not count that */ + newstr = PyString_FromStringAndSize(NULL, (int)len - 1); + if (newstr == NULL) + return PyErr_NoMemory(); + new = PyString_AS_STRING(newstr); + PyOS_snprintf(new, len, "%s=%s", s1, s2); + if (putenv(new)) { + Py_DECREF(newstr); + posix_error(); + return NULL; + } + /* Install the first arg and newstr in posix_putenv_garbage; + * this will cause previous value to be collected. This has to + * happen after the real putenv() call because the old value + * was still accessible until then. */ + if (PyDict_SetItem(posix_putenv_garbage, + PyTuple_GET_ITEM(args, 0), newstr)) { + /* really not much we can do; just leak */ + PyErr_Clear(); + } + else { + Py_DECREF(newstr); + } + +#if defined(PYOS_OS2) + } +#endif + Py_INCREF(Py_None); + return Py_None; +} +#endif /* putenv */ + +#ifdef HAVE_UNSETENV +PyDoc_STRVAR(posix_unsetenv__doc__, +"unsetenv(key)\n\n\ +Delete an environment variable."); + +static PyObject * +posix_unsetenv(PyObject *self, PyObject *args) +{ + char *s1; + + if (!PyArg_ParseTuple(args, "s:unsetenv", &s1)) + return NULL; + + unsetenv(s1); + + /* Remove the key from posix_putenv_garbage; + * this will cause it to be collected. This has to + * happen after the real unsetenv() call because the + * old value was still accessible until then. + */ + if (PyDict_DelItem(posix_putenv_garbage, + PyTuple_GET_ITEM(args, 0))) { + /* really not much we can do; just leak */ + PyErr_Clear(); + } + + Py_INCREF(Py_None); + return Py_None; +} +#endif /* unsetenv */ + +#ifdef HAVE_STRERROR +PyDoc_STRVAR(posix_strerror__doc__, +"strerror(code) -> string\n\n\ +Translate an error code to a message string."); + +static PyObject * +posix_strerror(PyObject *self, PyObject *args) +{ + int code; + char *message; + if (!PyArg_ParseTuple(args, "i:strerror", &code)) + return NULL; + message = strerror(code); + if (message == NULL) { + PyErr_SetString(PyExc_ValueError, + "strerror() argument out of range"); + return NULL; + } + return PyString_FromString(message); +} +#endif /* strerror */ + + +#ifdef HAVE_SYS_WAIT_H + +#ifdef WCOREDUMP +PyDoc_STRVAR(posix_WCOREDUMP__doc__, +"WCOREDUMP(status) -> bool\n\n\ +Return True if the process returning 'status' was dumped to a core file."); + +static PyObject * +posix_WCOREDUMP(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WCOREDUMP", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WCOREDUMP(status)); +#undef status_i +} +#endif /* WCOREDUMP */ + +#ifdef WIFCONTINUED +PyDoc_STRVAR(posix_WIFCONTINUED__doc__, +"WIFCONTINUED(status) -> bool\n\n\ +Return True if the process returning 'status' was continued from a\n\ +job control stop."); + +static PyObject * +posix_WIFCONTINUED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WCONTINUED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFCONTINUED(status)); +#undef status_i +} +#endif /* WIFCONTINUED */ + +#ifdef WIFSTOPPED +PyDoc_STRVAR(posix_WIFSTOPPED__doc__, +"WIFSTOPPED(status) -> bool\n\n\ +Return True if the process returning 'status' was stopped."); + +static PyObject * +posix_WIFSTOPPED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WIFSTOPPED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFSTOPPED(status)); +#undef status_i +} +#endif /* WIFSTOPPED */ + +#ifdef WIFSIGNALED +PyDoc_STRVAR(posix_WIFSIGNALED__doc__, +"WIFSIGNALED(status) -> bool\n\n\ +Return True if the process returning 'status' was terminated by a signal."); + +static PyObject * +posix_WIFSIGNALED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WIFSIGNALED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFSIGNALED(status)); +#undef status_i +} +#endif /* WIFSIGNALED */ + +#ifdef WIFEXITED +PyDoc_STRVAR(posix_WIFEXITED__doc__, +"WIFEXITED(status) -> bool\n\n\ +Return true if the process returning 'status' exited using the exit()\n\ +system call."); + +static PyObject * +posix_WIFEXITED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WIFEXITED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFEXITED(status)); +#undef status_i +} +#endif /* WIFEXITED */ + +#ifdef WEXITSTATUS +PyDoc_STRVAR(posix_WEXITSTATUS__doc__, +"WEXITSTATUS(status) -> integer\n\n\ +Return the process return code from 'status'."); + +static PyObject * +posix_WEXITSTATUS(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WEXITSTATUS", &status_i)) + { + return NULL; + } + + return Py_BuildValue("i", WEXITSTATUS(status)); +#undef status_i +} +#endif /* WEXITSTATUS */ + +#ifdef WTERMSIG +PyDoc_STRVAR(posix_WTERMSIG__doc__, +"WTERMSIG(status) -> integer\n\n\ +Return the signal that terminated the process that provided the 'status'\n\ +value."); + +static PyObject * +posix_WTERMSIG(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WTERMSIG", &status_i)) + { + return NULL; + } + + return Py_BuildValue("i", WTERMSIG(status)); +#undef status_i +} +#endif /* WTERMSIG */ + +#ifdef WSTOPSIG +PyDoc_STRVAR(posix_WSTOPSIG__doc__, +"WSTOPSIG(status) -> integer\n\n\ +Return the signal that stopped the process that provided\n\ +the 'status' value."); + +static PyObject * +posix_WSTOPSIG(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WSTOPSIG", &status_i)) + { + return NULL; + } + + return Py_BuildValue("i", WSTOPSIG(status)); +#undef status_i +} +#endif /* WSTOPSIG */ + +#endif /* HAVE_SYS_WAIT_H */ + + +#if defined(HAVE_FSTATVFS) +#ifdef _SCO_DS +/* SCO OpenServer 5.0 and later requires _SVID3 before it reveals the + needed definitions in sys/statvfs.h */ +#define _SVID3 +#endif +#include + +static PyObject* +_pystatvfs_fromstructstatvfs(struct statvfs st) { + PyObject *v = PyStructSequence_New(&StatVFSResultType); + if (v == NULL) + return NULL; + +#if !defined(HAVE_LARGEFILE_SUPPORT) + PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long) st.f_bsize)); + PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long) st.f_frsize)); + PyStructSequence_SET_ITEM(v, 2, PyInt_FromLong((long) st.f_blocks)); + PyStructSequence_SET_ITEM(v, 3, PyInt_FromLong((long) st.f_bfree)); + PyStructSequence_SET_ITEM(v, 4, PyInt_FromLong((long) st.f_bavail)); + PyStructSequence_SET_ITEM(v, 5, PyInt_FromLong((long) st.f_files)); + PyStructSequence_SET_ITEM(v, 6, PyInt_FromLong((long) st.f_ffree)); + PyStructSequence_SET_ITEM(v, 7, PyInt_FromLong((long) st.f_favail)); + PyStructSequence_SET_ITEM(v, 8, PyInt_FromLong((long) st.f_flag)); + PyStructSequence_SET_ITEM(v, 9, PyInt_FromLong((long) st.f_namemax)); +#else + PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long) st.f_bsize)); + PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long) st.f_frsize)); + PyStructSequence_SET_ITEM(v, 2, + PyLong_FromLongLong((PY_LONG_LONG) st.f_blocks)); + PyStructSequence_SET_ITEM(v, 3, + PyLong_FromLongLong((PY_LONG_LONG) st.f_bfree)); + PyStructSequence_SET_ITEM(v, 4, + PyLong_FromLongLong((PY_LONG_LONG) st.f_bavail)); + PyStructSequence_SET_ITEM(v, 5, + PyLong_FromLongLong((PY_LONG_LONG) st.f_files)); + PyStructSequence_SET_ITEM(v, 6, + PyLong_FromLongLong((PY_LONG_LONG) st.f_ffree)); + PyStructSequence_SET_ITEM(v, 7, + PyLong_FromLongLong((PY_LONG_LONG) st.f_favail)); + PyStructSequence_SET_ITEM(v, 8, PyInt_FromLong((long) st.f_flag)); + PyStructSequence_SET_ITEM(v, 9, PyInt_FromLong((long) st.f_namemax)); +#endif + + return v; +} + +PyDoc_STRVAR(posix_fstatvfs__doc__, +"fstatvfs(fd) -> statvfs result\n\n\ +Perform an fstatvfs system call on the given fd."); + +static PyObject * +posix_fstatvfs(PyObject *self, PyObject *args) +{ + int fd, res; + struct statvfs st; + + if (!PyArg_ParseTuple(args, "i:fstatvfs", &fd)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = fstatvfs(fd, &st); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error(); + + return _pystatvfs_fromstructstatvfs(st); +} +#endif /* HAVE_FSTATVFS */ + + +#if defined(HAVE_STATVFS) +#include + +PyDoc_STRVAR(posix_statvfs__doc__, +"statvfs(path) -> statvfs result\n\n\ +Perform a statvfs system call on the given path."); + +static PyObject * +posix_statvfs(PyObject *self, PyObject *args) +{ + char *path; + int res; + struct statvfs st; + if (!PyArg_ParseTuple(args, "s:statvfs", &path)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = statvfs(path, &st); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error_with_filename(path); + + return _pystatvfs_fromstructstatvfs(st); +} +#endif /* HAVE_STATVFS */ + + +#ifdef HAVE_TEMPNAM +PyDoc_STRVAR(posix_tempnam__doc__, +"tempnam([dir[, prefix]]) -> string\n\n\ +Return a unique name for a temporary file.\n\ +The directory and a prefix may be specified as strings; they may be omitted\n\ +or None if not needed."); + +static PyObject * +posix_tempnam(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + char *dir = NULL; + char *pfx = NULL; + char *name; + + if (!PyArg_ParseTuple(args, "|zz:tempnam", &dir, &pfx)) + return NULL; + + if (PyErr_Warn(PyExc_RuntimeWarning, + "tempnam is a potential security risk to your program") < 0) + return NULL; + +#ifdef MS_WINDOWS + name = _tempnam(dir, pfx); +#else + name = tempnam(dir, pfx); +#endif + if (name == NULL) + return PyErr_NoMemory(); + result = PyString_FromString(name); + free(name); + return result; +} +#endif + + +#ifdef HAVE_TMPFILE +PyDoc_STRVAR(posix_tmpfile__doc__, +"tmpfile() -> file object\n\n\ +Create a temporary file with no directory entries."); + +static PyObject * +posix_tmpfile(PyObject *self, PyObject *noargs) +{ + FILE *fp; + + fp = tmpfile(); + if (fp == NULL) + return posix_error(); + return PyFile_FromFile(fp, "", "w+b", fclose); +} +#endif + + +#ifdef HAVE_TMPNAM +PyDoc_STRVAR(posix_tmpnam__doc__, +"tmpnam() -> string\n\n\ +Return a unique name for a temporary file."); + +static PyObject * +posix_tmpnam(PyObject *self, PyObject *noargs) +{ + char buffer[L_tmpnam]; + char *name; + + if (PyErr_Warn(PyExc_RuntimeWarning, + "tmpnam is a potential security risk to your program") < 0) + return NULL; + +#ifdef USE_TMPNAM_R + name = tmpnam_r(buffer); +#else + name = tmpnam(buffer); +#endif + if (name == NULL) { + PyErr_SetObject(PyExc_OSError, + Py_BuildValue("is", 0, +#ifdef USE_TMPNAM_R + "unexpected NULL from tmpnam_r" +#else + "unexpected NULL from tmpnam" +#endif + )); + return NULL; + } + return PyString_FromString(buffer); +} +#endif + + +/* This is used for fpathconf(), pathconf(), confstr() and sysconf(). + * It maps strings representing configuration variable names to + * integer values, allowing those functions to be called with the + * magic names instead of polluting the module's namespace with tons of + * rarely-used constants. There are three separate tables that use + * these definitions. + * + * This code is always included, even if none of the interfaces that + * need it are included. The #if hackery needed to avoid it would be + * sufficiently pervasive that it's not worth the loss of readability. + */ +struct constdef { + char *name; + long value; +}; + +static int +conv_confname(PyObject *arg, int *valuep, struct constdef *table, + size_t tablesize) +{ + if (PyInt_Check(arg)) { + *valuep = PyInt_AS_LONG(arg); + return 1; + } + if (PyString_Check(arg)) { + /* look up the value in the table using a binary search */ + size_t lo = 0; + size_t mid; + size_t hi = tablesize; + int cmp; + char *confname = PyString_AS_STRING(arg); + while (lo < hi) { + mid = (lo + hi) / 2; + cmp = strcmp(confname, table[mid].name); + if (cmp < 0) + hi = mid; + else if (cmp > 0) + lo = mid + 1; + else { + *valuep = table[mid].value; + return 1; + } + } + PyErr_SetString(PyExc_ValueError, "unrecognized configuration name"); + } + else + PyErr_SetString(PyExc_TypeError, + "configuration names must be strings or integers"); + return 0; +} + + +#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF) +static struct constdef posix_constants_pathconf[] = { +#ifdef _PC_ABI_AIO_XFER_MAX + {"PC_ABI_AIO_XFER_MAX", _PC_ABI_AIO_XFER_MAX}, +#endif +#ifdef _PC_ABI_ASYNC_IO + {"PC_ABI_ASYNC_IO", _PC_ABI_ASYNC_IO}, +#endif +#ifdef _PC_ASYNC_IO + {"PC_ASYNC_IO", _PC_ASYNC_IO}, +#endif +#ifdef _PC_CHOWN_RESTRICTED + {"PC_CHOWN_RESTRICTED", _PC_CHOWN_RESTRICTED}, +#endif +#ifdef _PC_FILESIZEBITS + {"PC_FILESIZEBITS", _PC_FILESIZEBITS}, +#endif +#ifdef _PC_LAST + {"PC_LAST", _PC_LAST}, +#endif +#ifdef _PC_LINK_MAX + {"PC_LINK_MAX", _PC_LINK_MAX}, +#endif +#ifdef _PC_MAX_CANON + {"PC_MAX_CANON", _PC_MAX_CANON}, +#endif +#ifdef _PC_MAX_INPUT + {"PC_MAX_INPUT", _PC_MAX_INPUT}, +#endif +#ifdef _PC_NAME_MAX + {"PC_NAME_MAX", _PC_NAME_MAX}, +#endif +#ifdef _PC_NO_TRUNC + {"PC_NO_TRUNC", _PC_NO_TRUNC}, +#endif +#ifdef _PC_PATH_MAX + {"PC_PATH_MAX", _PC_PATH_MAX}, +#endif +#ifdef _PC_PIPE_BUF + {"PC_PIPE_BUF", _PC_PIPE_BUF}, +#endif +#ifdef _PC_PRIO_IO + {"PC_PRIO_IO", _PC_PRIO_IO}, +#endif +#ifdef _PC_SOCK_MAXBUF + {"PC_SOCK_MAXBUF", _PC_SOCK_MAXBUF}, +#endif +#ifdef _PC_SYNC_IO + {"PC_SYNC_IO", _PC_SYNC_IO}, +#endif +#ifdef _PC_VDISABLE + {"PC_VDISABLE", _PC_VDISABLE}, +#endif +}; + +static int +conv_path_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_pathconf, + sizeof(posix_constants_pathconf) + / sizeof(struct constdef)); +} +#endif + +#ifdef HAVE_FPATHCONF +PyDoc_STRVAR(posix_fpathconf__doc__, +"fpathconf(fd, name) -> integer\n\n\ +Return the configuration limit name for the file descriptor fd.\n\ +If there is no limit, return -1."); + +static PyObject * +posix_fpathconf(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name, fd; + + if (PyArg_ParseTuple(args, "iO&:fpathconf", &fd, + conv_path_confname, &name)) { + long limit; + + errno = 0; + limit = fpathconf(fd, name); + if (limit == -1 && errno != 0) + posix_error(); + else + result = PyInt_FromLong(limit); + } + return result; +} +#endif + + +#ifdef HAVE_PATHCONF +PyDoc_STRVAR(posix_pathconf__doc__, +"pathconf(path, name) -> integer\n\n\ +Return the configuration limit name for the file or directory path.\n\ +If there is no limit, return -1."); + +static PyObject * +posix_pathconf(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name; + char *path; + + if (PyArg_ParseTuple(args, "sO&:pathconf", &path, + conv_path_confname, &name)) { + long limit; + + errno = 0; + limit = pathconf(path, name); + if (limit == -1 && errno != 0) { + if (errno == EINVAL) + /* could be a path or name problem */ + posix_error(); + else + posix_error_with_filename(path); + } + else + result = PyInt_FromLong(limit); + } + return result; +} +#endif + +#ifdef HAVE_CONFSTR +static struct constdef posix_constants_confstr[] = { +#ifdef _CS_ARCHITECTURE + {"CS_ARCHITECTURE", _CS_ARCHITECTURE}, +#endif +#ifdef _CS_HOSTNAME + {"CS_HOSTNAME", _CS_HOSTNAME}, +#endif +#ifdef _CS_HW_PROVIDER + {"CS_HW_PROVIDER", _CS_HW_PROVIDER}, +#endif +#ifdef _CS_HW_SERIAL + {"CS_HW_SERIAL", _CS_HW_SERIAL}, +#endif +#ifdef _CS_INITTAB_NAME + {"CS_INITTAB_NAME", _CS_INITTAB_NAME}, +#endif +#ifdef _CS_LFS64_CFLAGS + {"CS_LFS64_CFLAGS", _CS_LFS64_CFLAGS}, +#endif +#ifdef _CS_LFS64_LDFLAGS + {"CS_LFS64_LDFLAGS", _CS_LFS64_LDFLAGS}, +#endif +#ifdef _CS_LFS64_LIBS + {"CS_LFS64_LIBS", _CS_LFS64_LIBS}, +#endif +#ifdef _CS_LFS64_LINTFLAGS + {"CS_LFS64_LINTFLAGS", _CS_LFS64_LINTFLAGS}, +#endif +#ifdef _CS_LFS_CFLAGS + {"CS_LFS_CFLAGS", _CS_LFS_CFLAGS}, +#endif +#ifdef _CS_LFS_LDFLAGS + {"CS_LFS_LDFLAGS", _CS_LFS_LDFLAGS}, +#endif +#ifdef _CS_LFS_LIBS + {"CS_LFS_LIBS", _CS_LFS_LIBS}, +#endif +#ifdef _CS_LFS_LINTFLAGS + {"CS_LFS_LINTFLAGS", _CS_LFS_LINTFLAGS}, +#endif +#ifdef _CS_MACHINE + {"CS_MACHINE", _CS_MACHINE}, +#endif +#ifdef _CS_PATH + {"CS_PATH", _CS_PATH}, +#endif +#ifdef _CS_RELEASE + {"CS_RELEASE", _CS_RELEASE}, +#endif +#ifdef _CS_SRPC_DOMAIN + {"CS_SRPC_DOMAIN", _CS_SRPC_DOMAIN}, +#endif +#ifdef _CS_SYSNAME + {"CS_SYSNAME", _CS_SYSNAME}, +#endif +#ifdef _CS_VERSION + {"CS_VERSION", _CS_VERSION}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_CFLAGS + {"CS_XBS5_ILP32_OFF32_CFLAGS", _CS_XBS5_ILP32_OFF32_CFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LDFLAGS + {"CS_XBS5_ILP32_OFF32_LDFLAGS", _CS_XBS5_ILP32_OFF32_LDFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LIBS + {"CS_XBS5_ILP32_OFF32_LIBS", _CS_XBS5_ILP32_OFF32_LIBS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LINTFLAGS + {"CS_XBS5_ILP32_OFF32_LINTFLAGS", _CS_XBS5_ILP32_OFF32_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_CFLAGS + {"CS_XBS5_ILP32_OFFBIG_CFLAGS", _CS_XBS5_ILP32_OFFBIG_CFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LDFLAGS + {"CS_XBS5_ILP32_OFFBIG_LDFLAGS", _CS_XBS5_ILP32_OFFBIG_LDFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LIBS + {"CS_XBS5_ILP32_OFFBIG_LIBS", _CS_XBS5_ILP32_OFFBIG_LIBS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LINTFLAGS + {"CS_XBS5_ILP32_OFFBIG_LINTFLAGS", _CS_XBS5_ILP32_OFFBIG_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_CFLAGS + {"CS_XBS5_LP64_OFF64_CFLAGS", _CS_XBS5_LP64_OFF64_CFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LDFLAGS + {"CS_XBS5_LP64_OFF64_LDFLAGS", _CS_XBS5_LP64_OFF64_LDFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LIBS + {"CS_XBS5_LP64_OFF64_LIBS", _CS_XBS5_LP64_OFF64_LIBS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LINTFLAGS + {"CS_XBS5_LP64_OFF64_LINTFLAGS", _CS_XBS5_LP64_OFF64_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_CFLAGS + {"CS_XBS5_LPBIG_OFFBIG_CFLAGS", _CS_XBS5_LPBIG_OFFBIG_CFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LDFLAGS + {"CS_XBS5_LPBIG_OFFBIG_LDFLAGS", _CS_XBS5_LPBIG_OFFBIG_LDFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LIBS + {"CS_XBS5_LPBIG_OFFBIG_LIBS", _CS_XBS5_LPBIG_OFFBIG_LIBS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS + {"CS_XBS5_LPBIG_OFFBIG_LINTFLAGS", _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS}, +#endif +#ifdef _MIPS_CS_AVAIL_PROCESSORS + {"MIPS_CS_AVAIL_PROCESSORS", _MIPS_CS_AVAIL_PROCESSORS}, +#endif +#ifdef _MIPS_CS_BASE + {"MIPS_CS_BASE", _MIPS_CS_BASE}, +#endif +#ifdef _MIPS_CS_HOSTID + {"MIPS_CS_HOSTID", _MIPS_CS_HOSTID}, +#endif +#ifdef _MIPS_CS_HW_NAME + {"MIPS_CS_HW_NAME", _MIPS_CS_HW_NAME}, +#endif +#ifdef _MIPS_CS_NUM_PROCESSORS + {"MIPS_CS_NUM_PROCESSORS", _MIPS_CS_NUM_PROCESSORS}, +#endif +#ifdef _MIPS_CS_OSREL_MAJ + {"MIPS_CS_OSREL_MAJ", _MIPS_CS_OSREL_MAJ}, +#endif +#ifdef _MIPS_CS_OSREL_MIN + {"MIPS_CS_OSREL_MIN", _MIPS_CS_OSREL_MIN}, +#endif +#ifdef _MIPS_CS_OSREL_PATCH + {"MIPS_CS_OSREL_PATCH", _MIPS_CS_OSREL_PATCH}, +#endif +#ifdef _MIPS_CS_OS_NAME + {"MIPS_CS_OS_NAME", _MIPS_CS_OS_NAME}, +#endif +#ifdef _MIPS_CS_OS_PROVIDER + {"MIPS_CS_OS_PROVIDER", _MIPS_CS_OS_PROVIDER}, +#endif +#ifdef _MIPS_CS_PROCESSORS + {"MIPS_CS_PROCESSORS", _MIPS_CS_PROCESSORS}, +#endif +#ifdef _MIPS_CS_SERIAL + {"MIPS_CS_SERIAL", _MIPS_CS_SERIAL}, +#endif +#ifdef _MIPS_CS_VENDOR + {"MIPS_CS_VENDOR", _MIPS_CS_VENDOR}, +#endif +}; + +static int +conv_confstr_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_confstr, + sizeof(posix_constants_confstr) + / sizeof(struct constdef)); +} + +PyDoc_STRVAR(posix_confstr__doc__, +"confstr(name) -> string\n\n\ +Return a string-valued system configuration variable."); + +static PyObject * +posix_confstr(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name; + char buffer[64]; + + if (PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name)) { + int len = confstr(name, buffer, sizeof(buffer)); + + errno = 0; + if (len == 0) { + if (errno != 0) + posix_error(); + else + result = PyString_FromString(""); + } + else { + if (len >= sizeof(buffer)) { + result = PyString_FromStringAndSize(NULL, len); + if (result != NULL) + confstr(name, PyString_AS_STRING(result), len+1); + } + else + result = PyString_FromString(buffer); + } + } + return result; +} +#endif + + +#ifdef HAVE_SYSCONF +static struct constdef posix_constants_sysconf[] = { +#ifdef _SC_2_CHAR_TERM + {"SC_2_CHAR_TERM", _SC_2_CHAR_TERM}, +#endif +#ifdef _SC_2_C_BIND + {"SC_2_C_BIND", _SC_2_C_BIND}, +#endif +#ifdef _SC_2_C_DEV + {"SC_2_C_DEV", _SC_2_C_DEV}, +#endif +#ifdef _SC_2_C_VERSION + {"SC_2_C_VERSION", _SC_2_C_VERSION}, +#endif +#ifdef _SC_2_FORT_DEV + {"SC_2_FORT_DEV", _SC_2_FORT_DEV}, +#endif +#ifdef _SC_2_FORT_RUN + {"SC_2_FORT_RUN", _SC_2_FORT_RUN}, +#endif +#ifdef _SC_2_LOCALEDEF + {"SC_2_LOCALEDEF", _SC_2_LOCALEDEF}, +#endif +#ifdef _SC_2_SW_DEV + {"SC_2_SW_DEV", _SC_2_SW_DEV}, +#endif +#ifdef _SC_2_UPE + {"SC_2_UPE", _SC_2_UPE}, +#endif +#ifdef _SC_2_VERSION + {"SC_2_VERSION", _SC_2_VERSION}, +#endif +#ifdef _SC_ABI_ASYNCHRONOUS_IO + {"SC_ABI_ASYNCHRONOUS_IO", _SC_ABI_ASYNCHRONOUS_IO}, +#endif +#ifdef _SC_ACL + {"SC_ACL", _SC_ACL}, +#endif +#ifdef _SC_AIO_LISTIO_MAX + {"SC_AIO_LISTIO_MAX", _SC_AIO_LISTIO_MAX}, +#endif +#ifdef _SC_AIO_MAX + {"SC_AIO_MAX", _SC_AIO_MAX}, +#endif +#ifdef _SC_AIO_PRIO_DELTA_MAX + {"SC_AIO_PRIO_DELTA_MAX", _SC_AIO_PRIO_DELTA_MAX}, +#endif +#ifdef _SC_ARG_MAX + {"SC_ARG_MAX", _SC_ARG_MAX}, +#endif +#ifdef _SC_ASYNCHRONOUS_IO + {"SC_ASYNCHRONOUS_IO", _SC_ASYNCHRONOUS_IO}, +#endif +#ifdef _SC_ATEXIT_MAX + {"SC_ATEXIT_MAX", _SC_ATEXIT_MAX}, +#endif +#ifdef _SC_AUDIT + {"SC_AUDIT", _SC_AUDIT}, +#endif +#ifdef _SC_AVPHYS_PAGES + {"SC_AVPHYS_PAGES", _SC_AVPHYS_PAGES}, +#endif +#ifdef _SC_BC_BASE_MAX + {"SC_BC_BASE_MAX", _SC_BC_BASE_MAX}, +#endif +#ifdef _SC_BC_DIM_MAX + {"SC_BC_DIM_MAX", _SC_BC_DIM_MAX}, +#endif +#ifdef _SC_BC_SCALE_MAX + {"SC_BC_SCALE_MAX", _SC_BC_SCALE_MAX}, +#endif +#ifdef _SC_BC_STRING_MAX + {"SC_BC_STRING_MAX", _SC_BC_STRING_MAX}, +#endif +#ifdef _SC_CAP + {"SC_CAP", _SC_CAP}, +#endif +#ifdef _SC_CHARCLASS_NAME_MAX + {"SC_CHARCLASS_NAME_MAX", _SC_CHARCLASS_NAME_MAX}, +#endif +#ifdef _SC_CHAR_BIT + {"SC_CHAR_BIT", _SC_CHAR_BIT}, +#endif +#ifdef _SC_CHAR_MAX + {"SC_CHAR_MAX", _SC_CHAR_MAX}, +#endif +#ifdef _SC_CHAR_MIN + {"SC_CHAR_MIN", _SC_CHAR_MIN}, +#endif +#ifdef _SC_CHILD_MAX + {"SC_CHILD_MAX", _SC_CHILD_MAX}, +#endif +#ifdef _SC_CLK_TCK + {"SC_CLK_TCK", _SC_CLK_TCK}, +#endif +#ifdef _SC_COHER_BLKSZ + {"SC_COHER_BLKSZ", _SC_COHER_BLKSZ}, +#endif +#ifdef _SC_COLL_WEIGHTS_MAX + {"SC_COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX}, +#endif +#ifdef _SC_DCACHE_ASSOC + {"SC_DCACHE_ASSOC", _SC_DCACHE_ASSOC}, +#endif +#ifdef _SC_DCACHE_BLKSZ + {"SC_DCACHE_BLKSZ", _SC_DCACHE_BLKSZ}, +#endif +#ifdef _SC_DCACHE_LINESZ + {"SC_DCACHE_LINESZ", _SC_DCACHE_LINESZ}, +#endif +#ifdef _SC_DCACHE_SZ + {"SC_DCACHE_SZ", _SC_DCACHE_SZ}, +#endif +#ifdef _SC_DCACHE_TBLKSZ + {"SC_DCACHE_TBLKSZ", _SC_DCACHE_TBLKSZ}, +#endif +#ifdef _SC_DELAYTIMER_MAX + {"SC_DELAYTIMER_MAX", _SC_DELAYTIMER_MAX}, +#endif +#ifdef _SC_EQUIV_CLASS_MAX + {"SC_EQUIV_CLASS_MAX", _SC_EQUIV_CLASS_MAX}, +#endif +#ifdef _SC_EXPR_NEST_MAX + {"SC_EXPR_NEST_MAX", _SC_EXPR_NEST_MAX}, +#endif +#ifdef _SC_FSYNC + {"SC_FSYNC", _SC_FSYNC}, +#endif +#ifdef _SC_GETGR_R_SIZE_MAX + {"SC_GETGR_R_SIZE_MAX", _SC_GETGR_R_SIZE_MAX}, +#endif +#ifdef _SC_GETPW_R_SIZE_MAX + {"SC_GETPW_R_SIZE_MAX", _SC_GETPW_R_SIZE_MAX}, +#endif +#ifdef _SC_ICACHE_ASSOC + {"SC_ICACHE_ASSOC", _SC_ICACHE_ASSOC}, +#endif +#ifdef _SC_ICACHE_BLKSZ + {"SC_ICACHE_BLKSZ", _SC_ICACHE_BLKSZ}, +#endif +#ifdef _SC_ICACHE_LINESZ + {"SC_ICACHE_LINESZ", _SC_ICACHE_LINESZ}, +#endif +#ifdef _SC_ICACHE_SZ + {"SC_ICACHE_SZ", _SC_ICACHE_SZ}, +#endif +#ifdef _SC_INF + {"SC_INF", _SC_INF}, +#endif +#ifdef _SC_INT_MAX + {"SC_INT_MAX", _SC_INT_MAX}, +#endif +#ifdef _SC_INT_MIN + {"SC_INT_MIN", _SC_INT_MIN}, +#endif +#ifdef _SC_IOV_MAX + {"SC_IOV_MAX", _SC_IOV_MAX}, +#endif +#ifdef _SC_IP_SECOPTS + {"SC_IP_SECOPTS", _SC_IP_SECOPTS}, +#endif +#ifdef _SC_JOB_CONTROL + {"SC_JOB_CONTROL", _SC_JOB_CONTROL}, +#endif +#ifdef _SC_KERN_POINTERS + {"SC_KERN_POINTERS", _SC_KERN_POINTERS}, +#endif +#ifdef _SC_KERN_SIM + {"SC_KERN_SIM", _SC_KERN_SIM}, +#endif +#ifdef _SC_LINE_MAX + {"SC_LINE_MAX", _SC_LINE_MAX}, +#endif +#ifdef _SC_LOGIN_NAME_MAX + {"SC_LOGIN_NAME_MAX", _SC_LOGIN_NAME_MAX}, +#endif +#ifdef _SC_LOGNAME_MAX + {"SC_LOGNAME_MAX", _SC_LOGNAME_MAX}, +#endif +#ifdef _SC_LONG_BIT + {"SC_LONG_BIT", _SC_LONG_BIT}, +#endif +#ifdef _SC_MAC + {"SC_MAC", _SC_MAC}, +#endif +#ifdef _SC_MAPPED_FILES + {"SC_MAPPED_FILES", _SC_MAPPED_FILES}, +#endif +#ifdef _SC_MAXPID + {"SC_MAXPID", _SC_MAXPID}, +#endif +#ifdef _SC_MB_LEN_MAX + {"SC_MB_LEN_MAX", _SC_MB_LEN_MAX}, +#endif +#ifdef _SC_MEMLOCK + {"SC_MEMLOCK", _SC_MEMLOCK}, +#endif +#ifdef _SC_MEMLOCK_RANGE + {"SC_MEMLOCK_RANGE", _SC_MEMLOCK_RANGE}, +#endif +#ifdef _SC_MEMORY_PROTECTION + {"SC_MEMORY_PROTECTION", _SC_MEMORY_PROTECTION}, +#endif +#ifdef _SC_MESSAGE_PASSING + {"SC_MESSAGE_PASSING", _SC_MESSAGE_PASSING}, +#endif +#ifdef _SC_MMAP_FIXED_ALIGNMENT + {"SC_MMAP_FIXED_ALIGNMENT", _SC_MMAP_FIXED_ALIGNMENT}, +#endif +#ifdef _SC_MQ_OPEN_MAX + {"SC_MQ_OPEN_MAX", _SC_MQ_OPEN_MAX}, +#endif +#ifdef _SC_MQ_PRIO_MAX + {"SC_MQ_PRIO_MAX", _SC_MQ_PRIO_MAX}, +#endif +#ifdef _SC_NACLS_MAX + {"SC_NACLS_MAX", _SC_NACLS_MAX}, +#endif +#ifdef _SC_NGROUPS_MAX + {"SC_NGROUPS_MAX", _SC_NGROUPS_MAX}, +#endif +#ifdef _SC_NL_ARGMAX + {"SC_NL_ARGMAX", _SC_NL_ARGMAX}, +#endif +#ifdef _SC_NL_LANGMAX + {"SC_NL_LANGMAX", _SC_NL_LANGMAX}, +#endif +#ifdef _SC_NL_MSGMAX + {"SC_NL_MSGMAX", _SC_NL_MSGMAX}, +#endif +#ifdef _SC_NL_NMAX + {"SC_NL_NMAX", _SC_NL_NMAX}, +#endif +#ifdef _SC_NL_SETMAX + {"SC_NL_SETMAX", _SC_NL_SETMAX}, +#endif +#ifdef _SC_NL_TEXTMAX + {"SC_NL_TEXTMAX", _SC_NL_TEXTMAX}, +#endif +#ifdef _SC_NPROCESSORS_CONF + {"SC_NPROCESSORS_CONF", _SC_NPROCESSORS_CONF}, +#endif +#ifdef _SC_NPROCESSORS_ONLN + {"SC_NPROCESSORS_ONLN", _SC_NPROCESSORS_ONLN}, +#endif +#ifdef _SC_NPROC_CONF + {"SC_NPROC_CONF", _SC_NPROC_CONF}, +#endif +#ifdef _SC_NPROC_ONLN + {"SC_NPROC_ONLN", _SC_NPROC_ONLN}, +#endif +#ifdef _SC_NZERO + {"SC_NZERO", _SC_NZERO}, +#endif +#ifdef _SC_OPEN_MAX + {"SC_OPEN_MAX", _SC_OPEN_MAX}, +#endif +#ifdef _SC_PAGESIZE + {"SC_PAGESIZE", _SC_PAGESIZE}, +#endif +#ifdef _SC_PAGE_SIZE + {"SC_PAGE_SIZE", _SC_PAGE_SIZE}, +#endif +#ifdef _SC_PASS_MAX + {"SC_PASS_MAX", _SC_PASS_MAX}, +#endif +#ifdef _SC_PHYS_PAGES + {"SC_PHYS_PAGES", _SC_PHYS_PAGES}, +#endif +#ifdef _SC_PII + {"SC_PII", _SC_PII}, +#endif +#ifdef _SC_PII_INTERNET + {"SC_PII_INTERNET", _SC_PII_INTERNET}, +#endif +#ifdef _SC_PII_INTERNET_DGRAM + {"SC_PII_INTERNET_DGRAM", _SC_PII_INTERNET_DGRAM}, +#endif +#ifdef _SC_PII_INTERNET_STREAM + {"SC_PII_INTERNET_STREAM", _SC_PII_INTERNET_STREAM}, +#endif +#ifdef _SC_PII_OSI + {"SC_PII_OSI", _SC_PII_OSI}, +#endif +#ifdef _SC_PII_OSI_CLTS + {"SC_PII_OSI_CLTS", _SC_PII_OSI_CLTS}, +#endif +#ifdef _SC_PII_OSI_COTS + {"SC_PII_OSI_COTS", _SC_PII_OSI_COTS}, +#endif +#ifdef _SC_PII_OSI_M + {"SC_PII_OSI_M", _SC_PII_OSI_M}, +#endif +#ifdef _SC_PII_SOCKET + {"SC_PII_SOCKET", _SC_PII_SOCKET}, +#endif +#ifdef _SC_PII_XTI + {"SC_PII_XTI", _SC_PII_XTI}, +#endif +#ifdef _SC_POLL + {"SC_POLL", _SC_POLL}, +#endif +#ifdef _SC_PRIORITIZED_IO + {"SC_PRIORITIZED_IO", _SC_PRIORITIZED_IO}, +#endif +#ifdef _SC_PRIORITY_SCHEDULING + {"SC_PRIORITY_SCHEDULING", _SC_PRIORITY_SCHEDULING}, +#endif +#ifdef _SC_REALTIME_SIGNALS + {"SC_REALTIME_SIGNALS", _SC_REALTIME_SIGNALS}, +#endif +#ifdef _SC_RE_DUP_MAX + {"SC_RE_DUP_MAX", _SC_RE_DUP_MAX}, +#endif +#ifdef _SC_RTSIG_MAX + {"SC_RTSIG_MAX", _SC_RTSIG_MAX}, +#endif +#ifdef _SC_SAVED_IDS + {"SC_SAVED_IDS", _SC_SAVED_IDS}, +#endif +#ifdef _SC_SCHAR_MAX + {"SC_SCHAR_MAX", _SC_SCHAR_MAX}, +#endif +#ifdef _SC_SCHAR_MIN + {"SC_SCHAR_MIN", _SC_SCHAR_MIN}, +#endif +#ifdef _SC_SELECT + {"SC_SELECT", _SC_SELECT}, +#endif +#ifdef _SC_SEMAPHORES + {"SC_SEMAPHORES", _SC_SEMAPHORES}, +#endif +#ifdef _SC_SEM_NSEMS_MAX + {"SC_SEM_NSEMS_MAX", _SC_SEM_NSEMS_MAX}, +#endif +#ifdef _SC_SEM_VALUE_MAX + {"SC_SEM_VALUE_MAX", _SC_SEM_VALUE_MAX}, +#endif +#ifdef _SC_SHARED_MEMORY_OBJECTS + {"SC_SHARED_MEMORY_OBJECTS", _SC_SHARED_MEMORY_OBJECTS}, +#endif +#ifdef _SC_SHRT_MAX + {"SC_SHRT_MAX", _SC_SHRT_MAX}, +#endif +#ifdef _SC_SHRT_MIN + {"SC_SHRT_MIN", _SC_SHRT_MIN}, +#endif +#ifdef _SC_SIGQUEUE_MAX + {"SC_SIGQUEUE_MAX", _SC_SIGQUEUE_MAX}, +#endif +#ifdef _SC_SIGRT_MAX + {"SC_SIGRT_MAX", _SC_SIGRT_MAX}, +#endif +#ifdef _SC_SIGRT_MIN + {"SC_SIGRT_MIN", _SC_SIGRT_MIN}, +#endif +#ifdef _SC_SOFTPOWER + {"SC_SOFTPOWER", _SC_SOFTPOWER}, +#endif +#ifdef _SC_SPLIT_CACHE + {"SC_SPLIT_CACHE", _SC_SPLIT_CACHE}, +#endif +#ifdef _SC_SSIZE_MAX + {"SC_SSIZE_MAX", _SC_SSIZE_MAX}, +#endif +#ifdef _SC_STACK_PROT + {"SC_STACK_PROT", _SC_STACK_PROT}, +#endif +#ifdef _SC_STREAM_MAX + {"SC_STREAM_MAX", _SC_STREAM_MAX}, +#endif +#ifdef _SC_SYNCHRONIZED_IO + {"SC_SYNCHRONIZED_IO", _SC_SYNCHRONIZED_IO}, +#endif +#ifdef _SC_THREADS + {"SC_THREADS", _SC_THREADS}, +#endif +#ifdef _SC_THREAD_ATTR_STACKADDR + {"SC_THREAD_ATTR_STACKADDR", _SC_THREAD_ATTR_STACKADDR}, +#endif +#ifdef _SC_THREAD_ATTR_STACKSIZE + {"SC_THREAD_ATTR_STACKSIZE", _SC_THREAD_ATTR_STACKSIZE}, +#endif +#ifdef _SC_THREAD_DESTRUCTOR_ITERATIONS + {"SC_THREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS}, +#endif +#ifdef _SC_THREAD_KEYS_MAX + {"SC_THREAD_KEYS_MAX", _SC_THREAD_KEYS_MAX}, +#endif +#ifdef _SC_THREAD_PRIORITY_SCHEDULING + {"SC_THREAD_PRIORITY_SCHEDULING", _SC_THREAD_PRIORITY_SCHEDULING}, +#endif +#ifdef _SC_THREAD_PRIO_INHERIT + {"SC_THREAD_PRIO_INHERIT", _SC_THREAD_PRIO_INHERIT}, +#endif +#ifdef _SC_THREAD_PRIO_PROTECT + {"SC_THREAD_PRIO_PROTECT", _SC_THREAD_PRIO_PROTECT}, +#endif +#ifdef _SC_THREAD_PROCESS_SHARED + {"SC_THREAD_PROCESS_SHARED", _SC_THREAD_PROCESS_SHARED}, +#endif +#ifdef _SC_THREAD_SAFE_FUNCTIONS + {"SC_THREAD_SAFE_FUNCTIONS", _SC_THREAD_SAFE_FUNCTIONS}, +#endif +#ifdef _SC_THREAD_STACK_MIN + {"SC_THREAD_STACK_MIN", _SC_THREAD_STACK_MIN}, +#endif +#ifdef _SC_THREAD_THREADS_MAX + {"SC_THREAD_THREADS_MAX", _SC_THREAD_THREADS_MAX}, +#endif +#ifdef _SC_TIMERS + {"SC_TIMERS", _SC_TIMERS}, +#endif +#ifdef _SC_TIMER_MAX + {"SC_TIMER_MAX", _SC_TIMER_MAX}, +#endif +#ifdef _SC_TTY_NAME_MAX + {"SC_TTY_NAME_MAX", _SC_TTY_NAME_MAX}, +#endif +#ifdef _SC_TZNAME_MAX + {"SC_TZNAME_MAX", _SC_TZNAME_MAX}, +#endif +#ifdef _SC_T_IOV_MAX + {"SC_T_IOV_MAX", _SC_T_IOV_MAX}, +#endif +#ifdef _SC_UCHAR_MAX + {"SC_UCHAR_MAX", _SC_UCHAR_MAX}, +#endif +#ifdef _SC_UINT_MAX + {"SC_UINT_MAX", _SC_UINT_MAX}, +#endif +#ifdef _SC_UIO_MAXIOV + {"SC_UIO_MAXIOV", _SC_UIO_MAXIOV}, +#endif +#ifdef _SC_ULONG_MAX + {"SC_ULONG_MAX", _SC_ULONG_MAX}, +#endif +#ifdef _SC_USHRT_MAX + {"SC_USHRT_MAX", _SC_USHRT_MAX}, +#endif +#ifdef _SC_VERSION + {"SC_VERSION", _SC_VERSION}, +#endif +#ifdef _SC_WORD_BIT + {"SC_WORD_BIT", _SC_WORD_BIT}, +#endif +#ifdef _SC_XBS5_ILP32_OFF32 + {"SC_XBS5_ILP32_OFF32", _SC_XBS5_ILP32_OFF32}, +#endif +#ifdef _SC_XBS5_ILP32_OFFBIG + {"SC_XBS5_ILP32_OFFBIG", _SC_XBS5_ILP32_OFFBIG}, +#endif +#ifdef _SC_XBS5_LP64_OFF64 + {"SC_XBS5_LP64_OFF64", _SC_XBS5_LP64_OFF64}, +#endif +#ifdef _SC_XBS5_LPBIG_OFFBIG + {"SC_XBS5_LPBIG_OFFBIG", _SC_XBS5_LPBIG_OFFBIG}, +#endif +#ifdef _SC_XOPEN_CRYPT + {"SC_XOPEN_CRYPT", _SC_XOPEN_CRYPT}, +#endif +#ifdef _SC_XOPEN_ENH_I18N + {"SC_XOPEN_ENH_I18N", _SC_XOPEN_ENH_I18N}, +#endif +#ifdef _SC_XOPEN_LEGACY + {"SC_XOPEN_LEGACY", _SC_XOPEN_LEGACY}, +#endif +#ifdef _SC_XOPEN_REALTIME + {"SC_XOPEN_REALTIME", _SC_XOPEN_REALTIME}, +#endif +#ifdef _SC_XOPEN_REALTIME_THREADS + {"SC_XOPEN_REALTIME_THREADS", _SC_XOPEN_REALTIME_THREADS}, +#endif +#ifdef _SC_XOPEN_SHM + {"SC_XOPEN_SHM", _SC_XOPEN_SHM}, +#endif +#ifdef _SC_XOPEN_UNIX + {"SC_XOPEN_UNIX", _SC_XOPEN_UNIX}, +#endif +#ifdef _SC_XOPEN_VERSION + {"SC_XOPEN_VERSION", _SC_XOPEN_VERSION}, +#endif +#ifdef _SC_XOPEN_XCU_VERSION + {"SC_XOPEN_XCU_VERSION", _SC_XOPEN_XCU_VERSION}, +#endif +#ifdef _SC_XOPEN_XPG2 + {"SC_XOPEN_XPG2", _SC_XOPEN_XPG2}, +#endif +#ifdef _SC_XOPEN_XPG3 + {"SC_XOPEN_XPG3", _SC_XOPEN_XPG3}, +#endif +#ifdef _SC_XOPEN_XPG4 + {"SC_XOPEN_XPG4", _SC_XOPEN_XPG4}, +#endif +}; + +static int +conv_sysconf_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_sysconf, + sizeof(posix_constants_sysconf) + / sizeof(struct constdef)); +} + +PyDoc_STRVAR(posix_sysconf__doc__, +"sysconf(name) -> integer\n\n\ +Return an integer-valued system configuration variable."); + +static PyObject * +posix_sysconf(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name; + + if (PyArg_ParseTuple(args, "O&:sysconf", conv_sysconf_confname, &name)) { + int value; + + errno = 0; + value = sysconf(name); + if (value == -1 && errno != 0) + posix_error(); + else + result = PyInt_FromLong(value); + } + return result; +} +#endif + + +/* This code is used to ensure that the tables of configuration value names + * are in sorted order as required by conv_confname(), and also to build the + * the exported dictionaries that are used to publish information about the + * names available on the host platform. + * + * Sorting the table at runtime ensures that the table is properly ordered + * when used, even for platforms we're not able to test on. It also makes + * it easier to add additional entries to the tables. + */ + +static int +cmp_constdefs(const void *v1, const void *v2) +{ + const struct constdef *c1 = + (const struct constdef *) v1; + const struct constdef *c2 = + (const struct constdef *) v2; + + return strcmp(c1->name, c2->name); +} + +static int +setup_confname_table(struct constdef *table, size_t tablesize, + char *tablename, PyObject *module) +{ + PyObject *d = NULL; + size_t i; + + qsort(table, tablesize, sizeof(struct constdef), cmp_constdefs); + d = PyDict_New(); + if (d == NULL) + return -1; + + for (i=0; i < tablesize; ++i) { + PyObject *o = PyInt_FromLong(table[i].value); + if (o == NULL || PyDict_SetItemString(d, table[i].name, o) == -1) { + Py_XDECREF(o); + Py_DECREF(d); + return -1; + } + Py_DECREF(o); + } + return PyModule_AddObject(module, tablename, d); +} + +/* Return -1 on failure, 0 on success. */ +static int +setup_confname_tables(PyObject *module) +{ +#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF) + if (setup_confname_table(posix_constants_pathconf, + sizeof(posix_constants_pathconf) + / sizeof(struct constdef), + "pathconf_names", module)) + return -1; +#endif +#ifdef HAVE_CONFSTR + if (setup_confname_table(posix_constants_confstr, + sizeof(posix_constants_confstr) + / sizeof(struct constdef), + "confstr_names", module)) + return -1; +#endif +#ifdef HAVE_SYSCONF + if (setup_confname_table(posix_constants_sysconf, + sizeof(posix_constants_sysconf) + / sizeof(struct constdef), + "sysconf_names", module)) + return -1; +#endif + return 0; +} + + +PyDoc_STRVAR(posix_abort__doc__, +"abort() -> does not return!\n\n\ +Abort the interpreter immediately. This 'dumps core' or otherwise fails\n\ +in the hardest way possible on the hosting operating system."); + +static PyObject * +posix_abort(PyObject *self, PyObject *noargs) +{ + abort(); + /*NOTREACHED*/ + Py_FatalError("abort() called from Python code didn't abort!"); + return NULL; +} + +#ifdef MS_WINDOWS +PyDoc_STRVAR(win32_startfile__doc__, +"startfile(filepath [, operation]) - Start a file with its associated\n\ +application.\n\ +\n\ +When \"operation\" is not specified or \"open\", this acts like\n\ +double-clicking the file in Explorer, or giving the file name as an\n\ +argument to the DOS \"start\" command: the file is opened with whatever\n\ +application (if any) its extension is associated.\n\ +When another \"operation\" is given, it specifies what should be done with\n\ +the file. A typical operation is \"print\".\n\ +\n\ +startfile returns as soon as the associated application is launched.\n\ +There is no option to wait for the application to close, and no way\n\ +to retrieve the application's exit status.\n\ +\n\ +The filepath is relative to the current directory. If you want to use\n\ +an absolute path, make sure the first character is not a slash (\"/\");\n\ +the underlying Win32 ShellExecute function doesn't work if it is."); + +static PyObject * +win32_startfile(PyObject *self, PyObject *args) +{ + char *filepath; + char *operation = NULL; + HINSTANCE rc; + if (!PyArg_ParseTuple(args, "et|s:startfile", + Py_FileSystemDefaultEncoding, &filepath, + &operation)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rc = ShellExecute((HWND)0, operation, filepath, + NULL, NULL, SW_SHOWNORMAL); + Py_END_ALLOW_THREADS + if (rc <= (HINSTANCE)32) { + PyObject *errval = win32_error("startfile", filepath); + PyMem_Free(filepath); + return errval; + } + PyMem_Free(filepath); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_GETLOADAVG +PyDoc_STRVAR(posix_getloadavg__doc__, +"getloadavg() -> (float, float, float)\n\n\ +Return the number of processes in the system run queue averaged over\n\ +the last 1, 5, and 15 minutes or raises OSError if the load average\n\ +was unobtainable"); + +static PyObject * +posix_getloadavg(PyObject *self, PyObject *noargs) +{ + double loadavg[3]; + if (getloadavg(loadavg, 3)!=3) { + PyErr_SetString(PyExc_OSError, "Load averages are unobtainable"); + return NULL; + } else + return Py_BuildValue("ddd", loadavg[0], loadavg[1], loadavg[2]); +} +#endif + +#ifdef MS_WINDOWS + +PyDoc_STRVAR(win32_urandom__doc__, +"urandom(n) -> str\n\n\ +Return a string of n random bytes suitable for cryptographic use."); + +typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\ + LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\ + DWORD dwFlags ); +typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\ + BYTE *pbBuffer ); + +static CRYPTGENRANDOM pCryptGenRandom = NULL; +static HCRYPTPROV hCryptProv = 0; + +static PyObject* +win32_urandom(PyObject *self, PyObject *args) +{ + int howMany; + PyObject* result; + + /* Read arguments */ + if (! PyArg_ParseTuple(args, "i:urandom", &howMany)) + return NULL; + if (howMany < 0) + return PyErr_Format(PyExc_ValueError, + "negative argument not allowed"); + + if (hCryptProv == 0) { + HINSTANCE hAdvAPI32 = NULL; + CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL; + + /* Obtain handle to the DLL containing CryptoAPI + This should not fail */ + hAdvAPI32 = GetModuleHandle("advapi32.dll"); + if(hAdvAPI32 == NULL) + return win32_error("GetModuleHandle", NULL); + + /* Obtain pointers to the CryptoAPI functions + This will fail on some early versions of Win95 */ + pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress( + hAdvAPI32, + "CryptAcquireContextA"); + if (pCryptAcquireContext == NULL) + return PyErr_Format(PyExc_NotImplementedError, + "CryptAcquireContextA not found"); + + pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress( + hAdvAPI32, "CryptGenRandom"); + if (pCryptAcquireContext == NULL) + return PyErr_Format(PyExc_NotImplementedError, + "CryptGenRandom not found"); + + /* Acquire context */ + if (! pCryptAcquireContext(&hCryptProv, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + return win32_error("CryptAcquireContext", NULL); + } + + /* Allocate bytes */ + result = PyString_FromStringAndSize(NULL, howMany); + if (result != NULL) { + /* Get random data */ + if (! pCryptGenRandom(hCryptProv, howMany, (unsigned char*) + PyString_AS_STRING(result))) { + Py_DECREF(result); + return win32_error("CryptGenRandom", NULL); + } + } + return result; +} +#endif + +static PyMethodDef posix_methods[] = { + {"access", posix_access, METH_VARARGS, posix_access__doc__}, +#ifdef HAVE_TTYNAME + {"ttyname", posix_ttyname, METH_VARARGS, posix_ttyname__doc__}, +#endif + {"chdir", posix_chdir, METH_VARARGS, posix_chdir__doc__}, + {"chmod", posix_chmod, METH_VARARGS, posix_chmod__doc__}, +#ifdef HAVE_CHOWN + {"chown", posix_chown, METH_VARARGS, posix_chown__doc__}, +#endif /* HAVE_CHOWN */ +#ifdef HAVE_LCHOWN + {"lchown", posix_lchown, METH_VARARGS, posix_lchown__doc__}, +#endif /* HAVE_LCHOWN */ +#ifdef HAVE_CHROOT + {"chroot", posix_chroot, METH_VARARGS, posix_chroot__doc__}, +#endif +#ifdef HAVE_CTERMID + {"ctermid", posix_ctermid, METH_NOARGS, posix_ctermid__doc__}, +#endif +#ifdef HAVE_GETCWD + {"getcwd", posix_getcwd, METH_NOARGS, posix_getcwd__doc__}, +#ifdef Py_USING_UNICODE + {"getcwdu", posix_getcwdu, METH_NOARGS, posix_getcwdu__doc__}, +#endif +#endif +#ifdef HAVE_LINK + {"link", posix_link, METH_VARARGS, posix_link__doc__}, +#endif /* HAVE_LINK */ + {"listdir", posix_listdir, METH_VARARGS, posix_listdir__doc__}, + {"lstat", posix_lstat, METH_VARARGS, posix_lstat__doc__}, + {"mkdir", posix_mkdir, METH_VARARGS, posix_mkdir__doc__}, +#ifdef HAVE_NICE + {"nice", posix_nice, METH_VARARGS, posix_nice__doc__}, +#endif /* HAVE_NICE */ +#ifdef HAVE_READLINK + {"readlink", posix_readlink, METH_VARARGS, posix_readlink__doc__}, +#endif /* HAVE_READLINK */ + {"rename", posix_rename, METH_VARARGS, posix_rename__doc__}, + {"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__}, + {"stat", posix_stat, METH_VARARGS, posix_stat__doc__}, + {"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__}, +#ifdef HAVE_SYMLINK + {"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__}, +#endif /* HAVE_SYMLINK */ +#ifdef HAVE_SYSTEM + {"system", posix_system, METH_VARARGS, posix_system__doc__}, +#endif + {"umask", posix_umask, METH_VARARGS, posix_umask__doc__}, +#ifdef HAVE_UNAME + {"uname", posix_uname, METH_NOARGS, posix_uname__doc__}, +#endif /* HAVE_UNAME */ + {"unlink", posix_unlink, METH_VARARGS, posix_unlink__doc__}, + {"remove", posix_unlink, METH_VARARGS, posix_remove__doc__}, + {"utime", posix_utime, METH_VARARGS, posix_utime__doc__}, +#ifdef HAVE_TIMES + {"times", posix_times, METH_NOARGS, posix_times__doc__}, +#endif /* HAVE_TIMES */ + {"_exit", posix__exit, METH_VARARGS, posix__exit__doc__}, +#ifdef HAVE_EXECV + {"execv", posix_execv, METH_VARARGS, posix_execv__doc__}, + {"execve", posix_execve, METH_VARARGS, posix_execve__doc__}, +#endif /* HAVE_EXECV */ +#ifdef HAVE_SPAWNV + {"spawnv", posix_spawnv, METH_VARARGS, posix_spawnv__doc__}, + {"spawnve", posix_spawnve, METH_VARARGS, posix_spawnve__doc__}, +#if defined(PYOS_OS2) + {"spawnvp", posix_spawnvp, METH_VARARGS, posix_spawnvp__doc__}, + {"spawnvpe", posix_spawnvpe, METH_VARARGS, posix_spawnvpe__doc__}, +#endif /* PYOS_OS2 */ +#endif /* HAVE_SPAWNV */ +#ifdef HAVE_FORK1 + {"fork1", posix_fork1, METH_NOARGS, posix_fork1__doc__}, +#endif /* HAVE_FORK1 */ +#ifdef HAVE_FORK + {"fork", posix_fork, METH_NOARGS, posix_fork__doc__}, +#endif /* HAVE_FORK */ +#if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) + {"openpty", posix_openpty, METH_NOARGS, posix_openpty__doc__}, +#endif /* HAVE_OPENPTY || HAVE__GETPTY || HAVE_DEV_PTMX */ +#ifdef HAVE_FORKPTY + {"forkpty", posix_forkpty, METH_NOARGS, posix_forkpty__doc__}, +#endif /* HAVE_FORKPTY */ +#ifdef HAVE_GETEGID + {"getegid", posix_getegid, METH_NOARGS, posix_getegid__doc__}, +#endif /* HAVE_GETEGID */ +#ifdef HAVE_GETEUID + {"geteuid", posix_geteuid, METH_NOARGS, posix_geteuid__doc__}, +#endif /* HAVE_GETEUID */ +#ifdef HAVE_GETGID + {"getgid", posix_getgid, METH_NOARGS, posix_getgid__doc__}, +#endif /* HAVE_GETGID */ +#ifdef HAVE_GETGROUPS + {"getgroups", posix_getgroups, METH_NOARGS, posix_getgroups__doc__}, +#endif + {"getpid", posix_getpid, METH_NOARGS, posix_getpid__doc__}, +#ifdef HAVE_GETPGRP + {"getpgrp", posix_getpgrp, METH_NOARGS, posix_getpgrp__doc__}, +#endif /* HAVE_GETPGRP */ +#ifdef HAVE_GETPPID + {"getppid", posix_getppid, METH_NOARGS, posix_getppid__doc__}, +#endif /* HAVE_GETPPID */ +#ifdef HAVE_GETUID + {"getuid", posix_getuid, METH_NOARGS, posix_getuid__doc__}, +#endif /* HAVE_GETUID */ +#ifdef HAVE_GETLOGIN + {"getlogin", posix_getlogin, METH_NOARGS, posix_getlogin__doc__}, +#endif +#ifdef HAVE_KILL + {"kill", posix_kill, METH_VARARGS, posix_kill__doc__}, +#endif /* HAVE_KILL */ +#ifdef HAVE_KILLPG + {"killpg", posix_killpg, METH_VARARGS, posix_killpg__doc__}, +#endif /* HAVE_KILLPG */ +#ifdef HAVE_PLOCK + {"plock", posix_plock, METH_VARARGS, posix_plock__doc__}, +#endif /* HAVE_PLOCK */ +#ifdef HAVE_POPEN + {"popen", posix_popen, METH_VARARGS, posix_popen__doc__}, +#ifdef MS_WINDOWS + {"popen2", win32_popen2, METH_VARARGS}, + {"popen3", win32_popen3, METH_VARARGS}, + {"popen4", win32_popen4, METH_VARARGS}, + {"startfile", win32_startfile, METH_VARARGS, win32_startfile__doc__}, +#else +#if defined(PYOS_OS2) && defined(PYCC_GCC) + {"popen2", os2emx_popen2, METH_VARARGS}, + {"popen3", os2emx_popen3, METH_VARARGS}, + {"popen4", os2emx_popen4, METH_VARARGS}, +#endif +#endif +#endif /* HAVE_POPEN */ +#ifdef HAVE_SETUID + {"setuid", posix_setuid, METH_VARARGS, posix_setuid__doc__}, +#endif /* HAVE_SETUID */ +#ifdef HAVE_SETEUID + {"seteuid", posix_seteuid, METH_VARARGS, posix_seteuid__doc__}, +#endif /* HAVE_SETEUID */ +#ifdef HAVE_SETEGID + {"setegid", posix_setegid, METH_VARARGS, posix_setegid__doc__}, +#endif /* HAVE_SETEGID */ +#ifdef HAVE_SETREUID + {"setreuid", posix_setreuid, METH_VARARGS, posix_setreuid__doc__}, +#endif /* HAVE_SETREUID */ +#ifdef HAVE_SETREGID + {"setregid", posix_setregid, METH_VARARGS, posix_setregid__doc__}, +#endif /* HAVE_SETREGID */ +#ifdef HAVE_SETGID + {"setgid", posix_setgid, METH_VARARGS, posix_setgid__doc__}, +#endif /* HAVE_SETGID */ +#ifdef HAVE_SETGROUPS + {"setgroups", posix_setgroups, METH_VARARGS, posix_setgroups__doc__}, +#endif /* HAVE_SETGROUPS */ +#ifdef HAVE_GETPGID + {"getpgid", posix_getpgid, METH_VARARGS, posix_getpgid__doc__}, +#endif /* HAVE_GETPGID */ +#ifdef HAVE_SETPGRP + {"setpgrp", posix_setpgrp, METH_NOARGS, posix_setpgrp__doc__}, +#endif /* HAVE_SETPGRP */ +#ifdef HAVE_WAIT + {"wait", posix_wait, METH_NOARGS, posix_wait__doc__}, +#endif /* HAVE_WAIT */ +#if defined(HAVE_WAITPID) || defined(HAVE_CWAIT) + {"waitpid", posix_waitpid, METH_VARARGS, posix_waitpid__doc__}, +#endif /* HAVE_WAITPID */ +#ifdef HAVE_GETSID + {"getsid", posix_getsid, METH_VARARGS, posix_getsid__doc__}, +#endif /* HAVE_GETSID */ +#ifdef HAVE_SETSID + {"setsid", posix_setsid, METH_NOARGS, posix_setsid__doc__}, +#endif /* HAVE_SETSID */ +#ifdef HAVE_SETPGID + {"setpgid", posix_setpgid, METH_VARARGS, posix_setpgid__doc__}, +#endif /* HAVE_SETPGID */ +#ifdef HAVE_TCGETPGRP + {"tcgetpgrp", posix_tcgetpgrp, METH_VARARGS, posix_tcgetpgrp__doc__}, +#endif /* HAVE_TCGETPGRP */ +#ifdef HAVE_TCSETPGRP + {"tcsetpgrp", posix_tcsetpgrp, METH_VARARGS, posix_tcsetpgrp__doc__}, +#endif /* HAVE_TCSETPGRP */ + {"open", posix_open, METH_VARARGS, posix_open__doc__}, + {"close", posix_close, METH_VARARGS, posix_close__doc__}, + {"dup", posix_dup, METH_VARARGS, posix_dup__doc__}, + {"dup2", posix_dup2, METH_VARARGS, posix_dup2__doc__}, + {"lseek", posix_lseek, METH_VARARGS, posix_lseek__doc__}, + {"read", posix_read, METH_VARARGS, posix_read__doc__}, + {"write", posix_write, METH_VARARGS, posix_write__doc__}, + {"fstat", posix_fstat, METH_VARARGS, posix_fstat__doc__}, + {"fdopen", posix_fdopen, METH_VARARGS, posix_fdopen__doc__}, + {"isatty", posix_isatty, METH_VARARGS, posix_isatty__doc__}, +#ifdef HAVE_PIPE + {"pipe", posix_pipe, METH_NOARGS, posix_pipe__doc__}, +#endif +#ifdef HAVE_MKFIFO + {"mkfifo", posix_mkfifo, METH_VARARGS, posix_mkfifo__doc__}, +#endif +#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) + {"mknod", posix_mknod, METH_VARARGS, posix_mknod__doc__}, +#endif +#ifdef HAVE_DEVICE_MACROS + {"major", posix_major, METH_VARARGS, posix_major__doc__}, + {"minor", posix_minor, METH_VARARGS, posix_minor__doc__}, + {"makedev", posix_makedev, METH_VARARGS, posix_makedev__doc__}, +#endif +#ifdef HAVE_FTRUNCATE + {"ftruncate", posix_ftruncate, METH_VARARGS, posix_ftruncate__doc__}, +#endif +#ifdef HAVE_PUTENV + {"putenv", posix_putenv, METH_VARARGS, posix_putenv__doc__}, +#endif +#ifdef HAVE_UNSETENV + {"unsetenv", posix_unsetenv, METH_VARARGS, posix_unsetenv__doc__}, +#endif +#ifdef HAVE_STRERROR + {"strerror", posix_strerror, METH_VARARGS, posix_strerror__doc__}, +#endif +#ifdef HAVE_FCHDIR + {"fchdir", posix_fchdir, METH_O, posix_fchdir__doc__}, +#endif +#ifdef HAVE_FSYNC + {"fsync", posix_fsync, METH_O, posix_fsync__doc__}, +#endif +#ifdef HAVE_FDATASYNC + {"fdatasync", posix_fdatasync, METH_O, posix_fdatasync__doc__}, +#endif +#ifdef HAVE_SYS_WAIT_H +#ifdef WCOREDUMP + {"WCOREDUMP", posix_WCOREDUMP, METH_VARARGS, posix_WCOREDUMP__doc__}, +#endif /* WCOREDUMP */ +#ifdef WIFCONTINUED + {"WIFCONTINUED",posix_WIFCONTINUED, METH_VARARGS, posix_WIFCONTINUED__doc__}, +#endif /* WIFCONTINUED */ +#ifdef WIFSTOPPED + {"WIFSTOPPED", posix_WIFSTOPPED, METH_VARARGS, posix_WIFSTOPPED__doc__}, +#endif /* WIFSTOPPED */ +#ifdef WIFSIGNALED + {"WIFSIGNALED", posix_WIFSIGNALED, METH_VARARGS, posix_WIFSIGNALED__doc__}, +#endif /* WIFSIGNALED */ +#ifdef WIFEXITED + {"WIFEXITED", posix_WIFEXITED, METH_VARARGS, posix_WIFEXITED__doc__}, +#endif /* WIFEXITED */ +#ifdef WEXITSTATUS + {"WEXITSTATUS", posix_WEXITSTATUS, METH_VARARGS, posix_WEXITSTATUS__doc__}, +#endif /* WEXITSTATUS */ +#ifdef WTERMSIG + {"WTERMSIG", posix_WTERMSIG, METH_VARARGS, posix_WTERMSIG__doc__}, +#endif /* WTERMSIG */ +#ifdef WSTOPSIG + {"WSTOPSIG", posix_WSTOPSIG, METH_VARARGS, posix_WSTOPSIG__doc__}, +#endif /* WSTOPSIG */ +#endif /* HAVE_SYS_WAIT_H */ +#ifdef HAVE_FSTATVFS + {"fstatvfs", posix_fstatvfs, METH_VARARGS, posix_fstatvfs__doc__}, +#endif +#ifdef HAVE_STATVFS + {"statvfs", posix_statvfs, METH_VARARGS, posix_statvfs__doc__}, +#endif +#ifdef HAVE_TMPFILE + {"tmpfile", posix_tmpfile, METH_NOARGS, posix_tmpfile__doc__}, +#endif +#ifdef HAVE_TEMPNAM + {"tempnam", posix_tempnam, METH_VARARGS, posix_tempnam__doc__}, +#endif +#ifdef HAVE_TMPNAM + {"tmpnam", posix_tmpnam, METH_NOARGS, posix_tmpnam__doc__}, +#endif +#ifdef HAVE_CONFSTR + {"confstr", posix_confstr, METH_VARARGS, posix_confstr__doc__}, +#endif +#ifdef HAVE_SYSCONF + {"sysconf", posix_sysconf, METH_VARARGS, posix_sysconf__doc__}, +#endif +#ifdef HAVE_FPATHCONF + {"fpathconf", posix_fpathconf, METH_VARARGS, posix_fpathconf__doc__}, +#endif +#ifdef HAVE_PATHCONF + {"pathconf", posix_pathconf, METH_VARARGS, posix_pathconf__doc__}, +#endif + {"abort", posix_abort, METH_NOARGS, posix_abort__doc__}, +#ifdef MS_WINDOWS + {"_getfullpathname", posix__getfullpathname, METH_VARARGS, NULL}, +#endif +#ifdef HAVE_GETLOADAVG + {"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__}, +#endif + #ifdef MS_WINDOWS + {"urandom", win32_urandom, METH_VARARGS, win32_urandom__doc__}, + #endif + {NULL, NULL} /* Sentinel */ +}; + + +static int +ins(PyObject *module, char *symbol, long value) +{ + return PyModule_AddIntConstant(module, symbol, value); +} + +#if defined(PYOS_OS2) +/* Insert Platform-Specific Constant Values (Strings & Numbers) of Common Use */ +static int insertvalues(PyObject *module) +{ + APIRET rc; + ULONG values[QSV_MAX+1]; + PyObject *v; + char *ver, tmp[50]; + + Py_BEGIN_ALLOW_THREADS + rc = DosQuerySysInfo(1L, QSV_MAX, &values[1], sizeof(ULONG) * QSV_MAX); + Py_END_ALLOW_THREADS + + if (rc != NO_ERROR) { + os2_error(rc); + return -1; + } + + if (ins(module, "meminstalled", values[QSV_TOTPHYSMEM])) return -1; + if (ins(module, "memkernel", values[QSV_TOTRESMEM])) return -1; + if (ins(module, "memvirtual", values[QSV_TOTAVAILMEM])) return -1; + if (ins(module, "maxpathlen", values[QSV_MAX_PATH_LENGTH])) return -1; + if (ins(module, "maxnamelen", values[QSV_MAX_COMP_LENGTH])) return -1; + if (ins(module, "revision", values[QSV_VERSION_REVISION])) return -1; + if (ins(module, "timeslice", values[QSV_MIN_SLICE])) return -1; + + switch (values[QSV_VERSION_MINOR]) { + case 0: ver = "2.00"; break; + case 10: ver = "2.10"; break; + case 11: ver = "2.11"; break; + case 30: ver = "3.00"; break; + case 40: ver = "4.00"; break; + case 50: ver = "5.00"; break; + default: + PyOS_snprintf(tmp, sizeof(tmp), + "%d-%d", values[QSV_VERSION_MAJOR], + values[QSV_VERSION_MINOR]); + ver = &tmp[0]; + } + + /* Add Indicator of the Version of the Operating System */ + if (PyModule_AddStringConstant(module, "version", tmp) < 0) + return -1; + + /* Add Indicator of Which Drive was Used to Boot the System */ + tmp[0] = 'A' + values[QSV_BOOT_DRIVE] - 1; + tmp[1] = ':'; + tmp[2] = '\0'; + + return PyModule_AddStringConstant(module, "bootdrive", tmp); +} +#endif + +static int +all_ins(PyObject *d) +{ +#ifdef F_OK + if (ins(d, "F_OK", (long)F_OK)) return -1; +#endif +#ifdef R_OK + if (ins(d, "R_OK", (long)R_OK)) return -1; +#endif +#ifdef W_OK + if (ins(d, "W_OK", (long)W_OK)) return -1; +#endif +#ifdef X_OK + if (ins(d, "X_OK", (long)X_OK)) return -1; +#endif +#ifdef NGROUPS_MAX + if (ins(d, "NGROUPS_MAX", (long)NGROUPS_MAX)) return -1; +#endif +#ifdef TMP_MAX + if (ins(d, "TMP_MAX", (long)TMP_MAX)) return -1; +#endif +#ifdef WCONTINUED + if (ins(d, "WCONTINUED", (long)WCONTINUED)) return -1; +#endif +#ifdef WNOHANG + if (ins(d, "WNOHANG", (long)WNOHANG)) return -1; +#endif +#ifdef WUNTRACED + if (ins(d, "WUNTRACED", (long)WUNTRACED)) return -1; +#endif +#ifdef O_RDONLY + if (ins(d, "O_RDONLY", (long)O_RDONLY)) return -1; +#endif +#ifdef O_WRONLY + if (ins(d, "O_WRONLY", (long)O_WRONLY)) return -1; +#endif +#ifdef O_RDWR + if (ins(d, "O_RDWR", (long)O_RDWR)) return -1; +#endif +#ifdef O_NDELAY + if (ins(d, "O_NDELAY", (long)O_NDELAY)) return -1; +#endif +#ifdef O_NONBLOCK + if (ins(d, "O_NONBLOCK", (long)O_NONBLOCK)) return -1; +#endif +#ifdef O_APPEND + if (ins(d, "O_APPEND", (long)O_APPEND)) return -1; +#endif +#ifdef O_DSYNC + if (ins(d, "O_DSYNC", (long)O_DSYNC)) return -1; +#endif +#ifdef O_RSYNC + if (ins(d, "O_RSYNC", (long)O_RSYNC)) return -1; +#endif +#ifdef O_SYNC + if (ins(d, "O_SYNC", (long)O_SYNC)) return -1; +#endif +#ifdef O_NOCTTY + if (ins(d, "O_NOCTTY", (long)O_NOCTTY)) return -1; +#endif +#ifdef O_CREAT + if (ins(d, "O_CREAT", (long)O_CREAT)) return -1; +#endif +#ifdef O_EXCL + if (ins(d, "O_EXCL", (long)O_EXCL)) return -1; +#endif +#ifdef O_TRUNC + if (ins(d, "O_TRUNC", (long)O_TRUNC)) return -1; +#endif +#ifdef O_BINARY + if (ins(d, "O_BINARY", (long)O_BINARY)) return -1; +#endif +#ifdef O_TEXT + if (ins(d, "O_TEXT", (long)O_TEXT)) return -1; +#endif +#ifdef O_LARGEFILE + if (ins(d, "O_LARGEFILE", (long)O_LARGEFILE)) return -1; +#endif +#ifdef O_SHLOCK + if (ins(d, "O_SHLOCK", (long)O_SHLOCK)) return -1; +#endif +#ifdef O_EXLOCK + if (ins(d, "O_EXLOCK", (long)O_EXLOCK)) return -1; +#endif + +/* MS Windows */ +#ifdef O_NOINHERIT + /* Don't inherit in child processes. */ + if (ins(d, "O_NOINHERIT", (long)O_NOINHERIT)) return -1; +#endif +#ifdef _O_SHORT_LIVED + /* Optimize for short life (keep in memory). */ + /* MS forgot to define this one with a non-underscore form too. */ + if (ins(d, "O_SHORT_LIVED", (long)_O_SHORT_LIVED)) return -1; +#endif +#ifdef O_TEMPORARY + /* Automatically delete when last handle is closed. */ + if (ins(d, "O_TEMPORARY", (long)O_TEMPORARY)) return -1; +#endif +#ifdef O_RANDOM + /* Optimize for random access. */ + if (ins(d, "O_RANDOM", (long)O_RANDOM)) return -1; +#endif +#ifdef O_SEQUENTIAL + /* Optimize for sequential access. */ + if (ins(d, "O_SEQUENTIAL", (long)O_SEQUENTIAL)) return -1; +#endif + +/* GNU extensions. */ +#ifdef O_DIRECT + /* Direct disk access. */ + if (ins(d, "O_DIRECT", (long)O_DIRECT)) return -1; +#endif +#ifdef O_DIRECTORY + /* Must be a directory. */ + if (ins(d, "O_DIRECTORY", (long)O_DIRECTORY)) return -1; +#endif +#ifdef O_NOFOLLOW + /* Do not follow links. */ + if (ins(d, "O_NOFOLLOW", (long)O_NOFOLLOW)) return -1; +#endif + + /* These come from sysexits.h */ +#ifdef EX_OK + if (ins(d, "EX_OK", (long)EX_OK)) return -1; +#endif /* EX_OK */ +#ifdef EX_USAGE + if (ins(d, "EX_USAGE", (long)EX_USAGE)) return -1; +#endif /* EX_USAGE */ +#ifdef EX_DATAERR + if (ins(d, "EX_DATAERR", (long)EX_DATAERR)) return -1; +#endif /* EX_DATAERR */ +#ifdef EX_NOINPUT + if (ins(d, "EX_NOINPUT", (long)EX_NOINPUT)) return -1; +#endif /* EX_NOINPUT */ +#ifdef EX_NOUSER + if (ins(d, "EX_NOUSER", (long)EX_NOUSER)) return -1; +#endif /* EX_NOUSER */ +#ifdef EX_NOHOST + if (ins(d, "EX_NOHOST", (long)EX_NOHOST)) return -1; +#endif /* EX_NOHOST */ +#ifdef EX_UNAVAILABLE + if (ins(d, "EX_UNAVAILABLE", (long)EX_UNAVAILABLE)) return -1; +#endif /* EX_UNAVAILABLE */ +#ifdef EX_SOFTWARE + if (ins(d, "EX_SOFTWARE", (long)EX_SOFTWARE)) return -1; +#endif /* EX_SOFTWARE */ +#ifdef EX_OSERR + if (ins(d, "EX_OSERR", (long)EX_OSERR)) return -1; +#endif /* EX_OSERR */ +#ifdef EX_OSFILE + if (ins(d, "EX_OSFILE", (long)EX_OSFILE)) return -1; +#endif /* EX_OSFILE */ +#ifdef EX_CANTCREAT + if (ins(d, "EX_CANTCREAT", (long)EX_CANTCREAT)) return -1; +#endif /* EX_CANTCREAT */ +#ifdef EX_IOERR + if (ins(d, "EX_IOERR", (long)EX_IOERR)) return -1; +#endif /* EX_IOERR */ +#ifdef EX_TEMPFAIL + if (ins(d, "EX_TEMPFAIL", (long)EX_TEMPFAIL)) return -1; +#endif /* EX_TEMPFAIL */ +#ifdef EX_PROTOCOL + if (ins(d, "EX_PROTOCOL", (long)EX_PROTOCOL)) return -1; +#endif /* EX_PROTOCOL */ +#ifdef EX_NOPERM + if (ins(d, "EX_NOPERM", (long)EX_NOPERM)) return -1; +#endif /* EX_NOPERM */ +#ifdef EX_CONFIG + if (ins(d, "EX_CONFIG", (long)EX_CONFIG)) return -1; +#endif /* EX_CONFIG */ +#ifdef EX_NOTFOUND + if (ins(d, "EX_NOTFOUND", (long)EX_NOTFOUND)) return -1; +#endif /* EX_NOTFOUND */ + +#ifdef HAVE_SPAWNV +#if defined(PYOS_OS2) && defined(PYCC_GCC) + if (ins(d, "P_WAIT", (long)P_WAIT)) return -1; + if (ins(d, "P_NOWAIT", (long)P_NOWAIT)) return -1; + if (ins(d, "P_OVERLAY", (long)P_OVERLAY)) return -1; + if (ins(d, "P_DEBUG", (long)P_DEBUG)) return -1; + if (ins(d, "P_SESSION", (long)P_SESSION)) return -1; + if (ins(d, "P_DETACH", (long)P_DETACH)) return -1; + if (ins(d, "P_PM", (long)P_PM)) return -1; + if (ins(d, "P_DEFAULT", (long)P_DEFAULT)) return -1; + if (ins(d, "P_MINIMIZE", (long)P_MINIMIZE)) return -1; + if (ins(d, "P_MAXIMIZE", (long)P_MAXIMIZE)) return -1; + if (ins(d, "P_FULLSCREEN", (long)P_FULLSCREEN)) return -1; + if (ins(d, "P_WINDOWED", (long)P_WINDOWED)) return -1; + if (ins(d, "P_FOREGROUND", (long)P_FOREGROUND)) return -1; + if (ins(d, "P_BACKGROUND", (long)P_BACKGROUND)) return -1; + if (ins(d, "P_NOCLOSE", (long)P_NOCLOSE)) return -1; + if (ins(d, "P_NOSESSION", (long)P_NOSESSION)) return -1; + if (ins(d, "P_QUOTE", (long)P_QUOTE)) return -1; + if (ins(d, "P_TILDE", (long)P_TILDE)) return -1; + if (ins(d, "P_UNRELATED", (long)P_UNRELATED)) return -1; + if (ins(d, "P_DEBUGDESC", (long)P_DEBUGDESC)) return -1; +#else + if (ins(d, "P_WAIT", (long)_P_WAIT)) return -1; + if (ins(d, "P_NOWAIT", (long)_P_NOWAIT)) return -1; + if (ins(d, "P_OVERLAY", (long)_OLD_P_OVERLAY)) return -1; + if (ins(d, "P_NOWAITO", (long)_P_NOWAITO)) return -1; + if (ins(d, "P_DETACH", (long)_P_DETACH)) return -1; +#endif +#endif + +#if defined(PYOS_OS2) + if (insertvalues(d)) return -1; +#endif + return 0; +} + + +#if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__)) && !defined(__QNX__) +#define INITFUNC initnt +#define MODNAME "nt" + +#elif defined(PYOS_OS2) +#define INITFUNC initos2 +#define MODNAME "os2" + +#else +#define INITFUNC initposix +#define MODNAME "posix" +#endif + +PyMODINIT_FUNC +INITFUNC(void) +{ + PyObject *m, *v; + + m = Py_InitModule3(MODNAME, + posix_methods, + posix__doc__); + if (m == NULL) + return; + + /* Initialize environ dictionary */ + v = convertenviron(); + Py_XINCREF(v); + if (v == NULL || PyModule_AddObject(m, "environ", v) != 0) + return; + Py_DECREF(v); + + if (all_ins(m)) + return; + + if (setup_confname_tables(m)) + return; + + Py_INCREF(PyExc_OSError); + PyModule_AddObject(m, "error", PyExc_OSError); + +#ifdef HAVE_PUTENV + if (posix_putenv_garbage == NULL) + posix_putenv_garbage = PyDict_New(); +#endif + + stat_result_desc.name = MODNAME ".stat_result"; + stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; + stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; + stat_result_desc.fields[9].name = PyStructSequence_UnnamedField; + PyStructSequence_InitType(&StatResultType, &stat_result_desc); + structseq_new = StatResultType.tp_new; + StatResultType.tp_new = statresult_new; + Py_INCREF((PyObject*) &StatResultType); + PyModule_AddObject(m, "stat_result", (PyObject*) &StatResultType); + + statvfs_result_desc.name = MODNAME ".statvfs_result"; + PyStructSequence_InitType(&StatVFSResultType, &statvfs_result_desc); + Py_INCREF((PyObject*) &StatVFSResultType); + PyModule_AddObject(m, "statvfs_result", + (PyObject*) &StatVFSResultType); +} diff --git a/src/main/resource/testFiles/cpython/revFiles/671582_f8e322_PC#_msi.c b/src/main/resource/testFiles/cpython/revFiles/671582_f8e322_PC#_msi.c new file mode 100644 index 0000000..6a99b40 --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/671582_f8e322_PC#_msi.c @@ -0,0 +1,1073 @@ +/* Helper library for MSI creation with Python. + * Copyright (C) 2005 Martin v. Löwis + * Licensed to PSF under a contributor agreement. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static PyObject *MSIError; + +static PyObject* +uuidcreate(PyObject* obj, PyObject*args) +{ + UUID result; + wchar_t *cresult; + PyObject *oresult; + + /* May return ok, local only, and no address. + For local only, the documentation says we still get a uuid. + For RPC_S_UUID_NO_ADDRESS, it's not clear whether we can + use the result. */ + if (UuidCreate(&result) == RPC_S_UUID_NO_ADDRESS) { + PyErr_SetString(PyExc_NotImplementedError, "processing 'no address' result"); + return NULL; + } + + if (UuidToStringW(&result, &cresult) == RPC_S_OUT_OF_MEMORY) { + PyErr_SetString(PyExc_MemoryError, "out of memory in uuidgen"); + return NULL; + } + + oresult = PyUnicode_FromWideChar(cresult, wcslen(cresult)); + RpcStringFreeW(&cresult); + return oresult; + +} + +/* FCI callback functions */ + +static FNFCIALLOC(cb_alloc) +{ + return malloc(cb); +} + +static FNFCIFREE(cb_free) +{ + free(memory); +} + +static FNFCIOPEN(cb_open) +{ + int result = _open(pszFile, oflag | O_NOINHERIT, pmode); + if (result == -1) + *err = errno; + return result; +} + +static FNFCIREAD(cb_read) +{ + UINT result = (UINT)_read((int)hf, memory, cb); + if (result != cb) + *err = errno; + return result; +} + +static FNFCIWRITE(cb_write) +{ + UINT result = (UINT)_write((int)hf, memory, cb); + if (result != cb) + *err = errno; + return result; +} + +static FNFCICLOSE(cb_close) +{ + int result = _close((int)hf); + if (result != 0) + *err = errno; + return result; +} + +static FNFCISEEK(cb_seek) +{ + long result = (long)_lseek((int)hf, dist, seektype); + if (result == -1) + *err = errno; + return result; +} + +static FNFCIDELETE(cb_delete) +{ + int result = remove(pszFile); + if (result != 0) + *err = errno; + return result; +} + +static FNFCIFILEPLACED(cb_fileplaced) +{ + return 0; +} + +static FNFCIGETTEMPFILE(cb_gettempfile) +{ + char *name = _tempnam("", "tmp"); + if ((name != NULL) && ((int)strlen(name) < cbTempName)) { + strcpy(pszTempName, name); + free(name); + return TRUE; + } + + if (name) free(name); + return FALSE; +} + +static FNFCISTATUS(cb_status) +{ + if (pv) { + _Py_IDENTIFIER(status); + + PyObject *result = _PyObject_CallMethodId(pv, &PyId_status, "iii", typeStatus, cb1, cb2); + if (result == NULL) + return -1; + Py_DECREF(result); + } + return 0; +} + +static FNFCIGETNEXTCABINET(cb_getnextcabinet) +{ + if (pv) { + _Py_IDENTIFIER(getnextcabinet); + + PyObject *result = _PyObject_CallMethodId(pv, &PyId_getnextcabinet, "i", pccab->iCab); + if (result == NULL) + return -1; + if (!PyBytes_Check(result)) { + PyErr_Format(PyExc_TypeError, + "Incorrect return type %s from getnextcabinet", + result->ob_type->tp_name); + Py_DECREF(result); + return FALSE; + } + strncpy(pccab->szCab, PyBytes_AsString(result), sizeof(pccab->szCab)); + return TRUE; + } + return FALSE; +} + +static FNFCIGETOPENINFO(cb_getopeninfo) +{ + BY_HANDLE_FILE_INFORMATION bhfi; + FILETIME filetime; + HANDLE handle; + + /* Need Win32 handle to get time stamps */ + handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) + return -1; + + if (GetFileInformationByHandle(handle, &bhfi) == FALSE) + { + CloseHandle(handle); + return -1; + } + + FileTimeToLocalFileTime(&bhfi.ftLastWriteTime, &filetime); + FileTimeToDosDateTime(&filetime, pdate, ptime); + + *pattribs = (int)(bhfi.dwFileAttributes & + (_A_RDONLY | _A_SYSTEM | _A_HIDDEN | _A_ARCH)); + + CloseHandle(handle); + + return _open(pszName, _O_RDONLY | _O_BINARY | O_NOINHERIT); +} + +static PyObject* fcicreate(PyObject* obj, PyObject* args) +{ + char *cabname, *p; + PyObject *files; + CCAB ccab; + HFCI hfci; + ERF erf; + Py_ssize_t i; + + + if (!PyArg_ParseTuple(args, "sO:FCICreate", &cabname, &files)) + return NULL; + + if (!PyList_Check(files)) { + PyErr_SetString(PyExc_TypeError, "FCICreate expects a list"); + return NULL; + } + + ccab.cb = INT_MAX; /* no need to split CAB into multiple media */ + ccab.cbFolderThresh = 1000000; /* flush directory after this many bytes */ + ccab.cbReserveCFData = 0; + ccab.cbReserveCFFolder = 0; + ccab.cbReserveCFHeader = 0; + + ccab.iCab = 1; + ccab.iDisk = 1; + + ccab.setID = 0; + ccab.szDisk[0] = '\0'; + + for (i = 0, p = cabname; *p; p = CharNext(p)) + if (*p == '\\' || *p == '/') + i = p - cabname + 1; + + if (i >= sizeof(ccab.szCabPath) || + strlen(cabname+i) >= sizeof(ccab.szCab)) { + PyErr_SetString(PyExc_ValueError, "path name too long"); + return 0; + } + + if (i > 0) { + memcpy(ccab.szCabPath, cabname, i); + ccab.szCabPath[i] = '\0'; + strcpy(ccab.szCab, cabname+i); + } else { + strcpy(ccab.szCabPath, ".\\"); + strcpy(ccab.szCab, cabname); + } + + hfci = FCICreate(&erf, cb_fileplaced, cb_alloc, cb_free, + cb_open, cb_read, cb_write, cb_close, cb_seek, cb_delete, + cb_gettempfile, &ccab, NULL); + + if (hfci == NULL) { + PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper); + return NULL; + } + + for (i=0; i < PyList_GET_SIZE(files); i++) { + PyObject *item = PyList_GET_ITEM(files, i); + char *filename, *cabname; + if (!PyArg_ParseTuple(item, "ss", &filename, &cabname)) + goto err; + if (!FCIAddFile(hfci, filename, cabname, FALSE, + cb_getnextcabinet, cb_status, cb_getopeninfo, + tcompTYPE_MSZIP)) + goto err; + } + + if (!FCIFlushCabinet(hfci, FALSE, cb_getnextcabinet, cb_status)) + goto err; + + if (!FCIDestroy(hfci)) + goto err; + + Py_INCREF(Py_None); + return Py_None; +err: + PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper); /* XXX better error type */ + FCIDestroy(hfci); + return NULL; +} + +typedef struct msiobj{ + PyObject_HEAD + MSIHANDLE h; +}msiobj; + +static void +msiobj_dealloc(msiobj* msidb) +{ + MsiCloseHandle(msidb->h); + msidb->h = 0; +} + +static PyObject* +msiobj_close(msiobj* msidb, PyObject *args) +{ + MsiCloseHandle(msidb->h); + msidb->h = 0; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +msierror(int status) +{ + int code; + char buf[2000]; + char *res = buf; + DWORD size = sizeof(buf); + MSIHANDLE err = MsiGetLastErrorRecord(); + + if (err == 0) { + switch(status) { + case ERROR_ACCESS_DENIED: + PyErr_SetString(MSIError, "access denied"); + return NULL; + case ERROR_FUNCTION_FAILED: + PyErr_SetString(MSIError, "function failed"); + return NULL; + case ERROR_INVALID_DATA: + PyErr_SetString(MSIError, "invalid data"); + return NULL; + case ERROR_INVALID_HANDLE: + PyErr_SetString(MSIError, "invalid handle"); + return NULL; + case ERROR_INVALID_STATE: + PyErr_SetString(MSIError, "invalid state"); + return NULL; + case ERROR_INVALID_PARAMETER: + PyErr_SetString(MSIError, "invalid parameter"); + return NULL; + default: + PyErr_Format(MSIError, "unknown error %x", status); + return NULL; + } + } + + code = MsiRecordGetInteger(err, 1); /* XXX code */ + if (MsiFormatRecord(0, err, res, &size) == ERROR_MORE_DATA) { + res = malloc(size+1); + MsiFormatRecord(0, err, res, &size); + res[size]='\0'; + } + MsiCloseHandle(err); + PyErr_SetString(MSIError, res); + if (res != buf) + free(res); + return NULL; +} + +/*************************** Record objects **********************/ + +static PyObject* +record_getfieldcount(msiobj* record, PyObject* args) +{ + return PyLong_FromLong(MsiRecordGetFieldCount(record->h)); +} + +static PyObject* +record_getinteger(msiobj* record, PyObject* args) +{ + unsigned int field; + int status; + + if (!PyArg_ParseTuple(args, "I:GetInteger", &field)) + return NULL; + status = MsiRecordGetInteger(record->h, field); + if (status == MSI_NULL_INTEGER){ + PyErr_SetString(MSIError, "could not convert record field to integer"); + return NULL; + } + return PyLong_FromLong((long) status); +} + +static PyObject* +record_getstring(msiobj* record, PyObject* args) +{ + unsigned int field; + unsigned int status; + WCHAR buf[2000]; + WCHAR *res = buf; + DWORD size = sizeof(buf); + PyObject* string; + + if (!PyArg_ParseTuple(args, "I:GetString", &field)) + return NULL; + status = MsiRecordGetStringW(record->h, field, res, &size); + if (status == ERROR_MORE_DATA) { + res = (WCHAR*) malloc((size + 1)*sizeof(WCHAR)); + if (res == NULL) + return PyErr_NoMemory(); + status = MsiRecordGetStringW(record->h, field, res, &size); + } + if (status != ERROR_SUCCESS) + return msierror((int) status); + string = PyUnicode_FromWideChar(res, size); + if (buf != res) + free(res); + return string; +} + +static PyObject* +record_cleardata(msiobj* record, PyObject *args) +{ + int status = MsiRecordClearData(record->h); + if (status != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +record_setstring(msiobj* record, PyObject *args) +{ + int status; + int field; + wchar_t *data; + + if (!PyArg_ParseTuple(args, "iu:SetString", &field, &data)) + return NULL; + + if ((status = MsiRecordSetStringW(record->h, field, data)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +record_setstream(msiobj* record, PyObject *args) +{ + int status; + int field; + wchar_t *data; + + if (!PyArg_ParseTuple(args, "iu:SetStream", &field, &data)) + return NULL; + + if ((status = MsiRecordSetStreamW(record->h, field, data)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +record_setinteger(msiobj* record, PyObject *args) +{ + int status; + int field; + int data; + + if (!PyArg_ParseTuple(args, "ii:SetInteger", &field, &data)) + return NULL; + + if ((status = MsiRecordSetInteger(record->h, field, data)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + + + +static PyMethodDef record_methods[] = { + { "GetFieldCount", (PyCFunction)record_getfieldcount, METH_NOARGS, + PyDoc_STR("GetFieldCount() -> int\nWraps MsiRecordGetFieldCount")}, + { "GetInteger", (PyCFunction)record_getinteger, METH_VARARGS, + PyDoc_STR("GetInteger(field) -> int\nWraps MsiRecordGetInteger")}, + { "GetString", (PyCFunction)record_getstring, METH_VARARGS, + PyDoc_STR("GetString(field) -> string\nWraps MsiRecordGetString")}, + { "SetString", (PyCFunction)record_setstring, METH_VARARGS, + PyDoc_STR("SetString(field,str) -> None\nWraps MsiRecordSetString")}, + { "SetStream", (PyCFunction)record_setstream, METH_VARARGS, + PyDoc_STR("SetStream(field,filename) -> None\nWraps MsiRecordSetInteger")}, + { "SetInteger", (PyCFunction)record_setinteger, METH_VARARGS, + PyDoc_STR("SetInteger(field,int) -> None\nWraps MsiRecordSetInteger")}, + { "ClearData", (PyCFunction)record_cleardata, METH_NOARGS, + PyDoc_STR("ClearData() -> int\nWraps MsiRecordGClearData")}, + { NULL, NULL } +}; + +static PyTypeObject record_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_msi.Record", /*tp_name*/ + sizeof(msiobj), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)msiobj_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + record_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +static PyObject* +record_new(MSIHANDLE h) +{ + msiobj *result = PyObject_NEW(struct msiobj, &record_Type); + + if (!result) { + MsiCloseHandle(h); + return NULL; + } + + result->h = h; + return (PyObject*)result; +} + +/*************************** SummaryInformation objects **************/ + +static PyObject* +summary_getproperty(msiobj* si, PyObject *args) +{ + int status; + int field; + PyObject *result; + UINT type; + INT ival; + FILETIME fval; + char sbuf[1000]; + char *sval = sbuf; + DWORD ssize = sizeof(sval); + + if (!PyArg_ParseTuple(args, "i:GetProperty", &field)) + return NULL; + + status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival, + &fval, sval, &ssize); + if (status == ERROR_MORE_DATA) { + sval = malloc(ssize); + status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival, + &fval, sval, &ssize); + } + + switch(type) { + case VT_I2: case VT_I4: + return PyLong_FromLong(ival); + case VT_FILETIME: + PyErr_SetString(PyExc_NotImplementedError, "FILETIME result"); + return NULL; + case VT_LPSTR: + result = PyBytes_FromStringAndSize(sval, ssize); + if (sval != sbuf) + free(sval); + return result; + } + PyErr_Format(PyExc_NotImplementedError, "result of type %d", type); + return NULL; +} + +static PyObject* +summary_getpropertycount(msiobj* si, PyObject *args) +{ + int status; + UINT result; + + status = MsiSummaryInfoGetPropertyCount(si->h, &result); + if (status != ERROR_SUCCESS) + return msierror(status); + + return PyLong_FromLong(result); +} + +static PyObject* +summary_setproperty(msiobj* si, PyObject *args) +{ + int status; + int field; + PyObject* data; + + if (!PyArg_ParseTuple(args, "iO:SetProperty", &field, &data)) + return NULL; + + if (PyUnicode_Check(data)) { + status = MsiSummaryInfoSetPropertyW(si->h, field, VT_LPSTR, + 0, NULL, PyUnicode_AsUnicode(data)); + } else if (PyLong_CheckExact(data)) { + long value = PyLong_AsLong(data); + if (value == -1 && PyErr_Occurred()) { + return NULL; + } + status = MsiSummaryInfoSetProperty(si->h, field, VT_I4, + value, NULL, NULL); + } else { + PyErr_SetString(PyExc_TypeError, "unsupported type"); + return NULL; + } + + if (status != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject* +summary_persist(msiobj* si, PyObject *args) +{ + int status; + + status = MsiSummaryInfoPersist(si->h); + if (status != ERROR_SUCCESS) + return msierror(status); + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef summary_methods[] = { + { "GetProperty", (PyCFunction)summary_getproperty, METH_VARARGS, + PyDoc_STR("GetProperty(propid) -> value\nWraps MsiSummaryInfoGetProperty")}, + { "GetPropertyCount", (PyCFunction)summary_getpropertycount, METH_NOARGS, + PyDoc_STR("GetProperty() -> int\nWraps MsiSummaryInfoGetPropertyCount")}, + { "SetProperty", (PyCFunction)summary_setproperty, METH_VARARGS, + PyDoc_STR("SetProperty(value) -> None\nWraps MsiSummaryInfoProperty")}, + { "Persist", (PyCFunction)summary_persist, METH_NOARGS, + PyDoc_STR("Persist() -> None\nWraps MsiSummaryInfoPersist")}, + { NULL, NULL } +}; + +static PyTypeObject summary_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_msi.SummaryInformation", /*tp_name*/ + sizeof(msiobj), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)msiobj_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + summary_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +/*************************** View objects **************/ + +static PyObject* +view_execute(msiobj *view, PyObject*args) +{ + int status; + MSIHANDLE params = 0; + PyObject *oparams = Py_None; + + if (!PyArg_ParseTuple(args, "O:Execute", &oparams)) + return NULL; + + if (oparams != Py_None) { + if (oparams->ob_type != &record_Type) { + PyErr_SetString(PyExc_TypeError, "Execute argument must be a record"); + return NULL; + } + params = ((msiobj*)oparams)->h; + } + + status = MsiViewExecute(view->h, params); + if (status != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +view_fetch(msiobj *view, PyObject*args) +{ + int status; + MSIHANDLE result; + + if ((status = MsiViewFetch(view->h, &result)) != ERROR_SUCCESS) + return msierror(status); + + return record_new(result); +} + +static PyObject* +view_getcolumninfo(msiobj *view, PyObject *args) +{ + int status; + int kind; + MSIHANDLE result; + + if (!PyArg_ParseTuple(args, "i:GetColumnInfo", &kind)) + return NULL; + + if ((status = MsiViewGetColumnInfo(view->h, kind, &result)) != ERROR_SUCCESS) + return msierror(status); + + return record_new(result); +} + +static PyObject* +view_modify(msiobj *view, PyObject *args) +{ + int kind; + PyObject *data; + int status; + + if (!PyArg_ParseTuple(args, "iO:Modify", &kind, &data)) + return NULL; + + if (data->ob_type != &record_Type) { + PyErr_SetString(PyExc_TypeError, "Modify expects a record object"); + return NULL; + } + + if ((status = MsiViewModify(view->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +view_close(msiobj *view, PyObject*args) +{ + int status; + + if ((status = MsiViewClose(view->h)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef view_methods[] = { + { "Execute", (PyCFunction)view_execute, METH_VARARGS, + PyDoc_STR("Execute(params=None) -> None\nWraps MsiViewExecute")}, + { "GetColumnInfo", (PyCFunction)view_getcolumninfo, METH_VARARGS, + PyDoc_STR("GetColumnInfo() -> result\nWraps MsiGetColumnInfo")}, + { "Fetch", (PyCFunction)view_fetch, METH_NOARGS, + PyDoc_STR("Fetch() -> result\nWraps MsiViewFetch")}, + { "Modify", (PyCFunction)view_modify, METH_VARARGS, + PyDoc_STR("Modify(mode,record) -> None\nWraps MsiViewModify")}, + { "Close", (PyCFunction)view_close, METH_NOARGS, + PyDoc_STR("Close() -> result\nWraps MsiViewClose")}, + { NULL, NULL } +}; + +static PyTypeObject msiview_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_msi.View", /*tp_name*/ + sizeof(msiobj), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)msiobj_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + view_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +/*************************** Database objects **************/ + +static PyObject* +msidb_openview(msiobj *msidb, PyObject *args) +{ + int status; + char *sql; + MSIHANDLE hView; + msiobj *result; + + if (!PyArg_ParseTuple(args, "s:OpenView", &sql)) + return NULL; + + if ((status = MsiDatabaseOpenView(msidb->h, sql, &hView)) != ERROR_SUCCESS) + return msierror(status); + + result = PyObject_NEW(struct msiobj, &msiview_Type); + if (!result) { + MsiCloseHandle(hView); + return NULL; + } + + result->h = hView; + return (PyObject*)result; +} + +static PyObject* +msidb_commit(msiobj *msidb, PyObject *args) +{ + int status; + + if ((status = MsiDatabaseCommit(msidb->h)) != ERROR_SUCCESS) + return msierror(status); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +msidb_getsummaryinformation(msiobj *db, PyObject *args) +{ + int status; + int count; + MSIHANDLE result; + msiobj *oresult; + + if (!PyArg_ParseTuple(args, "i:GetSummaryInformation", &count)) + return NULL; + + status = MsiGetSummaryInformation(db->h, NULL, count, &result); + if (status != ERROR_SUCCESS) + return msierror(status); + + oresult = PyObject_NEW(struct msiobj, &summary_Type); + if (!result) { + MsiCloseHandle(result); + return NULL; + } + + oresult->h = result; + return (PyObject*)oresult; +} + +static PyMethodDef db_methods[] = { + { "OpenView", (PyCFunction)msidb_openview, METH_VARARGS, + PyDoc_STR("OpenView(sql) -> viewobj\nWraps MsiDatabaseOpenView")}, + { "Commit", (PyCFunction)msidb_commit, METH_NOARGS, + PyDoc_STR("Commit() -> None\nWraps MsiDatabaseCommit")}, + { "GetSummaryInformation", (PyCFunction)msidb_getsummaryinformation, METH_VARARGS, + PyDoc_STR("GetSummaryInformation(updateCount) -> viewobj\nWraps MsiGetSummaryInformation")}, + { NULL, NULL } +}; + +static PyTypeObject msidb_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_msi.Database", /*tp_name*/ + sizeof(msiobj), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)msiobj_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + db_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +static PyObject* msiopendb(PyObject *obj, PyObject *args) +{ + int status; + char *path; + int persist; + MSIHANDLE h; + msiobj *result; + + if (!PyArg_ParseTuple(args, "si:MSIOpenDatabase", &path, &persist)) + return NULL; + + status = MsiOpenDatabase(path, (LPCSTR)persist, &h); + if (status != ERROR_SUCCESS) + return msierror(status); + + result = PyObject_NEW(struct msiobj, &msidb_Type); + if (!result) { + MsiCloseHandle(h); + return NULL; + } + result->h = h; + return (PyObject*)result; +} + +static PyObject* +createrecord(PyObject *o, PyObject *args) +{ + int count; + MSIHANDLE h; + + if (!PyArg_ParseTuple(args, "i:CreateRecord", &count)) + return NULL; + + h = MsiCreateRecord(count); + if (h == 0) + return msierror(0); + + return record_new(h); +} + + +static PyMethodDef msi_methods[] = { + {"UuidCreate", (PyCFunction)uuidcreate, METH_NOARGS, + PyDoc_STR("UuidCreate() -> string")}, + {"FCICreate", (PyCFunction)fcicreate, METH_VARARGS, + PyDoc_STR("fcicreate(cabname,files) -> None")}, + {"OpenDatabase", (PyCFunction)msiopendb, METH_VARARGS, + PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiOpenDatabase")}, + {"CreateRecord", (PyCFunction)createrecord, METH_VARARGS, + PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiCreateRecord")}, + {NULL, NULL} /* sentinel */ +}; + +static char msi_doc[] = "Documentation"; + + +static struct PyModuleDef _msimodule = { + PyModuleDef_HEAD_INIT, + "_msi", + msi_doc, + -1, + msi_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__msi(void) +{ + PyObject *m; + + m = PyModule_Create(&_msimodule); + if (m == NULL) + return NULL; + + PyModule_AddIntConstant(m, "MSIDBOPEN_CREATEDIRECT", (int)MSIDBOPEN_CREATEDIRECT); + PyModule_AddIntConstant(m, "MSIDBOPEN_CREATE", (int)MSIDBOPEN_CREATE); + PyModule_AddIntConstant(m, "MSIDBOPEN_DIRECT", (int)MSIDBOPEN_DIRECT); + PyModule_AddIntConstant(m, "MSIDBOPEN_READONLY", (int)MSIDBOPEN_READONLY); + PyModule_AddIntConstant(m, "MSIDBOPEN_TRANSACT", (int)MSIDBOPEN_TRANSACT); + PyModule_AddIntConstant(m, "MSIDBOPEN_PATCHFILE", (int)MSIDBOPEN_PATCHFILE); + + PyModule_AddIntMacro(m, MSICOLINFO_NAMES); + PyModule_AddIntMacro(m, MSICOLINFO_TYPES); + + PyModule_AddIntMacro(m, MSIMODIFY_SEEK); + PyModule_AddIntMacro(m, MSIMODIFY_REFRESH); + PyModule_AddIntMacro(m, MSIMODIFY_INSERT); + PyModule_AddIntMacro(m, MSIMODIFY_UPDATE); + PyModule_AddIntMacro(m, MSIMODIFY_ASSIGN); + PyModule_AddIntMacro(m, MSIMODIFY_REPLACE); + PyModule_AddIntMacro(m, MSIMODIFY_MERGE); + PyModule_AddIntMacro(m, MSIMODIFY_DELETE); + PyModule_AddIntMacro(m, MSIMODIFY_INSERT_TEMPORARY); + PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE); + PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_NEW); + PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_FIELD); + PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_DELETE); + + PyModule_AddIntMacro(m, PID_CODEPAGE); + PyModule_AddIntMacro(m, PID_TITLE); + PyModule_AddIntMacro(m, PID_SUBJECT); + PyModule_AddIntMacro(m, PID_AUTHOR); + PyModule_AddIntMacro(m, PID_KEYWORDS); + PyModule_AddIntMacro(m, PID_COMMENTS); + PyModule_AddIntMacro(m, PID_TEMPLATE); + PyModule_AddIntMacro(m, PID_LASTAUTHOR); + PyModule_AddIntMacro(m, PID_REVNUMBER); + PyModule_AddIntMacro(m, PID_LASTPRINTED); + PyModule_AddIntMacro(m, PID_CREATE_DTM); + PyModule_AddIntMacro(m, PID_LASTSAVE_DTM); + PyModule_AddIntMacro(m, PID_PAGECOUNT); + PyModule_AddIntMacro(m, PID_WORDCOUNT); + PyModule_AddIntMacro(m, PID_CHARCOUNT); + PyModule_AddIntMacro(m, PID_APPNAME); + PyModule_AddIntMacro(m, PID_SECURITY); + + MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL); + if (!MSIError) + return NULL; + PyModule_AddObject(m, "MSIError", MSIError); + return m; +} diff --git a/src/main/resource/testFiles/cpython/revFiles/719f5f_dcc6ef_Objects#intobject.c b/src/main/resource/testFiles/cpython/revFiles/719f5f_dcc6ef_Objects#intobject.c new file mode 100644 index 0000000..4c56bda --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/719f5f_dcc6ef_Objects#intobject.c @@ -0,0 +1,459 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Integer object implementation */ + +#include "allobjects.h" + +/* Standard Booleans */ + +intobject FalseObject = { + OB_HEAD_INIT(&Inttype) + 0 +}; + +intobject TrueObject = { + OB_HEAD_INIT(&Inttype) + 1 +}; + +static object * +err_ovf(msg) + char *msg; +{ + err_setstr(OverflowError, msg); + return NULL; +} + +/* Integers are quite normal objects, to make object handling uniform. + (Using odd pointers to represent integers would save much space + but require extra checks for this special case throughout the code.) + Since, a typical Python program spends much of its time allocating + and deallocating integers, these operations should be very fast. + Therefore we use a dedicated allocation scheme with a much lower + overhead (in space and time) than straight malloc(): a simple + dedicated free list, filled when necessary with memory from malloc(). +*/ + +#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */ +#define N_INTOBJECTS (BLOCK_SIZE / sizeof(intobject)) + +static intobject * +fill_free_list() +{ + intobject *p, *q; + p = NEW(intobject, N_INTOBJECTS); + if (p == NULL) + return (intobject *)err_nomem(); + q = p + N_INTOBJECTS; + while (--q > p) + *(intobject **)q = q-1; + *(intobject **)q = NULL; + return p + N_INTOBJECTS - 1; +} + +static intobject *free_list = NULL; + +object * +newintobject(ival) + long ival; +{ + register intobject *v; + if (free_list == NULL) { + if ((free_list = fill_free_list()) == NULL) + return NULL; + } + v = free_list; + free_list = *(intobject **)free_list; + NEWREF(v); + v->ob_type = &Inttype; + v->ob_ival = ival; + return (object *) v; +} + +static void +int_dealloc(v) + intobject *v; +{ + *(intobject **)v = free_list; + free_list = v; +} + +long +getintvalue(op) + register object *op; +{ + if (!is_intobject(op)) { + err_badcall(); + return -1; + } + else + return ((intobject *)op) -> ob_ival; +} + +/* Methods */ + +/* ARGSUSED */ +static int +int_print(v, fp, flags) + intobject *v; + FILE *fp; + int flags; /* Not used but required by interface */ +{ + fprintf(fp, "%ld", v->ob_ival); + return 0; +} + +static object * +int_repr(v) + intobject *v; +{ + char buf[20]; + sprintf(buf, "%ld", v->ob_ival); + return newstringobject(buf); +} + +static int +int_compare(v, w) + intobject *v, *w; +{ + register long i = v->ob_ival; + register long j = w->ob_ival; + return (i < j) ? -1 : (i > j) ? 1 : 0; +} + +static object * +int_add(v, w) + intobject *v; + intobject *w; +{ + register long a, b, x; + a = v->ob_ival; + b = w->ob_ival; + x = a + b; + if ((x^a) < 0 && (x^b) < 0) + return err_ovf("integer addition"); + return newintobject(x); +} + +static object * +int_sub(v, w) + intobject *v; + intobject *w; +{ + register long a, b, x; + a = v->ob_ival; + b = w->ob_ival; + x = a - b; + if ((x^a) < 0 && (x^~b) < 0) + return err_ovf("integer subtraction"); + return newintobject(x); +} + +static object * +int_mul(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + double x; + a = v->ob_ival; + b = w->ob_ival; + x = (double)a * (double)b; + if (x > 0x7fffffff || x < (double) (long) 0x80000000) + return err_ovf("integer multiplication"); + return newintobject(a * b); +} + +static int +i_divmod(x, y, p_xdivy, p_xmody) + register intobject *x, *y; + long *p_xdivy, *p_xmody; +{ + long xi = x->ob_ival; + long yi = y->ob_ival; + long xdivy, xmody; + + if (yi == 0) { + err_setstr(ZeroDivisionError, "integer division or modulo"); + return -1; + } + if (yi < 0) { + if (xi < 0) + xdivy = -xi / -yi; + else + xdivy = - (xi / -yi); + } + else { + if (xi < 0) + xdivy = - (-xi / yi); + else + xdivy = xi / yi; + } + xmody = xi - xdivy*yi; + if (xmody < 0 && yi > 0 || xmody > 0 && yi < 0) { + xmody += yi; + xdivy -= 1; + } + *p_xdivy = xdivy; + *p_xmody = xmody; + return 0; +} + +static object * +int_div(x, y) + intobject *x; + intobject *y; +{ + long d, m; + if (i_divmod(x, y, &d, &m) < 0) + return NULL; + return newintobject(d); +} + +static object * +int_mod(x, y) + intobject *x; + intobject *y; +{ + long d, m; + if (i_divmod(x, y, &d, &m) < 0) + return NULL; + return newintobject(m); +} + +static object * +int_divmod(x, y) + intobject *x; + intobject *y; +{ + object *v, *v0, *v1; + long d, m; + if (i_divmod(x, y, &d, &m) < 0) + return NULL; + v = newtupleobject(2); + v0 = newintobject(d); + v1 = newintobject(m); + if (v == NULL || v0 == NULL || v1 == NULL || + settupleitem(v, 0, v0) != 0 || + settupleitem(v, 1, v1) != 0) { + XDECREF(v); + XDECREF(v0); + XDECREF(v1); + v = NULL; + } + return v; +} + +static object * +int_pow(v, w) + intobject *v; + intobject *w; +{ + register long iv, iw, ix; + iv = v->ob_ival; + iw = w->ob_ival; + if (iw < 0) { + err_setstr(ValueError, "integer to the negative power"); + return NULL; + } + ix = 1; + while (--iw >= 0) { + long prev = ix; + ix = ix * iv; + if (iv == 0) + break; /* 0 to some power -- avoid ix / 0 */ + if (ix / iv != prev) + return err_ovf("integer pow()"); + } + return newintobject(ix); +} + +static object * +int_neg(v) + intobject *v; +{ + register long a, x; + a = v->ob_ival; + x = -a; + if (a < 0 && x < 0) + return err_ovf("integer negation"); + return newintobject(x); +} + +static object * +int_pos(v) + intobject *v; +{ + INCREF(v); + return (object *)v; +} + +static object * +int_abs(v) + intobject *v; +{ + if (v->ob_ival >= 0) + return int_pos(v); + else + return int_neg(v); +} + +static int +int_nonzero(v) + intobject *v; +{ + return v->ob_ival != 0; +} + +static object * +int_invert(v) + intobject *v; +{ + return newintobject(~v->ob_ival); +} + +static object * +int_lshift(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + a = v->ob_ival; + b = w->ob_ival; + if (b < 0) { + err_setstr(ValueError, "negative shift count"); + return NULL; + } + if (a == 0 || b == 0) { + INCREF(v); + return (object *) v; + } + if (b >= 32) { + return newintobject(0L); + } + a = (unsigned long)a << b; + return newintobject(a); +} + +static object * +int_rshift(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + a = v->ob_ival; + b = w->ob_ival; + if (b < 0) { + err_setstr(ValueError, "negative shift count"); + return NULL; + } + if (a == 0 || b == 0) { + INCREF(v); + return (object *) v; + } + if (b >= 32) { + if (a < 0) + a = -1; + else + a = 0; + } + else { + if (a < 0) + a = ~( ~(unsigned long)a >> b ); + else + a = (unsigned long)a >> b; + } + return newintobject(a); +} + +static object * +int_and(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + a = v->ob_ival; + b = w->ob_ival; + return newintobject(a & b); +} + +static object * +int_xor(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + a = v->ob_ival; + b = w->ob_ival; + return newintobject(a ^ b); +} + +static object * +int_or(v, w) + intobject *v; + intobject *w; +{ + register long a, b; + a = v->ob_ival; + b = w->ob_ival; + return newintobject(a | b); +} + +static number_methods int_as_number = { + int_add, /*nb_add*/ + int_sub, /*nb_subtract*/ + int_mul, /*nb_multiply*/ + int_div, /*nb_divide*/ + int_mod, /*nb_remainder*/ + int_divmod, /*nb_divmod*/ + int_pow, /*nb_power*/ + int_neg, /*nb_negative*/ + int_pos, /*nb_positive*/ + int_abs, /*nb_absolute*/ + int_nonzero, /*nb_nonzero*/ + int_invert, /*nb_invert*/ + int_lshift, /*nb_lshift*/ + int_rshift, /*nb_rshift*/ + int_and, /*nb_and*/ + int_xor, /*nb_xor*/ + int_or, /*nb_or*/ +}; + +typeobject Inttype = { + OB_HEAD_INIT(&Typetype) + 0, + "int", + sizeof(intobject), + 0, + int_dealloc, /*tp_dealloc*/ + int_print, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + int_compare, /*tp_compare*/ + int_repr, /*tp_repr*/ + &int_as_number, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ +}; diff --git a/src/main/resource/testFiles/cpython/revFiles/b110da_5abaa2_Modules#_cursesmodule.c b/src/main/resource/testFiles/cpython/revFiles/b110da_5abaa2_Modules#_cursesmodule.c new file mode 100644 index 0000000..5069285 --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/b110da_5abaa2_Modules#_cursesmodule.c @@ -0,0 +1,3486 @@ +/* + * This is a curses module for Python. + * + * Based on prior work by Lance Ellinghaus and Oliver Andrich + * Version 1.2 of this module: Copyright 1994 by Lance Ellinghouse, + * Cathedral City, California Republic, United States of America. + * + * Version 1.5b1, heavily extended for ncurses by Oliver Andrich: + * Copyright 1996,1997 by Oliver Andrich, Koblenz, Germany. + * + * Tidied for Python 1.6, and currently maintained by . + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this source file to use, copy, modify, merge, or publish it + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or in any new file that contains a substantial portion of + * this file. + * + * THE AUTHOR MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF + * THE SOFTWARE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT + * EXPRESS OR IMPLIED WARRANTY. THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE, STRICT LIABILITY OR + * ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + + A number of SysV or ncurses functions don't have wrappers yet; if you + need a given function, add it and send a patch. See + http://www.python.org/dev/patches/ for instructions on how to submit + patches to Python. + + Here's a list of currently unsupported functions: + + addchnstr addchstr color_set define_key + del_curterm delscreen dupwin inchnstr inchstr innstr keyok + mcprint mvaddchnstr mvaddchstr mvcur mvinchnstr + mvinchstr mvinnstr mmvwaddchnstr mvwaddchstr + mvwinchnstr mvwinchstr mvwinnstr newterm + restartterm ripoffline scr_dump + scr_init scr_restore scr_set scrl set_curterm set_term setterm + tgetent tgetflag tgetnum tgetstr tgoto timeout tputs + vidattr vidputs waddchnstr waddchstr + wcolor_set winchnstr winchstr winnstr wmouse_trafo wscrl + + Low-priority: + slk_attr slk_attr_off slk_attr_on slk_attr_set slk_attroff + slk_attron slk_attrset slk_clear slk_color slk_init slk_label + slk_noutrefresh slk_refresh slk_restore slk_set slk_touch + + Menu extension (ncurses and probably SYSV): + current_item free_item free_menu item_count item_description + item_index item_init item_name item_opts item_opts_off + item_opts_on item_term item_userptr item_value item_visible + menu_back menu_driver menu_fore menu_format menu_grey + menu_init menu_items menu_mark menu_opts menu_opts_off + menu_opts_on menu_pad menu_pattern menu_request_by_name + menu_request_name menu_spacing menu_sub menu_term menu_userptr + menu_win new_item new_menu pos_menu_cursor post_menu + scale_menu set_current_item set_item_init set_item_opts + set_item_term set_item_userptr set_item_value set_menu_back + set_menu_fore set_menu_format set_menu_grey set_menu_init + set_menu_items set_menu_mark set_menu_opts set_menu_pad + set_menu_pattern set_menu_spacing set_menu_sub set_menu_term + set_menu_userptr set_menu_win set_top_row top_row unpost_menu + + Form extension (ncurses and probably SYSV): + current_field data_ahead data_behind dup_field + dynamic_fieldinfo field_arg field_back field_buffer + field_count field_fore field_index field_info field_init + field_just field_opts field_opts_off field_opts_on field_pad + field_status field_term field_type field_userptr form_driver + form_fields form_init form_opts form_opts_off form_opts_on + form_page form_request_by_name form_request_name form_sub + form_term form_userptr form_win free_field free_form + link_field link_fieldtype move_field new_field new_form + new_page pos_form_cursor post_form scale_form + set_current_field set_field_back set_field_buffer + set_field_fore set_field_init set_field_just set_field_opts + set_field_pad set_field_status set_field_term set_field_type + set_field_userptr set_fieldtype_arg set_fieldtype_choice + set_form_fields set_form_init set_form_opts set_form_page + set_form_sub set_form_term set_form_userptr set_form_win + set_max_field set_new_page unpost_form + + +*/ + +/* Release Number */ + +char *PyCursesVersion = "2.2"; + +/* Includes */ + +#define PY_SSIZE_T_CLEAN + +#include "Python.h" + + +#ifdef __hpux +#define STRICT_SYSV_CURSES +#endif + +#define CURSES_MODULE +#include "py_curses.h" + +/* These prototypes are in , but including this header + #defines many common symbols (such as "lines") which breaks the + curses module in other ways. So the code will just specify + explicit prototypes here. */ +extern int setupterm(char *,int,int *); +#ifdef __sgi +#include +#endif + +#ifdef HAVE_LANGINFO_H +#include +#endif + +#if !defined(HAVE_NCURSES_H) && (defined(sgi) || defined(__sun) || defined(SCO5)) +#define STRICT_SYSV_CURSES /* Don't use ncurses extensions */ +typedef chtype attr_t; /* No attr_t type is available */ +#endif + +#if defined(_AIX) +#define STRICT_SYSV_CURSES +#endif + +/*[clinic input] +module curses +class curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=88c860abdbb50e0c]*/ + +#include "clinic/_cursesmodule.c.h" + +/* Definition of exception curses.error */ + +static PyObject *PyCursesError; + +/* Tells whether setupterm() has been called to initialise terminfo. */ +static int initialised_setupterm = FALSE; + +/* Tells whether initscr() has been called to initialise curses. */ +static int initialised = FALSE; + +/* Tells whether start_color() has been called to initialise color usage. */ +static int initialisedcolors = FALSE; + +static char *screen_encoding = NULL; + +/* Utility Macros */ +#define PyCursesSetupTermCalled \ + if (initialised_setupterm != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call (at least) setupterm() first"); \ + return 0; } + +#define PyCursesInitialised \ + if (initialised != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call initscr() first"); \ + return 0; } + +#define PyCursesInitialisedColor \ + if (initialisedcolors != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call start_color() first"); \ + return 0; } + +/* Utility Functions */ + +/* + * Check the return code from a curses function and return None + * or raise an exception as appropriate. These are exported using the + * capsule API. + */ + +static PyObject * +PyCursesCheckERR(int code, const char *fname) +{ + if (code != ERR) { + Py_INCREF(Py_None); + return Py_None; + } else { + if (fname == NULL) { + PyErr_SetString(PyCursesError, catchall_ERR); + } else { + PyErr_Format(PyCursesError, "%s() returned ERR", fname); + } + return NULL; + } +} + +/* Convert an object to a byte (an integer of type chtype): + + - int + - bytes of length 1 + - str of length 1 + + Return 1 on success, 0 on error (invalid type or integer overflow). */ +static int +PyCurses_ConvertToChtype(PyCursesWindowObject *win, PyObject *obj, chtype *ch) +{ + long value; + if(PyBytes_Check(obj) && PyBytes_Size(obj) == 1) { + value = (unsigned char)PyBytes_AsString(obj)[0]; + } + else if (PyUnicode_Check(obj)) { + if (PyUnicode_GetLength(obj) != 1) { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, " + "got a str of length %zi", + PyUnicode_GET_LENGTH(obj)); + return 0; + } + value = PyUnicode_READ_CHAR(obj, 0); + if (128 < value) { + PyObject *bytes; + const char *encoding; + if (win) + encoding = win->encoding; + else + encoding = screen_encoding; + bytes = PyUnicode_AsEncodedString(obj, encoding, NULL); + if (bytes == NULL) + return 0; + if (PyBytes_GET_SIZE(bytes) == 1) + value = (unsigned char)PyBytes_AS_STRING(bytes)[0]; + else + value = -1; + Py_DECREF(bytes); + if (value < 0) + goto overflow; + } + } + else if (PyLong_CheckExact(obj)) { + int long_overflow; + value = PyLong_AsLongAndOverflow(obj, &long_overflow); + if (long_overflow) + goto overflow; + } + else { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, got %s", + Py_TYPE(obj)->tp_name); + return 0; + } + *ch = (chtype)value; + if ((long)*ch != value) + goto overflow; + return 1; + +overflow: + PyErr_SetString(PyExc_OverflowError, + "byte doesn't fit in chtype"); + return 0; +} + +/* Convert an object to a byte (chtype) or a character (cchar_t): + + - int + - bytes of length 1 + - str of length 1 + + Return: + + - 2 if obj is a character (written into *wch) + - 1 if obj is a byte (written into *ch) + - 0 on error: raise an exception */ +static int +PyCurses_ConvertToCchar_t(PyCursesWindowObject *win, PyObject *obj, + chtype *ch +#ifdef HAVE_NCURSESW + , wchar_t *wch +#endif + ) +{ + long value; +#ifdef HAVE_NCURSESW + wchar_t buffer[2]; +#endif + + if (PyUnicode_Check(obj)) { +#ifdef HAVE_NCURSESW + if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, " + "got a str of length %zi", + PyUnicode_GET_LENGTH(obj)); + return 0; + } + *wch = buffer[0]; + return 2; +#else + return PyCurses_ConvertToChtype(win, obj, ch); +#endif + } + else if(PyBytes_Check(obj) && PyBytes_Size(obj) == 1) { + value = (unsigned char)PyBytes_AsString(obj)[0]; + } + else if (PyLong_CheckExact(obj)) { + int overflow; + value = PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow) { + PyErr_SetString(PyExc_OverflowError, + "int doesn't fit in long"); + return 0; + } + } + else { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, got %s", + Py_TYPE(obj)->tp_name); + return 0; + } + + *ch = (chtype)value; + if ((long)*ch != value) { + PyErr_Format(PyExc_OverflowError, + "byte doesn't fit in chtype"); + return 0; + } + return 1; +} + +/* Convert an object to a byte string (char*) or a wide character string + (wchar_t*). Return: + + - 2 if obj is a character string (written into *wch) + - 1 if obj is a byte string (written into *bytes) + - 0 on error: raise an exception */ +static int +PyCurses_ConvertToString(PyCursesWindowObject *win, PyObject *obj, + PyObject **bytes, wchar_t **wstr) +{ + if (PyUnicode_Check(obj)) { +#ifdef HAVE_NCURSESW + assert (wstr != NULL); + *wstr = PyUnicode_AsWideCharString(obj, NULL); + if (*wstr == NULL) + return 0; + return 2; +#else + assert (wstr == NULL); + *bytes = PyUnicode_AsEncodedString(obj, win->encoding, NULL); + if (*bytes == NULL) + return 0; + return 1; +#endif + } + else if (PyBytes_Check(obj)) { + Py_INCREF(obj); + *bytes = obj; + return 1; + } + + PyErr_Format(PyExc_TypeError, "expect bytes or str, got %s", + Py_TYPE(obj)->tp_name); + return 0; +} + +/* Function versions of the 3 functions for testing whether curses has been + initialised or not. */ + +static int func_PyCursesSetupTermCalled(void) +{ + PyCursesSetupTermCalled; + return 1; +} + +static int func_PyCursesInitialised(void) +{ + PyCursesInitialised; + return 1; +} + +static int func_PyCursesInitialisedColor(void) +{ + PyCursesInitialisedColor; + return 1; +} + +/***************************************************************************** + The Window Object +******************************************************************************/ + +/* Definition of the window type */ + +PyTypeObject PyCursesWindow_Type; + +/* Function prototype macros for Window object + + X - function name + TYPE - parameter Type + ERGSTR - format string for construction of the return value + PARSESTR - format string for argument parsing +*/ + +#define Window_NoArgNoReturnFunction(X) \ + static PyObject *PyCursesWindow_ ## X \ + (PyCursesWindowObject *self, PyObject *args) \ + { return PyCursesCheckERR(X(self->win), # X); } + +#define Window_NoArgTrueFalseFunction(X) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self) \ + { \ + if (X (self->win) == FALSE) { Py_INCREF(Py_False); return Py_False; } \ + else { Py_INCREF(Py_True); return Py_True; } } + +#define Window_NoArgNoReturnVoidFunction(X) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self) \ + { \ + X(self->win); Py_INCREF(Py_None); return Py_None; } + +#define Window_NoArg2TupleReturnFunction(X, TYPE, ERGSTR) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self) \ + { \ + TYPE arg1, arg2; \ + X(self->win,arg1,arg2); return Py_BuildValue(ERGSTR, arg1, arg2); } + +#define Window_OneArgNoReturnVoidFunction(X, TYPE, PARSESTR) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self, PyObject *args) \ + { \ + TYPE arg1; \ + if (!PyArg_ParseTuple(args, PARSESTR, &arg1)) return NULL; \ + X(self->win,arg1); Py_INCREF(Py_None); return Py_None; } + +#define Window_OneArgNoReturnFunction(X, TYPE, PARSESTR) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self, PyObject *args) \ + { \ + TYPE arg1; \ + if (!PyArg_ParseTuple(args,PARSESTR, &arg1)) return NULL; \ + return PyCursesCheckERR(X(self->win, arg1), # X); } + +#define Window_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \ + static PyObject * PyCursesWindow_ ## X \ + (PyCursesWindowObject *self, PyObject *args) \ + { \ + TYPE arg1, arg2; \ + if (!PyArg_ParseTuple(args,PARSESTR, &arg1, &arg2)) return NULL; \ + return PyCursesCheckERR(X(self->win, arg1, arg2), # X); } + +/* ------------- WINDOW routines --------------- */ + +Window_NoArgNoReturnFunction(untouchwin) +Window_NoArgNoReturnFunction(touchwin) +Window_NoArgNoReturnFunction(redrawwin) +Window_NoArgNoReturnFunction(winsertln) +Window_NoArgNoReturnFunction(werase) +Window_NoArgNoReturnFunction(wdeleteln) + +Window_NoArgTrueFalseFunction(is_wintouched) + +Window_NoArgNoReturnVoidFunction(wsyncup) +Window_NoArgNoReturnVoidFunction(wsyncdown) +Window_NoArgNoReturnVoidFunction(wstandend) +Window_NoArgNoReturnVoidFunction(wstandout) +Window_NoArgNoReturnVoidFunction(wcursyncup) +Window_NoArgNoReturnVoidFunction(wclrtoeol) +Window_NoArgNoReturnVoidFunction(wclrtobot) +Window_NoArgNoReturnVoidFunction(wclear) + +Window_OneArgNoReturnVoidFunction(idcok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnVoidFunction(immedok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnVoidFunction(wtimeout, int, "i;delay") + +Window_NoArg2TupleReturnFunction(getyx, int, "ii") +Window_NoArg2TupleReturnFunction(getbegyx, int, "ii") +Window_NoArg2TupleReturnFunction(getmaxyx, int, "ii") +Window_NoArg2TupleReturnFunction(getparyx, int, "ii") + +Window_OneArgNoReturnFunction(clearok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnFunction(idlok, int, "i;True(1) or False(0)") +#if defined(__NetBSD__) +Window_OneArgNoReturnVoidFunction(keypad, int, "i;True(1) or False(0)") +#else +Window_OneArgNoReturnFunction(keypad, int, "i;True(1) or False(0)") +#endif +Window_OneArgNoReturnFunction(leaveok, int, "i;True(1) or False(0)") +#if defined(__NetBSD__) +Window_OneArgNoReturnVoidFunction(nodelay, int, "i;True(1) or False(0)") +#else +Window_OneArgNoReturnFunction(nodelay, int, "i;True(1) or False(0)") +#endif +Window_OneArgNoReturnFunction(notimeout, int, "i;True(1) or False(0)") +Window_OneArgNoReturnFunction(scrollok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnFunction(winsdelln, int, "i;nlines") +Window_OneArgNoReturnFunction(syncok, int, "i;True(1) or False(0)") + +Window_TwoArgNoReturnFunction(mvwin, int, "ii;y,x") +Window_TwoArgNoReturnFunction(mvderwin, int, "ii;y,x") +Window_TwoArgNoReturnFunction(wmove, int, "ii;y,x") +#ifndef STRICT_SYSV_CURSES +Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns") +#endif + +/* Allocation and deallocation of Window Objects */ + +static PyObject * +PyCursesWindow_New(WINDOW *win, const char *encoding) +{ + PyCursesWindowObject *wo; + + if (encoding == NULL) { +#if defined(MS_WINDOWS) + char *buffer[100]; + UINT cp; + cp = GetConsoleOutputCP(); + if (cp != 0) { + PyOS_snprintf(buffer, sizeof(buffer), "cp%u", cp); + encoding = buffer; + } +#elif defined(CODESET) + const char *codeset = nl_langinfo(CODESET); + if (codeset != NULL && codeset[0] != 0) + encoding = codeset; +#endif + if (encoding == NULL) + encoding = "utf-8"; + } + + wo = PyObject_NEW(PyCursesWindowObject, &PyCursesWindow_Type); + if (wo == NULL) return NULL; + wo->win = win; + wo->encoding = _PyMem_Strdup(encoding); + if (wo->encoding == NULL) { + Py_DECREF(wo); + PyErr_NoMemory(); + return NULL; + } + return (PyObject *)wo; +} + +static void +PyCursesWindow_Dealloc(PyCursesWindowObject *wo) +{ + if (wo->win != stdscr) delwin(wo->win); + if (wo->encoding != NULL) + PyMem_Free(wo->encoding); + PyObject_DEL(wo); +} + +/* Addch, Addstr, Addnstr */ + +/*[clinic input] + +curses.window.addch + + [ + y: int + Y-coordinate. + x: int + X-coordinate. + ] + + ch: object + Character to add. + + [ + attr: long + Attributes for the character. + ] + / + +Paint character ch at (y, x) with attributes attr. + +Paint character ch at (y, x) with attributes attr, +overwriting any character previously painted at that location. +By default, the character position and attributes are the +current settings for the window object. +[clinic start generated code]*/ + +static PyObject * +curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1, int y, + int x, PyObject *ch, int group_right_1, long attr) +/*[clinic end generated code: output=99f7f85078ec06c3 input=5a41efb34a2de338]*/ +{ + PyCursesWindowObject *cwself = (PyCursesWindowObject *)self; + int coordinates_group = group_left_1; + int attr_group = group_right_1; + int rtn; + int type; + chtype cch = 0; +#ifdef HAVE_NCURSESW + wchar_t wstr[2]; + cchar_t wcval; +#endif + const char *funcname; + + if (!attr_group) + attr = A_NORMAL; + +#ifdef HAVE_NCURSESW + type = PyCurses_ConvertToCchar_t(cwself, ch, &cch, wstr); + if (type == 2) { + funcname = "add_wch"; + wstr[1] = L'\0'; + setcchar(&wcval, wstr, attr, 0, NULL); + if (coordinates_group) + rtn = mvwadd_wch(cwself->win,y,x, &wcval); + else { + rtn = wadd_wch(cwself->win, &wcval); + } + } + else +#else + type = PyCurses_ConvertToCchar_t(cwself, ch, &cch); +#endif + if (type == 1) { + funcname = "addch"; + if (coordinates_group) + rtn = mvwaddch(cwself->win,y,x, cch | attr); + else { + rtn = waddch(cwself->win, cch | attr); + } + } + else { + return NULL; + } + return PyCursesCheckERR(rtn, funcname); +} + +static PyObject * +PyCursesWindow_AddStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + long lattr; + int use_xy = FALSE, use_attr = FALSE; + const char *funcname; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"O;str", &strobj)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;str,attr", &strobj, &lattr)) + return NULL; + attr = lattr; + use_attr = TRUE; + break; + case 3: + if (!PyArg_ParseTuple(args,"iiO;int,int,str", &y, &x, &strobj)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOl;int,int,str,attr", &y, &x, &strobj, &lattr)) + return NULL; + attr = lattr; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "addstr requires 1 to 4 arguments"); + return NULL; + } +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + (void)wattrset(self->win,attr); + } +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "addwstr"; + if (use_xy == TRUE) + rtn = mvwaddwstr(self->win,y,x,wstr); + else + rtn = waddwstr(self->win,wstr); + PyMem_Free(wstr); + } + else +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "addstr"; + if (use_xy == TRUE) + rtn = mvwaddstr(self->win,y,x,str); + else + rtn = waddstr(self->win,str); + Py_DECREF(bytesobj); + } + if (use_attr == TRUE) + (void)wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, funcname); +} + +static PyObject * +PyCursesWindow_AddNStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn, x, y, n; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + long lattr; + int use_xy = FALSE, use_attr = FALSE; + const char *funcname; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"Oi;str,n", &strobj, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args,"Oil;str,n,attr", &strobj, &n, &lattr)) + return NULL; + attr = lattr; + use_attr = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOi;y,x,str,n", &y, &x, &strobj, &n)) + return NULL; + use_xy = TRUE; + break; + case 5: + if (!PyArg_ParseTuple(args,"iiOil;y,x,str,n,attr", &y, &x, &strobj, &n, &lattr)) + return NULL; + attr = lattr; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "addnstr requires 2 to 5 arguments"); + return NULL; + } +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; + + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + (void)wattrset(self->win,attr); + } +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "addnwstr"; + if (use_xy == TRUE) + rtn = mvwaddnwstr(self->win,y,x,wstr,n); + else + rtn = waddnwstr(self->win,wstr,n); + PyMem_Free(wstr); + } + else +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "addnstr"; + if (use_xy == TRUE) + rtn = mvwaddnstr(self->win,y,x,str,n); + else + rtn = waddnstr(self->win,str,n); + Py_DECREF(bytesobj); + } + if (use_attr == TRUE) + (void)wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, funcname); +} + +static PyObject * +PyCursesWindow_Bkgd(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype bkgd; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;ch or int,attr", &temp, &lattr)) + return NULL; + attr = lattr; + break; + default: + PyErr_SetString(PyExc_TypeError, "bkgd requires 1 or 2 arguments"); + return NULL; + } + + if (!PyCurses_ConvertToChtype(self, temp, &bkgd)) + return NULL; + + return PyCursesCheckERR(wbkgd(self->win, bkgd | attr), "bkgd"); +} + +static PyObject * +PyCursesWindow_AttrOff(PyCursesWindowObject *self, PyObject *args) +{ + long lattr; + if (!PyArg_ParseTuple(args,"l;attr", &lattr)) + return NULL; + return PyCursesCheckERR(wattroff(self->win, (attr_t)lattr), "attroff"); +} + +static PyObject * +PyCursesWindow_AttrOn(PyCursesWindowObject *self, PyObject *args) +{ + long lattr; + if (!PyArg_ParseTuple(args,"l;attr", &lattr)) + return NULL; + return PyCursesCheckERR(wattron(self->win, (attr_t)lattr), "attron"); +} + +static PyObject * +PyCursesWindow_AttrSet(PyCursesWindowObject *self, PyObject *args) +{ + long lattr; + if (!PyArg_ParseTuple(args,"l;attr", &lattr)) + return NULL; + return PyCursesCheckERR(wattrset(self->win, (attr_t)lattr), "attrset"); +} + +static PyObject * +PyCursesWindow_BkgdSet(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype bkgd; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;ch or int,attr", &temp, &lattr)) + return NULL; + attr = lattr; + break; + default: + PyErr_SetString(PyExc_TypeError, "bkgdset requires 1 or 2 arguments"); + return NULL; + } + + if (!PyCurses_ConvertToChtype(self, temp, &bkgd)) + return NULL; + + wbkgdset(self->win, bkgd | attr); + return PyCursesCheckERR(0, "bkgdset"); +} + +static PyObject * +PyCursesWindow_Border(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp[8]; + chtype ch[8]; + int i; + + /* Clear the array of parameters */ + for(i=0; i<8; i++) { + temp[i] = NULL; + ch[i] = 0; + } + + if (!PyArg_ParseTuple(args,"|OOOOOOOO;ls,rs,ts,bs,tl,tr,bl,br", + &temp[0], &temp[1], &temp[2], &temp[3], + &temp[4], &temp[5], &temp[6], &temp[7])) + return NULL; + + for(i=0; i<8; i++) { + if (temp[i] != NULL && !PyCurses_ConvertToChtype(self, temp[i], &ch[i])) + return NULL; + } + + wborder(self->win, + ch[0], ch[1], ch[2], ch[3], + ch[4], ch[5], ch[6], ch[7]); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCursesWindow_Box(PyCursesWindowObject *self, PyObject *args) +{ + chtype ch1=0,ch2=0; + switch(PyTuple_Size(args)){ + case 0: break; + default: + if (!PyArg_ParseTuple(args,"ll;vertint,horint", &ch1, &ch2)) + return NULL; + } + box(self->win,ch1,ch2); + Py_INCREF(Py_None); + return Py_None; +} + +#if defined(HAVE_NCURSES_H) || defined(MVWDELCH_IS_EXPRESSION) +#define py_mvwdelch mvwdelch +#else +int py_mvwdelch(WINDOW *w, int y, int x) +{ + mvwdelch(w,y,x); + /* On HP/UX, mvwdelch already returns. On other systems, + we may well run into this return statement. */ + return 0; +} +#endif + +/* chgat, added by Fabian Kreutz */ + +static PyObject * +PyCursesWindow_ChgAt(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + int num = -1; + short color; + attr_t attr = A_NORMAL; + long lattr; + int use_xy = FALSE; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"l;attr", &lattr)) + return NULL; + attr = lattr; + break; + case 2: + if (!PyArg_ParseTuple(args,"il;n,attr", &num, &lattr)) + return NULL; + attr = lattr; + break; + case 3: + if (!PyArg_ParseTuple(args,"iil;int,int,attr", &y, &x, &lattr)) + return NULL; + attr = lattr; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiil;int,int,n,attr", &y, &x, &num, &lattr)) + return NULL; + attr = lattr; + use_xy = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "chgat requires 1 to 4 arguments"); + return NULL; + } + + color = (short)((attr >> 8) & 0xff); + attr = attr - (color << 8); + + if (use_xy == TRUE) { + rtn = mvwchgat(self->win,y,x,num,attr,color,NULL); + touchline(self->win,y,1); + } else { + getyx(self->win,y,x); + rtn = wchgat(self->win,num,attr,color,NULL); + touchline(self->win,y,1); + } + return PyCursesCheckERR(rtn, "chgat"); +} + + +static PyObject * +PyCursesWindow_DelCh(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + + switch (PyTuple_Size(args)) { + case 0: + rtn = wdelch(self->win); + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x", &y, &x)) + return NULL; + rtn = py_mvwdelch(self->win,y,x); + break; + default: + PyErr_SetString(PyExc_TypeError, "delch requires 0 or 2 arguments"); + return NULL; + } + return PyCursesCheckERR(rtn, "[mv]wdelch"); +} + +static PyObject * +PyCursesWindow_DerWin(PyCursesWindowObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols, begin_y, begin_x; + + nlines = 0; + ncols = 0; + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;begin_y,begin_x",&begin_y,&begin_x)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiii;nlines,ncols,begin_y,begin_x", + &nlines,&ncols,&begin_y,&begin_x)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "derwin requires 2 or 4 arguments"); + return NULL; + } + + win = derwin(self->win,nlines,ncols,begin_y,begin_x); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win, NULL); +} + +static PyObject * +PyCursesWindow_EchoChar(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;ch or int,attr", &temp, &lattr)) + return NULL; + attr = lattr; + break; + default: + PyErr_SetString(PyExc_TypeError, "echochar requires 1 or 2 arguments"); + + + return NULL; + } + + if (!PyCurses_ConvertToChtype(self, temp, &ch)) + return NULL; + +#ifdef WINDOW_HAS_FLAGS + if (self->win->_flags & _ISPAD) + return PyCursesCheckERR(pechochar(self->win, ch | attr), + "echochar"); + else +#endif + return PyCursesCheckERR(wechochar(self->win, ch | attr), + "echochar"); +} + +#ifdef NCURSES_MOUSE_VERSION +static PyObject * +PyCursesWindow_Enclose(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + if (!PyArg_ParseTuple(args,"ii;y,x", &y, &x)) + return NULL; + + return PyLong_FromLong( wenclose(self->win,y,x) ); +} +#endif + +static PyObject * +PyCursesWindow_GetBkgd(PyCursesWindowObject *self) +{ + return PyLong_FromLong((long) getbkgd(self->win)); +} + +static PyObject * +PyCursesWindow_GetCh(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + int rtn; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + rtn = wgetch(self->win); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = mvwgetch(self->win,y,x); + Py_END_ALLOW_THREADS + break; + default: + PyErr_SetString(PyExc_TypeError, "getch requires 0 or 2 arguments"); + return NULL; + } + return PyLong_FromLong((long)rtn); +} + +static PyObject * +PyCursesWindow_GetKey(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + int rtn; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + rtn = wgetch(self->win); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = mvwgetch(self->win,y,x); + Py_END_ALLOW_THREADS + break; + default: + PyErr_SetString(PyExc_TypeError, "getkey requires 0 or 2 arguments"); + return NULL; + } + if (rtn == ERR) { + /* getch() returns ERR in nodelay mode */ + PyErr_CheckSignals(); + if (!PyErr_Occurred()) + PyErr_SetString(PyCursesError, "no input"); + return NULL; + } else if (rtn<=255) { + return Py_BuildValue("C", rtn); + } else { + const char *knp; +#if defined(__NetBSD__) + knp = unctrl(rtn); +#else + knp = keyname(rtn); +#endif + return PyUnicode_FromString((knp == NULL) ? "" : knp); + } +} + +#ifdef HAVE_NCURSESW +static PyObject * +PyCursesWindow_Get_WCh(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + int ct; + wint_t rtn; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + ct = wget_wch(self->win,&rtn); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS + ct = mvwget_wch(self->win,y,x,&rtn); + Py_END_ALLOW_THREADS + break; + default: + PyErr_SetString(PyExc_TypeError, "get_wch requires 0 or 2 arguments"); + return NULL; + } + if (ct == ERR) { + if (PyErr_CheckSignals()) + return NULL; + + /* get_wch() returns ERR in nodelay mode */ + PyErr_SetString(PyCursesError, "no input"); + return NULL; + } + if (ct == KEY_CODE_YES) + return PyLong_FromLong(rtn); + else + return PyUnicode_FromOrdinal(rtn); +} +#endif + +static PyObject * +PyCursesWindow_GetStr(PyCursesWindowObject *self, PyObject *args) +{ + int x, y, n; + char rtn[1024]; /* This should be big enough.. I hope */ + int rtn2; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + rtn2 = wgetnstr(self->win,rtn, 1023); + Py_END_ALLOW_THREADS + break; + case 1: + if (!PyArg_ParseTuple(args,"i;n", &n)) + return NULL; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + rtn2 = wgetnstr(self->win, rtn, Py_MIN(n, 1023)); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS +#ifdef STRICT_SYSV_CURSES + rtn2 = wmove(self->win,y,x)==ERR ? ERR : wgetnstr(self->win, rtn, 1023); +#else + rtn2 = mvwgetnstr(self->win,y,x,rtn, 1023); +#endif + Py_END_ALLOW_THREADS + break; + case 3: + if (!PyArg_ParseTuple(args,"iii;y,x,n", &y, &x, &n)) + return NULL; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); + return NULL; + } +#ifdef STRICT_SYSV_CURSES + Py_BEGIN_ALLOW_THREADS + rtn2 = wmove(self->win,y,x)==ERR ? ERR : + wgetnstr(self->win, rtn, Py_MIN(n, 1023)); + Py_END_ALLOW_THREADS +#else + Py_BEGIN_ALLOW_THREADS + rtn2 = mvwgetnstr(self->win, y, x, rtn, Py_MIN(n, 1023)); + Py_END_ALLOW_THREADS +#endif + break; + default: + PyErr_SetString(PyExc_TypeError, "getstr requires 0 to 3 arguments"); + return NULL; + } + if (rtn2 == ERR) + rtn[0] = 0; + return PyBytes_FromString(rtn); +} + +static PyObject * +PyCursesWindow_Hline(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + int n, x, y, code = OK; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args, "Oi;ch or int,n", &temp, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args, "Oil;ch or int,n,attr", &temp, &n, &lattr)) + return NULL; + attr = lattr; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiOi;y,x,ch or int,n", &y, &x, &temp, &n)) + return NULL; + code = wmove(self->win, y, x); + break; + case 5: + if (!PyArg_ParseTuple(args, "iiOil; y,x,ch or int,n,attr", + &y, &x, &temp, &n, &lattr)) + return NULL; + attr = lattr; + code = wmove(self->win, y, x); + break; + default: + PyErr_SetString(PyExc_TypeError, "hline requires 2 to 5 arguments"); + return NULL; + } + + if (code != ERR) { + if (!PyCurses_ConvertToChtype(self, temp, &ch)) + return NULL; + return PyCursesCheckERR(whline(self->win, ch | attr, n), "hline"); + } else + return PyCursesCheckERR(code, "wmove"); +} + +static PyObject * +PyCursesWindow_InsCh(PyCursesWindowObject *self, PyObject *args) +{ + int rtn, x, y, use_xy = FALSE; + PyObject *temp; + chtype ch = 0; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args, "Ol;ch or int,attr", &temp, &lattr)) + return NULL; + attr = lattr; + break; + case 3: + if (!PyArg_ParseTuple(args,"iiO;y,x,ch or int", &y, &x, &temp)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOl;y,x,ch or int, attr", &y, &x, &temp, &lattr)) + return NULL; + attr = lattr; + use_xy = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "insch requires 1 or 4 arguments"); + return NULL; + } + + if (!PyCurses_ConvertToChtype(self, temp, &ch)) + return NULL; + + if (use_xy == TRUE) + rtn = mvwinsch(self->win,y,x, ch | attr); + else { + rtn = winsch(self->win, ch | attr); + } + return PyCursesCheckERR(rtn, "insch"); +} + +static PyObject * +PyCursesWindow_InCh(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + unsigned long rtn; + + switch (PyTuple_Size(args)) { + case 0: + rtn = winch(self->win); + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + rtn = mvwinch(self->win,y,x); + break; + default: + PyErr_SetString(PyExc_TypeError, "inch requires 0 or 2 arguments"); + return NULL; + } + return PyLong_FromUnsignedLong(rtn); +} + +static PyObject * +PyCursesWindow_InStr(PyCursesWindowObject *self, PyObject *args) +{ + int x, y, n; + char rtn[1024]; /* This should be big enough.. I hope */ + int rtn2; + + switch (PyTuple_Size(args)) { + case 0: + rtn2 = winnstr(self->win,rtn, 1023); + break; + case 1: + if (!PyArg_ParseTuple(args,"i;n", &n)) + return NULL; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); + return NULL; + } + rtn2 = winnstr(self->win, rtn, Py_MIN(n, 1023)); + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + rtn2 = mvwinnstr(self->win,y,x,rtn,1023); + break; + case 3: + if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n)) + return NULL; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); + return NULL; + } + rtn2 = mvwinnstr(self->win, y, x, rtn, Py_MIN(n,1023)); + break; + default: + PyErr_SetString(PyExc_TypeError, "instr requires 0 or 3 arguments"); + return NULL; + } + if (rtn2 == ERR) + rtn[0] = 0; + return PyBytes_FromString(rtn); +} + +static PyObject * +PyCursesWindow_InsStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + long lattr; + int use_xy = FALSE, use_attr = FALSE; + const char *funcname; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"O;str", &strobj)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;str,attr", &strobj, &lattr)) + return NULL; + attr = lattr; + use_attr = TRUE; + break; + case 3: + if (!PyArg_ParseTuple(args,"iiO;y,x,str", &y, &x, &strobj)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOl;y,x,str,attr", &y, &x, &strobj, &lattr)) + return NULL; + attr = lattr; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "insstr requires 1 to 4 arguments"); + return NULL; + } + +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; + + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + (void)wattrset(self->win,attr); + } +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "inswstr"; + if (use_xy == TRUE) + rtn = mvwins_wstr(self->win,y,x,wstr); + else + rtn = wins_wstr(self->win,wstr); + PyMem_Free(wstr); + } + else +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "insstr"; + if (use_xy == TRUE) + rtn = mvwinsstr(self->win,y,x,str); + else + rtn = winsstr(self->win,str); + Py_DECREF(bytesobj); + } + if (use_attr == TRUE) + (void)wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, funcname); +} + +static PyObject * +PyCursesWindow_InsNStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn, x, y, n; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + long lattr; + int use_xy = FALSE, use_attr = FALSE; + const char *funcname; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"Oi;str,n", &strobj, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args,"Oil;str,n,attr", &strobj, &n, &lattr)) + return NULL; + attr = lattr; + use_attr = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOi;y,x,str,n", &y, &x, &strobj, &n)) + return NULL; + use_xy = TRUE; + break; + case 5: + if (!PyArg_ParseTuple(args,"iiOil;y,x,str,n,attr", &y, &x, &strobj, &n, &lattr)) + return NULL; + attr = lattr; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "insnstr requires 2 to 5 arguments"); + return NULL; + } + +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; + + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + (void)wattrset(self->win,attr); + } +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "insn_wstr"; + if (use_xy == TRUE) + rtn = mvwins_nwstr(self->win,y,x,wstr,n); + else + rtn = wins_nwstr(self->win,wstr,n); + PyMem_Free(wstr); + } + else +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "insnstr"; + if (use_xy == TRUE) + rtn = mvwinsnstr(self->win,y,x,str,n); + else + rtn = winsnstr(self->win,str,n); + Py_DECREF(bytesobj); + } + if (use_attr == TRUE) + (void)wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, funcname); +} + +static PyObject * +PyCursesWindow_Is_LineTouched(PyCursesWindowObject *self, PyObject *args) +{ + int line, erg; + if (!PyArg_ParseTuple(args,"i;line", &line)) + return NULL; + erg = is_linetouched(self->win, line); + if (erg == ERR) { + PyErr_SetString(PyExc_TypeError, + "is_linetouched: line number outside of boundaries"); + return NULL; + } else + if (erg == FALSE) { + Py_INCREF(Py_False); + return Py_False; + } else { + Py_INCREF(Py_True); + return Py_True; + } +} + +static PyObject * +PyCursesWindow_NoOutRefresh(PyCursesWindowObject *self, PyObject *args) +{ + int pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol; + int rtn; + +#ifndef WINDOW_HAS_FLAGS + if (0) +#else + if (self->win->_flags & _ISPAD) +#endif + { + switch(PyTuple_Size(args)) { + case 6: + if (!PyArg_ParseTuple(args, + "iiiiii;" \ + "pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol", + &pminrow, &pmincol, &sminrow, + &smincol, &smaxrow, &smaxcol)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = pnoutrefresh(self->win, + pminrow, pmincol, sminrow, + smincol, smaxrow, smaxcol); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "pnoutrefresh"); + default: + PyErr_SetString(PyCursesError, + "noutrefresh() called for a pad " + "requires 6 arguments"); + return NULL; + } + } else { + if (!PyArg_ParseTuple(args, ":noutrefresh")) + return NULL; + + Py_BEGIN_ALLOW_THREADS + rtn = wnoutrefresh(self->win); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "wnoutrefresh"); + } +} + +static PyObject * +PyCursesWindow_Overlay(PyCursesWindowObject *self, PyObject *args) +{ + PyCursesWindowObject *temp; + int use_copywin = FALSE; + int sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol; + int rtn; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O!;window object", + &PyCursesWindow_Type, &temp)) + return NULL; + break; + case 7: + if (!PyArg_ParseTuple(args, "O!iiiiii;window object, int, int, int, int, int, int", + &PyCursesWindow_Type, &temp, &sminrow, &smincol, + &dminrow, &dmincol, &dmaxrow, &dmaxcol)) + return NULL; + use_copywin = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, + "overlay requires one or seven arguments"); + return NULL; + } + + if (use_copywin == TRUE) { + rtn = copywin(self->win, temp->win, sminrow, smincol, + dminrow, dmincol, dmaxrow, dmaxcol, TRUE); + return PyCursesCheckERR(rtn, "copywin"); + } + else { + rtn = overlay(self->win, temp->win); + return PyCursesCheckERR(rtn, "overlay"); + } +} + +static PyObject * +PyCursesWindow_Overwrite(PyCursesWindowObject *self, PyObject *args) +{ + PyCursesWindowObject *temp; + int use_copywin = FALSE; + int sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol; + int rtn; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O!;window object", + &PyCursesWindow_Type, &temp)) + return NULL; + break; + case 7: + if (!PyArg_ParseTuple(args, "O!iiiiii;window object, int, int, int, int, int, int", + &PyCursesWindow_Type, &temp, &sminrow, &smincol, + &dminrow, &dmincol, &dmaxrow, &dmaxcol)) + return NULL; + use_copywin = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, + "overwrite requires one or seven arguments"); + return NULL; + } + + if (use_copywin == TRUE) { + rtn = copywin(self->win, temp->win, sminrow, smincol, + dminrow, dmincol, dmaxrow, dmaxcol, FALSE); + return PyCursesCheckERR(rtn, "copywin"); + } + else { + rtn = overwrite(self->win, temp->win); + return PyCursesCheckERR(rtn, "overwrite"); + } +} + +static PyObject * +PyCursesWindow_PutWin(PyCursesWindowObject *self, PyObject *stream) +{ + /* We have to simulate this by writing to a temporary FILE*, + then reading back, then writing to the argument stream. */ + char fn[100]; + int fd = -1; + FILE *fp = NULL; + PyObject *res = NULL; + + strcpy(fn, "/tmp/py.curses.putwin.XXXXXX"); + fd = mkstemp(fn); + if (fd < 0) + return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + if (_Py_set_inheritable(fd, 0, NULL) < 0) + goto exit; + fp = fdopen(fd, "wb+"); + if (fp == NULL) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + goto exit; + } + res = PyCursesCheckERR(putwin(self->win, fp), "putwin"); + if (res == NULL) + goto exit; + fseek(fp, 0, 0); + while (1) { + char buf[BUFSIZ]; + Py_ssize_t n = fread(buf, 1, BUFSIZ, fp); + _Py_IDENTIFIER(write); + + if (n <= 0) + break; + Py_DECREF(res); + res = _PyObject_CallMethodId(stream, &PyId_write, "y#", buf, n); + if (res == NULL) + break; + } + +exit: + if (fp != NULL) + fclose(fp); + else if (fd != -1) + close(fd); + remove(fn); + return res; +} + +static PyObject * +PyCursesWindow_RedrawLine(PyCursesWindowObject *self, PyObject *args) +{ + int beg, num; + if (!PyArg_ParseTuple(args, "ii;beg,num", &beg, &num)) + return NULL; + return PyCursesCheckERR(wredrawln(self->win,beg,num), "redrawln"); +} + +static PyObject * +PyCursesWindow_Refresh(PyCursesWindowObject *self, PyObject *args) +{ + int pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol; + int rtn; + +#ifndef WINDOW_HAS_FLAGS + if (0) +#else + if (self->win->_flags & _ISPAD) +#endif + { + switch(PyTuple_Size(args)) { + case 6: + if (!PyArg_ParseTuple(args, + "iiiiii;" \ + "pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol", + &pminrow, &pmincol, &sminrow, + &smincol, &smaxrow, &smaxcol)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + rtn = prefresh(self->win, + pminrow, pmincol, sminrow, + smincol, smaxrow, smaxcol); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "prefresh"); + default: + PyErr_SetString(PyCursesError, + "refresh() for a pad requires 6 arguments"); + return NULL; + } + } else { + if (!PyArg_ParseTuple(args, ":refresh")) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = wrefresh(self->win); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "prefresh"); + } +} + +static PyObject * +PyCursesWindow_SetScrollRegion(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + if (!PyArg_ParseTuple(args,"ii;top, bottom",&y,&x)) + return NULL; + return PyCursesCheckERR(wsetscrreg(self->win,y,x), "wsetscrreg"); +} + +static PyObject * +PyCursesWindow_SubWin(PyCursesWindowObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols, begin_y, begin_x; + + nlines = 0; + ncols = 0; + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;begin_y,begin_x",&begin_y,&begin_x)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiii;nlines,ncols,begin_y,begin_x", + &nlines,&ncols,&begin_y,&begin_x)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "subwin requires 2 or 4 arguments"); + return NULL; + } + + /* printf("Subwin: %i %i %i %i \n", nlines, ncols, begin_y, begin_x); */ +#ifdef WINDOW_HAS_FLAGS + if (self->win->_flags & _ISPAD) + win = subpad(self->win, nlines, ncols, begin_y, begin_x); + else +#endif + win = subwin(self->win, nlines, ncols, begin_y, begin_x); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win, self->encoding); +} + +static PyObject * +PyCursesWindow_Scroll(PyCursesWindowObject *self, PyObject *args) +{ + int nlines; + switch(PyTuple_Size(args)) { + case 0: + return PyCursesCheckERR(scroll(self->win), "scroll"); + case 1: + if (!PyArg_ParseTuple(args, "i;nlines", &nlines)) + return NULL; + return PyCursesCheckERR(wscrl(self->win, nlines), "scroll"); + default: + PyErr_SetString(PyExc_TypeError, "scroll requires 0 or 1 arguments"); + return NULL; + } +} + +static PyObject * +PyCursesWindow_TouchLine(PyCursesWindowObject *self, PyObject *args) +{ + int st, cnt, val; + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;start,count",&st,&cnt)) + return NULL; + return PyCursesCheckERR(touchline(self->win,st,cnt), "touchline"); + case 3: + if (!PyArg_ParseTuple(args, "iii;start,count,val", &st, &cnt, &val)) + return NULL; + return PyCursesCheckERR(wtouchln(self->win, st, cnt, val), "touchline"); + default: + PyErr_SetString(PyExc_TypeError, "touchline requires 2 or 3 arguments"); + return NULL; + } +} + +static PyObject * +PyCursesWindow_Vline(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + int n, x, y, code = OK; + attr_t attr = A_NORMAL; + long lattr; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args, "Oi;ch or int,n", &temp, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args, "Oil;ch or int,n,attr", &temp, &n, &lattr)) + return NULL; + attr = lattr; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiOi;y,x,ch or int,n", &y, &x, &temp, &n)) + return NULL; + code = wmove(self->win, y, x); + break; + case 5: + if (!PyArg_ParseTuple(args, "iiOil; y,x,ch or int,n,attr", + &y, &x, &temp, &n, &lattr)) + return NULL; + attr = lattr; + code = wmove(self->win, y, x); + break; + default: + PyErr_SetString(PyExc_TypeError, "vline requires 2 to 5 arguments"); + return NULL; + } + + if (code != ERR) { + if (!PyCurses_ConvertToChtype(self, temp, &ch)) + return NULL; + return PyCursesCheckERR(wvline(self->win, ch | attr, n), "vline"); + } else + return PyCursesCheckERR(code, "wmove"); +} + +static PyObject * +PyCursesWindow_get_encoding(PyCursesWindowObject *self, void *closure) +{ + return PyUnicode_FromString(self->encoding); +} + +static int +PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value) +{ + PyObject *ascii; + char *encoding; + + /* It is illegal to del win.encoding */ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "encoding may not be deleted"); + return -1; + } + + if (!PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "setting encoding to a non-string"); + return -1; + } + ascii = PyUnicode_AsASCIIString(value); + if (ascii == NULL) + return -1; + encoding = _PyMem_Strdup(PyBytes_AS_STRING(ascii)); + Py_DECREF(ascii); + if (encoding == NULL) { + PyErr_NoMemory(); + return -1; + } + PyMem_Free(self->encoding); + self->encoding = encoding; + return 0; +} + + +static PyMethodDef PyCursesWindow_Methods[] = { + CURSES_WINDOW_ADDCH_METHODDEF + {"addnstr", (PyCFunction)PyCursesWindow_AddNStr, METH_VARARGS}, + {"addstr", (PyCFunction)PyCursesWindow_AddStr, METH_VARARGS}, + {"attroff", (PyCFunction)PyCursesWindow_AttrOff, METH_VARARGS}, + {"attron", (PyCFunction)PyCursesWindow_AttrOn, METH_VARARGS}, + {"attrset", (PyCFunction)PyCursesWindow_AttrSet, METH_VARARGS}, + {"bkgd", (PyCFunction)PyCursesWindow_Bkgd, METH_VARARGS}, + {"chgat", (PyCFunction)PyCursesWindow_ChgAt, METH_VARARGS}, + {"bkgdset", (PyCFunction)PyCursesWindow_BkgdSet, METH_VARARGS}, + {"border", (PyCFunction)PyCursesWindow_Border, METH_VARARGS}, + {"box", (PyCFunction)PyCursesWindow_Box, METH_VARARGS}, + {"clear", (PyCFunction)PyCursesWindow_wclear, METH_NOARGS}, + {"clearok", (PyCFunction)PyCursesWindow_clearok, METH_VARARGS}, + {"clrtobot", (PyCFunction)PyCursesWindow_wclrtobot, METH_NOARGS}, + {"clrtoeol", (PyCFunction)PyCursesWindow_wclrtoeol, METH_NOARGS}, + {"cursyncup", (PyCFunction)PyCursesWindow_wcursyncup, METH_NOARGS}, + {"delch", (PyCFunction)PyCursesWindow_DelCh, METH_VARARGS}, + {"deleteln", (PyCFunction)PyCursesWindow_wdeleteln, METH_NOARGS}, + {"derwin", (PyCFunction)PyCursesWindow_DerWin, METH_VARARGS}, + {"echochar", (PyCFunction)PyCursesWindow_EchoChar, METH_VARARGS}, +#ifdef NCURSES_MOUSE_VERSION + {"enclose", (PyCFunction)PyCursesWindow_Enclose, METH_VARARGS}, +#endif + {"erase", (PyCFunction)PyCursesWindow_werase, METH_NOARGS}, + {"getbegyx", (PyCFunction)PyCursesWindow_getbegyx, METH_NOARGS}, + {"getbkgd", (PyCFunction)PyCursesWindow_GetBkgd, METH_NOARGS}, + {"getch", (PyCFunction)PyCursesWindow_GetCh, METH_VARARGS}, + {"getkey", (PyCFunction)PyCursesWindow_GetKey, METH_VARARGS}, +#ifdef HAVE_NCURSESW + {"get_wch", (PyCFunction)PyCursesWindow_Get_WCh, METH_VARARGS}, +#endif + {"getmaxyx", (PyCFunction)PyCursesWindow_getmaxyx, METH_NOARGS}, + {"getparyx", (PyCFunction)PyCursesWindow_getparyx, METH_NOARGS}, + {"getstr", (PyCFunction)PyCursesWindow_GetStr, METH_VARARGS}, + {"getyx", (PyCFunction)PyCursesWindow_getyx, METH_NOARGS}, + {"hline", (PyCFunction)PyCursesWindow_Hline, METH_VARARGS}, + {"idcok", (PyCFunction)PyCursesWindow_idcok, METH_VARARGS}, + {"idlok", (PyCFunction)PyCursesWindow_idlok, METH_VARARGS}, + {"immedok", (PyCFunction)PyCursesWindow_immedok, METH_VARARGS}, + {"inch", (PyCFunction)PyCursesWindow_InCh, METH_VARARGS}, + {"insch", (PyCFunction)PyCursesWindow_InsCh, METH_VARARGS}, + {"insdelln", (PyCFunction)PyCursesWindow_winsdelln, METH_VARARGS}, + {"insertln", (PyCFunction)PyCursesWindow_winsertln, METH_NOARGS}, + {"insnstr", (PyCFunction)PyCursesWindow_InsNStr, METH_VARARGS}, + {"insstr", (PyCFunction)PyCursesWindow_InsStr, METH_VARARGS}, + {"instr", (PyCFunction)PyCursesWindow_InStr, METH_VARARGS}, + {"is_linetouched", (PyCFunction)PyCursesWindow_Is_LineTouched, METH_VARARGS}, + {"is_wintouched", (PyCFunction)PyCursesWindow_is_wintouched, METH_NOARGS}, + {"keypad", (PyCFunction)PyCursesWindow_keypad, METH_VARARGS}, + {"leaveok", (PyCFunction)PyCursesWindow_leaveok, METH_VARARGS}, + {"move", (PyCFunction)PyCursesWindow_wmove, METH_VARARGS}, + {"mvderwin", (PyCFunction)PyCursesWindow_mvderwin, METH_VARARGS}, + {"mvwin", (PyCFunction)PyCursesWindow_mvwin, METH_VARARGS}, + {"nodelay", (PyCFunction)PyCursesWindow_nodelay, METH_VARARGS}, + {"notimeout", (PyCFunction)PyCursesWindow_notimeout, METH_VARARGS}, + {"noutrefresh", (PyCFunction)PyCursesWindow_NoOutRefresh, METH_VARARGS}, + {"overlay", (PyCFunction)PyCursesWindow_Overlay, METH_VARARGS}, + {"overwrite", (PyCFunction)PyCursesWindow_Overwrite, + METH_VARARGS}, + {"putwin", (PyCFunction)PyCursesWindow_PutWin, METH_O}, + {"redrawln", (PyCFunction)PyCursesWindow_RedrawLine, METH_VARARGS}, + {"redrawwin", (PyCFunction)PyCursesWindow_redrawwin, METH_NOARGS}, + {"refresh", (PyCFunction)PyCursesWindow_Refresh, METH_VARARGS}, +#ifndef STRICT_SYSV_CURSES + {"resize", (PyCFunction)PyCursesWindow_wresize, METH_VARARGS}, +#endif + {"scroll", (PyCFunction)PyCursesWindow_Scroll, METH_VARARGS}, + {"scrollok", (PyCFunction)PyCursesWindow_scrollok, METH_VARARGS}, + {"setscrreg", (PyCFunction)PyCursesWindow_SetScrollRegion, METH_VARARGS}, + {"standend", (PyCFunction)PyCursesWindow_wstandend, METH_NOARGS}, + {"standout", (PyCFunction)PyCursesWindow_wstandout, METH_NOARGS}, + {"subpad", (PyCFunction)PyCursesWindow_SubWin, METH_VARARGS}, + {"subwin", (PyCFunction)PyCursesWindow_SubWin, METH_VARARGS}, + {"syncdown", (PyCFunction)PyCursesWindow_wsyncdown, METH_NOARGS}, + {"syncok", (PyCFunction)PyCursesWindow_syncok, METH_VARARGS}, + {"syncup", (PyCFunction)PyCursesWindow_wsyncup, METH_NOARGS}, + {"timeout", (PyCFunction)PyCursesWindow_wtimeout, METH_VARARGS}, + {"touchline", (PyCFunction)PyCursesWindow_TouchLine, METH_VARARGS}, + {"touchwin", (PyCFunction)PyCursesWindow_touchwin, METH_NOARGS}, + {"untouchwin", (PyCFunction)PyCursesWindow_untouchwin, METH_NOARGS}, + {"vline", (PyCFunction)PyCursesWindow_Vline, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyGetSetDef PyCursesWindow_getsets[] = { + {"encoding", + (getter)PyCursesWindow_get_encoding, + (setter)PyCursesWindow_set_encoding, + "the typecode character used to create the array"}, + {NULL, NULL, NULL, NULL } /* sentinel */ +}; + +/* -------------------------------------------------------*/ + +PyTypeObject PyCursesWindow_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_curses.curses window", /*tp_name*/ + sizeof(PyCursesWindowObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyCursesWindow_Dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)0, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + PyCursesWindow_Methods, /*tp_methods*/ + 0, /* tp_members */ + PyCursesWindow_getsets, /* tp_getset */ +}; + +/********************************************************************* + Global Functions +**********************************************************************/ + +NoArgNoReturnFunction(beep) +NoArgNoReturnFunction(def_prog_mode) +NoArgNoReturnFunction(def_shell_mode) +NoArgNoReturnFunction(doupdate) +NoArgNoReturnFunction(endwin) +NoArgNoReturnFunction(flash) +NoArgNoReturnFunction(nocbreak) +NoArgNoReturnFunction(noecho) +NoArgNoReturnFunction(nonl) +NoArgNoReturnFunction(noraw) +NoArgNoReturnFunction(reset_prog_mode) +NoArgNoReturnFunction(reset_shell_mode) +NoArgNoReturnFunction(resetty) +NoArgNoReturnFunction(savetty) + +NoArgOrFlagNoReturnFunction(cbreak) +NoArgOrFlagNoReturnFunction(echo) +NoArgOrFlagNoReturnFunction(nl) +NoArgOrFlagNoReturnFunction(raw) + +NoArgReturnIntFunction(baudrate) +NoArgReturnIntFunction(termattrs) + +NoArgReturnStringFunction(termname) +NoArgReturnStringFunction(longname) + +NoArgTrueFalseFunction(can_change_color) +NoArgTrueFalseFunction(has_colors) +NoArgTrueFalseFunction(has_ic) +NoArgTrueFalseFunction(has_il) +NoArgTrueFalseFunction(isendwin) +NoArgNoReturnVoidFunction(flushinp) +NoArgNoReturnVoidFunction(noqiflush) + +static PyObject * +PyCurses_filter(PyObject *self) +{ + /* not checking for PyCursesInitialised here since filter() must + be called before initscr() */ + filter(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCurses_Color_Content(PyObject *self, PyObject *args) +{ + short color,r,g,b; + + PyCursesInitialised; + PyCursesInitialisedColor; + + if (!PyArg_ParseTuple(args, "h:color_content", &color)) return NULL; + + if (color_content(color, &r, &g, &b) != ERR) + return Py_BuildValue("(iii)", r, g, b); + else { + PyErr_SetString(PyCursesError, + "Argument 1 was out of range. Check value of COLORS."); + return NULL; + } +} + +static PyObject * +PyCurses_color_pair(PyObject *self, PyObject *args) +{ + int n; + + PyCursesInitialised; + PyCursesInitialisedColor; + + if (!PyArg_ParseTuple(args, "i:color_pair", &n)) return NULL; + return PyLong_FromLong((long) (n << 8)); +} + +static PyObject * +PyCurses_Curs_Set(PyObject *self, PyObject *args) +{ + int vis,erg; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args, "i:curs_set", &vis)) return NULL; + + erg = curs_set(vis); + if (erg == ERR) return PyCursesCheckERR(erg, "curs_set"); + + return PyLong_FromLong((long) erg); +} + +static PyObject * +PyCurses_Delay_Output(PyObject *self, PyObject *args) +{ + int ms; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args, "i:delay_output", &ms)) return NULL; + + return PyCursesCheckERR(delay_output(ms), "delay_output"); +} + +static PyObject * +PyCurses_EraseChar(PyObject *self) +{ + char ch; + + PyCursesInitialised; + + ch = erasechar(); + + return PyBytes_FromStringAndSize(&ch, 1); +} + +static PyObject * +PyCurses_getsyx(PyObject *self) +{ + int x = 0; + int y = 0; + + PyCursesInitialised; + + getsyx(y, x); + + return Py_BuildValue("(ii)", y, x); +} + +#ifdef NCURSES_MOUSE_VERSION +static PyObject * +PyCurses_GetMouse(PyObject *self) +{ + int rtn; + MEVENT event; + + PyCursesInitialised; + + rtn = getmouse( &event ); + if (rtn == ERR) { + PyErr_SetString(PyCursesError, "getmouse() returned ERR"); + return NULL; + } + return Py_BuildValue("(hiiil)", + (short)event.id, + event.x, event.y, event.z, + (long) event.bstate); +} + +static PyObject * +PyCurses_UngetMouse(PyObject *self, PyObject *args) +{ + MEVENT event; + + PyCursesInitialised; + if (!PyArg_ParseTuple(args, "hiiil", + &event.id, + &event.x, &event.y, &event.z, + (int *) &event.bstate)) + return NULL; + + return PyCursesCheckERR(ungetmouse(&event), "ungetmouse"); +} +#endif + +static PyObject * +PyCurses_GetWin(PyCursesWindowObject *self, PyObject *stream) +{ + char fn[100]; + int fd = -1; + FILE *fp = NULL; + PyObject *data; + size_t datalen; + WINDOW *win; + _Py_IDENTIFIER(read); + PyObject *res = NULL; + + PyCursesInitialised; + + strcpy(fn, "/tmp/py.curses.getwin.XXXXXX"); + fd = mkstemp(fn); + if (fd < 0) + return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + if (_Py_set_inheritable(fd, 0, NULL) < 0) + goto error; + fp = fdopen(fd, "wb+"); + if (fp == NULL) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + goto error; + } + + data = _PyObject_CallMethodId(stream, &PyId_read, NULL); + if (data == NULL) + goto error; + if (!PyBytes_Check(data)) { + PyErr_Format(PyExc_TypeError, + "f.read() returned %.100s instead of bytes", + data->ob_type->tp_name); + Py_DECREF(data); + goto error; + } + datalen = PyBytes_GET_SIZE(data); + if (fwrite(PyBytes_AS_STRING(data), 1, datalen, fp) != datalen) { + Py_DECREF(data); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); + goto error; + } + Py_DECREF(data); + + fseek(fp, 0, 0); + win = getwin(fp); + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + goto error; + } + res = PyCursesWindow_New(win, NULL); + +error: + if (fp != NULL) + fclose(fp); + else if (fd != -1) + close(fd); + remove(fn); + return res; +} + +static PyObject * +PyCurses_HalfDelay(PyObject *self, PyObject *args) +{ + unsigned char tenths; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args, "b:halfdelay", &tenths)) return NULL; + + return PyCursesCheckERR(halfdelay(tenths), "halfdelay"); +} + +#ifndef STRICT_SYSV_CURSES +/* No has_key! */ +static PyObject * PyCurses_has_key(PyObject *self, PyObject *args) +{ + int ch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"i",&ch)) return NULL; + + if (has_key(ch) == FALSE) { + Py_INCREF(Py_False); + return Py_False; + } + Py_INCREF(Py_True); + return Py_True; +} +#endif /* STRICT_SYSV_CURSES */ + +static PyObject * +PyCurses_Init_Color(PyObject *self, PyObject *args) +{ + short color, r, g, b; + + PyCursesInitialised; + PyCursesInitialisedColor; + + switch(PyTuple_Size(args)) { + case 4: + if (!PyArg_ParseTuple(args, "hhhh;color,r,g,b", &color, &r, &g, &b)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "init_color requires 4 arguments"); + return NULL; + } + + return PyCursesCheckERR(init_color(color, r, g, b), "init_color"); +} + +static PyObject * +PyCurses_Init_Pair(PyObject *self, PyObject *args) +{ + short pair, f, b; + + PyCursesInitialised; + PyCursesInitialisedColor; + + if (PyTuple_Size(args) != 3) { + PyErr_SetString(PyExc_TypeError, "init_pair requires 3 arguments"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "hhh;pair, f, b", &pair, &f, &b)) return NULL; + + return PyCursesCheckERR(init_pair(pair, f, b), "init_pair"); +} + +static PyObject *ModDict; + +static PyObject * +PyCurses_InitScr(PyObject *self) +{ + WINDOW *win; + PyCursesWindowObject *winobj; + + if (initialised == TRUE) { + wrefresh(stdscr); + return (PyObject *)PyCursesWindow_New(stdscr, NULL); + } + + win = initscr(); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + initialised = initialised_setupterm = TRUE; + +/* This was moved from initcurses() because it core dumped on SGI, + where they're not defined until you've called initscr() */ +#define SetDictInt(string,ch) \ + do { \ + PyObject *o = PyLong_FromLong((long) (ch)); \ + if (o && PyDict_SetItemString(ModDict, string, o) == 0) { \ + Py_DECREF(o); \ + } \ + } while (0) + + /* Here are some graphic symbols you can use */ + SetDictInt("ACS_ULCORNER", (ACS_ULCORNER)); + SetDictInt("ACS_LLCORNER", (ACS_LLCORNER)); + SetDictInt("ACS_URCORNER", (ACS_URCORNER)); + SetDictInt("ACS_LRCORNER", (ACS_LRCORNER)); + SetDictInt("ACS_LTEE", (ACS_LTEE)); + SetDictInt("ACS_RTEE", (ACS_RTEE)); + SetDictInt("ACS_BTEE", (ACS_BTEE)); + SetDictInt("ACS_TTEE", (ACS_TTEE)); + SetDictInt("ACS_HLINE", (ACS_HLINE)); + SetDictInt("ACS_VLINE", (ACS_VLINE)); + SetDictInt("ACS_PLUS", (ACS_PLUS)); +#if !defined(__hpux) || defined(HAVE_NCURSES_H) + /* On HP/UX 11, these are of type cchar_t, which is not an + integral type. If this is a problem on more platforms, a + configure test should be added to determine whether ACS_S1 + is of integral type. */ + SetDictInt("ACS_S1", (ACS_S1)); + SetDictInt("ACS_S9", (ACS_S9)); + SetDictInt("ACS_DIAMOND", (ACS_DIAMOND)); + SetDictInt("ACS_CKBOARD", (ACS_CKBOARD)); + SetDictInt("ACS_DEGREE", (ACS_DEGREE)); + SetDictInt("ACS_PLMINUS", (ACS_PLMINUS)); + SetDictInt("ACS_BULLET", (ACS_BULLET)); + SetDictInt("ACS_LARROW", (ACS_LARROW)); + SetDictInt("ACS_RARROW", (ACS_RARROW)); + SetDictInt("ACS_DARROW", (ACS_DARROW)); + SetDictInt("ACS_UARROW", (ACS_UARROW)); + SetDictInt("ACS_BOARD", (ACS_BOARD)); + SetDictInt("ACS_LANTERN", (ACS_LANTERN)); + SetDictInt("ACS_BLOCK", (ACS_BLOCK)); +#endif + SetDictInt("ACS_BSSB", (ACS_ULCORNER)); + SetDictInt("ACS_SSBB", (ACS_LLCORNER)); + SetDictInt("ACS_BBSS", (ACS_URCORNER)); + SetDictInt("ACS_SBBS", (ACS_LRCORNER)); + SetDictInt("ACS_SBSS", (ACS_RTEE)); + SetDictInt("ACS_SSSB", (ACS_LTEE)); + SetDictInt("ACS_SSBS", (ACS_BTEE)); + SetDictInt("ACS_BSSS", (ACS_TTEE)); + SetDictInt("ACS_BSBS", (ACS_HLINE)); + SetDictInt("ACS_SBSB", (ACS_VLINE)); + SetDictInt("ACS_SSSS", (ACS_PLUS)); + + /* The following are never available with strict SYSV curses */ +#ifdef ACS_S3 + SetDictInt("ACS_S3", (ACS_S3)); +#endif +#ifdef ACS_S7 + SetDictInt("ACS_S7", (ACS_S7)); +#endif +#ifdef ACS_LEQUAL + SetDictInt("ACS_LEQUAL", (ACS_LEQUAL)); +#endif +#ifdef ACS_GEQUAL + SetDictInt("ACS_GEQUAL", (ACS_GEQUAL)); +#endif +#ifdef ACS_PI + SetDictInt("ACS_PI", (ACS_PI)); +#endif +#ifdef ACS_NEQUAL + SetDictInt("ACS_NEQUAL", (ACS_NEQUAL)); +#endif +#ifdef ACS_STERLING + SetDictInt("ACS_STERLING", (ACS_STERLING)); +#endif + + SetDictInt("LINES", LINES); + SetDictInt("COLS", COLS); + + winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL); + screen_encoding = winobj->encoding; + return (PyObject *)winobj; +} + +static PyObject * +PyCurses_setupterm(PyObject* self, PyObject *args, PyObject* keywds) +{ + int fd = -1; + int err; + char* termstr = NULL; + + static char *kwlist[] = {"term", "fd", NULL}; + + if (!PyArg_ParseTupleAndKeywords( + args, keywds, "|zi:setupterm", kwlist, &termstr, &fd)) { + return NULL; + } + + if (fd == -1) { + PyObject* sys_stdout; + + sys_stdout = PySys_GetObject("stdout"); + + if (sys_stdout == NULL || sys_stdout == Py_None) { + PyErr_SetString( + PyCursesError, + "lost sys.stdout"); + return NULL; + } + + fd = PyObject_AsFileDescriptor(sys_stdout); + + if (fd == -1) { + return NULL; + } + } + + if (!initialised_setupterm && setupterm(termstr,fd,&err) == ERR) { + char* s = "setupterm: unknown error"; + + if (err == 0) { + s = "setupterm: could not find terminal"; + } else if (err == -1) { + s = "setupterm: could not find terminfo database"; + } + + PyErr_SetString(PyCursesError,s); + return NULL; + } + + initialised_setupterm = TRUE; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCurses_IntrFlush(PyObject *self, PyObject *args) +{ + int ch; + + PyCursesInitialised; + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"i;True(1), False(0)",&ch)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "intrflush requires 1 argument"); + return NULL; + } + + return PyCursesCheckERR(intrflush(NULL,ch), "intrflush"); +} + +#ifdef HAVE_CURSES_IS_TERM_RESIZED +static PyObject * +PyCurses_Is_Term_Resized(PyObject *self, PyObject *args) +{ + int lines; + int columns; + int result; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"ii:is_term_resized", &lines, &columns)) + return NULL; + result = is_term_resized(lines, columns); + if (result == TRUE) { + Py_INCREF(Py_True); + return Py_True; + } else { + Py_INCREF(Py_False); + return Py_False; + } +} +#endif /* HAVE_CURSES_IS_TERM_RESIZED */ + +#if !defined(__NetBSD__) +static PyObject * +PyCurses_KeyName(PyObject *self, PyObject *args) +{ + const char *knp; + int ch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"i",&ch)) return NULL; + + if (ch < 0) { + PyErr_SetString(PyExc_ValueError, "invalid key number"); + return NULL; + } + knp = keyname(ch); + + return PyBytes_FromString((knp == NULL) ? "" : knp); +} +#endif + +static PyObject * +PyCurses_KillChar(PyObject *self) +{ + char ch; + + ch = killchar(); + + return PyBytes_FromStringAndSize(&ch, 1); +} + +static PyObject * +PyCurses_Meta(PyObject *self, PyObject *args) +{ + int ch; + + PyCursesInitialised; + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"i;True(1), False(0)",&ch)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "meta requires 1 argument"); + return NULL; + } + + return PyCursesCheckERR(meta(stdscr, ch), "meta"); +} + +#ifdef NCURSES_MOUSE_VERSION +static PyObject * +PyCurses_MouseInterval(PyObject *self, PyObject *args) +{ + int interval; + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"i;interval",&interval)) + return NULL; + return PyCursesCheckERR(mouseinterval(interval), "mouseinterval"); +} + +static PyObject * +PyCurses_MouseMask(PyObject *self, PyObject *args) +{ + int newmask; + mmask_t oldmask, availmask; + + PyCursesInitialised; + if (!PyArg_ParseTuple(args,"i;mousemask",&newmask)) + return NULL; + availmask = mousemask(newmask, &oldmask); + return Py_BuildValue("(ll)", (long)availmask, (long)oldmask); +} +#endif + +static PyObject * +PyCurses_Napms(PyObject *self, PyObject *args) +{ + int ms; + + PyCursesInitialised; + if (!PyArg_ParseTuple(args, "i;ms", &ms)) return NULL; + + return Py_BuildValue("i", napms(ms)); +} + + +static PyObject * +PyCurses_NewPad(PyObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"ii;nlines,ncols",&nlines,&ncols)) return NULL; + + win = newpad(nlines, ncols); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win, NULL); +} + +static PyObject * +PyCurses_NewWindow(PyObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols, begin_y=0, begin_x=0; + + PyCursesInitialised; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;nlines,ncols",&nlines,&ncols)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiii;nlines,ncols,begin_y,begin_x", + &nlines,&ncols,&begin_y,&begin_x)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "newwin requires 2 or 4 arguments"); + return NULL; + } + + win = newwin(nlines,ncols,begin_y,begin_x); + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win, NULL); +} + +static PyObject * +PyCurses_Pair_Content(PyObject *self, PyObject *args) +{ + short pair,f,b; + + PyCursesInitialised; + PyCursesInitialisedColor; + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "h;pair", &pair)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "pair_content requires 1 argument"); + return NULL; + } + + if (pair_content(pair, &f, &b)==ERR) { + PyErr_SetString(PyCursesError, + "Argument 1 was out of range. (1..COLOR_PAIRS-1)"); + return NULL; + } + + return Py_BuildValue("(ii)", f, b); +} + +static PyObject * +PyCurses_pair_number(PyObject *self, PyObject *args) +{ + int n; + + PyCursesInitialised; + PyCursesInitialisedColor; + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "i;pairvalue", &n)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, + "pair_number requires 1 argument"); + return NULL; + } + + return PyLong_FromLong((long) ((n & A_COLOR) >> 8)); +} + +static PyObject * +PyCurses_Putp(PyObject *self, PyObject *args) +{ + char *str; + + if (!PyArg_ParseTuple(args,"y;str", &str)) + return NULL; + return PyCursesCheckERR(putp(str), "putp"); +} + +static PyObject * +PyCurses_QiFlush(PyObject *self, PyObject *args) +{ + int flag = 0; + + PyCursesInitialised; + + switch(PyTuple_Size(args)) { + case 0: + qiflush(); + Py_INCREF(Py_None); + return Py_None; + case 1: + if (!PyArg_ParseTuple(args, "i;True(1) or False(0)", &flag)) return NULL; + if (flag) qiflush(); + else noqiflush(); + Py_INCREF(Py_None); + return Py_None; + default: + PyErr_SetString(PyExc_TypeError, "qiflush requires 0 or 1 arguments"); + return NULL; + } +} + +/* Internal helper used for updating curses.LINES, curses.COLS, _curses.LINES + * and _curses.COLS */ +#if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM) +static int +update_lines_cols(void) +{ + PyObject *o; + PyObject *m = PyImport_ImportModuleNoBlock("curses"); + _Py_IDENTIFIER(LINES); + _Py_IDENTIFIER(COLS); + + if (!m) + return 0; + + o = PyLong_FromLong(LINES); + if (!o) { + Py_DECREF(m); + return 0; + } + if (_PyObject_SetAttrId(m, &PyId_LINES, o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + /* PyId_LINES.object will be initialized here. */ + if (PyDict_SetItem(ModDict, PyId_LINES.object, o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + Py_DECREF(o); + o = PyLong_FromLong(COLS); + if (!o) { + Py_DECREF(m); + return 0; + } + if (_PyObject_SetAttrId(m, &PyId_COLS, o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + if (PyDict_SetItem(ModDict, PyId_COLS.object, o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + Py_DECREF(o); + Py_DECREF(m); + return 1; +} + +static PyObject * +PyCurses_update_lines_cols(PyObject *self) +{ + return PyLong_FromLong((long) update_lines_cols()); +} + +#endif + +#ifdef HAVE_CURSES_RESIZETERM +static PyObject * +PyCurses_ResizeTerm(PyObject *self, PyObject *args) +{ + int lines; + int columns; + PyObject *result; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"ii:resizeterm", &lines, &columns)) + return NULL; + + result = PyCursesCheckERR(resizeterm(lines, columns), "resizeterm"); + if (!result) + return NULL; + if (!update_lines_cols()) + return NULL; + return result; +} + +#endif + +#ifdef HAVE_CURSES_RESIZE_TERM +static PyObject * +PyCurses_Resize_Term(PyObject *self, PyObject *args) +{ + int lines; + int columns; + + PyObject *result; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"ii:resize_term", &lines, &columns)) + return NULL; + + result = PyCursesCheckERR(resize_term(lines, columns), "resize_term"); + if (!result) + return NULL; + if (!update_lines_cols()) + return NULL; + return result; +} +#endif /* HAVE_CURSES_RESIZE_TERM */ + +static PyObject * +PyCurses_setsyx(PyObject *self, PyObject *args) +{ + int y,x; + + PyCursesInitialised; + + if (PyTuple_Size(args)!=2) { + PyErr_SetString(PyExc_TypeError, "setsyx requires 2 arguments"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "ii;y, x", &y, &x)) return NULL; + + setsyx(y,x); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCurses_Start_Color(PyObject *self) +{ + int code; + PyObject *c, *cp; + + PyCursesInitialised; + + code = start_color(); + if (code != ERR) { + initialisedcolors = TRUE; + c = PyLong_FromLong((long) COLORS); + if (c == NULL) + return NULL; + PyDict_SetItemString(ModDict, "COLORS", c); + Py_DECREF(c); + cp = PyLong_FromLong((long) COLOR_PAIRS); + if (cp == NULL) + return NULL; + PyDict_SetItemString(ModDict, "COLOR_PAIRS", cp); + Py_DECREF(cp); + Py_INCREF(Py_None); + return Py_None; + } else { + PyErr_SetString(PyCursesError, "start_color() returned ERR"); + return NULL; + } +} + +static PyObject * +PyCurses_tigetflag(PyObject *self, PyObject *args) +{ + char *capname; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "s", &capname)) + return NULL; + + return PyLong_FromLong( (long) tigetflag( capname ) ); +} + +static PyObject * +PyCurses_tigetnum(PyObject *self, PyObject *args) +{ + char *capname; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "s", &capname)) + return NULL; + + return PyLong_FromLong( (long) tigetnum( capname ) ); +} + +static PyObject * +PyCurses_tigetstr(PyObject *self, PyObject *args) +{ + char *capname; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "s", &capname)) + return NULL; + + capname = tigetstr( capname ); + if (capname == 0 || capname == (char*) -1) { + Py_INCREF(Py_None); + return Py_None; + } + return PyBytes_FromString( capname ); +} + +static PyObject * +PyCurses_tparm(PyObject *self, PyObject *args) +{ + char* fmt; + char* result = NULL; + int i1=0,i2=0,i3=0,i4=0,i5=0,i6=0,i7=0,i8=0,i9=0; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "y|iiiiiiiii:tparm", + &fmt, &i1, &i2, &i3, &i4, + &i5, &i6, &i7, &i8, &i9)) { + return NULL; + } + + result = tparm(fmt,i1,i2,i3,i4,i5,i6,i7,i8,i9); + if (!result) { + PyErr_SetString(PyCursesError, "tparm() returned NULL"); + return NULL; + } + + return PyBytes_FromString(result); +} + +static PyObject * +PyCurses_TypeAhead(PyObject *self, PyObject *args) +{ + int fd; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"i;fd",&fd)) return NULL; + + return PyCursesCheckERR(typeahead( fd ), "typeahead"); +} + +static PyObject * +PyCurses_UnCtrl(PyObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"O;ch or int",&temp)) return NULL; + + if (!PyCurses_ConvertToChtype(NULL, temp, &ch)) + return NULL; + + return PyBytes_FromString(unctrl(ch)); +} + +static PyObject * +PyCurses_UngetCh(PyObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"O;ch or int",&temp)) + return NULL; + + if (!PyCurses_ConvertToChtype(NULL, temp, &ch)) + return NULL; + + return PyCursesCheckERR(ungetch(ch), "ungetch"); +} + +#ifdef HAVE_NCURSESW +/* Convert an object to a character (wchar_t): + + - int + - str of length 1 + + Return 1 on success, 0 on error. */ +static int +PyCurses_ConvertToWchar_t(PyObject *obj, + wchar_t *wch) +{ + if (PyUnicode_Check(obj)) { + wchar_t buffer[2]; + if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, " + "got a str of length %zi", + PyUnicode_GET_LENGTH(obj)); + return 0; + } + *wch = buffer[0]; + return 2; + } + else if (PyLong_CheckExact(obj)) { + long value; + int overflow; + value = PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow) { + PyErr_SetString(PyExc_OverflowError, + "int doesn't fit in long"); + return 0; + } + *wch = (wchar_t)value; + if ((long)*wch != value) { + PyErr_Format(PyExc_OverflowError, + "character doesn't fit in wchar_t"); + return 0; + } + return 1; + } + else { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, got %s", + Py_TYPE(obj)->tp_name); + return 0; + } +} + +static PyObject * +PyCurses_Unget_Wch(PyObject *self, PyObject *args) +{ + PyObject *obj; + wchar_t wch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"O", &obj)) + return NULL; + + if (!PyCurses_ConvertToWchar_t(obj, &wch)) + return NULL; + return PyCursesCheckERR(unget_wch(wch), "unget_wch"); +} +#endif + +static PyObject * +PyCurses_Use_Env(PyObject *self, PyObject *args) +{ + int flag; + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"i;True(1), False(0)",&flag)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "use_env requires 1 argument"); + return NULL; + } + use_env(flag); + Py_INCREF(Py_None); + return Py_None; +} + +#ifndef STRICT_SYSV_CURSES +static PyObject * +PyCurses_Use_Default_Colors(PyObject *self) +{ + int code; + + PyCursesInitialised; + PyCursesInitialisedColor; + + code = use_default_colors(); + if (code != ERR) { + Py_INCREF(Py_None); + return Py_None; + } else { + PyErr_SetString(PyCursesError, "use_default_colors() returned ERR"); + return NULL; + } +} +#endif /* STRICT_SYSV_CURSES */ + +/* List of functions defined in the module */ + +static PyMethodDef PyCurses_methods[] = { + {"baudrate", (PyCFunction)PyCurses_baudrate, METH_NOARGS}, + {"beep", (PyCFunction)PyCurses_beep, METH_NOARGS}, + {"can_change_color", (PyCFunction)PyCurses_can_change_color, METH_NOARGS}, + {"cbreak", (PyCFunction)PyCurses_cbreak, METH_VARARGS}, + {"color_content", (PyCFunction)PyCurses_Color_Content, METH_VARARGS}, + {"color_pair", (PyCFunction)PyCurses_color_pair, METH_VARARGS}, + {"curs_set", (PyCFunction)PyCurses_Curs_Set, METH_VARARGS}, + {"def_prog_mode", (PyCFunction)PyCurses_def_prog_mode, METH_NOARGS}, + {"def_shell_mode", (PyCFunction)PyCurses_def_shell_mode, METH_NOARGS}, + {"delay_output", (PyCFunction)PyCurses_Delay_Output, METH_VARARGS}, + {"doupdate", (PyCFunction)PyCurses_doupdate, METH_NOARGS}, + {"echo", (PyCFunction)PyCurses_echo, METH_VARARGS}, + {"endwin", (PyCFunction)PyCurses_endwin, METH_NOARGS}, + {"erasechar", (PyCFunction)PyCurses_EraseChar, METH_NOARGS}, + {"filter", (PyCFunction)PyCurses_filter, METH_NOARGS}, + {"flash", (PyCFunction)PyCurses_flash, METH_NOARGS}, + {"flushinp", (PyCFunction)PyCurses_flushinp, METH_NOARGS}, +#ifdef NCURSES_MOUSE_VERSION + {"getmouse", (PyCFunction)PyCurses_GetMouse, METH_NOARGS}, + {"ungetmouse", (PyCFunction)PyCurses_UngetMouse, METH_VARARGS}, +#endif + {"getsyx", (PyCFunction)PyCurses_getsyx, METH_NOARGS}, + {"getwin", (PyCFunction)PyCurses_GetWin, METH_O}, + {"has_colors", (PyCFunction)PyCurses_has_colors, METH_NOARGS}, + {"has_ic", (PyCFunction)PyCurses_has_ic, METH_NOARGS}, + {"has_il", (PyCFunction)PyCurses_has_il, METH_NOARGS}, +#ifndef STRICT_SYSV_CURSES + {"has_key", (PyCFunction)PyCurses_has_key, METH_VARARGS}, +#endif + {"halfdelay", (PyCFunction)PyCurses_HalfDelay, METH_VARARGS}, + {"init_color", (PyCFunction)PyCurses_Init_Color, METH_VARARGS}, + {"init_pair", (PyCFunction)PyCurses_Init_Pair, METH_VARARGS}, + {"initscr", (PyCFunction)PyCurses_InitScr, METH_NOARGS}, + {"intrflush", (PyCFunction)PyCurses_IntrFlush, METH_VARARGS}, + {"isendwin", (PyCFunction)PyCurses_isendwin, METH_NOARGS}, +#ifdef HAVE_CURSES_IS_TERM_RESIZED + {"is_term_resized", (PyCFunction)PyCurses_Is_Term_Resized, METH_VARARGS}, +#endif +#if !defined(__NetBSD__) + {"keyname", (PyCFunction)PyCurses_KeyName, METH_VARARGS}, +#endif + {"killchar", (PyCFunction)PyCurses_KillChar, METH_NOARGS}, + {"longname", (PyCFunction)PyCurses_longname, METH_NOARGS}, + {"meta", (PyCFunction)PyCurses_Meta, METH_VARARGS}, +#ifdef NCURSES_MOUSE_VERSION + {"mouseinterval", (PyCFunction)PyCurses_MouseInterval, METH_VARARGS}, + {"mousemask", (PyCFunction)PyCurses_MouseMask, METH_VARARGS}, +#endif + {"napms", (PyCFunction)PyCurses_Napms, METH_VARARGS}, + {"newpad", (PyCFunction)PyCurses_NewPad, METH_VARARGS}, + {"newwin", (PyCFunction)PyCurses_NewWindow, METH_VARARGS}, + {"nl", (PyCFunction)PyCurses_nl, METH_VARARGS}, + {"nocbreak", (PyCFunction)PyCurses_nocbreak, METH_NOARGS}, + {"noecho", (PyCFunction)PyCurses_noecho, METH_NOARGS}, + {"nonl", (PyCFunction)PyCurses_nonl, METH_NOARGS}, + {"noqiflush", (PyCFunction)PyCurses_noqiflush, METH_NOARGS}, + {"noraw", (PyCFunction)PyCurses_noraw, METH_NOARGS}, + {"pair_content", (PyCFunction)PyCurses_Pair_Content, METH_VARARGS}, + {"pair_number", (PyCFunction)PyCurses_pair_number, METH_VARARGS}, + {"putp", (PyCFunction)PyCurses_Putp, METH_VARARGS}, + {"qiflush", (PyCFunction)PyCurses_QiFlush, METH_VARARGS}, + {"raw", (PyCFunction)PyCurses_raw, METH_VARARGS}, + {"reset_prog_mode", (PyCFunction)PyCurses_reset_prog_mode, METH_NOARGS}, + {"reset_shell_mode", (PyCFunction)PyCurses_reset_shell_mode, METH_NOARGS}, + {"resetty", (PyCFunction)PyCurses_resetty, METH_NOARGS}, +#ifdef HAVE_CURSES_RESIZETERM + {"resizeterm", (PyCFunction)PyCurses_ResizeTerm, METH_VARARGS}, +#endif +#ifdef HAVE_CURSES_RESIZE_TERM + {"resize_term", (PyCFunction)PyCurses_Resize_Term, METH_VARARGS}, +#endif + {"savetty", (PyCFunction)PyCurses_savetty, METH_NOARGS}, + {"setsyx", (PyCFunction)PyCurses_setsyx, METH_VARARGS}, + {"setupterm", (PyCFunction)PyCurses_setupterm, + METH_VARARGS|METH_KEYWORDS}, + {"start_color", (PyCFunction)PyCurses_Start_Color, METH_NOARGS}, + {"termattrs", (PyCFunction)PyCurses_termattrs, METH_NOARGS}, + {"termname", (PyCFunction)PyCurses_termname, METH_NOARGS}, + {"tigetflag", (PyCFunction)PyCurses_tigetflag, METH_VARARGS}, + {"tigetnum", (PyCFunction)PyCurses_tigetnum, METH_VARARGS}, + {"tigetstr", (PyCFunction)PyCurses_tigetstr, METH_VARARGS}, + {"tparm", (PyCFunction)PyCurses_tparm, METH_VARARGS}, + {"typeahead", (PyCFunction)PyCurses_TypeAhead, METH_VARARGS}, + {"unctrl", (PyCFunction)PyCurses_UnCtrl, METH_VARARGS}, + {"ungetch", (PyCFunction)PyCurses_UngetCh, METH_VARARGS}, +#if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM) + {"update_lines_cols", (PyCFunction)PyCurses_update_lines_cols, METH_NOARGS}, +#endif +#ifdef HAVE_NCURSESW + {"unget_wch", (PyCFunction)PyCurses_Unget_Wch, METH_VARARGS}, +#endif + {"use_env", (PyCFunction)PyCurses_Use_Env, METH_VARARGS}, +#ifndef STRICT_SYSV_CURSES + {"use_default_colors", (PyCFunction)PyCurses_Use_Default_Colors, METH_NOARGS}, +#endif + {NULL, NULL} /* sentinel */ +}; + +/* Initialization function for the module */ + + +static struct PyModuleDef _cursesmodule = { + PyModuleDef_HEAD_INIT, + "_curses", + NULL, + -1, + PyCurses_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__curses(void) +{ + PyObject *m, *d, *v, *c_api_object; + static void *PyCurses_API[PyCurses_API_pointers]; + + /* Initialize object type */ + if (PyType_Ready(&PyCursesWindow_Type) < 0) + return NULL; + + /* Initialize the C API pointer array */ + PyCurses_API[0] = (void *)&PyCursesWindow_Type; + PyCurses_API[1] = (void *)func_PyCursesSetupTermCalled; + PyCurses_API[2] = (void *)func_PyCursesInitialised; + PyCurses_API[3] = (void *)func_PyCursesInitialisedColor; + + /* Create the module and add the functions */ + m = PyModule_Create(&_cursesmodule); + if (m == NULL) + return NULL; + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + if (d == NULL) + return NULL; + ModDict = d; /* For PyCurses_InitScr to use later */ + + /* Add a capsule for the C API */ + c_api_object = PyCapsule_New(PyCurses_API, PyCurses_CAPSULE_NAME, NULL); + PyDict_SetItemString(d, "_C_API", c_api_object); + Py_DECREF(c_api_object); + + /* For exception curses.error */ + PyCursesError = PyErr_NewException("_curses.error", NULL, NULL); + PyDict_SetItemString(d, "error", PyCursesError); + + /* Make the version available */ + v = PyBytes_FromString(PyCursesVersion); + PyDict_SetItemString(d, "version", v); + PyDict_SetItemString(d, "__version__", v); + Py_DECREF(v); + + SetDictInt("ERR", ERR); + SetDictInt("OK", OK); + + /* Here are some attributes you can add to chars to print */ + + SetDictInt("A_ATTRIBUTES", A_ATTRIBUTES); + SetDictInt("A_NORMAL", A_NORMAL); + SetDictInt("A_STANDOUT", A_STANDOUT); + SetDictInt("A_UNDERLINE", A_UNDERLINE); + SetDictInt("A_REVERSE", A_REVERSE); + SetDictInt("A_BLINK", A_BLINK); + SetDictInt("A_DIM", A_DIM); + SetDictInt("A_BOLD", A_BOLD); + SetDictInt("A_ALTCHARSET", A_ALTCHARSET); +#if !defined(__NetBSD__) + SetDictInt("A_INVIS", A_INVIS); +#endif + SetDictInt("A_PROTECT", A_PROTECT); + SetDictInt("A_CHARTEXT", A_CHARTEXT); + SetDictInt("A_COLOR", A_COLOR); + + /* The following are never available with strict SYSV curses */ +#ifdef A_HORIZONTAL + SetDictInt("A_HORIZONTAL", A_HORIZONTAL); +#endif +#ifdef A_LEFT + SetDictInt("A_LEFT", A_LEFT); +#endif +#ifdef A_LOW + SetDictInt("A_LOW", A_LOW); +#endif +#ifdef A_RIGHT + SetDictInt("A_RIGHT", A_RIGHT); +#endif +#ifdef A_TOP + SetDictInt("A_TOP", A_TOP); +#endif +#ifdef A_VERTICAL + SetDictInt("A_VERTICAL", A_VERTICAL); +#endif + + SetDictInt("COLOR_BLACK", COLOR_BLACK); + SetDictInt("COLOR_RED", COLOR_RED); + SetDictInt("COLOR_GREEN", COLOR_GREEN); + SetDictInt("COLOR_YELLOW", COLOR_YELLOW); + SetDictInt("COLOR_BLUE", COLOR_BLUE); + SetDictInt("COLOR_MAGENTA", COLOR_MAGENTA); + SetDictInt("COLOR_CYAN", COLOR_CYAN); + SetDictInt("COLOR_WHITE", COLOR_WHITE); + +#ifdef NCURSES_MOUSE_VERSION + /* Mouse-related constants */ + SetDictInt("BUTTON1_PRESSED", BUTTON1_PRESSED); + SetDictInt("BUTTON1_RELEASED", BUTTON1_RELEASED); + SetDictInt("BUTTON1_CLICKED", BUTTON1_CLICKED); + SetDictInt("BUTTON1_DOUBLE_CLICKED", BUTTON1_DOUBLE_CLICKED); + SetDictInt("BUTTON1_TRIPLE_CLICKED", BUTTON1_TRIPLE_CLICKED); + + SetDictInt("BUTTON2_PRESSED", BUTTON2_PRESSED); + SetDictInt("BUTTON2_RELEASED", BUTTON2_RELEASED); + SetDictInt("BUTTON2_CLICKED", BUTTON2_CLICKED); + SetDictInt("BUTTON2_DOUBLE_CLICKED", BUTTON2_DOUBLE_CLICKED); + SetDictInt("BUTTON2_TRIPLE_CLICKED", BUTTON2_TRIPLE_CLICKED); + + SetDictInt("BUTTON3_PRESSED", BUTTON3_PRESSED); + SetDictInt("BUTTON3_RELEASED", BUTTON3_RELEASED); + SetDictInt("BUTTON3_CLICKED", BUTTON3_CLICKED); + SetDictInt("BUTTON3_DOUBLE_CLICKED", BUTTON3_DOUBLE_CLICKED); + SetDictInt("BUTTON3_TRIPLE_CLICKED", BUTTON3_TRIPLE_CLICKED); + + SetDictInt("BUTTON4_PRESSED", BUTTON4_PRESSED); + SetDictInt("BUTTON4_RELEASED", BUTTON4_RELEASED); + SetDictInt("BUTTON4_CLICKED", BUTTON4_CLICKED); + SetDictInt("BUTTON4_DOUBLE_CLICKED", BUTTON4_DOUBLE_CLICKED); + SetDictInt("BUTTON4_TRIPLE_CLICKED", BUTTON4_TRIPLE_CLICKED); + + SetDictInt("BUTTON_SHIFT", BUTTON_SHIFT); + SetDictInt("BUTTON_CTRL", BUTTON_CTRL); + SetDictInt("BUTTON_ALT", BUTTON_ALT); + + SetDictInt("ALL_MOUSE_EVENTS", ALL_MOUSE_EVENTS); + SetDictInt("REPORT_MOUSE_POSITION", REPORT_MOUSE_POSITION); +#endif + /* Now set everything up for KEY_ variables */ + { + int key; + char *key_n; + char *key_n2; +#if !defined(__NetBSD__) + for (key=KEY_MIN;key < KEY_MAX; key++) { + key_n = (char *)keyname(key); + if (key_n == NULL || strcmp(key_n,"UNKNOWN KEY")==0) + continue; + if (strncmp(key_n,"KEY_F(",6)==0) { + char *p1, *p2; + key_n2 = PyMem_Malloc(strlen(key_n)+1); + if (!key_n2) { + PyErr_NoMemory(); + break; + } + p1 = key_n; + p2 = key_n2; + while (*p1) { + if (*p1 != '(' && *p1 != ')') { + *p2 = *p1; + p2++; + } + p1++; + } + *p2 = (char)0; + } else + key_n2 = key_n; + SetDictInt(key_n2,key); + if (key_n2 != key_n) + PyMem_Free(key_n2); + } +#endif + SetDictInt("KEY_MIN", KEY_MIN); + SetDictInt("KEY_MAX", KEY_MAX); + } + return m; +} diff --git a/src/main/resource/testFiles/cpython/revFiles/ed6219_939667_Objects#abstract.c b/src/main/resource/testFiles/cpython/revFiles/ed6219_939667_Objects#abstract.c new file mode 100644 index 0000000..30f993b --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/ed6219_939667_Objects#abstract.c @@ -0,0 +1,1467 @@ +/*********************************************************** +Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, +The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI or Corporation for National Research Initiatives or +CNRI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +While CWI is the initial source for this software, a modified version +is made available by the Corporation for National Research Initiatives +(CNRI) at the Internet address ftp://ftp.python.org. + +STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH +CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Abstract Object Interface (many thanks to Jim Fulton) */ + +#include "Python.h" +#include + +/* Shorthands to return certain errors */ + +static PyObject * +type_error(msg) + char *msg; +{ + PyErr_SetString(PyExc_TypeError, msg); + return NULL; +} + +static PyObject * +null_error() +{ + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_SystemError, + "null argument to internal routine"); + return NULL; +} + +/* Copied with modifications from stropmodule.c: atoi, atof, atol */ + +static PyObject * +int_from_string(v) + PyObject *v; +{ + extern long PyOS_strtol Py_PROTO((const char *, char **, int)); + char *s, *end; + long x; + char buffer[256]; /* For errors */ + + s = PyString_AS_STRING(v); + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "empty string for int()"); + return NULL; + } + errno = 0; + x = PyOS_strtol(s, &end, 10); + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + sprintf(buffer, "invalid literal for int(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + else if (end-s != PyString_GET_SIZE(v)) { + PyErr_SetString(PyExc_ValueError, + "null byte in argument for int()"); + return NULL; + } + else if (errno != 0) { + sprintf(buffer, "int() literal too large: %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + return PyInt_FromLong(x); +} + +static PyObject * +long_from_string(v) + PyObject *v; +{ + char *s, *end; + PyObject *x; + char buffer[256]; /* For errors */ + + s = PyString_AS_STRING(v); + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "empty string for long()"); + return NULL; + } + x = PyLong_FromString(s, &end, 10); + if (x == NULL) + return NULL; + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + sprintf(buffer, "invalid literal for long(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + Py_DECREF(x); + return NULL; + } + else if (end-s != PyString_GET_SIZE(v)) { + PyErr_SetString(PyExc_ValueError, + "null byte in argument for float()"); + return NULL; + } + return x; +} + +static PyObject * +float_from_string(v) + PyObject *v; +{ + extern double strtod Py_PROTO((const char *, char **)); + char *s, *end; + double x; + char buffer[256]; /* For errors */ + + s = PyString_AS_STRING(v); + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "empty string for float()"); + return NULL; + } + errno = 0; + PyFPE_START_PROTECT("float_from_string", return 0) + x = strtod(s, &end); + PyFPE_END_PROTECT(x) + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + sprintf(buffer, "invalid literal for float(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + else if (end-s != PyString_GET_SIZE(v)) { + PyErr_SetString(PyExc_ValueError, + "null byte in argument for float()"); + return NULL; + } + else if (errno != 0) { + sprintf(buffer, "float() literal too large: %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + return PyFloat_FromDouble(x); +} + +/* Operations on any object */ + +int +PyObject_Cmp(o1, o2, result) + PyObject *o1; + PyObject *o2; + int *result; +{ + int r; + + if (o1 == NULL || o2 == NULL) { + null_error(); + return -1; + } + r = PyObject_Compare(o1, o2); + if (PyErr_Occurred()) + return -1; + *result = r; + return 0; +} + +PyObject * +PyObject_Type(o) + PyObject *o; +{ + PyObject *v; + + if (o == NULL) + return null_error(); + v = (PyObject *)o->ob_type; + Py_INCREF(v); + return v; +} + +int +PyObject_Length(o) + PyObject *o; +{ + PySequenceMethods *m; + + if (o == NULL) { + null_error(); + return -1; + } + + m = o->ob_type->tp_as_sequence; + if (m && m->sq_length) + return m->sq_length(o); + + return PyMapping_Length(o); +} + +PyObject * +PyObject_GetItem(o, key) + PyObject *o; + PyObject *key; +{ + PyMappingMethods *m; + + if (o == NULL || key == NULL) + return null_error(); + + m = o->ob_type->tp_as_mapping; + if (m && m->mp_subscript) + return m->mp_subscript(o, key); + + if (PyInt_Check(key)) + return PySequence_GetItem(o, PyInt_AsLong(key)); + + return type_error("unsubscriptable object"); +} + +int +PyObject_SetItem(o, key, value) + PyObject *o; + PyObject *key; + PyObject *value; +{ + PyMappingMethods *m; + + if (o == NULL || key == NULL || value == NULL) { + null_error(); + return -1; + } + m = o->ob_type->tp_as_mapping; + if (m && m->mp_ass_subscript) + return m->mp_ass_subscript(o, key, value); + + if (PyInt_Check(key)) + return PySequence_SetItem(o, PyInt_AsLong(key), value); + + type_error("object does not support item assignment"); + return -1; +} + +int +PyObject_DelItem(o, key) + PyObject *o; + PyObject *key; +{ + PyMappingMethods *m; + + if (o == NULL || key == NULL) { + null_error(); + return -1; + } + m = o->ob_type->tp_as_mapping; + if (m && m->mp_ass_subscript) + return m->mp_ass_subscript(o, key, (PyObject*)NULL); + + if (PyInt_Check(key)) + return PySequence_DelItem(o, PyInt_AsLong(key)); + + type_error("object does not support item deletion"); + return -1; +} + +/* Operations on numbers */ + +int +PyNumber_Check(o) + PyObject *o; +{ + return o && o->ob_type->tp_as_number; +} + +/* Binary operators */ + +#define BINOP(v, w, opname, ropname, thisfunc) \ + if (PyInstance_Check(v) || PyInstance_Check(w)) \ + return PyInstance_DoBinOp(v, w, opname, ropname, thisfunc) + +PyObject * +PyNumber_Or(v, w) + PyObject *v, *w; +{ + extern int PyNumber_Coerce(); + + BINOP(v, w, "__or__", "__ror__", PyNumber_Or); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_or) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for |"); +} + +PyObject * +PyNumber_Xor(v, w) + PyObject *v, *w; +{ + extern int PyNumber_Coerce(); + + BINOP(v, w, "__xor__", "__rxor__", PyNumber_Xor); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_xor) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for ^"); +} + +PyObject * +PyNumber_And(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__and__", "__rand__", PyNumber_And); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_and) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for &"); +} + +PyObject * +PyNumber_Lshift(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__lshift__", "__rlshift__", PyNumber_Lshift); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_lshift) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for <<"); +} + +PyObject * +PyNumber_Rshift(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__rshift__", "__rrshift__", PyNumber_Rshift); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_rshift) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for >>"); +} + +PyObject * +PyNumber_Add(v, w) + PyObject *v, *w; +{ + PySequenceMethods *m; + + BINOP(v, w, "__add__", "__radd__", PyNumber_Add); + m = v->ob_type->tp_as_sequence; + if (m && m->sq_concat) + return (*m->sq_concat)(v, w); + else if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_add) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for +"); +} + +PyObject * +PyNumber_Subtract(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__sub__", "__rsub__", PyNumber_Subtract); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_subtract) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for -"); +} + +PyObject * +PyNumber_Multiply(v, w) + PyObject *v, *w; +{ + PyTypeObject *tp = v->ob_type; + PySequenceMethods *m; + + BINOP(v, w, "__mul__", "__rmul__", PyNumber_Multiply); + if (tp->tp_as_number != NULL && + w->ob_type->tp_as_sequence != NULL && + !PyInstance_Check(v)) { + /* number*sequence -- swap v and w */ + PyObject *tmp = v; + v = w; + w = tmp; + tp = v->ob_type; + } + if (tp->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyInstance_Check(v)) { + /* Instances of user-defined classes get their + other argument uncoerced, so they may + implement sequence*number as well as + number*number. */ + Py_INCREF(v); + Py_INCREF(w); + } + else if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_multiply) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + m = tp->tp_as_sequence; + if (m && m->sq_repeat) { + if (!PyInt_Check(w)) + return type_error( + "can't multiply sequence with non-int"); + return (*m->sq_repeat)(v, (int)PyInt_AsLong(w)); + } + return type_error("bad operand type(s) for *"); +} + +PyObject * +PyNumber_Divide(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__div__", "__rdiv__", PyNumber_Divide); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_divide) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for /"); +} + +PyObject * +PyNumber_Remainder(v, w) + PyObject *v, *w; +{ + if (PyString_Check(v)) + return PyString_Format(v, w); + BINOP(v, w, "__mod__", "__rmod__", PyNumber_Remainder); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_remainder) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for %"); +} + +PyObject * +PyNumber_Divmod(v, w) + PyObject *v, *w; +{ + BINOP(v, w, "__divmod__", "__rdivmod__", PyNumber_Divmod); + if (v->ob_type->tp_as_number != NULL) { + PyObject *x = NULL; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *)); + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_divmod) != NULL) + x = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (f != NULL) + return x; + } + return type_error("bad operand type(s) for divmod()"); +} + +/* Power (binary or ternary) */ + +static PyObject * +do_pow(v, w) + PyObject *v, *w; +{ + PyObject *res; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *, PyObject *)); + BINOP(v, w, "__pow__", "__rpow__", do_pow); + if (v->ob_type->tp_as_number == NULL || + w->ob_type->tp_as_number == NULL) { + PyErr_SetString(PyExc_TypeError, + "pow(x, y) requires numeric arguments"); + return NULL; + } + if ( +#ifndef WITHOUT_COMPLEX + !PyComplex_Check(v) && +#endif + PyFloat_Check(w) && PyFloat_AsDouble(v) < 0.0) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_ValueError, + "negative number to float power"); + return NULL; + } + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + if ((f = v->ob_type->tp_as_number->nb_power) != NULL) + res = (*f)(v, w, Py_None); + else + res = type_error("pow(x, y) not defined for these operands"); + Py_DECREF(v); + Py_DECREF(w); + return res; +} + +PyObject * +PyNumber_Power(v, w, z) + PyObject *v, *w, *z; +{ + PyObject *res; + PyObject *v1, *z1, *w2, *z2; + PyObject * (*f) Py_FPROTO((PyObject *, PyObject *, PyObject *)); + + if (z == Py_None) + return do_pow(v, w); + /* XXX The ternary version doesn't do class instance coercions */ + if (PyInstance_Check(v)) + return v->ob_type->tp_as_number->nb_power(v, w, z); + if (v->ob_type->tp_as_number == NULL || + z->ob_type->tp_as_number == NULL || + w->ob_type->tp_as_number == NULL) { + return type_error("pow(x, y, z) requires numeric arguments"); + } + if (PyNumber_Coerce(&v, &w) != 0) + return NULL; + res = NULL; + v1 = v; + z1 = z; + if (PyNumber_Coerce(&v1, &z1) != 0) + goto error2; + w2 = w; + z2 = z1; + if (PyNumber_Coerce(&w2, &z2) != 0) + goto error1; + if ((f = v1->ob_type->tp_as_number->nb_power) != NULL) + res = (*f)(v1, w2, z2); + else + res = type_error( + "pow(x, y, z) not defined for these operands"); + Py_DECREF(w2); + Py_DECREF(z2); + error1: + Py_DECREF(v1); + Py_DECREF(z1); + error2: + Py_DECREF(v); + Py_DECREF(w); + return res; +} + +/* Unary operators and functions */ + +PyObject * +PyNumber_Negative(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_negative) + return (*m->nb_negative)(o); + + return type_error("bad operand type for unary -"); +} + +PyObject * +PyNumber_Positive(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_positive) + return (*m->nb_positive)(o); + + return type_error("bad operand type for unary +"); +} + +PyObject * +PyNumber_Invert(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_invert) + return (*m->nb_invert)(o); + + return type_error("bad operand type for unary ~"); +} + +PyObject * +PyNumber_Absolute(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_absolute) + return m->nb_absolute(o); + + return type_error("bad operand type for abs()"); +} + +PyObject * +PyNumber_Int(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + if (PyString_Check(o)) + return int_from_string(o); + m = o->ob_type->tp_as_number; + if (m && m->nb_int) + return m->nb_int(o); + + return type_error("object can't be converted to int"); +} + +PyObject * +PyNumber_Long(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + if (PyString_Check(o)) + return long_from_string(o); + m = o->ob_type->tp_as_number; + if (m && m->nb_long) + return m->nb_long(o); + + return type_error("object can't be converted to long"); +} + +PyObject * +PyNumber_Float(o) + PyObject *o; +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + if (PyString_Check(o)) + return float_from_string(o); + m = o->ob_type->tp_as_number; + if (m && m->nb_float) + return m->nb_float(o); + + return type_error("object can't be converted to float"); +} + +/* Operations on sequences */ + +int +PySequence_Check(s) + PyObject *s; +{ + return s != NULL && s->ob_type->tp_as_sequence; +} + +int +PySequence_Length(s) + PyObject *s; +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_length) + return m->sq_length(s); + + type_error("len() of unsized object"); + return -1; +} + +PyObject * +PySequence_Concat(s, o) + PyObject *s; + PyObject *o; +{ + PySequenceMethods *m; + + if (s == NULL || o == NULL) + return null_error(); + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_concat) + return m->sq_concat(s, o); + + return type_error("object can't be concatenated"); +} + +PyObject * +PySequence_Repeat(o, count) + PyObject *o; + int count; +{ + PySequenceMethods *m; + + if (o == NULL) + return null_error(); + + m = o->ob_type->tp_as_sequence; + if (m && m->sq_repeat) + return m->sq_repeat(o, count); + + return type_error("object can't be repeated"); +} + +PyObject * +PySequence_GetItem(s, i) + PyObject *s; + int i; +{ + PySequenceMethods *m; + + if (s == NULL) + return null_error(); + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_item) { + if (i < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return NULL; + i += l; + } + } + return m->sq_item(s, i); + } + + return type_error("unindexable object"); +} + +PyObject * +PySequence_GetSlice(s, i1, i2) + PyObject *s; + int i1; + int i2; +{ + PySequenceMethods *m; + + if (!s) return null_error(); + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_slice) { + if (i1 < 0 || i2 < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return NULL; + if (i1 < 0) + i1 += l; + if (i2 < 0) + i2 += l; + } + } + return m->sq_slice(s, i1, i2); + } + + return type_error("unsliceable object"); +} + +int +PySequence_SetItem(s, i, o) + PyObject *s; + int i; + PyObject *o; +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_item) { + if (i < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return -1; + i += l; + } + } + return m->sq_ass_item(s, i, o); + } + + type_error("object doesn't support item assignment"); + return -1; +} + +int +PySequence_DelItem(s, i) + PyObject *s; + int i; +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_item) { + if (i < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return -1; + i += l; + } + } + return m->sq_ass_item(s, i, (PyObject *)NULL); + } + + type_error("object doesn't support item deletion"); + return -1; +} + +int +PySequence_SetSlice(s, i1, i2, o) + PyObject *s; + int i1; + int i2; + PyObject *o; +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_slice) { + if (i1 < 0 || i2 < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return -1; + if (i1 < 0) + i1 += l; + if (i2 < 0) + i2 += l; + } + } + return m->sq_ass_slice(s, i1, i2, o); + } + type_error("object doesn't support slice assignment"); + return -1; +} + +int +PySequence_DelSlice(s, i1, i2) + PyObject *s; + int i1; + int i2; +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_slice) { + if (i1 < 0 || i2 < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return -1; + if (i1 < 0) + i1 += l; + if (i2 < 0) + i2 += l; + } + } + return m->sq_ass_slice(s, i1, i2, (PyObject *)NULL); + } + type_error("object doesn't support slice deletion"); + return -1; +} + +PyObject * +PySequence_Tuple(v) + PyObject *v; +{ + PySequenceMethods *m; + + if (v == NULL) + return null_error(); + + if (PyTuple_Check(v)) { + Py_INCREF(v); + return v; + } + + if (PyList_Check(v)) + return PyList_AsTuple(v); + + if (PyString_Check(v)) { + int n = PyString_Size(v); + PyObject *t = PyTuple_New(n); + if (t != NULL) { + int i; + char *p = PyString_AsString(v); + for (i = 0; i < n; i++) { + PyObject *item = + PyString_FromStringAndSize(p+i, 1); + if (item == NULL) { + Py_DECREF(t); + t = NULL; + break; + } + PyTuple_SetItem(t, i, item); + } + } + return t; + } + + /* Generic sequence object */ + m = v->ob_type->tp_as_sequence; + if (m && m->sq_item) { + /* XXX Should support indefinite-length sequences */ + int i; + PyObject *t; + int n = PySequence_Length(v); + if (n < 0) + return NULL; + t = PyTuple_New(n); + if (t == NULL) + return NULL; + for (i = 0; i < n; i++) { + PyObject *item = (*m->sq_item)(v, i); + if (item == NULL) { + Py_DECREF(t); + t = NULL; + break; + } + PyTuple_SetItem(t, i, item); + } + return t; + } + + /* None of the above */ + return type_error("tuple() argument must be a sequence"); +} + +PyObject * +PySequence_List(v) + PyObject *v; +{ + PySequenceMethods *m; + + m = v->ob_type->tp_as_sequence; + if (m && m->sq_item) { + /* XXX Should support indefinite-length sequences */ + int i; + PyObject *l; + int n = PySequence_Length(v); + if (n < 0) + return NULL; + l = PyList_New(n); + if (l == NULL) + return NULL; + for (i = 0; i < n; i++) { + PyObject *item = (*m->sq_item)(v, i); + if (item == NULL) { + Py_DECREF(l); + l = NULL; + break; + } + PyList_SetItem(l, i, item); + } + return l; + } + return type_error("list() argument must be a sequence"); +} + +int +PySequence_Count(s, o) + PyObject *s; + PyObject *o; +{ + int l, i, n, cmp, err; + PyObject *item; + + if (s == NULL || o == NULL) { + null_error(); + return -1; + } + + l = PySequence_Length(s); + if (l < 0) + return -1; + + n = 0; + for (i = 0; i < l; i++) { + item = PySequence_GetItem(s, i); + if (item == NULL) + return -1; + err = PyObject_Cmp(item, o, &cmp); + Py_DECREF(item); + if (err < 0) + return err; + if (cmp == 0) + n++; + } + return n; +} + +int +PySequence_Contains(w, v) /* v in w */ + PyObject *w; + PyObject *v; +{ + int i, cmp; + PyObject *x; + PySequenceMethods *sq; + + /* Special case for char in string */ + if (PyString_Check(w)) { + register char *s, *end; + register char c; + if (!PyString_Check(v) || PyString_Size(v) != 1) { + PyErr_SetString(PyExc_TypeError, + "string member test needs char left operand"); + return -1; + } + c = PyString_AsString(v)[0]; + s = PyString_AsString(w); + end = s + PyString_Size(w); + while (s < end) { + if (c == *s++) + return 1; + } + return 0; + } + + sq = w->ob_type->tp_as_sequence; + if (sq == NULL || sq->sq_item == NULL) { + PyErr_SetString(PyExc_TypeError, + "'in' or 'not in' needs sequence right argument"); + return -1; + } + + for (i = 0; ; i++) { + x = (*sq->sq_item)(w, i); + if (x == NULL) { + if (PyErr_ExceptionMatches(PyExc_IndexError)) { + PyErr_Clear(); + break; + } + return -1; + } + cmp = PyObject_Compare(v, x); + Py_XDECREF(x); + if (cmp == 0) + return 1; + if (PyErr_Occurred()) + return -1; + } + + return 0; +} + +/* Backwards compatibility */ +#undef PySequence_In +int +PySequence_In(w, v) + PyObject *w; + PyObject *v; +{ + return PySequence_Contains(w, v); +} + +int +PySequence_Index(s, o) + PyObject *s; + PyObject *o; +{ + int l, i, cmp, err; + PyObject *item; + + if (s == NULL || o == NULL) { + null_error(); + return -1; + } + + l = PySequence_Length(s); + if (l < 0) + return -1; + + for (i = 0; i < l; i++) { + item = PySequence_GetItem(s, i); + if (item == NULL) + return -1; + err = PyObject_Cmp(item, o, &cmp); + Py_DECREF(item); + if (err < 0) + return err; + if (cmp == 0) + return i; + } + + PyErr_SetString(PyExc_ValueError, "sequence.index(x): x not in list"); + return -1; +} + +/* Operations on mappings */ + +int +PyMapping_Check(o) + PyObject *o; +{ + return o && o->ob_type->tp_as_mapping; +} + +int +PyMapping_Length(o) + PyObject *o; +{ + PyMappingMethods *m; + + if (o == NULL) { + null_error(); + return -1; + } + + m = o->ob_type->tp_as_mapping; + if (m && m->mp_length) + return m->mp_length(o); + + type_error("len() of unsized object"); + return -1; +} + +PyObject * +PyMapping_GetItemString(o, key) + PyObject *o; + char *key; +{ + PyObject *okey, *r; + + if (key == NULL) + return null_error(); + + okey = PyString_FromString(key); + if (okey == NULL) + return NULL; + r = PyObject_GetItem(o, okey); + Py_DECREF(okey); + return r; +} + +int +PyMapping_SetItemString(o, key, value) + PyObject *o; + char *key; + PyObject *value; +{ + PyObject *okey; + int r; + + if (key == NULL) { + null_error(); + return -1; + } + + okey = PyString_FromString(key); + if (okey == NULL) + return -1; + r = PyObject_SetItem(o, okey, value); + Py_DECREF(okey); + return r; +} + +int +PyMapping_HasKeyString(o, key) + PyObject *o; + char *key; +{ + PyObject *v; + + v = PyMapping_GetItemString(o, key); + if (v) { + Py_DECREF(v); + return 1; + } + PyErr_Clear(); + return 0; +} + +int +PyMapping_HasKey(o, key) + PyObject *o; + PyObject *key; +{ + PyObject *v; + + v = PyObject_GetItem(o, key); + if (v) { + Py_DECREF(v); + return 1; + } + PyErr_Clear(); + return 0; +} + +/* Operations on callable objects */ + +/* XXX PyCallable_Check() is in object.c */ + +PyObject * +PyObject_CallObject(o, a) + PyObject *o, *a; +{ + PyObject *r; + PyObject *args = a; + + if (args == NULL) { + args = PyTuple_New(0); + if (args == NULL) + return NULL; + } + + r = PyEval_CallObject(o, args); + + if (args != a) { + Py_DECREF(args); + } + + return r; +} + +PyObject * +#ifdef HAVE_STDARG_PROTOTYPES +/* VARARGS 2 */ +PyObject_CallFunction(PyObject *callable, char *format, ...) +#else +/* VARARGS */ + PyObject_CallFunction(va_alist) va_dcl +#endif +{ + va_list va; + PyObject *args, *retval; +#ifdef HAVE_STDARG_PROTOTYPES + va_start(va, format); +#else + PyObject *callable; + char *format; + va_start(va); + callable = va_arg(va, PyObject *); + format = va_arg(va, char *); +#endif + + if (callable == NULL) { + va_end(va); + return null_error(); + } + + if (format) + args = Py_VaBuildValue(format, va); + else + args = PyTuple_New(0); + + va_end(va); + + if (args == NULL) + return NULL; + + if (!PyTuple_Check(args)) { + PyObject *a; + + a = PyTuple_New(1); + if (a == NULL) + return NULL; + if (PyTuple_SetItem(a, 0, args) < 0) + return NULL; + args = a; + } + retval = PyObject_CallObject(callable, args); + + Py_DECREF(args); + + return retval; +} + +PyObject * +#ifdef HAVE_STDARG_PROTOTYPES +/* VARARGS 2 */ +PyObject_CallMethod(PyObject *o, char *name, char *format, ...) +#else +/* VARARGS */ + PyObject_CallMethod(va_alist) va_dcl +#endif +{ + va_list va; + PyObject *args, *func = 0, *retval; +#ifdef HAVE_STDARG_PROTOTYPES + va_start(va, format); +#else + PyObject *o; + char *name; + char *format; + va_start(va); + o = va_arg(va, PyObject *); + name = va_arg(va, char *); + format = va_arg(va, char *); +#endif + + if (o == NULL || name == NULL) { + va_end(va); + return null_error(); + } + + func = PyObject_GetAttrString(o, name); + if (func == NULL) { + va_end(va); + PyErr_SetString(PyExc_AttributeError, name); + return 0; + } + + if (!PyCallable_Check(func)) { + va_end(va); + return type_error("call of non-callable attribute"); + } + + if (format && *format) + args = Py_VaBuildValue(format, va); + else + args = PyTuple_New(0); + + va_end(va); + + if (!args) + return NULL; + + if (!PyTuple_Check(args)) { + PyObject *a; + + a = PyTuple_New(1); + if (a == NULL) + return NULL; + if (PyTuple_SetItem(a, 0, args) < 0) + return NULL; + args = a; + } + + retval = PyObject_CallObject(func, args); + + Py_DECREF(args); + Py_DECREF(func); + + return retval; +} diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_395434_d3a0db_ext#date#lib#parse_tz.c b/src/main/resource/testFiles/php-src/prevFiles/prev_395434_d3a0db_ext#date#lib#parse_tz.c new file mode 100644 index 0000000..a892930 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_395434_d3a0db_ext#date#lib#parse_tz.c @@ -0,0 +1,384 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2008 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Derick Rethans | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "timelib.h" + +#include + +#ifdef HAVE_STRING_H +#include +#else +#include +#endif +#include "timezonedb.h" + +#if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__)) +# if defined(__LITTLE_ENDIAN__) +# undef WORDS_BIGENDIAN +# else +# if defined(__BIG_ENDIAN__) +# define WORDS_BIGENDIAN +# endif +# endif +#endif + +#ifdef WORDS_BIGENDIAN +#define timelib_conv_int(l) (l) +#else +#define timelib_conv_int(l) ((l & 0x000000ff) << 24) + ((l & 0x0000ff00) << 8) + ((l & 0x00ff0000) >> 8) + ((l & 0xff000000) >> 24) +#endif + +static void read_header(char **tzf, timelib_tzinfo *tz) +{ + uint32_t buffer[6]; + + memcpy(&buffer, *tzf, sizeof(buffer)); + tz->ttisgmtcnt = timelib_conv_int(buffer[0]); + tz->ttisstdcnt = timelib_conv_int(buffer[1]); + tz->leapcnt = timelib_conv_int(buffer[2]); + tz->timecnt = timelib_conv_int(buffer[3]); + tz->typecnt = timelib_conv_int(buffer[4]); + tz->charcnt = timelib_conv_int(buffer[5]); + *tzf += sizeof(buffer); +} + +static void read_transistions(char **tzf, timelib_tzinfo *tz) +{ + int32_t *buffer = NULL; + uint32_t i; + unsigned char *cbuffer = NULL; + + if (tz->timecnt) { + buffer = (int32_t*) malloc(tz->timecnt * sizeof(int32_t)); + if (!buffer) { + return; + } + memcpy(buffer, *tzf, sizeof(int32_t) * tz->timecnt); + *tzf += (sizeof(int32_t) * tz->timecnt); + for (i = 0; i < tz->timecnt; i++) { + buffer[i] = timelib_conv_int(buffer[i]); + } + + cbuffer = (unsigned char*) malloc(tz->timecnt * sizeof(unsigned char)); + if (!cbuffer) { + return; + } + memcpy(cbuffer, *tzf, sizeof(unsigned char) * tz->timecnt); + *tzf += sizeof(unsigned char) * tz->timecnt; + } + + tz->trans = buffer; + tz->trans_idx = cbuffer; +} + +static void read_types(char **tzf, timelib_tzinfo *tz) +{ + unsigned char *buffer; + int32_t *leap_buffer; + unsigned int i, j; + + buffer = (unsigned char*) malloc(tz->typecnt * sizeof(unsigned char) * 6); + if (!buffer) { + return; + } + memcpy(buffer, *tzf, sizeof(unsigned char) * 6 * tz->typecnt); + *tzf += sizeof(unsigned char) * 6 * tz->typecnt; + + tz->type = (ttinfo*) malloc(tz->typecnt * sizeof(struct ttinfo)); + if (!tz->type) { + return; + } + + for (i = 0; i < tz->typecnt; i++) { + j = i * 6; + tz->type[i].offset = (buffer[j] * 16777216) + (buffer[j + 1] * 65536) + (buffer[j + 2] * 256) + buffer[j + 3]; + tz->type[i].isdst = buffer[j + 4]; + tz->type[i].abbr_idx = buffer[j + 5]; + } + free(buffer); + + tz->timezone_abbr = (char*) malloc(tz->charcnt); + if (!tz->timezone_abbr) { + return; + } + memcpy(tz->timezone_abbr, *tzf, sizeof(char) * tz->charcnt); + *tzf += sizeof(char) * tz->charcnt; + + leap_buffer = (int32_t *) malloc(tz->leapcnt * 2 * sizeof(int32_t)); + if (!leap_buffer) { + return; + } + memcpy(leap_buffer, *tzf, sizeof(int32_t) * tz->leapcnt * 2); + *tzf += sizeof(int32_t) * tz->leapcnt * 2; + + tz->leap_times = (tlinfo*) malloc(tz->leapcnt * sizeof(tlinfo)); + if (!tz->leap_times) { + return; + } + for (i = 0; i < tz->leapcnt; i++) { + tz->leap_times[i].trans = timelib_conv_int(leap_buffer[i * 2]); + tz->leap_times[i].offset = timelib_conv_int(leap_buffer[i * 2 + 1]); + } + free(leap_buffer); + + buffer = (unsigned char*) malloc(tz->ttisstdcnt * sizeof(unsigned char)); + if (!buffer) { + return; + } + memcpy(buffer, *tzf, sizeof(unsigned char) * tz->ttisstdcnt); + *tzf += sizeof(unsigned char) * tz->ttisstdcnt; + + for (i = 0; i < tz->ttisstdcnt; i++) { + tz->type[i].isstdcnt = buffer[i]; + } + free(buffer); + + buffer = (unsigned char*) malloc(tz->ttisgmtcnt * sizeof(unsigned char)); + if (!buffer) { + return; + } + memcpy(buffer, *tzf, sizeof(unsigned char) * tz->ttisgmtcnt); + *tzf += sizeof(unsigned char) * tz->ttisgmtcnt; + + for (i = 0; i < tz->ttisgmtcnt; i++) { + tz->type[i].isgmtcnt = buffer[i]; + } + free(buffer); +} + +void timelib_dump_tzinfo(timelib_tzinfo *tz) +{ + uint32_t i; + + printf("UTC/Local count: %lu\n", (unsigned long) tz->ttisgmtcnt); + printf("Std/Wall count: %lu\n", (unsigned long) tz->ttisstdcnt); + printf("Leap.sec. count: %lu\n", (unsigned long) tz->leapcnt); + printf("Trans. count: %lu\n", (unsigned long) tz->timecnt); + printf("Local types count: %lu\n", (unsigned long) tz->typecnt); + printf("Zone Abbr. count: %lu\n", (unsigned long) tz->charcnt); + + printf ("%8s (%12s) = %3d [%5ld %1d %3d '%s' (%d,%d)]\n", + "", "", 0, + (long int) tz->type[0].offset, + tz->type[0].isdst, + tz->type[0].abbr_idx, + &tz->timezone_abbr[tz->type[0].abbr_idx], + tz->type[0].isstdcnt, + tz->type[0].isgmtcnt + ); + for (i = 0; i < tz->timecnt; i++) { + printf ("%08X (%12d) = %3d [%5ld %1d %3d '%s' (%d,%d)]\n", + tz->trans[i], tz->trans[i], tz->trans_idx[i], + (long int) tz->type[tz->trans_idx[i]].offset, + tz->type[tz->trans_idx[i]].isdst, + tz->type[tz->trans_idx[i]].abbr_idx, + &tz->timezone_abbr[tz->type[tz->trans_idx[i]].abbr_idx], + tz->type[tz->trans_idx[i]].isstdcnt, + tz->type[tz->trans_idx[i]].isgmtcnt + ); + } + for (i = 0; i < tz->leapcnt; i++) { + printf ("%08X (%12ld) = %d\n", + tz->leap_times[i].trans, + (long) tz->leap_times[i].trans, + tz->leap_times[i].offset); + } +} + +static int seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb) +{ + int left = 0, right = tzdb->index_size - 1; + + do { + int mid = ((unsigned)left + right) >> 1; + int cmp = strcasecmp(timezone, tzdb->index[mid].id); + + if (cmp < 0) { + right = mid - 1; + } else if (cmp > 0) { + left = mid + 1; + } else { /* (cmp == 0) */ + (*tzf) = &(tzdb->data[tzdb->index[mid].pos + 20]); + return 1; + } + + } while (left <= right); + + return 0; +} + +const timelib_tzdb *timelib_builtin_db(void) +{ + return &timezonedb_builtin; +} + +const timelib_tzdb_index_entry *timelib_timezone_builtin_identifiers_list(int *count) +{ + *count = sizeof(timezonedb_idx_builtin) / sizeof(*timezonedb_idx_builtin); + return timezonedb_idx_builtin; +} + +int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb) +{ + const unsigned char *tzf; + return (seek_to_tz_position(&tzf, timezone, tzdb)); +} + +timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb) +{ + const unsigned char *tzf; + timelib_tzinfo *tmp; + + if (seek_to_tz_position(&tzf, timezone, tzdb)) { + tmp = timelib_tzinfo_ctor(timezone); + + read_header((char**) &tzf, tmp); + read_transistions((char**) &tzf, tmp); + read_types((char**) &tzf, tmp); + } else { + tmp = NULL; + } + + return tmp; +} + +static ttinfo* fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time) +{ + uint32_t i; + + /* If there is no transistion time, we pick the first one, if that doesn't + * exist we return NULL */ + if (!tz->timecnt || !tz->trans) { + *transition_time = 0; + if (tz->typecnt == 1) { + return &(tz->type[0]); + } + return NULL; + } + + /* If the TS is lower than the first transistion time, then we scan over + * all the transistion times to find the first non-DST one, or the first + * one in case there are only DST entries. Not sure which smartass came up + * with this idea in the first though :) */ + if (ts < tz->trans[0]) { + uint32_t j; + + *transition_time = 0; + j = 0; + while (j < tz->timecnt && tz->type[j].isdst) { + ++j; + } + if (j == tz->timecnt) { + j = 0; + } + return &(tz->type[j]); + } + + /* In all other cases we loop through the available transtion times to find + * the correct entry */ + for (i = 0; i < tz->timecnt; i++) { + if (ts < tz->trans[i]) { + *transition_time = tz->trans[i - 1]; + return &(tz->type[tz->trans_idx[i - 1]]); + } + } + *transition_time = tz->trans[tz->timecnt - 1]; + return &(tz->type[tz->trans_idx[tz->timecnt - 1]]); +} + +static tlinfo* fetch_leaptime_offset(timelib_tzinfo *tz, timelib_sll ts) +{ + int i; + + if (!tz->leapcnt || !tz->leap_times) { + return NULL; + } + + for (i = tz->leapcnt - 1; i > 0; i--) { + if (ts > tz->leap_times[i].trans) { + return &(tz->leap_times[i]); + } + } + return NULL; +} + +int timelib_timestamp_is_in_dst(timelib_sll ts, timelib_tzinfo *tz) +{ + ttinfo *to; + timelib_sll dummy; + + if ((to = fetch_timezone_offset(tz, ts, &dummy))) { + return to->isdst; + } + return -1; +} + +timelib_time_offset *timelib_get_time_zone_info(timelib_sll ts, timelib_tzinfo *tz) +{ + ttinfo *to; + tlinfo *tl; + int32_t offset = 0, leap_secs = 0; + char *abbr; + timelib_time_offset *tmp = timelib_time_offset_ctor(); + timelib_sll transistion_time; + + if ((to = fetch_timezone_offset(tz, ts, &transistion_time))) { + offset = to->offset; + abbr = &(tz->timezone_abbr[to->abbr_idx]); + tmp->is_dst = to->isdst; + tmp->transistion_time = transistion_time; + } else { + offset = 0; + abbr = tz->timezone_abbr; + tmp->is_dst = 0; + tmp->transistion_time = 0; + } + + if ((tl = fetch_leaptime_offset(tz, ts))) { + leap_secs = -tl->offset; + } + + tmp->offset = offset; + tmp->leap_secs = leap_secs; + tmp->abbr = abbr ? strdup(abbr) : strdup("GMT"); + + return tmp; +} + +timelib_sll timelib_get_current_offset(timelib_time *t) +{ + timelib_time_offset *gmt_offset; + timelib_sll retval; + + switch (t->zone_type) { + case TIMELIB_ZONETYPE_ABBR: + case TIMELIB_ZONETYPE_OFFSET: + return t->z * 60; + + case TIMELIB_ZONETYPE_ID: + gmt_offset = timelib_get_time_zone_info(t->sse, t->tz_info); + retval = gmt_offset->offset; + timelib_time_offset_dtor(gmt_offset); + return retval; + + default: + return 0; + } +} diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_470a39_b38730_Zend#zend_stream.c b/src/main/resource/testFiles/php-src/prevFiles/prev_470a39_b38730_Zend#zend_stream.c new file mode 100644 index 0000000..a514751 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_470a39_b38730_Zend#zend_stream.c @@ -0,0 +1,333 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2008 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Wez Furlong | + | Scott MacVicar | + | Nuno Lopes | + | Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + + +#include "zend.h" +#include "zend_compile.h" + +#include +#include +#if HAVE_SYS_MMAN_H +# include +#endif + +ZEND_DLIMPORT int isatty(int fd); + +static size_t zend_stream_stdio_reader(void *handle, char *buf, size_t len TSRMLS_DC) /* {{{ */ +{ + return fread(buf, 1, len, (FILE*)handle); +} +/* }}} */ + +static void zend_stream_stdio_closer(void *handle TSRMLS_DC) /* {{{ */ +{ + if (handle && (FILE*)handle != stdin) { + fclose((FILE*)handle); + } +} +/* }}} */ + +static size_t zend_stream_stdio_fsizer(void *handle TSRMLS_DC) /* {{{ */ +{ + struct stat buf; + if (handle && fstat(fileno((FILE*)handle), &buf) == 0) { + return buf.st_size; + } + return 0; +} +/* }}} */ + +static void zend_stream_unmap(zend_stream *stream TSRMLS_DC) { /* {{{ */ +#if HAVE_MMAP + if (stream->mmap.map) { + munmap(stream->mmap.map, stream->mmap.len); + } else +#endif + if (stream->mmap.buf) { + efree(stream->mmap.buf); + } + stream->mmap.len = 0; + stream->mmap.pos = 0; + stream->mmap.map = 0; + stream->mmap.buf = 0; + stream->handle = stream->mmap.old_handle; +} /* }}} */ + +static void zend_stream_mmap_closer(zend_stream *stream TSRMLS_DC) /* {{{ */ +{ + zend_stream_unmap(stream TSRMLS_CC); + if (stream->mmap.old_closer && stream->handle) { + stream->mmap.old_closer(stream->handle TSRMLS_CC); + } +} /* }}} */ + +static inline int zend_stream_is_mmap(zend_file_handle *file_handle) { /* {{{ */ + return file_handle->type == ZEND_HANDLE_MAPPED; +} /* }}} */ + +static size_t zend_stream_fsize(zend_file_handle *file_handle TSRMLS_DC) /* {{{ */ +{ + struct stat buf; + + if (zend_stream_is_mmap(file_handle)) { + return file_handle->handle.stream.mmap.len; + } + if (file_handle->type == ZEND_HANDLE_STREAM || file_handle->type == ZEND_HANDLE_MAPPED) { + return file_handle->handle.stream.fsizer(file_handle->handle.stream.handle TSRMLS_CC); + } + if (file_handle->handle.fp && fstat(fileno(file_handle->handle.fp), &buf) == 0) { + return buf.st_size; + } + + return -1; +} /* }}} */ + +ZEND_API int zend_stream_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* {{{ */ +{ + if (zend_stream_open_function) { + return zend_stream_open_function(filename, handle TSRMLS_CC); + } + handle->type = ZEND_HANDLE_FP; + handle->opened_path = NULL; + handle->handle.fp = zend_fopen(filename, &handle->opened_path TSRMLS_CC); + handle->filename = (char *)filename; + handle->free_filename = 0; + memset(&handle->handle.stream.mmap, 0, sizeof(zend_mmap)); + + return (handle->handle.fp) ? SUCCESS : FAILURE; +} +/* }}} */ + +ZEND_API int zend_stream_getc(zend_file_handle *file_handle TSRMLS_DC) /* {{{ */ +{ + char buf; + if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf) TSRMLS_CC)) { + return (int)buf; + } + return EOF; +} +/* }}} */ + +static size_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len TSRMLS_DC) /* {{{ */ +{ + if (!zend_stream_is_mmap(file_handle) && file_handle->handle.stream.isatty) { + int c = '*'; + size_t n; + +#ifdef NETWARE + /* + c != 4 check is there as fread of a character in NetWare LibC gives 4 upon ^D character. + Ascii value 4 is actually EOT character which is not defined anywhere in the LibC + or else we can use instead of hardcoded 4. + */ + for (n = 0; n < len && (c = zend_stream_getc(file_handle TSRMLS_CC)) != EOF && c != 4 && c != '\n'; ++n) { +#else + for (n = 0; n < len && (c = zend_stream_getc(file_handle TSRMLS_CC)) != EOF && c != '\n'; ++n) { +#endif + buf[n] = (char)c; + } + if (c == '\n') { + buf[n++] = (char)c; + } + + return n; + } + return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len TSRMLS_CC); +} +/* }}} */ + +ZEND_API int zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len TSRMLS_DC) /* {{{ */ +{ + size_t size; + zend_stream_type old_type; + + if (file_handle->type == ZEND_HANDLE_FILENAME) { + if (zend_stream_open(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) { + return FAILURE; + } + } + + switch (file_handle->type) { + case ZEND_HANDLE_FD: + file_handle->type = ZEND_HANDLE_FP; + file_handle->handle.fp = fdopen(file_handle->handle.fd, "rb"); + /* no break; */ + case ZEND_HANDLE_FP: + if (!file_handle->handle.fp) { + return FAILURE; + } + memset(&file_handle->handle.stream.mmap, 0, sizeof(zend_mmap)); + file_handle->handle.stream.isatty = isatty(fileno((FILE *)file_handle->handle.stream.handle)) ? 1 : 0; + file_handle->handle.stream.reader = (zend_stream_reader_t)zend_stream_stdio_reader; + file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_stdio_closer; + file_handle->handle.stream.fsizer = (zend_stream_fsizer_t)zend_stream_stdio_fsizer; + memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap)); + /* no break; */ + case ZEND_HANDLE_STREAM: + /* nothing to do */ + break; + + case ZEND_HANDLE_MAPPED: + file_handle->handle.stream.mmap.pos = 0; + *buf = file_handle->handle.stream.mmap.buf; + *len = file_handle->handle.stream.mmap.len; + return SUCCESS; + + default: + return FAILURE; + } + + size = zend_stream_fsize(file_handle TSRMLS_CC); + if (size == (size_t)-1) { + return FAILURE; + } + + old_type = file_handle->type; + file_handle->type = ZEND_HANDLE_STREAM; /* we might still be _FP but we need fsize() work */ + + if (old_type == ZEND_HANDLE_FP && !file_handle->handle.stream.isatty && size) { +#if HAVE_MMAP + if (file_handle->handle.fp) { + /* *buf[size] is zeroed automatically by the kernel */ + *buf = mmap(0, size + ZEND_MMAP_AHEAD, PROT_READ, MAP_PRIVATE, fileno(file_handle->handle.fp), 0); + if (*buf != MAP_FAILED) { + file_handle->handle.stream.mmap.len = size; + file_handle->handle.stream.mmap.map = *buf; + file_handle->handle.stream.mmap.buf = *buf; + goto return_mapped; + } + } +#endif + file_handle->handle.stream.mmap.map = 0; + file_handle->handle.stream.mmap.buf = *buf = safe_emalloc(1, size, ZEND_MMAP_AHEAD); + file_handle->handle.stream.mmap.len = zend_stream_read(file_handle, *buf, size TSRMLS_CC); + } else { + size_t read, remain = 4*1024; + *buf = emalloc(remain); + size = 0; + + while ((read = zend_stream_read(file_handle, *buf + size, remain TSRMLS_CC)) > 0) { + size += read; + remain -= read; + + if (remain == 0) { + *buf = safe_erealloc(*buf, size, 2, 0); + remain = size; + } + } + file_handle->handle.stream.mmap.map = 0; + file_handle->handle.stream.mmap.len = size; + if (size && remain < ZEND_MMAP_AHEAD) { + *buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD); + } + file_handle->handle.stream.mmap.buf = *buf; + } + + if (file_handle->handle.stream.mmap.len == 0) { + *buf = erealloc(*buf, ZEND_MMAP_AHEAD); + file_handle->handle.stream.mmap.buf = *buf; + } + + if (ZEND_MMAP_AHEAD) { + memset(file_handle->handle.stream.mmap.buf + file_handle->handle.stream.mmap.len, 0, ZEND_MMAP_AHEAD); + } + +return_mapped: + file_handle->type = ZEND_HANDLE_MAPPED; + file_handle->handle.stream.mmap.pos = 0; + file_handle->handle.stream.mmap.old_handle = file_handle->handle.stream.handle; + file_handle->handle.stream.mmap.old_closer = file_handle->handle.stream.closer; + file_handle->handle.stream.handle = &file_handle->handle.stream; + file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_mmap_closer; + + *buf = file_handle->handle.stream.mmap.buf; + *len = file_handle->handle.stream.mmap.len; + + return SUCCESS; +} +/* }}} */ + +ZEND_API void zend_file_handle_dtor(zend_file_handle *fh TSRMLS_DC) /* {{{ */ + { + switch (fh->type) { + case ZEND_HANDLE_FD: + /* nothing to do */ + break; + case ZEND_HANDLE_FP: + fclose(fh->handle.fp); + break; + case ZEND_HANDLE_STREAM: + case ZEND_HANDLE_MAPPED: + if (fh->handle.stream.closer && fh->handle.stream.handle) { + fh->handle.stream.closer(fh->handle.stream.handle TSRMLS_CC); + } + fh->handle.stream.handle = NULL; + break; + case ZEND_HANDLE_FILENAME: + /* We're only supposed to get here when destructing the used_files hash, + * which doesn't really contain open files, but references to their names/paths + */ + break; + } + if (fh->opened_path) { + efree(fh->opened_path); + fh->opened_path = NULL; + } + if (fh->free_filename && fh->filename) { + efree(fh->filename); + fh->filename = NULL; + } +} +/* }}} */ + +ZEND_API int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */ + { + if (fh1->type != fh2->type) { + return 0; + } + switch (fh1->type) { + case ZEND_HANDLE_FD: + return fh1->handle.fd == fh2->handle.fd; + case ZEND_HANDLE_FP: + return fh1->handle.fp == fh2->handle.fp; + case ZEND_HANDLE_STREAM: + return fh1->handle.stream.handle == fh2->handle.stream.handle; + case ZEND_HANDLE_MAPPED: + return (fh1->handle.stream.handle == &fh1->handle.stream && + fh2->handle.stream.handle == &fh2->handle.stream && + fh1->handle.stream.mmap.old_handle == fh2->handle.stream.mmap.old_handle) + || fh1->handle.stream.handle == fh2->handle.stream.handle; + default: + return 0; + } + return 0; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_499b55_595741_Zend#zend_execute_API.c b/src/main/resource/testFiles/php-src/prevFiles/prev_499b55_595741_Zend#zend_execute_API.c new file mode 100644 index 0000000..7fb89bc --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_499b55_595741_Zend#zend_execute_API.c @@ -0,0 +1,1748 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2014 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include +#include + +#include "zend.h" +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_API.h" +#include "zend_stack.h" +#include "zend_constants.h" +#include "zend_extensions.h" +#include "zend_exceptions.h" +#include "zend_closures.h" +#include "zend_generators.h" +#include "zend_vm.h" +#include "zend_float.h" +#ifdef HAVE_SYS_TIME_H +#include +#endif + +ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data TSRMLS_DC); +ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data_ptr, zend_fcall_info *fci, int return_value_used TSRMLS_DC); + +/* true globals */ +ZEND_API const zend_fcall_info empty_fcall_info = { 0, NULL, {{0},0}, NULL, NULL, 0, NULL, NULL, 0 }; +ZEND_API const zend_fcall_info_cache empty_fcall_info_cache = { 0, NULL, NULL, NULL, NULL }; + +#ifdef ZEND_WIN32 +#include +static WNDCLASS wc; +static HWND timeout_window; +static HANDLE timeout_thread_event; +static HANDLE timeout_thread_handle; +static DWORD timeout_thread_id; +static int timeout_thread_initialized=0; +#endif + +#if 0&&ZEND_DEBUG +static void (*original_sigsegv_handler)(int); +static void zend_handle_sigsegv(int dummy) /* {{{ */ +{ + fflush(stdout); + fflush(stderr); + if (original_sigsegv_handler == zend_handle_sigsegv) { + signal(SIGSEGV, original_sigsegv_handler); + } else { + signal(SIGSEGV, SIG_DFL); + } + { + TSRMLS_FETCH(); + + fprintf(stderr, "SIGSEGV caught on opcode %d on opline %d of %s() at %s:%d\n\n", + active_opline->opcode, + active_opline-EG(active_op_array)->opcodes, + get_active_function_name(TSRMLS_C), + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C)); +/* See http://support.microsoft.com/kb/190351 */ +#ifdef PHP_WIN32 + fflush(stderr); +#endif + } + if (original_sigsegv_handler!=zend_handle_sigsegv) { + original_sigsegv_handler(dummy); + } +} +/* }}} */ +#endif + +static void zend_extension_activator(zend_extension *extension TSRMLS_DC) /* {{{ */ +{ + if (extension->activate) { + extension->activate(); + } +} +/* }}} */ + +static void zend_extension_deactivator(zend_extension *extension TSRMLS_DC) /* {{{ */ +{ + if (extension->deactivate) { + extension->deactivate(); + } +} +/* }}} */ + +static int clean_non_persistent_function(zend_function *function TSRMLS_DC) /* {{{ */ +{ + return (function->type == ZEND_INTERNAL_FUNCTION) ? ZEND_HASH_APPLY_STOP : ZEND_HASH_APPLY_REMOVE; +} +/* }}} */ + +static int clean_non_persistent_function_full(zend_function *function TSRMLS_DC) /* {{{ */ +{ + return (function->type == ZEND_INTERNAL_FUNCTION) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; +} +/* }}} */ + +static int clean_non_persistent_class(zend_class_entry **ce TSRMLS_DC) /* {{{ */ +{ + return ((*ce)->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY_STOP : ZEND_HASH_APPLY_REMOVE; +} +/* }}} */ + +static int clean_non_persistent_class_full(zend_class_entry **ce TSRMLS_DC) /* {{{ */ +{ + return ((*ce)->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; +} +/* }}} */ + +void init_executor(TSRMLS_D) /* {{{ */ +{ + zval tmp; + zend_init_fpu(TSRMLS_C); + + ZVAL_NULL(&EG(uninitialized_zval)); + /* trick to make uninitialized_zval never be modified, passed by ref, etc. */ + ZVAL_NULL(&EG(error_zval)); +/* destroys stack frame, therefore makes core dumps worthless */ +#if 0&&ZEND_DEBUG + original_sigsegv_handler = signal(SIGSEGV, zend_handle_sigsegv); +#endif + + EG(symtable_cache_ptr) = EG(symtable_cache) - 1; + EG(symtable_cache_limit) = EG(symtable_cache) + SYMTABLE_CACHE_SIZE - 1; + EG(no_extensions) = 0; + + EG(function_table) = CG(function_table); + EG(class_table) = CG(class_table); + + EG(in_execution) = 0; + EG(in_autoload) = NULL; + EG(autoload_func) = NULL; + EG(error_handling) = EH_NORMAL; + + zend_vm_stack_init(TSRMLS_C); + ZVAL_LONG(&tmp, 0); + zend_vm_stack_push(&tmp TSRMLS_CC); + + zend_hash_init(&EG(symbol_table).ht, 50, NULL, ZVAL_PTR_DTOR, 0); + EG(active_symbol_table) = &EG(symbol_table).ht; + + zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_activator TSRMLS_CC); + EG(opline_ptr) = NULL; + + zend_hash_init(&EG(included_files), 5, NULL, NULL, 0); + + EG(ticks_count) = 0; + + ZVAL_UNDEF(&EG(user_error_handler)); + + EG(current_execute_data) = NULL; + + zend_stack_init(&EG(user_error_handlers_error_reporting)); + zend_stack_init(&EG(user_error_handlers)); + zend_stack_init(&EG(user_exception_handlers)); + + zend_objects_store_init(&EG(objects_store), 1024); + + EG(full_tables_cleanup) = 0; +#ifdef ZEND_WIN32 + EG(timed_out) = 0; +#endif + + EG(exception) = NULL; + EG(prev_exception) = NULL; + + EG(scope) = NULL; + EG(called_scope) = NULL; + + ZVAL_UNDEF(&EG(This)); + + EG(active_op_array) = NULL; + + EG(active) = 1; + EG(start_op) = NULL; +} +/* }}} */ + +static int zval_call_destructor(zval *zv TSRMLS_DC) /* {{{ */ +{ + if (Z_TYPE_P(zv) == IS_OBJECT && Z_REFCOUNT_P(zv) == 1) { + return ZEND_HASH_APPLY_REMOVE; + } else { + return ZEND_HASH_APPLY_KEEP; + } +} +/* }}} */ + +void shutdown_destructors(TSRMLS_D) /* {{{ */ +{ + zend_try { + int symbols; + do { + symbols = zend_hash_num_elements(&EG(symbol_table).ht); + zend_hash_reverse_apply(&EG(symbol_table).ht, (apply_func_t) zval_call_destructor TSRMLS_CC); + } while (symbols != zend_hash_num_elements(&EG(symbol_table).ht)); + zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC); + } zend_catch { + /* if we couldn't destruct cleanly, mark all objects as destructed anyway */ + zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC); + } zend_end_try(); +} +/* }}} */ + +void shutdown_executor(TSRMLS_D) /* {{{ */ +{ + zend_try { + +/* Removed because this can not be safely done, e.g. in this situation: + Object 1 creates object 2 + Object 3 holds reference to object 2. + Now when 1 and 2 are destroyed, 3 can still access 2 in its destructor, with + very problematic results */ +/* zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC); */ + +/* Moved after symbol table cleaners, because some of the cleaners can call + destructors, which would use EG(symtable_cache_ptr) and thus leave leaks */ +/* while (EG(symtable_cache_ptr)>=EG(symtable_cache)) { + zend_hash_destroy(*EG(symtable_cache_ptr)); + efree(*EG(symtable_cache_ptr)); + EG(symtable_cache_ptr)--; + } +*/ + zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_deactivator TSRMLS_CC); + zend_hash_graceful_reverse_destroy(&EG(symbol_table).ht); + } zend_end_try(); + + zend_try { + zval *zeh; + /* remove error handlers before destroying classes and functions, + * so that if handler used some class, crash would not happen */ + if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { + zeh = &EG(user_error_handler); + zval_ptr_dtor(zeh); + ZVAL_UNDEF(&EG(user_error_handler)); + } + + if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { + zeh = &EG(user_exception_handler); + zval_ptr_dtor(zeh); + ZVAL_UNDEF(&EG(user_exception_handler)); + } + + zend_stack_destroy(&EG(user_error_handlers_error_reporting)); + zend_stack_init(&EG(user_error_handlers_error_reporting)); + zend_stack_clean(&EG(user_error_handlers), (void (*)(void *))ZVAL_DESTRUCTOR, 1); + zend_stack_clean(&EG(user_exception_handlers), (void (*)(void *))ZVAL_DESTRUCTOR, 1); + } zend_end_try(); + + zend_try { + /* Cleanup static data for functions and arrays. + * We need a separate cleanup stage because of the following problem: + * Suppose we destroy class X, which destroys the class's function table, + * and in the function table we have function foo() that has static $bar. + * Now if an object of class X is assigned to $bar, its destructor will be + * called and will fail since X's function table is in mid-destruction. + * So we want first of all to clean up all data and then move to tables destruction. + * Note that only run-time accessed data need to be cleaned up, pre-defined data can + * not contain objects and thus are not probelmatic */ + if (EG(full_tables_cleanup)) { + zend_hash_apply(EG(function_table), (apply_func_t) zend_cleanup_function_data_full TSRMLS_CC); + zend_hash_apply(EG(class_table), (apply_func_t) zend_cleanup_class_data TSRMLS_CC); + } else { + zend_hash_reverse_apply(EG(function_table), (apply_func_t) zend_cleanup_function_data TSRMLS_CC); + zend_hash_reverse_apply(EG(class_table), (apply_func_t) zend_cleanup_user_class_data TSRMLS_CC); + zend_cleanup_internal_classes(TSRMLS_C); + } + } zend_end_try(); + + zend_try { + zend_objects_store_free_object_storage(&EG(objects_store) TSRMLS_CC); + + zend_vm_stack_destroy(TSRMLS_C); + + /* Destroy all op arrays */ + if (EG(full_tables_cleanup)) { + zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC); + zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC); + } else { + zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function TSRMLS_CC); + zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class TSRMLS_CC); + } + + while (EG(symtable_cache_ptr)>=EG(symtable_cache)) { + zend_hash_destroy(*EG(symtable_cache_ptr)); + FREE_HASHTABLE(*EG(symtable_cache_ptr)); + EG(symtable_cache_ptr)--; + } + } zend_end_try(); + + zend_try { + clean_non_persistent_constants(TSRMLS_C); + } zend_end_try(); + + zend_try { +#if 0&&ZEND_DEBUG + signal(SIGSEGV, original_sigsegv_handler); +#endif + + zend_hash_destroy(&EG(included_files)); + + zend_stack_destroy(&EG(user_error_handlers_error_reporting)); + zend_stack_destroy(&EG(user_error_handlers)); + zend_stack_destroy(&EG(user_exception_handlers)); + zend_objects_store_destroy(&EG(objects_store)); + if (EG(in_autoload)) { + zend_hash_destroy(EG(in_autoload)); + FREE_HASHTABLE(EG(in_autoload)); + } + } zend_end_try(); + + zend_shutdown_fpu(TSRMLS_C); + + EG(active) = 0; +} +/* }}} */ + +/* return class name and "::" or "". */ +ZEND_API const char *get_active_class_name(const char **space TSRMLS_DC) /* {{{ */ +{ + if (!zend_is_executing(TSRMLS_C)) { + if (space) { + *space = ""; + } + return ""; + } + switch (EG(current_execute_data)->function_state.function->type) { + case ZEND_USER_FUNCTION: + case ZEND_INTERNAL_FUNCTION: + { + zend_class_entry *ce = EG(current_execute_data)->function_state.function->common.scope; + + if (space) { + *space = ce ? "::" : ""; + } + return ce ? ce->name->val : ""; + } + default: + if (space) { + *space = ""; + } + return ""; + } +} +/* }}} */ + +ZEND_API const char *get_active_function_name(TSRMLS_D) /* {{{ */ +{ + if (!zend_is_executing(TSRMLS_C)) { + return NULL; + } + switch (EG(current_execute_data)->function_state.function->type) { + case ZEND_USER_FUNCTION: { + const char *function_name = ((zend_op_array *) EG(current_execute_data)->function_state.function)->function_name->val; + + if (function_name) { + return function_name; + } else { + return "main"; + } + } + break; + case ZEND_INTERNAL_FUNCTION: + return ((zend_internal_function *) EG(current_execute_data)->function_state.function)->function_name->val; + break; + default: + return NULL; + } +} +/* }}} */ + +ZEND_API const char *zend_get_executed_filename(TSRMLS_D) /* {{{ */ +{ + if (EG(active_op_array)) { + return EG(active_op_array)->filename->val; + } else { + return "[no active file]"; + } +} +/* }}} */ + +ZEND_API uint zend_get_executed_lineno(TSRMLS_D) /* {{{ */ +{ + if(EG(exception) && EG(opline_ptr) && active_opline->opcode == ZEND_HANDLE_EXCEPTION && + active_opline->lineno == 0 && EG(opline_before_exception)) { + return EG(opline_before_exception)->lineno; + } + if (EG(opline_ptr)) { + return active_opline->lineno; + } else { + return 0; + } +} +/* }}} */ + +ZEND_API zend_bool zend_is_executing(TSRMLS_D) /* {{{ */ +{ + return EG(in_execution); +} +/* }}} */ + +ZEND_API void _zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) /* {{{ */ +{ + TSRMLS_FETCH(); + i_zval_ptr_dtor(zval_ptr ZEND_FILE_LINE_RELAY_CC TSRMLS_CC); +} +/* }}} */ + +ZEND_API void _zval_internal_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) /* {{{ */ +{ + if (IS_REFCOUNTED(Z_TYPE_P(zval_ptr))) { + Z_DELREF_P(zval_ptr); + if (Z_REFCOUNT_P(zval_ptr) == 0) { + _zval_internal_dtor_for_ptr(zval_ptr ZEND_FILE_LINE_CC); + } else if (Z_REFCOUNT_P(zval_ptr) == 1) { + if (Z_ISREF_P(zval_ptr)) { +//??? zend_reference *ref = Z_REF_P(zval_ptr); +//??? ZVAL_COPY_VALUE(zval_ptr, Z_REFVAL_P(zval_ptr)); +//??? efree(ref); + } + } + } +} +/* }}} */ + +ZEND_API int zend_is_true(zval *op TSRMLS_DC) /* {{{ */ +{ + return i_zend_is_true(op TSRMLS_CC); +} +/* }}} */ + +#include "../TSRM/tsrm_strtok_r.h" + +#define IS_VISITED_CONSTANT 0x080 //??? IS_CONSTANT_INDEX +#define IS_CONSTANT_VISITED(p) (Z_TYPE_P(p) & IS_VISITED_CONSTANT) +#define Z_REAL_TYPE_P(p) (Z_TYPE_P(p) & ~IS_VISITED_CONSTANT) +#define MARK_CONSTANT_VISITED(p) Z_TYPE_P(p) |= IS_VISITED_CONSTANT + +static void zval_deep_copy(zval *p) +{ + zval value; + + ZVAL_DUP(&value, p); +//??? Z_TYPE(value) &= ~IS_CONSTANT_INDEX; +//??? zval_copy_ctor(&value); +//??? Z_TYPE(value) = Z_TYPE_P(p); + ZVAL_COPY_VALUE(p, &value); +} + +ZEND_API int zval_update_constant_ex(zval *p, void *arg, zend_class_entry *scope TSRMLS_DC) /* {{{ */ +{ + zend_bool inline_change = (zend_bool) (zend_uintptr_t) arg; + zval const_value; + char *colon; + + if (IS_CONSTANT_VISITED(p)) { + zend_error(E_ERROR, "Cannot declare self-referencing constant '%s'", Z_STRVAL_P(p)); + } else if ((Z_TYPE_P(p) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT) { + int refcount; + zend_uchar is_ref; + + SEPARATE_ZVAL_IF_NOT_REF(p); + + MARK_CONSTANT_VISITED(p); + + refcount = Z_REFCOUNT_P(p); +//??? is_ref = Z_ISREF_P(p); + + if (!zend_get_constant_ex(Z_STRVAL_P(p), Z_STRLEN_P(p), &const_value, scope, Z_REAL_TYPE_P(p) TSRMLS_CC)) { + char *actual = Z_STRVAL_P(p); + + if ((colon = (char*)zend_memrchr(Z_STRVAL_P(p), ':', Z_STRLEN_P(p)))) { + int len; + + zend_error(E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(p)); + len = Z_STRLEN_P(p) - ((colon - Z_STRVAL_P(p)) + 1); + if (inline_change) { + zend_string *tmp = STR_INIT(colon + 1, len, 0); + STR_RELEASE(Z_STR_P(p)); + Z_STR_P(p) = tmp; + } else { +//??? Z_STRVAL_P(p) = colon + 1; + Z_STR_P(p) = STR_INIT(colon + 1, len, 0); + } + } else { + zend_string *save = Z_STR_P(p); + char *slash; + int actual_len = Z_STRLEN_P(p); + if ((Z_TYPE_P(p) & IS_CONSTANT_UNQUALIFIED) && (slash = (char *)zend_memrchr(actual, '\\', actual_len))) { + actual = slash + 1; + actual_len -= (actual - Z_STRVAL_P(p)); + if (inline_change) { + zend_string *s = STR_INIT(actual, actual_len, 0); +//??? STR_RELEASE(Z_STR_P(p)); + Z_STR_P(p) = s; + } + } + if (actual[0] == '\\') { + if (inline_change) { + memmove(Z_STRVAL_P(p), Z_STRVAL_P(p)+1, Z_STRLEN_P(p)); + --Z_STRLEN_P(p); + } else { + ++actual; + } + --actual_len; + } + if ((Z_TYPE_P(p) & IS_CONSTANT_UNQUALIFIED) == 0) { + if (save->val[0] == '\\') { + zend_error(E_ERROR, "Undefined constant '%s'", save->val + 1); + } else { + zend_error(E_ERROR, "Undefined constant '%s'", save->val); + } + if (inline_change) { +//??? STR_RELEASE(save); + } + save = NULL; + } + if (inline_change && save && save->val != actual) { +//??? STR_RELEASE(save); + } + zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'", actual, actual); + p->type = IS_STRING; + if (!inline_change) { + ZVAL_STRINGL(p, actual, actual_len); + } + } + } else { + if (inline_change) { + STR_RELEASE(Z_STR_P(p)); + } + ZVAL_COPY_VALUE(p, &const_value); + } + + if (IS_REFCOUNTED(Z_TYPE_P(p))) Z_SET_REFCOUNT_P(p, refcount); +//??? Z_SET_ISREF_TO_P(p, is_ref); + } else if (Z_TYPE_P(p) == IS_CONSTANT_ARRAY) { + zval *element, new_val; + zend_string *str_index; + ulong num_index; + int ret; + + SEPARATE_ZVAL_IF_NOT_REF(p); + + Z_TYPE_P(p) = IS_ARRAY; + if (!inline_change) { + HashTable *ht = Z_ARRVAL_P(p); + ZVAL_NEW_ARR(p); + zend_hash_init(Z_ARRVAL_P(p), zend_hash_num_elements(ht), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(Z_ARRVAL_P(p), ht, zval_deep_copy); + } + + /* First go over the array and see if there are any constant indices */ + zend_hash_internal_pointer_reset(Z_ARRVAL_P(p)); + while ((element = zend_hash_get_current_data(Z_ARRVAL_P(p))) != NULL) { + if (zend_hash_get_current_key_ex(Z_ARRVAL_P(p), &str_index, &num_index, 0, NULL) != HASH_KEY_IS_STRING) { + zend_hash_move_forward(Z_ARRVAL_P(p)); + continue; + } + if (!(str_index->gc.u.v.flags & (IS_STR_CONSTANT | IS_STR_AST))) { + zend_hash_move_forward(Z_ARRVAL_P(p)); + continue; + } + + if (str_index->gc.u.v.flags & IS_STR_AST) { + zend_ast_evaluate(&const_value, (zend_ast *)str_index->val, scope TSRMLS_CC); + zend_ast_destroy((zend_ast *)str_index->val); +//??? + } else if (!zend_get_constant_ex(str_index->val, str_index->len, &const_value, scope, str_index->gc.u.v.flags TSRMLS_CC)) { + char *actual, *str; + const char *save = str_index->val; + int len; + + str = str_index->val; + len = str_index->len; + if ((colon = (char*)zend_memrchr(str, ':', len))) { + zend_error(E_ERROR, "Undefined class constant '%s'", str); + len -= ((colon - str) + 1); + str = colon; + } else { + if (str_index->gc.u.v.flags & IS_STR_CONSTANT_UNQUALIFIED) { + if ((actual = (char *)zend_memrchr(str, '\\', len))) { + actual++; + len -= (actual - str); + str = actual; + } + } + if (str[0] == '\\') { + ++str; + --len; + } + if (save[0] == '\\') { + ++save; + } + if (!(str_index->gc.u.v.flags & IS_STR_CONSTANT_UNQUALIFIED)) { + zend_error(E_ERROR, "Undefined constant '%s'", save); + } + zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'", str, str); + } + if (str == str_index->val && len == str_index->len) { + ZVAL_STR(&const_value, str_index); + } else { + ZVAL_STRINGL(&const_value, str, len); + } + } + + if (Z_REFCOUNT_P(element) > 1) { + ZVAL_DUP(&new_val, element); + + /* preserve this bit for inheritance */ +//??? Z_TYPE_P(element) |= IS_CONSTANT_INDEX; + zval_ptr_dtor(element); + ZVAL_COPY_VALUE(element, &new_val); + } + + switch (Z_TYPE(const_value)) { + case IS_STRING: + ret = zend_symtable_update_current_key(Z_ARRVAL_P(p), Z_STR(const_value), HASH_UPDATE_KEY_IF_BEFORE); + break; + case IS_BOOL: + case IS_LONG: + ret = zend_hash_update_current_key_ex(Z_ARRVAL_P(p), HASH_KEY_IS_LONG, NULL, Z_LVAL(const_value), HASH_UPDATE_KEY_IF_BEFORE, NULL); + break; + case IS_DOUBLE: + ret = zend_hash_update_current_key_ex(Z_ARRVAL_P(p), HASH_KEY_IS_LONG, NULL, zend_dval_to_lval(Z_DVAL(const_value)), HASH_UPDATE_KEY_IF_BEFORE, NULL); + break; + case IS_NULL: + ret = zend_hash_update_current_key_ex(Z_ARRVAL_P(p), HASH_KEY_IS_STRING, STR_EMPTY_ALLOC(), 0, HASH_UPDATE_KEY_IF_BEFORE, NULL); + break; + default: + ret = SUCCESS; + break; + } + if (ret == SUCCESS) { + zend_hash_move_forward(Z_ARRVAL_P(p)); + } + zval_dtor(&const_value); + } + zend_hash_apply_with_argument(Z_ARRVAL_P(p), (apply_func_arg_t) zval_update_constant_inline_change, (void *) scope TSRMLS_CC); + zend_hash_internal_pointer_reset(Z_ARRVAL_P(p)); + } else if (Z_TYPE_P(p) == IS_CONSTANT_AST) { + SEPARATE_ZVAL_IF_NOT_REF(p); + + zend_ast_evaluate(&const_value, Z_ASTVAL_P(p), scope TSRMLS_CC); + if (inline_change) { + zend_ast_destroy(Z_ASTVAL_P(p)); + } + ZVAL_COPY_VALUE(p, &const_value); + } + return 0; +} +/* }}} */ + +ZEND_API int zval_update_constant_inline_change(zval *pp, void *scope TSRMLS_DC) /* {{{ */ +{ + return zval_update_constant_ex(pp, (void*)1, scope TSRMLS_CC); +} +/* }}} */ + +ZEND_API int zval_update_constant_no_inline_change(zval *pp, void *scope TSRMLS_DC) /* {{{ */ +{ + return zval_update_constant_ex(pp, (void*)0, scope TSRMLS_CC); +} +/* }}} */ + +ZEND_API int zval_update_constant(zval *pp, void *arg TSRMLS_DC) /* {{{ */ +{ + return zval_update_constant_ex(pp, arg, NULL TSRMLS_CC); +} +/* }}} */ + +int call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, zend_uint param_count, zval params[] TSRMLS_DC) /* {{{ */ +{ + zval *params_array; + zend_uint i; + int ex_retval; + + if (param_count) { + params_array = (zval *) emalloc(sizeof(zval) * param_count); + for (i=0; iretval); + + if (!EG(active)) { + return FAILURE; /* executor is already inactive */ + } + + if (EG(exception)) { + return FAILURE; /* we would result in an instable executor otherwise */ + } + + switch (fci->size) { + case sizeof(zend_fcall_info): + break; /* nothing to do currently */ + default: + zend_error(E_ERROR, "Corrupted fcall_info provided to zend_call_function()"); + break; + } + + /* Initialize execute_data */ + if (EG(current_execute_data)) { + execute_data = *EG(current_execute_data); + EX(op_array) = NULL; + EX(opline) = NULL; + ZVAL_UNDEF(&EX(object)); + } else { + /* This only happens when we're called outside any execute()'s + * It shouldn't be strictly necessary to NULL execute_data out, + * but it may make bugs easier to spot + */ + memset(&execute_data, 0, sizeof(zend_execute_data)); + } + + if (!fci_cache || !fci_cache->initialized) { + char *callable_name; + char *error = NULL; + + if (!fci_cache) { + fci_cache = &fci_cache_local; + } + + if (!zend_is_callable_ex(&fci->function_name, fci->object_ptr, IS_CALLABLE_CHECK_SILENT, &callable_name, NULL, fci_cache, &error TSRMLS_CC)) { + if (error) { + zend_error(E_WARNING, "Invalid callback %s, %s", callable_name, error); + efree(error); + } + if (callable_name) { + efree(callable_name); + } + return FAILURE; + } else if (error) { + /* Capitalize the first latter of the error message */ + if (error[0] >= 'a' && error[0] <= 'z') { + error[0] += ('A' - 'a'); + } + zend_error(E_STRICT, "%s", error); + efree(error); + } + efree(callable_name); + } + + EX(function_state).function = fci_cache->function_handler; + calling_scope = fci_cache->calling_scope; + called_scope = fci_cache->called_scope; + fci->object_ptr = fci_cache->object_ptr; + if (fci->object_ptr) { + ZVAL_COPY_VALUE(&EX(object), fci->object_ptr); + } else { + ZVAL_UNDEF(&EX(object)); + } + if (fci->object_ptr && Z_TYPE_P(fci->object_ptr) == IS_OBJECT && + (!EG(objects_store).object_buckets || + !IS_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(fci->object_ptr)]))) { + return FAILURE; + } + + if (EX(function_state).function->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) { + if (EX(function_state).function->common.fn_flags & ZEND_ACC_ABSTRACT) { + zend_error_noreturn(E_ERROR, "Cannot call abstract method %s::%s()", EX(function_state).function->common.scope->name->val, EX(function_state).function->common.function_name->val); + } + if (EX(function_state).function->common.fn_flags & ZEND_ACC_DEPRECATED) { + zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated", + EX(function_state).function->common.scope ? EX(function_state).function->common.scope->name->val : "", + EX(function_state).function->common.scope ? "::" : "", + EX(function_state).function->common.function_name->val); + } + } + + ZEND_VM_STACK_GROW_IF_NEEDED(fci->param_count + 1); + + for (i=0; iparam_count; i++) { + zval *param; + + if (ARG_SHOULD_BE_SENT_BY_REF(EX(function_state).function, i + 1)) { + if (Z_REFCOUNTED(fci->params[i])) { + if (!Z_ISREF(fci->params[i]) && Z_REFCOUNT(fci->params[i]) > 1) { + zval new_zval; + + if (fci->no_separation && + !ARG_MAY_BE_SENT_BY_REF(EX(function_state).function, i + 1)) { + if (i || UNEXPECTED(ZEND_VM_STACK_ELEMETS(EG(argument_stack)) == (EG(argument_stack)->top))) { + /* hack to clean up the stack */ + ZVAL_LONG(&tmp, i); + zend_vm_stack_push(&tmp TSRMLS_CC); + zend_vm_stack_clear_multiple(0 TSRMLS_CC); + } + + zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given", + i+1, + EX(function_state).function->common.scope ? EX(function_state).function->common.scope->name->val : "", + EX(function_state).function->common.scope ? "::" : "", + EX(function_state).function->common.function_name->val); + return FAILURE; + } + + ZVAL_DUP(&new_zval, &fci->params[i]); + Z_DELREF(fci->params[i]); + ZVAL_COPY_VALUE(&fci->params[i], &new_zval); + } + Z_ADDREF(fci->params[i]); + } +//??? Z_SET_ISREF_PP(fci->params[i]); + param = &fci->params[i]; + } else if (Z_ISREF(fci->params[i]) && + /* don't separate references for __call */ + (EX(function_state).function->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0 ) { + param = &tmp; + ZVAL_DUP(param, &fci->params[i]); + } else { + param = &tmp; + ZVAL_COPY(param, &fci->params[i]); + } + zend_vm_stack_push(param TSRMLS_CC); + } + + EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C); + ZVAL_LONG(&tmp, fci->param_count); + zend_vm_stack_push(&tmp TSRMLS_CC); + + current_scope = EG(scope); + EG(scope) = calling_scope; + + ZVAL_COPY_VALUE(¤t_this, &EG(This)); + + current_called_scope = EG(called_scope); + if (called_scope) { + EG(called_scope) = called_scope; + } else if (EX(function_state).function->type != ZEND_INTERNAL_FUNCTION) { + EG(called_scope) = NULL; + } + + if (fci->object_ptr) { + if ((EX(function_state).function->common.fn_flags & ZEND_ACC_STATIC)) { + ZVAL_UNDEF(&EG(This)); + } else { + ZVAL_COPY(&EG(This), fci->object_ptr); + } + } else { + ZVAL_UNDEF(&EG(This)); + } + + EX(prev_execute_data) = EG(current_execute_data); + EG(current_execute_data) = &execute_data; + + if (EX(function_state).function->type == ZEND_USER_FUNCTION) { + calling_symbol_table = EG(active_symbol_table); + EG(scope) = EX(function_state).function->common.scope; + if (fci->symbol_table) { + EG(active_symbol_table) = fci->symbol_table; + } else { + EG(active_symbol_table) = NULL; + } + + original_op_array = EG(active_op_array); + EG(active_op_array) = (zend_op_array *) EX(function_state).function; + original_opline_ptr = EG(opline_ptr); + + if (EG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { + zend_generator_create_zval(EG(active_op_array), fci->retval TSRMLS_CC); + } else { + zend_execute(EG(active_op_array), fci->retval TSRMLS_CC); + } + + if (!fci->symbol_table && EG(active_symbol_table)) { + zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); + } + EG(active_symbol_table) = calling_symbol_table; + EG(active_op_array) = original_op_array; + EG(opline_ptr) = original_opline_ptr; + } else if (EX(function_state).function->type == ZEND_INTERNAL_FUNCTION) { + int call_via_handler = (EX(function_state).function->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0; +//??? ALLOC_INIT_ZVAL(*fci->retval_ptr_ptr); + if (EX(function_state).function->common.scope) { + EG(scope) = EX(function_state).function->common.scope; + } + if (EXPECTED(zend_execute_internal == NULL)) { + /* saves one function call if zend_execute_internal is not used */ + EX(function_state).function->internal_function.handler(fci->param_count, fci->retval, fci->object_ptr, 1 TSRMLS_CC); + } else { + zend_execute_internal(&execute_data, fci, 1 TSRMLS_CC); + } + /* We shouldn't fix bad extensions here, + because it can break proper ones (Bug #34045) + if (!EX(function_state).function->common.return_reference) + { + INIT_PZVAL(*fci->retval_ptr_ptr); + }*/ + if (EG(exception) && fci->retval) { + zval_ptr_dtor(fci->retval); + ZVAL_UNDEF(fci->retval); + } + + if (call_via_handler) { + /* We must re-initialize function again */ + fci_cache->initialized = 0; + } + } else { /* ZEND_OVERLOADED_FUNCTION */ +//??? ALLOC_INIT_ZVAL(*fci->retval_ptr_ptr); + + /* Not sure what should be done here if it's a static method */ + if (fci->object_ptr) { + Z_OBJ_HT_P(fci->object_ptr)->call_method(EX(function_state).function->common.function_name, fci->param_count, fci->retval, fci->object_ptr, 1 TSRMLS_CC); + } else { + zend_error_noreturn(E_ERROR, "Cannot call overloaded function for non-object"); + } + + if (EX(function_state).function->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { + efree((char*)EX(function_state).function->common.function_name); + } + efree(EX(function_state).function); + + if (EG(exception) && fci->retval) { + zval_ptr_dtor(fci->retval); + ZVAL_UNDEF(fci->retval); + } + } + zend_vm_stack_clear_multiple(0 TSRMLS_CC); + + zval_ptr_dtor(&EG(This)); + EG(called_scope) = current_called_scope; + EG(scope) = current_scope; + ZVAL_COPY_VALUE(&EG(This), ¤t_this); + EG(current_execute_data) = EX(prev_execute_data); + + if (EG(exception)) { + zend_throw_exception_internal(NULL TSRMLS_CC); + } + return SUCCESS; +} +/* }}} */ + +ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zend_literal *key, int use_autoload TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *ce = NULL; + zval args[1]; + zval local_retval; + int retval; + zend_string *lc_name; + zend_fcall_info fcall_info; + zend_fcall_info_cache fcall_cache; + + if (key) { + lc_name = Z_STR(key->constant); + } else { + if (name == NULL || !name->len) { + return NULL; + } + + if (name->val[0] == '\\') { + lc_name = STR_ALLOC(name->len - 1, 0); + zend_str_tolower_copy(lc_name->val, name->val + 1, name->len - 1); + } else { + lc_name = STR_ALLOC(name->len, 0); + zend_str_tolower_copy(lc_name->val, name->val, name->len); + } + } + + ce = zend_hash_find_ptr(EG(class_table), lc_name); + if (ce) { + if (!key) { + STR_FREE(lc_name); + } + return ce; + } + + /* The compiler is not-reentrant. Make sure we __autoload() only during run-time + * (doesn't impact functionality of __autoload() + */ + if (!use_autoload || zend_is_compiling(TSRMLS_C)) { + if (!key) { + STR_FREE(lc_name); + } + return NULL; + } + + /* Verify class name before passing it to __autoload() */ + if (strspn(name->val, "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\") != name->len) { + if (!key) { + STR_FREE(lc_name); + } + return NULL; + } + + if (EG(in_autoload) == NULL) { + ALLOC_HASHTABLE(EG(in_autoload)); + zend_hash_init(EG(in_autoload), 0, NULL, NULL, 0); + } + + if (zend_hash_add_empty_element(EG(in_autoload), lc_name) == NULL) { + if (!key) { + STR_FREE(lc_name); + } + return NULL; + } + + ZVAL_UNDEF(&local_retval); + + if (name->val[0] == '\\') { + ZVAL_STRINGL(&args[0], name->val + 1, name->len - 1); + } else { + ZVAL_STR(&args[0], STR_COPY(name)); + } + + fcall_info.size = sizeof(fcall_info); + fcall_info.function_table = EG(function_table); + ZVAL_STRINGL(&fcall_info.function_name, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1); + fcall_info.symbol_table = NULL; + fcall_info.retval = &local_retval; + fcall_info.param_count = 1; + fcall_info.params = args; + fcall_info.object_ptr = NULL; + fcall_info.no_separation = 1; + + fcall_cache.initialized = EG(autoload_func) ? 1 : 0; + fcall_cache.function_handler = EG(autoload_func); + fcall_cache.calling_scope = NULL; + fcall_cache.called_scope = NULL; + fcall_cache.object_ptr = NULL; + + zend_exception_save(TSRMLS_C); + retval = zend_call_function(&fcall_info, &fcall_cache TSRMLS_CC); + zend_exception_restore(TSRMLS_C); + + EG(autoload_func) = fcall_cache.function_handler; + + zval_ptr_dtor(&args[0]); + zval_dtor(&fcall_info.function_name); + + zend_hash_del(EG(in_autoload), lc_name); + + zval_ptr_dtor(&local_retval); + + if (retval == SUCCESS) { + ce = zend_hash_find_ptr(EG(class_table), lc_name); + } + if (!key) { + STR_FREE(lc_name); + } + return ce; +} +/* }}} */ + +ZEND_API zend_class_entry *zend_lookup_class(zend_string *name TSRMLS_DC) /* {{{ */ +{ + return zend_lookup_class_ex(name, NULL, 1 TSRMLS_CC); +} +/* }}} */ + +ZEND_API int zend_eval_stringl(char *str, int str_len, zval *retval_ptr, char *string_name TSRMLS_DC) /* {{{ */ +{ + zval pv; + zend_op_array *new_op_array; + zend_op_array *original_active_op_array = EG(active_op_array); + zend_uint original_compiler_options; + int retval; + + if (retval_ptr) { + ZVAL_STR(&pv, STR_ALLOC(str_len + sizeof("return ;")-1, 1)); + memcpy(Z_STRVAL(pv), "return ", sizeof("return ") - 1); + memcpy(Z_STRVAL(pv) + sizeof("return ") - 1, str, str_len); + Z_STRVAL(pv)[Z_STRLEN(pv) - 1] = ';'; + Z_STRVAL(pv)[Z_STRLEN(pv)] = '\0'; + } else { + ZVAL_STRINGL(&pv, str, str_len); + } + + /*printf("Evaluating '%s'\n", pv.value.str.val);*/ + + original_compiler_options = CG(compiler_options); + CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL; + new_op_array = zend_compile_string(&pv, string_name TSRMLS_CC); + CG(compiler_options) = original_compiler_options; + + if (new_op_array) { + zval local_retval; + zend_op **original_opline_ptr = EG(opline_ptr); + int orig_interactive = CG(interactive); + + EG(active_op_array) = new_op_array; + EG(no_extensions)=1; + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + CG(interactive) = 0; + + zend_try { + ZVAL_UNDEF(&local_retval); + zend_execute(new_op_array, &local_retval TSRMLS_CC); + } zend_catch { + destroy_op_array(new_op_array TSRMLS_CC); + efree(new_op_array); + zend_bailout(); + } zend_end_try(); + + CG(interactive) = orig_interactive; + if (Z_TYPE(local_retval) != IS_UNDEF) { + if (retval_ptr) { + COPY_PZVAL_TO_ZVAL(*retval_ptr, &local_retval); + } else { + zval_ptr_dtor(&local_retval); + } + } else { + if (retval_ptr) { + ZVAL_NULL(retval_ptr); + } + } + + EG(no_extensions)=0; + EG(opline_ptr) = original_opline_ptr; + EG(active_op_array) = original_active_op_array; + destroy_op_array(new_op_array TSRMLS_CC); + efree(new_op_array); + retval = SUCCESS; + } else { + retval = FAILURE; + } + zval_dtor(&pv); + return retval; +} +/* }}} */ + +ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name TSRMLS_DC) /* {{{ */ +{ + return zend_eval_stringl(str, strlen(str), retval_ptr, string_name TSRMLS_CC); +} +/* }}} */ + +ZEND_API int zend_eval_stringl_ex(char *str, int str_len, zval *retval_ptr, char *string_name, int handle_exceptions TSRMLS_DC) /* {{{ */ +{ + int result; + + result = zend_eval_stringl(str, str_len, retval_ptr, string_name TSRMLS_CC); + if (handle_exceptions && EG(exception)) { + zend_exception_error(EG(exception), E_ERROR TSRMLS_CC); + result = FAILURE; + } + return result; +} +/* }}} */ + +ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name, int handle_exceptions TSRMLS_DC) /* {{{ */ +{ + return zend_eval_stringl_ex(str, strlen(str), retval_ptr, string_name, handle_exceptions TSRMLS_CC); +} +/* }}} */ + +void execute_new_code(TSRMLS_D) /* {{{ */ +{ + zend_op *opline, *end; + zend_op *ret_opline; + int orig_interactive; + + if (!(CG(active_op_array)->fn_flags & ZEND_ACC_INTERACTIVE) + || CG(context).backpatch_count>0 + || CG(active_op_array)->function_name + || CG(active_op_array)->type!=ZEND_USER_FUNCTION) { + return; + } + + ret_opline = get_next_op(CG(active_op_array) TSRMLS_CC); + ret_opline->opcode = ZEND_RETURN; + ret_opline->op1_type = IS_CONST; + ret_opline->op1.constant = zend_add_literal(CG(active_op_array), &EG(uninitialized_zval) TSRMLS_CC); + SET_UNUSED(ret_opline->op2); + + if (!EG(start_op)) { + EG(start_op) = CG(active_op_array)->opcodes; + } + + opline=EG(start_op); + end=CG(active_op_array)->opcodes+CG(active_op_array)->last; + + while (oplineop1_type == IS_CONST) { + opline->op1.zv = &CG(active_op_array)->literals[opline->op1.constant].constant; + } + if (opline->op2_type == IS_CONST) { + opline->op2.zv = &CG(active_op_array)->literals[opline->op2.constant].constant; + } + switch (opline->opcode) { + case ZEND_GOTO: + if (Z_TYPE_P(opline->op2.zv) != IS_LONG) { + zend_resolve_goto_label(CG(active_op_array), opline, 1 TSRMLS_CC); + } + /* break omitted intentionally */ + case ZEND_JMP: + opline->op1.jmp_addr = &CG(active_op_array)->opcodes[opline->op1.opline_num]; + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_JMP_SET: + case ZEND_JMP_SET_VAR: + opline->op2.jmp_addr = &CG(active_op_array)->opcodes[opline->op2.opline_num]; + break; + } + ZEND_VM_SET_OPCODE_HANDLER(opline); + opline++; + } + + zend_release_labels(1 TSRMLS_CC); + + EG(active_op_array) = CG(active_op_array); + orig_interactive = CG(interactive); + CG(interactive) = 0; + zend_execute(CG(active_op_array), NULL TSRMLS_CC); + CG(interactive) = orig_interactive; + + if (EG(exception)) { + zend_exception_error(EG(exception), E_ERROR TSRMLS_CC); + } + + CG(active_op_array)->last -= 1; /* get rid of that ZEND_RETURN */ + EG(start_op) = CG(active_op_array)->opcodes+CG(active_op_array)->last; +} +/* }}} */ + +ZEND_API void zend_timeout(int dummy) /* {{{ */ +{ + TSRMLS_FETCH(); + + if (zend_on_timeout) { +#ifdef ZEND_SIGNALS + /* + We got here because we got a timeout signal, so we are in a signal handler + at this point. However, we want to be able to timeout any user-supplied + shutdown functions, so pretend we are not in a signal handler while we are + calling these + */ + SIGG(running) = 0; +#endif + zend_on_timeout(EG(timeout_seconds) TSRMLS_CC); + } + + zend_error(E_ERROR, "Maximum execution time of %d second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s"); +} +/* }}} */ + +#ifdef ZEND_WIN32 +static LRESULT CALLBACK zend_timeout_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) /* {{{ */ +{ + switch (message) { + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_REGISTER_ZEND_TIMEOUT: + /* wParam is the thread id pointer, lParam is the timeout amount in seconds */ + if (lParam == 0) { + KillTimer(timeout_window, wParam); + } else { +#ifdef ZTS + void ***tsrm_ls; +#endif + SetTimer(timeout_window, wParam, lParam*1000, NULL); +#ifdef ZTS + tsrm_ls = ts_resource_ex(0, &wParam); + if (!tsrm_ls) { + /* shouldn't normally happen */ + break; + } +#endif + EG(timed_out) = 0; + } + break; + case WM_UNREGISTER_ZEND_TIMEOUT: + /* wParam is the thread id pointer */ + KillTimer(timeout_window, wParam); + break; + case WM_TIMER: { +#ifdef ZTS + void ***tsrm_ls; + + tsrm_ls = ts_resource_ex(0, &wParam); + if (!tsrm_ls) { + /* Thread died before receiving its timeout? */ + break; + } +#endif + KillTimer(timeout_window, wParam); + EG(timed_out) = 1; + } + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} +/* }}} */ + +static unsigned __stdcall timeout_thread_proc(void *pArgs) /* {{{ */ +{ + MSG message; + + wc.style=0; + wc.lpfnWndProc = zend_timeout_WndProc; + wc.cbClsExtra=0; + wc.cbWndExtra=0; + wc.hInstance=NULL; + wc.hIcon=NULL; + wc.hCursor=NULL; + wc.hbrBackground=(HBRUSH)(COLOR_BACKGROUND + 5); + wc.lpszMenuName=NULL; + wc.lpszClassName = "Zend Timeout Window"; + if (!RegisterClass(&wc)) { + return -1; + } + timeout_window = CreateWindow(wc.lpszClassName, wc.lpszClassName, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL); + SetEvent(timeout_thread_event); + while (GetMessage(&message, NULL, 0, 0)) { + SendMessage(timeout_window, message.message, message.wParam, message.lParam); + if (message.message == WM_QUIT) { + break; + } + } + DestroyWindow(timeout_window); + UnregisterClass(wc.lpszClassName, NULL); + SetEvent(timeout_thread_handle); + return 0; +} +/* }}} */ + +void zend_init_timeout_thread(void) /* {{{ */ +{ + timeout_thread_event = CreateEvent(NULL, FALSE, FALSE, NULL); + timeout_thread_handle = CreateEvent(NULL, FALSE, FALSE, NULL); + _beginthreadex(NULL, 0, timeout_thread_proc, NULL, 0, &timeout_thread_id); + WaitForSingleObject(timeout_thread_event, INFINITE); +} +/* }}} */ + +void zend_shutdown_timeout_thread(void) /* {{{ */ +{ + if (!timeout_thread_initialized) { + return; + } + PostThreadMessage(timeout_thread_id, WM_QUIT, 0, 0); + + /* Wait for thread termination */ + WaitForSingleObject(timeout_thread_handle, 5000); + CloseHandle(timeout_thread_handle); + timeout_thread_initialized = 0; +} +/* }}} */ + +#endif + +/* This one doesn't exists on QNX */ +#ifndef SIGPROF +#define SIGPROF 27 +#endif + +void zend_set_timeout(long seconds, int reset_signals) /* {{{ */ +{ + TSRMLS_FETCH(); + + EG(timeout_seconds) = seconds; + +#ifdef ZEND_WIN32 + if(!seconds) { + return; + } + if (timeout_thread_initialized == 0 && InterlockedIncrement(&timeout_thread_initialized) == 1) { + /* We start up this process-wide thread here and not in zend_startup(), because if Zend + * is initialized inside a DllMain(), you're not supposed to start threads from it. + */ + zend_init_timeout_thread(); + } + PostThreadMessage(timeout_thread_id, WM_REGISTER_ZEND_TIMEOUT, (WPARAM) GetCurrentThreadId(), (LPARAM) seconds); +#else +# ifdef HAVE_SETITIMER + { + struct itimerval t_r; /* timeout requested */ + int signo; + + if(seconds) { + t_r.it_value.tv_sec = seconds; + t_r.it_value.tv_usec = t_r.it_interval.tv_sec = t_r.it_interval.tv_usec = 0; + +# ifdef __CYGWIN__ + setitimer(ITIMER_REAL, &t_r, NULL); + } + signo = SIGALRM; +# else + setitimer(ITIMER_PROF, &t_r, NULL); + } + signo = SIGPROF; +# endif + + if (reset_signals) { +# ifdef ZEND_SIGNALS + zend_signal(signo, zend_timeout TSRMLS_CC); +# else + sigset_t sigset; + + signal(signo, zend_timeout); + sigemptyset(&sigset); + sigaddset(&sigset, signo); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); +# endif + } + } +# endif /* HAVE_SETITIMER */ +#endif +} +/* }}} */ + +void zend_unset_timeout(TSRMLS_D) /* {{{ */ +{ +#ifdef ZEND_WIN32 + if(timeout_thread_initialized) { + PostThreadMessage(timeout_thread_id, WM_UNREGISTER_ZEND_TIMEOUT, (WPARAM) GetCurrentThreadId(), (LPARAM) 0); + } +#else +# ifdef HAVE_SETITIMER + if (EG(timeout_seconds)) { + struct itimerval no_timeout; + + no_timeout.it_value.tv_sec = no_timeout.it_value.tv_usec = no_timeout.it_interval.tv_sec = no_timeout.it_interval.tv_usec = 0; + +#ifdef __CYGWIN__ + setitimer(ITIMER_REAL, &no_timeout, NULL); +#else + setitimer(ITIMER_PROF, &no_timeout, NULL); +#endif + } +# endif +#endif +} +/* }}} */ + +zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *ce; + int use_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) == 0; + int silent = (fetch_type & ZEND_FETCH_CLASS_SILENT) != 0; + + fetch_type &= ZEND_FETCH_CLASS_MASK; + +check_fetch_type: + switch (fetch_type) { + case ZEND_FETCH_CLASS_SELF: + if (!EG(scope)) { + zend_error(E_ERROR, "Cannot access self:: when no class scope is active"); + } + return EG(scope); + case ZEND_FETCH_CLASS_PARENT: + if (!EG(scope)) { + zend_error(E_ERROR, "Cannot access parent:: when no class scope is active"); + } + if (!EG(scope)->parent) { + zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent"); + } + return EG(scope)->parent; + case ZEND_FETCH_CLASS_STATIC: + if (!EG(called_scope)) { + zend_error(E_ERROR, "Cannot access static:: when no class scope is active"); + } + return EG(called_scope); + case ZEND_FETCH_CLASS_AUTO: { + fetch_type = zend_get_class_fetch_type(class_name->val, class_name->len); + if (fetch_type!=ZEND_FETCH_CLASS_DEFAULT) { + goto check_fetch_type; + } + } + break; + } + + if ((ce = zend_lookup_class_ex(class_name, NULL, use_autoload TSRMLS_CC)) == NULL) { + if (use_autoload) { + if (!silent && !EG(exception)) { + if (fetch_type == ZEND_FETCH_CLASS_INTERFACE) { + zend_error(E_ERROR, "Interface '%s' not found", class_name->val); + } else if (fetch_type == ZEND_FETCH_CLASS_TRAIT) { + zend_error(E_ERROR, "Trait '%s' not found", class_name->val); + } else { + zend_error(E_ERROR, "Class '%s' not found", class_name->val); + } + } + } + return NULL; + } + return ce; +} +/* }}} */ + +zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, const zend_literal *key, int fetch_type TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *ce; + int use_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) == 0; + + if ((ce = zend_lookup_class_ex(class_name, key, use_autoload TSRMLS_CC)) == NULL) { + if (use_autoload) { + if ((fetch_type & ZEND_FETCH_CLASS_SILENT) == 0 && !EG(exception)) { + if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_INTERFACE) { + zend_error(E_ERROR, "Interface '%s' not found", class_name->val); + } else if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_TRAIT) { + zend_error(E_ERROR, "Trait '%s' not found", class_name->val); + } else { + zend_error(E_ERROR, "Class '%s' not found", class_name->val); + } + } + } + return NULL; + } + return ce; +} +/* }}} */ + +#define MAX_ABSTRACT_INFO_CNT 3 +#define MAX_ABSTRACT_INFO_FMT "%s%s%s%s" +#define DISPLAY_ABSTRACT_FN(idx) \ + ai.afn[idx] ? ZEND_FN_SCOPE_NAME(ai.afn[idx]) : "", \ + ai.afn[idx] ? "::" : "", \ + ai.afn[idx] ? ai.afn[idx]->common.function_name->val : "", \ + ai.afn[idx] && ai.afn[idx + 1] ? ", " : (ai.afn[idx] && ai.cnt > MAX_ABSTRACT_INFO_CNT ? ", ..." : "") + +typedef struct _zend_abstract_info { + zend_function *afn[MAX_ABSTRACT_INFO_CNT + 1]; + int cnt; + int ctor; +} zend_abstract_info; + +static int zend_verify_abstract_class_function(zval *zv, zend_abstract_info *ai TSRMLS_DC) /* {{{ */ +{ + zend_function *fn = Z_PTR_P(zv); + + if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { + if (ai->cnt < MAX_ABSTRACT_INFO_CNT) { + ai->afn[ai->cnt] = fn; + } + if (fn->common.fn_flags & ZEND_ACC_CTOR) { + if (!ai->ctor) { + ai->cnt++; + ai->ctor = 1; + } else { + ai->afn[ai->cnt] = NULL; + } + } else { + ai->cnt++; + } + } + return 0; +} +/* }}} */ + +void zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC) /* {{{ */ +{ + zend_abstract_info ai; + + if ((ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) && !(ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) { + memset(&ai, 0, sizeof(ai)); + + zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t) zend_verify_abstract_class_function, &ai TSRMLS_CC); + + if (ai.cnt) { + zend_error(E_ERROR, "Class %s contains %d abstract method%s and must therefore be declared abstract or implement the remaining methods (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")", + ce->name->val, ai.cnt, + ai.cnt > 1 ? "s" : "", + DISPLAY_ABSTRACT_FN(0), + DISPLAY_ABSTRACT_FN(1), + DISPLAY_ABSTRACT_FN(2) + ); + } + } +} +/* }}} */ + +ZEND_API void zend_reset_all_cv(HashTable *symbol_table TSRMLS_DC) /* {{{ */ +{ + zend_execute_data *ex; + int i; + + for (ex = EG(current_execute_data); ex; ex = ex->prev_execute_data) { + if (ex->op_array && ex->symbol_table == symbol_table) { + for (i = 0; i < ex->op_array->last_var; i++) { + ZVAL_UNDEF(EX_VAR_NUM_2(ex, i)); + } + } + } +} +/* }}} */ + +ZEND_API void zend_delete_variable(zend_execute_data *ex, HashTable *ht, zend_string *name TSRMLS_DC) /* {{{ */ +{ + if (zend_hash_del(ht, name) == SUCCESS) { + while (ex && ex->symbol_table == ht) { + int i; + + if (ex->op_array) { + for (i = 0; i < ex->op_array->last_var; i++) { + if (ex->op_array->vars[i]->h == name->h && + ex->op_array->vars[i]->len == name->len && + !memcmp(ex->op_array->vars[i]->val, name->val, name->len)) { + ZVAL_UNDEF(EX_VAR_NUM_2(ex, i)); + break; + } + } + } + ex = ex->prev_execute_data; + } + } +} +/* }}} */ + +ZEND_API int zend_delete_global_variable(zend_string *name TSRMLS_DC) /* {{{ */ +{ + zend_execute_data *ex; + + if (zend_hash_del(&EG(symbol_table).ht, name) == SUCCESS) { + for (ex = EG(current_execute_data); ex; ex = ex->prev_execute_data) { + if (ex->op_array && ex->symbol_table == &EG(symbol_table).ht) { + int i; + for (i = 0; i < ex->op_array->last_var; i++) { + if (ex->op_array->vars[i]->h == name->h && + ex->op_array->vars[i]->len == name->len && + !memcmp(ex->op_array->vars[i]->val, name->val, name->len) + ) { + ZVAL_UNDEF(EX_VAR_NUM_2(ex, i)); + break; + } + } + } + } + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +ZEND_API void zend_rebuild_symbol_table(TSRMLS_D) /* {{{ */ +{ + zend_uint i; + zend_execute_data *ex; + + if (!EG(active_symbol_table)) { + + /* Search for last called user function */ + ex = EG(current_execute_data); + while (ex && !ex->op_array) { + ex = ex->prev_execute_data; + } + if (ex && ex->symbol_table) { + EG(active_symbol_table) = ex->symbol_table; + return; + } + + if (ex && ex->op_array) { + if (EG(symtable_cache_ptr)>=EG(symtable_cache)) { + /*printf("Cache hit! Reusing %x\n", symtable_cache[symtable_cache_ptr]);*/ + EG(active_symbol_table) = *(EG(symtable_cache_ptr)--); + } else { + ALLOC_HASHTABLE(EG(active_symbol_table)); + zend_hash_init(EG(active_symbol_table), ex->op_array->last_var, NULL, ZVAL_PTR_DTOR, 0); + /*printf("Cache miss! Initialized %x\n", EG(active_symbol_table));*/ + } + ex->symbol_table = EG(active_symbol_table); + + if (ex->op_array->this_var != -1 && + Z_TYPE_P(EX_VAR_NUM_2(ex, ex->op_array->this_var)) == IS_UNDEF && + Z_TYPE(EG(This)) != IS_UNDEF) { + ZVAL_COPY_VALUE(EX_VAR_NUM_2(ex, ex->op_array->this_var), &EG(This)); + } + for (i = 0; i < ex->op_array->last_var; i++) { + if (Z_TYPE_P(EX_VAR_NUM_2(ex, i)) != IS_UNDEF) { + zval *zv = zend_hash_update(EG(active_symbol_table), + ex->op_array->vars[i], + EX_VAR_NUM_2(ex, i)); + ZVAL_INDIRECT(EX_VAR_NUM_2(ex, i), zv); + } + } + } + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_4f7339_317bcb_ext#standard#dns.c b/src/main/resource/testFiles/php-src/prevFiles/prev_4f7339_317bcb_ext#standard#dns.c new file mode 100644 index 0000000..6a89446 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_4f7339_317bcb_ext#standard#dns.c @@ -0,0 +1,1048 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: The typical suspects | + | Pollita | + | Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* {{{ includes */ +#include "php.h" +#include "php_network.h" + +#if HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef PHP_WIN32 +# include "win32/inet.h" +# include +# include +# include +#else /* This holds good for NetWare too, both for Winsock and Berkeley sockets */ +#include +#if HAVE_ARPA_INET_H +#include +#endif +#include +#ifdef _OSD_POSIX +#undef STATUS +#undef T_UNSPEC +#endif +#if HAVE_ARPA_NAMESER_H +#ifdef DARWIN +# define BIND_8_COMPAT 1 +#endif +#include +#endif +#if HAVE_RESOLV_H +#include +#endif +#ifdef HAVE_DNS_H +#include +#endif +#endif + +/* Borrowed from SYS/SOCKET.H */ +#if defined(NETWARE) && defined(USE_WINSOCK) +#define AF_INET 2 /* internetwork: UDP, TCP, etc. */ +#endif + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 255 +#endif + +/* For the local hostname obtained via gethostname which is different from the + dns-related MAXHOSTNAMELEN constant above */ +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 255 +#endif + +#include "php_dns.h" + +/* type compat */ +#ifndef DNS_T_A +#define DNS_T_A 1 +#endif +#ifndef DNS_T_NS +#define DNS_T_NS 2 +#endif +#ifndef DNS_T_CNAME +#define DNS_T_CNAME 5 +#endif +#ifndef DNS_T_SOA +#define DNS_T_SOA 6 +#endif +#ifndef DNS_T_PTR +#define DNS_T_PTR 12 +#endif +#ifndef DNS_T_HINFO +#define DNS_T_HINFO 13 +#endif +#ifndef DNS_T_MINFO +#define DNS_T_MINFO 14 +#endif +#ifndef DNS_T_MX +#define DNS_T_MX 15 +#endif +#ifndef DNS_T_TXT +#define DNS_T_TXT 16 +#endif +#ifndef DNS_T_AAAA +#define DNS_T_AAAA 28 +#endif +#ifndef DNS_T_SRV +#define DNS_T_SRV 33 +#endif +#ifndef DNS_T_NAPTR +#define DNS_T_NAPTR 35 +#endif +#ifndef DNS_T_A6 +#define DNS_T_A6 38 +#endif + +#ifndef DNS_T_ANY +#define DNS_T_ANY 255 +#endif +/* }}} */ + +static char *php_gethostbyaddr(char *ip); +static char *php_gethostbyname(char *name); + +#ifdef HAVE_GETHOSTNAME +/* {{{ proto string gethostname() + Get the host name of the current machine */ +PHP_FUNCTION(gethostname) +{ + char buf[HOST_NAME_MAX]; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (gethostname(buf, sizeof(buf) - 1)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to fetch host [%d]: %s", errno, strerror(errno)); + RETURN_FALSE; + } + + RETURN_STRING(buf, 1); +} +/* }}} */ +#endif + +/* TODO: Reimplement the gethostby* functions using the new winxp+ API, in dns_win32.c, then + we can have a dns.c, dns_unix.c and dns_win32.c instead of a messy dns.c full of #ifdef +*/ + +/* {{{ proto string gethostbyaddr(string ip_address) + Get the Internet host name corresponding to a given IP address */ +PHP_FUNCTION(gethostbyaddr) +{ + char *addr; + int addr_len; + char *hostname; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &addr, &addr_len) == FAILURE) { + return; + } + + hostname = php_gethostbyaddr(addr); + + if (hostname == NULL) { +#if HAVE_IPV6 && HAVE_INET_PTON + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Address is not a valid IPv4 or IPv6 address"); +#else + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Address is not in a.b.c.d form"); +#endif + RETVAL_FALSE; + } else { + RETVAL_STRING(hostname, 0); + } +} +/* }}} */ + +/* {{{ php_gethostbyaddr */ +static char *php_gethostbyaddr(char *ip) +{ +#if HAVE_IPV6 && HAVE_INET_PTON + struct in6_addr addr6; +#endif + struct in_addr addr; + struct hostent *hp; + +#if HAVE_IPV6 && HAVE_INET_PTON + if (inet_pton(AF_INET6, ip, &addr6)) { + hp = gethostbyaddr((char *) &addr6, sizeof(addr6), AF_INET6); + } else if (inet_pton(AF_INET, ip, &addr)) { + hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); + } else { + return NULL; + } +#else + addr.s_addr = inet_addr(ip); + + if (addr.s_addr == -1) { + return NULL; + } + + hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); +#endif + + if (!hp || hp->h_name == NULL || hp->h_name[0] == '\0') { + return estrdup(ip); + } + + return estrdup(hp->h_name); +} +/* }}} */ + +/* {{{ proto string gethostbyname(string hostname) + Get the IP address corresponding to a given Internet host name */ +PHP_FUNCTION(gethostbyname) +{ + char *hostname; + int hostname_len; + char *addr; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &hostname_len) == FAILURE) { + return; + } + + addr = php_gethostbyname(hostname); + + RETVAL_STRING(addr, 0); +} +/* }}} */ + +/* {{{ proto array gethostbynamel(string hostname) + Return a list of IP addresses that a given hostname resolves to. */ +PHP_FUNCTION(gethostbynamel) +{ + char *hostname; + int hostname_len; + struct hostent *hp; + struct in_addr in; + int i; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &hostname_len) == FAILURE) { + return; + } + + hp = gethostbyname(hostname); + if (hp == NULL || hp->h_addr_list == NULL) { + RETURN_FALSE; + } + + array_init(return_value); + + for (i = 0 ; hp->h_addr_list[i] != 0 ; i++) { + in = *(struct in_addr *) hp->h_addr_list[i]; + add_next_index_string(return_value, inet_ntoa(in), 1); + } +} +/* }}} */ + +/* {{{ php_gethostbyname */ +static char *php_gethostbyname(char *name) +{ + struct hostent *hp; + struct in_addr in; + + hp = gethostbyname(name); + + if (!hp || !*(hp->h_addr_list)) { + return estrdup(name); + } + + memcpy(&in.s_addr, *(hp->h_addr_list), sizeof(in.s_addr)); + + return estrdup(inet_ntoa(in)); +} +/* }}} */ + +#if HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32) +# define PHP_DNS_NUM_TYPES 12 /* Number of DNS Types Supported by PHP currently */ + +# define PHP_DNS_A 0x00000001 +# define PHP_DNS_NS 0x00000002 +# define PHP_DNS_CNAME 0x00000010 +# define PHP_DNS_SOA 0x00000020 +# define PHP_DNS_PTR 0x00000800 +# define PHP_DNS_HINFO 0x00001000 +# define PHP_DNS_MX 0x00004000 +# define PHP_DNS_TXT 0x00008000 +# define PHP_DNS_A6 0x01000000 +# define PHP_DNS_SRV 0x02000000 +# define PHP_DNS_NAPTR 0x04000000 +# define PHP_DNS_AAAA 0x08000000 +# define PHP_DNS_ANY 0x10000000 +# define PHP_DNS_ALL (PHP_DNS_A|PHP_DNS_NS|PHP_DNS_CNAME|PHP_DNS_SOA|PHP_DNS_PTR|PHP_DNS_HINFO|PHP_DNS_MX|PHP_DNS_TXT|PHP_DNS_A6|PHP_DNS_SRV|PHP_DNS_NAPTR|PHP_DNS_AAAA) +#endif /* HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32) */ + +/* Note: These functions are defined in ext/standard/dns_win32.c for Windows! */ +#if !defined(PHP_WIN32) && (HAVE_DNS_SEARCH_FUNC && !(defined(__BEOS__) || defined(NETWARE))) + +#ifndef HFIXEDSZ +#define HFIXEDSZ 12 /* fixed data in header */ +#endif /* HFIXEDSZ */ + +#ifndef QFIXEDSZ +#define QFIXEDSZ 4 /* fixed data in query */ +#endif /* QFIXEDSZ */ + +#undef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 1024 + +#ifndef MAXRESOURCERECORDS +#define MAXRESOURCERECORDS 64 +#endif /* MAXRESOURCERECORDS */ + +typedef union { + HEADER qb1; + u_char qb2[65536]; +} querybuf; + +/* just a hack to free resources allocated by glibc in __res_nsend() + * See also: + * res_thread_freeres() in glibc/resolv/res_init.c + * __libc_res_nsend() in resolv/res_send.c + * */ + +#if defined(__GLIBC__) && !defined(HAVE_DEPRECATED_DNS_FUNCS) +#define php_dns_free_res(__res__) _php_dns_free_res(__res__) +static void _php_dns_free_res(struct __res_state res) { /* {{{ */ + int ns; + for (ns = 0; ns < MAXNS; ns++) { + if (res._u._ext.nsaddrs[ns] != NULL) { + free (res._u._ext.nsaddrs[ns]); + res._u._ext.nsaddrs[ns] = NULL; + } + } +} /* }}} */ +#else +#define php_dns_free_res(__res__) +#endif + +/* {{{ proto bool dns_check_record(string host [, string type]) + Check DNS records corresponding to a given Internet host name or IP address */ +PHP_FUNCTION(dns_check_record) +{ +#ifndef MAXPACKET +#define MAXPACKET 8192 /* max packet size used internally by BIND */ +#endif + u_char ans[MAXPACKET]; + char *hostname, *rectype = NULL; + int hostname_len, rectype_len = 0; + int type = T_MX, i; +#if defined(HAVE_DNS_SEARCH) + struct sockaddr_storage from; + uint32_t fromsize = sizeof(from); + dns_handle_t handle; +#elif defined(HAVE_RES_NSEARCH) + struct __res_state state; + struct __res_state *handle = &state; +#endif + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &hostname, &hostname_len, &rectype, &rectype_len) == FAILURE) { + return; + } + + if (hostname_len == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host cannot be empty"); + RETURN_FALSE; + } + + if (rectype) { + if (!strcasecmp("A", rectype)) type = T_A; + else if (!strcasecmp("NS", rectype)) type = DNS_T_NS; + else if (!strcasecmp("MX", rectype)) type = DNS_T_MX; + else if (!strcasecmp("PTR", rectype)) type = DNS_T_PTR; + else if (!strcasecmp("ANY", rectype)) type = DNS_T_ANY; + else if (!strcasecmp("SOA", rectype)) type = DNS_T_SOA; + else if (!strcasecmp("TXT", rectype)) type = DNS_T_TXT; + else if (!strcasecmp("CNAME", rectype)) type = DNS_T_CNAME; + else if (!strcasecmp("AAAA", rectype)) type = DNS_T_AAAA; + else if (!strcasecmp("SRV", rectype)) type = DNS_T_SRV; + else if (!strcasecmp("NAPTR", rectype)) type = DNS_T_NAPTR; + else if (!strcasecmp("A6", rectype)) type = DNS_T_A6; + else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type '%s' not supported", rectype); + RETURN_FALSE; + } + } + +#if defined(HAVE_DNS_SEARCH) + handle = dns_open(NULL); + if (handle == NULL) { + RETURN_FALSE; + } +#elif defined(HAVE_RES_NSEARCH) + memset(&state, 0, sizeof(state)); + if (res_ninit(handle)) { + RETURN_FALSE; + } +#else + res_init(); +#endif + + RETVAL_TRUE; + i = php_dns_search(handle, hostname, C_IN, type, ans, sizeof(ans)); + + if (i < 0) { + RETVAL_FALSE; + } + + php_dns_free_handle(handle); +} +/* }}} */ + +#if HAVE_FULL_DNS_FUNCS + +/* {{{ php_parserr */ +static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int store, int raw, zval **subarray) +{ + u_short type, class, dlen; + u_long ttl; + long n, i; + u_short s; + u_char *tp, *p; + char name[MAXHOSTNAMELEN]; + int have_v6_break = 0, in_v6_break = 0; + + *subarray = NULL; + + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, sizeof(name) - 2); + if (n < 0) { + return NULL; + } + cp += n; + + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + if (type_to_fetch != T_ANY && type != type_to_fetch) { + cp += dlen; + return cp; + } + + if (!store) { + cp += dlen; + return cp; + } + + ALLOC_INIT_ZVAL(*subarray); + array_init(*subarray); + + add_assoc_string(*subarray, "host", name, 1); + add_assoc_string(*subarray, "class", "IN", 1); + add_assoc_long(*subarray, "ttl", ttl); + + if (raw) { + add_assoc_long(*subarray, "type", type); + add_assoc_stringl(*subarray, "data", (char*) cp, (uint) dlen, 1); + cp += dlen; + return cp; + } + + switch (type) { + case DNS_T_A: + add_assoc_string(*subarray, "type", "A", 1); + snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); + add_assoc_string(*subarray, "ip", name, 1); + cp += dlen; + break; + case DNS_T_MX: + add_assoc_string(*subarray, "type", "MX", 1); + GETSHORT(n, cp); + add_assoc_long(*subarray, "pri", n); + /* no break; */ + case DNS_T_CNAME: + if (type == DNS_T_CNAME) { + add_assoc_string(*subarray, "type", "CNAME", 1); + } + /* no break; */ + case DNS_T_NS: + if (type == DNS_T_NS) { + add_assoc_string(*subarray, "type", "NS", 1); + } + /* no break; */ + case DNS_T_PTR: + if (type == DNS_T_PTR) { + add_assoc_string(*subarray, "type", "PTR", 1); + } + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "target", name, 1); + break; + case DNS_T_HINFO: + /* See RFC 1010 for values */ + add_assoc_string(*subarray, "type", "HINFO", 1); + n = *cp & 0xFF; + cp++; + add_assoc_stringl(*subarray, "cpu", (char*)cp, n, 1); + cp += n; + n = *cp & 0xFF; + cp++; + add_assoc_stringl(*subarray, "os", (char*)cp, n, 1); + cp += n; + break; + case DNS_T_TXT: + { + int ll = 0; + zval *entries = NULL; + + add_assoc_string(*subarray, "type", "TXT", 1); + tp = emalloc(dlen + 1); + + MAKE_STD_ZVAL(entries); + array_init(entries); + + while (ll < dlen) { + n = cp[ll]; + memcpy(tp + ll , cp + ll + 1, n); + add_next_index_stringl(entries, cp + ll + 1, n, 1); + ll = ll + n + 1; + } + tp[dlen] = '\0'; + cp += dlen; + + add_assoc_stringl(*subarray, "txt", tp, (dlen>0)?dlen - 1:0, 0); + add_assoc_zval(*subarray, "entries", entries); + } + break; + case DNS_T_SOA: + add_assoc_string(*subarray, "type", "SOA", 1); + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "mname", name, 1); + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "rname", name, 1); + GETLONG(n, cp); + add_assoc_long(*subarray, "serial", n); + GETLONG(n, cp); + add_assoc_long(*subarray, "refresh", n); + GETLONG(n, cp); + add_assoc_long(*subarray, "retry", n); + GETLONG(n, cp); + add_assoc_long(*subarray, "expire", n); + GETLONG(n, cp); + add_assoc_long(*subarray, "minimum-ttl", n); + break; + case DNS_T_AAAA: + tp = (u_char*)name; + for(i=0; i < 8; i++) { + GETSHORT(s, cp); + if (s != 0) { + if (tp > (u_char *)name) { + in_v6_break = 0; + tp[0] = ':'; + tp++; + } + tp += sprintf((char*)tp,"%x",s); + } else { + if (!have_v6_break) { + have_v6_break = 1; + in_v6_break = 1; + tp[0] = ':'; + tp++; + } else if (!in_v6_break) { + tp[0] = ':'; + tp++; + tp[0] = '0'; + tp++; + } + } + } + if (have_v6_break && in_v6_break) { + tp[0] = ':'; + tp++; + } + tp[0] = '\0'; + add_assoc_string(*subarray, "type", "AAAA", 1); + add_assoc_string(*subarray, "ipv6", name, 1); + break; + case DNS_T_A6: + p = cp; + add_assoc_string(*subarray, "type", "A6", 1); + n = ((int)cp[0]) & 0xFF; + cp++; + add_assoc_long(*subarray, "masklen", n); + tp = (u_char*)name; + if (n > 15) { + have_v6_break = 1; + in_v6_break = 1; + tp[0] = ':'; + tp++; + } + if (n % 16 > 8) { + /* Partial short */ + if (cp[0] != 0) { + if (tp > (u_char *)name) { + in_v6_break = 0; + tp[0] = ':'; + tp++; + } + sprintf((char*)tp, "%x", cp[0] & 0xFF); + } else { + if (!have_v6_break) { + have_v6_break = 1; + in_v6_break = 1; + tp[0] = ':'; + tp++; + } else if (!in_v6_break) { + tp[0] = ':'; + tp++; + tp[0] = '0'; + tp++; + } + } + cp++; + } + for (i = (n + 8) / 16; i < 8; i++) { + GETSHORT(s, cp); + if (s != 0) { + if (tp > (u_char *)name) { + in_v6_break = 0; + tp[0] = ':'; + tp++; + } + tp += sprintf((char*)tp,"%x",s); + } else { + if (!have_v6_break) { + have_v6_break = 1; + in_v6_break = 1; + tp[0] = ':'; + tp++; + } else if (!in_v6_break) { + tp[0] = ':'; + tp++; + tp[0] = '0'; + tp++; + } + } + } + if (have_v6_break && in_v6_break) { + tp[0] = ':'; + tp++; + } + tp[0] = '\0'; + add_assoc_string(*subarray, "ipv6", name, 1); + if (cp < p + dlen) { + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "chain", name, 1); + } + break; + case DNS_T_SRV: + add_assoc_string(*subarray, "type", "SRV", 1); + GETSHORT(n, cp); + add_assoc_long(*subarray, "pri", n); + GETSHORT(n, cp); + add_assoc_long(*subarray, "weight", n); + GETSHORT(n, cp); + add_assoc_long(*subarray, "port", n); + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "target", name, 1); + break; + case DNS_T_NAPTR: + add_assoc_string(*subarray, "type", "NAPTR", 1); + GETSHORT(n, cp); + add_assoc_long(*subarray, "order", n); + GETSHORT(n, cp); + add_assoc_long(*subarray, "pref", n); + n = (cp[0] & 0xFF); + add_assoc_stringl(*subarray, "flags", (char*)++cp, n, 1); + cp += n; + n = (cp[0] & 0xFF); + add_assoc_stringl(*subarray, "services", (char*)++cp, n, 1); + cp += n; + n = (cp[0] & 0xFF); + add_assoc_stringl(*subarray, "regex", (char*)++cp, n, 1); + cp += n; + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "replacement", name, 1); + break; + default: + zval_ptr_dtor(subarray); + *subarray = NULL; + cp += dlen; + break; + } + + return cp; +} +/* }}} */ + +/* {{{ proto array|false dns_get_record(string hostname [, int type[, array authns, array addtl]]) + Get any Resource Record corresponding to a given Internet host name */ +PHP_FUNCTION(dns_get_record) +{ + char *hostname; + int hostname_len; + long type_param = PHP_DNS_ANY; + zval *authns = NULL, *addtl = NULL; + int type_to_fetch; +#if defined(HAVE_DNS_SEARCH) + struct sockaddr_storage from; + uint32_t fromsize = sizeof(from); + dns_handle_t handle; +#elif defined(HAVE_RES_NSEARCH) + struct __res_state state; + struct __res_state *handle = &state; +#endif + HEADER *hp; + querybuf answer; + u_char *cp = NULL, *end = NULL; + int n, qd, an, ns = 0, ar = 0; + int type, first_query = 1, store_results = 1; + zend_bool raw = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz!z!b", + &hostname, &hostname_len, &type_param, &authns, &addtl, &raw) == FAILURE) { + return; + } + + if (authns) { + zval_dtor(authns); + array_init(authns); + } + if (addtl) { + zval_dtor(addtl); + array_init(addtl); + } + + if (!raw) { + if ((type_param & ~PHP_DNS_ALL) && (type_param != PHP_DNS_ANY)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type '%ld' not supported", type_param); + RETURN_FALSE; + } + } else { + if ((type_param < 1) || (type_param > 0xFFFF)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Numeric DNS record type must be between 1 and 65535, '%ld' given", type_param); + RETURN_FALSE; + } + } + + /* Initialize the return array */ + array_init(return_value); + + /* - We emulate an or'ed type mask by querying type by type. (Steps 0 - NUMTYPES-1 ) + * If additional info is wanted we check again with DNS_T_ANY (step NUMTYPES / NUMTYPES+1 ) + * store_results is used to skip storing the results retrieved in step + * NUMTYPES+1 when results were already fetched. + * - In case of PHP_DNS_ANY we use the directly fetch DNS_T_ANY. (step NUMTYPES+1 ) + * - In case of raw mode, we query only the requestd type instead of looping type by type + * before going with the additional info stuff. + */ + + if (raw) { + type = -1; + } else if (type_param == PHP_DNS_ANY) { + type = PHP_DNS_NUM_TYPES + 1; + } else { + type = 0; + } + + for ( ; + type < (addtl ? (PHP_DNS_NUM_TYPES + 2) : PHP_DNS_NUM_TYPES) || first_query; + type++ + ) { + first_query = 0; + switch (type) { + case -1: /* raw */ + type_to_fetch = type_param; + /* skip over the rest and go directly to additional records */ + type = PHP_DNS_NUM_TYPES - 1; + break; + case 0: + type_to_fetch = type_param&PHP_DNS_A ? DNS_T_A : 0; + break; + case 1: + type_to_fetch = type_param&PHP_DNS_NS ? DNS_T_NS : 0; + break; + case 2: + type_to_fetch = type_param&PHP_DNS_CNAME ? DNS_T_CNAME : 0; + break; + case 3: + type_to_fetch = type_param&PHP_DNS_SOA ? DNS_T_SOA : 0; + break; + case 4: + type_to_fetch = type_param&PHP_DNS_PTR ? DNS_T_PTR : 0; + break; + case 5: + type_to_fetch = type_param&PHP_DNS_HINFO ? DNS_T_HINFO : 0; + break; + case 6: + type_to_fetch = type_param&PHP_DNS_MX ? DNS_T_MX : 0; + break; + case 7: + type_to_fetch = type_param&PHP_DNS_TXT ? DNS_T_TXT : 0; + break; + case 8: + type_to_fetch = type_param&PHP_DNS_AAAA ? DNS_T_AAAA : 0; + break; + case 9: + type_to_fetch = type_param&PHP_DNS_SRV ? DNS_T_SRV : 0; + break; + case 10: + type_to_fetch = type_param&PHP_DNS_NAPTR ? DNS_T_NAPTR : 0; + break; + case 11: + type_to_fetch = type_param&PHP_DNS_A6 ? DNS_T_A6 : 0; + break; + case PHP_DNS_NUM_TYPES: + store_results = 0; + continue; + default: + case (PHP_DNS_NUM_TYPES + 1): + type_to_fetch = DNS_T_ANY; + break; + } + + if (type_to_fetch) { +#if defined(HAVE_DNS_SEARCH) + handle = dns_open(NULL); + if (handle == NULL) { + zval_dtor(return_value); + RETURN_FALSE; + } +#elif defined(HAVE_RES_NSEARCH) + memset(&state, 0, sizeof(state)); + if (res_ninit(handle)) { + zval_dtor(return_value); + RETURN_FALSE; + } +#else + res_init(); +#endif + + n = php_dns_search(handle, hostname, C_IN, type_to_fetch, answer.qb2, sizeof answer); + + if (n < 0) { + php_dns_free_handle(handle); + continue; + } + + cp = answer.qb2 + HFIXEDSZ; + end = answer.qb2 + n; + hp = (HEADER *)&answer; + qd = ntohs(hp->qdcount); + an = ntohs(hp->ancount); + ns = ntohs(hp->nscount); + ar = ntohs(hp->arcount); + + /* Skip QD entries, they're only used by dn_expand later on */ + while (qd-- > 0) { + n = dn_skipname(cp, end); + if (n < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse DNS data received"); + zval_dtor(return_value); + php_dns_free_handle(handle); + RETURN_FALSE; + } + cp += n + QFIXEDSZ; + } + + /* YAY! Our real answers! */ + while (an-- && cp && cp < end) { + zval *retval; + + cp = php_parserr(cp, &answer, type_to_fetch, store_results, raw, &retval); + if (retval != NULL && store_results) { + add_next_index_zval(return_value, retval); + } + } + + if (authns || addtl) { + /* List of Authoritative Name Servers + * Process when only requesting addtl so that we can skip through the section + */ + while (ns-- > 0 && cp && cp < end) { + zval *retval = NULL; + + cp = php_parserr(cp, &answer, DNS_T_ANY, authns != NULL, raw, &retval); + if (retval != NULL) { + add_next_index_zval(authns, retval); + } + } + } + + if (addtl) { + /* Additional records associated with authoritative name servers */ + while (ar-- > 0 && cp && cp < end) { + zval *retval = NULL; + + cp = php_parserr(cp, &answer, DNS_T_ANY, 1, raw, &retval); + if (retval != NULL) { + add_next_index_zval(addtl, retval); + } + } + } + php_dns_free_handle(handle); + } + } +} +/* }}} */ + +/* {{{ proto bool dns_get_mx(string hostname, array mxhosts [, array weight]) + Get MX records corresponding to a given Internet host name */ +PHP_FUNCTION(dns_get_mx) +{ + char *hostname; + int hostname_len; + zval *mx_list, *weight_list = NULL; + int count, qdc; + u_short type, weight; + u_char ans[MAXPACKET]; + char buf[MAXHOSTNAMELEN]; + HEADER *hp; + u_char *cp, *end; + int i; +#if defined(HAVE_DNS_SEARCH) + struct sockaddr_storage from; + uint32_t fromsize = sizeof(from); + dns_handle_t handle; +#elif defined(HAVE_RES_NSEARCH) + struct __res_state state; + struct __res_state *handle = &state; +#endif + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) { + return; + } + + zval_dtor(mx_list); + array_init(mx_list); + + if (weight_list) { + zval_dtor(weight_list); + array_init(weight_list); + } + +#if defined(HAVE_DNS_SEARCH) + handle = dns_open(NULL); + if (handle == NULL) { + RETURN_FALSE; + } +#elif defined(HAVE_RES_NSEARCH) + memset(&state, 0, sizeof(state)); + if (res_ninit(handle)) { + RETURN_FALSE; + } +#else + res_init(); +#endif + + i = php_dns_search(handle, hostname, C_IN, DNS_T_MX, (u_char *)&ans, sizeof(ans)); + if (i < 0) { + RETURN_FALSE; + } + if (i > (int)sizeof(ans)) { + i = sizeof(ans); + } + hp = (HEADER *)&ans; + cp = (u_char *)&ans + HFIXEDSZ; + end = (u_char *)&ans +i; + for (qdc = ntohs((unsigned short)hp->qdcount); qdc--; cp += i + QFIXEDSZ) { + if ((i = dn_skipname(cp, end)) < 0 ) { + php_dns_free_handle(handle); + RETURN_FALSE; + } + } + count = ntohs((unsigned short)hp->ancount); + while (--count >= 0 && cp < end) { + if ((i = dn_skipname(cp, end)) < 0 ) { + php_dns_free_handle(handle); + RETURN_FALSE; + } + cp += i; + GETSHORT(type, cp); + cp += INT16SZ + INT32SZ; + GETSHORT(i, cp); + if (type != DNS_T_MX) { + cp += i; + continue; + } + GETSHORT(weight, cp); + if ((i = dn_expand(ans, end, cp, buf, sizeof(buf)-1)) < 0) { + php_dns_free_handle(handle); + RETURN_FALSE; + } + cp += i; + add_next_index_string(mx_list, buf, 1); + if (weight_list) { + add_next_index_long(weight_list, weight); + } + } + php_dns_free_handle(handle); + RETURN_TRUE; +} +/* }}} */ +#endif /* HAVE_FULL_DNS_FUNCS */ +#endif /* !defined(PHP_WIN32) && (HAVE_DNS_SEARCH_FUNC && !(defined(__BEOS__) || defined(NETWARE))) */ + +#if HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32) +PHP_MINIT_FUNCTION(dns) { + REGISTER_LONG_CONSTANT("DNS_A", PHP_DNS_A, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_NS", PHP_DNS_NS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_CNAME", PHP_DNS_CNAME, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_SOA", PHP_DNS_SOA, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_PTR", PHP_DNS_PTR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_HINFO", PHP_DNS_HINFO, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_MX", PHP_DNS_MX, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_TXT", PHP_DNS_TXT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_SRV", PHP_DNS_SRV, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_NAPTR", PHP_DNS_NAPTR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_AAAA", PHP_DNS_AAAA, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_A6", PHP_DNS_A6, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_ANY", PHP_DNS_ANY, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_ALL", PHP_DNS_ALL, CONST_CS | CONST_PERSISTENT); + return SUCCESS; +} +#endif /* HAVE_FULL_DNS_FUNCS */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_52ffe2_9b50cf_ext#fileinfo#libmagic#cdf.c b/src/main/resource/testFiles/php-src/prevFiles/prev_52ffe2_9b50cf_ext#fileinfo#libmagic#cdf.c new file mode 100644 index 0000000..9d8d042 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_52ffe2_9b50cf_ext#fileinfo#libmagic#cdf.c @@ -0,0 +1,1111 @@ +/*- + * Copyright (c) 2008 Christos Zoulas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Parse composite document files, the format used in Microsoft Office + * document files before they switched to zipped xml. + * Info from: http://sc.openoffice.org/compdocfileformat.pdf + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: cdf.c,v 1.17 2009/02/03 20:27:51 christos Exp $") +#endif + +#include +#ifdef CDF_DEBUG +#include +#endif +#include + +#ifdef PHP_WIN32 +#include "win32/unistd.h" +#else +#include +#endif + +#include +#include +#include + +#ifndef EFTYPE +#define EFTYPE EINVAL +#endif + +#include "cdf.h" + +#ifndef __arraycount +#define __arraycount(a) (sizeof(a) / sizeof(a[0])) +#endif + +#ifdef CDF_DEBUG +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif + +static union { + char s[4]; + uint32_t u; +} cdf_bo; + +#define NEED_SWAP (cdf_bo.u == (uint32_t)0x01020304) + +#define CDF_TOLE8(x) (NEED_SWAP ? cdf_tole8(x) : (uint64_t)(x)) +#define CDF_TOLE4(x) (NEED_SWAP ? cdf_tole4(x) : (uint32_t)(x)) +#define CDF_TOLE2(x) (NEED_SWAP ? cdf_tole2(x) : (uint16_t)(x)) + +/* + * swap a short + */ +uint16_t +cdf_tole2(uint16_t sv) +{ + uint16_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[1]; + d[1] = s[0]; + return rv; +} + +/* + * swap an int + */ +uint32_t +cdf_tole4(uint32_t sv) +{ + uint32_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + return rv; +} + +/* + * swap a quad + */ +uint64_t +cdf_tole8(uint64_t sv) +{ + uint64_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[7]; + d[1] = s[6]; + d[2] = s[5]; + d[3] = s[4]; + d[4] = s[3]; + d[5] = s[2]; + d[6] = s[1]; + d[7] = s[0]; + return rv; +} + +#define CDF_UNPACK(a) \ + (void)memcpy(&(a), &buf[len], sizeof(a)), len += sizeof(a) +#define CDF_UNPACKA(a) \ + (void)memcpy((a), &buf[len], sizeof(a)), len += sizeof(a) + +void +cdf_swap_header(cdf_header_t *h) +{ + size_t i; + + h->h_magic = CDF_TOLE8(h->h_magic); + h->h_uuid[0] = CDF_TOLE8(h->h_uuid[0]); + h->h_uuid[1] = CDF_TOLE8(h->h_uuid[1]); + h->h_revision = CDF_TOLE2(h->h_revision); + h->h_version = CDF_TOLE2(h->h_version); + h->h_byte_order = CDF_TOLE2(h->h_byte_order); + h->h_sec_size_p2 = CDF_TOLE2(h->h_sec_size_p2); + h->h_short_sec_size_p2 = CDF_TOLE2(h->h_short_sec_size_p2); + h->h_num_sectors_in_sat = CDF_TOLE4(h->h_num_sectors_in_sat); + h->h_secid_first_directory = CDF_TOLE4(h->h_secid_first_directory); + h->h_min_size_standard_stream = + CDF_TOLE4(h->h_min_size_standard_stream); + h->h_secid_first_sector_in_short_sat = + CDF_TOLE4(h->h_secid_first_sector_in_short_sat); + h->h_num_sectors_in_short_sat = + CDF_TOLE4(h->h_num_sectors_in_short_sat); + h->h_secid_first_sector_in_master_sat = + CDF_TOLE4(h->h_secid_first_sector_in_master_sat); + h->h_num_sectors_in_master_sat = + CDF_TOLE4(h->h_num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) + h->h_master_sat[i] = CDF_TOLE4(h->h_master_sat[i]); +} + +void +cdf_unpack_header(cdf_header_t *h, char *buf) +{ + size_t i; + size_t len = 0; + + CDF_UNPACK(h->h_magic); + CDF_UNPACKA(h->h_uuid); + CDF_UNPACK(h->h_revision); + CDF_UNPACK(h->h_version); + CDF_UNPACK(h->h_byte_order); + CDF_UNPACK(h->h_sec_size_p2); + CDF_UNPACK(h->h_short_sec_size_p2); + CDF_UNPACKA(h->h_unused0); + CDF_UNPACK(h->h_num_sectors_in_sat); + CDF_UNPACK(h->h_secid_first_directory); + CDF_UNPACKA(h->h_unused1); + CDF_UNPACK(h->h_min_size_standard_stream); + CDF_UNPACK(h->h_secid_first_sector_in_short_sat); + CDF_UNPACK(h->h_num_sectors_in_short_sat); + CDF_UNPACK(h->h_secid_first_sector_in_master_sat); + CDF_UNPACK(h->h_num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) + CDF_UNPACK(h->h_master_sat[i]); +} + +void +cdf_swap_dir(cdf_directory_t *d) +{ + d->d_namelen = CDF_TOLE2(d->d_namelen); + d->d_left_child = CDF_TOLE4(d->d_left_child); + d->d_right_child = CDF_TOLE4(d->d_right_child); + d->d_storage = CDF_TOLE4(d->d_storage); + d->d_storage_uuid[0] = CDF_TOLE8(d->d_storage_uuid[0]); + d->d_storage_uuid[1] = CDF_TOLE8(d->d_storage_uuid[1]); + d->d_flags = CDF_TOLE4(d->d_flags); + d->d_created = CDF_TOLE8(d->d_created); + d->d_modified = CDF_TOLE8(d->d_modified); + d->d_stream_first_sector = CDF_TOLE4(d->d_stream_first_sector); + d->d_size = CDF_TOLE4(d->d_size); +} + +void +cdf_swap_class(cdf_classid_t *d) +{ + d->cl_dword = CDF_TOLE4(d->cl_dword); + d->cl_word[0] = CDF_TOLE2(d->cl_word[0]); + d->cl_word[1] = CDF_TOLE2(d->cl_word[1]); +} + +void +cdf_unpack_dir(cdf_directory_t *d, char *buf) +{ + size_t len = 0; + + CDF_UNPACKA(d->d_name); + CDF_UNPACK(d->d_namelen); + CDF_UNPACK(d->d_type); + CDF_UNPACK(d->d_color); + CDF_UNPACK(d->d_left_child); + CDF_UNPACK(d->d_right_child); + CDF_UNPACK(d->d_storage); + CDF_UNPACKA(d->d_storage_uuid); + CDF_UNPACK(d->d_flags); + CDF_UNPACK(d->d_created); + CDF_UNPACK(d->d_modified); + CDF_UNPACK(d->d_stream_first_sector); + CDF_UNPACK(d->d_size); + CDF_UNPACK(d->d_unused0); +} + +int +cdf_read_header(int fd, cdf_header_t *h) +{ + char buf[512]; + (void)memcpy(cdf_bo.s, "\01\02\03\04", 4); + if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) + return -1; + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) + return -1; + cdf_unpack_header(h, buf); + cdf_swap_header(h); + if (h->h_magic != CDF_MAGIC) { + DPRINTF(("Bad magic 0x%x != 0x$x\n", h->h_magic, CDF_MAGIC)); + errno = EFTYPE; + return -1; + } + return 0; +} + + +ssize_t +cdf_read_sector(int fd, void *buf, size_t offs, size_t len, + const cdf_header_t *h, cdf_secid_t id) +{ + assert((size_t)CDF_SEC_SIZE(h) == len); + if (lseek(fd, (off_t)CDF_SEC_POS(h, id), SEEK_SET) == (off_t)-1) + return -1; + return read(fd, ((char *)buf) + offs, len); +} + +ssize_t +cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs, + size_t len, const cdf_header_t *h, cdf_secid_t id) +{ + assert((size_t)CDF_SHORT_SEC_SIZE(h) == len); + (void)memcpy(((char *)buf) + offs, + ((const char *)sst->sst_tab) + CDF_SHORT_SEC_POS(h, id), len); + return len; +} + +/* + * Read the sector allocation table. + */ +int +cdf_read_sat(int fd, cdf_header_t *h, cdf_sat_t *sat) +{ + size_t i, j, k; + size_t ss = CDF_SEC_SIZE(h); + cdf_secid_t *msa, mid; + + for (i = 0; i < __arraycount(h->h_master_sat); i++) + if (h->h_master_sat[i] == CDF_SECID_FREE) + break; + + sat->sat_len = (h->h_num_sectors_in_master_sat + i); + if ((sat->sat_tab = calloc(sat->sat_len, ss)) == NULL) + return -1; + + for (i = 0; i < __arraycount(h->h_master_sat); i++) { + if (h->h_master_sat[i] < 0) + break; + if (cdf_read_sector(fd, sat->sat_tab, ss * i, ss, h, + h->h_master_sat[i]) != (ssize_t)ss) { + DPRINTF(("Reading sector %d", h->h_master_sat[i])); + goto out1; + } + } + + if ((msa = calloc(1, ss)) == NULL) + goto out1; + + mid = h->h_secid_first_sector_in_master_sat; + for (j = 0; j < h->h_num_sectors_in_master_sat; j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Reading master sector loop limit")); + errno = EFTYPE; + goto out2; + } + if (cdf_read_sector(fd, msa, 0, ss, h, mid) != (ssize_t)ss) { + DPRINTF(("Reading master sector %d", mid)); + goto out2; + } + for (k = 0; k < (ss / sizeof(mid)) - 1; k++, i++) + if (cdf_read_sector(fd, sat->sat_tab, ss * i, ss, h, + CDF_TOLE4(msa[k])) != (ssize_t)ss) { + DPRINTF(("Reading sector %d", + CDF_TOLE4(msa[k]))); + goto out2; + } + mid = CDF_TOLE4(msa[(ss / sizeof(mid)) - 1]); + } + free(msa); + return 0; +out2: + free(msa); +out1: + free(sat->sat_tab); + return -1; +} + +size_t +cdf_count_chain(const cdf_header_t *h, const cdf_sat_t *sat, + cdf_secid_t sid) +{ + size_t i, j, s = CDF_SEC_SIZE(h) / sizeof(cdf_secid_t); + cdf_secid_t maxsector = (cdf_secid_t)(sat->sat_len * s); + + DPRINTF(("Chain:")); + for (j = i = 0; sid >= 0; i++, j++) { + DPRINTF((" %d", sid)); + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Counting chain loop limit")); + errno = EFTYPE; + return (size_t)-1; + } + if (sid > maxsector) { + DPRINTF(("Sector %d > %d\n", sid, maxsector)); + errno = EFTYPE; + return (size_t)-1; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + DPRINTF(("\n")); + return i; +} + +int +cdf_read_long_sector_chain(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + size_t ss = CDF_SEC_SIZE(h), i, j; + ssize_t nr; + scn->sst_len = cdf_count_chain(h, sat, sid); + scn->sst_dirlen = len; + + if (scn->sst_len == (size_t)-1) + return -1; + + scn->sst_tab = calloc(scn->sst_len, ss); + if (scn->sst_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if ((nr = cdf_read_sector(fd, scn->sst_tab, i * ss, ss, h, + sid)) != (ssize_t)ss) { + if (i == scn->sst_len - 1 && nr > 0) { + /* Last sector might be truncated */ + return 0; + } + DPRINTF(("Reading long sector chain %d", sid)); + goto out; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read long sector chain loop limit")); + errno = EFTYPE; + goto out; + } + } + return 0; +out: + free(scn->sst_tab); + return (size_t)-1; +} + +int +cdf_read_short_sector_chain(const cdf_header_t *h, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + size_t ss = CDF_SHORT_SEC_SIZE(h), i, j; + scn->sst_len = cdf_count_chain(h, ssat, sid); + scn->sst_dirlen = len; + + if (scn->sst_len == (size_t)-1) + return -1; + + scn->sst_tab = calloc(scn->sst_len, ss); + if (scn->sst_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read short sector chain loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h, + sid) != (ssize_t)ss) { + DPRINTF(("Reading short sector chain %d", sid)); + goto out; + } + sid = CDF_TOLE4(ssat->sat_tab[sid]); + } + return 0; +out: + free(scn->sst_tab); + return (size_t)-1; +} + +int +cdf_read_sector_chain(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + + if (len < h->h_min_size_standard_stream) + return cdf_read_short_sector_chain(h, ssat, sst, sid, len, + scn); + else + return cdf_read_long_sector_chain(fd, h, sat, sid, len, scn); +} + +int +cdf_read_dir(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_dir_t *dir) +{ + size_t i, j; + size_t ss = CDF_SEC_SIZE(h), ns, nd; + char *buf; + cdf_secid_t sid = h->h_secid_first_directory; + + ns = cdf_count_chain(h, sat, sid); + if (ns == (size_t)-1) + return -1; + + nd = ss / CDF_DIRECTORY_SIZE; + + dir->dir_len = ns * nd; + dir->dir_tab = calloc(dir->dir_len, sizeof(dir->dir_tab[0])); + if (dir->dir_tab == NULL) + return -1; + + if ((buf = malloc(ss)) == NULL) { + free(dir->dir_tab); + return -1; + } + + for (j = i = 0; i < ns; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read dir loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_sector(fd, buf, 0, ss, h, sid) != (ssize_t)ss) { + DPRINTF(("Reading directory sector %d", sid)); + goto out; + } + for (j = 0; j < nd; j++) { + cdf_unpack_dir(&dir->dir_tab[i * nd + j], + &buf[j * CDF_DIRECTORY_SIZE]); + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + if (NEED_SWAP) + for (i = 0; i < dir->dir_len; i++) + cdf_swap_dir(&dir->dir_tab[i]); + free(buf); + return 0; +out: + free(dir->dir_tab); + free(buf); + return -1; +} + + +int +cdf_read_ssat(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_sat_t *ssat) +{ + size_t i, j; + size_t ss = CDF_SEC_SIZE(h); + cdf_secid_t sid = h->h_secid_first_sector_in_short_sat; + + ssat->sat_len = cdf_count_chain(h, sat, sid); + if (ssat->sat_len == (size_t)-1) + return -1; + + ssat->sat_tab = calloc(ssat->sat_len, ss); + if (ssat->sat_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read short sat sector loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_sector(fd, ssat->sat_tab, i * ss, ss, h, sid) != + (ssize_t)ss) { + DPRINTF(("Reading short sat sector %d", sid)); + goto out; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + return 0; +out: + free(ssat->sat_tab); + return -1; +} + +int +cdf_read_short_stream(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_dir_t *dir, cdf_stream_t *scn) +{ + size_t i; + const cdf_directory_t *d; + + for (i = 0; i < dir->dir_len; i++) + if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE) + break; + + if (i == dir->dir_len) { + DPRINTF(("Cannot find root storage node\n")); + errno = EFTYPE; + return -1; + } + d = &dir->dir_tab[i]; + + /* If the it is not there, just fake it; some docs don't have it */ + if (d->d_stream_first_sector < 0) { + scn->sst_tab = NULL; + scn->sst_len = 0; + return 0; + } + + return cdf_read_long_sector_chain(fd, h, sat, + d->d_stream_first_sector, d->d_size, scn); +} + +static int +cdf_namecmp(const char *d, const uint16_t *s, size_t l) +{ + for (; l--; d++, s++) + if (*d != CDF_TOLE2(*s)) + return (unsigned char)*d - CDF_TOLE2(*s); + return 0; +} + +int +cdf_read_summary_info(int fd, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir, cdf_stream_t *scn) +{ + size_t i; + const cdf_directory_t *d; + static const char name[] = "\05SummaryInformation"; + + for (i = 0; i < dir->dir_len; i++) + if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_USER_STREAM && + cdf_namecmp(name, dir->dir_tab[i].d_name, sizeof(name)) + == 0) + break; + + if (i == dir->dir_len) { + DPRINTF(("Cannot find summary information section\n")); + errno = EFTYPE; + return -1; + } + d = &dir->dir_tab[i]; + return cdf_read_sector_chain(fd, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, scn); +} + +int +cdf_read_property_info(const cdf_stream_t *sst, uint32_t offs, + cdf_property_info_t **info, size_t *count, size_t *maxcount) +{ + const cdf_section_header_t *shp; + cdf_section_header_t sh; + const uint32_t *p, *q, *e; + int16_t s16; + int32_t s32; + uint32_t u32; + int64_t s64; + uint64_t u64; + cdf_timestamp_t tp; + size_t i, o, nelements, j; + cdf_property_info_t *inp; + + shp = (const void *)((const char *)sst->sst_tab + offs); + sh.sh_len = CDF_TOLE4(shp->sh_len); + sh.sh_properties = CDF_TOLE4(shp->sh_properties); + DPRINTF(("section len: %d properties %d\n", sh.sh_len, + sh.sh_properties)); + if (*maxcount) { + *maxcount += sh.sh_properties; + inp = realloc(*info, *maxcount * sizeof(*inp)); + } else { + *maxcount = sh.sh_properties; + inp = malloc(*maxcount * sizeof(*inp)); + } + if (inp == NULL) + goto out; + *info = inp; + inp += *count; + *count += sh.sh_properties; + p = (const void *)((const char *)sst->sst_tab + offs + sizeof(sh)); + e = (const void *)(((const char *)shp) + sh.sh_len); + for (i = 0; i < sh.sh_properties; i++) { + q = (const uint32_t *)((const char *)p + + CDF_TOLE4(p[(i << 1) + 1])) - 2; + if (q > e) { + DPRINTF(("Ran of the end %p > %p\n", q, e)); + goto out; + } + inp[i].pi_id = CDF_TOLE4(p[i << 1]); + inp[i].pi_type = CDF_TOLE4(q[0]); + DPRINTF(("%d) id=%x type=%x offs=%x\n", i, inp[i].pi_id, + inp[i].pi_type, (const char *)q - (const char *)p)); + if (inp[i].pi_type & CDF_VECTOR) { + nelements = CDF_TOLE4(q[1]); + o = 2; + } else { + nelements = 1; + o = 1; + } + if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED)) + goto unknown; + switch (inp[i].pi_type & CDF_TYPEMASK) { + case CDF_EMPTY: + break; + case CDF_SIGNED16: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s16, &q[o], sizeof(s16)); + inp[i].pi_s16 = CDF_TOLE2(s16); + break; + case CDF_SIGNED32: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s32, &q[o], sizeof(s32)); + inp[i].pi_s32 = CDF_TOLE4(s32); + break; + case CDF_BOOL: + case CDF_UNSIGNED32: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u32, &q[o], sizeof(u32)); + inp[i].pi_u32 = CDF_TOLE4(u32); + break; + case CDF_SIGNED64: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s64, &q[o], sizeof(s64)); + inp[i].pi_s64 = CDF_TOLE4(s64); + break; + case CDF_UNSIGNED64: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u64, &q[o], sizeof(u64)); + inp[i].pi_u64 = CDF_TOLE4(u64); + break; + case CDF_LENGTH32_STRING: + if (nelements > 1) { + size_t nelem = inp - *info; + *maxcount += nelements; + inp = realloc(*info, *maxcount * sizeof(*inp)); + if (inp == NULL) + goto out; + *info = inp; + inp = *info + nelem; + } + DPRINTF(("nelements = %d\n", nelements)); + for (j = 0; j < nelements; j++, i++) { + uint32_t l = CDF_TOLE4(q[o]); + inp[i].pi_str.s_len = l; + inp[i].pi_str.s_buf = (const char *)(&q[o+1]); + DPRINTF(("l = %d, r = %d, s = %s\n", l, + CDF_ROUND(l, sizeof(l)), + inp[i].pi_str.s_buf)); + l = 4 + CDF_ROUND(l, sizeof(l)); + o += l >> 2; + } + i--; + break; + case CDF_FILETIME: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&tp, &q[o], sizeof(tp)); + inp[i].pi_tp = CDF_TOLE8(tp); + break; + case CDF_CLIPBOARD: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + break; + default: + unknown: + DPRINTF(("Don't know how to deal with %x\n", + inp[i].pi_type)); + goto out; + } + } + return 0; +out: + free(*info); + return -1; +} + +int +cdf_unpack_summary_info(const cdf_stream_t *sst, cdf_summary_info_header_t *ssi, + cdf_property_info_t **info, size_t *count) +{ + size_t i, maxcount; + const cdf_summary_info_header_t *si = sst->sst_tab; + const cdf_section_declaration_t *sd = (const void *) + ((const char *)sst->sst_tab + CDF_SECTION_DECLARATION_OFFSET); + + ssi->si_byte_order = CDF_TOLE2(si->si_byte_order); + ssi->si_os_version = CDF_TOLE2(si->si_os_version); + ssi->si_os = CDF_TOLE2(si->si_os); + ssi->si_class = si->si_class; + cdf_swap_class(&ssi->si_class); + ssi->si_count = CDF_TOLE2(si->si_count); + *count = 0; + maxcount = 0; + *info = NULL; + for (i = 0; i < CDF_TOLE4(si->si_count); i++) { + if (i >= CDF_LOOP_LIMIT) { + DPRINTF(("Unpack summary info loop limit")); + errno = EFTYPE; + return -1; + } + if (cdf_read_property_info(sst, CDF_TOLE4(sd->sd_offset), + info, count, &maxcount) == -1) + return -1; + } + return 0; +} + + + +int +cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id) +{ + return snprintf(buf, buflen, "%.8x-%.4x-%.4x-%.2x%.2x-" + "%.2x%.2x%.2x%.2x%.2x%.2x", id->cl_dword, id->cl_word[0], + id->cl_word[1], id->cl_two[0], id->cl_two[1], id->cl_six[0], + id->cl_six[1], id->cl_six[2], id->cl_six[3], id->cl_six[4], + id->cl_six[5]); +} + +static const struct { + uint32_t v; + const char *n; +} vn[] = { + { CDF_PROPERTY_CODE_PAGE, "Code page" }, + { CDF_PROPERTY_TITLE, "Title" }, + { CDF_PROPERTY_SUBJECT, "Subject" }, + { CDF_PROPERTY_AUTHOR, "Author" }, + { CDF_PROPERTY_KEYWORDS, "Keywords" }, + { CDF_PROPERTY_COMMENTS, "Comments" }, + { CDF_PROPERTY_TEMPLATE, "Template" }, + { CDF_PROPERTY_LAST_SAVED_BY, "Last Saved By" }, + { CDF_PROPERTY_REVISION_NUMBER, "Revision Number" }, + { CDF_PROPERTY_TOTAL_EDITING_TIME, "Total Editing Time" }, + { CDF_PROPERTY_LAST_PRINTED, "Last Printed" }, + { CDF_PROPERTY_CREATE_TIME, "Create Time/Date" }, + { CDF_PROPERTY_LAST_SAVED_TIME, "Last Saved Time/Date" }, + { CDF_PROPERTY_NUMBER_OF_PAGES, "Number of Pages" }, + { CDF_PROPERTY_NUMBER_OF_WORDS, "Number of Words" }, + { CDF_PROPERTY_NUMBER_OF_CHARACTERS, "Number of Characters" }, + { CDF_PROPERTY_THUMBNAIL, "Thumbnail" }, + { CDF_PROPERTY_NAME_OF_APPLICATION, "Name of Creating Application" }, + { CDF_PROPERTY_SECURITY, "Security" }, + { CDF_PROPERTY_LOCALE_ID, "Locale ID" }, +}; + +int +cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p) +{ + size_t i; + + for (i = 0; i < __arraycount(vn); i++) + if (vn[i].v == p) + return snprintf(buf, bufsiz, "%s", vn[i].n); + return snprintf(buf, bufsiz, "0x%x", p); +} + +int +cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts) +{ + size_t len = 0; + int days, hours, mins, secs; + + ts /= CDF_TIME_PREC; + secs = ts % 60; + ts /= 60; + mins = ts % 60; + ts /= 60; + hours = ts % 24; + ts /= 24; + days = ts; + + if (days) { + len += snprintf(buf + len, bufsiz - len, "%dd+", days); + if (len >= bufsiz) + return len; + } + + if (days || hours) { + len += snprintf(buf + len, bufsiz - len, "%.2d:", hours); + if (len >= bufsiz) + return len; + } + + len += snprintf(buf + len, bufsiz - len, "%.2d:", mins); + if (len >= bufsiz) + return len; + + len += snprintf(buf + len, bufsiz - len, "%.2d", secs); + return len; +} + + +#ifdef CDF_DEBUG +void +cdf_dump_header(const cdf_header_t *h) +{ + size_t i; + +#define DUMP(a, b) printf("%40.40s = " a "\n", # b, h->h_ ## b) + DUMP("%d", revision); + DUMP("%d", version); + DUMP("0x%x", byte_order); + DUMP("%d", sec_size_p2); + DUMP("%d", short_sec_size_p2); + DUMP("%d", num_sectors_in_sat); + DUMP("%d", secid_first_directory); + DUMP("%d", min_size_standard_stream); + DUMP("%d", secid_first_sector_in_short_sat); + DUMP("%d", num_sectors_in_short_sat); + DUMP("%d", secid_first_sector_in_master_sat); + DUMP("%d", num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) { + if (h->h_master_sat[i] == CDF_SECID_FREE) + break; + printf("%35.35s[%.3zu] = %d\n", + "master_sat", i, h->h_master_sat[i]); + } +} + +void +cdf_dump_sat(const char *prefix, const cdf_header_t *h, const cdf_sat_t *sat) +{ + size_t i, j, s = CDF_SEC_SIZE(h) / sizeof(cdf_secid_t); + + for (i = 0; i < sat->sat_len; i++) { + printf("%s[%zu]:\n", prefix, i); + for (j = 0; j < s; j++) { + printf("%5d, ", CDF_TOLE4(sat->sat_tab[s * i + j])); + if ((j + 1) % 10 == 0) + printf("\n"); + } + printf("\n"); + } +} + +void +cdf_dump(void *v, size_t len) +{ + size_t i, j; + unsigned char *p = v; + char abuf[16]; + printf("%.4x: ", 0); + for (i = 0, j = 0; i < len; i++, p++) { + printf("%.2x ", *p); + abuf[j++] = isprint(*p) ? *p : '.'; + if (j == 16) { + j = 0; + abuf[15] = '\0'; + printf("%s\n%.4x: ", abuf, i + 1); + } + } + printf("\n"); +} + +void +cdf_dump_stream(const cdf_header_t *h, const cdf_stream_t *sst) +{ + size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? + CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); + cdf_dump(sst->sst_tab, ss * sst->sst_len); +} + +void +cdf_dump_dir(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir) +{ + size_t i, j; + cdf_directory_t *d; + char name[__arraycount(d->d_name)]; + cdf_stream_t scn; + struct timeval ts; + + static const char *types[] = { "empty", "user storage", + "user stream", "lockbytes", "property", "root storage" }; + + for (i = 0; i < dir->dir_len; i++) { + d = &dir->dir_tab[i]; + for (j = 0; j < sizeof(name); j++) + name[j] = (char)CDF_TOLE2(d->d_name[j]); + printf("Directory %zu: %s\n", i, name); + if (d->d_type < __arraycount(types)) + printf("Type: %s\n", types[d->d_type]); + else + printf("Type: %d\n", d->d_type); + printf("Color: %s\n", d->d_color ? "black" : "red"); + printf("Left child: %d\n", d->d_left_child); + printf("Right child: %d\n", d->d_right_child); + printf("Flags: 0x%x\n", d->d_flags); + cdf_timestamp_to_timespec(&ts, d->d_created); + printf("Created %s", ctime(&ts.tv_sec)); + cdf_timestamp_to_timespec(&ts, d->d_modified); + printf("Modified %s", ctime(&ts.tv_sec)); + printf("Stream %d\n", d->d_stream_first_sector); + printf("Size %d\n", d->d_size); + switch (d->d_type) { + case CDF_DIR_TYPE_USER_STORAGE: + printf("Storage: %d\n", d->d_storage); + break; + case CDF_DIR_TYPE_USER_STREAM: + if (sst == NULL) + break; + if (cdf_read_sector_chain(fd, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, &scn) == -1) { + warn("Can't read stream for %s at %d len %d", + name, d->d_stream_first_sector, d->d_size); + break; + } + cdf_dump_stream(h, &scn); + free(scn.sst_tab); + break; + default: + break; + } + + } +} + +void +cdf_dump_property_info(const cdf_property_info_t *info, size_t count) +{ + cdf_timestamp_t tp; + struct timeval ts; + char buf[64]; + size_t i; + + for (i = 0; i < count; i++) { + cdf_print_property_name(buf, sizeof(buf), info[i].pi_id); + printf("%zu) %s: ", i, buf); + switch (info[i].pi_type) { + case CDF_SIGNED16: + printf("signed 16 [%hd]\n", info[i].pi_s16); + break; + case CDF_SIGNED32: + printf("signed 32 [%d]\n", info[i].pi_s32); + break; + case CDF_UNSIGNED32: + printf("unsigned 32 [%u]\n", info[i].pi_u32); + break; + case CDF_LENGTH32_STRING: + printf("string %u [%.*s]\n", info[i].pi_str.s_len, + info[i].pi_str.s_len, info[i].pi_str.s_buf); + break; + case CDF_FILETIME: + tp = info[i].pi_tp; + if (tp < 1000000000000000LL) { + cdf_print_elapsed_time(buf, sizeof(buf), tp); + printf("timestamp %s\n", buf); + } else { + cdf_timestamp_to_timespec(&ts, tp); + printf("timestamp %s", ctime(&ts.tv_sec)); + } + break; + case CDF_CLIPBOARD: + printf("CLIPBOARD %u\n", info[i].pi_u32); + break; + default: + DPRINTF(("Don't know how to deal with %x\n", + info[i].pi_type)); + break; + } + } +} + + +void +cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst) +{ + char buf[128]; + cdf_summary_info_header_t ssi; + cdf_property_info_t *info; + size_t count; + + (void)&h; + if (cdf_unpack_summary_info(sst, &ssi, &info, &count) == -1) + return; + printf("Endian: %x\n", ssi.si_byte_order); + printf("Os Version %d.%d\n", ssi.si_os_version & 0xff, + ssi.si_os_version >> 8); + printf("Os %d\n", ssi.si_os); + cdf_print_classid(buf, sizeof(buf), &ssi.si_class); + printf("Class %s\n", buf); + printf("Count %d\n", ssi.si_count); + cdf_dump_property_info(info, count); + free(info); +} + +#endif + +#ifdef TEST +int +main(int argc, char *argv[]) +{ + int fd, i; + cdf_header_t h; + cdf_sat_t sat, ssat; + cdf_stream_t sst, scn; + cdf_dir_t dir; + + if (argc < 2) { + (void)fprintf(stderr, "Usage: %s \n", getprogname()); + return -1; + } + + for (i = 1; i < argc; i++) { + if ((fd = open(argv[1], O_RDONLY)) == -1) + err(1, "Cannot open `%s'", argv[1]); + + if (cdf_read_header(fd, &h) == -1) + err(1, "Cannot read header"); +#ifdef CDF_DEBUG + cdf_dump_header(&h); +#endif + + if (cdf_read_sat(fd, &h, &sat) == -1) + err(1, "Cannot read sat"); +#ifdef CDF_DEBUG + cdf_dump_sat("SAT", &h, &sat); +#endif + + if (cdf_read_ssat(fd, &h, &sat, &ssat) == -1) + err(1, "Cannot read ssat"); +#ifdef CDF_DEBUG + cdf_dump_sat("SSAT", &h, &ssat); +#endif + + if (cdf_read_dir(fd, &h, &sat, &dir) == -1) + err(1, "Cannot read dir"); + + if (cdf_read_short_stream(fd, &h, &sat, &dir, &sst) == -1) + err(1, "Cannot read short stream"); +#ifdef CDF_DEBUG + cdf_dump_stream(&h, &sst); +#endif + +#ifdef CDF_DEBUG + cdf_dump_dir(fd, &h, &sat, &ssat, &sst, &dir); +#endif + + + if (cdf_read_summary_info(fd, &h, &sat, &ssat, &sst, &dir, + &scn) == -1) + err(1, "Cannot read summary info"); +#ifdef CDF_DEBUG + cdf_dump_summary_info(&h, &scn); +#endif + + (void)close(fd); + } + + return 0; +} +#endif diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_5317e8_9068c2_main#php_variables.c b/src/main/resource/testFiles/php-src/prevFiles/prev_5317e8_9068c2_main#php_variables.c new file mode 100644 index 0000000..467ee20 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_5317e8_9068c2_main#php_variables.c @@ -0,0 +1,1074 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2008 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Zeev Suraski | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include +#include "php.h" +#include "ext/standard/php_standard.h" +#include "ext/standard/credits.h" +#include "php_variables.h" +#include "php_globals.h" +#include "php_content_types.h" +#include "SAPI.h" +#include "php_logos.h" +#include "zend_globals.h" + +/* for systems that need to override reading of environment variables */ +void _php_import_environment_variables(zval *array_ptr TSRMLS_DC); +PHPAPI void (*php_import_environment_variables)(zval *array_ptr TSRMLS_DC) = _php_import_environment_variables; + +PHPAPI void php_register_variable(char *var, char *strval, zval *track_vars_array TSRMLS_DC) +{ + php_register_variable_safe(var, strval, strlen(strval), track_vars_array TSRMLS_CC); +} + +/* binary-safe version */ +PHPAPI void php_register_variable_safe(char *var, char *strval, int str_len, zval *track_vars_array TSRMLS_DC) +{ + zval new_entry; + assert(strval != NULL); + + /* Prepare value */ + Z_STRLEN(new_entry) = str_len; + Z_STRVAL(new_entry) = estrndup(strval, Z_STRLEN(new_entry)); + Z_TYPE(new_entry) = IS_STRING; + + php_register_variable_ex(var, &new_entry, track_vars_array TSRMLS_CC); +} + +PHPAPI void php_u_register_variable_safe(UChar *var, UChar *strval, int str_len, zval *track_vars_array TSRMLS_DC) +{ + zval new_entry; + assert(strval != NULL); + + /* Prepare value */ + Z_USTRLEN(new_entry) = str_len; + Z_USTRVAL(new_entry) = eustrndup(strval, Z_USTRLEN(new_entry)); + Z_TYPE(new_entry) = IS_UNICODE; + + php_u_register_variable_ex(var, &new_entry, track_vars_array TSRMLS_CC); +} + +PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars_array TSRMLS_DC) +{ + char *p = NULL; + char *ip; /* index pointer */ + char *index; + char *var, *var_orig; + int var_len, index_len; + zval *gpc_element, **gpc_element_p; + zend_bool is_array = 0; + HashTable *symtable1 = NULL; + + assert(var_name != NULL); + + if (track_vars_array) { + symtable1 = Z_ARRVAL_P(track_vars_array); + } + + if (!symtable1) { + /* Nothing to do */ + zval_dtor(val); + return; + } + + /* + * Prepare variable name + */ + + var_orig = estrdup(var_name); + var = var_orig; + /* ignore leading spaces in the variable name */ + while (*var && *var==' ') { + var++; + } + + /* ensure that we don't have spaces or dots in the variable name (not binary safe) */ + for (p = var; *p; p++) { + if (*p == ' ' || *p == '.') { + *p='_'; + } else if (*p == '[') { + is_array = 1; + ip = p; + *p = 0; + break; + } + } + var_len = p - var; + + if (var_len==0) { /* empty variable name, or variable name with a space in it */ + zval_dtor(val); + efree(var_orig); + return; + } + + /* GLOBALS hijack attempt, reject parameter */ + if (symtable1 == EG(active_symbol_table) && + var_len == sizeof("GLOBALS")-1 && + !memcmp(var, "GLOBALS", sizeof("GLOBALS")-1)) { + zval_dtor(val); + efree(var_orig); + return; + } + + index = var; + index_len = var_len; + + if (is_array) { + int nest_level = 0; + while (1) { + char *index_s; + int new_idx_len = 0; + + if(++nest_level > PG(max_input_nesting_level)) { + HashTable *ht; + /* too many levels of nesting */ + + ht = Z_ARRVAL_P(track_vars_array); + + zend_hash_del(ht, var, var_len + 1); + zval_dtor(val); + + /* do not output the error message to the screen, + this helps us to to avoid "information disclosure" */ + if (!PG(display_errors)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variable nesting level exceeded %ld. To increase the limit change max_input_nesting_level in php.ini.", PG(max_input_nesting_level)); + } + efree(var_orig); + return; + } + + ip++; + index_s = ip; + if (isspace(*ip)) { + ip++; + } + if (*ip==']') { + index_s = NULL; + } else { + ip = strchr(ip, ']'); + if (!ip) { + /* PHP variables cannot contain '[' in their names, so we replace the character with a '_' */ + *(index_s - 1) = '_'; + + index_len = 0; + if (index) { + index_len = strlen(index); + } + goto plain_var; + return; + } + *ip = 0; + new_idx_len = strlen(index_s); + } + + if (!index) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } else { + if (zend_rt_symtable_find(symtable1, index, index_len + 1, (void **) &gpc_element_p) == FAILURE + || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_rt_symtable_update(symtable1, index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } + } + symtable1 = Z_ARRVAL_PP(gpc_element_p); + /* ip pointed to the '[' character, now obtain the key */ + index = index_s; + index_len = new_idx_len; + + ip++; + if (*ip == '[') { + is_array = 1; + *ip = 0; + } else { + goto plain_var; + } + } + } else { +plain_var: + MAKE_STD_ZVAL(gpc_element); + gpc_element->value = val->value; + Z_TYPE_P(gpc_element) = Z_TYPE_P(val); + if (!index) { + zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } else { + /* + * According to rfc2965, more specific paths are listed above the less specific ones. + * If we encounter a duplicate cookie name, we should skip it, since it is not possible + * to have the same (plain text) cookie name for the same path and we should not overwrite + * more specific cookies with the less specific ones. + */ + if (PG(http_globals)[TRACK_VARS_COOKIE] && + symtable1 == Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_COOKIE]) && + zend_rt_symtable_exists(symtable1, index, index_len+1)) { + zval_ptr_dtor(&gpc_element); + } else { + zend_rt_symtable_update(symtable1, index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } + } + } + efree(var_orig); +} + +PHPAPI void php_u_register_variable_ex(UChar *var, zval *val, zval *track_vars_array TSRMLS_DC) +{ + UChar *p = NULL; + UChar *ip; /* index pointer */ + UChar *index; + int var_len, index_len; + zval *gpc_element, **gpc_element_p; + zend_bool is_array; + HashTable *symtable1=NULL; + + assert(var != NULL); + + if (track_vars_array) { + symtable1 = Z_ARRVAL_P(track_vars_array); + } + + if (!symtable1) { + /* Nothing to do */ + zval_dtor(val); + return; + } + + /* + * Prepare variable name + */ + ip = u_strchr(var, 0x5b /*'['*/); + if (ip) { + is_array = 1; + *ip = 0; + } else { + is_array = 0; + } + /* ignore leading spaces in the variable name */ + while (*var && *var==0x20 /*' '*/) { + var++; + } + var_len = u_strlen(var); + if (var_len==0) { /* empty variable name, or variable name with a space in it */ + zval_dtor(val); + return; + } + /* ensure that we don't have spaces or dots in the variable name (not binary safe) */ + for (p=var; *p; p++) { + switch(*p) { + case 0x20: /*' '*/ + case 0x2e: /*'.'*/ + *p=0x5f; /*'_'*/ + break; + } + } + + index = var; + index_len = var_len; + + if (is_array) { + int nest_level = 0; + while (1) { + zstr escaped_index = NULL_ZSTR; + UChar *index_s; + int new_idx_len = 0; + + if(++nest_level > PG(max_input_nesting_level)) { + HashTable *ht; + zstr tmp_var; + /* too many levels of nesting */ + + ht = Z_ARRVAL_P(track_vars_array); + + tmp_var.u = var; + zend_u_hash_del(ht, IS_UNICODE, tmp_var, var_len + 1); + zval_dtor(val); + + /* do not output the error message to the screen, + this helps us to to avoid "information disclosure" */ + if (!PG(display_errors)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variable nesting level exceeded %ld. To increase the limit change max_input_nesting_level in php.ini.", PG(max_input_nesting_level)); + } + return; + } + + ip++; + index_s = ip; + if (u_isspace(*ip)) { + ip++; + } + if (*ip==0x5d /*']'*/) { + index_s = NULL; + } else { + ip = u_strchr(ip, 0x5d /*']'*/); + if (!ip) { + /* PHP variables cannot contain '[' in their names, so we replace the character with a '_' */ + *(index_s - 1) = 0x5f; /*'_'*/ + + index_len = var_len = 0; + if (index) { + index_len = var_len = u_strlen(index); + } + goto plain_var; + return; + } + *ip = 0; + new_idx_len = u_strlen(index_s); + } + + if (!index) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } else { + escaped_index.u = index; + + if (zend_u_symtable_find(symtable1, IS_UNICODE, escaped_index, index_len+1, (void **) &gpc_element_p)==FAILURE + || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_u_symtable_update(symtable1, IS_UNICODE, escaped_index, index_len+1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } + if (index!=escaped_index.u) { + efree(escaped_index.u); + } + } + symtable1 = Z_ARRVAL_PP(gpc_element_p); + /* ip pointed to the '[' character, now obtain the key */ + index = index_s; + index_len = new_idx_len; + + ip++; + if (*ip==0x5b /*'['*/) { + is_array = 1; + *ip = 0; + } else { + is_array = 0; + } + } + } else { +plain_var: + MAKE_STD_ZVAL(gpc_element); + gpc_element->value = val->value; + Z_TYPE_P(gpc_element) = Z_TYPE_P(val); + if (!index) { + zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } else { + /* UTODO fix for php_addslashes case */ + /* char *escaped_index = php_addslashes(index, index_len, &index_len, 0 TSRMLS_CC); */ + zstr escaped_index; + + escaped_index.u = index; + zend_u_symtable_update(symtable1, IS_UNICODE, escaped_index, index_len+1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + /* efree(escaped_index); */ + } + } +} + +SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler) +{ + char *var, *val, *e, *s, *p; + zval *array_ptr = (zval *) arg; + UConverter *input_conv = UG(http_input_encoding_conv); + + if (SG(request_info).post_data == NULL) { + return; + } + + if (!input_conv) { + input_conv = ZEND_U_CONVERTER(UG(output_encoding_conv)); + } + + s = SG(request_info).post_data; + e = s + SG(request_info).post_data_length; + + while (s < e && (p = memchr(s, '&', (e - s)))) { +last_value: + if ((val = memchr(s, '=', (p - s)))) { /* have a value */ + if (UG(unicode)) { + UChar *u_var, *u_val; + int u_var_len, u_val_len; + int var_len; + int val_len; + UErrorCode status1 = U_ZERO_ERROR, status2 = U_ZERO_ERROR; + + var = s; + var_len = val - s; + + php_url_decode(var, var_len); + val++; + val_len = php_url_decode(val, (p - val)); + zend_string_to_unicode_ex(input_conv, &u_var, &u_var_len, var, var_len, &status1); + zend_string_to_unicode_ex(input_conv, &u_val, &u_val_len, val, val_len, &status2); + if (U_SUCCESS(status1) && U_SUCCESS(status2)) { + /* UTODO add input filtering */ + php_u_register_variable_safe(u_var, u_val, u_val_len, array_ptr TSRMLS_CC); + } else { + /* UTODO set a user-accessible flag to indicate that conversion failed? */ + } + efree(u_var); + efree(u_val); + } else { + unsigned int val_len, new_val_len; + + var = s; + + php_url_decode(var, (val - s)); + val++; + val_len = php_url_decode(val, (p - val)); + val = estrndup(val, val_len); + if (sapi_module.input_filter(PARSE_POST, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); + } + } + s = p + 1; + } + if (s < e) { + p = e; + goto last_value; + } +} + +SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter) +{ + /* TODO: check .ini setting here and apply user-defined input filter */ + if(new_val_len) *new_val_len = val_len; + return 1; +} + +SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data) +{ + char *res = NULL, *var, *val, *separator = NULL; + const char *c_var; + zval *array_ptr; + int free_buffer = 0; + char *strtok_buf = NULL; + UConverter *input_conv = UG(http_input_encoding_conv); + + switch (arg) { + case PARSE_POST: + case PARSE_GET: + case PARSE_COOKIE: + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + switch (arg) { + case PARSE_POST: + if (PG(http_globals)[TRACK_VARS_POST]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_POST]); + } + PG(http_globals)[TRACK_VARS_POST] = array_ptr; + break; + case PARSE_GET: + if (PG(http_globals)[TRACK_VARS_GET]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_GET]); + } + PG(http_globals)[TRACK_VARS_GET] = array_ptr; + break; + case PARSE_COOKIE: + if (PG(http_globals)[TRACK_VARS_COOKIE]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_COOKIE]); + } + PG(http_globals)[TRACK_VARS_COOKIE] = array_ptr; + break; + } + break; + default: + array_ptr = destArray; + break; + } + + if (arg == PARSE_POST) { + sapi_handle_post(array_ptr TSRMLS_CC); + return; + } + + if (arg == PARSE_GET) { /* GET data */ + c_var = SG(request_info).query_string; + if (c_var && *c_var) { + res = (char *) estrdup(c_var); + free_buffer = 1; + } else { + free_buffer = 0; + } + } else if (arg == PARSE_COOKIE) { /* Cookie data */ + c_var = SG(request_info).cookie_data; + if (c_var && *c_var) { + res = (char *) estrdup(c_var); + free_buffer = 1; + } else { + free_buffer = 0; + } + } else if (arg == PARSE_STRING) { /* String data */ + res = str; + free_buffer = 1; + } + + if (!res) { + return; + } + + switch (arg) { + case PARSE_GET: + case PARSE_STRING: + separator = (char *) estrdup(PG(arg_separator).input); + break; + case PARSE_COOKIE: + separator = ";\0"; + break; + } + + if (!input_conv) { + input_conv = ZEND_U_CONVERTER(UG(output_encoding_conv)); + } + + var = php_strtok_r(res, separator, &strtok_buf); + + while (var) { + int var_len; + + val = strchr(var, '='); + + if (arg == PARSE_COOKIE) { + /* Remove leading spaces from cookie names, needed for multi-cookie header where ; can be followed by a space */ + while (isspace(*var)) { + var++; + } + if (var == val || *var == '\0') { + goto next_var; + } + } + + if (val) { + *val++ = '\0'; + } + var_len = strlen(var); + php_url_decode(var, var_len); + + if (UG(unicode)) { + UChar *u_var, *u_val; + int u_var_len, u_val_len; + UErrorCode status = U_ZERO_ERROR; + + zend_string_to_unicode_ex(input_conv, &u_var, &u_var_len, var, var_len, &status); + if (U_FAILURE(status)) { + /* UTODO set a user-accessible flag to indicate that conversion failed? */ + efree(u_var); + goto next_var; + } + + if (val) { /* have a value */ + int val_len; + /* unsigned int new_val_len; see below */ + + val_len = php_url_decode(val, strlen(val)); + zend_string_to_unicode_ex(input_conv, &u_val, &u_val_len, val, val_len, &status); + if (U_FAILURE(status)) { + /* UTODO set a user-accessible flag to indicate that conversion failed? */ + efree(u_var); + efree(u_val); + goto next_var; + } + php_u_register_variable_safe(u_var, u_val, u_val_len, array_ptr TSRMLS_CC); + /* UTODO need to make input_filter Unicode aware */ + /* + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + */ + efree(u_var); + efree(u_val); + } else { + u_val_len = 0; + u_val = USTR_MAKE(""); + php_u_register_variable_safe(u_var, u_val, u_val_len, array_ptr TSRMLS_CC); + /* + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + */ + efree(u_var); + efree(u_val); + } + } else { + if (val) { /* have a value */ + int val_len; + unsigned int new_val_len; + + val_len = php_url_decode(val, strlen(val)); + val = estrndup(val, val_len); + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); + } else { + int val_len; + unsigned int new_val_len; + + val_len = 0; + val = estrndup("", val_len); + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); + } + } +next_var: + var = php_strtok_r(NULL, separator, &strtok_buf); + } + + if (arg != PARSE_COOKIE) { + efree(separator); + } + + if (free_buffer) { + efree(res); + } +} + +void _php_import_environment_variables(zval *array_ptr TSRMLS_DC) +{ + char buf[128]; + char **env, *p, *t = buf; + size_t alloc_size = sizeof(buf); + unsigned long nlen; /* ptrdiff_t is not portable */ + + for (env = environ; env != NULL && *env != NULL; env++) { + p = strchr(*env, '='); + if (!p) { /* malformed entry? */ + continue; + } + nlen = p - *env; + if (nlen >= alloc_size) { + alloc_size = nlen + 64; + t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size)); + } + memcpy(t, *env, nlen); + t[nlen] = '\0'; + php_register_variable(t, p + 1, array_ptr TSRMLS_CC); + } + if (t != buf && t != NULL) { + efree(t); + } +} + +zend_bool php_std_auto_global_callback(char *name, uint name_len TSRMLS_DC) +{ + zend_printf("%s\n", name); + return 0; /* don't rearm */ +} + +/* {{{ php_build_argv + */ +static void php_build_argv(char *s, zval *track_vars_array TSRMLS_DC) +{ + zval *arr, *argc, *tmp; + int count = 0; + char *ss, *space; + + if (!(SG(request_info).argc || track_vars_array)) { + return; + } + + ALLOC_INIT_ZVAL(arr); + array_init(arr); + + /* Prepare argv */ + if (SG(request_info).argc) { /* are we in cli sapi? */ + int i; + for (i = 0; i < SG(request_info).argc; i++) { + ALLOC_ZVAL(tmp); + /* leave args as binary, since the encoding is not known */ + ZVAL_STRING(tmp, SG(request_info).argv[i], 1); + INIT_PZVAL(tmp); + if (zend_hash_next_index_insert(Z_ARRVAL_P(arr), &tmp, sizeof(zval *), NULL) == FAILURE) { + if (Z_TYPE_P(tmp) == IS_STRING) { + efree(Z_STRVAL_P(tmp)); + } + } + } + } else if (s && *s) { + ss = s; + while (ss) { + space = strchr(ss, '+'); + if (space) { + *space = '\0'; + } + /* auto-type */ + ALLOC_ZVAL(tmp); + ZVAL_RT_STRING(tmp, ss, 1); + INIT_PZVAL(tmp); + count++; + if (zend_hash_next_index_insert(Z_ARRVAL_P(arr), &tmp, sizeof(zval *), NULL) == FAILURE) { + efree(Z_STRVAL_P(tmp)); + } + if (space) { + *space = '+'; + ss = space + 1; + } else { + ss = space; + } + } + } + + /* prepare argc */ + ALLOC_INIT_ZVAL(argc); + if (SG(request_info).argc) { + Z_LVAL_P(argc) = SG(request_info).argc; + } else { + Z_LVAL_P(argc) = count; + } + Z_TYPE_P(argc) = IS_LONG; + + if (SG(request_info).argc) { + Z_ADDREF_P(arr); + Z_ADDREF_P(argc); + zend_ascii_hash_update(&EG(symbol_table), "argv", sizeof("argv"), &arr, sizeof(zval *), NULL); + zend_ascii_hash_add(&EG(symbol_table), "argc", sizeof("argc"), &argc, sizeof(zval *), NULL); + } + if (track_vars_array) { + Z_ADDREF_P(arr); + Z_ADDREF_P(argc); + zend_ascii_hash_update(Z_ARRVAL_P(track_vars_array), "argv", sizeof("argv"), &arr, sizeof(zval *), NULL); + zend_ascii_hash_update(Z_ARRVAL_P(track_vars_array), "argc", sizeof("argc"), &argc, sizeof(zval *), NULL); + } + zval_ptr_dtor(&arr); + zval_ptr_dtor(&argc); +} +/* }}} */ + +/* {{{ php_handle_special_queries + */ +PHPAPI int php_handle_special_queries(TSRMLS_D) +{ + if (PG(expose_php) && SG(request_info).query_string && SG(request_info).query_string[0] == '=') { + if (php_info_logos(SG(request_info).query_string + 1 TSRMLS_CC)) { + return 1; + } else if (!strcmp(SG(request_info).query_string + 1, PHP_CREDITS_GUID)) { + php_print_credits(PHP_CREDITS_ALL TSRMLS_CC); + return 1; + } + } + return 0; +} +/* }}} */ + +/* {{{ php_register_server_variables + */ +static inline void php_register_server_variables(TSRMLS_D) +{ + zval *array_ptr = NULL; + + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + if (PG(http_globals)[TRACK_VARS_SERVER]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_SERVER]); + } + PG(http_globals)[TRACK_VARS_SERVER] = array_ptr; + + /* Server variables */ + if (sapi_module.register_server_variables) { + sapi_module.register_server_variables(array_ptr TSRMLS_CC); + } + + /* PHP Authentication support */ + if (SG(request_info).auth_user) { + php_register_variable("PHP_AUTH_USER", SG(request_info).auth_user, array_ptr TSRMLS_CC); + } + if (SG(request_info).auth_password) { + php_register_variable("PHP_AUTH_PW", SG(request_info).auth_password, array_ptr TSRMLS_CC); + } + if (SG(request_info).auth_digest) { + php_register_variable("PHP_AUTH_DIGEST", SG(request_info).auth_digest, array_ptr TSRMLS_CC); + } + /* store request init time */ + { + zval new_entry; + Z_TYPE(new_entry) = IS_LONG; + Z_LVAL(new_entry) = sapi_get_request_time(TSRMLS_C); + php_register_variable_ex("REQUEST_TIME", &new_entry, array_ptr TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ php_autoglobal_merge + */ +static void php_autoglobal_merge(HashTable *dest, HashTable *src TSRMLS_DC) +{ + zval **src_entry, **dest_entry; + zstr string_key; + uint string_key_len; + ulong num_key; + HashPosition pos; + int key_type; + + zend_hash_internal_pointer_reset_ex(src, &pos); + while (zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS) { + key_type = zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos); + if (Z_TYPE_PP(src_entry) != IS_ARRAY + || ((key_type == HASH_KEY_IS_UNICODE || key_type == HASH_KEY_IS_STRING) && zend_u_hash_find(dest, key_type, string_key, string_key_len, (void **) &dest_entry) != SUCCESS) + || (key_type == HASH_KEY_IS_LONG && zend_hash_index_find(dest, num_key, (void **)&dest_entry) != SUCCESS) + || Z_TYPE_PP(dest_entry) != IS_ARRAY + ) { + Z_ADDREF_PP(src_entry); + if (key_type == HASH_KEY_IS_STRING || key_type == HASH_KEY_IS_UNICODE) { + zend_u_hash_update(dest, key_type, string_key, string_key_len, src_entry, sizeof(zval *), NULL); + } else { + zend_hash_index_update(dest, num_key, src_entry, sizeof(zval *), NULL); + } + } else { + SEPARATE_ZVAL(dest_entry); + php_autoglobal_merge(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry) TSRMLS_CC); + } + zend_hash_move_forward_ex(src, &pos); + } +} +/* }}} */ + +static zend_bool php_auto_globals_create_server(char *name, uint name_len TSRMLS_DC); +static zend_bool php_auto_globals_create_env(char *name, uint name_len TSRMLS_DC); +static zend_bool php_auto_globals_create_request(char *name, uint name_len TSRMLS_DC); + +/* {{{ php_hash_environment + */ +int php_hash_environment(TSRMLS_D) +{ + char *p; + unsigned char _gpc_flags[5] = {0, 0, 0, 0, 0}; + zend_bool jit_initialization = PG(auto_globals_jit); + struct auto_global_record { + char *name; + uint name_len; + zend_bool jit_initialization; + } auto_global_records[] = { + { "_POST", sizeof("_POST"), 0 }, + { "_GET", sizeof("_GET"), 0 }, + { "_COOKIE", sizeof("_COOKIE"), 0 }, + { "_SERVER", sizeof("_SERVER"), 1 }, + { "_ENV", sizeof("_ENV"), 1 }, + { "_FILES", sizeof("_FILES"), 0 }, + }; + size_t num_track_vars = sizeof(auto_global_records)/sizeof(struct auto_global_record); + size_t i; + + /* jit_initialization = 0; */ + for (i=0; i | + | Rob Richards | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#if HAVE_LIBXML && HAVE_DOM +#include "php_dom.h" +#include +#ifdef LIBXML_SCHEMAS_ENABLED +#include +#include +#endif + +typedef struct _idsIterator idsIterator; +struct _idsIterator { + xmlChar *elementId; + xmlNode *element; +}; + +#define DOM_LOAD_STRING 0 +#define DOM_LOAD_FILE 1 + +static void idsHashScanner(void *payload, void *data, xmlChar *name) +{ + idsIterator *priv = (idsIterator *) data; + + if (priv->element == NULL && xmlStrEqual (name, priv->elementId)) { + priv->element = ((xmlNode *)((xmlID *)payload)->attr)->parent; + } +} + +/* +* class domdocument extends domnode +* +* URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-i-Document +* Since: +*/ + +zend_function_entry php_dom_document_class_functions[] = { + PHP_FALIAS(createElement, dom_document_create_element, NULL) + PHP_FALIAS(createDocumentFragment, dom_document_create_document_fragment, NULL) + PHP_FALIAS(createTextNode, dom_document_create_text_node, NULL) + PHP_FALIAS(createComment, dom_document_create_comment, NULL) + PHP_FALIAS(createCDATASection, dom_document_create_cdatasection, NULL) + PHP_FALIAS(createProcessingInstruction, dom_document_create_processing_instruction, NULL) + PHP_FALIAS(createAttribute, dom_document_create_attribute, NULL) + PHP_FALIAS(createEntityReference, dom_document_create_entity_reference, NULL) + PHP_FALIAS(getElementsByTagName, dom_document_get_elements_by_tag_name, NULL) + PHP_FALIAS(importNode, dom_document_import_node, NULL) + PHP_FALIAS(createElementNS, dom_document_create_element_ns, NULL) + PHP_FALIAS(createAttributeNS, dom_document_create_attribute_ns, NULL) + PHP_FALIAS(getElementsByTagNameNS, dom_document_get_elements_by_tag_name_ns, NULL) + PHP_FALIAS(getElementById, dom_document_get_element_by_id, NULL) + PHP_FALIAS(adoptNode, dom_document_adopt_node, NULL) + PHP_FALIAS(normalizeDocument, dom_document_normalize_document, NULL) + PHP_FALIAS(renameNode, dom_document_rename_node, NULL) + PHP_FALIAS(load, dom_document_load, NULL) + PHP_FALIAS(save, dom_document_save, NULL) + PHP_FALIAS(loadXML, dom_document_loadxml, NULL) + PHP_FALIAS(saveXML, dom_document_savexml, NULL) + PHP_FALIAS(domdocument, dom_document_document, NULL) + PHP_FALIAS(validate, dom_document_validate, NULL) + PHP_FALIAS(xinclude, dom_document_xinclude, NULL) +#if defined(LIBXML_HTML_ENABLED) + PHP_FALIAS(loadHTML, dom_document_load_html, NULL) + PHP_FALIAS(loadHTMLFile, dom_document_load_html_file, NULL) + PHP_FALIAS(saveHTML, dom_document_save_html, NULL) + PHP_FALIAS(saveHTMLFile, dom_document_save_html_file, NULL) +#endif /* defined(LIBXML_HTML_ENABLED) */ +#if defined(LIBXML_SCHEMAS_ENABLED) + PHP_FALIAS(schemaValidate, dom_document_schema_validate_file, NULL) + PHP_FALIAS(schemaValidateSource, dom_document_schema_validate_xml, NULL) + PHP_FALIAS(relaxNGValidate, dom_document_relaxNG_validate_file, NULL) + PHP_FALIAS(relaxNGValidateSource, dom_document_relaxNG_validate_xml, NULL) +#endif + {NULL, NULL, NULL} +}; + +/* {{{ proto doctype documenttype +readonly=yes +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-B63ED1A31 +Since: +*/ +int dom_document_doctype_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + xmlDtdPtr dtdptr; + int ret; + + docp = (xmlDocPtr) dom_object_get_node(obj); + + dtdptr = xmlGetIntSubset(docp); + if (!dtdptr) { + return FAILURE; + } + + ALLOC_ZVAL(*retval); + if (NULL == (*retval = php_dom_create_object((xmlNodePtr) dtdptr, &ret, NULL, *retval, obj TSRMLS_CC))) { + php_error(E_WARNING, "Cannot create required DOM object"); + return FAILURE; + } + return SUCCESS; + +} + +/* }}} */ + + + +/* {{{ proto implementation domimplementation +readonly=yes +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1B793EBA +Since: +*/ +int dom_document_implementation_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + ALLOC_ZVAL(*retval); + php_dom_create_implementation(retval TSRMLS_CC); + return SUCCESS; +} + +/* }}} */ + + + +/* {{{ proto domelement document_element documentElement +readonly=yes +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-87CD092 +Since: +*/ +int dom_document_document_element_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + xmlNode *root; + int ret; + + docp = (xmlDocPtr) dom_object_get_node(obj); + + root = xmlDocGetRootElement(docp); + if (!root) { + return FAILURE; + } + + ALLOC_ZVAL(*retval); + if (NULL == (*retval = php_dom_create_object(root, &ret, NULL, *retval, obj TSRMLS_CC))) { + php_error(E_WARNING, "Cannot create required DOM object"); + return FAILURE; + } + return SUCCESS; +} + +/* }}} */ + + +/* {{{ proto actual_encoding string +readonly=no +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-actualEncoding +Since: DOM Level 3 +*/ +/* READ ONLY FOR NOW USING ENCODING PROPERTY +int dom_document_actual_encoding_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + ALLOC_ZVAL(*retval); + ZVAL_NULL(*retval); + return SUCCESS; +} + +int dom_document_actual_encoding_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + return SUCCESS; +} +*/ + +/* }}} */ + +/* {{{ proto encoding string +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-encoding +Since: DOM Level 3 +*/ +int dom_document_encoding_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + char *encoding; + + docp = (xmlDocPtr) dom_object_get_node(obj); + encoding = (char *) docp->encoding; + ALLOC_ZVAL(*retval); + + if (encoding != NULL) { + ZVAL_STRING(*retval, encoding, 1); + } else { + ZVAL_NULL(*retval); + } + + return SUCCESS; +} + +int dom_document_encoding_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + xmlDoc *docp; + xmlCharEncodingHandlerPtr handler; + + docp = (xmlDocPtr) dom_object_get_node(obj); + + handler = xmlFindCharEncodingHandler(Z_STRVAL_P(newval)); + + if (handler != NULL) { + xmlCharEncCloseFunc(handler); + if (docp->encoding != NULL) { + xmlFree((xmlChar *)docp->encoding); + } + docp->encoding = xmlStrdup((const xmlChar *) Z_STRVAL_P(newval)); + return SUCCESS; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Document Encoding"); + } + + return SUCCESS; +} + +/* }}} */ + + + +/* {{{ proto standalone boolean +readonly=no +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-standalone +Since: DOM Level 3 +*/ +int dom_document_standalone_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + int standalone; + + docp = (xmlDocPtr) dom_object_get_node(obj); + ALLOC_ZVAL(*retval); + standalone = docp->standalone; + ZVAL_BOOL(*retval, standalone); + + return SUCCESS; +} + +int dom_document_standalone_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + xmlDoc *docp; + int standalone; + + docp = (xmlDocPtr) dom_object_get_node(obj); + standalone = Z_LVAL_P(newval); + if (standalone > 0) { + docp->standalone = 1; + } + else if (standalone < 0) { + docp->standalone = -1; + } + else { + docp->standalone = 0; + } + + return SUCCESS; +} + +/* }}} */ + + + +/* {{{ proto version string +readonly=no +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-version +Since: DOM Level 3 +*/ +int dom_document_version_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + char *version; + + docp = (xmlDocPtr) dom_object_get_node(obj); + version = (char *) docp->version; + ALLOC_ZVAL(*retval); + + if (version != NULL) { + ZVAL_STRING(*retval, version, 1); + } else { + ZVAL_NULL(*retval); + } + + return SUCCESS; +} + +int dom_document_version_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + xmlDoc *docp; + + docp = (xmlDocPtr) dom_object_get_node(obj); + if (docp->version != NULL) { + xmlFree((xmlChar *) docp->version ); + } + + docp->version = xmlStrdup((const xmlChar *) Z_STRVAL_P(newval)); + + return SUCCESS; +} + +/* }}} */ + +/* {{{ proto strict_error_checking boolean +readonly=no +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-strictErrorChecking +Since: DOM Level 3 +*/ +int dom_document_strict_error_checking_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->stricterror); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_strict_error_checking_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->stricterror = Z_LVAL_P(newval); + } + + return SUCCESS; +} + +/* }}} */ + +/* {{{ proto formatOutput boolean +readonly=no +*/ +int dom_document_format_output_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->formatoutput); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_format_output_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->formatoutput = Z_LVAL_P(newval); + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ proto validateonParse boolean +readonly=no +*/ +int dom_document_validate_on_parse_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->validateonparse); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_validate_on_parse_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->validateonparse = Z_LVAL_P(newval); + } + + return SUCCESS; +} +/* }}} */ + + +/* {{{ proto resolveExternals boolean +readonly=no +*/ +int dom_document_resolve_externals_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->resolveexternals); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_resolve_externals_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->resolveexternals = Z_LVAL_P(newval); + } + + return SUCCESS; +} +/* }}} */ + + +/* {{{ proto preserveWhiteSpace boolean +readonly=no +*/ +int dom_document_preserve_whitespace_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->preservewhitespace); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_preserve_whitespace_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->preservewhitespace = Z_LVAL_P(newval); + } + + return SUCCESS; +} +/* }}} */ + + +/* {{{ proto substituteEntities boolean +readonly=no +*/ +int dom_document_substitue_entities_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->substituteentities); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_substitue_entities_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->substituteentities = Z_LVAL_P(newval); + } + + return SUCCESS; +} +/* }}} */ + + +/* {{{ proto document_uri string +readonly=no +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-documentURI +Since: DOM Level 3 +*/ +int dom_document_document_uri_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + char *url; + + docp = (xmlDocPtr) dom_object_get_node(obj); + + ALLOC_ZVAL(*retval); + url = (char *) docp->URL; + if (url != NULL) { + ZVAL_STRING(*retval, url, 1); + } else { + ZVAL_NULL(*retval); + } + + return SUCCESS; +} + +int dom_document_document_uri_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + xmlDoc *docp; + + docp = (xmlDocPtr) dom_object_get_node(obj); + if (docp->URL != NULL) { + xmlFree((xmlChar *) docp->URL); + } + + docp->URL = xmlStrdup((const xmlChar *) Z_STRVAL_P(newval)); + + return SUCCESS; +} + +/* }}} */ + + + +/* {{{ proto config domconfiguration +readonly=yes +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-config +Since: DOM Level 3 +*/ +int dom_document_config_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + ALLOC_ZVAL(*retval); + ZVAL_NULL(*retval); + return SUCCESS; +} + +/* }}} */ + + + +/* {{{ proto domelement dom_document_create_element(string tagName); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-2141741547 +Since: +*/ +PHP_FUNCTION(dom_document_create_element) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + dom_object *intern; + int ret, name_len, value_len; + char *name, *value = NULL; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &name, &name_len, &value, &value_len) == FAILURE) { + return; + } + + node = xmlNewDocNode(docp, NULL, name, value); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_element */ + + +/* {{{ proto domdocumentfragment dom_document_create_document_fragment(); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-35CB04B5 +Since: +*/ +PHP_FUNCTION(dom_document_create_document_fragment) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + dom_object *intern; + int ret; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + node = xmlNewDocFragment(docp); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_document_fragment */ + + +/* {{{ proto domtext dom_document_create_text_node(string data); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1975348127 +Since: +*/ +PHP_FUNCTION(dom_document_create_text_node) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + int ret, value_len; + dom_object *intern; + char *value; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) == FAILURE) { + return; + } + + node = xmlNewDocText(docp, (xmlChar *) value); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_text_node */ + + +/* {{{ proto domcomment dom_document_create_comment(string data); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1334481328 +Since: +*/ +PHP_FUNCTION(dom_document_create_comment) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + int ret, value_len; + dom_object *intern; + char *value; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) == FAILURE) { + return; + } + + node = xmlNewDocComment(docp, (xmlChar *) value); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_comment */ + + +/* {{{ proto domcdatasection dom_document_create_cdatasection(string data); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D26C0AF8 +Since: +*/ +PHP_FUNCTION(dom_document_create_cdatasection) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + int ret, value_len; + dom_object *intern; + char *value; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) == FAILURE) { + return; + } + + node = xmlNewCDataBlock(docp, (xmlChar *) value, value_len); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_cdatasection */ + + +/* {{{ proto domprocessinginstruction dom_document_create_processing_instruction(string target, string data); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-135944439 +Since: +*/ +PHP_FUNCTION(dom_document_create_processing_instruction) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + int ret, value_len, name_len; + dom_object *intern; + char *name, *value = NULL; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &name, &name_len, &value, &value_len) == FAILURE) { + return; + } + + node = xmlNewPI((xmlChar *) name, (xmlChar *) value); + if (!node) { + RETURN_FALSE; + } + + node->doc = docp; + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_processing_instruction */ + + +/* {{{ proto domattr dom_document_create_attribute(string name); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1084891198 +Since: +*/ +PHP_FUNCTION(dom_document_create_attribute) +{ + zval *id, *rv = NULL; + xmlAttrPtr node; + xmlDocPtr docp; + int ret, name_len; + dom_object *intern; + char *name; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { + return; + } + + if (name_len == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Attribute name is required"); + RETURN_FALSE; + } + + node = xmlNewDocProp(docp, (xmlChar *) name, NULL); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, (xmlNodePtr) node, &ret, intern); + +} +/* }}} end dom_document_create_attribute */ + + +/* {{{ proto domentityreference dom_document_create_entity_reference(string name); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-392B75AE +Since: +*/ +PHP_FUNCTION(dom_document_create_entity_reference) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp = NULL; + dom_object *intern; + int ret, name_len; + char *name; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { + return; + } + + node = xmlNewReference(docp, name); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, (xmlNodePtr) node, &ret, intern); +} +/* }}} end dom_document_create_entity_reference */ + + +/* {{{ proto domnodelist dom_document_get_elements_by_tag_name(string tagname); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C9094 +Since: +*/ +PHP_FUNCTION(dom_document_get_elements_by_tag_name) +{ + zval *id; + xmlDocPtr docp; + int name_len; + dom_object *intern, *namednode; + char *name; + xmlChar *local; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { + return; + } + + php_dom_create_interator(return_value, DOM_NODELIST TSRMLS_CC); + namednode = (dom_object *)zend_objects_get_address(return_value TSRMLS_CC); + local = xmlCharStrndup(name, name_len); + dom_namednode_iter(intern, 0, namednode, NULL, local, NULL); +} +/* }}} end dom_document_get_elements_by_tag_name */ + + +/* {{{ proto domnode dom_document_import_node(node importedNode, boolean deep); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Core-Document-importNode +Since: DOM Level 2 +*/ +PHP_FUNCTION(dom_document_import_node) +{ + zval *rv = NULL; + zval *id, *node; + xmlDocPtr docp; + xmlNodePtr nodep, retnodep; + dom_object *intern, *nodeobj; + int ret; + long recursive = 0; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|l", &node, &recursive) == FAILURE) { + return; + } + + DOM_GET_OBJ(nodep, node, xmlNodePtr, nodeobj); + + if (nodep->type == XML_HTML_DOCUMENT_NODE || nodep->type == XML_DOCUMENT_NODE + || nodep->type == XML_DOCUMENT_TYPE_NODE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot import: Node Type Not Supported"); + RETURN_FALSE; + } + + if (nodep->doc == docp) { + retnodep = nodep; + } else { + retnodep = xmlDocCopyNode(nodep, docp, recursive); + if (!retnodep) { + RETURN_FALSE; + } + } + + DOM_RET_OBJ(rv, (xmlNodePtr) retnodep, &ret, intern); +} +/* }}} end dom_document_import_node */ + + +/* {{{ proto domelement dom_document_create_element_ns(string namespaceURI, string qualifiedName); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNS +Since: DOM Level 2 +*/ +PHP_FUNCTION(dom_document_create_element_ns) +{ + zval *id, *rv = NULL; + xmlDocPtr docp; + xmlNodePtr nodep = NULL; + xmlNsPtr nsptr = NULL; + int ret, uri_len = 0, name_len = 0; + char *uri, *name; + char *localname = NULL, *prefix = NULL; + int errorcode; + dom_object *intern; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) { + return; + } + + errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len); + + if (errorcode == 0) { + nodep = xmlNewDocNode (docp, NULL, localname, NULL); + if (nodep != NULL && uri != NULL) { + nsptr = xmlSearchNsByHref (nodep->doc, nodep, uri); + if (nsptr == NULL) { + nsptr = dom_get_ns(nodep, uri, &errorcode, prefix); + } + xmlSetNs(nodep, nsptr); + } + } + + xmlFree(localname); + if (prefix != NULL) { + xmlFree(prefix); + } + + if (errorcode != 0) { + if (nodep != NULL) { + xmlFreeNode(nodep); + } + php_dom_throw_error(errorcode, dom_get_strict_error(intern->document) TSRMLS_CC); + RETURN_FALSE; + } + + if (nodep == NULL) { + RETURN_FALSE; + } + + + nodep->ns = nsptr; + + DOM_RET_OBJ(rv, nodep, &ret, intern); +} +/* }}} end dom_document_create_element_ns */ + + +/* {{{ proto domattr dom_document_create_attribute_ns(string namespaceURI, string qualifiedName); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrAttrNS +Since: DOM Level 2 +*/ +PHP_FUNCTION(dom_document_create_attribute_ns) +{ + zval *id, *rv = NULL; + xmlDocPtr docp; + xmlNodePtr nodep = NULL, root; + xmlNsPtr nsptr; + int ret, uri_len = 0, name_len = 0; + char *uri, *name; + char *localname = NULL, *prefix = NULL; + dom_object *intern; + int errorcode; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) { + return; + } + + root = xmlDocGetRootElement(docp); + if (root != NULL) { + errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len); + if (errorcode == 0) { + nodep = (xmlNodePtr) xmlNewDocProp(docp, localname, NULL); + if (nodep != NULL && uri_len > 0) { + nsptr = xmlSearchNsByHref (nodep->doc, root, uri); + if (nsptr == NULL) { + nsptr = dom_get_ns(root, uri, &errorcode, prefix); + } + xmlSetNs(nodep, nsptr); + } + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Document Missing Root Element"); + RETURN_FALSE; + } + + xmlFree(localname); + if (prefix != NULL) { + xmlFree(prefix); + } + + if (errorcode != 0) { + if (nodep != NULL) { + xmlFreeProp((xmlAttrPtr) nodep); + } + php_dom_throw_error(errorcode, dom_get_strict_error(intern->document) TSRMLS_CC); + RETURN_FALSE; + } + + if (nodep == NULL) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, nodep, &ret, intern); +} +/* }}} end dom_document_create_attribute_ns */ + + +/* {{{ proto domnodelist dom_document_get_elements_by_tag_name_ns(string namespaceURI, string localName); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBTNNS +Since: DOM Level 2 +*/ +PHP_FUNCTION(dom_document_get_elements_by_tag_name_ns) +{ + zval *id; + xmlDocPtr docp; + int uri_len, name_len; + dom_object *intern, *namednode; + char *uri, *name; + xmlChar *local, *nsuri; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &uri, &uri_len, &name, &name_len) == FAILURE) { + return; + } + + php_dom_create_interator(return_value, DOM_NODELIST TSRMLS_CC); + namednode = (dom_object *)zend_objects_get_address(return_value TSRMLS_CC); + local = xmlCharStrndup(name, name_len); + nsuri = xmlCharStrndup(uri, uri_len); + dom_namednode_iter(intern, 0, namednode, NULL, local, nsuri); +} +/* }}} end dom_document_get_elements_by_tag_name_ns */ + + +/* {{{ proto domelement dom_document_get_element_by_id(string elementId); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBId +Since: DOM Level 2 +*/ +PHP_FUNCTION(dom_document_get_element_by_id) +{ + zval *id, *rv = NULL; + xmlDocPtr docp; + idsIterator iter; + xmlHashTable *ids = NULL; + int ret,idname_len; + dom_object *intern; + char *idname; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &idname, &idname_len) == FAILURE) { + return; + } + + ids = (xmlHashTable *) docp->ids; + if (ids) { + iter.elementId = (xmlChar *) idname; + iter.element = NULL; + xmlHashScan(ids, (void *)idsHashScanner, &iter); + DOM_RET_OBJ(rv, (xmlNodePtr) iter.element, &ret, intern); + } else { + RETVAL_NULL(); + } +} +/* }}} end dom_document_get_element_by_id */ + + +/* {{{ proto domnode dom_document_adopt_node(node source); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-adoptNode +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_adopt_node) +{ + DOM_NOT_IMPLEMENTED(); +} +/* }}} end dom_document_adopt_node */ + + +/* {{{ proto dom_void dom_document_normalize_document(); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-normalizeDocument +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_normalize_document) +{ + zval *id; + xmlDocPtr docp; + dom_object *intern; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + DOM_NO_ARGS(); + + dom_normalize((xmlNodePtr) docp TSRMLS_CC); +} +/* }}} end dom_document_normalize_document */ + + +/* {{{ proto domnode dom_document_rename_node(node n, string namespaceURI, string qualifiedName); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-renameNode +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_rename_node) +{ + DOM_NOT_IMPLEMENTED(); +} +/* }}} end dom_document_rename_node */ + +/* {{{ proto domnode dom_document_document([string version], [string encoding]); */ +PHP_FUNCTION(dom_document_document) +{ + + zval *id; + xmlDoc *docp = NULL, *olddoc; + dom_object *intern; + char *encoding, *version = NULL; + int encoding_len = 0, version_len = 0, refcount; + + id = getThis(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ss", &version, &version_len, &encoding, &encoding_len) == FAILURE) { + return; + } + + docp = xmlNewDoc(version); + if (!docp) + RETURN_FALSE; + + if (encoding_len > 0) { + docp->encoding = (const xmlChar*)xmlStrdup(encoding); + } + + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + if (intern != NULL) { + olddoc = (xmlDocPtr) dom_object_get_node(intern); + if (olddoc != NULL) { + php_libxml_decrement_node_ptr((php_libxml_node_object *) intern TSRMLS_CC); + refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern TSRMLS_CC); + if (refcount != 0) { + olddoc->_private = NULL; + } + } + intern->document = NULL; + php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp TSRMLS_CC); + php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)docp, (void *)intern TSRMLS_CC); + } +} +/* }}} end dom_document_document */ + +char *_dom_get_valid_file_path(char *source, char *resolved_path, int resolved_path_len TSRMLS_DC) { + xmlURI *uri; + xmlChar *escsource; + char *file_dest; + int isFileUri = 0; + + uri = xmlCreateURI(); + escsource = xmlURIEscapeStr(source, ":"); + xmlParseURIReference(uri, escsource); + xmlFree(escsource); + + if (uri->scheme != NULL) { + /* absolute file uris - libxml only supports localhost or empty host */ + if (strncasecmp(source, "file:///",8) == 0) { + isFileUri = 1; +#ifdef PHP_WIN32 + source += 8; +#else + source += 7; +#endif + } else if (strncasecmp(source, "file://localhost/",17) == 0) { + isFileUri = 1; +#ifdef PHP_WIN32 + source += 17; +#else + source += 16; +#endif + } + } + + file_dest = source; + + if ((uri->scheme == NULL || isFileUri)) { + /* XXX possible buffer overflow if VCWD_REALPATH does not know size of resolved_path */ + if (! VCWD_REALPATH(source, resolved_path)) { + expand_filepath(source, resolved_path TSRMLS_CC); + } + file_dest = resolved_path; + } + + xmlFreeURI(uri); + + return file_dest; +} + + +/* {{{ */ +static xmlDocPtr dom_document_parser(zval *id, int mode, char *source TSRMLS_DC) { + xmlDocPtr ret; + xmlParserCtxtPtr ctxt = NULL; + dom_doc_props *doc_props; + dom_object *intern; + php_libxml_ref_obj *document = NULL; + int validate, resolve_externals, keep_blanks, substitute_ent; + int resolved_path_len; + char *directory=NULL, resolved_path[MAXPATHLEN]; + + if (id != NULL) { + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + document = intern->document; + } + + doc_props = dom_get_doc_props(document); + validate = doc_props->validateonparse; + resolve_externals = doc_props->resolveexternals; + keep_blanks = doc_props->preservewhitespace; + substitute_ent = doc_props->substituteentities; + + if (document == NULL) { + efree(doc_props); + } + + xmlInitParser(); + + keep_blanks = xmlKeepBlanksDefault(keep_blanks); + + if (mode == DOM_LOAD_FILE) { + char *file_dest = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC); + if (file_dest) { + ctxt = xmlCreateFileParserCtxt(file_dest); + } + + } else { + ctxt = xmlCreateDocParserCtxt(source); + } + + xmlKeepBlanksDefault(keep_blanks); + /* xmlIndentTreeOutput default is changed in xmlKeepBlanksDefault + reset back to 1 which is default value */ + + xmlIndentTreeOutput = 1; + + if (ctxt == NULL) { + return(NULL); + } + + /* If loading from memory, we need to set the base directory for the document */ + if (mode != DOM_LOAD_FILE) { +#if HAVE_GETCWD + directory = VCWD_GETCWD(resolved_path, MAXPATHLEN); +#elif HAVE_GETWD + directory = VCWD_GETWD(resolved_path); +#endif + if (directory) { + if(ctxt->directory != NULL) { + xmlFree((char *) ctxt->directory); + } + resolved_path_len = strlen(resolved_path); + if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) { + resolved_path[resolved_path_len] = DEFAULT_SLASH; + resolved_path[++resolved_path_len] = '\0'; + } + ctxt->directory = (char *) xmlCanonicPath((const xmlChar *) resolved_path); + } + } + + ctxt->recovery = 0; + ctxt->validate = validate; + ctxt->loadsubset = (resolve_externals * XML_COMPLETE_ATTRS); + ctxt->replaceEntities = substitute_ent; + + ctxt->vctxt.error = php_libxml_ctx_error; + ctxt->vctxt.warning = php_libxml_ctx_warning; + + if (ctxt->sax != NULL) { + ctxt->sax->error = php_libxml_ctx_error; + ctxt->sax->warning = php_libxml_ctx_warning; + } + + xmlParseDocument(ctxt); + + if (ctxt->wellFormed) { + ret = ctxt->myDoc; + /* If loading from memory, set the base reference uri for the document */ + if (ret->URL == NULL && ctxt->directory != NULL) { + ret->URL = xmlStrdup(ctxt->directory); + } + } else { + ret = NULL; + xmlFreeDoc(ctxt->myDoc); + ctxt->myDoc = NULL; + } + + xmlFreeParserCtxt(ctxt); + + return(ret); +} +/* }}} */ + +/* {{{ static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) */ +static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) { + zval *id, *rv = NULL; + xmlDoc *docp = NULL, *newdoc; + dom_doc_props *doc_prop; + dom_object *intern; + char *source; + int source_len, refcount, ret; + + id = getThis(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &source, &source_len) == FAILURE) { + return; + } + + newdoc = dom_document_parser(id, mode, source TSRMLS_CC); + + if (!newdoc) + RETURN_FALSE; + + if (id != NULL) { + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + if (intern != NULL) { + docp = (xmlDocPtr) dom_object_get_node(intern); + doc_prop = NULL; + if (docp != NULL) { + php_libxml_decrement_node_ptr((php_libxml_node_object *) intern TSRMLS_CC); + doc_prop = intern->document->doc_props; + intern->document->doc_props = NULL; + refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern TSRMLS_CC); + if (refcount != 0) { + docp->_private = NULL; + } + } + intern->document = NULL; + php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc TSRMLS_CC); + intern->document->doc_props = doc_prop; + } + + php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern TSRMLS_CC); + + RETURN_TRUE; + } else { + DOM_RET_OBJ(rv, (xmlNodePtr) newdoc, &ret, NULL); + } +} +/* }}} end dom_parser_document */ + +/* {{{ proto boolean domnode dom_document_load(string source); +URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-load +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_load) +{ + dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); +} +/* }}} end dom_document_load */ + +/* {{{ proto boolean domnode dom_document_loadxml(string source); +URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-loadXML +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_loadxml) +{ + dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); +} +/* }}} end dom_document_loadxml */ + +/* {{{ proto long domnode dom_document_save(string file); +Convenience method to save to file +*/ +PHP_FUNCTION(dom_document_save) +{ + zval *id; + xmlDoc *docp; + int file_len, bytes, format; + dom_object *intern; + dom_doc_props *doc_props; + char *file; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) { + return; + } + + /* encoding handled by property on doc */ + + doc_props = dom_get_doc_props(intern->document); + format = doc_props->formatoutput; + bytes = xmlSaveFormatFileEnc(file, docp, NULL, format); + + if (bytes == -1) { + RETURN_FALSE; + } + RETURN_LONG(bytes); +} +/* }}} end dom_document_save */ + +/* {{{ proto string domnode dom_document_savexml([node n]); +URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_savexml) +{ + zval *id, *nodep = NULL; + xmlDoc *docp; + xmlNode *node; + xmlBufferPtr buf; + xmlChar *mem; + dom_object *intern, *nodeobj; + dom_doc_props *doc_props; + int size, format; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|o", &nodep) == FAILURE) { + return; + } + + doc_props = dom_get_doc_props(intern->document); + format = doc_props->formatoutput; + + if (nodep != NULL) { + /* Dump contents of Node */ + DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj); + if (node->doc != docp) { + php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document) TSRMLS_CC); + RETURN_FALSE; + } + buf = xmlBufferCreate(); + if (!buf) { + php_error(E_WARNING, "Could not fetch buffer"); + RETURN_FALSE; + } + + xmlNodeDump(buf, docp, node, 0, format); + mem = (xmlChar*) xmlBufferContent(buf); + if (!mem) { + xmlBufferFree(buf); + RETURN_FALSE; + } + RETVAL_STRING(mem, 1); + xmlBufferFree(buf); + } else { + /* Encoding is handled from the encoding property set on the document */ + xmlDocDumpFormatMemory(docp, &mem, &size, format); + if (!size) { + RETURN_FALSE; + } + RETVAL_STRINGL(mem, size, 1); + xmlFree(mem); + } +} +/* }}} end dom_document_savexml */ + +/* {{{ proto int dom_document_xinclude() + Substitutues xincludes in a DomDocument */ +PHP_FUNCTION(dom_document_xinclude) +{ + zval *id; + xmlDoc *docp; + int err; + dom_object *intern; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + err = xmlXIncludeProcess (docp); + + if (err) { + RETVAL_LONG(err); + } else { + RETVAL_FALSE; + } + +} + + +/* {{{ proto string domnode dom_document_validate(); +Since: DOM extended +*/ +PHP_FUNCTION(dom_document_validate) +{ + zval *id; + xmlDoc *docp; + dom_object *intern; + xmlValidCtxt cvp; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (docp->intSubset == NULL) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "No DTD given in XML-Document"); + } + + cvp.userData = NULL; + cvp.error = (xmlValidityErrorFunc) php_libxml_error_handler; + cvp.warning = (xmlValidityErrorFunc) php_libxml_error_handler; + + if (xmlValidateDocument(&cvp, docp)) { + RETVAL_TRUE; + } else { + RETVAL_FALSE; + } +} +/* }}} end dom_document_validate */ + +#if defined(LIBXML_SCHEMAS_ENABLED) +static void +_dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type) +{ + zval *id; + xmlDoc *docp; + dom_object *intern; + char *source = NULL, *valid_file = NULL; + int source_len = 0; + xmlSchemaParserCtxtPtr parser; + xmlSchemaPtr sptr; + xmlSchemaValidCtxtPtr vptr; + int is_valid; + char resolved_path[MAXPATHLEN + 1]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &source, &source_len) == FAILURE) { + return; + } + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + switch (type) { + case DOM_LOAD_FILE: + valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC); + if (!valid_file) { + php_error(E_WARNING, "Invalid Schema file source"); + RETURN_FALSE; + } + parser = xmlSchemaNewParserCtxt(valid_file); + break; + case DOM_LOAD_STRING: + parser = xmlSchemaNewMemParserCtxt(source, source_len); + /* If loading from memory, we need to set the base directory for the document + but it is not apparent how to do that for schema's */ + break; + } + + xmlSchemaSetParserErrors(parser, + (xmlSchemaValidityErrorFunc) php_libxml_error_handler, + (xmlSchemaValidityWarningFunc) php_libxml_error_handler, + parser); + sptr = xmlSchemaParse(parser); + xmlSchemaFreeParserCtxt(parser); + if (!sptr) { + php_error(E_WARNING, "Invalid Schema"); + RETURN_FALSE; + } + + docp = (xmlDocPtr) dom_object_get_node(intern); + + vptr = xmlSchemaNewValidCtxt(sptr); + if (!vptr) { + xmlSchemaFree(sptr); + php_error(E_ERROR, "Invalid Schema Validation Context"); + RETURN_FALSE; + } + + xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr); + is_valid = xmlSchemaValidateDoc(vptr, docp); + xmlSchemaFree(sptr); + xmlSchemaFreeValidCtxt(vptr); + + if (is_valid == 0) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} + +/* {{{ proto boolean domnode _dom_document_schema_validate(string filename); */ +PHP_FUNCTION(dom_document_schema_validate_file) +{ + _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); +} +/* }}} end _dom_document_schema_validate */ + +/* {{{ proto boolean domnode _dom_document_schema_validate(string source); */ +PHP_FUNCTION(dom_document_schema_validate_xml) +{ + _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); +} +/* }}} end _dom_document_schema_validate */ + + +static void +_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int type) +{ + zval *id; + xmlDoc *docp; + dom_object *intern; + char *source = NULL, *valid_file = NULL; + int source_len = 0; + xmlRelaxNGParserCtxtPtr parser; + xmlRelaxNGPtr sptr; + xmlRelaxNGValidCtxtPtr vptr; + int is_valid; + char resolved_path[MAXPATHLEN + 1]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &source, &source_len) == FAILURE) { + return; + } + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + switch (type) { + case DOM_LOAD_FILE: + valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC); + if (!valid_file) { + php_error(E_WARNING, "Invalid RelaxNG file source"); + RETURN_FALSE; + } + parser = xmlRelaxNGNewParserCtxt(valid_file); + break; + case DOM_LOAD_STRING: + parser = xmlRelaxNGNewMemParserCtxt(source, source_len); + /* If loading from memory, we need to set the base directory for the document + but it is not apparent how to do that for schema's */ + break; + } + + xmlRelaxNGSetParserErrors(parser, + (xmlRelaxNGValidityErrorFunc) php_libxml_error_handler, + (xmlRelaxNGValidityWarningFunc) php_libxml_error_handler, + parser); + sptr = xmlRelaxNGParse(parser); + xmlRelaxNGFreeParserCtxt(parser); + if (!sptr) { + php_error(E_WARNING, "Invalid RelaxNG"); + RETURN_FALSE; + } + + docp = (xmlDocPtr) dom_object_get_node(intern); + + vptr = xmlRelaxNGNewValidCtxt(sptr); + if (!vptr) { + xmlRelaxNGFree(sptr); + php_error(E_ERROR, "Invalid RelaxNG Validation Context"); + RETURN_FALSE; + } + + xmlRelaxNGSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr); + is_valid = xmlRelaxNGValidateDoc(vptr, docp); + xmlRelaxNGFree(sptr); + xmlRelaxNGFreeValidCtxt(vptr); + + if (is_valid == 0) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} + +/* {{{ proto boolean domnode dom_document_relaxNG_validate_file(string filename); */ +PHP_FUNCTION(dom_document_relaxNG_validate_file) +{ + _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); +} +/* }}} end dom_document_relaxNG_validate_file */ + +/* {{{ proto boolean domnode dom_document_relaxNG_validate_xml(string source); */ +PHP_FUNCTION(dom_document_relaxNG_validate_xml) +{ + _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); +} +/* }}} end dom_document_relaxNG_validate_xml */ + +#endif + +#if defined(LIBXML_HTML_ENABLED) + +static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) +{ + zval *id, *rv = NULL; + xmlDoc *docp = NULL, *newdoc; + dom_object *intern; + dom_doc_props *doc_prop; + char *source; + int source_len, refcount, ret; + htmlParserCtxtPtr ctxt; + + id = getThis(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &source, &source_len) == FAILURE) { + return; + } + + if (mode == DOM_LOAD_FILE) { + ctxt = htmlCreateFileParserCtxt(source, NULL); + } else { + source_len = xmlStrlen(source); + ctxt = htmlCreateMemoryParserCtxt(source, source_len); + } + ctxt->vctxt.error = php_libxml_ctx_error; + ctxt->vctxt.warning = php_libxml_ctx_warning; + if (ctxt->sax != NULL) { + ctxt->sax->error = php_libxml_ctx_error; + ctxt->sax->warning = php_libxml_ctx_warning; + } + htmlParseDocument(ctxt); + newdoc = ctxt->myDoc; + htmlFreeParserCtxt(ctxt); + + if (!newdoc) + RETURN_FALSE; + + if (id != NULL) { + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + if (intern != NULL) { + docp = (xmlDocPtr) dom_object_get_node(intern); + doc_prop = NULL; + if (docp != NULL) { + php_libxml_decrement_node_ptr((php_libxml_node_object *) intern TSRMLS_CC); + doc_prop = intern->document->doc_props; + intern->document->doc_props = NULL; + refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern TSRMLS_CC); + if (refcount != 0) { + docp->_private = NULL; + } + } + intern->document = NULL; + php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc TSRMLS_CC); + intern->document->doc_props = doc_prop; + } + + php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern TSRMLS_CC); + + RETURN_TRUE; + } else { + DOM_RET_OBJ(rv, (xmlNodePtr) newdoc, &ret, NULL); + } +} + +/* {{{ proto boolean domnode dom_document_load_html_file(string source); +Since: DOM extended +*/ +PHP_FUNCTION(dom_document_load_html_file) +{ + dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); +} +/* }}} end dom_document_load_html_file */ + +/* {{{ proto boolean domnode dom_document_load_html(string source); +Since: DOM extended +*/ +PHP_FUNCTION(dom_document_load_html) +{ + dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); +} +/* }}} end dom_document_load_html */ + +/* {{{ proto long dom_document_save_html_file(string file); +Convenience method to save to file as html +*/ +PHP_FUNCTION(dom_document_save_html_file) +{ + zval *id; + xmlDoc *docp; + int file_len, bytes, format; + dom_object *intern; + dom_doc_props *doc_props; + char *file; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) { + return; + } + + /* encoding handled by property on doc */ + + doc_props = dom_get_doc_props(intern->document); + format = doc_props->formatoutput; + bytes = htmlSaveFileFormat(file, docp, NULL, format); + + if (bytes == -1) { + RETURN_FALSE; + } + RETURN_LONG(bytes); +} +/* }}} end dom_document_save_html_file */ + +/* {{{ proto string dom_document_save_html(); +Convenience method to output as html +*/ +PHP_FUNCTION(dom_document_save_html) +{ + zval *id; + xmlDoc *docp; + dom_object *intern; + xmlChar *mem; + int size; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + htmlDocDumpMemory(docp, &mem, &size); + if (!size) { + if (mem) + xmlFree(mem); + RETURN_FALSE; + } + RETVAL_STRINGL(mem, size, 1); + xmlFree(mem); +} +/* }}} end dom_document_save_html */ + +#endif /* defined(LIBXML_HTML_ENABLED) */ + +#endif /* HAVE_LIBXML && HAVE_DOM */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_81f05c1_b1d8f1_ext#mysqli#mysqli_nonapi.c b/src/main/resource/testFiles/php-src/prevFiles/prev_81f05c1_b1d8f1_ext#mysqli#mysqli_nonapi.c new file mode 100644 index 0000000..cf97f65 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_81f05c1_b1d8f1_ext#mysqli#mysqli_nonapi.c @@ -0,0 +1,269 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Georg Richter | + +----------------------------------------------------------------------+ + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_mysqli.h" + +/* {{{ proto object mysqli_connect([string hostname [,string username [,string passwd [,string dbname [,int port [,string socket]]]]]]) + Open a connection to a mysql server */ +PHP_FUNCTION(mysqli_connect) +{ + MYSQL *mysql = NULL; + MYSQLI_RESOURCE *mysqli_resource; + zval *object = getThis(); + char *hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL; + unsigned int hostname_len, username_len, passwd_len, dbname_len, socket_len; + unsigned int port=0; + + + if (getThis() && !ZEND_NUM_ARGS()) { + RETURN_NULL(); + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ssssls", &hostname, &hostname_len, &username, &username_len, + &passwd, &passwd_len, &dbname, &dbname_len, &port, &socket, &socket_len) == FAILURE) { + return; + } + + /* TODO: safe mode handling */ + if (PG(sql_safe_mode)){ + } else { + if (!passwd) { + passwd = MyG(default_pw); + if (!username){ + username = MyG(default_user); + if (!hostname) { + hostname = MyG(default_host); + } + } + } + } + + mysql = mysql_init(NULL); + + if (mysql_real_connect(mysql,hostname,username,passwd,dbname,port,socket,0) == NULL) { + /* Save error messages */ + + MYSQLI_REPORT_MYSQL_ERROR(mysql); + php_mysqli_set_error(mysql_errno(mysql), (char *) mysql_error(mysql) TSRMLS_CC); + + if (!(MyG(report_mode) & MYSQLI_REPORT_ERROR)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysql_error(mysql)); + } + /* free mysql structure */ + mysql_close(mysql); + RETURN_FALSE; + } + + /* clear error */ + php_mysqli_set_error(mysql_errno(mysql), (char *) mysql_error(mysql) TSRMLS_CC); + + mysql->reconnect = 0; + + mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); + mysqli_resource->ptr = (void *)mysql; + + if (!object) { + MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_link_class_entry); + } else { + ((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->ptr = mysqli_resource; + ((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->valid = 1; + } +} +/* }}} */ + +#ifdef HAVE_EMBEDDED_MYSQLI +/* {{{ proto object mysqli_embedded_connect(void) + Open a connection to a embedded mysql server */ +PHP_FUNCTION(mysqli_embedded_connect) +{ + MYSQL *mysql; + MYSQLI_RESOURCE *mysqli_resource; + zval *object = getThis(); + char *dbname = NULL; + int dblen = 0; + + if (!MyG(embedded)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Embedded server was not initialized."); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &dbname, &dblen) == FAILURE) { + return; + } + + mysql = mysql_init(NULL); + + if (mysql_real_connect(mysql, NULL, NULL, NULL, dbname, 0, NULL, 0) == NULL) { + MYSQLI_REPORT_MYSQL_ERROR(mysql); + php_mysqli_set_error(mysql_errno(mysql), (char *) mysql_error(mysql) TSRMLS_CC); + + if (!(MyG(report_mode) & MYSQLI_REPORT_ERROR)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysql_error(mysql)); + } + /* free mysql structure */ + mysql_close(mysql); + RETURN_FALSE; + } + + php_mysqli_set_error(mysql_errno(mysql), (char *) mysql_error(mysql) TSRMLS_CC); + + mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); + mysqli_resource->ptr = (void *)mysql; + + if (!object) { + MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_link_class_entry); + } else { + ((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->ptr = mysqli_resource; + } +} +/* }}} */ +#endif + +/* {{{ proto int mysqli_connect_errno(void) + Returns the numerical value of the error message from last connect command */ +PHP_FUNCTION(mysqli_connect_errno) +{ + RETURN_LONG(MyG(error_no)); +} +/* }}} */ + +/* {{{ proto string mysqli_connect_error(void) + Returns the text of the error message from previous MySQL operation */ +PHP_FUNCTION(mysqli_connect_error) +{ + if (MyG(error_msg)) { + RETURN_STRING(MyG(error_msg),1); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto mixed mysqli_fetch_array (object result [,int resulttype]) + Fetch a result row as an associative array, a numeric array, or both */ +PHP_FUNCTION(mysqli_fetch_array) +{ + php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); +} +/* }}} */ + +/* {{{ proto mixed mysqli_fetch_assoc (object result) + Fetch a result row as an associative array */ +PHP_FUNCTION(mysqli_fetch_assoc) +{ + php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 0); +} +/* }}} */ + +/* {{{ proto mixed mysqli_fetch_object (object result [, string class_name [, NULL|array ctor_params]]) + Fetch a result row as an object */ +PHP_FUNCTION(mysqli_fetch_object) +{ + php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 1); +} +/* }}} */ + +/* {{{ proto bool mysqli_multi_query(object link, string query) + Binary-safe version of mysql_query() */ +PHP_FUNCTION(mysqli_multi_query) +{ + MYSQL *mysql; + zval *mysql_link; + char *query = NULL; + unsigned int query_len; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &mysql_link, mysqli_link_class_entry, &query, &query_len) == FAILURE) { + return; + } + MYSQLI_FETCH_RESOURCE(mysql, MYSQL *, &mysql_link, "mysqli_link"); + + MYSQLI_ENABLE_MQ; + if (mysql_real_query(mysql, query, query_len)) { + MYSQLI_DISABLE_MQ; + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto mixed mysqli_query(object link, string query [,int resultmode]) */ +PHP_FUNCTION(mysqli_query) +{ + MYSQL *mysql; + zval *mysql_link; + MYSQLI_RESOURCE *mysqli_resource; + MYSQL_RES *result; + char *query = NULL; + unsigned int query_len; + unsigned int resultmode = 0; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", &mysql_link, mysqli_link_class_entry, &query, &query_len, &resultmode) == FAILURE) { + return; + } + MYSQLI_FETCH_RESOURCE(mysql, MYSQL*, &mysql_link, "mysqli_link"); + + MYSQLI_DISABLE_MQ; + + if (mysql_real_query(mysql, query, query_len)) { + MYSQLI_REPORT_MYSQL_ERROR(mysql); + RETURN_FALSE; + } + + + if (!mysql_field_count(mysql)) { + if (MyG(report_mode) & MYSQLI_REPORT_INDEX) { + php_mysqli_report_index(query, mysql->server_status TSRMLS_CC); + } + RETURN_TRUE; + } + + result = (resultmode == MYSQLI_USE_RESULT) ? mysql_use_result(mysql) : mysql_store_result(mysql); + + if (!result) { + RETURN_FALSE; + } + + if (MyG(report_mode) & MYSQLI_REPORT_INDEX) { + php_mysqli_report_index(query, mysql->server_status TSRMLS_CC); + } + + mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); + mysqli_resource->ptr = (void *)result; + MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_820983_90ee88_ext#standard#html.c b/src/main/resource/testFiles/php-src/prevFiles/prev_820983_90ee88_ext#standard#html.c new file mode 100644 index 0000000..615eb47 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_820983_90ee88_ext#standard#html.c @@ -0,0 +1,1366 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jaakko Hyvätti | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* + * HTML entity resources: + * + * http://msdn.microsoft.com/workshop/author/dhtml/reference/charsets/charset2.asp + * http://msdn.microsoft.com/workshop/author/dhtml/reference/charsets/charset3.asp + * http://www.unicode.org/Public/MAPPINGS/OBSOLETE/UNI2SGML.TXT + * + * http://www.w3.org/TR/2002/REC-xhtml1-20020801/dtds.html#h-A2 + * + */ + +#include "php.h" +#if PHP_WIN32 +#include "config.w32.h" +#elif defined NETWARE +#include "config.nw.h" +#else +#include +#endif +#include "reg.h" +#include "html.h" +#include "php_string.h" +#include "SAPI.h" +#if HAVE_LOCALE_H +#include +#endif +#if HAVE_LANGINFO_H +#include +#endif + +#if HAVE_MBSTRING +# include "ext/mbstring/mbstring.h" +ZEND_EXTERN_MODULE_GLOBALS(mbstring) +#endif + +enum entity_charset { cs_terminator, cs_8859_1, cs_cp1252, + cs_8859_15, cs_utf_8, cs_big5, cs_gb2312, + cs_big5hkscs, cs_sjis, cs_eucjp, cs_koi8r, + cs_cp1251, cs_8859_5, cs_cp866, cs_macroman + }; +typedef const char *entity_table_t; + +/* codepage 1252 is a Windows extension to iso-8859-1. */ +static entity_table_t ent_cp_1252[] = { + "euro", NULL, "sbquo", "fnof", "bdquo", "hellip", "dagger", + "Dagger", "circ", "permil", "Scaron", "lsaquo", "OElig", + NULL, NULL, NULL, NULL, "lsquo", "rsquo", "ldquo", "rdquo", + "bull", "ndash", "mdash", "tilde", "trade", "scaron", "rsaquo", + "oelig", NULL, NULL, "Yuml" +}; + +static entity_table_t ent_iso_8859_1[] = { + "nbsp", "iexcl", "cent", "pound", "curren", "yen", "brvbar", + "sect", "uml", "copy", "ordf", "laquo", "not", "shy", "reg", + "macr", "deg", "plusmn", "sup2", "sup3", "acute", "micro", + "para", "middot", "cedil", "sup1", "ordm", "raquo", "frac14", + "frac12", "frac34", "iquest", "Agrave", "Aacute", "Acirc", + "Atilde", "Auml", "Aring", "AElig", "Ccedil", "Egrave", + "Eacute", "Ecirc", "Euml", "Igrave", "Iacute", "Icirc", + "Iuml", "ETH", "Ntilde", "Ograve", "Oacute", "Ocirc", "Otilde", + "Ouml", "times", "Oslash", "Ugrave", "Uacute", "Ucirc", "Uuml", + "Yacute", "THORN", "szlig", "agrave", "aacute", "acirc", + "atilde", "auml", "aring", "aelig", "ccedil", "egrave", + "eacute", "ecirc", "euml", "igrave", "iacute", "icirc", + "iuml", "eth", "ntilde", "ograve", "oacute", "ocirc", "otilde", + "ouml", "divide", "oslash", "ugrave", "uacute", "ucirc", + "uuml", "yacute", "thorn", "yuml" +}; + +static entity_table_t ent_iso_8859_15[] = { + "nbsp", "iexcl", "cent", "pound", "euro", "yen", "Scaron", + "sect", "scaron", "copy", "ordf", "laquo", "not", "shy", "reg", + "macr", "deg", "plusmn", "sup2", "sup3", NULL, /* Zcaron */ + "micro", "para", "middot", NULL, /* zcaron */ "sup1", "ordm", + "raquo", "OElig", "oelig", "Yuml", "iquest", "Agrave", "Aacute", + "Acirc", "Atilde", "Auml", "Aring", "AElig", "Ccedil", "Egrave", + "Eacute", "Ecirc", "Euml", "Igrave", "Iacute", "Icirc", + "Iuml", "ETH", "Ntilde", "Ograve", "Oacute", "Ocirc", "Otilde", + "Ouml", "times", "Oslash", "Ugrave", "Uacute", "Ucirc", "Uuml", + "Yacute", "THORN", "szlig", "agrave", "aacute", "acirc", + "atilde", "auml", "aring", "aelig", "ccedil", "egrave", + "eacute", "ecirc", "euml", "igrave", "iacute", "icirc", + "iuml", "eth", "ntilde", "ograve", "oacute", "ocirc", "otilde", + "ouml", "divide", "oslash", "ugrave", "uacute", "ucirc", + "uuml", "yacute", "thorn", "yuml" +}; + +static entity_table_t ent_uni_338_402[] = { + /* 338 (0x0152) */ + "OElig", "oelig", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 352 (0x0160) */ + "Scaron", "scaron", NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 376 (0x0178) + "Yuml", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, */ + /* 400 (0x0190)*/ + NULL, NULL, "fnof" +}; + +static entity_table_t ent_uni_spacing[] = { + /* 710 */ + "circ", + /* 711 - 730 */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 731 - 732 */ + NULL, "tilde" +}; + +static entity_table_t ent_uni_greek[] = { + /* 913 */ + "Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", + "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", + NULL, "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega", + /* 938 - 944 are not mapped */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", + "iota", "kappa", "lambda", "mu", "nu", "xi", "omicron", "pi", "rho", + "sigmaf", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega", + /* 970 - 976 are not mapped */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "thetasym", "upsih", + NULL, NULL, NULL, + "piv" +}; + +static entity_table_t ent_uni_punct[] = { + /* 8194 */ + "ensp", "emsp", NULL, NULL, NULL, NULL, NULL, + "thinsp", NULL, NULL, "zwnj", "zwj", "lrm", "rlm", + NULL, NULL, NULL, "ndash", "mdash", NULL, NULL, NULL, + /* 8216 */ + "lsquo", "rsquo", "sbquo", NULL, "ldquo", "rdquo", "bdquo", NULL, + "dagger", "Dagger", "bull", NULL, NULL, NULL, "hellip", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "permil", NULL, + /* 8242 */ + "prime", "Prime", NULL, NULL, NULL, NULL, NULL, "lsaquo", "rsaquo", NULL, + NULL, NULL, "oline", NULL, NULL, NULL, NULL, NULL, + "frasl" +}; + +static entity_table_t ent_uni_euro[] = { + "euro" +}; + +static entity_table_t ent_uni_8465_8501[] = { + /* 8465 */ + "image", NULL, NULL, NULL, NULL, NULL, NULL, + /* 8472 */ + "weierp", NULL, NULL, NULL, + /* 8476 */ + "real", NULL, NULL, NULL, NULL, NULL, + /* 8482 */ + "trade", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8501 */ + "alefsym", +}; + +static entity_table_t ent_uni_8592_9002[] = { + /* 8592 (0x2190) */ + "larr", "uarr", "rarr", "darr", "harr", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8608 (0x21a0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8624 (0x21b0) */ + NULL, NULL, NULL, NULL, NULL, "crarr", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8640 (0x21c0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8656 (0x21d0) */ + "lArr", "uArr", "rArr", "dArr", "hArr", "vArr", NULL, NULL, + NULL, NULL, "lAarr", "rAarr", NULL, "rarrw", NULL, NULL, + /* 8672 (0x21e0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8704 (0x2200) */ + "forall", "comp", "part", "exist", "nexist", "empty", NULL, "nabla", + "isin", "notin", "epsis", "ni", "notni", "bepsi", NULL, "prod", + /* 8720 (0x2210) */ + "coprod", "sum", "minus", "mnplus", "plusdo", NULL, "setmn", "lowast", + "compfn", NULL, "radic", NULL, NULL, "prop", "infin", "ang90", + /* 8736 (0x2220) */ + "ang", "angmsd", "angsph", "mid", "nmid", "par", "npar", "and", + "or", "cap", "cup", "int", NULL, NULL, "conint", NULL, + /* 8752 (0x2230) */ + NULL, NULL, NULL, NULL, "there4", "becaus", NULL, NULL, + NULL, NULL, NULL, NULL, "sim", "bsim", NULL, NULL, + /* 8768 (0x2240) */ + "wreath", "nsim", NULL, "sime", "nsime", "cong", NULL, "ncong", + "asymp", "nap", "ape", NULL, "bcong", "asymp", "bump", "bumpe", + /* 8784 (0x2250) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8800 (0x2260) */ + "ne", "equiv", NULL, NULL, "le", "ge", "lE", "gE", + "lnE", "gnE", "Lt", "Gt", "twixt", NULL, "nlt", "ngt", + /* 8816 (0x2270) */ + "nles", "nges", "lsim", "gsim", NULL, NULL, "lg", "gl", + NULL, NULL, "pr", "sc", "cupre", "sscue", "prsim", "scsim", + /* 8832 (0x2280) */ + "npr", "nsc", "sub", "sup", "nsub", "nsup", "sube", "supe", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8848 (0x2290) */ + NULL, NULL, NULL, NULL, NULL, "oplus", NULL, "otimes", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8864 (0x22a0) */ + NULL, NULL, NULL, NULL, NULL, "perp", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8880 (0x22b0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8896 (0x22c0) */ + NULL, NULL, NULL, NULL, NULL, "sdot", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8912 (0x22d0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8928 (0x22e0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8944 (0x22f0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8960 (0x2300) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "lceil", "rceil", "lfloor", "rfloor", NULL, NULL, NULL, NULL, + /* 8976 (0x2310) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8992 (0x2320) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, "lang", "rang" +}; + +static entity_table_t ent_uni_9674[] = { + /* 9674 */ + "loz" +}; + +static entity_table_t ent_uni_9824_9830[] = { + /* 9824 */ + "spades", NULL, NULL, "clubs", NULL, "hearts", "diams" +}; + +static entity_table_t ent_koi8r[] = { + "#1105", /* "jo "*/ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, "#1025", /* "JO" */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "#1102", "#1072", "#1073", "#1094", "#1076", "#1077", "#1092", + "#1075", "#1093", "#1080", "#1081", "#1082", "#1083", "#1084", + "#1085", "#1086", "#1087", "#1103", "#1088", "#1089", "#1090", + "#1091", "#1078", "#1074", "#1100", "#1099", "#1079", "#1096", + "#1101", "#1097", "#1095", "#1098", "#1070", "#1040", "#1041", + "#1062", "#1044", "#1045", "#1060", "#1043", "#1061", "#1048", + "#1049", "#1050", "#1051", "#1052", "#1053", "#1054", "#1055", + "#1071", "#1056", "#1057", "#1058", "#1059", "#1046", "#1042", + "#1068", "#1067", "#1047", "#1064", "#1069", "#1065", "#1063", + "#1066" +}; + +static entity_table_t ent_cp_1251[] = { + "#1026", "#1027", "#8218", "#1107", "#8222", "hellip", "dagger", + "Dagger", "euro", "permil", "#1033", "#8249", "#1034", "#1036", + "#1035", "#1039", "#1106", "#8216", "#8217", "#8219", "#8220", + "bull", "ndash", "mdash", NULL, "trade", "#1113", "#8250", + "#1114", "#1116", "#1115", "#1119", "nbsp", "#1038", "#1118", + "#1032", "curren", "#1168", "brvbar", "sect", "#1025", "copy", + "#1028", "laquo", "not", "shy", "reg", "#1031", "deg", "plusmn", + "#1030", "#1110", "#1169", "micro", "para", "middot", "#1105", + "#8470", "#1108", "raquo", "#1112", "#1029", "#1109", "#1111", + "#1040", "#1041", "#1042", "#1043", "#1044", "#1045", "#1046", + "#1047", "#1048", "#1049", "#1050", "#1051", "#1052", "#1053", + "#1054", "#1055", "#1056", "#1057", "#1058", "#1059", "#1060", + "#1061", "#1062", "#1063", "#1064", "#1065", "#1066", "#1067", + "#1068", "#1069", "#1070", "#1071", "#1072", "#1073", "#1074", + "#1075", "#1076", "#1077", "#1078", "#1079", "#1080", "#1081", + "#1082", "#1083", "#1084", "#1085", "#1086", "#1087", "#1088", + "#1089", "#1090", "#1091", "#1092", "#1093", "#1094", "#1095", + "#1096", "#1097", "#1098", "#1099", "#1100", "#1101", "#1102", + "#1103" +}; + +static entity_table_t ent_iso_8859_5[] = { + "#1056", "#1057", "#1058", "#1059", "#1060", "#1061", "#1062", + "#1063", "#1064", "#1065", "#1066", "#1067", "#1068", "#1069", + "#1070", "#1071", "#1072", "#1073", "#1074", "#1075", "#1076", + "#1077", "#1078", "#1079", "#1080", "#1081", "#1082", "#1083", + "#1084", "#1085", "#1086", "#1087", "#1088", "#1089", "#1090", + "#1091", "#1092", "#1093", "#1094", "#1095", "#1096", "#1097", + "#1098", "#1099", "#1100", "#1101", "#1102", "#1103", "#1104", + "#1105", "#1106", "#1107", "#1108", "#1109", "#1110", "#1111", + "#1112", "#1113", "#1114", "#1115", "#1116", "#1117", "#1118", + "#1119" +}; + +static entity_table_t ent_cp_866[] = { + + "#9492", "#9524", "#9516", "#9500", "#9472", "#9532", "#9566", + "#9567", "#9562", "#9556", "#9577", "#9574", "#9568", "#9552", + "#9580", "#9575", "#9576", "#9572", "#9573", "#9561", "#9560", + "#9554", "#9555", "#9579", "#9578", "#9496", "#9484", "#9608", + "#9604", "#9612", "#9616", "#9600", "#1088", "#1089", "#1090", + "#1091", "#1092", "#1093", "#1094", "#1095", "#1096", "#1097", + "#1098", "#1099", "#1100", "#1101", "#1102", "#1103", "#1025", + "#1105", "#1028", "#1108", "#1031", "#1111", "#1038", "#1118", + "#176", "#8729", "#183", "#8730", "#8470", "#164", "#9632", + "#160" +}; + +/* MacRoman has a couple of low-ascii chars that need mapping too */ +/* Vertical tab (ASCII 11) is often used to store line breaks inside */ +/* DB exports, this mapping changes it to a space */ +static entity_table_t ent_macroman[] = { + "sp", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, "quot", NULL, + NULL, NULL, "amp", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "lt", NULL, "gt", NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, "Auml", "Aring", "Ccedil", "Eacute", "Ntilde", "Ouml", + "Uuml", "aacute", "agrave", "acirc", "auml", "atilde", "aring", + "ccedil", "eacute", "egrave", "ecirc", "euml", "iacute", "igrave", + "icirc", "iuml", "ntilde", "oacute", "ograve", "ocirc", "ouml", + "otilde", "uacute", "ugrave", "ucirc", "uuml", "dagger", "deg", + "cent", "pound", "sect", "bull", "para", "szlig", "reg", + "copy", "trade", "acute", "uml", "ne", "AElig", "Oslash", + "infin", "plusmn", "le", "ge", "yen", "micro", "part", + "sum", "prod", "pi", "int", "ordf", "ordm", "Omega", + "aelig", "oslash", "iquest", "iexcl", "not", "radic", "fnof", + "asymp", "#8710", "laquo", "raquo", "hellip", "nbsp", "Agrave", + "Atilde", "Otilde", "OElig", "oelig", "ndash", "mdash", "ldquo", + "rdquo", "lsquo", "rsquo", "divide", "loz", "yuml", "Yuml", + "frasl", "euro", "lsaquo", "rsaquo", "#xFB01", "#xFB02", "Dagger", + "middot", "sbquo", "bdquo", "permil", "Acirc", "Ecirc", "Aacute", + "Euml", "Egrave", "Iacute", "Icirc", "Iuml", "Igrave", "Oacute", + "Ocirc", "#xF8FF", "Ograve", "Uacute", "Ucirc", "Ugrave", "#305", + "circ", "tilde", "macr", "#728", "#729", "#730", "cedil", + "#733", "#731", "#711" +}; + +struct html_entity_map { + enum entity_charset charset; /* charset identifier */ + unsigned short basechar; /* char code at start of table */ + unsigned short endchar; /* last char code in the table */ + entity_table_t *table; /* the table of mappings */ +}; + +static const struct html_entity_map entity_map[] = { + { cs_cp1252, 0x80, 0x9f, ent_cp_1252 }, + { cs_cp1252, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_8859_1, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_8859_15, 0xa0, 0xff, ent_iso_8859_15 }, + { cs_utf_8, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_utf_8, 338, 402, ent_uni_338_402 }, + { cs_utf_8, 710, 732, ent_uni_spacing }, + { cs_utf_8, 913, 982, ent_uni_greek }, + { cs_utf_8, 8194, 8260, ent_uni_punct }, + { cs_utf_8, 8364, 8364, ent_uni_euro }, + { cs_utf_8, 8465, 8501, ent_uni_8465_8501 }, + { cs_utf_8, 8592, 9002, ent_uni_8592_9002 }, + { cs_utf_8, 9674, 9674, ent_uni_9674 }, + { cs_utf_8, 9824, 9830, ent_uni_9824_9830 }, + { cs_big5, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_gb2312, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_big5hkscs, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_sjis, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_eucjp, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_koi8r, 0xa3, 0xff, ent_koi8r }, + { cs_cp1251, 0x80, 0xff, ent_cp_1251 }, + { cs_8859_5, 0xc0, 0xff, ent_iso_8859_5 }, + { cs_cp866, 0xc0, 0xff, ent_cp_866 }, + { cs_macroman, 0x0b, 0xff, ent_macroman }, + { cs_terminator } +}; + +static const struct { + const char *codeset; + enum entity_charset charset; +} charset_map[] = { + { "ISO-8859-1", cs_8859_1 }, + { "ISO8859-1", cs_8859_1 }, + { "ISO-8859-15", cs_8859_15 }, + { "ISO8859-15", cs_8859_15 }, + { "utf-8", cs_utf_8 }, + { "cp1252", cs_cp1252 }, + { "Windows-1252", cs_cp1252 }, + { "1252", cs_cp1252 }, + { "BIG5", cs_big5 }, + { "950", cs_big5 }, + { "GB2312", cs_gb2312 }, + { "936", cs_gb2312 }, + { "BIG5-HKSCS", cs_big5hkscs }, + { "Shift_JIS", cs_sjis }, + { "SJIS", cs_sjis }, + { "932", cs_sjis }, + { "EUCJP", cs_eucjp }, + { "EUC-JP", cs_eucjp }, + { "KOI8-R", cs_koi8r }, + { "koi8-ru", cs_koi8r }, + { "koi8r", cs_koi8r }, + { "cp1251", cs_cp1251 }, + { "Windows-1251", cs_cp1251 }, + { "win-1251", cs_cp1251 }, + { "iso8859-5", cs_8859_5 }, + { "iso-8859-5", cs_8859_5 }, + { "cp866", cs_cp866 }, + { "866", cs_cp866 }, + { "ibm866", cs_cp866 }, + { "MacRoman", cs_macroman }, + { NULL } +}; + +static const struct { + unsigned short charcode; + char *entity; + int entitylen; + int flags; +} basic_entities[] = { + { '"', """, 6, ENT_HTML_QUOTE_DOUBLE }, + { '\'', "'", 6, ENT_HTML_QUOTE_SINGLE }, + { '\'', "'", 5, ENT_HTML_QUOTE_SINGLE }, + { '<', "<", 4, 0 }, + { '>', ">", 4, 0 }, + { 0, NULL, 0, 0 } +}; + +struct basic_entities_dec { + unsigned short charcode; + char entity[8]; + int entitylen; +}; + +#define MB_RETURN { \ + *newpos = pos; \ + mbseq[mbpos] = '\0'; \ + *mbseqlen = mbpos; \ + return this_char; } + +#define MB_WRITE(mbchar) { \ + mbspace--; \ + if (mbspace == 0) { \ + MB_RETURN; \ + } \ + mbseq[mbpos++] = (mbchar); } + +/* {{{ get_next_char + */ +inline static unsigned short get_next_char(enum entity_charset charset, + unsigned char * str, + int * newpos, + unsigned char * mbseq, + int * mbseqlen) +{ + int pos = *newpos; + int mbpos = 0; + int mbspace = *mbseqlen; + unsigned short this_char = str[pos++]; + + if (mbspace <= 0) { + *mbseqlen = 0; + return this_char; + } + + MB_WRITE((unsigned char)this_char); + + switch (charset) { + case cs_utf_8: + { + unsigned long utf = 0; + int stat = 0; + int more = 1; + + /* unpack utf-8 encoding into a wide char. + * Code stolen from the mbstring extension */ + + do { + if (this_char < 0x80) { + more = 0; + break; + } else if (this_char < 0xc0) { + switch (stat) { + case 0x10: /* 2, 2nd */ + case 0x21: /* 3, 3rd */ + case 0x32: /* 4, 4th */ + case 0x43: /* 5, 5th */ + case 0x54: /* 6, 6th */ + /* last byte in sequence */ + more = 0; + utf |= (this_char & 0x3f); + this_char = (unsigned short)utf; + break; + case 0x20: /* 3, 2nd */ + case 0x31: /* 4, 3rd */ + case 0x42: /* 5, 4th */ + case 0x53: /* 6, 5th */ + /* penultimate char */ + utf |= ((this_char & 0x3f) << 6); + stat++; + break; + case 0x30: /* 4, 2nd */ + case 0x41: /* 5, 3rd */ + case 0x52: /* 6, 4th */ + utf |= ((this_char & 0x3f) << 12); + stat++; + break; + case 0x40: /* 5, 2nd */ + case 0x51: + utf |= ((this_char & 0x3f) << 18); + stat++; + break; + case 0x50: /* 6, 2nd */ + utf |= ((this_char & 0x3f) << 24); + stat++; + break; + default: + /* invalid */ + more = 0; + } + } + /* lead byte */ + else if (this_char < 0xe0) { + stat = 0x10; /* 2 byte */ + utf = (this_char & 0x1f) << 6; + } else if (this_char < 0xf0) { + stat = 0x20; /* 3 byte */ + utf = (this_char & 0xf) << 12; + } else if (this_char < 0xf8) { + stat = 0x30; /* 4 byte */ + utf = (this_char & 0x7) << 18; + } else if (this_char < 0xfc) { + stat = 0x40; /* 5 byte */ + utf = (this_char & 0x3) << 24; + } else if (this_char < 0xfe) { + stat = 0x50; /* 6 byte */ + utf = (this_char & 0x1) << 30; + } else { + /* invalid; bail */ + more = 0; + break; + } + + if (more) { + this_char = str[pos++]; + MB_WRITE((unsigned char)this_char); + } + } while (more); + } + break; + case cs_big5: + case cs_gb2312: + case cs_big5hkscs: + { + /* check if this is the first of a 2-byte sequence */ + if (this_char >= 0xa1 && this_char <= 0xfe) { + /* peek at the next char */ + unsigned char next_char = str[pos]; + if ((next_char >= 0x40 && next_char <= 0x7e) || + (next_char >= 0xa1 && next_char <= 0xfe)) { + /* yes, this a wide char */ + this_char <<= 8; + MB_WRITE(next_char); + this_char |= next_char; + pos++; + } + + } + break; + } + case cs_sjis: + { + /* check if this is the first of a 2-byte sequence */ + if ( (this_char >= 0x81 && this_char <= 0x9f) || + (this_char >= 0xe0 && this_char <= 0xef) + ) { + /* peek at the next char */ + unsigned char next_char = str[pos]; + if ((next_char >= 0x40 && next_char <= 0x7e) || + (next_char >= 0x80 && next_char <= 0xfc)) + { + /* yes, this a wide char */ + this_char <<= 8; + MB_WRITE(next_char); + this_char |= next_char; + pos++; + } + + } + break; + } + case cs_eucjp: + { + /* check if this is the first of a multi-byte sequence */ + if (this_char >= 0xa1 && this_char <= 0xfe) { + /* peek at the next char */ + unsigned char next_char = str[pos]; + if (next_char >= 0xa1 && next_char <= 0xfe) { + /* yes, this a jis kanji char */ + this_char <<= 8; + MB_WRITE(next_char); + this_char |= next_char; + pos++; + } + + } else if (this_char == 0x8e) { + /* peek at the next char */ + unsigned char next_char = str[pos]; + if (next_char >= 0xa1 && next_char <= 0xdf) { + /* JIS X 0201 kana */ + this_char <<= 8; + MB_WRITE(next_char); + this_char |= next_char; + pos++; + } + + } else if (this_char == 0x8f) { + /* peek at the next two char */ + unsigned char next_char = str[pos]; + unsigned char next2_char = str[pos+1]; + if ((next_char >= 0xa1 && next_char <= 0xfe) && + (next2_char >= 0xa1 && next2_char <= 0xfe)) { + /* JIS X 0212 hojo-kanji */ + this_char <<= 8; + MB_WRITE(next_char); + this_char |= next_char; + pos++; + this_char <<= 8; + MB_WRITE(next2_char); + this_char |= next2_char; + pos++; + } + + } + break; + } + default: + break; + } + MB_RETURN; +} +/* }}} */ + +/* {{{ entity_charset determine_charset + * returns the charset identifier based on current locale or a hint. + * defaults to iso-8859-1 */ +static enum entity_charset determine_charset(char *charset_hint TSRMLS_DC) +{ + int i; + enum entity_charset charset = cs_8859_1; + int len = 0; + zval *uf_result = NULL; + + /* Guarantee default behaviour for backwards compatibility */ + if (charset_hint == NULL) + return cs_8859_1; + + if ((len = strlen(charset_hint)) != 0) { + goto det_charset; + } +#if HAVE_MBSTRING +#if !defined(COMPILE_DL_MBSTRING) + /* XXX: Ugly things. Why don't we look for a more sophisticated way? */ + switch (MBSTRG(current_internal_encoding)) { + case mbfl_no_encoding_8859_1: + return cs_8859_1; + + case mbfl_no_encoding_utf8: + return cs_utf_8; + + case mbfl_no_encoding_euc_jp: + case mbfl_no_encoding_eucjp_win: + return cs_eucjp; + + case mbfl_no_encoding_sjis: + case mbfl_no_encoding_sjis_win: + case mbfl_no_encoding_sjis_mac: + return cs_sjis; + + case mbfl_no_encoding_cp1252: + return cs_cp1252; + + case mbfl_no_encoding_8859_15: + return cs_8859_15; + + case mbfl_no_encoding_big5: + return cs_big5; + + case mbfl_no_encoding_euc_cn: + case mbfl_no_encoding_hz: + case mbfl_no_encoding_cp936: + return cs_gb2312; + + case mbfl_no_encoding_koi8r: + return cs_koi8r; + + case mbfl_no_encoding_cp866: + return cs_cp866; + + case mbfl_no_encoding_cp1251: + return cs_cp1251; + + case mbfl_no_encoding_8859_5: + return cs_8859_5; + + default: + ; + } +#else + { + zval nm_mb_internal_encoding; + + ZVAL_STRING(&nm_mb_internal_encoding, "mb_internal_encoding", 0); + + if (call_user_function_ex(CG(function_table), NULL, &nm_mb_internal_encoding, &uf_result, 0, NULL, 1, NULL TSRMLS_CC) != FAILURE) { + + charset_hint = Z_STRVAL_P(uf_result); + len = Z_STRLEN_P(uf_result); + + goto det_charset; + } + } +#endif +#endif + + charset_hint = SG(default_charset); + if (charset_hint != NULL && (len=strlen(charset_hint)) != 0) { + goto det_charset; + } + + /* try to detect the charset for the locale */ +#if HAVE_NL_LANGINFO && HAVE_LOCALE_H && defined(CODESET) + charset_hint = nl_langinfo(CODESET); + if (charset_hint != NULL && (len=strlen(charset_hint)) != 0) { + goto det_charset; + } +#endif + +#if HAVE_LOCALE_H + /* try to figure out the charset from the locale */ + { + char *localename; + char *dot, *at; + + /* lang[_territory][.codeset][@modifier] */ + localename = setlocale(LC_CTYPE, NULL); + + dot = strchr(localename, '.'); + if (dot) { + dot++; + /* locale specifies a codeset */ + at = strchr(dot, '@'); + if (at) + len = at - dot; + else + len = strlen(dot); + charset_hint = dot; + } else { + /* no explicit name; see if the name itself + * is the charset */ + charset_hint = localename; + len = strlen(charset_hint); + } + } +#endif + +det_charset: + + if (charset_hint) { + int found = 0; + + /* now walk the charset map and look for the codeset */ + for (i = 0; charset_map[i].codeset; i++) { + if (strncasecmp(charset_hint, charset_map[i].codeset, len) == 0) { + charset = charset_map[i].charset; + found = 1; + break; + } + } + if (!found) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "charset `%s' not supported, assuming iso-8859-1", + charset_hint); + } + } + if (uf_result != NULL) { + zval_ptr_dtor(&uf_result); + } + return charset; +} +/* }}} */ + +/* {{{ php_utf32_utf8 */ +size_t php_utf32_utf8(unsigned char *buf, int k) +{ + size_t retval = 0; + + if (k < 0x80) { + buf[0] = k; + retval = 1; + } else if (k < 0x800) { + buf[0] = 0xc0 | (k >> 6); + buf[1] = 0x80 | (k & 0x3f); + retval = 2; + } else if (k < 0x10000) { + buf[0] = 0xe0 | (k >> 12); + buf[1] = 0x80 | ((k >> 6) & 0x3f); + buf[2] = 0x80 | (k & 0x3f); + retval = 3; + } else if (k < 0x200000) { + buf[0] = 0xf0 | (k >> 18); + buf[1] = 0x80 | ((k >> 12) & 0x3f); + buf[2] = 0x80 | ((k >> 6) & 0x3f); + buf[3] = 0x80 | (k & 0x3f); + retval = 4; + } else if (k < 0x4000000) { + buf[0] = 0xf8 | (k >> 24); + buf[1] = 0x80 | ((k >> 18) & 0x3f); + buf[2] = 0x80 | ((k >> 12) & 0x3f); + buf[3] = 0x80 | ((k >> 6) & 0x3f); + buf[4] = 0x80 | (k & 0x3f); + retval = 5; + } else { + buf[0] = 0xfc | (k >> 30); + buf[1] = 0x80 | ((k >> 24) & 0x3f); + buf[2] = 0x80 | ((k >> 18) & 0x3f); + buf[3] = 0x80 | ((k >> 12) & 0x3f); + buf[4] = 0x80 | ((k >> 6) & 0x3f); + buf[5] = 0x80 | (k & 0x3f); + retval = 6; + } + buf[retval] = '\0'; + + return retval; +} +/* }}} */ + +/* {{{ php_unescape_html_entities + */ +PHPAPI char *php_unescape_html_entities(unsigned char *old, int oldlen, int *newlen, int all, int quote_style, char *hint_charset TSRMLS_DC) +{ + int retlen; + int j, k; + char *replaced, *ret, *p, *q, *lim, *next; + enum entity_charset charset = determine_charset(hint_charset TSRMLS_CC); + unsigned char replacement[15]; + int replacement_len; + + ret = estrdup(old); + retlen = oldlen; + if (!retlen) { + goto empty_source; + } + + if (all) { + /* look for a match in the maps for this charset */ + for (j = 0; entity_map[j].charset != cs_terminator; j++) { + if (entity_map[j].charset != charset) + continue; + + for (k = entity_map[j].basechar; k <= entity_map[j].endchar; k++) { + unsigned char entity[32]; + int entity_length = 0; + + if (entity_map[j].table[k - entity_map[j].basechar] == NULL) + continue; + + entity[0] = '&'; + entity_length = strlen(entity_map[j].table[k - entity_map[j].basechar]); + strncpy(&entity[1], entity_map[j].table[k - entity_map[j].basechar], sizeof(entity) - 2); + entity[entity_length+1] = ';'; + entity[entity_length+2] = '\0'; + entity_length += 2; + + /* When we have MBCS entities in the tables above, this will need to handle it */ + replacement_len = 0; + switch (charset) { + case cs_8859_1: + case cs_cp1252: + case cs_8859_15: + case cs_cp1251: + case cs_8859_5: + case cs_cp866: + replacement[0] = k; + replacement[1] = '\0'; + replacement_len = 1; + break; + + case cs_big5: + case cs_gb2312: + case cs_big5hkscs: + case cs_sjis: + case cs_eucjp: + /* we cannot properly handle those multibyte encodings + * with php_str_to_str. skip it. */ + continue; + + case cs_utf_8: + replacement_len = php_utf32_utf8(replacement, k); + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot yet handle MBCS!"); + return 0; + } + + if (php_memnstr(ret, entity, entity_length, ret+retlen)) { + replaced = php_str_to_str(ret, retlen, entity, entity_length, replacement, replacement_len, &retlen); + efree(ret); + ret = replaced; + } + } + } + } + + for (j = 0; basic_entities[j].charcode != 0; j++) { + + if (basic_entities[j].flags && (quote_style & basic_entities[j].flags) == 0) + continue; + + replacement[0] = (unsigned char)basic_entities[j].charcode; + replacement[1] = '\0'; + + if (php_memnstr(ret, basic_entities[j].entity, basic_entities[j].entitylen, ret+retlen)) { + replaced = php_str_to_str(ret, retlen, basic_entities[j].entity, basic_entities[j].entitylen, replacement, 1, &retlen); + efree(ret); + ret = replaced; + } + } + + /* replace numeric entities & "&" */ + lim = ret + retlen; + for (p = ret, q = ret; p < lim;) { + int code; + + if (p[0] == '&') { + if (p + 2 < lim) { + if (p[1] == '#') { + int invalid_code = 0; + + if (p[2] == 'x' || p[2] == 'X') { + code = strtol(p + 3, &next, 16); + } else { + code = strtol(p + 2, &next, 10); + } + + if (next != NULL && *next == ';') { + switch (charset) { + case cs_utf_8: + q += php_utf32_utf8(q, code); + break; + + case cs_8859_1: + case cs_8859_5: + case cs_8859_15: + if ((code >= 0x80 && code < 0xa0) || code > 0xff) { + invalid_code = 1; + } else { + *(q++) = code; + } + break; + + case cs_cp1252: + case cs_cp1251: + case cs_cp866: + if (code > 0xff) { + invalid_code = 1; + } else { + *(q++) = code; + } + break; + + case cs_big5: + case cs_big5hkscs: + case cs_sjis: + case cs_eucjp: + if (code >= 0x80) { + invalid_code = 1; + } else { + *(q++) = code; + } + break; + + case cs_gb2312: + if (code >= 0x81) { + invalid_code = 1; + } else { + *(q++) = code; + } + break; + + default: + /* for backwards compatilibity */ + invalid_code = 1; + break; + } + if (invalid_code) { + for (; p <= next; p++) { + *(q++) = *p; + } + } + p = next + 1; + } else { + *(q++) = *(p++); + *(q++) = *(p++); + } + } else if (p + 4 < lim && + p[1] == 'a' && p[2] == 'm' &&p[3] == 'p' && + p[4] == ';') { + *(q++) = '&'; + p += 5; + } else { + *(q++) = *(p++); + *(q++) = *(p++); + } + } else { + *(q++) = *(p++); + } + } else { + *(q++) = *(p++); + } + } + *q = '\0'; + retlen = (size_t)(q - ret); +empty_source: + *newlen = retlen; + return ret; +} +/* }}} */ + + + + +/* {{{ php_escape_html_entities + */ +PHPAPI char *php_escape_html_entities(unsigned char *old, int oldlen, int *newlen, int all, int quote_style, char *hint_charset TSRMLS_DC) +{ + int i, j, maxlen, len; + char *replaced; + enum entity_charset charset = determine_charset(hint_charset TSRMLS_CC); + int matches_map; + + maxlen = 2 * oldlen; + if (maxlen < 128) + maxlen = 128; + replaced = emalloc (maxlen); + len = 0; + + i = 0; + while (i < oldlen) { + unsigned char mbsequence[16]; /* allow up to 15 characters in a multibyte sequence */ + int mbseqlen = sizeof(mbsequence); + unsigned short this_char = get_next_char(charset, old, &i, mbsequence, &mbseqlen); + + matches_map = 0; + + if (len + 9 > maxlen) + replaced = erealloc (replaced, maxlen += 128); + + if (all) { + /* look for a match in the maps for this charset */ + unsigned char *rep = NULL; + + + for (j = 0; entity_map[j].charset != cs_terminator; j++) { + if (entity_map[j].charset == charset + && this_char >= entity_map[j].basechar + && this_char <= entity_map[j].endchar) { + rep = (unsigned char*)entity_map[j].table[this_char - entity_map[j].basechar]; + if (rep == NULL) { + /* there is no entity for this position; fall through and + * just output the character itself */ + break; + } + + matches_map = 1; + break; + } + } + + if (matches_map) { + replaced[len++] = '&'; + strcpy(replaced + len, rep); + len += strlen(rep); + replaced[len++] = ';'; + } + } + if (!matches_map) { + int is_basic = 0; + + if (this_char == '&') { + memcpy(replaced + len, "&", sizeof("&") - 1); + len += sizeof("&") - 1; + is_basic = 1; + } else { + for (j = 0; basic_entities[j].charcode != 0; j++) { + if ((basic_entities[j].charcode != this_char) || + (basic_entities[j].flags && + (quote_style & basic_entities[j].flags) == 0)) { + continue; + } + + memcpy(replaced + len, basic_entities[j].entity, basic_entities[j].entitylen); + len += basic_entities[j].entitylen; + + is_basic = 1; + break; + } + } + + if (!is_basic) { + /* a wide char without a named entity; pass through the original sequence */ + if (mbseqlen > 1) { + memcpy(replaced + len, mbsequence, mbseqlen); + len += mbseqlen; + } else { + replaced[len++] = (unsigned char)this_char; + } + } + } + } + replaced[len] = '\0'; + *newlen = len; + + return replaced; + + +} +/* }}} */ + +/* {{{ php_html_entities + */ +static void php_html_entities(INTERNAL_FUNCTION_PARAMETERS, int all) +{ + char *str, *hint_charset = NULL; + int str_len, hint_charset_len = 0; + int len; + long quote_style = ENT_COMPAT; + char *replaced; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len, "e_style, &hint_charset, &hint_charset_len) == FAILURE) { + return; + } + + replaced = php_escape_html_entities(str, str_len, &len, all, quote_style, hint_charset TSRMLS_CC); + RETVAL_STRINGL(replaced, len, 0); +} +/* }}} */ + +#define HTML_SPECIALCHARS 0 +#define HTML_ENTITIES 1 + +/* {{{ register_html_constants + */ +void register_html_constants(INIT_FUNC_ARGS) +{ + REGISTER_LONG_CONSTANT("HTML_SPECIALCHARS", HTML_SPECIALCHARS, CONST_PERSISTENT|CONST_CS); + REGISTER_LONG_CONSTANT("HTML_ENTITIES", HTML_ENTITIES, CONST_PERSISTENT|CONST_CS); + REGISTER_LONG_CONSTANT("ENT_COMPAT", ENT_COMPAT, CONST_PERSISTENT|CONST_CS); + REGISTER_LONG_CONSTANT("ENT_QUOTES", ENT_QUOTES, CONST_PERSISTENT|CONST_CS); + REGISTER_LONG_CONSTANT("ENT_NOQUOTES", ENT_NOQUOTES, CONST_PERSISTENT|CONST_CS); +} +/* }}} */ + +/* {{{ proto string htmlspecialchars(string string [, int quote_style][, string charset]) + Convert special characters to HTML entities */ +PHP_FUNCTION(htmlspecialchars) +{ + php_html_entities(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto string htmlspecialchars(string string [, int quote_style]) + Convert special HTML entities back to characters */ +PHP_FUNCTION(htmlspecialchars_decode) +{ + char *str, *new_str, *e, *p; + int len, j, i, new_len; + long quote_style = ENT_COMPAT; + struct basic_entities_dec basic_entities_dec[8]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, "e_style) == FAILURE) { + return; + } + + new_str = estrndup(str, len); + new_len = len; + e = new_str + new_len; + + if (!(p = memchr(new_str, '&', new_len))) { + RETURN_STRINGL(new_str, new_len, 0); + } + + for (j = 0, i = 0; basic_entities[i].charcode != 0; i++) { + if (basic_entities[i].flags && !(quote_style & basic_entities[i].flags)) { + continue; + } + basic_entities_dec[j].charcode = basic_entities[i].charcode; + memcpy(basic_entities_dec[j].entity, basic_entities[i].entity, basic_entities[i].entitylen + 1); + basic_entities_dec[j].entitylen = basic_entities[i].entitylen; + j++; + } + basic_entities_dec[j].charcode = '&'; + basic_entities_dec[j].entitylen = sizeof("&") - 1; + memcpy(basic_entities_dec[j].entity, "&", sizeof("&")); + i = j + 1; + + do { + int l = e - p; + + for (j = 0; j < i; j++) { + if (basic_entities_dec[j].entitylen > l) { + continue; + } + if (!memcmp(p, basic_entities_dec[j].entity, basic_entities_dec[j].entitylen)) { + int e_len = basic_entities_dec[j].entitylen - 1; + + *p++ = basic_entities_dec[j].charcode; + memmove(p, p + e_len, (e - p - e_len)); + e -= e_len; + goto done; + } + } + p++; + +done: + if (p >= e) { + break; + } + } while ((p = memchr(p, '&', (e - p)))); + + new_len = e - new_str; + + new_str[new_len] = '\0'; + RETURN_STRINGL(new_str, new_len, 0); +} +/* }}} */ + +/* {{{ proto string html_entity_decode(string string [, int quote_style][, string charset]) + Convert all HTML entities to their applicable characters */ +PHP_FUNCTION(html_entity_decode) +{ + char *str, *hint_charset = NULL; + int str_len, hint_charset_len, len; + long quote_style = ENT_COMPAT; + char *replaced; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len, + "e_style, &hint_charset, &hint_charset_len) == FAILURE) { + return; + } + + replaced = php_unescape_html_entities(str, str_len, &len, 1, quote_style, hint_charset TSRMLS_CC); + RETVAL_STRINGL(replaced, len, 0); +} +/* }}} */ + + +/* {{{ proto string htmlentities(string string [, int quote_style][, string charset]) + Convert all applicable characters to HTML entities */ +PHP_FUNCTION(htmlentities) +{ + php_html_entities(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto array get_html_translation_table([int table [, int quote_style]]) + Returns the internal translation table used by htmlspecialchars and htmlentities */ +PHP_FUNCTION(get_html_translation_table) +{ + long which = HTML_SPECIALCHARS, quote_style = ENT_COMPAT; + int i, j; + char ind[2]; + enum entity_charset charset = determine_charset(NULL TSRMLS_CC); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll", &which, "e_style) == FAILURE) { + return; + } + + array_init(return_value); + + ind[1] = 0; + + switch (which) { + case HTML_ENTITIES: + for (j=0; entity_map[j].charset != cs_terminator; j++) { + if (entity_map[j].charset != charset) + continue; + for (i = 0; i <= entity_map[j].endchar - entity_map[j].basechar; i++) { + char buffer[16]; + + if (entity_map[j].table[i] == NULL) + continue; + /* what about wide chars here ?? */ + ind[0] = i + entity_map[j].basechar; + sprintf(buffer, "&%s;", entity_map[j].table[i]); + add_assoc_string(return_value, ind, buffer, 1); + + } + } + /* break thru */ + + case HTML_SPECIALCHARS: + for (j = 0; basic_entities[j].charcode != 0; j++) { + + if (basic_entities[j].flags && (quote_style & basic_entities[j].flags) == 0) + continue; + + ind[0] = (unsigned char)basic_entities[j].charcode; + add_assoc_stringl(return_value, ind, basic_entities[j].entity, basic_entities[j].entitylen, 1); + } + add_assoc_stringl(return_value, "&", "&", sizeof("&") - 1, 1); + + break; + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_920358_bd306c_ext#dom#xpath.c b/src/main/resource/testFiles/php-src/prevFiles/prev_920358_bd306c_ext#dom#xpath.c new file mode 100644 index 0000000..977407f --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_920358_bd306c_ext#dom#xpath.c @@ -0,0 +1,267 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Christian Stocker | + | Rob Richards | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#if HAVE_LIBXML && HAVE_DOM +#include "php_dom.h" + + +/* +* class domxpath +*/ + +#if defined(LIBXML_XPATH_ENABLED) + +zend_function_entry php_dom_xpath_class_functions[] = { + PHP_FALIAS(domxpath, dom_xpath_xpath, NULL) + PHP_FALIAS(register_namespace, dom_xpath_register_ns, NULL) + PHP_FALIAS(query, dom_xpath_query, NULL) + {NULL, NULL, NULL} +}; + +/* {{{ proto domxpath dom_xpath_xpath(domDocument doc); */ +PHP_FUNCTION(dom_xpath_xpath) +{ + zval *id, *doc; + xmlDocPtr docp = NULL; + dom_object *docobj, *intern; + xmlXPathContextPtr ctx, oldctx; + + id = getThis(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &doc) == FAILURE) { + return; + } + + DOM_GET_OBJ(docp, doc, xmlDocPtr, docobj); + + ctx = xmlXPathNewContext(docp); + if (ctx == NULL) { + RETURN_FALSE; + } + + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + if (intern != NULL) { + oldctx = (xmlXPathContextPtr)intern->ptr; + if (oldctx != NULL) { + php_libxml_decrement_doc_ref((php_libxml_node_object *)intern TSRMLS_CC); + xmlXPathFreeContext(oldctx); + } + intern->ptr = ctx; + intern->document = docobj->document; + php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp TSRMLS_CC); + } +} +/* }}} end dom_xpath_xpath */ + +/* {{{ proto domdocument document document */ +int dom_xpath_document_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp = NULL; + xmlXPathContextPtr ctx; + int ret; + + ctx = (xmlXPathContextPtr) obj->ptr; + + if (ctx) { + docp = (xmlDocPtr) ctx->doc; + } else { + printf("NONE"); + } + + ALLOC_ZVAL(*retval); + if (NULL == (*retval = php_dom_create_object((xmlNodePtr) docp, &ret, NULL, *retval, obj TSRMLS_CC))) { + php_error(E_WARNING, "Cannot create required DOM object"); + return FAILURE; + } + return SUCCESS; +} + +/* {{{ proto boolean dom_xpath_register_ns(string prefix, string uri); */ +PHP_FUNCTION(dom_xpath_register_ns) +{ + zval *id; + xmlXPathContextPtr ctxp; + int prefix_len, ns_uri_len; + dom_object *intern; + unsigned char *prefix, *ns_uri; + + DOM_GET_THIS(id); + + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + + ctxp = (xmlXPathContextPtr) intern->ptr; + if (ctxp == NULL) { + php_error(E_WARNING, "Invalid XPath Context"); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &prefix, &prefix_len, &ns_uri, &ns_uri_len) == FAILURE) { + RETURN_FALSE; + } + + if (xmlXPathRegisterNs(ctxp, prefix, ns_uri) != 0) { + RETURN_FALSE + } + RETURN_TRUE; +} + +static void dom_xpath_iter(zval *baseobj, dom_object *intern) +{ + dom_nnodemap_object *mapptr; + + mapptr = (dom_nnodemap_object *)intern->ptr; + mapptr->baseobjptr = baseobj; + mapptr->nodetype = DOM_NODESET; + +} + +/* {{{ proto domnodelist dom_xpath_query(string expr [,domNode context]); */ +PHP_FUNCTION(dom_xpath_query) +{ + zval *id, *retval, *context = NULL; + xmlXPathContextPtr ctxp; + xmlNodePtr nodep = NULL; + xmlXPathObjectPtr xpathobjp; + int expr_len, ret, nsnbr = 0; + dom_object *intern, *nodeobj; + char *expr; + xmlDoc *docp = NULL; + xmlNsPtr *ns; + + DOM_GET_THIS(id); + + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + + ctxp = (xmlXPathContextPtr) intern->ptr; + if (ctxp == NULL) { + php_error(E_WARNING, "Invalid XPath Context"); + RETURN_FALSE; + } + + docp = (xmlDocPtr) ctxp->doc; + if (docp == NULL) { + php_error(E_WARNING, "Invalid XPath Document Pointer"); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|o", &expr, &expr_len, &context) == FAILURE) { + RETURN_FALSE; + } + + if (context != NULL) { + DOM_GET_OBJ(nodep, context, xmlNodePtr, nodeobj); + } + + if (!nodep) { + nodep = xmlDocGetRootElement(docp); + } + + if (nodep && docp != nodep->doc) { + php_error(E_WARNING, "Node From Wrong Document"); + RETURN_FALSE; + } + + ctxp->node = nodep; + + /* Register namespaces in the node */ + ns = xmlGetNsList(docp, nodep); + + if (ns != NULL) { + while (ns[nsnbr] != NULL) + nsnbr++; + } + + + ctxp->namespaces = ns; + ctxp->nsNr = nsnbr; + + xpathobjp = xmlXPathEvalExpression(expr, ctxp); + ctxp->node = NULL; + + if (ns != NULL) { + xmlFree(ns); + ctxp->namespaces = NULL; + ctxp->nsNr = 0; + } + + if (! xpathobjp) { + RETURN_FALSE; + } + + + if (xpathobjp->type == XPATH_NODESET) { + int i; + xmlNodeSetPtr nodesetp; + + if (NULL == (nodesetp = xpathobjp->nodesetval)) { + xmlXPathFreeObject (xpathobjp); + RETURN_FALSE; + } + + MAKE_STD_ZVAL(retval); + array_init(retval); + + for (i = 0; i < nodesetp->nodeNr; i++) { + xmlNodePtr node = nodesetp->nodeTab[i]; + zval *child; + + MAKE_STD_ZVAL(child); + + if (node->type == XML_NAMESPACE_DECL) { + xmlNsPtr curns; + xmlNodePtr nsparent; + + nsparent = node->_private; + curns = xmlNewNs(NULL, node->name, NULL); + if (node->children) { + curns->prefix = xmlStrdup((char *) node->children); + } + if (node->children) { + node = xmlNewDocNode(docp, NULL, (char *) node->children, node->name); + } else { + node = xmlNewDocNode(docp, NULL, "xmlns", node->name); + } + node->type = XML_NAMESPACE_DECL; + node->parent = nsparent; + node->ns = curns; + } + child = php_dom_create_object(node, &ret, NULL, child, intern TSRMLS_CC); + add_next_index_zval(retval, child); + } + } + + php_dom_create_interator(return_value, DOM_NODELIST TSRMLS_CC); + intern = (dom_object *)zend_objects_get_address(return_value TSRMLS_CC); + dom_xpath_iter(retval, intern); + + xmlXPathFreeObject(xpathobjp); +} +/* }}} end dom_xpath_query */ + +#endif /* LIBXML_XPATH_ENABLED */ + +/* }}} */ +#endif diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_9baa92_f9c232_sapi#apache2handler#sapi_apache2.c b/src/main/resource/testFiles/php-src/prevFiles/prev_9baa92_f9c232_sapi#apache2handler#sapi_apache2.c new file mode 100644 index 0000000..d0ac95a --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_9baa92_f9c232_sapi#apache2handler#sapi_apache2.c @@ -0,0 +1,591 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Sascha Schumann | + | Parts based on Apache 1.3 SAPI module by | + | Rasmus Lerdorf and Zeev Suraski | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS + +#include "php.h" +#include "php_main.h" +#include "php_ini.h" +#include "php_variables.h" +#include "SAPI.h" + +#include + +#include "ext/standard/php_smart_str.h" +#ifndef NETWARE +#include "ext/standard/php_standard.h" +#else +#include "ext/standard/basic_functions.h" +#endif + +#include "apr_strings.h" +#include "ap_config.h" +#include "util_filter.h" +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#include "http_core.h" +#include "ap_mpm.h" + +#include "php_apache.h" + +/* UnixWare and Netware define shutdown to _shutdown, which causes problems later + * on when using a structure member named shutdown. Since this source + * file does not use the system call shutdown, it is safe to #undef it.K + */ +#undef shutdown + +#define PHP_MAGIC_TYPE "application/x-httpd-php" +#define PHP_SOURCE_MAGIC_TYPE "application/x-httpd-php-source" +#define PHP_SCRIPT "php5-script" + +/* A way to specify the location of the php.ini dir in an apache directive */ +char *apache2_php_ini_path_override = NULL; + +static int +php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC) +{ + request_rec *r; + php_struct *ctx; + + ctx = SG(server_context); + r = ctx->r; + + if (ap_rwrite(str, str_length, r) < 0) { + php_handle_aborted_connection(); + } + + return str_length; /* we always consume all the data passed to us. */ +} + +static int +php_apache_sapi_header_handler(sapi_header_struct *sapi_header,sapi_headers_struct *sapi_headers TSRMLS_DC) +{ + php_struct *ctx; + char *val; + + ctx = SG(server_context); + + val = strchr(sapi_header->header, ':'); + + if (!val) { + sapi_free_header(sapi_header); + return 0; + } + + *val = '\0'; + + do { + val++; + } while (*val == ' '); + + if (!strcasecmp(sapi_header->header, "content-type")) { + val = apr_pstrdup(ctx->r->pool, val); + ap_set_content_type(ctx->r, val); + } else if (sapi_header->replace) { + apr_table_set(ctx->r->headers_out, sapi_header->header, val); + } else { + apr_table_add(ctx->r->headers_out, sapi_header->header, val); + } + + sapi_free_header(sapi_header); + + return 0; +} + +static int +php_apache_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) +{ + php_struct *ctx = SG(server_context); + + ctx->r->status = SG(sapi_headers).http_response_code; + + return SAPI_HEADER_SENT_SUCCESSFULLY; +} + +static int +php_apache_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC) +{ + apr_size_t len, tlen=0; + php_struct *ctx = SG(server_context); + request_rec *r; + apr_bucket_brigade *brigade; + + r = ctx->r; + brigade = ctx->brigade; + len = count_bytes; + + /* + * This loop is needed because ap_get_brigade() can return us partial data + * which would cause premature termination of request read. Therefor we + * need to make sure that if data is avaliable we fill the buffer completely. + */ + + while (ap_get_brigade(r->input_filters, brigade, AP_MODE_READBYTES, APR_BLOCK_READ, len) == APR_SUCCESS) { + apr_brigade_flatten(brigade, buf, &len); + apr_brigade_cleanup(brigade); + tlen += len; + if (tlen == count_bytes || !len) { + break; + } + buf += len; + len = count_bytes - tlen; + } + + return tlen; +} + +static struct stat* +php_apache_sapi_get_stat(TSRMLS_D) +{ + php_struct *ctx = SG(server_context); + + ctx->finfo.st_uid = ctx->r->finfo.user; + ctx->finfo.st_gid = ctx->r->finfo.group; + ctx->finfo.st_dev = ctx->r->finfo.device; + ctx->finfo.st_ino = ctx->r->finfo.inode; +#if defined(NETWARE) && defined(CLIB_STAT_PATCH) + ctx->finfo.st_atime.tv_sec = apr_time_sec(ctx->r->finfo.atime); + ctx->finfo.st_mtime.tv_sec = apr_time_sec(ctx->r->finfo.mtime); + ctx->finfo.st_ctime.tv_sec = apr_time_sec(ctx->r->finfo.ctime); +#else + ctx->finfo.st_atime = apr_time_sec(ctx->r->finfo.atime); + ctx->finfo.st_mtime = apr_time_sec(ctx->r->finfo.mtime); + ctx->finfo.st_ctime = apr_time_sec(ctx->r->finfo.ctime); +#endif + + ctx->finfo.st_size = ctx->r->finfo.size; + ctx->finfo.st_nlink = ctx->r->finfo.nlink; + + return &ctx->finfo; +} + +static char * +php_apache_sapi_read_cookies(TSRMLS_D) +{ + php_struct *ctx = SG(server_context); + const char *http_cookie; + + http_cookie = apr_table_get(ctx->r->headers_in, "cookie"); + + /* The SAPI interface should use 'const char *' */ + return (char *) http_cookie; +} + +static char * +php_apache_sapi_getenv(char *name, size_t name_len TSRMLS_DC) +{ + php_struct *ctx = SG(server_context); + const char *env_var; + + env_var = apr_table_get(ctx->r->subprocess_env, name); + + return (char *) env_var; +} + +static void +php_apache_sapi_register_variables(zval *track_vars_array TSRMLS_DC) +{ + php_struct *ctx = SG(server_context); + const apr_array_header_t *arr = apr_table_elts(ctx->r->subprocess_env); + char *key, *val; + + APR_ARRAY_FOREACH_OPEN(arr, key, val) + if (!val) val = ""; + php_register_variable(key, val, track_vars_array TSRMLS_CC); + APR_ARRAY_FOREACH_CLOSE() + + php_register_variable("PHP_SELF", ctx->r->uri, track_vars_array TSRMLS_CC); +} + +static void +php_apache_sapi_flush(void *server_context) +{ + php_struct *ctx; + request_rec *r; + TSRMLS_FETCH(); + + ctx = server_context; + + /* If we haven't registered a server_context yet, + * then don't bother flushing. */ + if (!server_context) { + return; + } + + r = ctx->r; + + sapi_send_headers(TSRMLS_C); + + r->status = SG(sapi_headers).http_response_code; + SG(headers_sent) = 1; + + if (ap_rflush(r) < 0 || r->connection->aborted) { + php_handle_aborted_connection(); + } +} + +static void php_apache_sapi_log_message(char *msg) +{ + php_struct *ctx; + TSRMLS_FETCH(); + + ctx = SG(server_context); + + /* We use APLOG_STARTUP because it keeps us from printing the + * data and time information at the beginning of the error log + * line. Not sure if this is correct, but it mirrors what happens + * with Apache 1.3 -- rbb + */ + if (ctx == NULL) { /* we haven't initialized our ctx yet, oh well */ + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL, "%s", msg); + } else { + ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, ctx->r, "%s", msg); + } +} + +static void php_apache_sapi_log_message_ex(char *msg, request_rec *r) +{ + if (r) { + ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, r, msg, r->filename); + } else { + php_apache_sapi_log_message(msg); + } +} + +static time_t php_apache_sapi_get_request_time(TSRMLS_D) { + php_struct *ctx = SG(server_context); + return apr_time_sec(ctx->r->request_time); +} + +extern zend_module_entry php_apache_module; + +static int php_apache2_startup(sapi_module_struct *sapi_module) +{ + if (php_module_startup(sapi_module, &php_apache_module, 1)==FAILURE) { + return FAILURE; + } + return SUCCESS; +} + +static sapi_module_struct apache2_sapi_module = { + "apache2handler", + "Apache 2.0 Handler", + + php_apache2_startup, /* startup */ + php_module_shutdown_wrapper, /* shutdown */ + + NULL, /* activate */ + NULL, /* deactivate */ + + php_apache_sapi_ub_write, /* unbuffered write */ + php_apache_sapi_flush, /* flush */ + php_apache_sapi_get_stat, /* get uid */ + php_apache_sapi_getenv, /* getenv */ + + php_error, /* error handler */ + + php_apache_sapi_header_handler, /* header handler */ + php_apache_sapi_send_headers, /* send headers handler */ + NULL, /* send header handler */ + + php_apache_sapi_read_post, /* read POST data */ + php_apache_sapi_read_cookies, /* read Cookies */ + + php_apache_sapi_register_variables, + php_apache_sapi_log_message, /* Log message */ + php_apache_sapi_get_request_time, /* Request Time */ + + STANDARD_SAPI_MODULE_PROPERTIES +}; + +static apr_status_t +php_apache_server_shutdown(void *tmp) +{ + apache2_sapi_module.shutdown(&apache2_sapi_module); + sapi_shutdown(); +#ifdef ZTS + tsrm_shutdown(); +#endif + return APR_SUCCESS; +} + +static void php_apache_add_version(apr_pool_t *p) +{ + TSRMLS_FETCH(); + if (PG(expose_php)) { + ap_add_version_component(p, "PHP/" PHP_VERSION); + } +} + +static int php_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) +{ +#ifndef ZTS + int threaded_mpm; + + ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm); + if(threaded_mpm) { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, 0, "Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe. You need to recompile PHP."); + return DONE; + } +#endif + /* When this is NULL, apache won't override the hard-coded default + * php.ini path setting. */ + apache2_php_ini_path_override = NULL; + return OK; +} + +static int +php_apache_server_startup(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) +{ + void *data = NULL; + const char *userdata_key = "apache2hook_post_config"; + + /* Apache will load, unload and then reload a DSO module. This + * prevents us from starting PHP until the second load. */ + apr_pool_userdata_get(&data, userdata_key, s->process->pool); + if (data == NULL) { + /* We must use set() here and *not* setn(), otherwise the + * static string pointed to by userdata_key will be mapped + * to a different location when the DSO is reloaded and the + * pointers won't match, causing get() to return NULL when + * we expected it to return non-NULL. */ + apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool); + return OK; + } + + /* Set up our overridden path. */ + if (apache2_php_ini_path_override) { + apache2_sapi_module.php_ini_path_override = apache2_php_ini_path_override; + } +#ifdef ZTS + tsrm_startup(1, 1, 0, NULL); +#endif + sapi_startup(&apache2_sapi_module); + apache2_sapi_module.startup(&apache2_sapi_module); + apr_pool_cleanup_register(pconf, NULL, php_apache_server_shutdown, apr_pool_cleanup_null); + php_apache_add_version(pconf); + + return OK; +} + +static apr_status_t php_server_context_cleanup(void *data_) +{ + void **data = data_; + *data = NULL; + return APR_SUCCESS; +} + +static void php_apache_request_ctor(request_rec *r, php_struct *ctx TSRMLS_DC) +{ + char *content_type; + char *content_length; + const char *auth; + + SG(sapi_headers).http_response_code = !r->status ? HTTP_OK : r->status; + SG(request_info).content_type = apr_table_get(r->headers_in, "Content-Type"); + SG(request_info).query_string = apr_pstrdup(r->pool, r->args); + SG(request_info).request_method = r->method; + SG(request_info).request_uri = apr_pstrdup(r->pool, r->uri); + SG(request_info).path_translated = apr_pstrdup(r->pool, r->filename); + r->no_local_copy = 1; + + content_type = sapi_get_default_content_type(TSRMLS_C); + ap_set_content_type(r, apr_pstrdup(r->pool, content_type)); + efree(content_type); + + content_length = (char *) apr_table_get(r->headers_in, "Content-Length"); + SG(request_info).content_length = (content_length ? atoi(content_length) : 0); + + apr_table_unset(r->headers_out, "Content-Length"); + apr_table_unset(r->headers_out, "Last-Modified"); + apr_table_unset(r->headers_out, "Expires"); + apr_table_unset(r->headers_out, "ETag"); + if (!PG(safe_mode) || (PG(safe_mode) && !ap_auth_type(r))) { + auth = apr_table_get(r->headers_in, "Authorization"); + php_handle_auth_data(auth TSRMLS_CC); + ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user); + } else { + SG(request_info).auth_user = NULL; + SG(request_info).auth_password = NULL; + } + php_request_startup(TSRMLS_C); +} + +static void php_apache_request_dtor(request_rec *r TSRMLS_DC) +{ + php_request_shutdown(NULL); +} + +static int php_handler(request_rec *r) +{ + php_struct *ctx; + void *conf; + apr_bucket_brigade *brigade; + apr_bucket *bucket; + apr_status_t rv; + request_rec *parent_req = NULL; + TSRMLS_FETCH(); + + conf = ap_get_module_config(r->per_dir_config, &php5_module); + apply_config(conf); + + if (strcmp(r->handler, PHP_MAGIC_TYPE) && strcmp(r->handler, PHP_SOURCE_MAGIC_TYPE) && strcmp(r->handler, PHP_SCRIPT)) { + /* Check for xbithack in this case. */ + if (!AP2(xbithack) || strcmp(r->handler, "text/html") || !(r->finfo.protection & APR_UEXECUTE)) { + zend_try { + zend_ini_deactivate(TSRMLS_C); + } zend_end_try(); + return DECLINED; + } + } + + /* handle situations where user turns the engine off */ + if (!AP2(engine)) { + zend_try { + zend_ini_deactivate(TSRMLS_C); + } zend_end_try(); + return DECLINED; + } + + if (r->finfo.filetype == 0) { + php_apache_sapi_log_message_ex("script '%s' not found or unable to stat", r); + zend_try { + zend_ini_deactivate(TSRMLS_C); + } zend_end_try(); + return HTTP_NOT_FOUND; + } + if (r->finfo.filetype == APR_DIR) { + php_apache_sapi_log_message_ex("attempt to invoke directory '%s' as script", r); + zend_try { + zend_ini_deactivate(TSRMLS_C); + } zend_end_try(); + return HTTP_FORBIDDEN; + } + + /* Setup the CGI variables if this is the main request */ + if (r->main == NULL || + /* .. or if the sub-request envinronment differs from the main-request. */ + r->subprocess_env != r->main->subprocess_env + ) { + /* setup standard CGI variables */ + ap_add_common_vars(r); + ap_add_cgi_vars(r); + } + +zend_first_try { + + ctx = SG(server_context); + if (ctx == NULL) { + ctx = SG(server_context) = apr_pcalloc(r->pool, sizeof(*ctx)); + /* register a cleanup so we clear out the SG(server_context) + * after each request. Note: We pass in the pointer to the + * server_context in case this is handled by a different thread. + */ + apr_pool_cleanup_register(r->pool, (void *)&SG(server_context), php_server_context_cleanup, apr_pool_cleanup_null); + + ctx->r = r; + brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc); + ctx->brigade = brigade; + + php_apache_request_ctor(r, ctx TSRMLS_CC); + } else { + parent_req = ctx->r; + ctx->r = r; + brigade = ctx->brigade; + } + + if (AP2(last_modified)) { + ap_update_mtime(r, r->finfo.mtime); + ap_set_last_modified(r); + } + + /* Determine if we need to parse the file or show the source */ + if (strncmp(r->handler, PHP_SOURCE_MAGIC_TYPE, sizeof(PHP_SOURCE_MAGIC_TYPE) - 1) == 0) { + zend_syntax_highlighter_ini syntax_highlighter_ini; + php_get_highlight_struct(&syntax_highlighter_ini); + highlight_file((char *)r->filename, &syntax_highlighter_ini TSRMLS_CC); + } else { + zend_file_handle zfd; + + zfd.type = ZEND_HANDLE_FILENAME; + zfd.filename = (char *) r->filename; + zfd.free_filename = 0; + zfd.opened_path = NULL; + + if (!parent_req) { + php_execute_script(&zfd TSRMLS_CC); + } else { + zend_execute_scripts(ZEND_INCLUDE TSRMLS_CC, NULL, 1, &zfd); + } +#if MEMORY_LIMIT + { + char *mem_usage; + + mem_usage = apr_psprintf(ctx->r->pool, "%u", AG(allocated_memory_peak)); + apr_table_set(r->notes, "mod_php_memory_usage", mem_usage); + } +#endif + } + +} zend_end_try(); + + if (!parent_req) { + php_apache_request_dtor(r TSRMLS_CC); + ctx->request_processed = 1; + bucket = apr_bucket_eos_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(brigade, bucket); + + rv = ap_pass_brigade(r->output_filters, brigade); + if (rv != APR_SUCCESS || r->connection->aborted) { +zend_first_try { + php_handle_aborted_connection(); +} zend_end_try(); + } + apr_brigade_cleanup(brigade); + } else { + ctx->r = parent_req; + } + + return OK; +} + +void php_ap2_register_hook(apr_pool_t *p) +{ + ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_a0a56e_7afbb7_ext#com_dotnet#com_extension.c b/src/main/resource/testFiles/php-src/prevFiles/prev_a0a56e_7afbb7_ext#com_dotnet#com_extension.c new file mode 100644 index 0000000..5652680 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_a0a56e_7afbb7_ext#com_dotnet#com_extension.c @@ -0,0 +1,363 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_com_dotnet.h" +#include "php_com_dotnet_internal.h" +#include "Zend/zend_exceptions.h" + +ZEND_DECLARE_MODULE_GLOBALS(com_dotnet) +static PHP_GINIT_FUNCTION(com_dotnet); + +TsHashTable php_com_typelibraries; + +zend_class_entry + *php_com_variant_class_entry, + *php_com_exception_class_entry, + *php_com_saproxy_class_entry; + +zend_function_entry com_dotnet_functions[] = { + PHP_FE(variant_set, NULL) + PHP_FE(variant_add, NULL) + PHP_FE(variant_cat, NULL) + PHP_FE(variant_sub, NULL) + PHP_FE(variant_mul, NULL) + PHP_FE(variant_and, NULL) + PHP_FE(variant_div, NULL) + PHP_FE(variant_eqv, NULL) + PHP_FE(variant_idiv, NULL) + PHP_FE(variant_imp, NULL) + PHP_FE(variant_mod, NULL) + PHP_FE(variant_or, NULL) + PHP_FE(variant_pow, NULL) + PHP_FE(variant_xor, NULL) + PHP_FE(variant_abs, NULL) + PHP_FE(variant_fix, NULL) + PHP_FE(variant_int, NULL) + PHP_FE(variant_neg, NULL) + PHP_FE(variant_not, NULL) + PHP_FE(variant_round, NULL) + PHP_FE(variant_cmp, NULL) + PHP_FE(variant_date_to_timestamp, NULL) + PHP_FE(variant_date_from_timestamp, NULL) + PHP_FE(variant_get_type, NULL) + PHP_FE(variant_set_type, NULL) + PHP_FE(variant_cast, NULL) + /* com_com.c */ + PHP_FE(com_create_guid, NULL) + PHP_FE(com_event_sink, NULL) + PHP_FE(com_print_typeinfo, NULL) + PHP_FE(com_message_pump, NULL) + PHP_FE(com_load_typelib, NULL) + PHP_FE(com_get_active_object, NULL) + { NULL, NULL, NULL } +}; + +/* {{{ com_dotnet_module_entry + */ +zend_module_entry com_dotnet_module_entry = { + STANDARD_MODULE_HEADER, + "com_dotnet", + com_dotnet_functions, + PHP_MINIT(com_dotnet), + PHP_MSHUTDOWN(com_dotnet), + PHP_RINIT(com_dotnet), + PHP_RSHUTDOWN(com_dotnet), + PHP_MINFO(com_dotnet), + "0.1", + PHP_MODULE_GLOBALS(com_dotnet), + PHP_GINIT(com_dotnet), + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX +}; +/* }}} */ + +#ifdef COMPILE_DL_COM_DOTNET +ZEND_GET_MODULE(com_dotnet) +#endif + +/* {{{ PHP_INI + */ + +/* com.typelib_file is the path to a file containing a + * list of typelibraries to register *persistently*. + * lines starting with ; are comments + * append #cis to end of typelib name to cause its constants + * to be loaded case insensitively */ +static PHP_INI_MH(OnTypeLibFileUpdate) +{ + FILE *typelib_file; + char *typelib_name_buffer; + char *strtok_buf = NULL; + int cached; + + if (!new_value || !new_value[0] || (typelib_file = VCWD_FOPEN(new_value, "r"))==NULL) { + return FAILURE; + } + + typelib_name_buffer = (char *) emalloc(sizeof(char)*1024); + + while (fgets(typelib_name_buffer, 1024, typelib_file)) { + ITypeLib *pTL; + char *typelib_name; + char *modifier, *ptr; + int mode = CONST_CS | CONST_PERSISTENT; /* CONST_PERSISTENT is ok here */ + + if (typelib_name_buffer[0]==';') { + continue; + } + typelib_name = php_strtok_r(typelib_name_buffer, "\r\n", &strtok_buf); /* get rid of newlines */ + if (typelib_name == NULL) { + continue; + } + typelib_name = php_strtok_r(typelib_name, "#", &strtok_buf); + modifier = php_strtok_r(NULL, "#", &strtok_buf); + if (modifier != NULL) { + if (!strcmp(modifier, "cis") || !strcmp(modifier, "case_insensitive")) { + mode &= ~CONST_CS; + } + } + + /* Remove leading/training white spaces on search_string */ + while (isspace(*typelib_name)) {/* Ends on '\0' in worst case */ + typelib_name ++; + } + ptr = typelib_name + strlen(typelib_name) - 1; + while ((ptr != typelib_name) && isspace(*ptr)) { + *ptr = '\0'; + ptr--; + } + + if ((pTL = php_com_load_typelib_via_cache(typelib_name, COMG(code_page), &cached TSRMLS_CC)) != NULL) { + if (!cached) { + php_com_import_typelib(pTL, mode, COMG(code_page) TSRMLS_CC); + } + ITypeLib_Release(pTL); + } + } + + efree(typelib_name_buffer); + fclose(typelib_file); + + return SUCCESS; +} + +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("com.allow_dcom", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_dcom, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.autoregister_verbose", "0", PHP_INI_ALL, OnUpdateBool, autoreg_verbose, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.autoregister_typelib", "0", PHP_INI_ALL, OnUpdateBool, autoreg_on, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.autoregister_casesensitive", "1", PHP_INI_ALL, OnUpdateBool, autoreg_case_sensitive, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.code_page", "", PHP_INI_ALL, OnUpdateLong, code_page, zend_com_dotnet_globals, com_dotnet_globals) + PHP_INI_ENTRY("com.typelib_file", "", PHP_INI_SYSTEM, OnTypeLibFileUpdate) +PHP_INI_END() +/* }}} */ + +/* {{{ PHP_GINIT_FUNCTION + */ +static PHP_GINIT_FUNCTION(com_dotnet) +{ + memset(com_dotnet_globals, 0, sizeof(*com_dotnet_globals)); + com_dotnet_globals->code_page = CP_ACP; +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(com_dotnet) +{ + zend_class_entry ce, *tmp; + + REGISTER_INI_ENTRIES(); + + php_com_wrapper_minit(INIT_FUNC_ARGS_PASSTHRU); + php_com_persist_minit(INIT_FUNC_ARGS_PASSTHRU); + + INIT_CLASS_ENTRY(ce, "com_exception", NULL); + php_com_exception_class_entry = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC); + php_com_exception_class_entry->ce_flags |= ZEND_ACC_FINAL; +/* php_com_exception_class_entry->constructor->common.fn_flags |= ZEND_ACC_PROTECTED; */ + + INIT_CLASS_ENTRY(ce, "com_safearray_proxy", NULL); + php_com_saproxy_class_entry = zend_register_internal_class(&ce TSRMLS_CC); + php_com_saproxy_class_entry->ce_flags |= ZEND_ACC_FINAL; +/* php_com_saproxy_class_entry->constructor->common.fn_flags |= ZEND_ACC_PROTECTED; */ + php_com_saproxy_class_entry->get_iterator = php_com_saproxy_iter_get; + + INIT_CLASS_ENTRY(ce, "variant", NULL); + ce.create_object = php_com_object_new; + php_com_variant_class_entry = zend_register_internal_class(&ce TSRMLS_CC); + php_com_variant_class_entry->get_iterator = php_com_iter_get; + + INIT_CLASS_ENTRY(ce, "com", NULL); + ce.create_object = php_com_object_new; + tmp = zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC); + tmp->get_iterator = php_com_iter_get; + + zend_ts_hash_init(&php_com_typelibraries, 0, NULL, php_com_typelibrary_dtor, 1); + +#if HAVE_MSCOREE_H + INIT_CLASS_ENTRY(ce, "dotnet", NULL); + ce.create_object = php_com_object_new; + tmp = zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC); + tmp->get_iterator = php_com_iter_get; +#endif + +#define COM_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT) + + COM_CONST(CLSCTX_INPROC_SERVER); + COM_CONST(CLSCTX_INPROC_HANDLER); + COM_CONST(CLSCTX_LOCAL_SERVER); + COM_CONST(CLSCTX_REMOTE_SERVER); + COM_CONST(CLSCTX_SERVER); + COM_CONST(CLSCTX_ALL); + +#if 0 + COM_CONST(DISPATCH_METHOD); + COM_CONST(DISPATCH_PROPERTYGET); + COM_CONST(DISPATCH_PROPERTYPUT); +#endif + + COM_CONST(VT_NULL); + COM_CONST(VT_EMPTY); + COM_CONST(VT_UI1); + COM_CONST(VT_I1); + COM_CONST(VT_UI2); + COM_CONST(VT_I2); + COM_CONST(VT_UI4); + COM_CONST(VT_I4); + COM_CONST(VT_R4); + COM_CONST(VT_R8); + COM_CONST(VT_BOOL); + COM_CONST(VT_ERROR); + COM_CONST(VT_CY); + COM_CONST(VT_DATE); + COM_CONST(VT_BSTR); + COM_CONST(VT_DECIMAL); + COM_CONST(VT_UNKNOWN); + COM_CONST(VT_DISPATCH); + COM_CONST(VT_VARIANT); + COM_CONST(VT_INT); + COM_CONST(VT_UINT); + COM_CONST(VT_ARRAY); + COM_CONST(VT_BYREF); + + COM_CONST(CP_ACP); + COM_CONST(CP_MACCP); + COM_CONST(CP_OEMCP); + COM_CONST(CP_UTF7); + COM_CONST(CP_UTF8); + COM_CONST(CP_SYMBOL); + COM_CONST(CP_THREAD_ACP); + + COM_CONST(VARCMP_LT); + COM_CONST(VARCMP_EQ); + COM_CONST(VARCMP_GT); + COM_CONST(VARCMP_NULL); + + COM_CONST(NORM_IGNORECASE); + COM_CONST(NORM_IGNORENONSPACE); + COM_CONST(NORM_IGNORESYMBOLS); + COM_CONST(NORM_IGNOREWIDTH); + COM_CONST(NORM_IGNOREKANATYPE); +#ifdef NORM_IGNOREKASHIDA + COM_CONST(NORM_IGNOREKASHIDA); +#endif + COM_CONST(DISP_E_DIVBYZERO); + COM_CONST(DISP_E_OVERFLOW); + COM_CONST(DISP_E_BADINDEX); + COM_CONST(MK_E_UNAVAILABLE); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(com_dotnet) +{ + UNREGISTER_INI_ENTRIES(); +#if HAVE_MSCOREE_H + if (COMG(dotnet_runtime_stuff)) { + php_com_dotnet_mshutdown(TSRMLS_C); + } +#endif + + zend_ts_hash_destroy(&php_com_typelibraries); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RINIT_FUNCTION + */ +PHP_RINIT_FUNCTION(com_dotnet) +{ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +PHP_RSHUTDOWN_FUNCTION(com_dotnet) +{ +#if HAVE_MSCOREE_H + if (COMG(dotnet_runtime_stuff)) { + php_com_dotnet_rshutdown(TSRMLS_C); + } +#endif + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(com_dotnet) +{ + php_info_print_table_start(); + + php_info_print_table_header(2, "COM support", "enabled"); + php_info_print_table_header(2, "DCOM support", COMG(allow_dcom) ? "enabled" : "disabled"); + +#if HAVE_MSCOREE_H + php_info_print_table_header(2, ".Net support", "enabled"); +#else + php_info_print_table_header(2, ".Net support", "not present in this build"); +#endif + + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_a10e77_634012_ext#phar#tar.c b/src/main/resource/testFiles/php-src/prevFiles/prev_a10e77_634012_ext#phar#tar.c new file mode 100644 index 0000000..9d1e5bc --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_a10e77_634012_ext#phar#tar.c @@ -0,0 +1,1312 @@ +/* + +----------------------------------------------------------------------+ + | TAR archive support for Phar | + +----------------------------------------------------------------------+ + | Copyright (c) 2005-2012 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Dmitry Stogov | + | Gregory Beaver | + +----------------------------------------------------------------------+ +*/ + +#include "phar_internal.h" + +static php_uint32 phar_tar_number(char *buf, int len) /* {{{ */ +{ + php_uint32 num = 0; + int i = 0; + + while (i < len && buf[i] == ' ') { + ++i; + } + + while (i < len && buf[i] >= '0' && buf[i] <= '7') { + num = num * 8 + (buf[i] - '0'); + ++i; + } + + return num; +} +/* }}} */ + +/* adapted from format_octal() in libarchive + * + * Copyright (c) 2003-2009 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +static int phar_tar_octal(char *buf, php_uint32 val, int len) /* {{{ */ +{ + char *p = buf; + int s = len; + + p += len; /* Start at the end and work backwards. */ + while (s-- > 0) { + *--p = (char)('0' + (val & 7)); + val >>= 3; + } + + if (val == 0) + return SUCCESS; + + /* If it overflowed, fill field with max value. */ + while (len-- > 0) + *p++ = '7'; + + return FAILURE; +} +/* }}} */ + +static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */ +{ + php_uint32 sum = 0; + char *end = buf + len; + + while (buf != end) { + sum += (unsigned char)*buf; + ++buf; + } + return sum; +} +/* }}} */ + +int phar_is_tar(char *buf, char *fname) /* {{{ */ +{ + tar_header *header = (tar_header *) buf; + php_uint32 checksum = phar_tar_number(header->checksum, sizeof(header->checksum)); + php_uint32 ret; + char save[sizeof(header->checksum)]; + + /* assume that the first filename in a tar won't begin with checksum, sizeof(header->checksum)); + memset(header->checksum, ' ', sizeof(header->checksum)); + ret = (checksum == phar_tar_checksum(buf, 512)); + memcpy(header->checksum, save, sizeof(header->checksum)); + if (!ret && strstr(fname, ".tar")) { + /* probably a corrupted tar - so we will pretend it is one */ + return 1; + } + return ret; +} +/* }}} */ + +int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */ +{ + phar_archive_data *phar; + int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error TSRMLS_CC); + + if (FAILURE == ret) { + return FAILURE; + } + + if (pphar) { + *pphar = phar; + } + + phar->is_data = is_data; + + if (phar->is_tar) { + return ret; + } + + if (phar->is_brandnew) { + phar->is_tar = 1; + phar->is_zip = 0; + phar->internal_file_start = 0; + return SUCCESS; + } + + /* we've reached here - the phar exists and is a regular phar */ + if (error) { + spprintf(error, 4096, "phar tar error: \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a tar-based phar", fname); + } + return FAILURE; +} +/* }}} */ + +static int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp TSRMLS_DC) /* {{{ */ +{ + char *metadata; + size_t save = php_stream_tell(fp), read; + phar_entry_info *mentry; + + metadata = (char *) emalloc(entry->uncompressed_filesize + 1); + + read = php_stream_read(fp, metadata, entry->uncompressed_filesize); + if (read != entry->uncompressed_filesize) { + efree(metadata); + php_stream_seek(fp, save, SEEK_SET); + return FAILURE; + } + + if (phar_parse_metadata(&metadata, &entry->metadata, entry->uncompressed_filesize TSRMLS_CC) == FAILURE) { + /* if not valid serialized data, it is a regular string */ + efree(metadata); + php_stream_seek(fp, save, SEEK_SET); + return FAILURE; + } + + if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) { + entry->phar->metadata = entry->metadata; + entry->metadata = NULL; + } else if (entry->filename_len >= sizeof(".phar/.metadata/") + sizeof("/.metadata.bin") - 1 && SUCCESS == zend_hash_find(&(entry->phar->manifest), entry->filename + sizeof(".phar/.metadata/") - 1, entry->filename_len - (sizeof("/.metadata.bin") - 1 + sizeof(".phar/.metadata/") - 1), (void *)&mentry)) { + /* transfer this metadata to the entry it refers */ + mentry->metadata = entry->metadata; + entry->metadata = NULL; + } + + efree(metadata); + php_stream_seek(fp, save, SEEK_SET); + return SUCCESS; +} +/* }}} */ + +int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */ +{ + char buf[512], *actual_alias = NULL, *p; + phar_entry_info entry = {0}; + size_t pos = 0, read, totalsize; + tar_header *hdr; + php_uint32 sum1, sum2, size, old; + phar_archive_data *myphar, **actual; + int last_was_longlink = 0; + + if (error) { + *error = NULL; + } + + php_stream_seek(fp, 0, SEEK_END); + totalsize = php_stream_tell(fp); + php_stream_seek(fp, 0, SEEK_SET); + read = php_stream_read(fp, buf, sizeof(buf)); + + if (read != sizeof(buf)) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is not a tar file or is truncated", fname); + } + php_stream_close(fp); + return FAILURE; + } + + hdr = (tar_header*)buf; + old = (memcmp(hdr->magic, "ustar", sizeof("ustar")-1) != 0); + + myphar = (phar_archive_data *) pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist)); + myphar->is_persistent = PHAR_G(persist); + /* estimate number of entries, can't be certain with tar files */ + zend_hash_init(&myphar->manifest, 2 + (totalsize >> 12), + zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)myphar->is_persistent); + zend_hash_init(&myphar->mounted_dirs, 5, + zend_get_hash_value, NULL, (zend_bool)myphar->is_persistent); + zend_hash_init(&myphar->virtual_dirs, 4 + (totalsize >> 11), + zend_get_hash_value, NULL, (zend_bool)myphar->is_persistent); + myphar->is_tar = 1; + /* remember whether this entire phar was compressed with gz/bzip2 */ + myphar->flags = compression; + + entry.is_tar = 1; + entry.is_crc_checked = 1; + entry.phar = myphar; + pos += sizeof(buf); + + do { + phar_entry_info *newentry; + + pos = php_stream_tell(fp); + hdr = (tar_header*) buf; + sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum)); + if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) { + break; + } + memset(hdr->checksum, ' ', sizeof(hdr->checksum)); + sum2 = phar_tar_checksum(buf, old?sizeof(old_tar_header):sizeof(tar_header)); + + size = entry.uncompressed_filesize = entry.compressed_filesize = + phar_tar_number(hdr->size, sizeof(hdr->size)); + + if (((!old && hdr->prefix[0] == 0) || old) && strlen(hdr->name) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) { + off_t curloc; + + if (size > 511) { + if (error) { + spprintf(error, 4096, "phar error: tar-based phar \"%s\" has signature that is larger than 511 bytes, cannot process", fname); + } +bail: + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + curloc = php_stream_tell(fp); + read = php_stream_read(fp, buf, size); + if (read != size) { + if (error) { + spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be read", fname); + } + goto bail; + } +#ifdef WORDS_BIGENDIAN +# define PHAR_GET_32(buffer) \ + (((((unsigned char*)(buffer))[3]) << 24) \ + | ((((unsigned char*)(buffer))[2]) << 16) \ + | ((((unsigned char*)(buffer))[1]) << 8) \ + | (((unsigned char*)(buffer))[0])) +#else +# define PHAR_GET_32(buffer) (php_uint32) *(buffer) +#endif + myphar->sig_flags = PHAR_GET_32(buf); + if (FAILURE == phar_verify_signature(fp, php_stream_tell(fp) - size - 512, myphar->sig_flags, buf + 8, size - 8, fname, &myphar->signature, &myphar->sig_len, error TSRMLS_CC)) { + if (error) { + char *save = *error; + spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be verified: %s", fname, save); + efree(save); + } + goto bail; + } + php_stream_seek(fp, curloc + 512, SEEK_SET); + /* signature checked out, let's ensure this is the last file in the phar */ + if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) { + /* this is not good enough - seek succeeds even on truncated tars */ + php_stream_seek(fp, 512, SEEK_CUR); + if ((uint)php_stream_tell(fp) > totalsize) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } + + read = php_stream_read(fp, buf, sizeof(buf)); + + if (read != sizeof(buf)) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + hdr = (tar_header*) buf; + sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum)); + + if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) { + break; + } + + if (error) { + spprintf(error, 4096, "phar error: \"%s\" has entries after signature, invalid phar", fname); + } + + goto bail; + } + + if (!last_was_longlink && hdr->typeflag == 'L') { + last_was_longlink = 1; + /* support the ././@LongLink system for storing long filenames */ + entry.filename_len = entry.uncompressed_filesize; + entry.filename = pemalloc(entry.filename_len+1, myphar->is_persistent); + + read = php_stream_read(fp, entry.filename, entry.filename_len); + if (read != entry.filename_len) { + efree(entry.filename); + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + entry.filename[entry.filename_len] = '\0'; + + /* skip blank stuff */ + size = ((size+511)&~511) - size; + + /* this is not good enough - seek succeeds even on truncated tars */ + php_stream_seek(fp, size, SEEK_CUR); + if ((uint)php_stream_tell(fp) > totalsize) { + efree(entry.filename); + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + read = php_stream_read(fp, buf, sizeof(buf)); + + if (read != sizeof(buf)) { + efree(entry.filename); + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + continue; + } else if (!last_was_longlink && !old && hdr->prefix[0] != 0) { + char name[256]; + int i, j; + + for (i = 0; i < 155; i++) { + name[i] = hdr->prefix[i]; + if (name[i] == '\0') { + break; + } + } + name[i++] = '/'; + for (j = 0; j < 100; j++) { + name[i+j] = hdr->name[j]; + if (name[i+j] == '\0') { + break; + } + } + + entry.filename_len = i+j; + + if (name[entry.filename_len - 1] == '/') { + /* some tar programs store directories with trailing slash */ + entry.filename_len--; + } + entry.filename = pestrndup(name, entry.filename_len, myphar->is_persistent); + } else if (!last_was_longlink) { + int i; + + /* calculate strlen, which can be no longer than 100 */ + for (i = 0; i < 100; i++) { + if (hdr->name[i] == '\0') { + break; + } + } + entry.filename_len = i; + entry.filename = pestrndup(hdr->name, i, myphar->is_persistent); + + if (entry.filename[entry.filename_len - 1] == '/') { + /* some tar programs store directories with trailing slash */ + entry.filename[entry.filename_len - 1] = '\0'; + entry.filename_len--; + } + } + last_was_longlink = 0; + + phar_add_virtual_dirs(myphar, entry.filename, entry.filename_len TSRMLS_CC); + + if (sum1 != sum2) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename); + } + pefree(entry.filename, myphar->is_persistent); + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + entry.tar_type = ((old & (hdr->typeflag == '\0')) ? TAR_FILE : hdr->typeflag); + entry.offset = entry.offset_abs = pos; /* header_offset unused in tar */ + entry.fp_type = PHAR_FP; + entry.flags = phar_tar_number(hdr->mode, sizeof(hdr->mode)) & PHAR_ENT_PERM_MASK; + entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime)); + entry.is_persistent = myphar->is_persistent; +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) +#endif + if (old && entry.tar_type == TAR_FILE && S_ISDIR(entry.flags)) { + entry.tar_type = TAR_DIR; + } + + if (entry.tar_type == TAR_DIR) { + entry.is_dir = 1; + } else { + entry.is_dir = 0; + } + + entry.link = NULL; + + if (entry.tar_type == TAR_LINK) { + if (!zend_hash_exists(&myphar->manifest, hdr->linkname, strlen(hdr->linkname))) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file \"%s\"", fname, hdr->linkname); + } + pefree(entry.filename, entry.is_persistent); + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + entry.link = estrdup(hdr->linkname); + } else if (entry.tar_type == TAR_SYMLINK) { + entry.link = estrdup(hdr->linkname); + } + phar_set_inode(&entry TSRMLS_CC); + zend_hash_add(&myphar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), (void **) &newentry); + + if (entry.is_persistent) { + ++entry.manifest_pos; + } + + if (entry.filename_len >= sizeof(".phar/.metadata")-1 && !memcmp(entry.filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) { + if (FAILURE == phar_tar_process_metadata(newentry, fp TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: tar-based phar \"%s\" has invalid metadata in magic file \"%s\"", fname, entry.filename); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } + + if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { + /* found explicit alias */ + if (size > 511) { + if (error) { + spprintf(error, 4096, "phar error: tar-based phar \"%s\" has alias that is larger than 511 bytes, cannot process", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + read = php_stream_read(fp, buf, size); + + if (read == size) { + buf[size] = '\0'; + if (!phar_validate_alias(buf, size)) { + if (size > 50) { + buf[50] = '.'; + buf[51] = '.'; + buf[52] = '.'; + buf[53] = '\0'; + } + + if (error) { + spprintf(error, 4096, "phar error: invalid alias \"%s\" in tar-based phar \"%s\"", buf, fname); + } + + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + actual_alias = pestrndup(buf, size, myphar->is_persistent); + myphar->alias = actual_alias; + myphar->alias_len = size; + php_stream_seek(fp, pos, SEEK_SET); + } else { + if (error) { + spprintf(error, 4096, "phar error: Unable to read alias from tar-based phar \"%s\"", fname); + } + + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } + + size = (size+511)&~511; + + if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) { + /* this is not good enough - seek succeeds even on truncated tars */ + php_stream_seek(fp, size, SEEK_CUR); + if ((uint)php_stream_tell(fp) > totalsize) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } + + read = php_stream_read(fp, buf, sizeof(buf)); + + if (read != sizeof(buf)) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } while (read != 0); + + if (zend_hash_exists(&(myphar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) { + myphar->is_data = 0; + } else { + myphar->is_data = 1; + } + + /* ensure signature set */ + if (!myphar->is_data && PHAR_G(require_hash) && !myphar->signature) { + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + if (error) { + spprintf(error, 0, "tar-based phar \"%s\" does not have a signature", fname); + } + return FAILURE; + } + + myphar->fname = pestrndup(fname, fname_len, myphar->is_persistent); +#ifdef PHP_WIN32 + phar_unixify_path_separators(myphar->fname, fname_len); +#endif + myphar->fname_len = fname_len; + myphar->fp = fp; + p = strrchr(myphar->fname, '/'); + + if (p) { + myphar->ext = memchr(p, '.', (myphar->fname + fname_len) - p); + if (myphar->ext == p) { + myphar->ext = memchr(p + 1, '.', (myphar->fname + fname_len) - p - 1); + } + if (myphar->ext) { + myphar->ext_len = (myphar->fname + fname_len) - myphar->ext; + } + } + + phar_request_initialize(TSRMLS_C); + + if (SUCCESS != zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len, (void*)&myphar, sizeof(phar_archive_data*), (void **)&actual)) { + if (error) { + spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\" to phar registry", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + myphar = *actual; + + if (actual_alias) { + phar_archive_data **fd_ptr; + + myphar->is_temporary_alias = 0; + + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, myphar->alias_len TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname); + } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len); + return FAILURE; + } + } + + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); + } else { + phar_archive_data **fd_ptr; + + if (alias_len) { + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname); + } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len); + return FAILURE; + } + } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); + myphar->alias = pestrndup(alias, alias_len, myphar->is_persistent); + myphar->alias_len = alias_len; + } else { + myphar->alias = pestrndup(myphar->fname, fname_len, myphar->is_persistent); + myphar->alias_len = fname_len; + } + + myphar->is_temporary_alias = 1; + } + + if (pphar) { + *pphar = myphar; + } + + return SUCCESS; +} +/* }}} */ + +struct _phar_pass_tar_info { + php_stream *old; + php_stream *new; + int free_fp; + int free_ufp; + char **error; +}; + +static int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */ +{ + tar_header header; + size_t pos; + phar_entry_info *entry = (phar_entry_info *) pDest; + struct _phar_pass_tar_info *fp = (struct _phar_pass_tar_info *)argument; + char padding[512]; + + if (entry->is_mounted) { + return ZEND_HASH_APPLY_KEEP; + } + + if (entry->is_deleted) { + if (entry->fp_refcount <= 0) { + return ZEND_HASH_APPLY_REMOVE; + } else { + /* we can't delete this in-memory until it is closed */ + return ZEND_HASH_APPLY_KEEP; + } + } + + phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len TSRMLS_CC); + memset((char *) &header, 0, sizeof(header)); + + if (entry->filename_len > 100) { + char *boundary; + if (entry->filename_len > 256) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too long for tar file format", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + boundary = entry->filename + entry->filename_len - 101; + while (*boundary && *boundary != '/') { + ++boundary; + } + if (!*boundary || ((boundary - entry->filename) > 155)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too long for tar file format", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + memcpy(header.prefix, entry->filename, boundary - entry->filename); + memcpy(header.name, boundary + 1, entry->filename_len - (boundary + 1 - entry->filename)); + } else { + memcpy(header.name, entry->filename, entry->filename_len); + } + + phar_tar_octal(header.mode, entry->flags & PHAR_ENT_PERM_MASK, sizeof(header.mode)-1); + + if (FAILURE == phar_tar_octal(header.size, entry->uncompressed_filesize, sizeof(header.size)-1)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + if (FAILURE == phar_tar_octal(header.mtime, entry->timestamp, sizeof(header.mtime)-1)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, file modification time of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + /* calc checksum */ + header.typeflag = entry->tar_type; + + if (entry->link) { + strncpy(header.linkname, entry->link, strlen(entry->link)); + } + + strncpy(header.magic, "ustar", sizeof("ustar")-1); + strncpy(header.version, "00", sizeof("00")-1); + strncpy(header.checksum, " ", sizeof(" ")-1); + entry->crc32 = phar_tar_checksum((char *)&header, sizeof(header)); + + if (FAILURE == phar_tar_octal(header.checksum, entry->crc32, sizeof(header.checksum)-1)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, checksum of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + /* write header */ + entry->header_offset = php_stream_tell(fp->new); + + if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for file \"%s\" could not be written", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + pos = php_stream_tell(fp->new); /* save start of file within tar */ + + /* write contents */ + if (entry->uncompressed_filesize) { + if (FAILURE == phar_open_entry_fp(entry, fp->error, 0 TSRMLS_CC)) { + return ZEND_HASH_APPLY_STOP; + } + + if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written, seek failed", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + if (SUCCESS != phar_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), fp->new, entry->uncompressed_filesize, NULL)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + memset(padding, 0, 512); + php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize); + } + + if (!entry->is_modified && entry->fp_refcount) { + /* open file pointers refer to this fp, do not free the stream */ + switch (entry->fp_type) { + case PHAR_FP: + fp->free_fp = 0; + break; + case PHAR_UFP: + fp->free_ufp = 0; + default: + break; + } + } + + entry->is_modified = 0; + + if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) { + if (!entry->fp_refcount) { + php_stream_close(entry->fp); + } + entry->fp = NULL; + } + + entry->fp_type = PHAR_FP; + + /* note new location within tar */ + entry->offset = entry->offset_abs = pos; + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +int phar_tar_setmetadata(zval *metadata, phar_entry_info *entry, char **error TSRMLS_DC) /* {{{ */ +{ + php_serialize_data_t metadata_hash; + + if (entry->metadata_str.c) { + smart_str_free(&entry->metadata_str); + } + + entry->metadata_str.c = 0; + entry->metadata_str.len = 0; + PHP_VAR_SERIALIZE_INIT(metadata_hash); + php_var_serialize(&entry->metadata_str, &metadata, &metadata_hash TSRMLS_CC); + PHP_VAR_SERIALIZE_DESTROY(metadata_hash); + entry->uncompressed_filesize = entry->compressed_filesize = entry->metadata_str.len; + + if (entry->fp && entry->fp_type == PHAR_MOD) { + php_stream_close(entry->fp); + } + + entry->fp_type = PHAR_MOD; + entry->is_modified = 1; + entry->fp = php_stream_fopen_tmpfile(); + entry->offset = entry->offset_abs = 0; + + if (entry->metadata_str.len != php_stream_write(entry->fp, entry->metadata_str.c, entry->metadata_str.len)) { + spprintf(error, 0, "phar tar error: unable to write metadata to magic metadata file \"%s\"", entry->filename); + zend_hash_del(&(entry->phar->manifest), entry->filename, entry->filename_len); + return ZEND_HASH_APPLY_STOP; + } + + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +static int phar_tar_setupmetadata(void *pDest, void *argument TSRMLS_DC) /* {{{ */ +{ + int lookfor_len; + struct _phar_pass_tar_info *i = (struct _phar_pass_tar_info *)argument; + char *lookfor, **error = i->error; + phar_entry_info *entry = (phar_entry_info *)pDest, *metadata, newentry = {0}; + + if (entry->filename_len >= sizeof(".phar/.metadata") && !memcmp(entry->filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) { + if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) { + return phar_tar_setmetadata(entry->phar->metadata, entry, error TSRMLS_CC); + } + /* search for the file this metadata entry references */ + if (entry->filename_len >= sizeof(".phar/.metadata/") + sizeof("/.metadata.bin") - 1 && !zend_hash_exists(&(entry->phar->manifest), entry->filename + sizeof(".phar/.metadata/") - 1, entry->filename_len - (sizeof("/.metadata.bin") - 1 + sizeof(".phar/.metadata/") - 1))) { + /* this is orphaned metadata, erase it */ + return ZEND_HASH_APPLY_REMOVE; + } + /* we can keep this entry, the file that refers to it exists */ + return ZEND_HASH_APPLY_KEEP; + } + + if (!entry->is_modified) { + return ZEND_HASH_APPLY_KEEP; + } + + /* now we are dealing with regular files, so look for metadata */ + lookfor_len = spprintf(&lookfor, 0, ".phar/.metadata/%s/.metadata.bin", entry->filename); + + if (!entry->metadata) { + zend_hash_del(&(entry->phar->manifest), lookfor, lookfor_len); + efree(lookfor); + return ZEND_HASH_APPLY_KEEP; + } + + if (SUCCESS == zend_hash_find(&(entry->phar->manifest), lookfor, lookfor_len, (void **)&metadata)) { + int ret; + ret = phar_tar_setmetadata(entry->metadata, metadata, error TSRMLS_CC); + efree(lookfor); + return ret; + } + + newentry.filename = lookfor; + newentry.filename_len = lookfor_len; + newentry.phar = entry->phar; + newentry.tar_type = TAR_FILE; + newentry.is_tar = 1; + + if (SUCCESS != zend_hash_add(&(entry->phar->manifest), lookfor, lookfor_len, (void *)&newentry, sizeof(phar_entry_info), (void **)&metadata)) { + efree(lookfor); + spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for file \"%s\"", entry->filename); + return ZEND_HASH_APPLY_STOP; + } + + return phar_tar_setmetadata(entry->metadata, metadata, error TSRMLS_CC); +} +/* }}} */ + +int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */ +{ + phar_entry_info entry = {0}; + static const char newstub[] = "is_persistent) { + if (error) { + spprintf(error, 0, "internal error: attempt to flush cached tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + + if (phar->is_data) { + goto nostub; + } + + /* set alias */ + if (!phar->is_temporary_alias && phar->alias_len) { + entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1); + entry.filename_len = sizeof(".phar/alias.txt")-1; + entry.fp = php_stream_fopen_tmpfile(); + + if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) { + if (error) { + spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + + entry.uncompressed_filesize = phar->alias_len; + + if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { + if (error) { + spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + } else { + zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1); + } + + /* set stub */ + if (user_stub && !defaultstub) { + char *pos; + if (len < 0) { + /* resource passed in */ + if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) { + if (error) { + spprintf(error, 0, "unable to access resource to copy stub to new tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + if (len == -1) { + len = PHP_STREAM_COPY_ALL; + } else { + len = -len; + } + user_stub = 0; +#if PHP_MAJOR_VERSION >= 6 + if (!(len = php_stream_copy_to_mem(stubfile, (void **) &user_stub, len, 0)) || !user_stub) { +#else + if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) { +#endif + if (error) { + spprintf(error, 0, "unable to read resource to copy stub to new tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + free_user_stub = 1; + } else { + free_user_stub = 0; + } + + tmp = estrndup(user_stub, len); + if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) { + efree(tmp); + if (error) { + spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", phar->fname); + } + if (free_user_stub) { + efree(user_stub); + } + return EOF; + } + pos = user_stub + (pos - tmp); + efree(tmp); + + len = pos - user_stub + 18; + entry.fp = php_stream_fopen_tmpfile(); + entry.uncompressed_filesize = len + 5; + + if ((size_t)len != php_stream_write(entry.fp, user_stub, len) + || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) { + if (error) { + spprintf(error, 0, "unable to create stub from string in new tar-based phar \"%s\"", phar->fname); + } + if (free_user_stub) { + efree(user_stub); + } + php_stream_close(entry.fp); + return EOF; + } + + entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1); + entry.filename_len = sizeof(".phar/stub.php")-1; + zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); + + if (free_user_stub) { + efree(user_stub); + } + } else { + /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */ + entry.fp = php_stream_fopen_tmpfile(); + + if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) { + php_stream_close(entry.fp); + if (error) { + spprintf(error, 0, "unable to %s stub in%star-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname); + } + return EOF; + } + + entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1; + entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1); + entry.filename_len = sizeof(".phar/stub.php")-1; + + if (!defaultstub) { + if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) { + if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { + php_stream_close(entry.fp); + efree(entry.filename); + if (error) { + spprintf(error, 0, "unable to create stub in tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + } else { + php_stream_close(entry.fp); + efree(entry.filename); + } + } else { + if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { + php_stream_close(entry.fp); + efree(entry.filename); + if (error) { + spprintf(error, 0, "unable to overwrite stub in tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + } + } +nostub: + if (phar->fp && !phar->is_brandnew) { + oldfile = phar->fp; + closeoldfile = 0; + php_stream_rewind(oldfile); + } else { + oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL); + closeoldfile = oldfile != NULL; + } + + newfile = php_stream_fopen_tmpfile(); + + if (!newfile) { + if (error) { + spprintf(error, 0, "unable to create temporary file"); + } + if (closeoldfile) { + php_stream_close(oldfile); + } + return EOF; + } + + pass.old = oldfile; + pass.new = newfile; + pass.error = error; + pass.free_fp = 1; + pass.free_ufp = 1; + + if (phar->metadata) { + phar_entry_info *mentry; + if (SUCCESS == zend_hash_find(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void **)&mentry)) { + if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error TSRMLS_CC)) { + if (closeoldfile) { + php_stream_close(oldfile); + } + return EOF; + } + } else { + phar_entry_info newentry = {0}; + + newentry.filename = estrndup(".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1); + newentry.filename_len = sizeof(".phar/.metadata.bin")-1; + newentry.phar = phar; + newentry.tar_type = TAR_FILE; + newentry.is_tar = 1; + + if (SUCCESS != zend_hash_add(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void *)&newentry, sizeof(phar_entry_info), (void **)&mentry)) { + spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for phar archive \"%s\"", phar->fname); + if (closeoldfile) { + php_stream_close(oldfile); + } + return EOF; + } + + if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error TSRMLS_CC)) { + zend_hash_del(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1); + if (closeoldfile) { + php_stream_close(oldfile); + } + return EOF; + } + } + } + + zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_setupmetadata, (void *) &pass TSRMLS_CC); + + if (error && *error) { + if (closeoldfile) { + php_stream_close(oldfile); + } + + /* on error in the hash iterator above, error is set */ + php_stream_close(newfile); + return EOF; + } + + zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_writeheaders, (void *) &pass TSRMLS_CC); + + /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */ + if (!phar->is_data || phar->sig_flags) { + if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, error TSRMLS_CC)) { + if (error) { + char *save = *error; + spprintf(error, 0, "phar error: unable to write signature to tar-based phar: %s", save); + efree(save); + } + + if (closeoldfile) { + php_stream_close(oldfile); + } + + php_stream_close(newfile); + return EOF; + } + + entry.filename = ".phar/signature.bin"; + entry.filename_len = sizeof(".phar/signature.bin")-1; + entry.fp = php_stream_fopen_tmpfile(); + +#ifdef WORDS_BIGENDIAN +# define PHAR_SET_32(var, buffer) \ + *(php_uint32 *)(var) = (((((unsigned char*)&(buffer))[3]) << 24) \ + | ((((unsigned char*)&(buffer))[2]) << 16) \ + | ((((unsigned char*)&(buffer))[1]) << 8) \ + | (((unsigned char*)&(buffer))[0])) +#else +# define PHAR_SET_32(var, buffer) *(php_uint32 *)(var) = (php_uint32) (buffer) +#endif + PHAR_SET_32(sigbuf, phar->sig_flags); + PHAR_SET_32(sigbuf + 4, signature_length); + + if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) { + efree(signature); + if (error) { + spprintf(error, 0, "phar error: unable to write signature to tar-based phar %s", phar->fname); + } + + if (closeoldfile) { + php_stream_close(oldfile); + } + php_stream_close(newfile); + return EOF; + } + + efree(signature); + entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8; + /* throw out return value and write the signature */ + entry.filename_len = phar_tar_writeheaders((void *)&entry, (void *)&pass TSRMLS_CC); + + if (error && *error) { + if (closeoldfile) { + php_stream_close(oldfile); + } + /* error is set by writeheaders */ + php_stream_close(newfile); + return EOF; + } + } /* signature */ + + /* add final zero blocks */ + buf = (char *) ecalloc(1024, 1); + php_stream_write(newfile, buf, 1024); + efree(buf); + + if (closeoldfile) { + php_stream_close(oldfile); + } + + /* on error in the hash iterator above, error is set */ + if (error && *error) { + php_stream_close(newfile); + return EOF; + } + + if (phar->fp && pass.free_fp) { + php_stream_close(phar->fp); + } + + if (phar->ufp) { + if (pass.free_ufp) { + php_stream_close(phar->ufp); + } + phar->ufp = NULL; + } + + phar->is_brandnew = 0; + php_stream_rewind(newfile); + + if (phar->donotflush) { + /* deferred flush */ + phar->fp = newfile; + } else { + phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); + if (!phar->fp) { + phar->fp = newfile; + if (error) { + spprintf(error, 0, "unable to open new phar \"%s\" for writing", phar->fname); + } + return EOF; + } + + if (phar->flags & PHAR_FILE_COMPRESSED_GZ) { + php_stream_filter *filter; + /* to properly compress, we have to tell zlib to add a zlib header */ + zval filterparams; + + array_init(&filterparams); +/* this is defined in zlib's zconf.h */ +#ifndef MAX_WBITS +#define MAX_WBITS 15 +#endif + add_assoc_long(&filterparams, "window", MAX_WBITS + 16); + filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC); + zval_dtor(&filterparams); + + if (!filter) { + /* copy contents uncompressed rather than lose them */ + phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL); + php_stream_close(newfile); + if (error) { + spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname); + } + return EOF; + } + + php_stream_filter_append(&phar->fp->writefilters, filter); + phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL); + php_stream_filter_flush(filter, 1); + php_stream_filter_remove(filter, 1 TSRMLS_CC); + php_stream_close(phar->fp); + /* use the temp stream as our base */ + phar->fp = newfile; + } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) { + php_stream_filter *filter; + + filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC); + php_stream_filter_append(&phar->fp->writefilters, filter); + phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL); + php_stream_filter_flush(filter, 1); + php_stream_filter_remove(filter, 1 TSRMLS_CC); + php_stream_close(phar->fp); + /* use the temp stream as our base */ + phar->fp = newfile; + } else { + phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL); + /* we could also reopen the file in "rb" mode but there is no need for that */ + php_stream_close(newfile); + } + } + return EOF; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_a29791_796ff1_Zend#zend_API.c b/src/main/resource/testFiles/php-src/prevFiles/prev_a29791_796ff1_Zend#zend_API.c new file mode 100644 index 0000000..1fe7a46 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_a29791_796ff1_Zend#zend_API.c @@ -0,0 +1,978 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + + +#include "zend.h" +#include "zend_execute.h" +#include "zend_API.h" +#include "modules.h" +#include "zend_constants.h" + +#ifdef HAVE_STDARG_H +#include +#endif + +/* these variables are true statics/globals, and have to be mutex'ed on every access */ +static int module_count=0; +HashTable module_registry; + +/* this function doesn't check for too many parameters */ +ZEND_API int zend_get_parameters(int ht, int param_count, ...) +{ + void **p; + int arg_count; + va_list ptr; + zval **param, *param_ptr; + ELS_FETCH(); + + p = EG(argument_stack).top_element-2; + arg_count = (ulong) *p; + + if (param_count>arg_count) { + return FAILURE; + } + + va_start(ptr, param_count); + + while (param_count>0) { + param = va_arg(ptr, zval **); + param_ptr = *(p-param_count); + if (!PZVAL_IS_REF(param_ptr) && param_ptr->refcount>1) { + zval *new_tmp; + + ALLOC_ZVAL(new_tmp); + *new_tmp = *param_ptr; + zval_copy_ctor(new_tmp); + INIT_PZVAL(new_tmp); + param_ptr = new_tmp; + ((zval *) *(p-param_count))->refcount--; + *(p-param_count) = param_ptr; + } + *param = param_ptr; + param_count--; + } + va_end(ptr); + + return SUCCESS; +} + + +ZEND_API int zend_get_parameters_array(int ht, int param_count, zval **argument_array) +{ + void **p; + int arg_count; + zval *param_ptr; + ELS_FETCH(); + + p = EG(argument_stack).top_element-2; + arg_count = (ulong) *p; + + if (param_count>arg_count) { + return FAILURE; + } + + + while (param_count>0) { + param_ptr = *(p-param_count); + if (!PZVAL_IS_REF(param_ptr) && param_ptr->refcount>1) { + zval *new_tmp; + + ALLOC_ZVAL(new_tmp); + *new_tmp = *param_ptr; + zval_copy_ctor(new_tmp); + INIT_PZVAL(new_tmp); + param_ptr = new_tmp; + ((zval *) *(p-param_count))->refcount--; + *(p-param_count) = param_ptr; + } + *(argument_array++) = param_ptr; + param_count--; + } + + return SUCCESS; +} + + + + +/* Zend-optimized Extended functions */ +/* this function doesn't check for too many parameters */ +ZEND_API int zend_get_parameters_ex(int param_count, ...) +{ + void **p; + int arg_count; + va_list ptr; + zval ***param; + ELS_FETCH(); + + p = EG(argument_stack).top_element-2; + arg_count = (ulong) *p; + + if (param_count>arg_count) { + return FAILURE; + } + + va_start(ptr, param_count); + while (param_count>0) { + param = va_arg(ptr, zval ***); + *param = (zval **) p-(param_count--); + } + va_end(ptr); + + return SUCCESS; +} + + +ZEND_API int zend_get_parameters_array_ex(int param_count, zval ***argument_array) +{ + void **p; + int arg_count; + ELS_FETCH(); + + p = EG(argument_stack).top_element-2; + arg_count = (ulong) *p; + + if (param_count>arg_count) { + return FAILURE; + } + + while (param_count>0) { + *(argument_array++) = (zval **) p-(param_count--); + } + + return SUCCESS; +} + + +ZEND_API int ParameterPassedByReference(int ht, uint n) +{ + void **p; + ulong arg_count; + zval *arg; + ELS_FETCH(); + + p = EG(argument_stack).elements+EG(argument_stack).top-2; + arg_count = (ulong) *p; + + if (n>arg_count) { + return FAILURE; + } + arg = (zval *) *(p-arg_count+n-1); + return PZVAL_IS_REF(arg); +} + + +ZEND_API void wrong_param_count() +{ + zend_error(E_WARNING,"Wrong parameter count for %s()",get_active_function_name()); +} + + +ZEND_API inline int _array_init(zval *arg ZEND_FILE_LINE_DC) +{ + ALLOC_HASHTABLE_REL(arg->value.ht); + + if (!arg->value.ht || zend_hash_init(arg->value.ht, 0, NULL, ZVAL_PTR_DTOR, 0)) { + zend_error(E_CORE_ERROR, "Cannot allocate memory for array"); + return FAILURE; + } + arg->type = IS_ARRAY; + return SUCCESS; +} + + +ZEND_API inline int _object_init_ex(zval *arg, zend_class_entry *class_type ZEND_FILE_LINE_DC) +{ + zval *tmp; + + if (!class_type->constants_updated) { + zend_hash_apply_with_argument(&class_type->default_properties, (int (*)(void *,void *)) zval_update_constant, (void *) 1); + class_type->constants_updated = 1; + } + + ALLOC_HASHTABLE_REL(arg->value.obj.properties); + zend_hash_init(arg->value.obj.properties, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(arg->value.obj.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + arg->type = IS_OBJECT; + arg->value.obj.ce = class_type; + return SUCCESS; +} + + +ZEND_API inline int _object_init(zval *arg ZEND_FILE_LINE_DC) +{ + return _object_init_ex(arg, &zend_standard_class_def ZEND_FILE_LINE_CC); +} + + +ZEND_API inline int add_assoc_function(zval *arg, char *key,void (*function_ptr)(INTERNAL_FUNCTION_PARAMETERS)) +{ + zend_error(E_WARNING, "add_assoc_function() is no longer supported"); + return FAILURE; +} + + +ZEND_API inline int add_assoc_long(zval *arg, char *key, long n) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_LONG; + tmp->value.lval = n; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_assoc_unset(zval *arg, char *key) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_NULL; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + +ZEND_API inline int add_assoc_bool(zval *arg, char *key, int b) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_BOOL; + tmp->value.lval = b; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_assoc_resource(zval *arg, char *key, int r) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_RESOURCE; + tmp->value.lval = r; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_assoc_double(zval *arg, char *key, double d) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_DOUBLE; + tmp->value.dval = d; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_assoc_string(zval *arg, char *key, char *str, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_assoc_stringl(zval *arg, char *key, char *str, uint length, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_index_long(zval *arg, uint index, long n) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_LONG; + tmp->value.lval = n; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_index_unset(zval *arg, uint index) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_NULL; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_index_bool(zval *arg, uint index, int b) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_BOOL; + tmp->value.lval = b; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_index_resource(zval *arg, uint index, int r) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_RESOURCE; + tmp->value.lval = r; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_index_double(zval *arg, uint index, double d) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_DOUBLE; + tmp->value.dval = d; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_index_string(zval *arg, uint index, char *str, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_index_stringl(zval *arg, uint index, char *str, uint length, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_next_index_long(zval *arg, long n) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_LONG; + tmp->value.lval = n; + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_next_index_unset(zval *arg) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_NULL; + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_next_index_bool(zval *arg, int b) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_BOOL; + tmp->value.lval = b; + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_next_index_resource(zval *arg, int r) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_RESOURCE; + tmp->value.lval = r; + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_next_index_double(zval *arg, double d) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_DOUBLE; + tmp->value.dval = d; + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_next_index_string(zval *arg, char *str, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_next_index_stringl(zval *arg, char *str, uint length, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_get_assoc_string(zval *arg, char *key, char *str, void **dest, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), dest); +} + + +ZEND_API inline int add_get_assoc_stringl(zval *arg, char *key, char *str, uint length, void **dest, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), dest); +} + + +ZEND_API inline int add_get_index_long(zval *arg, uint index, long l, void **dest) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_LONG; + tmp->value.lval = l; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),dest); +} + + +ZEND_API inline int add_get_index_double(zval *arg, uint index, double d, void **dest) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_DOUBLE; + tmp->value.dval= d; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),dest); +} + + +ZEND_API inline int add_get_index_string(zval *arg, uint index, char *str, void **dest, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),dest); +} + + +ZEND_API inline int add_get_index_stringl(zval *arg, uint index, char *str, uint length, void **dest, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),dest); +} + + +ZEND_API inline int add_property_long(zval *arg, char *key, long n) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_LONG; + tmp->value.lval = n; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + +ZEND_API inline int add_property_bool(zval *arg, char *key, int b) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_BOOL; + tmp->value.lval = b; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + +ZEND_API inline int add_property_unset(zval *arg, char *key) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_NULL; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + +ZEND_API inline int add_property_resource(zval *arg, char *key, long n) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_RESOURCE; + tmp->value.lval = n; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_property_double(zval *arg, char *key, double d) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_DOUBLE; + tmp->value.dval = d; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_property_string(zval *arg, char *key, char *str, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_property_stringl(zval *arg, char *key, char *str, uint length, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API int zend_startup_module(zend_module_entry *module) +{ + if (module) { + module->module_number = zend_next_free_module(); + if (module->module_startup_func) { + ELS_FETCH(); + + if (module->module_startup_func(MODULE_PERSISTENT, module->module_number ELS_CC)==FAILURE) { + zend_error(E_CORE_ERROR,"Unable to start %s module",module->name); + return FAILURE; + } + } + module->type = MODULE_PERSISTENT; + zend_register_module(module); + } + return SUCCESS; +} + + +/* registers all functions in *library_functions in the function hash */ +int zend_register_functions(zend_function_entry *functions, HashTable *function_table) +{ + zend_function_entry *ptr = functions; + zend_function function; + zend_internal_function *internal_function = (zend_internal_function *)&function; + int count=0,unload=0; + HashTable *target_function_table = function_table; + CLS_FETCH(); + + if (!target_function_table) { + target_function_table = CG(function_table); + } + internal_function->type = ZEND_INTERNAL_FUNCTION; + + while (ptr->fname) { + internal_function->handler = ptr->handler; + internal_function->arg_types = ptr->func_arg_types; + internal_function->function_name = ptr->fname; + if (!internal_function->handler) { + zend_error(E_CORE_WARNING,"Null function defined as active function"); + zend_unregister_functions(functions, count, target_function_table); + return FAILURE; + } + if (zend_hash_add(target_function_table, ptr->fname, strlen(ptr->fname)+1, &function, sizeof(zend_function), NULL) == FAILURE) { + unload=1; + break; + } + ptr++; + count++; + } + if (unload) { /* before unloading, display all remaining bad function in the module */ + while (ptr->fname) { + if (zend_hash_exists(target_function_table, ptr->fname, strlen(ptr->fname)+1)) { + zend_error(E_CORE_WARNING, "Function registration failed - duplicate name - %s",ptr->fname); + } + ptr++; + } + zend_unregister_functions(functions, count, target_function_table); + return FAILURE; + } + return SUCCESS; +} + +/* count=-1 means erase all functions, otherwise, + * erase the first count functions + */ +void zend_unregister_functions(zend_function_entry *functions, int count, HashTable *function_table) +{ + zend_function_entry *ptr = functions; + int i=0; + HashTable *target_function_table = function_table; + CLS_FETCH(); + + if (!target_function_table) { + target_function_table = CG(function_table); + } + while (ptr->fname) { + if (count!=-1 && i>=count) { + break; + } +#if 0 + zend_printf("Unregistering %s()\n", ptr->fname); +#endif + zend_hash_del(target_function_table, ptr->fname, strlen(ptr->fname)+1); + ptr++; + i++; + } +} + + +ZEND_API int zend_register_module(zend_module_entry *module) +{ +#if 0 + zend_printf("%s: Registering module %d\n",module->name, module->module_number); +#endif + if (module->functions && zend_register_functions(module->functions, NULL)==FAILURE) { + zend_error(E_CORE_WARNING,"%s: Unable to register functions, unable to load",module->name); + return FAILURE; + } + module->module_started=1; + return zend_hash_add(&module_registry, module->name,strlen(module->name)+1,(void *)module,sizeof(zend_module_entry),NULL); +} + + +void module_destructor(zend_module_entry *module) +{ + if (module->type == MODULE_TEMPORARY) { + zend_clean_module_rsrc_dtors(module->module_number); + clean_module_constants(module->module_number); + if (module->request_shutdown_func) + module->request_shutdown_func(module->type, module->module_number); + } + + if (module->module_started && module->module_shutdown_func) { +#if 0 + zend_printf("%s: Module shutdown\n",module->name); +#endif + module->module_shutdown_func(module->type, module->module_number); + } + module->module_started=0; + if (module->functions) { + zend_unregister_functions(module->functions, -1, NULL); + } + +#if HAVE_LIBDL + if (module->handle) { + dlclose(module->handle); + } +#endif +} + + +/* call request startup for all modules */ +int module_registry_request_startup(zend_module_entry *module) +{ + if (module->request_startup_func) { + ELS_FETCH(); + +#if 0 + zend_printf("%s: Request startup\n",module->name); +#endif + if (module->request_startup_func(module->type, module->module_number ELS_CC)==FAILURE) { + zend_error(E_WARNING, "request_startup() for %s module failed", module->name); + exit(1); + } + } + return 0; +} + + +/* for persistent modules - call request shutdown and flag NOT to erase + * for temporary modules - do nothing, and flag to erase + */ +int module_registry_cleanup(zend_module_entry *module) +{ + switch(module->type) { + case MODULE_PERSISTENT: + if (module->request_shutdown_func) { +#if 0 + zend_printf("%s: Request shutdown\n",module->name); +#endif + module->request_shutdown_func(module->type, module->module_number); + } + return 0; + break; + case MODULE_TEMPORARY: + return 1; + break; + } + return 0; +} + + +/* return the next free module number */ +int zend_next_free_module(void) +{ + return ++module_count; +} + +/* If parent_ce is not NULL then it inherits from parent_ce + * If parent_ce is NULL and parent_name isn't then it looks for the parent and inherits from it + * If both parent_ce and parent_name are NULL it does a regular class registration + * If parent_name is specified but not found NULL is returned + */ +ZEND_API zend_class_entry *zend_register_internal_class_ex(zend_class_entry *class_entry, zend_class_entry *parent_ce, char *parent_name) +{ + zend_class_entry *register_class; + CLS_FETCH(); + + if (!parent_ce && parent_name) { + if (zend_hash_find(CG(class_table), parent_name, strlen(parent_name)+1, (void **) &parent_ce)==FAILURE) { + return NULL; + } + } + + register_class = zend_register_internal_class(class_entry); + + if (parent_ce) { + do_inheritance(register_class, parent_ce); + } + return register_class; +} + +ZEND_API zend_class_entry *zend_register_internal_class(zend_class_entry *class_entry) +{ + zend_class_entry *register_class; + char *lowercase_name = zend_strndup(class_entry->name, class_entry->name_length); + CLS_FETCH(); + + zend_str_tolower(lowercase_name, class_entry->name_length); + + class_entry->type = ZEND_INTERNAL_CLASS; + class_entry->parent = NULL; + class_entry->refcount = (int *) malloc(sizeof(int)); + *class_entry->refcount = 1; + class_entry->constants_updated = 0; + zend_hash_init(&class_entry->default_properties, 0, NULL, ZVAL_PTR_DTOR, 1); + zend_hash_init(&class_entry->function_table, 0, NULL, ZEND_FUNCTION_DTOR, 1); + + + if (class_entry->builtin_functions) { + zend_register_functions(class_entry->builtin_functions, &class_entry->function_table); + } + + zend_hash_update(CG(class_table), lowercase_name, class_entry->name_length+1, class_entry, sizeof(zend_class_entry), (void **) ®ister_class); + free(lowercase_name); + return register_class; +} + + +ZEND_API zend_module_entry *zend_get_module(int module_number) +{ + zend_module_entry *module; + + if (zend_hash_index_find(&module_registry, module_number, (void **) &module)==SUCCESS) { + return module; + } else { + return NULL; + } +} + +ZEND_API int zend_set_hash_symbol(zval *symbol, char *name, int name_length, + int is_ref, int num_symbol_tables, ...) +{ + HashTable *symbol_table; + va_list symbol_table_list; + + if (num_symbol_tables <= 0) return FAILURE; + + symbol->is_ref = is_ref; + + va_start(symbol_table_list, num_symbol_tables); + while(num_symbol_tables-- > 0) { + symbol_table = va_arg(symbol_table_list, HashTable *); + zend_hash_update(symbol_table, name, name_length + 1, &symbol, sizeof(zval *), NULL); + zval_add_ref(&symbol); + } + va_end(symbol_table_list); + return SUCCESS; +} + + + + +/* Disabled functions support */ + +static ZEND_FUNCTION(display_disabled_function) +{ + zend_error(E_WARNING, "%s() has been disabled for security reasons.", get_active_function_name()); +} + + +static zend_function_entry disabled_function[] = { + ZEND_FE(display_disabled_function, NULL) + { NULL, NULL, NULL } +}; + + +ZEND_API int zend_disable_function(char *function_name, uint function_name_length) +{ + CLS_FETCH(); + + if (zend_hash_del(CG(function_table), function_name, function_name_length+1)==FAILURE) { + return FAILURE; + } + disabled_function[0].fname = function_name; + return zend_register_functions(disabled_function, CG(function_table)); +} diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_a347ed_d2b435_ext#ereg#ereg.c b/src/main/resource/testFiles/php-src/prevFiles/prev_a347ed_d2b435_ext#ereg#ereg.c new file mode 100644 index 0000000..0335347 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_a347ed_d2b435_ext#ereg#ereg.c @@ -0,0 +1,658 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2002 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jim Winstead | + | Jaakko Hyvätti | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include +#include "php.h" +#include "php_string.h" +#include "reg.h" +#include "ext/standard/info.h" + +#ifdef ZTS +int reg_globals_id; +#else +static php_reg_globals reg_globals; +#endif + +typedef struct { + regex_t preg; + int cflags; +} reg_cache; + +/* {{{ _php_regcomp + */ +static int _php_regcomp(regex_t *preg, const char *pattern, int cflags) +{ + int r = 0; + int patlen = strlen(pattern); + reg_cache *rc = NULL; + TSRMLS_FETCH(); + + if(zend_hash_find(®(ht_rc), (char *) pattern, patlen+1, (void **) &rc) == FAILURE || + rc->cflags != cflags) { + r = regcomp(preg, pattern, cflags); + if(!r) { + reg_cache rcp; + + rcp.cflags = cflags; + memcpy(&rcp.preg, preg, sizeof(*preg)); + zend_hash_update(®(ht_rc), (char *) pattern, patlen+1, + (void *) &rcp, sizeof(rcp), NULL); + } + } else { + memcpy(preg, &rc->preg, sizeof(*preg)); + } + + return r; +} +/* }}} */ + +static void _free_reg_cache(reg_cache *rc) +{ + regfree(&rc->preg); +} + +#undef regfree +#define regfree(a); +#undef regcomp +#define regcomp(a, b, c) _php_regcomp(a, b, c) + +static void php_reg_init_globals(php_reg_globals *reg_globals TSRMLS_DC) +{ + zend_hash_init(®_globals->ht_rc, 0, NULL, (void (*)(void *)) _free_reg_cache, 1); +} + +PHP_MINIT_FUNCTION(regex) +{ +#ifdef ZTS + ts_allocate_id(®_globals_id, sizeof(php_reg_globals), (ts_allocate_ctor) php_reg_init_globals, NULL); +#else + php_reg_init_globals(®_globals TSRMLS_CC); +#endif + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(regex) +{ + zend_hash_destroy(®(ht_rc)); + return SUCCESS; +} + +PHP_MINFO_FUNCTION(regex) +{ +#if HSREGEX + php_info_print_table_row(2, "Regex Library", "Bundled library enabled"); +#else + php_info_print_table_row(2, "Regex Library", "System library enabled"); +#endif +} + + +/* {{{ php_reg_eprint + * php_reg_eprint - convert error number to name + */ +static void php_reg_eprint(int err, regex_t *re) { + char *buf = NULL, *message = NULL; + size_t len; + size_t buf_len; + +#ifdef REG_ITOA + /* get the length of the message */ + buf_len = regerror(REG_ITOA | err, re, NULL, 0); + if (buf_len) { + buf = (char *)emalloc(buf_len * sizeof(char)); + if (!buf) return; /* fail silently */ + /* finally, get the error message */ + regerror(REG_ITOA | err, re, buf, buf_len); + } +#else + buf_len = 0; +#endif + len = regerror(err, re, NULL, 0); + if (len) { + message = (char *)emalloc((buf_len + len + 2) * sizeof(char)); + if (!message) { + return; /* fail silently */ + } + if (buf_len) { + snprintf(message, buf_len, "%s: ", buf); + buf_len += 1; /* so pointer math below works */ + } + /* drop the message into place */ + regerror(err, re, message + buf_len, len); + + php_error(E_WARNING, "%s", message); + } + + STR_FREE(buf); + STR_FREE(message); +} +/* }}} */ + +/* {{{ php_ereg + */ +static void php_ereg(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval **regex, /* Regular expression */ + **findin, /* String to apply expression to */ + **array = NULL; /* Optional register array */ + regex_t re; + regmatch_t *subs; + int err, match_len, string_len; + uint i; + int copts = 0; + off_t start, end; + char *buf = NULL; + char *string = NULL; + int argc = ZEND_NUM_ARGS(); + + if (argc < 2 || argc > 3 || + zend_get_parameters_ex(argc, ®ex, &findin, &array) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (icase) + copts |= REG_ICASE; + + if (argc == 2) + copts |= REG_NOSUB; + + /* compile the regular expression from the supplied regex */ + if (Z_TYPE_PP(regex) == IS_STRING) { + err = regcomp(&re, Z_STRVAL_PP(regex), REG_EXTENDED | copts); + } else { + /* we convert numbers to integers and treat them as a string */ + if (Z_TYPE_PP(regex) == IS_DOUBLE) + convert_to_long_ex(regex); /* get rid of decimal places */ + convert_to_string_ex(regex); + /* don't bother doing an extended regex with just a number */ + err = regcomp(&re, Z_STRVAL_PP(regex), copts); + } + + if (err) { + php_reg_eprint(err, &re); + RETURN_FALSE; + } + + /* make a copy of the string we're looking in */ + convert_to_string_ex(findin); + string = estrndup(Z_STRVAL_PP(findin), Z_STRLEN_PP(findin)); + + /* allocate storage for (sub-)expression-matches */ + subs = (regmatch_t *)ecalloc(sizeof(regmatch_t),re.re_nsub+1); + if (!subs) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg"); + RETURN_FALSE; + } + + /* actually execute the regular expression */ + err = regexec(&re, string, re.re_nsub+1, subs, 0); + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + regfree(&re); + efree(subs); + RETURN_FALSE; + } + match_len = 1; + + if (array && err != REG_NOMATCH) { + match_len = (int) (subs[0].rm_eo - subs[0].rm_so); + string_len = Z_STRLEN_PP(findin) + 1; + + buf = emalloc(string_len); + if (!buf) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg"); + regfree(&re); + efree(subs); + RETURN_FALSE; + } + + zval_dtor(*array); /* start with clean array */ + array_init(*array); + + for (i = 0; i <= re.re_nsub; i++) { + start = subs[i].rm_so; + end = subs[i].rm_eo; + if (start != -1 && end > 0 && start < string_len && end < string_len && start < end) { + add_index_stringl(*array, i, string+start, end-start, 1); + } else { + add_index_bool(*array, i, 0); + } + } + efree(buf); + } + + efree(subs); + efree(string); + if (err == REG_NOMATCH) { + RETVAL_FALSE; + } else { + if (match_len == 0) + match_len = 1; + RETVAL_LONG(match_len); + } + regfree(&re); +} +/* }}} */ + +/* {{{ proto int ereg(string pattern, string string [, array registers]) + Regular expression match */ +PHP_FUNCTION(ereg) +{ + php_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto int eregi(string pattern, string string [, array registers]) + Case-insensitive regular expression match */ +PHP_FUNCTION(eregi) +{ + php_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ php_reg_replace + * this is the meat and potatoes of regex replacement! */ +PHPAPI char *php_reg_replace(const char *pattern, const char *replace, const char *string, int icase, int extended) +{ + regex_t re; + regmatch_t *subs; + + char *buf, /* buf is where we build the replaced string */ + *nbuf, /* nbuf is used when we grow the buffer */ + *walkbuf; /* used to walk buf when replacing backrefs */ + const char *walk; /* used to walk replacement string for backrefs */ + int buf_len; + int pos, tmp, string_len, new_l; + int err, copts = 0; + + string_len = strlen(string); + + if (icase) + copts = REG_ICASE; + if (extended) + copts |= REG_EXTENDED; + + err = regcomp(&re, pattern, copts); + if (err) { + php_reg_eprint(err, &re); + return ((char *) -1); + } + + + /* allocate storage for (sub-)expression-matches */ + subs = (regmatch_t *)ecalloc(sizeof(regmatch_t),re.re_nsub+1); + if (!subs) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg_replace"); + return ((char *) -1); + } + + /* start with a buffer that is twice the size of the stringo + we're doing replacements in */ + buf_len = 2 * string_len + 1; + buf = emalloc(buf_len * sizeof(char)); + if (!buf) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg_replace"); + efree(subs); + regfree(&re); + return ((char *) -1); + } + + err = pos = 0; + buf[0] = '\0'; + while (!err) { + err = regexec(&re, &string[pos], re.re_nsub+1, subs, (pos ? REG_NOTBOL : 0)); + + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + efree(subs); + efree(buf); + regfree(&re); + return ((char *) -1); + } + + if (!err) { + /* backref replacement is done in two passes: + 1) find out how long the string will be, and allocate buf + 2) copy the part before match, replacement and backrefs to buf + + Jaakko Hyvätti + */ + + new_l = strlen(buf) + subs[0].rm_so; /* part before the match */ + walk = replace; + while (*walk) + if ('\\' == *walk + && '0' <= walk[1] && '9' >= walk[1] + && subs[walk[1] - '0'].rm_so > -1 + && subs[walk[1] - '0'].rm_eo > -1) { + new_l += subs[walk[1] - '0'].rm_eo + - subs[walk[1] - '0'].rm_so; + walk += 2; + } else { + new_l++; + walk++; + } + + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + tmp = strlen(buf); + /* copy the part of the string before the match */ + strncat(buf, &string[pos], subs[0].rm_so); + + /* copy replacement and backrefs */ + walkbuf = &buf[tmp + subs[0].rm_so]; + walk = replace; + while (*walk) + if ('\\' == *walk + && '0' <= walk[1] && '9' >= walk[1] + && subs[walk[1] - '0'].rm_so > -1 + && subs[walk[1] - '0'].rm_eo > -1 + /* this next case shouldn't happen. it does. */ + && subs[walk[1] - '0'].rm_so <= subs[walk[1] - '0'].rm_eo) { + tmp = subs[walk[1] - '0'].rm_eo + - subs[walk[1] - '0'].rm_so; + memcpy (walkbuf, + &string[pos + subs[walk[1] - '0'].rm_so], + tmp); + walkbuf += tmp; + walk += 2; + } else + *walkbuf++ = *walk++; + *walkbuf = '\0'; + + /* and get ready to keep looking for replacements */ + if (subs[0].rm_so == subs[0].rm_eo) { + if (subs[0].rm_so + pos >= string_len) + break; + new_l = strlen (buf) + 1; + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + pos += subs[0].rm_eo + 1; + buf [new_l-1] = string [pos-1]; + buf [new_l] = '\0'; + } else { + pos += subs[0].rm_eo; + } + } else { /* REG_NOMATCH */ + new_l = strlen(buf) + strlen(&string[pos]); + if (new_l + 1 > buf_len) { + buf_len = new_l + 1; /* now we know exactly how long it is */ + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + /* stick that last bit of string on our output */ + strcat(buf, &string[pos]); + } + } + + /* don't want to leak memory .. */ + efree(subs); + regfree(&re); + + /* whew. */ + return (buf); +} +/* }}} */ + +/* {{{ php_ereg_replace + */ +static void php_ereg_replace(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval **arg_pattern, + **arg_replace, + **arg_string; + char *pattern; + char *string; + char *replace; + char *ret; + + if (ZEND_NUM_ARGS() != 3 || + zend_get_parameters_ex(3, &arg_pattern, &arg_replace, &arg_string) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (Z_TYPE_PP(arg_pattern) == IS_STRING) { + if (Z_STRVAL_PP(arg_pattern) && Z_STRLEN_PP(arg_pattern)) + pattern = estrndup(Z_STRVAL_PP(arg_pattern), Z_STRLEN_PP(arg_pattern)); + else + pattern = empty_string; + } else { + convert_to_long_ex(arg_pattern); + pattern = emalloc(2); + pattern[0] = (char) Z_LVAL_PP(arg_pattern); + pattern[1] = '\0'; + } + + if (Z_TYPE_PP(arg_replace) == IS_STRING) { + if (Z_STRVAL_PP(arg_replace) && Z_STRLEN_PP(arg_replace)) + replace = estrndup(Z_STRVAL_PP(arg_replace), Z_STRLEN_PP(arg_replace)); + else + replace = empty_string; + } else { + convert_to_long_ex(arg_replace); + replace = emalloc(2); + replace[0] = (char) Z_LVAL_PP(arg_replace); + replace[1] = '\0'; + } + + convert_to_string_ex(arg_string); + if (Z_STRVAL_PP(arg_string) && Z_STRLEN_PP(arg_string)) + string = estrndup(Z_STRVAL_PP(arg_string), Z_STRLEN_PP(arg_string)); + else + string = empty_string; + + /* do the actual work */ + ret = php_reg_replace(pattern, replace, string, icase, 1); + if (ret == (char *) -1) { + RETVAL_FALSE; + } else { + RETVAL_STRING(ret, 1); + STR_FREE(ret); + } + + STR_FREE(string); + STR_FREE(replace); + STR_FREE(pattern); +} +/* }}} */ + +/* {{{ proto string ereg_replace(string pattern, string replacement, string string) + Replace regular expression */ +PHP_FUNCTION(ereg_replace) +{ + php_ereg_replace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto string eregi_replace(string pattern, string replacement, string string) + Case insensitive replace regular expression */ +PHP_FUNCTION(eregi_replace) +{ + php_ereg_replace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ php_split + */ +static void php_split(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + zval **spliton, **str, **arg_count = NULL; + regex_t re; + regmatch_t subs[1]; + char *strp, *endp; + int err, size, count = -1, copts = 0; + int argc = ZEND_NUM_ARGS(); + + if (argc < 2 || argc > 3 || + zend_get_parameters_ex(argc, &spliton, &str, &arg_count) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (argc > 2) { + convert_to_long_ex(arg_count); + count = Z_LVAL_PP(arg_count); + } + + if (icase) + copts = REG_ICASE; + + convert_to_string_ex(spliton); + convert_to_string_ex(str); + + strp = Z_STRVAL_PP(str); + endp = strp + Z_STRLEN_PP(str); + + err = regcomp(&re, Z_STRVAL_PP(spliton), REG_EXTENDED | copts); + if (err) { + php_reg_eprint(err, &re); + RETURN_FALSE; + } + + if (array_init(return_value) == FAILURE) { + regfree(&re); + RETURN_FALSE; + } + + /* churn through str, generating array entries as we go */ + while ((count == -1 || count > 1) && !(err = regexec(&re, strp, 1, subs, 0))) { + if (subs[0].rm_so == 0 && subs[0].rm_eo) { + /* match is at start of string, return empty string */ + add_next_index_stringl(return_value, empty_string, 0, 1); + /* skip ahead the length of the regex match */ + strp += subs[0].rm_eo; + } else if (subs[0].rm_so == 0 && subs[0].rm_eo == 0) { + /* No more matches */ + regfree(&re); + php_error(E_WARNING, "Invalid Regular Expression to split()"); + zend_hash_destroy(Z_ARRVAL_P(return_value)); + efree(Z_ARRVAL_P(return_value)); + RETURN_FALSE; + } else { + /* On a real match */ + + /* make a copy of the substring */ + size = subs[0].rm_so; + + /* add it to the array */ + add_next_index_stringl(return_value, strp, size, 1); + + /* point at our new starting point */ + strp = strp + subs[0].rm_eo; + } + + /* if we're only looking for a certain number of points, + stop looking once we hit it */ + if (count != -1) { + count--; + } + } + + /* see if we encountered an error */ + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + regfree(&re); + zend_hash_destroy(Z_ARRVAL_P(return_value)); + efree(Z_ARRVAL_P(return_value)); + RETURN_FALSE; + } + + /* otherwise we just have one last element to add to the array */ + size = endp - strp; + + add_next_index_stringl(return_value, strp, size, 1); + + regfree(&re); +} +/* }}} */ + +/* {{{ proto array split(string pattern, string string [, int limit]) + Split string into array by regular expression */ +PHP_FUNCTION(split) +{ + php_split(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto array spliti(string pattern, string string [, int limit]) + Split string into array by regular expression case-insensitive */ + +PHP_FUNCTION(spliti) +{ + php_split(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} + +/* }}} */ + +/* {{{ proto string sql_regcase(string string) + Make regular expression for case insensitive match */ +PHPAPI PHP_FUNCTION(sql_regcase) +{ + zval **string; + char *tmp; + unsigned char c; + register int i, j; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &string)==FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_string_ex(string); + + tmp = emalloc((Z_STRLEN_PP(string) * 4) + 1); + + for (i = j = 0; i < Z_STRLEN_PP(string); i++) { + c = (unsigned char) Z_STRVAL_PP(string)[i]; + if(isalpha(c)) { + tmp[j++] = '['; + tmp[j++] = toupper(c); + tmp[j++] = tolower(c); + tmp[j++] = ']'; + } else { + tmp[j++] = c; + } + } + tmp[j] = 0; + + RETVAL_STRINGL(tmp, j, 1); + efree(tmp); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_b88698_34b3dc_ext#standard#reg.c b/src/main/resource/testFiles/php-src/prevFiles/prev_b88698_34b3dc_ext#standard#reg.c new file mode 100644 index 0000000..c839e1a --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_b88698_34b3dc_ext#standard#reg.c @@ -0,0 +1,664 @@ +/* + +----------------------------------------------------------------------+ + | PHP HTML Embedded Scripting Language Version 3.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997,1998 PHP Development Team (See Credits file) | + +----------------------------------------------------------------------+ + | This program is free software; you can redistribute it and/or modify | + | it under the terms of one of the following licenses: | + | | + | A) the GNU General Public License as published by the Free Software | + | Foundation; either version 2 of the License, or (at your option) | + | any later version. | + | | + | B) the PHP License as published by the PHP Development Team and | + | included in the distribution in the file: LICENSE | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + | | + | You should have received a copy of both licenses referred to here. | + | If you did not, or have any questions about PHP licensing, please | + | contact core@php.net. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jim Winstead | + | Jaakko Hyvätti | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include +#include "php.h" +#include "php3_string.h" +#include "php_regex.h" +#include "reg.h" + +unsigned char third_argument_force_ref[] = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE }; + +function_entry reg_functions[] = { + {"ereg", php3_ereg, third_argument_force_ref }, + {"ereg_replace", php3_eregreplace, NULL }, + {"eregi", php3_eregi, third_argument_force_ref }, + {"eregi_replace", php3_eregireplace, NULL }, + {"split", php3_split, NULL}, + {"join", php3_implode, NULL}, + {"sql_regcase", php3_sql_regcase, NULL}, + {NULL, NULL, NULL} +}; + + +static int php_minit_regex(INIT_FUNC_ARGS); +static int php_mshutdown_regex(SHUTDOWN_FUNC_ARGS); +static void php_info_regex(ZEND_MODULE_INFO_FUNC_ARGS); + +zend_module_entry regexp_module_entry = { + "Regular Expressions", + reg_functions, + php_minit_regex, php_mshutdown_regex, + NULL, NULL, php_info_regex, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef ZTS +int reg_globals_id; +#else +static php_reg_globals reg_globals; +#endif + +typedef struct { + regex_t preg; + int cflags; +} reg_cache; + +static int _php_regcomp(regex_t *preg, const char *pattern, int cflags) +{ + int r = 0; + int patlen = strlen(pattern); + reg_cache *rc = NULL; + REGLS_FETCH(); + + if(_php3_hash_find(®(ht_rc), (char *) pattern, patlen+1, (void **) &rc) == FAILURE || + rc->cflags != cflags) { + r = regcomp(preg, pattern, cflags); + if(!r) { + reg_cache rcp; + + rcp.cflags = cflags; + memcpy(&rcp.preg, preg, sizeof(*preg)); + _php3_hash_update(®(ht_rc), (char *) pattern, patlen+1, + (void *) &rcp, sizeof(rcp), NULL); + } + } else { + memcpy(preg, &rc->preg, sizeof(*preg)); + } + + return r; +} + +static void _free_reg_cache(reg_cache *rc) +{ + regfree(&rc->preg); +} + +#define regfree(a); +#define regcomp(a,b,c) _php_regcomp(a,b,c) + +static void php_reg_init_globals(php_reg_globals *reg_globals) +{ + _php3_hash_init(®_globals->ht_rc, 0, NULL, (void (*)(void *)) _free_reg_cache, 1); +} + +static int php_minit_regex(INIT_FUNC_ARGS) +{ +#ifdef ZTS + reg_globals_id = ts_allocate_id(sizeof(php_reg_globals), php_reg_init_globals, NULL); +#else + php_reg_init_globals(®_globals); +#endif + + return SUCCESS; +} + +static int php_mshutdown_regex(SHUTDOWN_FUNC_ARGS) +{ + REGLS_FETCH(); + + _php3_hash_destroy(®(ht_rc)); + return SUCCESS; +} + +static void php_info_regex(ZEND_MODULE_INFO_FUNC_ARGS) +{ +#if HSREGEX + PUTS("Bundled regex library enabled\n"); +#else + PUTS("System regex library enabled\n"); +#endif +} + + +/* This is the maximum number of (..) constructs we'll generate from a + call to ereg() or eregi() with the optional third argument. */ +#define NS 10 + +/* + * _php3_reg_eprint - convert error number to name + */ +static void _php3_reg_eprint(int err, regex_t *re) { + char *buf = NULL, *message = NULL; + size_t len; + size_t buf_len; + +#ifdef REG_ITOA + /* get the length of the message */ + buf_len = regerror(REG_ITOA | err, re, NULL, 0); + if (buf_len) { + buf = (char *)emalloc(buf_len * sizeof(char)); + if (!buf) return; /* fail silently */ + /* finally, get the error message */ + regerror(REG_ITOA | err, re, buf, buf_len); + } +#else + buf_len = 0; +#endif + len = regerror(err, re, NULL, 0); + if (len) { + message = (char *)emalloc((buf_len + len + 2) * sizeof(char)); + if (!message) { + return; /* fail silently */ + } + if (buf_len) { + snprintf(message, buf_len, "%s: ", buf); + buf_len += 1; /* so pointer math below works */ + } + /* drop the message into place */ + regerror(err, re, message + buf_len, len); + + php3_error(E_WARNING, "%s", message); + } + + STR_FREE(buf); + STR_FREE(message); +} + +static void _php3_ereg(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval *regex, /* Regular expression */ + *findin, /* String to apply expression to */ + *array = NULL; /* Optional register array */ + regex_t re; + regmatch_t subs[NS]; + int err, i, match_len, string_len; + int copts = 0; + off_t start, end; + char *buf = NULL; + char *string = NULL; + + if (icase) + copts |= REG_ICASE; + + switch(ARG_COUNT(ht)) { + case 2: + if (getParameters(ht, 2, ®ex, &findin) == FAILURE) { + WRONG_PARAM_COUNT; + } + /* don't bother doing substring matching if we're not going + to make use of the information */ + copts |= REG_NOSUB; + break; + case 3: + if (getParameters(ht, 3, ®ex, &findin, &array) == FAILURE) { + WRONG_PARAM_COUNT; + } + if (!ParameterPassedByReference(ht, 3)) { + php3_error(E_WARNING, "Array to be filled with values must be passed by reference."); + RETURN_FALSE; + } + break; + default: + WRONG_PARAM_COUNT; + } + + + /* compile the regular expression from the supplied regex */ + if (regex->type == IS_STRING) { + err = regcomp(&re, regex->value.str.val, REG_EXTENDED | copts); + } else { + /* we convert numbers to integers and treat them as a string */ + if (regex->type == IS_DOUBLE) + convert_to_long(regex); /* get rid of decimal places */ + convert_to_string(regex); + /* don't bother doing an extended regex with just a number */ + err = regcomp(&re, regex->value.str.val, copts); + } + + if (err) { + _php3_reg_eprint(err, &re); + RETURN_FALSE; + } + + /* make a copy of the string we're looking in */ + convert_to_string(findin); + string = estrndup(findin->value.str.val, findin->value.str.len); + + /* actually execute the regular expression */ + err = regexec(&re, string, (size_t) NS, subs, 0); + if (err && err != REG_NOMATCH) { + _php3_reg_eprint(err, &re); + regfree(&re); + RETURN_FALSE; + } + match_len = 1; + + if (array && err != REG_NOMATCH) { + match_len = (int) (subs[0].rm_eo - subs[0].rm_so); + string_len = strlen(string) + 1; + + buf = emalloc(string_len); + if (!buf) { + php3_error(E_WARNING, "Unable to allocate memory in _php3_ereg"); + RETURN_FALSE; + } + + pval_destructor(array); /* start with clean array */ + array_init(array); + + for (i = 0; i < NS; i++) { + start = subs[i].rm_so; + end = subs[i].rm_eo; + if (start != -1 && end > 0 && start < string_len && end < string_len && start < end) { + add_index_stringl(array, i, string+start, end-start, 1); + } + } + efree(buf); + } + + efree(string); + if (err == REG_NOMATCH) { + RETVAL_FALSE; + } else { + if (match_len == 0) + match_len = 1; + RETVAL_LONG(match_len); + } + regfree(&re); +} + +/* {{{ proto int ereg(string pattern, string string [, array registers]) + Regular expression match */ +PHP_FUNCTION(ereg) +{ + _php3_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto int eregi(string pattern, string string [, array registers]) + Case-insensitive regular expression match */ +PHP_FUNCTION(eregi) +{ + _php3_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* this is the meat and potatoes of regex replacement! */ +char *_php3_regreplace(const char *pattern, const char *replace, const char *string, int icase, int extended) +{ + regex_t re; + regmatch_t subs[NS]; + + char *buf, /* buf is where we build the replaced string */ + *nbuf, /* nbuf is used when we grow the buffer */ + *walkbuf; /* used to walk buf when replacing backrefs */ + const char *walk; /* used to walk replacement string for backrefs */ + int buf_len; + int pos, tmp, string_len, new_l; + int err, copts = 0; + + string_len = strlen(string); + if (!string_len) + return (char *)string; + + if (icase) + copts = REG_ICASE; + if (extended) + copts |= REG_EXTENDED; + err = regcomp(&re, pattern, copts); + if (err) { + _php3_reg_eprint(err, &re); + return ((char *) -1); + } + + /* start with a buffer that is twice the size of the stringo + we're doing replacements in */ + buf_len = 2 * string_len + 1; + buf = emalloc(buf_len * sizeof(char)); + if (!buf) { + php3_error(E_WARNING, "Unable to allocate memory in _php3_regreplace"); + regfree(&re); + return ((char *) -1); + } + + err = pos = 0; + buf[0] = '\0'; + + while (!err) { + err = regexec(&re, &string[pos], (size_t) NS, subs, (pos ? REG_NOTBOL : 0)); + + if (err && err != REG_NOMATCH) { + _php3_reg_eprint(err, &re); + regfree(&re); + return ((char *) -1); + } + if (!err) { + /* backref replacement is done in two passes: + 1) find out how long the string will be, and allocate buf + 2) copy the part before match, replacement and backrefs to buf + + Jaakko Hyvätti + */ + + new_l = strlen(buf) + subs[0].rm_so; /* part before the match */ + walk = replace; + while (*walk) + if ('\\' == *walk + && '0' <= walk[1] && '9' >= walk[1] + && subs[walk[1] - '0'].rm_so > -1 + && subs[walk[1] - '0'].rm_eo > -1) { + new_l += subs[walk[1] - '0'].rm_eo + - subs[walk[1] - '0'].rm_so; + walk += 2; + } else { + new_l++; + walk++; + } + + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + tmp = strlen(buf); + /* copy the part of the string before the match */ + strncat(buf, &string[pos], subs[0].rm_so); + + /* copy replacement and backrefs */ + walkbuf = &buf[tmp + subs[0].rm_so]; + walk = replace; + while (*walk) + if ('\\' == *walk + && '0' <= walk[1] && '9' >= walk[1] + && subs[walk[1] - '0'].rm_so > -1 + && subs[walk[1] - '0'].rm_eo > -1) { + tmp = subs[walk[1] - '0'].rm_eo + - subs[walk[1] - '0'].rm_so; + memcpy (walkbuf, + &string[pos + subs[walk[1] - '0'].rm_so], + tmp); + walkbuf += tmp; + walk += 2; + } else + *walkbuf++ = *walk++; + *walkbuf = '\0'; + + /* and get ready to keep looking for replacements */ + if (subs[0].rm_so == subs[0].rm_eo) { + if (subs[0].rm_so + pos >= string_len) + break; + new_l = strlen (buf) + 1; + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + pos += subs[0].rm_eo + 1; + buf [new_l-1] = string [pos-1]; + buf [new_l] = '\0'; + } else { + pos += subs[0].rm_eo; + } + } else { /* REG_NOMATCH */ + new_l = strlen(buf) + strlen(&string[pos]); + if (new_l + 1 > buf_len) { + buf_len = new_l + 1; /* now we know exactly how long it is */ + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + /* stick that last bit of string on our output */ + strcat(buf, &string[pos]); + } + } + + /* don't want to leak memory .. */ + regfree(&re); + + /* whew. */ + return (buf); +} + +static void _php3_eregreplace(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval *arg_pattern, + *arg_replace, + *arg_string; + char *pattern; + char *string; + char *replace; + char *ret; + + if (ARG_COUNT(ht) != 3 || getParameters(ht, 3, &arg_pattern, &arg_replace, &arg_string) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (arg_pattern->type == IS_STRING) { + if (arg_pattern->value.str.val && arg_pattern->value.str.len) + pattern = estrndup(arg_pattern->value.str.val,arg_pattern->value.str.len); + else + pattern = empty_string; + } else { + convert_to_long(arg_pattern); + pattern = emalloc(2); + pattern[0] = (char) arg_pattern->value.lval; + pattern[1] = '\0'; + } + + if (arg_replace->type == IS_STRING) { + if (arg_replace->value.str.val && arg_replace->value.str.len) + replace = estrndup(arg_replace->value.str.val, arg_replace->value.str.len); + else + replace = empty_string; + } else { + convert_to_long(arg_replace); + replace = emalloc(2); + replace[0] = (char) arg_replace->value.lval; + replace[1] = '\0'; + } + + convert_to_string(arg_string); + if (arg_string->value.str.val && arg_string->value.str.len) + string = estrndup(arg_string->value.str.val, arg_string->value.str.len); + else + string = empty_string; + + /* do the actual work */ + ret = _php3_regreplace(pattern, replace, string, icase, 1); + if (ret == (char *) -1) { + RETVAL_FALSE; + } else { + RETVAL_STRING(ret,1); + STR_FREE(ret); + } + STR_FREE(string); + STR_FREE(replace); + STR_FREE(pattern); +} + +/* {{{ proto string ereg_replace(string pattern, string string [, array registers]) + Replace regular expression */ +PHP_FUNCTION(eregreplace) +{ + _php3_eregreplace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto string eregi_replace(string pattern, string string [, array registers]) + Case insensitive replace regular expression */ +PHP_FUNCTION(eregireplace) +{ + _php3_eregreplace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* ("root", "passwd", "uid", "gid", "other:stuff:like:/bin/sh") + = split(":", $passwd_file, 5); */ +/* {{{ proto array split(string pattern, string string [, int limit]) + split string into array by regular expression */ +PHP_FUNCTION(split) +{ + pval *spliton, *str, *arg_count = NULL; + regex_t re; + regmatch_t subs[1]; + char *strp, *endp; + int err, size, count; + + switch (ARG_COUNT(ht)) { + case 2: + if (getParameters(ht, 2, &spliton, &str) == FAILURE) + WRONG_PARAM_COUNT; + count = -1; + break; + case 3: + if (getParameters(ht, 3, &spliton, &str, &arg_count) == FAILURE) + WRONG_PARAM_COUNT; + convert_to_long(arg_count); + count = arg_count->value.lval; + break; + default: + WRONG_PARAM_COUNT; + } + + convert_to_string(spliton); + convert_to_string(str); + + strp = str->value.str.val; + endp = str->value.str.val + strlen(str->value.str.val); + + err = regcomp(&re, spliton->value.str.val, REG_EXTENDED); + if (err) { + php3_error(E_WARNING, "unexpected regex error (%d)", err); + RETURN_FALSE; + } + + if (array_init(return_value) == FAILURE) { + regfree(&re); + RETURN_FALSE; + } + + /* churn through str, generating array entries as we go */ + while ((count == -1 || count > 0) && !(err = regexec(&re, strp, 1, subs, 0))) { + if (subs[0].rm_so == 0 && subs[0].rm_eo) { + /* match is at start of string, return empty string */ + add_next_index_stringl(return_value, empty_string, 0, 1); + /* skip ahead the length of the regex match */ + strp+=subs[0].rm_eo; + } else if (subs[0].rm_so==0 && subs[0].rm_eo==0) { + /* No more matches */ + regfree(&re); + php3_error(E_WARNING, "bad regular expression for split()"); + _php3_hash_destroy(return_value->value.ht); + efree(return_value->value.ht); + RETURN_FALSE; + } else { + /* On a real match */ + + /* make a copy of the substring */ + size = subs[0].rm_so; + + /* add it to the array */ + add_next_index_stringl(return_value, strp, size, 1); + + /* point at our new starting point */ + strp = strp + subs[0].rm_eo; + } + + /* if we're only looking for a certain number of points, + stop looking once we hit it */ + if (count != -1) { + count--; + } + } + + /* see if we encountered an error */ + if (err && err != REG_NOMATCH) { + php3_error(E_WARNING, "unexpected regex error (%d)", err); + regfree(&re); + _php3_hash_destroy(return_value->value.ht); + efree(return_value->value.ht); + RETURN_FALSE; + } + + /* otherwise we just have one last element to add to the array */ + if (count == -1 || err==REG_NOMATCH) { + size = endp - strp; + + add_next_index_stringl(return_value, strp, size, 1); + } + + regfree(&re); + + return; +} +/* }}} */ + +/* {{{ proto string sql_regcase(string string) + Make regular expression for case insensitive match */ +PHPAPI PHP_FUNCTION(sql_regcase) +{ + pval *string; + char *tmp; + unsigned char c; + register int i, j; + + if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &string)==FAILURE) { + WRONG_PARAM_COUNT; + } + + convert_to_string(string); + + tmp = (char *) emalloc(string->value.str.len*4+1); + + for (i=j=0; ivalue.str.len; i++) { + c = (unsigned char) string->value.str.val[i]; + if(isalpha(c)) { + tmp[j++] = '['; + tmp[j++] = toupper(c); + tmp[j++] = tolower(c); + tmp[j++] = ']'; + } else { + tmp[j++] = c; + } + } + tmp[j]=0; + + tmp = erealloc(tmp, j + 1); + + RETVAL_STRINGL(tmp, j, 0); +} +/* }}} */ + + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_bb5faa_c5152b_TSRM#tsrm_virtual_cwd.c b/src/main/resource/testFiles/php-src/prevFiles/prev_bb5faa_c5152b_TSRM#tsrm_virtual_cwd.c new file mode 100644 index 0000000..d533245 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_bb5faa_c5152b_TSRM#tsrm_virtual_cwd.c @@ -0,0 +1,1941 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Sascha Schumann | + | Pierre Joye | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tsrm_virtual_cwd.h" +#include "tsrm_strtok_r.h" + +#ifdef TSRM_WIN32 +#include +#include "tsrm_win32.h" +# ifndef IO_REPARSE_TAG_SYMLINK +# define IO_REPARSE_TAG_SYMLINK 0xA000000C +# endif + +# ifndef VOLUME_NAME_NT +# define VOLUME_NAME_NT 0x2 +# endif + +# ifndef VOLUME_NAME_DOS +# define VOLUME_NAME_DOS 0x0 +# endif +#endif + +#ifndef S_IFLNK +# define S_IFLNK 0120000 +#endif + +#ifdef NETWARE +#include +#endif + +#ifndef HAVE_REALPATH +#define realpath(x,y) strcpy(y,x) +#endif + +#define VIRTUAL_CWD_DEBUG 0 + +#include "TSRM.h" + +/* Only need mutex for popen() in Windows and NetWare because it doesn't chdir() on UNIX */ +#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS) +MUTEX_T cwd_mutex; +#endif + +#ifdef ZTS +ts_rsrc_id cwd_globals_id; +#else +virtual_cwd_globals cwd_globals; +#endif + +cwd_state main_cwd_state; /* True global */ + +#ifndef TSRM_WIN32 +#include +#else +#include +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) ((mode) & _S_IFDIR) +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) ((mode) & _S_IFREG) +#endif + +#ifdef TSRM_WIN32 +#include +#define tsrm_strtok_r(a,b,c) _tcstok((a),(b)) +#define TOKENIZER_STRING "/\\" + +static int php_check_dots(const char *element, int n) +{ + while (n-- > 0) if (element[n] != '.') break; + + return (n != -1); +} + +#define IS_DIRECTORY_UP(element, len) \ + (len >= 2 && !php_check_dots(element, len)) + +#define IS_DIRECTORY_CURRENT(element, len) \ + (len == 1 && element[0] == '.') + +#elif defined(NETWARE) +/* NetWare has strtok() (in LibC) and allows both slashes in paths, like Windows -- + but rest of the stuff is like Unix */ +/* strtok() call in LibC is abending when used in a different address space -- hence using + PHP's version itself for now */ +/*#define tsrm_strtok_r(a,b,c) strtok((a),(b))*/ +#define TOKENIZER_STRING "/\\" + +#else +#define TOKENIZER_STRING "/" +#endif + + +/* default macros */ + +#ifndef IS_DIRECTORY_UP +#define IS_DIRECTORY_UP(element, len) \ + (len == 2 && element[0] == '.' && element[1] == '.') +#endif + +#ifndef IS_DIRECTORY_CURRENT +#define IS_DIRECTORY_CURRENT(element, len) \ + (len == 1 && element[0] == '.') +#endif + +/* define this to check semantics */ +#define IS_DIR_OK(s) (1) + +#ifndef IS_DIR_OK +#define IS_DIR_OK(state) (php_is_dir_ok(state) == 0) +#endif + + +#define CWD_STATE_COPY(d, s) \ + (d)->cwd_length = (s)->cwd_length; \ + (d)->cwd = (char *) malloc((s)->cwd_length+1); \ + memcpy((d)->cwd, (s)->cwd, (s)->cwd_length+1); + +#define CWD_STATE_FREE(s) \ + free((s)->cwd); + +#ifdef TSRM_WIN32 + +#ifdef CTL_CODE +#undef CTL_CODE +#endif +#define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define METHOD_BUFFERED 0 +#define FILE_ANY_ACCESS 0 +#define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE ( 16 * 1024 ) + +typedef struct { + unsigned long ReparseTag; + unsigned short ReparseDataLength; + unsigned short Reserved; + union { + struct { + unsigned short SubstituteNameOffset; + unsigned short SubstituteNameLength; + unsigned short PrintNameOffset; + unsigned short PrintNameLength; + unsigned long Flags; + wchar_t ReparseTarget[1]; + } SymbolicLinkReparseBuffer; + struct { + unsigned short SubstituteNameOffset; + unsigned short SubstituteNameLength; + unsigned short PrintNameOffset; + unsigned short PrintNameLength; + wchar_t ReparseTarget[1]; + } MountPointReparseBuffer; + struct { + unsigned char ReparseTarget[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER; + +#define SECS_BETWEEN_EPOCHS (__int64)11644473600 +#define SECS_TO_100NS (__int64)10000000 +static inline time_t FileTimeToUnixTime(const FILETIME FileTime) +{ + __int64 UnixTime; + long *nsec = NULL; + SYSTEMTIME SystemTime; + FileTimeToSystemTime(&FileTime, &SystemTime); + + UnixTime = ((__int64)FileTime.dwHighDateTime << 32) + + FileTime.dwLowDateTime; + + UnixTime -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS); + + if (nsec) { + *nsec = (UnixTime % SECS_TO_100NS) * (__int64)100; + } + + UnixTime /= SECS_TO_100NS; /* now convert to seconds */ + + if ((time_t)UnixTime != UnixTime) { + UnixTime = 0; + } + return (time_t)UnixTime; +} + +CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len){ /* {{{ */ + HINSTANCE kernel32; + HANDLE hFile; + DWORD dwRet; + + typedef BOOL (WINAPI *gfpnh_func)(HANDLE, LPTSTR, DWORD, DWORD); + gfpnh_func pGetFinalPathNameByHandle; + + kernel32 = LoadLibrary("kernel32.dll"); + + if (kernel32) { + pGetFinalPathNameByHandle = (gfpnh_func)GetProcAddress(kernel32, "GetFinalPathNameByHandleA"); + if (pGetFinalPathNameByHandle == NULL) { + return -1; + } + } else { + return -1; + } + + hFile = CreateFile(link, // file to open + GENERIC_READ, // open for reading + FILE_SHARE_READ, // share for reading + NULL, // default security + OPEN_EXISTING, // existing file only + FILE_FLAG_BACKUP_SEMANTICS, // normal file + NULL); // no attr. template + + if( hFile == INVALID_HANDLE_VALUE) { + return -1; + } + + dwRet = pGetFinalPathNameByHandle(hFile, target, MAXPATHLEN, VOLUME_NAME_DOS); + if(dwRet >= MAXPATHLEN) { + return -1; + } + + CloseHandle(hFile); + + if(dwRet > 4) { + /* Skip first 4 characters if they are "\??\" */ + if(target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] == '\\') { + char tmp[MAXPATHLEN]; + unsigned int offset = 4; + dwRet -= 4; + + /* \??\UNC\ */ + if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') { + offset += 2; + dwRet -= 2; + target[offset] = '\\'; + } + + memcpy(tmp, target + offset, dwRet); + memcpy(target, tmp, dwRet); + } + } + + target[dwRet] = '\0'; + return dwRet; +} +/* }}} */ + +CWD_API int php_sys_stat_ex(const char *path, struct stat *buf, int lstat) /* {{{ */ +{ + WIN32_FILE_ATTRIBUTE_DATA data; + __int64 t; + const size_t path_len = strlen(path); + + if (!GetFileAttributesEx(path, GetFileExInfoStandard, &data)) { + return stat(path, buf); + } + + if (path_len >= 1 && path[1] == ':') { + if (path[0] >= 'A' && path[0] <= 'Z') { + buf->st_dev = buf->st_rdev = path[0] - 'A'; + } else { + buf->st_dev = buf->st_rdev = path[0] - 'a'; + } + } else if (IS_UNC_PATH(path, path_len)) { + buf->st_dev = buf->st_rdev = 0; + } else { + char cur_path[MAXPATHLEN+1]; + DWORD len = sizeof(cur_path); + char *tmp = cur_path; + + while(1) { + DWORD r = GetCurrentDirectory(len, tmp); + if (r < len) { + if (tmp[1] == ':') { + if (path[0] >= 'A' && path[0] <= 'Z') { + buf->st_dev = buf->st_rdev = path[0] - 'A'; + } else { + buf->st_dev = buf->st_rdev = path[0] - 'a'; + } + } else { + buf->st_dev = buf->st_rdev = -1; + } + break; + } else if (!r) { + buf->st_dev = buf->st_rdev = -1; + break; + } else { + len = r+1; + tmp = (char*)malloc(len); + } + } + if (tmp != cur_path) { + free(tmp); + } + } + + buf->st_uid = buf->st_gid = buf->st_ino = 0; + + if (lstat && data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + /* File is a reparse point. Get the target */ + HANDLE hLink = NULL; + REPARSE_DATA_BUFFER * pbuffer; + unsigned int retlength = 0; + TSRM_ALLOCA_FLAG(use_heap_large); + + hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); + if(hLink == INVALID_HANDLE_VALUE) { + return -1; + } + + pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large); + if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) { + tsrm_free_alloca(pbuffer, use_heap_large); + CloseHandle(hLink); + return -1; + } + + CloseHandle(hLink); + + if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + buf->st_mode = S_IFLNK; + buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6)); + } + +#if 0 /* Not used yet */ + else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { + buf->st_mode |=; + } +#endif + tsrm_free_alloca(pbuffer, use_heap_large); + } else { + buf->st_mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) : S_IFREG; + buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6)); + } + + if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + int len = strlen(path); + + if (path[len-4] == '.') { + if (_memicmp(path+len-3, "exe", 3) == 0 || + _memicmp(path+len-3, "com", 3) == 0 || + _memicmp(path+len-3, "bat", 3) == 0 || + _memicmp(path+len-3, "cmd", 3) == 0) { + buf->st_mode |= (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)); + } + } + } + + buf->st_nlink = 1; + t = data.nFileSizeHigh; + t = t << 32; + t |= data.nFileSizeLow; + buf->st_size = t; + buf->st_atime = FileTimeToUnixTime(data.ftLastAccessTime); + buf->st_ctime = FileTimeToUnixTime(data.ftCreationTime); + buf->st_mtime = FileTimeToUnixTime(data.ftLastWriteTime); + return 0; +} +/* }}} */ +#endif + +static int php_is_dir_ok(const cwd_state *state) /* {{{ */ +{ + struct stat buf; + + if (php_sys_stat(state->cwd, &buf) == 0 && S_ISDIR(buf.st_mode)) + return (0); + + return (1); +} +/* }}} */ + +static int php_is_file_ok(const cwd_state *state) /* {{{ */ +{ + struct stat buf; + + if (php_sys_stat(state->cwd, &buf) == 0 && S_ISREG(buf.st_mode)) + return (0); + + return (1); +} +/* }}} */ + +static void cwd_globals_ctor(virtual_cwd_globals *cwd_g TSRMLS_DC) /* {{{ */ +{ + CWD_STATE_COPY(&cwd_g->cwd, &main_cwd_state); + cwd_g->realpath_cache_size = 0; + cwd_g->realpath_cache_size_limit = REALPATH_CACHE_SIZE; + cwd_g->realpath_cache_ttl = REALPATH_CACHE_TTL; + memset(cwd_g->realpath_cache, 0, sizeof(cwd_g->realpath_cache)); +} +/* }}} */ + +static void cwd_globals_dtor(virtual_cwd_globals *cwd_g TSRMLS_DC) /* {{{ */ +{ + CWD_STATE_FREE(&cwd_g->cwd); + realpath_cache_clean(TSRMLS_C); +} +/* }}} */ + +CWD_API void virtual_cwd_startup(void) /* {{{ */ +{ + char cwd[MAXPATHLEN]; + char *result; + +#ifdef NETWARE + result = getcwdpath(cwd, NULL, 1); + if(result) + { + char *c=cwd; + while(c = strchr(c, '\\')) + { + *c='/'; + ++c; + } + } +#else + result = getcwd(cwd, sizeof(cwd)); +#endif + if (!result) { + cwd[0] = '\0'; + } + + main_cwd_state.cwd_length = strlen(cwd); +#ifdef TSRM_WIN32 + if (main_cwd_state.cwd_length >= 2 && cwd[1] == ':') { + cwd[0] = toupper(cwd[0]); + } +#endif + main_cwd_state.cwd = strdup(cwd); + +#ifdef ZTS + ts_allocate_id(&cwd_globals_id, sizeof(virtual_cwd_globals), (ts_allocate_ctor) cwd_globals_ctor, (ts_allocate_dtor) cwd_globals_dtor); +#else + cwd_globals_ctor(&cwd_globals TSRMLS_CC); +#endif + +#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS) + cwd_mutex = tsrm_mutex_alloc(); +#endif +} +/* }}} */ + +CWD_API void virtual_cwd_shutdown(void) /* {{{ */ +{ +#ifndef ZTS + cwd_globals_dtor(&cwd_globals TSRMLS_CC); +#endif +#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS) + tsrm_mutex_free(cwd_mutex); +#endif + + free(main_cwd_state.cwd); /* Don't use CWD_STATE_FREE because the non global states will probably use emalloc()/efree() */ +} +/* }}} */ + +CWD_API char *virtual_getcwd_ex(size_t *length TSRMLS_DC) /* {{{ */ +{ + cwd_state *state; + + state = &CWDG(cwd); + + if (state->cwd_length == 0) { + char *retval; + + *length = 1; + retval = (char *) malloc(2); + if (retval == NULL) { + return NULL; + } + retval[0] = DEFAULT_SLASH; + retval[1] = '\0'; + return retval; + } + +#ifdef TSRM_WIN32 + /* If we have something like C: */ + if (state->cwd_length == 2 && state->cwd[state->cwd_length-1] == ':') { + char *retval; + + *length = state->cwd_length+1; + retval = (char *) malloc(*length+1); + if (retval == NULL) { + return NULL; + } + memcpy(retval, state->cwd, *length); + retval[0] = toupper(retval[0]); + retval[*length-1] = DEFAULT_SLASH; + retval[*length] = '\0'; + return retval; + } +#endif + *length = state->cwd_length; + return strdup(state->cwd); +} +/* }}} */ + +/* Same semantics as UNIX getcwd() */ +CWD_API char *virtual_getcwd(char *buf, size_t size TSRMLS_DC) /* {{{ */ +{ + size_t length; + char *cwd; + + cwd = virtual_getcwd_ex(&length TSRMLS_CC); + + if (buf == NULL) { + return cwd; + } + if (length > size-1) { + free(cwd); + errno = ERANGE; /* Is this OK? */ + return NULL; + } + memcpy(buf, cwd, length+1); + free(cwd); + return buf; +} +/* }}} */ + +#ifdef PHP_WIN32 +static inline unsigned long realpath_cache_key(const char *path, int path_len TSRMLS_DC) /* {{{ */ +{ + register unsigned long h; + char *bucket_key_start = tsrm_win32_get_path_sid_key(path TSRMLS_CC); + char *bucket_key = (char *)bucket_key_start; + const char *e = bucket_key + strlen(bucket_key); + + if (!bucket_key) { + return 0; + } + + for (h = 2166136261U; bucket_key < e;) { + h *= 16777619; + h ^= *bucket_key++; + } + HeapFree(GetProcessHeap(), 0, (LPVOID)bucket_key_start); + return h; +} +/* }}} */ +#else +static inline unsigned long realpath_cache_key(const char *path, int path_len) /* {{{ */ +{ + register unsigned long h; + const char *e = path + path_len; + + for (h = 2166136261U; path < e;) { + h *= 16777619; + h ^= *path++; + } + + return h; +} +/* }}} */ +#endif /* defined(PHP_WIN32) */ + +CWD_API void realpath_cache_clean(TSRMLS_D) /* {{{ */ +{ + int i; + + for (i = 0; i < sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]); i++) { + realpath_cache_bucket *p = CWDG(realpath_cache)[i]; + while (p != NULL) { + realpath_cache_bucket *r = p; + p = p->next; + free(r); + } + CWDG(realpath_cache)[i] = NULL; + } + CWDG(realpath_cache_size) = 0; +} +/* }}} */ + +CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC) /* {{{ */ +{ +#ifdef PHP_WIN32 + unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC); +#else + unsigned long key = realpath_cache_key(path, path_len); +#endif + unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); + realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n]; + + while (*bucket != NULL) { + if (key == (*bucket)->key && path_len == (*bucket)->path_len && + memcmp(path, (*bucket)->path, path_len) == 0) { + realpath_cache_bucket *r = *bucket; + *bucket = (*bucket)->next; + CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1; + free(r); + return; + } else { + bucket = &(*bucket)->next; + } + } +} +/* }}} */ + +static inline void realpath_cache_add(const char *path, int path_len, const char *realpath, int realpath_len, int is_dir, time_t t TSRMLS_DC) /* {{{ */ +{ + long size = sizeof(realpath_cache_bucket) + path_len + 1; + int same = 1; + + if (realpath_len != path_len || + memcmp(path, realpath, path_len) != 0) { + size += realpath_len + 1; + same = 0; + } + + if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) { + realpath_cache_bucket *bucket = malloc(size); + unsigned long n; + + if (bucket == NULL) { + return; + } + +#ifdef PHP_WIN32 + bucket->key = realpath_cache_key(path, path_len TSRMLS_CC); +#else + bucket->key = realpath_cache_key(path, path_len); +#endif + bucket->path = (char*)bucket + sizeof(realpath_cache_bucket); + memcpy(bucket->path, path, path_len+1); + bucket->path_len = path_len; + if (same) { + bucket->realpath = bucket->path; + } else { + bucket->realpath = bucket->path + (path_len + 1); + memcpy(bucket->realpath, realpath, realpath_len+1); + } + bucket->realpath_len = realpath_len; + bucket->is_dir = is_dir; +#ifdef PHP_WIN32 + bucket->is_rvalid = 0; + bucket->is_readable = 0; + bucket->is_wvalid = 0; + bucket->is_writable = 0; +#endif + bucket->expires = t + CWDG(realpath_cache_ttl); + n = bucket->key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); + bucket->next = CWDG(realpath_cache)[n]; + CWDG(realpath_cache)[n] = bucket; + CWDG(realpath_cache_size) += size; + } +} +/* }}} */ + +static inline realpath_cache_bucket* realpath_cache_find(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */ +{ +#ifdef PHP_WIN32 + unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC); +#else + unsigned long key = realpath_cache_key(path, path_len); +#endif + + unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); + realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n]; + + while (*bucket != NULL) { + if (CWDG(realpath_cache_ttl) && (*bucket)->expires < t) { + realpath_cache_bucket *r = *bucket; + *bucket = (*bucket)->next; + CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1; + free(r); + } else if (key == (*bucket)->key && path_len == (*bucket)->path_len && + memcmp(path, (*bucket)->path, path_len) == 0) { + return *bucket; + } else { + bucket = &(*bucket)->next; + } + } + return NULL; +} +/* }}} */ + +CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */ +{ + return realpath_cache_find(path, path_len, t TSRMLS_CC); +} +/* }}} */ + +CWD_API int realpath_cache_size(TSRMLS_D) +{ + return CWDG(realpath_cache_size); +} + +CWD_API int realpath_cache_max_buckets(TSRMLS_D) +{ + return (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); +} + +CWD_API realpath_cache_bucket** realpath_cache_get_buckets(TSRMLS_D) +{ + return CWDG(realpath_cache); +} + + +#undef LINK_MAX +#define LINK_MAX 32 + +static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, int use_realpath, int is_dir, int *link_is_dir TSRMLS_DC) /* {{{ */ +{ + int i, j, save; + int directory = 0; +#ifdef TSRM_WIN32 + WIN32_FIND_DATA data; + HANDLE hFind; + TSRM_ALLOCA_FLAG(use_heap_large) +#else + struct stat st; +#endif + realpath_cache_bucket *bucket; + char *tmp; + TSRM_ALLOCA_FLAG(use_heap) + + while (1) { + if (len <= start) { + return start; + } + + i = len; + while (i > start && !IS_SLASH(path[i-1])) { + i--; + } + + if (i == len || + (i == len - 1 && path[i] == '.')) { + /* remove double slashes and '.' */ + len = i - 1; + is_dir = 1; + continue; + } else if (i == len - 2 && path[i] == '.' && path[i+1] == '.') { + /* remove '..' and previous directory */ + if (i - 1 <= start) { + return start ? start : len; + } + j = tsrm_realpath_r(path, start, i-1, ll, t, use_realpath, 1, NULL TSRMLS_CC); + if (j > start) { + j--; + while (j > start && !IS_SLASH(path[j])) { + j--; + } + if (!start) { + /* leading '..' must not be removed in case of relative path */ + if (j == 0 && path[0] == '.' && path[1] == '.' && + IS_SLASH(path[2])) { + path[3] = '.'; + path[4] = '.'; + path[5] = DEFAULT_SLASH; + j = 5; + } else if (j > 0 && + path[j+1] == '.' && path[j+2] == '.' && + IS_SLASH(path[j+3])) { + j += 4; + path[j++] = '.'; + path[j++] = '.'; + path[j] = DEFAULT_SLASH; + } + } + } else if (!start && !j) { + /* leading '..' must not be removed in case of relative path */ + path[0] = '.'; + path[1] = '.'; + path[2] = DEFAULT_SLASH; + j = 2; + } + return j; + } + + path[len] = 0; + + save = (use_realpath != CWD_EXPAND); + + if (start && save && CWDG(realpath_cache_size_limit)) { + /* cache lookup for absolute path */ + if (!*t) { + *t = time(0); + } + if ((bucket = realpath_cache_find(path, len, *t TSRMLS_CC)) != NULL) { + if (is_dir && !bucket->is_dir) { + /* not a directory */ + return -1; + } else { + if (link_is_dir) { + *link_is_dir = bucket->is_dir; + } + memcpy(path, bucket->realpath, bucket->realpath_len + 1); + return bucket->realpath_len; + } + } + } + +#ifdef TSRM_WIN32 + if (save && (hFind = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE) { + if (use_realpath == CWD_REALPATH) { + /* file not found */ + return -1; + } + /* continue resolution anyway but don't save result in the cache */ + save = 0; + } + + if (save) { + FindClose(hFind); + } + + tmp = tsrm_do_alloca(len+1, use_heap); + memcpy(tmp, path, len+1); + + if(save && + !(IS_UNC_PATH(path, len) && len >= 3 && path[2] != '?') && + (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + /* File is a reparse point. Get the target */ + HANDLE hLink = NULL; + REPARSE_DATA_BUFFER * pbuffer; + unsigned int retlength = 0; + int bufindex = 0, isabsolute = 0; + wchar_t * reparsetarget; + BOOL isVolume = FALSE; + char printname[MAX_PATH]; + char substitutename[MAX_PATH]; + int printname_len, substitutename_len; + int substitutename_off = 0; + + if(++(*ll) > LINK_MAX) { + return -1; + } + + hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); + if(hLink == INVALID_HANDLE_VALUE) { + return -1; + } + + pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large); + if (pbuffer == NULL) { + return -1; + } + if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) { + tsrm_free_alloca(pbuffer, use_heap_large); + CloseHandle(hLink); + return -1; + } + + CloseHandle(hLink); + + if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + reparsetarget = pbuffer->SymbolicLinkReparseBuffer.ReparseTarget; + printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR); + isabsolute = (pbuffer->SymbolicLinkReparseBuffer.Flags == 0) ? 1 : 0; + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR), + printname_len + 1, + printname, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR); + printname[printname_len] = 0; + + substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR); + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), + substitutename_len + 1, + substitutename, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + substitutename[substitutename_len] = 0; + } + else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { + isabsolute = 1; + reparsetarget = pbuffer->MountPointReparseBuffer.ReparseTarget; + printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR); + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR), + printname_len + 1, + printname, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + printname[pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)] = 0; + + substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR); + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), + substitutename_len + 1, + substitutename, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + substitutename[substitutename_len] = 0; + } else { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + } + + if(isabsolute && substitutename_len > 4) { + /* Do not resolve volumes (for now). A mounted point can + target a volume without a drive, it is not certain that + all IO functions we use in php and its deps support + path with volume GUID instead of the DOS way, like: + d:\test\mnt\foo + \\?\Volume{62d1c3f8-83b9-11de-b108-806e6f6e6963}\foo + */ + if (strncmp(substitutename, "\\??\\Volume{",11) == 0 + || strncmp(substitutename, "\\\\?\\Volume{",11) == 0 + || strncmp(substitutename, "\\??\\UNC\\", 8) == 0 + ) { + isVolume = TRUE; + substitutename_off = 0; + } else + /* do not use the \??\ and \\?\ prefix*/ + if (strncmp(substitutename, "\\??\\", 4) == 0 + || strncmp(substitutename, "\\\\?\\", 4) == 0) { + substitutename_off = 4; + } + } + + if (!isVolume) { + char * tmp2 = substitutename + substitutename_off; + for(bufindex = 0; bufindex < (substitutename_len - substitutename_off); bufindex++) { + *(path + bufindex) = *(tmp2 + bufindex); + } + + *(path + bufindex) = 0; + j = bufindex; + } else { + j = len; + } + + +#if VIRTUAL_CWD_DEBUG + fprintf(stderr, "reparse: print: %s ", printname); + fprintf(stderr, "sub: %s ", substitutename); + fprintf(stderr, "resolved: %s ", path); +#endif + tsrm_free_alloca(pbuffer, use_heap_large); + + if(isabsolute == 1) { + if (!((j == 3) && (path[1] == ':') && (path[2] == '\\'))) { + /* use_realpath is 0 in the call below coz path is absolute*/ + j = tsrm_realpath_r(path, 0, j, ll, t, 0, is_dir, &directory TSRMLS_CC); + if(j < 0) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + } + } + else { + if(i + j >= MAXPATHLEN - 1) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + + memmove(path+i, path, j+1); + memcpy(path, tmp, i-1); + path[i-1] = DEFAULT_SLASH; + j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC); + if(j < 0) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + } + directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + + if(link_is_dir) { + *link_is_dir = directory; + } + } + else { + if (save) { + directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + if (is_dir && !directory) { + /* not a directory */ + return -1; + } + } + +#elif defined(NETWARE) + save = 0; + tmp = tsrm_do_alloca(len+1, use_heap); + memcpy(tmp, path, len+1); +#else + if (save && php_sys_lstat(path, &st) < 0) { + if (use_realpath == CWD_REALPATH) { + /* file not found */ + return -1; + } + /* continue resolution anyway but don't save result in the cache */ + save = 0; + } + + tmp = tsrm_do_alloca(len+1, use_heap); + memcpy(tmp, path, len+1); + + if (save && S_ISLNK(st.st_mode)) { + if (++(*ll) > LINK_MAX || (j = php_sys_readlink(tmp, path, MAXPATHLEN)) < 0) { + /* too many links or broken symlinks */ + tsrm_free_alloca(tmp, use_heap); + return -1; + } + path[j] = 0; + if (IS_ABSOLUTE_PATH(path, j)) { + j = tsrm_realpath_r(path, 1, j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC); + if (j < 0) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + } else { + if (i + j >= MAXPATHLEN-1) { + tsrm_free_alloca(tmp, use_heap); + return -1; /* buffer overflow */ + } + memmove(path+i, path, j+1); + memcpy(path, tmp, i-1); + path[i-1] = DEFAULT_SLASH; + j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC); + if (j < 0) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + } + if (link_is_dir) { + *link_is_dir = directory; + } + } else { + if (save) { + directory = S_ISDIR(st.st_mode); + if (link_is_dir) { + *link_is_dir = directory; + } + if (is_dir && !directory) { + /* not a directory */ + return -1; + } + } +#endif + if (i - 1 <= start) { + j = start; + } else { + /* some leading directories may be unaccessable */ + j = tsrm_realpath_r(path, start, i-1, ll, t, save ? CWD_FILEPATH : use_realpath, 1, NULL TSRMLS_CC); + if (j > start) { + path[j++] = DEFAULT_SLASH; + } + } +#ifdef TSRM_WIN32 + if (j < 0 || j + len - i >= MAXPATHLEN-1) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + if (save) { + i = strlen(data.cFileName); + memcpy(path+j, data.cFileName, i+1); + j += i; + } else { + /* use the original file or directory name as it wasn't found */ + memcpy(path+j, tmp+i, len-i+1); + j += (len-i); + } + } +#else + if (j < 0 || j + len - i >= MAXPATHLEN-1) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + memcpy(path+j, tmp+i, len-i+1); + j += (len-i); + } +#endif + + if (save && start && CWDG(realpath_cache_size_limit)) { + /* save absolute path in the cache */ + realpath_cache_add(tmp, len, path, j, directory, *t TSRMLS_CC); + } + + tsrm_free_alloca(tmp, use_heap); + return j; + } +} +/* }}} */ + +/* Resolve path relatively to state and put the real path into state */ +/* returns 0 for ok, 1 for error */ +CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath TSRMLS_DC) /* {{{ */ +{ + int path_length = strlen(path); + char resolved_path[MAXPATHLEN]; + int start = 1; + int ll = 0; + time_t t; + int ret; + int add_slash; + void *tmp; + + if (path_length == 0 || path_length >= MAXPATHLEN-1) { +#ifdef TSRM_WIN32 +# if _MSC_VER < 1300 + errno = EINVAL; +# else + _set_errno(EINVAL); +# endif +#else + errno = EINVAL; +#endif + return 1; + } + +#if VIRTUAL_CWD_DEBUG + fprintf(stderr,"cwd = %s path = %s\n", state->cwd, path); +#endif + + /* cwd_length can be 0 when getcwd() fails. + * This can happen under solaris when a dir does not have read permissions + * but *does* have execute permissions */ + if (!IS_ABSOLUTE_PATH(path, path_length)) { + if (state->cwd_length == 0) { + /* resolve relative path */ + start = 0; + memcpy(resolved_path , path, path_length + 1); + } else { + int state_cwd_length = state->cwd_length; + +#ifdef TSRM_WIN32 + if (IS_SLASH(path[0])) { + if (state->cwd[1] == ':') { + /* Copy only the drive name */ + state_cwd_length = 2; + } else if (IS_UNC_PATH(state->cwd, state->cwd_length)) { + /* Copy only the share name */ + state_cwd_length = 2; + while (IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (state->cwd[state_cwd_length] && + !IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (state->cwd[state_cwd_length] && + !IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + } + } +#endif + if (path_length + state_cwd_length + 1 >= MAXPATHLEN-1) { + return 1; + } + memcpy(resolved_path, state->cwd, state_cwd_length); + resolved_path[state_cwd_length] = DEFAULT_SLASH; + memcpy(resolved_path + state_cwd_length + 1, path, path_length + 1); + path_length += state_cwd_length + 1; + } + } else { +#ifdef TSRM_WIN32 + if (path_length > 2 && path[1] == ':' && !IS_SLASH(path[2])) { + resolved_path[0] = path[0]; + resolved_path[1] = ':'; + resolved_path[2] = DEFAULT_SLASH; + memcpy(resolved_path + 3, path + 2, path_length - 1); + path_length++; + } else +#endif + memcpy(resolved_path, path, path_length + 1); + } + +#ifdef TSRM_WIN32 + if (memchr(resolved_path, '*', path_length) || + memchr(resolved_path, '?', path_length)) { + return 1; + } +#endif + +#ifdef TSRM_WIN32 + if (IS_UNC_PATH(resolved_path, path_length)) { + /* skip UNC name */ + resolved_path[0] = DEFAULT_SLASH; + resolved_path[1] = DEFAULT_SLASH; + start = 2; + while (!IS_SLASH(resolved_path[start])) { + if (resolved_path[start] == 0) { + goto verify; + } + resolved_path[start] = toupper(resolved_path[start]); + start++; + } + resolved_path[start++] = DEFAULT_SLASH; + while (!IS_SLASH(resolved_path[start])) { + if (resolved_path[start] == 0) { + goto verify; + } + resolved_path[start] = toupper(resolved_path[start]); + start++; + } + resolved_path[start++] = DEFAULT_SLASH; + } else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) { + /* skip DRIVE name */ + resolved_path[0] = toupper(resolved_path[0]); + resolved_path[2] = DEFAULT_SLASH; + start = 3; + } +#elif defined(NETWARE) + if (IS_ABSOLUTE_PATH(resolved_path, path_length)) { + /* skip VOLUME name */ + start = 0; + while (start != ':') { + if (resolved_path[start] == 0) return -1; + start++; + } + start++; + if (!IS_SLASH(resolved_path[start])) return -1; + resolved_path[start++] = DEFAULT_SLASH; + } +#endif + + add_slash = (use_realpath != CWD_REALPATH) && path_length > 0 && IS_SLASH(resolved_path[path_length-1]); + t = CWDG(realpath_cache_ttl) ? 0 : -1; + path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL TSRMLS_CC); + + if (path_length < 0) { + errno = ENOENT; + return 1; + } + + if (!start && !path_length) { + resolved_path[path_length++] = '.'; + } + if (add_slash && path_length && !IS_SLASH(resolved_path[path_length-1])) { + if (path_length >= MAXPATHLEN-1) { + return -1; + } + resolved_path[path_length++] = DEFAULT_SLASH; + } + resolved_path[path_length] = 0; + +#ifdef TSRM_WIN32 +verify: +#endif + if (verify_path) { + cwd_state old_state; + + CWD_STATE_COPY(&old_state, state); + state->cwd_length = path_length; + + tmp = realloc(state->cwd, state->cwd_length+1); + if (tmp == NULL) { +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "Out of memory\n"); +#endif + return 1; + } + state->cwd = (char *) tmp; + + memcpy(state->cwd, resolved_path, state->cwd_length+1); + if (verify_path(state)) { + CWD_STATE_FREE(state); + *state = old_state; + ret = 1; + } else { + CWD_STATE_FREE(&old_state); + ret = 0; + } + } else { + state->cwd_length = path_length; + tmp = realloc(state->cwd, state->cwd_length+1); + if (tmp == NULL) { +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "Out of memory\n"); +#endif + return 1; + } + state->cwd = (char *) tmp; + + memcpy(state->cwd, resolved_path, state->cwd_length+1); + ret = 0; + } + +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "virtual_file_ex() = %s\n",state->cwd); +#endif + return (ret); +} +/* }}} */ + +CWD_API int virtual_chdir(const char *path TSRMLS_DC) /* {{{ */ +{ + return virtual_file_ex(&CWDG(cwd), path, php_is_dir_ok, CWD_REALPATH TSRMLS_CC)?-1:0; +} +/* }}} */ + +CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path TSRMLS_DC) TSRMLS_DC) /* {{{ */ +{ + int length = strlen(path); + char *temp; + int retval; + TSRM_ALLOCA_FLAG(use_heap) + + if (length == 0) { + return 1; /* Can't cd to empty string */ + } + while(--length >= 0 && !IS_SLASH(path[length])) { + } + + if (length == -1) { + /* No directory only file name */ + errno = ENOENT; + return -1; + } + + if (length == COPY_WHEN_ABSOLUTE(path) && IS_ABSOLUTE_PATH(path, length+1)) { /* Also use trailing slash if this is absolute */ + length++; + } + temp = (char *) tsrm_do_alloca(length+1, use_heap); + memcpy(temp, path, length); + temp[length] = 0; +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "Changing directory to %s\n", temp); +#endif + retval = p_chdir(temp TSRMLS_CC); + tsrm_free_alloca(temp, use_heap); + return retval; +} +/* }}} */ + +CWD_API char *virtual_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + char *retval; + char cwd[MAXPATHLEN]; + + /* realpath("") returns CWD */ + if (!*path) { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + retval = NULL; + goto end; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + if (VCWD_GETCWD(cwd, MAXPATHLEN)) { + path = cwd; + } + } else if (!IS_ABSOLUTE_PATH(path, strlen(path))) { + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + } else { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + retval = NULL; + goto end; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + } + + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)==0) { + int len = new_state.cwd_length>MAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length; + + memcpy(real_path, new_state.cwd, len); + real_path[len] = '\0'; + retval = real_path; + } else { + retval = NULL; + } + + CWD_STATE_FREE(&new_state); +end: + return retval; +} +/* }}} */ + +CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + retval = virtual_file_ex(&new_state, path, verify_path, CWD_FILEPATH TSRMLS_CC); + + *filepath = new_state.cwd; + + return retval; + +} +/* }}} */ + +CWD_API int virtual_filepath(const char *path, char **filepath TSRMLS_DC) /* {{{ */ +{ + return virtual_filepath_ex(path, filepath, php_is_file_ok TSRMLS_CC); +} +/* }}} */ + +CWD_API FILE *virtual_fopen(const char *path, const char *mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + FILE *f; + + if (path[0] == '\0') { /* Fail to open empty path */ + return NULL; + } + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return NULL; + } + + f = fopen(new_state.cwd, mode); + + CWD_STATE_FREE(&new_state); + return f; +} +/* }}} */ + +CWD_API int virtual_access(const char *pathname, int mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + +#if defined(TSRM_WIN32) + ret = tsrm_win32_access(new_state.cwd, mode TSRMLS_CC); +#else + ret = access(new_state.cwd, mode); +#endif + + CWD_STATE_FREE(&new_state); + + return ret; +} +/* }}} */ + +#if HAVE_UTIME +#ifdef TSRM_WIN32 +static void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */ +{ + // Note that LONGLONG is a 64-bit value + LONGLONG ll; + + ll = Int32x32To64(t, 10000000) + 116444736000000000; + pft->dwLowDateTime = (DWORD)ll; + pft->dwHighDateTime = ll >> 32; +} +/* }}} */ + +TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */ +{ + FILETIME mtime, atime; + HANDLE hFile; + + hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, + OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL); + + /* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but + the CreateFile operation succeeds */ + if (GetLastError() == ERROR_ALREADY_EXISTS) { + SetLastError(0); + } + + if ( hFile == INVALID_HANDLE_VALUE ) { + return -1; + } + + if (!buf) { + SYSTEMTIME st; + GetSystemTime(&st); + SystemTimeToFileTime(&st, &mtime); + atime = mtime; + } else { + UnixTimeToFileTime(buf->modtime, &mtime); + UnixTimeToFileTime(buf->actime, &atime); + } + if (!SetFileTime(hFile, NULL, &atime, &mtime)) { + CloseHandle(hFile); + return -1; + } + CloseHandle(hFile); + return 1; +} +/* }}} */ +#endif + +CWD_API int virtual_utime(const char *filename, struct utimbuf *buf TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + +#ifdef TSRM_WIN32 + ret = win32_utime(new_state.cwd, buf); +#else + ret = utime(new_state.cwd, buf); +#endif + + CWD_STATE_FREE(&new_state); + return ret; +} +/* }}} */ +#endif + +CWD_API int virtual_chmod(const char *filename, mode_t mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + ret = chmod(new_state.cwd, mode); + + CWD_STATE_FREE(&new_state); + return ret; +} +/* }}} */ + +#if !defined(TSRM_WIN32) && !defined(NETWARE) +CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + if (link) { +#if HAVE_LCHOWN + ret = lchown(new_state.cwd, owner, group); +#else + ret = -1; +#endif + } else { + ret = chown(new_state.cwd, owner, group); + } + + CWD_STATE_FREE(&new_state); + return ret; +} +/* }}} */ +#endif + +CWD_API int virtual_open(const char *path TSRMLS_DC, int flags, ...) /* {{{ */ +{ + cwd_state new_state; + int f; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + if (flags & O_CREAT) { + mode_t mode; + va_list arg; + + va_start(arg, flags); + mode = (mode_t) va_arg(arg, int); + va_end(arg); + + f = open(new_state.cwd, flags, mode); + } else { + f = open(new_state.cwd, flags); + } + CWD_STATE_FREE(&new_state); + return f; +} +/* }}} */ + +CWD_API int virtual_creat(const char *path, mode_t mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int f; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + f = creat(new_state.cwd, mode); + + CWD_STATE_FREE(&new_state); + return f; +} +/* }}} */ + +CWD_API int virtual_rename(char *oldname, char *newname TSRMLS_DC) /* {{{ */ +{ + cwd_state old_state; + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&old_state, &CWDG(cwd)); + if (virtual_file_ex(&old_state, oldname, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&old_state); + return -1; + } + oldname = old_state.cwd; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, newname, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&old_state); + CWD_STATE_FREE(&new_state); + return -1; + } + newname = new_state.cwd; + + /* rename on windows will fail if newname already exists. + MoveFileEx has to be used */ +#ifdef TSRM_WIN32 + /* MoveFileEx returns 0 on failure, other way 'round for this function */ + retval = (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED) == 0) ? -1 : 0; +#else + retval = rename(oldname, newname); +#endif + + CWD_STATE_FREE(&old_state); + CWD_STATE_FREE(&new_state); + + return retval; +} +/* }}} */ + +CWD_API int virtual_stat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = php_sys_stat(new_state.cwd, buf); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_lstat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = php_sys_lstat(new_state.cwd, buf); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_unlink(const char *path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = unlink(new_state.cwd); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_mkdir(const char *pathname, mode_t mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + +#ifdef TSRM_WIN32 + retval = mkdir(new_state.cwd); +#else + retval = mkdir(new_state.cwd, mode); +#endif + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_rmdir(const char *pathname TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = rmdir(new_state.cwd); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +#ifdef TSRM_WIN32 +DIR *opendir(const char *name); +#endif + +CWD_API DIR *virtual_opendir(const char *pathname TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + DIR *retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return NULL; + } + + retval = opendir(new_state.cwd); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +#ifdef TSRM_WIN32 +CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */ +{ + return popen_ex(command, type, CWDG(cwd).cwd, NULL TSRMLS_CC); +} +/* }}} */ +#elif defined(NETWARE) +/* On NetWare, the trick of prepending "cd cwd; " doesn't work so we need to perform + a VCWD_CHDIR() and mutex it + */ +CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */ +{ + char prev_cwd[MAXPATHLEN]; + char *getcwd_result; + FILE *retval; + + getcwd_result = VCWD_GETCWD(prev_cwd, MAXPATHLEN); + if (!getcwd_result) { + return NULL; + } + +#ifdef ZTS + tsrm_mutex_lock(cwd_mutex); +#endif + + VCWD_CHDIR(CWDG(cwd).cwd); + retval = popen(command, type); + VCWD_CHDIR(prev_cwd); + +#ifdef ZTS + tsrm_mutex_unlock(cwd_mutex); +#endif + + return retval; +} +/* }}} */ +#else /* Unix */ +CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */ +{ + int command_length; + int dir_length, extra = 0; + char *command_line; + char *ptr, *dir; + FILE *retval; + + command_length = strlen(command); + + dir_length = CWDG(cwd).cwd_length; + dir = CWDG(cwd).cwd; + while (dir_length > 0) { + if (*dir == '\'') extra+=3; + dir++; + dir_length--; + } + dir_length = CWDG(cwd).cwd_length; + dir = CWDG(cwd).cwd; + + ptr = command_line = (char *) malloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1); + if (!command_line) { + return NULL; + } + memcpy(ptr, "cd ", sizeof("cd ")-1); + ptr += sizeof("cd ")-1; + + if (CWDG(cwd).cwd_length == 0) { + *ptr++ = DEFAULT_SLASH; + } else { + *ptr++ = '\''; + while (dir_length > 0) { + switch (*dir) { + case '\'': + *ptr++ = '\''; + *ptr++ = '\\'; + *ptr++ = '\''; + /* fall-through */ + default: + *ptr++ = *dir; + } + dir++; + dir_length--; + } + *ptr++ = '\''; + } + + *ptr++ = ' '; + *ptr++ = ';'; + *ptr++ = ' '; + + memcpy(ptr, command, command_length+1); + retval = popen(command_line, type); + + free(command_line); + return retval; +} +/* }}} */ +#endif + +CWD_API char *tsrm_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + char cwd[MAXPATHLEN]; + + /* realpath("") returns CWD */ + if (!*path) { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + return NULL; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + if (VCWD_GETCWD(cwd, MAXPATHLEN)) { + path = cwd; + } + } else if (!IS_ABSOLUTE_PATH(path, strlen(path)) && + VCWD_GETCWD(cwd, MAXPATHLEN)) { + new_state.cwd = strdup(cwd); + new_state.cwd_length = strlen(cwd); + } else { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + return NULL; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + } + + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)) { + free(new_state.cwd); + return NULL; + } + + if (real_path) { + int copy_len = new_state.cwd_length>MAXPATHLEN-1 ? MAXPATHLEN-1 : new_state.cwd_length; + memcpy(real_path, new_state.cwd, copy_len); + real_path[copy_len] = '\0'; + free(new_state.cwd); + return real_path; + } else { + return new_state.cwd; + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_c3c87e_c9c279_ext#zip#php_zip.c b/src/main/resource/testFiles/php-src/prevFiles/prev_c3c87e_c9c279_ext#zip#php_zip.c new file mode 100644 index 0000000..a547709 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_c3c87e_c9c279_ext#zip#php_zip.c @@ -0,0 +1,3144 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Piere-Alain Joye | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/standard/file.h" +#include "ext/standard/php_string.h" +#include "ext/pcre/php_pcre.h" +#include "ext/standard/php_filestat.h" +#include "php_zip.h" + +/* zip_open is a macro for renaming libzip zipopen, so we need to use PHP_NAMED_FUNCTION */ +static PHP_NAMED_FUNCTION(zif_zip_open); +static PHP_NAMED_FUNCTION(zif_zip_read); +static PHP_NAMED_FUNCTION(zif_zip_close); +static PHP_NAMED_FUNCTION(zif_zip_entry_read); +static PHP_NAMED_FUNCTION(zif_zip_entry_filesize); +static PHP_NAMED_FUNCTION(zif_zip_entry_name); +static PHP_NAMED_FUNCTION(zif_zip_entry_compressedsize); +static PHP_NAMED_FUNCTION(zif_zip_entry_compressionmethod); +static PHP_NAMED_FUNCTION(zif_zip_entry_open); +static PHP_NAMED_FUNCTION(zif_zip_entry_close); + +#ifdef HAVE_GLOB +#ifndef PHP_WIN32 +#include +#else +#include "win32/glob.h" +#endif +#endif + +/* {{{ Resource le */ +static int le_zip_dir; +#define le_zip_dir_name "Zip Directory" +static int le_zip_entry; +#define le_zip_entry_name "Zip Entry" +/* }}} */ + +/* {{{ PHP_ZIP_STAT_INDEX(za, index, flags, sb) */ +#define PHP_ZIP_STAT_INDEX(za, index, flags, sb) \ + if (zip_stat_index(za, index, flags, &sb) != 0) { \ + RETURN_FALSE; \ + } +/* }}} */ + +/* {{{ PHP_ZIP_STAT_PATH(za, path, path_len, flags, sb) */ +#define PHP_ZIP_STAT_PATH(za, path, path_len, flags, sb) \ + if (path_len < 1) { \ + php_error_docref(NULL, E_NOTICE, "Empty string as entry name"); \ + RETURN_FALSE; \ + } \ + if (zip_stat(za, path, flags, &sb) != 0) { \ + RETURN_FALSE; \ + } +/* }}} */ + +/* {{{ PHP_ZIP_SET_FILE_COMMENT(za, index, comment, comment_len) */ +#define PHP_ZIP_SET_FILE_COMMENT(za, index, comment, comment_len) \ + if (comment_len == 0) { \ + /* Passing NULL remove the existing comment */ \ + if (zip_set_file_comment(za, index, NULL, 0) < 0) { \ + RETURN_FALSE; \ + } \ + } else if (zip_set_file_comment(za, index, comment, comment_len) < 0) { \ + RETURN_FALSE; \ + } \ + RETURN_TRUE; +/* }}} */ + +# define add_ascii_assoc_string add_assoc_string +# define add_ascii_assoc_long add_assoc_long + +/* Flatten a path by making a relative path (to .)*/ +static char * php_zip_make_relative_path(char *path, size_t path_len) /* {{{ */ +{ + char *path_begin = path; + size_t i; + + if (path_len < 1 || path == NULL) { + return NULL; + } + + if (IS_SLASH(path[0])) { + return path + 1; + } + + i = path_len; + + while (1) { + while (i > 0 && !IS_SLASH(path[i])) { + i--; + } + + if (!i) { + return path; + } + + if (i >= 2 && (path[i -1] == '.' || path[i -1] == ':')) { + /* i is the position of . or :, add 1 for / */ + path_begin = path + i + 1; + break; + } + i--; + } + + return path_begin; +} +/* }}} */ + +# define CWD_STATE_ALLOC(l) emalloc(l) +# define CWD_STATE_FREE(s) efree(s) + +/* {{{ php_zip_extract_file */ +static int php_zip_extract_file(struct zip * za, char *dest, char *file, int file_len) +{ + php_stream_statbuf ssb; + struct zip_file *zf; + struct zip_stat sb; + char b[8192]; + int n, len, ret; + php_stream *stream; + char *fullpath; + char *file_dirname_fullpath; + char file_dirname[MAXPATHLEN]; + size_t dir_len; + int is_dir_only = 0; + char *path_cleaned; + size_t path_cleaned_len; + cwd_state new_state; + zend_string *file_basename; + + new_state.cwd = CWD_STATE_ALLOC(1); + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + + /* Clean/normlize the path and then transform any path (absolute or relative) + to a path relative to cwd (../../mydir/foo.txt > mydir/foo.txt) + */ + virtual_file_ex(&new_state, file, NULL, CWD_EXPAND); + path_cleaned = php_zip_make_relative_path(new_state.cwd, new_state.cwd_length); + if(!path_cleaned) { + return 0; + } + path_cleaned_len = strlen(path_cleaned); + + if (path_cleaned_len >= MAXPATHLEN || zip_stat(za, file, 0, &sb) != 0) { + return 0; + } + + /* it is a directory only, see #40228 */ + if (path_cleaned_len > 1 && IS_SLASH(path_cleaned[path_cleaned_len - 1])) { + len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file); + is_dir_only = 1; + } else { + memcpy(file_dirname, path_cleaned, path_cleaned_len); + dir_len = php_dirname(file_dirname, path_cleaned_len); + + if (dir_len <= 0 || (dir_len == 1 && file_dirname[0] == '.')) { + len = spprintf(&file_dirname_fullpath, 0, "%s", dest); + } else { + len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file_dirname); + } + + file_basename = php_basename(path_cleaned, path_cleaned_len, NULL, 0); + + if (ZIP_OPENBASEDIR_CHECKPATH(file_dirname_fullpath)) { + efree(file_dirname_fullpath); + zend_string_release(file_basename); + CWD_STATE_FREE(new_state.cwd); + return 0; + } + } + + /* let see if the path already exists */ + if (php_stream_stat_path_ex(file_dirname_fullpath, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL) < 0) { + ret = php_stream_mkdir(file_dirname_fullpath, 0777, PHP_STREAM_MKDIR_RECURSIVE|REPORT_ERRORS, NULL); + if (!ret) { + efree(file_dirname_fullpath); + if (!is_dir_only) { + zend_string_release(file_basename); + CWD_STATE_FREE(new_state.cwd); + } + return 0; + } + } + + /* it is a standalone directory, job done */ + if (is_dir_only) { + efree(file_dirname_fullpath); + CWD_STATE_FREE(new_state.cwd); + return 1; + } + + len = spprintf(&fullpath, 0, "%s/%s", file_dirname_fullpath, file_basename->val); + if (!len) { + efree(file_dirname_fullpath); + zend_string_release(file_basename); + CWD_STATE_FREE(new_state.cwd); + return 0; + } else if (len > MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "Full extraction path exceed MAXPATHLEN (%i)", MAXPATHLEN); + efree(file_dirname_fullpath); + zend_string_release(file_basename); + CWD_STATE_FREE(new_state.cwd); + return 0; + } + + /* check again the full path, not sure if it + * is required, does a file can have a different + * safemode status as its parent folder? + */ + if (ZIP_OPENBASEDIR_CHECKPATH(fullpath)) { + efree(fullpath); + efree(file_dirname_fullpath); + zend_string_release(file_basename); + CWD_STATE_FREE(new_state.cwd); + return 0; + } + + stream = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS, NULL); + + if (stream == NULL) { + n = -1; + goto done; + } + + zf = zip_fopen(za, file, 0); + if (zf == NULL) { + n = -1; + php_stream_close(stream); + goto done; + } + + n = 0; + + while ((n=zip_fread(zf, b, sizeof(b))) > 0) { + php_stream_write(stream, b, n); + } + + php_stream_close(stream); + n = zip_fclose(zf); + +done: + efree(fullpath); + zend_string_release(file_basename); + efree(file_dirname_fullpath); + CWD_STATE_FREE(new_state.cwd); + + if (n<0) { + return 0; + } else { + return 1; + } +} +/* }}} */ + +static int php_zip_add_file(struct zip *za, const char *filename, size_t filename_len, + char *entry_name, size_t entry_name_len, long offset_start, long offset_len) /* {{{ */ +{ + struct zip_source *zs; + char resolved_path[MAXPATHLEN]; + zval exists_flag; + + + if (ZIP_OPENBASEDIR_CHECKPATH(filename)) { + return -1; + } + + if (!expand_filepath(filename, resolved_path)) { + return -1; + } + + php_stat(resolved_path, strlen(resolved_path), FS_EXISTS, &exists_flag); + if (Z_TYPE(exists_flag) == IS_FALSE) { + return -1; + } + + zs = zip_source_file(za, resolved_path, offset_start, offset_len); + if (!zs) { + return -1; + } + if (zip_file_add(za, entry_name, zs, ZIP_FL_OVERWRITE) < 0) { + zip_source_free(zs); + return -1; + } else { + zip_error_clear(za); + return 1; + } +} +/* }}} */ + +static int php_zip_parse_options(zval *options, zend_long *remove_all_path, char **remove_path, size_t *remove_path_len, char **add_path, size_t *add_path_len) /* {{{ */ +{ + zval *option; + if ((option = zend_hash_str_find(HASH_OF(options), "remove_all_path", sizeof("remove_all_path") - 1)) != NULL) { + *remove_all_path = zval_get_long(option); + } + + /* If I add more options, it would make sense to create a nice static struct and loop over it. */ + if ((option = zend_hash_str_find(HASH_OF(options), "remove_path", sizeof("remove_path") - 1)) != NULL) { + if (Z_TYPE_P(option) != IS_STRING) { + php_error_docref(NULL, E_WARNING, "remove_path option expected to be a string"); + return -1; + } + + if (Z_STRLEN_P(option) < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string given as remove_path option"); + return -1; + } + + if (Z_STRLEN_P(option) >= MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "remove_path string is too long (max: %i, %i given)", + MAXPATHLEN - 1, Z_STRLEN_P(option)); + return -1; + } + *remove_path_len = Z_STRLEN_P(option); + *remove_path = Z_STRVAL_P(option); + } + + if ((option = zend_hash_str_find(HASH_OF(options), "add_path", sizeof("add_path") - 1)) != NULL) { + if (Z_TYPE_P(option) != IS_STRING) { + php_error_docref(NULL, E_WARNING, "add_path option expected to be a string"); + return -1; + } + + if (Z_STRLEN_P(option) < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string given as the add_path option"); + return -1; + } + + if (Z_STRLEN_P(option) >= MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "add_path string too long (max: %i, %i given)", + MAXPATHLEN - 1, Z_STRLEN_P(option)); + return -1; + } + *add_path_len = Z_STRLEN_P(option); + *add_path = Z_STRVAL_P(option); + } + return 1; +} +/* }}} */ + +/* {{{ REGISTER_ZIP_CLASS_CONST_LONG */ +#define REGISTER_ZIP_CLASS_CONST_LONG(const_name, value) \ + zend_declare_class_constant_long(zip_class_entry, const_name, sizeof(const_name)-1, (zend_long)value); +/* }}} */ + +/* {{{ ZIP_FROM_OBJECT */ +#define ZIP_FROM_OBJECT(intern, object) \ + { \ + ze_zip_object *obj = Z_ZIP_P(object); \ + intern = obj->za; \ + if (!intern) { \ + php_error_docref(NULL, E_WARNING, "Invalid or uninitialized Zip object"); \ + RETURN_FALSE; \ + } \ + } +/* }}} */ + +/* {{{ RETURN_SB(sb) */ +#define RETURN_SB(sb) \ + { \ + array_init(return_value); \ + add_ascii_assoc_string(return_value, "name", (char *)(sb)->name); \ + add_ascii_assoc_long(return_value, "index", (zend_long) (sb)->index); \ + add_ascii_assoc_long(return_value, "crc", (zend_long) (sb)->crc); \ + add_ascii_assoc_long(return_value, "size", (zend_long) (sb)->size); \ + add_ascii_assoc_long(return_value, "mtime", (zend_long) (sb)->mtime); \ + add_ascii_assoc_long(return_value, "comp_size", (zend_long) (sb)->comp_size); \ + add_ascii_assoc_long(return_value, "comp_method", (zend_long) (sb)->comp_method); \ + } +/* }}} */ + +static int php_zip_status(struct zip *za) /* {{{ */ +{ +#if LIBZIP_VERSION_MAJOR < 1 + int zep, syp; + + zip_error_get(za, &zep, &syp); +#else + int zep; + zip_error_t *err; + + err = zip_get_error(za); + zep = zip_error_code_zip(err); + zip_error_fini(err); +#endif + return zep; +} +/* }}} */ + +static int php_zip_status_sys(struct zip *za) /* {{{ */ +{ +#if LIBZIP_VERSION_MAJOR < 1 + int zep, syp; + + zip_error_get(za, &zep, &syp); +#else + int syp; + zip_error_t *err; + + err = zip_get_error(za); + syp = zip_error_code_system(err); + zip_error_fini(err); +#endif + return syp; +} +/* }}} */ + +static int php_zip_get_num_files(struct zip *za) /* {{{ */ +{ + return zip_get_num_files(za); +} +/* }}} */ + +static char * php_zipobj_get_filename(ze_zip_object *obj) /* {{{ */ +{ + + if (!obj) { + return NULL; + } + + if (obj->filename) { + return obj->filename; + } + return NULL; +} +/* }}} */ + +static char * php_zipobj_get_zip_comment(struct zip *za, int *len) /* {{{ */ +{ + if (za) { + return (char *)zip_get_archive_comment(za, len, 0); + } + return NULL; +} +/* }}} */ + +#ifdef HAVE_GLOB /* {{{ */ +#ifndef GLOB_ONLYDIR +#define GLOB_ONLYDIR (1<<30) +#define GLOB_EMULATE_ONLYDIR +#define GLOB_FLAGMASK (~GLOB_ONLYDIR) +#else +#define GLOB_FLAGMASK (~0) +#endif +#ifndef GLOB_BRACE +# define GLOB_BRACE 0 +#endif +#ifndef GLOB_MARK +# define GLOB_MARK 0 +#endif +#ifndef GLOB_NOSORT +# define GLOB_NOSORT 0 +#endif +#ifndef GLOB_NOCHECK +# define GLOB_NOCHECK 0 +#endif +#ifndef GLOB_NOESCAPE +# define GLOB_NOESCAPE 0 +#endif +#ifndef GLOB_ERR +# define GLOB_ERR 0 +#endif + +/* This is used for checking validity of passed flags (passing invalid flags causes segfault in glob()!! */ +#define GLOB_AVAILABLE_FLAGS (0 | GLOB_BRACE | GLOB_MARK | GLOB_NOSORT | GLOB_NOCHECK | GLOB_NOESCAPE | GLOB_ERR | GLOB_ONLYDIR) + +#endif /* }}} */ + +int php_zip_glob(char *pattern, int pattern_len, zend_long flags, zval *return_value) /* {{{ */ +{ +#ifdef HAVE_GLOB + char cwd[MAXPATHLEN]; + int cwd_skip = 0; +#ifdef ZTS + char work_pattern[MAXPATHLEN]; + char *result; +#endif + glob_t globbuf; + int n; + int ret; + + if (pattern_len >= MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN); + return -1; + } + + if ((GLOB_AVAILABLE_FLAGS & flags) != flags) { + php_error_docref(NULL, E_WARNING, "At least one of the passed flags is invalid or not supported on this platform"); + return -1; + } + +#ifdef ZTS + if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) { + result = VCWD_GETCWD(cwd, MAXPATHLEN); + if (!result) { + cwd[0] = '\0'; + } +#ifdef PHP_WIN32 + if (IS_SLASH(*pattern)) { + cwd[2] = '\0'; + } +#endif + cwd_skip = strlen(cwd)+1; + + snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern); + pattern = work_pattern; + } +#endif + + globbuf.gl_offs = 0; + if (0 != (ret = glob(pattern, flags & GLOB_FLAGMASK, NULL, &globbuf))) { +#ifdef GLOB_NOMATCH + if (GLOB_NOMATCH == ret) { + /* Some glob implementation simply return no data if no matches + were found, others return the GLOB_NOMATCH error code. + We don't want to treat GLOB_NOMATCH as an error condition + so that PHP glob() behaves the same on both types of + implementations and so that 'foreach (glob() as ...' + can be used for simple glob() calls without further error + checking. + */ + array_init(return_value); + return 0; + } +#endif + return 0; + } + + /* now catch the FreeBSD style of "no matches" */ + if (!globbuf.gl_pathc || !globbuf.gl_pathv) { + array_init(return_value); + return 0; + } + + /* we assume that any glob pattern will match files from one directory only + so checking the dirname of the first match should be sufficient */ + strncpy(cwd, globbuf.gl_pathv[0], MAXPATHLEN); + if (ZIP_OPENBASEDIR_CHECKPATH(cwd)) { + return -1; + } + + array_init(return_value); + for (n = 0; n < globbuf.gl_pathc; n++) { + /* we need to do this every time since GLOB_ONLYDIR does not guarantee that + * all directories will be filtered. GNU libc documentation states the + * following: + * If the information about the type of the file is easily available + * non-directories will be rejected but no extra work will be done to + * determine the information for each file. I.e., the caller must still be + * able to filter directories out. + */ + if (flags & GLOB_ONLYDIR) { + zend_stat_t s; + + if (0 != VCWD_STAT(globbuf.gl_pathv[n], &s)) { + continue; + } + + if (S_IFDIR != (s.st_mode & S_IFMT)) { + continue; + } + } + add_next_index_string(return_value, globbuf.gl_pathv[n]+cwd_skip); + } + + globfree(&globbuf); + return globbuf.gl_pathc; +#else + php_error_docref(NULL, E_ERROR, "Glob support is not available"); + return 0; +#endif /* HAVE_GLOB */ +} +/* }}} */ + +int php_zip_pcre(zend_string *regexp, char *path, int path_len, zval *return_value) /* {{{ */ +{ +#ifdef ZTS + char cwd[MAXPATHLEN]; + int cwd_skip = 0; + char work_path[MAXPATHLEN]; + char *result; +#endif + int files_cnt; + zend_string **namelist; + +#ifdef ZTS + if (!IS_ABSOLUTE_PATH(path, path_len)) { + result = VCWD_GETCWD(cwd, MAXPATHLEN); + if (!result) { + cwd[0] = '\0'; + } +#ifdef PHP_WIN32 + if (IS_SLASH(*path)) { + cwd[2] = '\0'; + } +#endif + cwd_skip = strlen(cwd)+1; + + snprintf(work_path, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, path); + path = work_path; + } +#endif + + if (ZIP_OPENBASEDIR_CHECKPATH(path)) { + return -1; + } + + files_cnt = php_stream_scandir(path, &namelist, NULL, (void *) php_stream_dirent_alphasort); + + if (files_cnt > 0) { + pcre *re = NULL; + pcre_extra *pcre_extra = NULL; + int preg_options = 0, i; + + re = pcre_get_compiled_regex(regexp, &pcre_extra, &preg_options); + if (!re) { + php_error_docref(NULL, E_WARNING, "Invalid expression"); + return -1; + } + + array_init(return_value); + + /* only the files, directories are ignored */ + for (i = 0; i < files_cnt; i++) { + zend_stat_t s; + char fullpath[MAXPATHLEN]; + int ovector[3]; + int matches; + int namelist_len = namelist[i]->len; + + if ((namelist_len == 1 && namelist[i]->val[0] == '.') || + (namelist_len == 2 && namelist[i]->val[0] == '.' && namelist[i]->val[1] == '.')) { + zend_string_release(namelist[i]); + continue; + } + + if ((path_len + namelist_len + 1) >= MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "add_path string too long (max: %i, %i given)", + MAXPATHLEN - 1, (path_len + namelist_len + 1)); + zend_string_release(namelist[i]); + break; + } + + snprintf(fullpath, MAXPATHLEN, "%s%c%s", path, DEFAULT_SLASH, namelist[i]->val); + + if (0 != VCWD_STAT(fullpath, &s)) { + php_error_docref(NULL, E_WARNING, "Cannot read <%s>", fullpath); + zend_string_release(namelist[i]); + continue; + } + + if (S_IFDIR == (s.st_mode & S_IFMT)) { + zend_string_release(namelist[i]); + continue; + } + + matches = pcre_exec(re, NULL, namelist[i]->val, namelist[i]->len, 0, 0, ovector, 3); + /* 0 means that the vector is too small to hold all the captured substring offsets */ + if (matches < 0) { + zend_string_release(namelist[i]); + continue; + } + + add_next_index_string(return_value, fullpath); + zend_string_release(namelist[i]); + } + efree(namelist); + } + return files_cnt; +} +/* }}} */ + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_open, 0, 0, 1) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_close, 0, 0, 1) + ZEND_ARG_INFO(0, zip) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_read, 0, 0, 1) + ZEND_ARG_INFO(0, zip) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_open, 0, 0, 2) + ZEND_ARG_INFO(0, zip_dp) + ZEND_ARG_INFO(0, zip_entry) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_close, 0, 0, 1) + ZEND_ARG_INFO(0, zip_ent) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_read, 0, 0, 1) + ZEND_ARG_INFO(0, zip_entry) + ZEND_ARG_INFO(0, len) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_name, 0, 0, 1) + ZEND_ARG_INFO(0, zip_entry) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_compressedsize, 0, 0, 1) + ZEND_ARG_INFO(0, zip_entry) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_filesize, 0, 0, 1) + ZEND_ARG_INFO(0, zip_entry) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_compressionmethod, 0, 0, 1) + ZEND_ARG_INFO(0, zip_entry) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ zend_function_entry */ +static const zend_function_entry zip_functions[] = { + ZEND_RAW_FENTRY("zip_open", zif_zip_open, arginfo_zip_open, 0) + ZEND_RAW_FENTRY("zip_close", zif_zip_close, arginfo_zip_close, 0) + ZEND_RAW_FENTRY("zip_read", zif_zip_read, arginfo_zip_read, 0) + PHP_FE(zip_entry_open, arginfo_zip_entry_open) + PHP_FE(zip_entry_close, arginfo_zip_entry_close) + PHP_FE(zip_entry_read, arginfo_zip_entry_read) + PHP_FE(zip_entry_filesize, arginfo_zip_entry_filesize) + PHP_FE(zip_entry_name, arginfo_zip_entry_name) + PHP_FE(zip_entry_compressedsize, arginfo_zip_entry_compressedsize) + PHP_FE(zip_entry_compressionmethod, arginfo_zip_entry_compressionmethod) +#ifdef PHP_FE_END + PHP_FE_END +#else + {NULL,NULL,NULL} +#endif +}; +/* }}} */ + +/* {{{ ZE2 OO definitions */ +static zend_class_entry *zip_class_entry; +static zend_object_handlers zip_object_handlers; + +static HashTable zip_prop_handlers; + +typedef int (*zip_read_int_t)(struct zip *za); +typedef char *(*zip_read_const_char_t)(struct zip *za, int *len); +typedef char *(*zip_read_const_char_from_ze_t)(ze_zip_object *obj); + +typedef struct _zip_prop_handler { + zip_read_int_t read_int_func; + zip_read_const_char_t read_const_char_func; + zip_read_const_char_from_ze_t read_const_char_from_obj_func; + + int type; +} zip_prop_handler; +/* }}} */ + +static void php_zip_register_prop_handler(HashTable *prop_handler, char *name, zip_read_int_t read_int_func, zip_read_const_char_t read_char_func, zip_read_const_char_from_ze_t read_char_from_obj_func, int rettype) /* {{{ */ +{ + zip_prop_handler hnd; + + hnd.read_const_char_func = read_char_func; + hnd.read_int_func = read_int_func; + hnd.read_const_char_from_obj_func = read_char_from_obj_func; + hnd.type = rettype; + zend_hash_str_add_mem(prop_handler, name, strlen(name), &hnd, sizeof(zip_prop_handler)); +} +/* }}} */ + +static zval *php_zip_property_reader(ze_zip_object *obj, zip_prop_handler *hnd, zval *rv) /* {{{ */ +{ + const char *retchar = NULL; + int retint = 0; + int len = 0; + + if (obj && obj->za != NULL) { + if (hnd->read_const_char_func) { + retchar = hnd->read_const_char_func(obj->za, &len); + } else { + if (hnd->read_int_func) { + retint = hnd->read_int_func(obj->za); + if (retint == -1) { + php_error_docref(NULL, E_WARNING, "Internal zip error returned"); + return NULL; + } + } else { + if (hnd->read_const_char_from_obj_func) { + retchar = hnd->read_const_char_from_obj_func(obj); + len = strlen(retchar); + } + } + } + } + + switch (hnd->type) { + case IS_STRING: + if (retchar) { + ZVAL_STRINGL(rv, (char *) retchar, len); + } else { + ZVAL_EMPTY_STRING(rv); + } + break; + /* case IS_TRUE */ + case IS_FALSE: + ZVAL_BOOL(rv, (long)retint); + break; + case IS_LONG: + ZVAL_LONG(rv, (long)retint); + break; + default: + ZVAL_NULL(rv); + } + + return rv; +} +/* }}} */ + +static zval *php_zip_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) /* {{{ */ +{ + ze_zip_object *obj; + zval tmp_member; + zval *retval = NULL; + zip_prop_handler *hnd = NULL; + zend_object_handlers *std_hnd; + + if (Z_TYPE_P(member) != IS_STRING) { + ZVAL_DUP(&tmp_member, member); + convert_to_string(&tmp_member); + member = &tmp_member; + cache_slot = NULL; + } + + obj = Z_ZIP_P(object); + + if (obj->prop_handler != NULL) { + hnd = zend_hash_find_ptr(obj->prop_handler, Z_STR_P(member)); + } + + if (hnd == NULL) { + std_hnd = zend_get_std_object_handlers(); + retval = std_hnd->get_property_ptr_ptr(object, member, type, cache_slot); + } + + if (member == &tmp_member) { + zval_dtor(member); + } + + return retval; +} +/* }}} */ + +static zval *php_zip_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */ +{ + ze_zip_object *obj; + zval tmp_member; + zval *retval = NULL; + zip_prop_handler *hnd = NULL; + zend_object_handlers *std_hnd; + + if (Z_TYPE_P(member) != IS_STRING) { + ZVAL_DUP(&tmp_member, member); + convert_to_string(&tmp_member); + member = &tmp_member; + cache_slot = NULL; + } + + obj = Z_ZIP_P(object); + + if (obj->prop_handler != NULL) { + hnd = zend_hash_find_ptr(obj->prop_handler, Z_STR_P(member)); + } + + if (hnd != NULL) { + retval = php_zip_property_reader(obj, hnd, rv); + if (retval == NULL) { + retval = &EG(uninitialized_zval); + } + } else { + std_hnd = zend_get_std_object_handlers(); + retval = std_hnd->read_property(object, member, type, cache_slot, rv); + } + + if (member == &tmp_member) { + zval_dtor(member); + } + + return retval; +} +/* }}} */ + +static int php_zip_has_property(zval *object, zval *member, int type, void **cache_slot) /* {{{ */ +{ + ze_zip_object *obj; + zval tmp_member; + zip_prop_handler *hnd = NULL; + zend_object_handlers *std_hnd; + int retval = 0; + + if (Z_TYPE_P(member) != IS_STRING) { + ZVAL_DUP(&tmp_member, member); + convert_to_string(&tmp_member); + member = &tmp_member; + cache_slot = NULL; + } + + obj = Z_ZIP_P(object); + + if (obj->prop_handler != NULL) { + hnd = zend_hash_find_ptr(obj->prop_handler, Z_STR_P(member)); + } + + if (hnd != NULL) { + zval tmp, *prop; + + if (type == 2) { + retval = 1; + } else if ((prop = php_zip_property_reader(obj, hnd, &tmp)) != NULL) { + if (type == 1) { + retval = zend_is_true(&tmp); + } else if (type == 0) { + retval = (Z_TYPE(tmp) != IS_NULL); + } + } + + zval_ptr_dtor(&tmp); + } else { + std_hnd = zend_get_std_object_handlers(); + retval = std_hnd->has_property(object, member, type, cache_slot); + } + + if (member == &tmp_member) { + zval_dtor(member); + } + + return retval; +} +/* }}} */ + +static HashTable *php_zip_get_properties(zval *object)/* {{{ */ +{ + ze_zip_object *obj; + HashTable *props; + zip_prop_handler *hnd; + zend_string *key; + + obj = Z_ZIP_P(object); + props = zend_std_get_properties(object); + + if (obj->prop_handler == NULL) { + return NULL; + } + + ZEND_HASH_FOREACH_STR_KEY_PTR(obj->prop_handler, key, hnd) { + zval *ret, val; + ret = php_zip_property_reader(obj, hnd, &val); + if (ret == NULL) { + ret = &EG(uninitialized_zval); + } + zend_hash_update(props, key, ret); + } ZEND_HASH_FOREACH_END(); + + return props; +} +/* }}} */ + +static void php_zip_object_free_storage(zend_object *object) /* {{{ */ +{ + ze_zip_object * intern = php_zip_fetch_object(object); + int i; + + if (!intern) { + return; + } + if (intern->za) { + if (zip_close(intern->za) != 0) { + php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context"); + return; + } + intern->za = NULL; + } + + if (intern->buffers_cnt>0) { + for (i=0; ibuffers_cnt; i++) { + efree(intern->buffers[i]); + } + efree(intern->buffers); + } + + intern->za = NULL; + zend_object_std_dtor(&intern->zo); + + if (intern->filename) { + efree(intern->filename); + } +} +/* }}} */ + +static zend_object *php_zip_object_new(zend_class_entry *class_type) /* {{{ */ +{ + ze_zip_object *intern; + + intern = ecalloc(1, sizeof(ze_zip_object) + zend_object_properties_size(class_type)); + intern->prop_handler = &zip_prop_handlers; + zend_object_std_init(&intern->zo, class_type); + object_properties_init(&intern->zo, class_type); + intern->zo.handlers = &zip_object_handlers; + + return &intern->zo; +} +/* }}} */ + +/* {{{ Resource dtors */ + +/* {{{ php_zip_free_dir */ +static void php_zip_free_dir(zend_resource *rsrc) +{ + zip_rsrc * zip_int = (zip_rsrc *) rsrc->ptr; + + if (zip_int) { + if (zip_int->za) { + if (zip_close(zip_int->za) != 0) { + php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context"); + } + zip_int->za = NULL; + } + + efree(rsrc->ptr); + + rsrc->ptr = NULL; + } +} +/* }}} */ + +/* {{{ php_zip_free_entry */ +static void php_zip_free_entry(zend_resource *rsrc) +{ + zip_read_rsrc *zr_rsrc = (zip_read_rsrc *) rsrc->ptr; + + if (zr_rsrc) { + if (zr_rsrc->zf) { + zip_fclose(zr_rsrc->zf); + zr_rsrc->zf = NULL; + } + efree(zr_rsrc); + rsrc->ptr = NULL; + } +} +/* }}} */ + +/* }}}*/ + +/* reset macro */ + +/* {{{ function prototypes */ +static PHP_MINIT_FUNCTION(zip); +static PHP_MSHUTDOWN_FUNCTION(zip); +static PHP_MINFO_FUNCTION(zip); +/* }}} */ + +/* {{{ zip_module_entry + */ +zend_module_entry zip_module_entry = { + STANDARD_MODULE_HEADER, + "zip", + zip_functions, + PHP_MINIT(zip), + PHP_MSHUTDOWN(zip), + NULL, + NULL, + PHP_MINFO(zip), + PHP_ZIP_VERSION, + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_ZIP +ZEND_GET_MODULE(zip) +#endif +/* set macro */ + +/* {{{ proto resource zip_open(string filename) +Create new zip using source uri for output */ +static PHP_NAMED_FUNCTION(zif_zip_open) +{ + char resolved_path[MAXPATHLEN + 1]; + zip_rsrc *rsrc_int; + int err = 0; + zend_string *filename; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &filename) == FAILURE) { + return; + } + + if (filename->len == 0) { + php_error_docref(NULL, E_WARNING, "Empty string as source"); + RETURN_FALSE; + } + + if (ZIP_OPENBASEDIR_CHECKPATH(filename->val)) { + RETURN_FALSE; + } + + if(!expand_filepath(filename->val, resolved_path)) { + RETURN_FALSE; + } + + rsrc_int = (zip_rsrc *)emalloc(sizeof(zip_rsrc)); + + rsrc_int->za = zip_open(resolved_path, 0, &err); + if (rsrc_int->za == NULL) { + efree(rsrc_int); + RETURN_LONG((zend_long)err); + } + + rsrc_int->index_current = 0; + rsrc_int->num_files = zip_get_num_files(rsrc_int->za); + + RETURN_RES(zend_register_resource(rsrc_int, le_zip_dir)); +} +/* }}} */ + +/* {{{ proto void zip_close(resource zip) + Close a Zip archive */ +static PHP_NAMED_FUNCTION(zif_zip_close) +{ + zval * zip; + zip_rsrc *z_rsrc = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip) == FAILURE) { + return; + } + + if ((z_rsrc = (zip_rsrc *)zend_fetch_resource(Z_RES_P(zip), le_zip_dir_name, le_zip_dir)) == NULL) { + RETURN_FALSE; + } + + /* really close the zip will break BC :-D */ + zend_list_close(Z_RES_P(zip)); +} +/* }}} */ + +/* {{{ proto resource zip_read(resource zip) + Returns the next file in the archive */ +static PHP_NAMED_FUNCTION(zif_zip_read) +{ + zval *zip_dp; + zip_read_rsrc *zr_rsrc; + int ret; + zip_rsrc *rsrc_int; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip_dp) == FAILURE) { + return; + } + + if ((rsrc_int = (zip_rsrc *)zend_fetch_resource(Z_RES_P(zip_dp), le_zip_dir_name, le_zip_dir)) == NULL) { + RETURN_FALSE; + } + + if (rsrc_int && rsrc_int->za) { + if (rsrc_int->index_current >= rsrc_int->num_files) { + RETURN_FALSE; + } + + zr_rsrc = emalloc(sizeof(zip_read_rsrc)); + + ret = zip_stat_index(rsrc_int->za, rsrc_int->index_current, 0, &zr_rsrc->sb); + + if (ret != 0) { + efree(zr_rsrc); + RETURN_FALSE; + } + + zr_rsrc->zf = zip_fopen_index(rsrc_int->za, rsrc_int->index_current, 0); + if (zr_rsrc->zf) { + rsrc_int->index_current++; + RETURN_RES(zend_register_resource(zr_rsrc, le_zip_entry)); + } else { + efree(zr_rsrc); + RETURN_FALSE; + } + + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool zip_entry_open(resource zip_dp, resource zip_entry [, string mode]) + Open a Zip File, pointed by the resource entry */ +/* Dummy function to follow the old API */ +static PHP_NAMED_FUNCTION(zif_zip_entry_open) +{ + zval * zip; + zval * zip_entry; + char *mode = NULL; + size_t mode_len = 0; + zip_read_rsrc * zr_rsrc; + zip_rsrc *z_rsrc; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|s", &zip, &zip_entry, &mode, &mode_len) == FAILURE) { + return; + } + + if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) { + RETURN_FALSE; + } + + if ((z_rsrc = (zip_rsrc *)zend_fetch_resource(Z_RES_P(zip), le_zip_dir_name, le_zip_dir)) == NULL) { + RETURN_FALSE; + } + + if (zr_rsrc->zf != NULL) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool zip_entry_close(resource zip_ent) + Close a zip entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_close) +{ + zval * zip_entry; + zip_read_rsrc * zr_rsrc; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip_entry) == FAILURE) { + return; + } + + if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) { + RETURN_FALSE; + } + + RETURN_BOOL(SUCCESS == zend_list_close(Z_RES_P(zip_entry))); +} +/* }}} */ + +/* {{{ proto mixed zip_entry_read(resource zip_entry [, int len]) + Read from an open directory entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_read) +{ + zval * zip_entry; + zend_long len = 0; + zip_read_rsrc * zr_rsrc; + zend_string *buffer; + int n = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &zip_entry, &len) == FAILURE) { + return; + } + + if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) { + RETURN_FALSE; + } + + if (len <= 0) { + len = 1024; + } + + if (zr_rsrc->zf) { + buffer = zend_string_alloc(len, 0); + n = zip_fread(zr_rsrc->zf, buffer->val, buffer->len); + if (n > 0) { + buffer->val[n] = '\0'; + buffer->len = n; + RETURN_NEW_STR(buffer); + } else { + zend_string_free(buffer); + RETURN_EMPTY_STRING() + } + } else { + RETURN_FALSE; + } +} +/* }}} */ + +static void php_zip_entry_get_info(INTERNAL_FUNCTION_PARAMETERS, int opt) /* {{{ */ +{ + zval * zip_entry; + zip_read_rsrc * zr_rsrc; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip_entry) == FAILURE) { + return; + } + + if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) { + RETURN_FALSE; + } + + if (!zr_rsrc->zf) { + RETURN_FALSE; + } + + switch (opt) { + case 0: + RETURN_STRING((char *)zr_rsrc->sb.name); + break; + case 1: + RETURN_LONG((zend_long) (zr_rsrc->sb.comp_size)); + break; + case 2: + RETURN_LONG((zend_long) (zr_rsrc->sb.size)); + break; + case 3: + switch (zr_rsrc->sb.comp_method) { + case 0: + RETURN_STRING("stored"); + break; + case 1: + RETURN_STRING("shrunk"); + break; + case 2: + case 3: + case 4: + case 5: + RETURN_STRING("reduced"); + break; + case 6: + RETURN_STRING("imploded"); + break; + case 7: + RETURN_STRING("tokenized"); + break; + case 8: + RETURN_STRING("deflated"); + break; + case 9: + RETURN_STRING("deflatedX"); + break; + case 10: + RETURN_STRING("implodedX"); + break; + default: + RETURN_FALSE; + } + RETURN_LONG((zend_long) (zr_rsrc->sb.comp_method)); + break; + } + +} +/* }}} */ + +/* {{{ proto string zip_entry_name(resource zip_entry) + Return the name given a ZZip entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_name) +{ + php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto int zip_entry_compressedsize(resource zip_entry) + Return the compressed size of a ZZip entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_compressedsize) +{ + php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto int zip_entry_filesize(resource zip_entry) + Return the actual filesize of a ZZip entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_filesize) +{ + php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2); +} +/* }}} */ + +/* {{{ proto string zip_entry_compressionmethod(resource zip_entry) + Return a string containing the compression method used on a particular entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_compressionmethod) +{ + php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3); +} +/* }}} */ + +/* {{{ proto mixed ZipArchive::open(string source [, int flags]) +Create new zip using source uri for output, return TRUE on success or the error code */ +static ZIPARCHIVE_METHOD(open) +{ + struct zip *intern; + int err = 0; + zend_long flags = 0; + char *resolved_path; + zend_string *filename; + zval *self = getThis(); + ze_zip_object *ze_obj = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &filename, &flags) == FAILURE) { + return; + } + + if (self) { + /* We do not use ZIP_FROM_OBJECT, zip init function here */ + ze_obj = Z_ZIP_P(self); + } + + if (filename->len == 0) { + php_error_docref(NULL, E_WARNING, "Empty string as source"); + RETURN_FALSE; + } + + if (ZIP_OPENBASEDIR_CHECKPATH(filename->val)) { + RETURN_FALSE; + } + + if (!(resolved_path = expand_filepath(filename->val, NULL))) { + RETURN_FALSE; + } + + if (ze_obj->za) { + /* we already have an opened zip, free it */ + if (zip_close(ze_obj->za) != 0) { + php_error_docref(NULL, E_WARNING, "Empty string as source"); + efree(resolved_path); + RETURN_FALSE; + } + ze_obj->za = NULL; + } + if (ze_obj->filename) { + efree(ze_obj->filename); + ze_obj->filename = NULL; + } + + intern = zip_open(resolved_path, flags, &err); + if (!intern || err) { + efree(resolved_path); + RETURN_LONG((zend_long)err); + } + ze_obj->filename = resolved_path; + ze_obj->filename_len = strlen(resolved_path); + ze_obj->za = intern; + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto resource ZipArchive::setPassword(string password) +Set the password for the active archive */ +static ZIPARCHIVE_METHOD(setPassword) +{ + struct zip *intern; + zval *self = getThis(); + char *password; + size_t password_len; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &password, &password_len) == FAILURE) { + return; + } + + if (password_len < 1) { + RETURN_FALSE; + } else { + int res = zip_set_default_password(intern, (const char *)password); + if (res == 0) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::close() +close the zip archive */ +static ZIPARCHIVE_METHOD(close) +{ + struct zip *intern; + zval *self = getThis(); + ze_zip_object *ze_obj; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + ze_obj = Z_ZIP_P(self); + + if (zip_close(intern)) { + zip_discard(intern); + } + + efree(ze_obj->filename); + ze_obj->filename = NULL; + ze_obj->filename_len = 0; + ze_obj->za = NULL; + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string ZipArchive::getStatusString() + * Returns the status error message, system and/or zip messages */ +static ZIPARCHIVE_METHOD(getStatusString) +{ + struct zip *intern; + zval *self = getThis(); +#if LIBZIP_VERSION_MAJOR < 1 + int zep, syp, len; + char error_string[128]; +#else + zip_error_t *err; +#endif + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + +#if LIBZIP_VERSION_MAJOR < 1 + zip_error_get(intern, &zep, &syp); + + len = zip_error_to_str(error_string, 128, zep, syp); + RETVAL_STRINGL(error_string, len); +#else + err = zip_get_error(intern); + RETVAL_STRING(zip_error_strerror(err)); + zip_error_fini(err); +#endif +} +/* }}} */ + +/* {{{ proto bool ZipArchive::createEmptyDir(string dirname) +Returns the index of the entry named filename in the archive */ +static ZIPARCHIVE_METHOD(addEmptyDir) +{ + struct zip *intern; + zval *self = getThis(); + char *dirname; + size_t dirname_len; + int idx; + struct zip_stat sb; + char *s; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &dirname, &dirname_len) == FAILURE) { + return; + } + + if (dirname_len<1) { + RETURN_FALSE; + } + + if (dirname[dirname_len-1] != '/') { + s=(char *)emalloc(dirname_len+2); + strcpy(s, dirname); + s[dirname_len] = '/'; + s[dirname_len+1] = '\0'; + } else { + s = dirname; + } + + idx = zip_stat(intern, s, 0, &sb); + if (idx >= 0) { + RETVAL_FALSE; + } else { + if (zip_add_dir(intern, (const char *)s) == -1) { + RETVAL_FALSE; + } + zip_error_clear(intern); + RETVAL_TRUE; + } + + if (s != dirname) { + efree(s); + } +} +/* }}} */ + +static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ +{ + struct zip *intern; + zval *self = getThis(); + char *path = NULL; + char *remove_path = NULL; + char *add_path = NULL; + size_t add_path_len, remove_path_len = 0, path_len = 0; + zend_long remove_all_path = 0; + zend_long flags = 0; + zval *options = NULL; + int found; + zend_string *pattern; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + /* 1 == glob, 2 == pcre */ + if (type == 1) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|la", + &pattern, &flags, &options) == FAILURE) { + return; + } + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|sa", + &pattern, &path, &path_len, &options) == FAILURE) { + return; + } + } + + if (pattern->len == 0) { + php_error_docref(NULL, E_NOTICE, "Empty string as pattern"); + RETURN_FALSE; + } + if (options && (php_zip_parse_options(options, &remove_all_path, &remove_path, &remove_path_len, + &add_path, &add_path_len) < 0)) { + RETURN_FALSE; + } + + if (remove_path && remove_path_len > 1) { + size_t real_len = strlen(remove_path); + if ((real_len > 1) && ((remove_path[real_len - 1] == '/') || (remove_path[real_len - 1] == '\\'))) { + remove_path[real_len - 1] = '\0'; + } + } + + if (type == 1) { + found = php_zip_glob(pattern->val, pattern->len, flags, return_value); + } else { + found = php_zip_pcre(pattern, path, path_len, return_value); + } + + if (found > 0) { + int i; + zval *zval_file; + + for (i = 0; i < found; i++) { + char *file_stripped, *entry_name; + size_t entry_name_len, file_stripped_len; + char entry_name_buf[MAXPATHLEN]; + zend_string *basename = NULL; + + if ((zval_file = zend_hash_index_find(Z_ARRVAL_P(return_value), i)) != NULL) { + if (remove_all_path) { + basename = php_basename(Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), NULL, 0); + file_stripped = basename->val; + file_stripped_len = basename->len; + } else if (remove_path && strstr(Z_STRVAL_P(zval_file), remove_path) != NULL) { + file_stripped = Z_STRVAL_P(zval_file) + remove_path_len + 1; + file_stripped_len = Z_STRLEN_P(zval_file) - remove_path_len - 1; + } else { + file_stripped = Z_STRVAL_P(zval_file); + file_stripped_len = Z_STRLEN_P(zval_file); + } + + if (add_path) { + if ((add_path_len + file_stripped_len) > MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "Entry name too long (max: %d, %pd given)", + MAXPATHLEN - 1, (add_path_len + file_stripped_len)); + zval_ptr_dtor(return_value); + RETURN_FALSE; + } + + snprintf(entry_name_buf, MAXPATHLEN, "%s%s", add_path, file_stripped); + entry_name = entry_name_buf; + entry_name_len = strlen(entry_name); + } else { + entry_name = Z_STRVAL_P(zval_file); + entry_name_len = Z_STRLEN_P(zval_file); + } + if (basename) { + zend_string_release(basename); + basename = NULL; + } + if (php_zip_add_file(intern, Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), + entry_name, entry_name_len, 0, 0) < 0) { + zval_dtor(return_value); + RETURN_FALSE; + } + } + } + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::addGlob(string pattern[,int flags [, array options]]) +Add files matching the glob pattern. See php's glob for the pattern syntax. */ +static ZIPARCHIVE_METHOD(addGlob) +{ + php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto bool ZipArchive::addPattern(string pattern[, string path [, array options]]) +Add files matching the pcre pattern. See php's pcre for the pattern syntax. */ +static ZIPARCHIVE_METHOD(addPattern) +{ + php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2); +} +/* }}} */ + +/* {{{ proto bool ZipArchive::addFile(string filepath[, string entryname[, int start [, int length]]]) +Add a file in a Zip archive using its path and the name to use. */ +static ZIPARCHIVE_METHOD(addFile) +{ + struct zip *intern; + zval *self = getThis(); + char *entry_name = NULL; + size_t entry_name_len = 0; + zend_long offset_start = 0, offset_len = 0; + zend_string *filename; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|sll", + &filename, &entry_name, &entry_name_len, &offset_start, &offset_len) == FAILURE) { + return; + } + + if (filename->len == 0) { + php_error_docref(NULL, E_NOTICE, "Empty string as filename"); + RETURN_FALSE; + } + + if (entry_name_len == 0) { + entry_name = filename->val; + entry_name_len = filename->len; + } + + if (php_zip_add_file(intern, filename->val, filename->len, entry_name, entry_name_len, 0, 0) < 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::addFromString(string name, string content) +Add a file using content and the entry name */ +static ZIPARCHIVE_METHOD(addFromString) +{ + struct zip *intern; + zval *self = getThis(); + zend_string *buffer; + char *name; + size_t name_len; + ze_zip_object *ze_obj; + struct zip_source *zs; + int pos = 0; + int cur_idx; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS", + &name, &name_len, &buffer) == FAILURE) { + return; + } + + ze_obj = Z_ZIP_P(self); + if (ze_obj->buffers_cnt) { + ze_obj->buffers = (char **)erealloc(ze_obj->buffers, sizeof(char *) * (ze_obj->buffers_cnt+1)); + pos = ze_obj->buffers_cnt++; + } else { + ze_obj->buffers = (char **)emalloc(sizeof(char *)); + ze_obj->buffers_cnt++; + pos = 0; + } + ze_obj->buffers[pos] = (char *)emalloc(buffer->len + 1); + memcpy(ze_obj->buffers[pos], buffer->val, buffer->len + 1); + + zs = zip_source_buffer(intern, ze_obj->buffers[pos], buffer->len, 0); + + if (zs == NULL) { + RETURN_FALSE; + } + + cur_idx = zip_name_locate(intern, (const char *)name, 0); + /* TODO: fix _zip_replace */ + if (cur_idx >= 0) { + if (zip_delete(intern, cur_idx) == -1) { + zip_source_free(zs); + RETURN_FALSE; + } + } + + if (zip_add(intern, name, zs) == -1) { + zip_source_free(zs); + RETURN_FALSE; + } else { + zip_error_clear(intern); + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto array ZipArchive::statName(string filename[, int flags]) +Returns the information about a the zip entry filename */ +static ZIPARCHIVE_METHOD(statName) +{ + struct zip *intern; + zval *self = getThis(); + zend_long flags = 0; + struct zip_stat sb; + zend_string *name; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &name, &flags) == FAILURE) { + return; + } + + PHP_ZIP_STAT_PATH(intern, name->val, name->len, flags, sb); + + RETURN_SB(&sb); +} +/* }}} */ + +/* {{{ proto resource ZipArchive::statIndex(int index[, int flags]) +Returns the zip entry informations using its index */ +static ZIPARCHIVE_METHOD(statIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index, flags = 0; + + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", + &index, &flags) == FAILURE) { + return; + } + + if (zip_stat_index(intern, index, flags, &sb) != 0) { + RETURN_FALSE; + } + RETURN_SB(&sb); +} +/* }}} */ + +/* {{{ proto int ZipArchive::locateName(string filename[, int flags]) +Returns the index of the entry named filename in the archive */ +static ZIPARCHIVE_METHOD(locateName) +{ + struct zip *intern; + zval *self = getThis(); + zend_long flags = 0; + zend_long idx = -1; + zend_string *name; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &name, &flags) == FAILURE) { + return; + } + + if (name->len < 1) { + RETURN_FALSE; + } + + idx = (zend_long)zip_name_locate(intern, (const char *)name->val, flags); + + if (idx >= 0) { + RETURN_LONG(idx); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string ZipArchive::getNameIndex(int index [, int flags]) +Returns the name of the file at position index */ +static ZIPARCHIVE_METHOD(getNameIndex) +{ + struct zip *intern; + zval *self = getThis(); + const char *name; + zend_long flags = 0, index = 0; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", + &index, &flags) == FAILURE) { + return; + } + + name = zip_get_name(intern, (int) index, flags); + + if (name) { + RETVAL_STRING((char *)name); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setArchiveComment(string comment) +Set or remove (NULL/'') the comment of the archive */ +static ZIPARCHIVE_METHOD(setArchiveComment) +{ + struct zip *intern; + zval *self = getThis(); + size_t comment_len; + char * comment; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &comment, &comment_len) == FAILURE) { + return; + } + if (zip_set_archive_comment(intern, (const char *)comment, (int)comment_len)) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto string ZipArchive::getArchiveComment([int flags]) +Returns the comment of an entry using its index */ +static ZIPARCHIVE_METHOD(getArchiveComment) +{ + struct zip *intern; + zval *self = getThis(); + zend_long flags = 0; + const char * comment; + int comment_len = 0; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) { + return; + } + + comment = zip_get_archive_comment(intern, &comment_len, (int)flags); + if(comment==NULL) { + RETURN_FALSE; + } + RETURN_STRINGL((char *)comment, (zend_long)comment_len); +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setCommentName(string name, string comment) +Set or remove (NULL/'') the comment of an entry using its Name */ +static ZIPARCHIVE_METHOD(setCommentName) +{ + struct zip *intern; + zval *self = getThis(); + size_t comment_len, name_len; + char * comment, *name; + int idx; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", + &name, &name_len, &comment, &comment_len) == FAILURE) { + return; + } + + if (name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as entry name"); + } + + idx = zip_name_locate(intern, name, 0); + if (idx < 0) { + RETURN_FALSE; + } + PHP_ZIP_SET_FILE_COMMENT(intern, idx, comment, comment_len); +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setCommentIndex(int index, string comment) +Set or remove (NULL/'') the comment of an entry using its index */ +static ZIPARCHIVE_METHOD(setCommentIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index; + size_t comment_len; + char * comment; + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", + &index, &comment, &comment_len) == FAILURE) { + return; + } + + PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + PHP_ZIP_SET_FILE_COMMENT(intern, index, comment, comment_len); +} +/* }}} */ + +/* those constants/functions are only available in libzip since 0.11.2 */ +#ifdef ZIP_OPSYS_DEFAULT + +/* {{{ proto bool ZipArchive::setExternalAttributesName(string name, int opsys, int attr [, int flags]) +Set external attributes for file in zip, using its name */ +static ZIPARCHIVE_METHOD(setExternalAttributesName) +{ + struct zip *intern; + zval *self = getThis(); + size_t name_len; + char *name; + zend_long flags=0, opsys, attr; + zip_int64_t idx; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|l", + &name, &name_len, &opsys, &attr, &flags) == FAILURE) { + return; + } + + if (name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as entry name"); + } + + idx = zip_name_locate(intern, name, 0); + if (idx < 0) { + RETURN_FALSE; + } + if (zip_file_set_external_attributes(intern, idx, (zip_flags_t)flags, + (zip_uint8_t)(opsys&0xff), (zip_uint32_t)attr) < 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setExternalAttributesIndex(int index, int opsys, int attr [, int flags]) +Set external attributes for file in zip, using its index */ +static ZIPARCHIVE_METHOD(setExternalAttributesIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index, flags=0, opsys, attr; + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll|l", + &index, &opsys, &attr, &flags) == FAILURE) { + return; + } + + PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + if (zip_file_set_external_attributes(intern, (zip_uint64_t)index, + (zip_flags_t)flags, (zip_uint8_t)(opsys&0xff), (zip_uint32_t)attr) < 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::getExternalAttributesName(string name, int &opsys, int &attr [, int flags]) +Get external attributes for file in zip, using its name */ +static ZIPARCHIVE_METHOD(getExternalAttributesName) +{ + struct zip *intern; + zval *self = getThis(), *z_opsys, *z_attr; + size_t name_len; + char *name; + zend_long flags=0; + zip_uint8_t opsys; + zip_uint32_t attr; + zip_int64_t idx; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/z/|l", + &name, &name_len, &z_opsys, &z_attr, &flags) == FAILURE) { + return; + } + + if (name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as entry name"); + } + + idx = zip_name_locate(intern, name, 0); + if (idx < 0) { + RETURN_FALSE; + } + if (zip_file_get_external_attributes(intern, idx, + (zip_flags_t)flags, &opsys, &attr) < 0) { + RETURN_FALSE; + } + zval_ptr_dtor(z_opsys); + ZVAL_LONG(z_opsys, opsys); + zval_ptr_dtor(z_attr); + ZVAL_LONG(z_attr, attr); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::getExternalAttributesIndex(int index, int &opsys, int &attr [, int flags]) +Get external attributes for file in zip, using its index */ +static ZIPARCHIVE_METHOD(getExternalAttributesIndex) +{ + struct zip *intern; + zval *self = getThis(), *z_opsys, *z_attr; + zend_long index, flags=0; + zip_uint8_t opsys; + zip_uint32_t attr; + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz/z/|l", + &index, &z_opsys, &z_attr, &flags) == FAILURE) { + return; + } + + PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + if (zip_file_get_external_attributes(intern, (zip_uint64_t)index, + (zip_flags_t)flags, &opsys, &attr) < 0) { + RETURN_FALSE; + } + zval_dtor(z_opsys); + ZVAL_LONG(z_opsys, opsys); + zval_dtor(z_attr); + ZVAL_LONG(z_attr, attr); + RETURN_TRUE; +} +/* }}} */ +#endif /* ifdef ZIP_OPSYS_DEFAULT */ + +/* {{{ proto string ZipArchive::getCommentName(string name[, int flags]) +Returns the comment of an entry using its name */ +static ZIPARCHIVE_METHOD(getCommentName) +{ + struct zip *intern; + zval *self = getThis(); + size_t name_len; + int idx; + zend_long flags = 0; + int comment_len = 0; + const char * comment; + char *name; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", + &name, &name_len, &flags) == FAILURE) { + return; + } + if (name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as entry name"); + RETURN_FALSE; + } + + idx = zip_name_locate(intern, name, 0); + if (idx < 0) { + RETURN_FALSE; + } + + comment = zip_get_file_comment(intern, idx, &comment_len, (int)flags); + RETURN_STRINGL((char *)comment, (zend_long)comment_len); +} +/* }}} */ + +/* {{{ proto string ZipArchive::getCommentIndex(int index[, int flags]) +Returns the comment of an entry using its index */ +static ZIPARCHIVE_METHOD(getCommentIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index, flags = 0; + const char * comment; + int comment_len = 0; + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", + &index, &flags) == FAILURE) { + return; + } + + PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + comment = zip_get_file_comment(intern, index, &comment_len, (int)flags); + RETURN_STRINGL((char *)comment, (zend_long)comment_len); +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setCompressionName(string name, int comp_method[, int comp_flags]) +Set the compression of a file in zip, using its name */ +static ZIPARCHIVE_METHOD(setCompressionName) + { + struct zip *intern; + zval *this = getThis(); + size_t name_len; + char *name; + zip_int64_t idx; + zend_long comp_method, comp_flags = 0; + + if (!this) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", + &name, &name_len, &comp_method, &comp_flags) == FAILURE) { + return; + } + + if (name_len < 1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Empty string as entry name"); + } + + idx = zip_name_locate(intern, name, 0); + if (idx < 0) { + RETURN_FALSE; + } + + if (zip_set_file_compression(intern, (zip_uint64_t)idx, + (zip_int32_t)comp_method, (zip_uint32_t)comp_flags) != 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setCompressionIndex(int index, int comp_method[, int comp_flags]) +Set the compression of a file in zip, using its index */ +static ZIPARCHIVE_METHOD(setCompressionIndex) +{ + struct zip *intern; + zval *this = getThis(); + zend_long index; + zend_long comp_method, comp_flags = 0; + + if (!this) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|l", + &index, &comp_method, &comp_flags) == FAILURE) { + return; + } + + if (zip_set_file_compression(intern, (zip_uint64_t)index, + (zip_int32_t)comp_method, (zip_uint32_t)comp_flags) != 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::deleteIndex(int index) +Delete a file using its index */ +static ZIPARCHIVE_METHOD(deleteIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { + return; + } + + if (index < 0) { + RETURN_FALSE; + } + + if (zip_delete(intern, index) < 0) { + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::deleteName(string name) +Delete a file using its index */ +static ZIPARCHIVE_METHOD(deleteName) +{ + struct zip *intern; + zval *self = getThis(); + size_t name_len; + char *name; + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) { + return; + } + if (name_len < 1) { + RETURN_FALSE; + } + + PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb); + if (zip_delete(intern, sb.index)) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::renameIndex(int index, string new_name) +Rename an entry selected by its index to new_name */ +static ZIPARCHIVE_METHOD(renameIndex) +{ + struct zip *intern; + zval *self = getThis(); + + char *new_name; + size_t new_name_len; + zend_long index; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &index, &new_name, &new_name_len) == FAILURE) { + return; + } + + if (index < 0) { + RETURN_FALSE; + } + + if (new_name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as new entry name"); + RETURN_FALSE; + } + if (zip_rename(intern, index, (const char *)new_name) != 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::renameName(string name, string new_name) +Rename an entry selected by its name to new_name */ +static ZIPARCHIVE_METHOD(renameName) +{ + struct zip *intern; + zval *self = getThis(); + struct zip_stat sb; + char *name, *new_name; + size_t name_len, new_name_len; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &new_name, &new_name_len) == FAILURE) { + return; + } + + if (new_name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as new entry name"); + RETURN_FALSE; + } + + PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb); + + if (zip_rename(intern, sb.index, (const char *)new_name)) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::unchangeIndex(int index) +Changes to the file at position index are reverted */ +static ZIPARCHIVE_METHOD(unchangeIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { + return; + } + + if (index < 0) { + RETURN_FALSE; + } + + if (zip_unchange(intern, index) != 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::unchangeName(string name) +Changes to the file named 'name' are reverted */ +static ZIPARCHIVE_METHOD(unchangeName) +{ + struct zip *intern; + zval *self = getThis(); + struct zip_stat sb; + char *name; + size_t name_len; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) { + return; + } + + if (name_len < 1) { + RETURN_FALSE; + } + + PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb); + + if (zip_unchange(intern, sb.index) != 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::unchangeAll() +All changes to files and global information in archive are reverted */ +static ZIPARCHIVE_METHOD(unchangeAll) +{ + struct zip *intern; + zval *self = getThis(); + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zip_unchange_all(intern) != 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::unchangeArchive() +Revert all global changes to the archive archive. For now, this only reverts archive comment changes. */ +static ZIPARCHIVE_METHOD(unchangeArchive) +{ + struct zip *intern; + zval *self = getThis(); + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zip_unchange_archive(intern) != 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::extractTo(string pathto[, mixed files]) +Extract one or more file from a zip archive */ +/* TODO: + * - allow index or array of indeces + * - replace path + * - patterns + */ +static ZIPARCHIVE_METHOD(extractTo) +{ + struct zip *intern; + + zval *self = getThis(); + zval *zval_files = NULL; + zval *zval_file = NULL; + php_stream_statbuf ssb; + char *pathto; + size_t pathto_len; + int ret, i; + + int nelems; + + if (!self) { + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|z", &pathto, &pathto_len, &zval_files) == FAILURE) { + return; + } + + if (pathto_len < 1) { + RETURN_FALSE; + } + + if (php_stream_stat_path_ex(pathto, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL) < 0) { + ret = php_stream_mkdir(pathto, 0777, PHP_STREAM_MKDIR_RECURSIVE, NULL); + if (!ret) { + RETURN_FALSE; + } + } + + ZIP_FROM_OBJECT(intern, self); + if (zval_files && (Z_TYPE_P(zval_files) != IS_NULL)) { + switch (Z_TYPE_P(zval_files)) { + case IS_STRING: + if (!php_zip_extract_file(intern, pathto, Z_STRVAL_P(zval_files), Z_STRLEN_P(zval_files))) { + RETURN_FALSE; + } + break; + case IS_ARRAY: + nelems = zend_hash_num_elements(Z_ARRVAL_P(zval_files)); + if (nelems == 0 ) { + RETURN_FALSE; + } + for (i = 0; i < nelems; i++) { + if ((zval_file = zend_hash_index_find(Z_ARRVAL_P(zval_files), i)) != NULL) { + switch (Z_TYPE_P(zval_file)) { + case IS_LONG: + break; + case IS_STRING: + if (!php_zip_extract_file(intern, pathto, Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file))) { + RETURN_FALSE; + } + break; + } + } + } + break; + case IS_LONG: + default: + php_error_docref(NULL, E_WARNING, "Invalid argument, expect string or array of strings"); + break; + } + } else { + /* Extract all files */ + int filecount = zip_get_num_files(intern); + + if (filecount == -1) { + php_error_docref(NULL, E_WARNING, "Illegal archive"); + RETURN_FALSE; + } + + for (i = 0; i < filecount; i++) { + char *file = (char*)zip_get_name(intern, i, ZIP_FL_UNCHANGED); + if (!php_zip_extract_file(intern, pathto, file, strlen(file))) { + RETURN_FALSE; + } + } + } + RETURN_TRUE; +} +/* }}} */ + +static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ +{ + struct zip *intern; + zval *self = getThis(); + + struct zip_stat sb; + struct zip_file *zf; + + zend_long index = -1; + zend_long flags = 0; + zend_long len = 0; + + zend_string *filename; + zend_string *buffer; + + int n = 0; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (type == 1) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|ll", &filename, &len, &flags) == FAILURE) { + return; + } + PHP_ZIP_STAT_PATH(intern, filename->val, filename->len, flags, sb); + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ll", &index, &len, &flags) == FAILURE) { + return; + } + PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + } + + if (sb.size < 1) { + RETURN_EMPTY_STRING(); + } + + if (len < 1) { + len = sb.size; + } + if (index >= 0) { + zf = zip_fopen_index(intern, index, flags); + } else { + zf = zip_fopen(intern, filename->val, flags); + } + + if (zf == NULL) { + RETURN_FALSE; + } + + buffer = zend_string_alloc(len, 0); + n = zip_fread(zf, buffer->val, buffer->len); + if (n < 1) { + zend_string_free(buffer); + RETURN_EMPTY_STRING(); + } + + zip_fclose(zf); + buffer->val[n] = '\0'; + buffer->len = n; + RETURN_NEW_STR(buffer); +} +/* }}} */ + +/* {{{ proto string ZipArchive::getFromName(string entryname[, int len [, int flags]]) +get the contents of an entry using its name */ +static ZIPARCHIVE_METHOD(getFromName) +{ + php_zip_get_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto string ZipArchive::getFromIndex(int index[, int len [, int flags]]) +get the contents of an entry using its index */ +static ZIPARCHIVE_METHOD(getFromIndex) +{ + php_zip_get_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto resource ZipArchive::getStream(string entryname) +get a stream for an entry using its name */ +static ZIPARCHIVE_METHOD(getStream) +{ + struct zip *intern; + zval *self = getThis(); + struct zip_stat sb; + char *mode = "rb"; + zend_string *filename; + php_stream *stream; + ze_zip_object *obj; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &filename) == FAILURE) { + return; + } + + if (zip_stat(intern, filename->val, 0, &sb) != 0) { + RETURN_FALSE; + } + + obj = Z_ZIP_P(self); + + stream = php_stream_zip_open(obj->filename, filename->val, mode STREAMS_CC); + if (stream) { + php_stream_to_zval(stream, return_value); + } +} +/* }}} */ + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_open, 0, 0, 1) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setpassword, 0, 0, 1) + ZEND_ARG_INFO(0, password) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_ziparchive__void, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_addemptydir, 0, 0, 1) + ZEND_ARG_INFO(0, dirname) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_addglob, 0, 0, 1) + ZEND_ARG_INFO(0, pattern) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_addpattern, 0, 0, 1) + ZEND_ARG_INFO(0, pattern) + ZEND_ARG_INFO(0, path) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_addfile, 0, 0, 1) + ZEND_ARG_INFO(0, filepath) + ZEND_ARG_INFO(0, entryname) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, length) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_addfromstring, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, content) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_statname, 0, 0, 1) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_statindex, 0, 0, 1) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setarchivecomment, 0, 0, 1) + ZEND_ARG_INFO(0, comment) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setcommentindex, 0, 0, 2) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, comment) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getcommentname, 0, 0, 1) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getcommentindex, 0, 0, 1) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_renameindex, 0, 0, 2) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, new_name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_renamename, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, new_name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_unchangeindex, 0, 0, 1) + ZEND_ARG_INFO(0, index) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_unchangename, 0, 0, 1) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_extractto, 0, 0, 1) + ZEND_ARG_INFO(0, pathto) + ZEND_ARG_INFO(0, files) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getfromname, 0, 0, 1) + ZEND_ARG_INFO(0, entryname) + ZEND_ARG_INFO(0, len) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getfromindex, 0, 0, 1) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, len) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getarchivecomment, 0, 0, 0) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setcommentname, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, comment) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getstream, 0, 0, 1) + ZEND_ARG_INFO(0, entryname) +ZEND_END_ARG_INFO() + +#ifdef ZIP_OPSYS_DEFAULT +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setextattrname, 0, 0, 3) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, opsys) + ZEND_ARG_INFO(0, attr) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setextattrindex, 0, 0, 3) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, opsys) + ZEND_ARG_INFO(0, attr) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getextattrname, 0, 0, 3) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(1, opsys) + ZEND_ARG_INFO(1, attr) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getextattrindex, 0, 0, 3) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(1, opsys) + ZEND_ARG_INFO(1, attr) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() +#endif /* ifdef ZIP_OPSYS_DEFAULT */ +/* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setcompname, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, compflags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setcompindex, 0, 0, 2) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, compflags) +ZEND_END_ARG_INFO() + +/* {{{ ze_zip_object_class_functions */ +static const zend_function_entry zip_class_functions[] = { + ZIPARCHIVE_ME(open, arginfo_ziparchive_open, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setPassword, arginfo_ziparchive_setpassword, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(close, arginfo_ziparchive__void, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getStatusString, arginfo_ziparchive__void, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(addEmptyDir, arginfo_ziparchive_addemptydir, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(addFromString, arginfo_ziparchive_addfromstring, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(addFile, arginfo_ziparchive_addfile, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(addGlob, arginfo_ziparchive_addglob, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(addPattern, arginfo_ziparchive_addpattern, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(renameIndex, arginfo_ziparchive_renameindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(renameName, arginfo_ziparchive_renamename, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setArchiveComment, arginfo_ziparchive_setarchivecomment, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getArchiveComment, arginfo_ziparchive_getarchivecomment, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setCommentIndex, arginfo_ziparchive_setcommentindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setCommentName, arginfo_ziparchive_setcommentname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getCommentIndex, arginfo_ziparchive_getcommentindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getCommentName, arginfo_ziparchive_getcommentname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(deleteIndex, arginfo_ziparchive_unchangeindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(deleteName, arginfo_ziparchive_unchangename, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(statName, arginfo_ziparchive_statname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(statIndex, arginfo_ziparchive_statindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(locateName, arginfo_ziparchive_statname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getNameIndex, arginfo_ziparchive_statindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(unchangeArchive, arginfo_ziparchive__void, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(unchangeAll, arginfo_ziparchive__void, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(unchangeIndex, arginfo_ziparchive_unchangeindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(unchangeName, arginfo_ziparchive_unchangename, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(extractTo, arginfo_ziparchive_extractto, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getFromName, arginfo_ziparchive_getfromname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getFromIndex, arginfo_ziparchive_getfromindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getStream, arginfo_ziparchive_getstream, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setExternalAttributesName, arginfo_ziparchive_setextattrname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setExternalAttributesIndex, arginfo_ziparchive_setextattrindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getExternalAttributesName, arginfo_ziparchive_getextattrname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getExternalAttributesIndex, arginfo_ziparchive_getextattrindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setCompressionName, arginfo_ziparchive_setcompname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setCompressionIndex, arginfo_ziparchive_setcompindex, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; +/* }}} */ + +static void php_zip_free_prop_handler(zval *el) /* {{{ */ { + pefree(Z_PTR_P(el), 1); +} /* }}} */ + +/* {{{ PHP_MINIT_FUNCTION */ +static PHP_MINIT_FUNCTION(zip) +{ + zend_class_entry ce; + + memcpy(&zip_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + zip_object_handlers.offset = XtOffsetOf(ze_zip_object, zo); + zip_object_handlers.free_obj = php_zip_object_free_storage; + zip_object_handlers.clone_obj = NULL; + zip_object_handlers.get_property_ptr_ptr = php_zip_get_property_ptr_ptr; + + zip_object_handlers.get_properties = php_zip_get_properties; + zip_object_handlers.read_property = php_zip_read_property; + zip_object_handlers.has_property = php_zip_has_property; + + INIT_CLASS_ENTRY(ce, "ZipArchive", zip_class_functions); + ce.create_object = php_zip_object_new; + zip_class_entry = zend_register_internal_class(&ce); + + zend_hash_init(&zip_prop_handlers, 0, NULL, php_zip_free_prop_handler, 1); + php_zip_register_prop_handler(&zip_prop_handlers, "status", php_zip_status, NULL, NULL, IS_LONG); + php_zip_register_prop_handler(&zip_prop_handlers, "statusSys", php_zip_status_sys, NULL, NULL, IS_LONG); + php_zip_register_prop_handler(&zip_prop_handlers, "numFiles", php_zip_get_num_files, NULL, NULL, IS_LONG); + php_zip_register_prop_handler(&zip_prop_handlers, "filename", NULL, NULL, php_zipobj_get_filename, IS_STRING); + php_zip_register_prop_handler(&zip_prop_handlers, "comment", NULL, php_zipobj_get_zip_comment, NULL, IS_STRING); + + REGISTER_ZIP_CLASS_CONST_LONG("CREATE", ZIP_CREATE); + REGISTER_ZIP_CLASS_CONST_LONG("EXCL", ZIP_EXCL); + REGISTER_ZIP_CLASS_CONST_LONG("CHECKCONS", ZIP_CHECKCONS); + REGISTER_ZIP_CLASS_CONST_LONG("OVERWRITE", ZIP_OVERWRITE); + + REGISTER_ZIP_CLASS_CONST_LONG("FL_NOCASE", ZIP_FL_NOCASE); + REGISTER_ZIP_CLASS_CONST_LONG("FL_NODIR", ZIP_FL_NODIR); + REGISTER_ZIP_CLASS_CONST_LONG("FL_COMPRESSED", ZIP_FL_COMPRESSED); + REGISTER_ZIP_CLASS_CONST_LONG("FL_UNCHANGED", ZIP_FL_UNCHANGED); + REGISTER_ZIP_CLASS_CONST_LONG("CM_DEFAULT", ZIP_CM_DEFAULT); + REGISTER_ZIP_CLASS_CONST_LONG("CM_STORE", ZIP_CM_STORE); + REGISTER_ZIP_CLASS_CONST_LONG("CM_SHRINK", ZIP_CM_SHRINK); + REGISTER_ZIP_CLASS_CONST_LONG("CM_REDUCE_1", ZIP_CM_REDUCE_1); + REGISTER_ZIP_CLASS_CONST_LONG("CM_REDUCE_2", ZIP_CM_REDUCE_2); + REGISTER_ZIP_CLASS_CONST_LONG("CM_REDUCE_3", ZIP_CM_REDUCE_3); + REGISTER_ZIP_CLASS_CONST_LONG("CM_REDUCE_4", ZIP_CM_REDUCE_4); + REGISTER_ZIP_CLASS_CONST_LONG("CM_IMPLODE", ZIP_CM_IMPLODE); + REGISTER_ZIP_CLASS_CONST_LONG("CM_DEFLATE", ZIP_CM_DEFLATE); + REGISTER_ZIP_CLASS_CONST_LONG("CM_DEFLATE64", ZIP_CM_DEFLATE64); + REGISTER_ZIP_CLASS_CONST_LONG("CM_PKWARE_IMPLODE", ZIP_CM_PKWARE_IMPLODE); + REGISTER_ZIP_CLASS_CONST_LONG("CM_BZIP2", ZIP_CM_BZIP2); + REGISTER_ZIP_CLASS_CONST_LONG("CM_LZMA", ZIP_CM_LZMA); + REGISTER_ZIP_CLASS_CONST_LONG("CM_TERSE", ZIP_CM_TERSE); + REGISTER_ZIP_CLASS_CONST_LONG("CM_LZ77", ZIP_CM_LZ77); + REGISTER_ZIP_CLASS_CONST_LONG("CM_WAVPACK", ZIP_CM_WAVPACK); + REGISTER_ZIP_CLASS_CONST_LONG("CM_PPMD", ZIP_CM_PPMD); + + /* Error code */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_OK", ZIP_ER_OK); /* N No error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_MULTIDISK", ZIP_ER_MULTIDISK); /* N Multi-disk zip archives not supported */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_RENAME", ZIP_ER_RENAME); /* S Renaming temporary file failed */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_CLOSE", ZIP_ER_CLOSE); /* S Closing zip archive failed */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_SEEK", ZIP_ER_SEEK); /* S Seek error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_READ", ZIP_ER_READ); /* S Read error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_WRITE", ZIP_ER_WRITE); /* S Write error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_CRC", ZIP_ER_CRC); /* N CRC error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_ZIPCLOSED", ZIP_ER_ZIPCLOSED); /* N Containing zip archive was closed */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_NOENT", ZIP_ER_NOENT); /* N No such file */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_EXISTS", ZIP_ER_EXISTS); /* N File already exists */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_OPEN", ZIP_ER_OPEN); /* S Can't open file */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_TMPOPEN", ZIP_ER_TMPOPEN); /* S Failure to create temporary file */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_ZLIB", ZIP_ER_ZLIB); /* Z Zlib error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_MEMORY", ZIP_ER_MEMORY); /* N Malloc failure */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_CHANGED", ZIP_ER_CHANGED); /* N Entry has been changed */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_COMPNOTSUPP", ZIP_ER_COMPNOTSUPP);/* N Compression method not supported */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_EOF", ZIP_ER_EOF); /* N Premature EOF */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_INVAL", ZIP_ER_INVAL); /* N Invalid argument */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_NOZIP", ZIP_ER_NOZIP); /* N Not a zip archive */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_INTERNAL", ZIP_ER_INTERNAL); /* N Internal error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_INCONS", ZIP_ER_INCONS); /* N Zip archive inconsistent */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_REMOVE", ZIP_ER_REMOVE); /* S Can't remove file */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_DELETED", ZIP_ER_DELETED); /* N Entry has been deleted */ + +#ifdef ZIP_OPSYS_DEFAULT + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_DOS", ZIP_OPSYS_DOS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_AMIGA", ZIP_OPSYS_AMIGA); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_OPENVMS", ZIP_OPSYS_OPENVMS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_UNIX", ZIP_OPSYS_UNIX); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_VM_CMS", ZIP_OPSYS_VM_CMS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_ATARI_ST", ZIP_OPSYS_ATARI_ST); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_OS_2", ZIP_OPSYS_OS_2); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_MACINTOSH", ZIP_OPSYS_MACINTOSH); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_Z_SYSTEM", ZIP_OPSYS_Z_SYSTEM); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_Z_CPM", ZIP_OPSYS_CPM); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_WINDOWS_NTFS", ZIP_OPSYS_WINDOWS_NTFS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_MVS", ZIP_OPSYS_MVS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_VSE", ZIP_OPSYS_VSE); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_ACORN_RISC", ZIP_OPSYS_ACORN_RISC); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_VFAT", ZIP_OPSYS_VFAT); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_ALTERNATE_MVS", ZIP_OPSYS_ALTERNATE_MVS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_BEOS", ZIP_OPSYS_BEOS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_TANDEM", ZIP_OPSYS_TANDEM); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_OS_400", ZIP_OPSYS_OS_400); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_OS_X", ZIP_OPSYS_OS_X); + + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_DEFAULT", ZIP_OPSYS_DEFAULT); +#endif /* ifdef ZIP_OPSYS_DEFAULT */ + + php_register_url_stream_wrapper("zip", &php_stream_zip_wrapper); + + le_zip_dir = zend_register_list_destructors_ex(php_zip_free_dir, NULL, le_zip_dir_name, module_number); + le_zip_entry = zend_register_list_destructors_ex(php_zip_free_entry, NULL, le_zip_entry_name, module_number); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +static PHP_MSHUTDOWN_FUNCTION(zip) +{ + zend_hash_destroy(&zip_prop_handlers); + php_unregister_url_stream_wrapper("zip"); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +static PHP_MINFO_FUNCTION(zip) +{ + php_info_print_table_start(); + + php_info_print_table_row(2, "Zip", "enabled"); + php_info_print_table_row(2, "Extension Version","$Id$"); + php_info_print_table_row(2, "Zip version", PHP_ZIP_VERSION); + php_info_print_table_row(2, "Libzip version", LIBZIP_VERSION); + + php_info_print_table_end(); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_c4ee76_2a1218_ext#ftp#ftp.c b/src/main/resource/testFiles/php-src/prevFiles/prev_c4ee76_2a1218_ext#ftp#ftp.c new file mode 100644 index 0000000..38d972c --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_c4ee76_2a1218_ext#ftp#ftp.c @@ -0,0 +1,1919 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2005 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andrew Skalski | + | Stefan Esser (resume functions) | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" + +#if HAVE_FTP + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#ifdef PHP_WIN32 +#include +#elif defined(NETWARE) +#ifdef USE_WINSOCK /* Modified to use Winsock (NOVSOCK2.H), atleast for now */ +#include +#else +#include +#include +#include +#endif +#else +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#include +#include +#include +#endif +#include + +#if HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#if HAVE_OPENSSL_EXT +#include +#endif + +#include "ftp.h" +#include "ext/standard/fsock.h" + +/* Additional headers for NetWare */ +#if defined(NETWARE) && !defined(USE_WINSOCK) +#include +#endif + +/* sends an ftp command, returns true on success, false on error. + * it sends the string "cmd args\r\n" if args is non-null, or + * "cmd\r\n" if args is null + */ +static int ftp_putcmd( ftpbuf_t *ftp, + const char *cmd, + const char *args); + +/* wrapper around send/recv to handle timeouts */ +static int my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len); +static int my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len); +static int my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen); + +/* reads a line the socket , returns true on success, false on error */ +static int ftp_readline(ftpbuf_t *ftp); + +/* reads an ftp response, returns true on success, false on error */ +static int ftp_getresp(ftpbuf_t *ftp); + +/* sets the ftp transfer type */ +static int ftp_type(ftpbuf_t *ftp, ftptype_t type); + +/* opens up a data stream */ +static databuf_t* ftp_getdata(ftpbuf_t *ftp TSRMLS_DC); + +/* accepts the data connection, returns updated data buffer */ +static databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC); + +/* closes the data connection, returns NULL */ +static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data); + +/* generic file lister */ +static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC); + +/* IP and port conversion box */ +union ipbox { + struct in_addr ia[2]; + unsigned short s[4]; + unsigned char c[8]; +}; + +/* {{{ ftp_open + */ +ftpbuf_t* +ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC) +{ + ftpbuf_t *ftp; + socklen_t size; + struct timeval tv; + + + /* alloc the ftp structure */ + ftp = ecalloc(1, sizeof(*ftp)); + + tv.tv_sec = timeout_sec; + tv.tv_usec = 0; + + ftp->fd = php_network_connect_socket_to_host(host, + (unsigned short) (port ? port : 21), SOCK_STREAM, + 0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC); + if (ftp->fd == -1) { + goto bail; + } + + /* Default Settings */ + ftp->timeout_sec = timeout_sec; + ftp->nb = 0; + + size = sizeof(ftp->localaddr); + memset(&ftp->localaddr, 0, size); + if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + if (!ftp_getresp(ftp) || ftp->resp != 220) { + goto bail; + } + + return ftp; + +bail: + if (ftp->fd != -1) { + closesocket(ftp->fd); + } + efree(ftp); + return NULL; +} +/* }}} */ + +/* {{{ ftp_close + */ +ftpbuf_t* +ftp_close(ftpbuf_t *ftp) +{ + if (ftp == NULL) { + return NULL; + } + if (ftp->data) { + data_close(ftp, ftp->data); + } + if (ftp->fd != -1) { +#if HAVE_OPENSSL_EXT + if (ftp->ssl_active) { + SSL_shutdown(ftp->ssl_handle); + } +#endif + closesocket(ftp->fd); + } + ftp_gc(ftp); + efree(ftp); + return NULL; +} +/* }}} */ + +/* {{{ ftp_gc + */ +void +ftp_gc(ftpbuf_t *ftp) +{ + if (ftp == NULL) { + return; + } + if (ftp->pwd) { + efree(ftp->pwd); + ftp->pwd = NULL; + } + if (ftp->syst) { + efree(ftp->syst); + ftp->syst = NULL; + } +} +/* }}} */ + +/* {{{ ftp_quit + */ +int +ftp_quit(ftpbuf_t *ftp) +{ + if (ftp == NULL) { + return 0; + } + + if (!ftp_putcmd(ftp, "QUIT", NULL)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 221) { + return 0; + } + + if (ftp->pwd) { + efree(ftp->pwd); + ftp->pwd = NULL; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_login + */ +int +ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC) +{ +#if HAVE_OPENSSL_EXT + SSL_CTX *ctx = NULL; +#endif + if (ftp == NULL) { + return 0; + } + +#if HAVE_OPENSSL_EXT + if (ftp->use_ssl && !ftp->ssl_active) { + if (!ftp_putcmd(ftp, "AUTH", "TLS")) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + + if (ftp->resp != 234) { + if (!ftp_putcmd(ftp, "AUTH", "SSL")) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + + if (ftp->resp != 334) { + ftp->use_ssl = 0; + } else { + ftp->old_ssl = 1; + ftp->use_ssl_for_data = 1; + } + } + + /* now enable ssl if we still need to */ + if (ftp->use_ssl) { + ctx = SSL_CTX_new(SSLv23_client_method()); + if (ctx == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL context"); + return 0; + } + + ftp->ssl_handle = SSL_new(ctx); + if (ftp->ssl_handle == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL handle"); + SSL_CTX_free(ctx); + return 0; + } + + SSL_set_fd(ftp->ssl_handle, ftp->fd); + + if (SSL_connect(ftp->ssl_handle) <= 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed"); + SSL_shutdown(ftp->ssl_handle); + return 0; + } + + ftp->ssl_active = 1; + + if (!ftp->old_ssl) { + + /* set protection buffersize to zero */ + if (!ftp_putcmd(ftp, "PBSZ", "0")) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + + /* enable data conn encryption */ + if (!ftp_putcmd(ftp, "PROT", "P")) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + + ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299); + } + } + } +#endif + + if (!ftp_putcmd(ftp, "USER", user)) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + if (ftp->resp == 230) { + return 1; + } + if (ftp->resp != 331) { + return 0; + } + if (!ftp_putcmd(ftp, "PASS", pass)) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + return (ftp->resp == 230); +} +/* }}} */ + +/* {{{ ftp_reinit + */ +int +ftp_reinit(ftpbuf_t *ftp) +{ + if (ftp == NULL) { + return 0; + } + + ftp_gc(ftp); + + ftp->nb = 0; + + if (!ftp_putcmd(ftp, "REIN", NULL)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 220) { + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_syst + */ +const char* +ftp_syst(ftpbuf_t *ftp) +{ + char *syst, *end; + + if (ftp == NULL) { + return NULL; + } + + /* default to cached value */ + if (ftp->syst) { + return ftp->syst; + } + if (!ftp_putcmd(ftp, "SYST", NULL)) { + return NULL; + } + if (!ftp_getresp(ftp) || ftp->resp != 215) { + return NULL; + } + syst = ftp->inbuf; + while (*syst == ' ') { + syst++; + } + if ((end = strchr(syst, ' '))) { + *end = 0; + } + ftp->syst = estrdup(syst); + if (end) { + *end = ' '; + } + return ftp->syst; +} +/* }}} */ + +/* {{{ ftp_pwd + */ +const char* +ftp_pwd(ftpbuf_t *ftp) +{ + char *pwd, *end; + + if (ftp == NULL) { + return NULL; + } + + /* default to cached value */ + if (ftp->pwd) { + return ftp->pwd; + } + if (!ftp_putcmd(ftp, "PWD", NULL)) { + return NULL; + } + if (!ftp_getresp(ftp) || ftp->resp != 257) { + return NULL; + } + /* copy out the pwd from response */ + if ((pwd = strchr(ftp->inbuf, '"')) == NULL) { + return NULL; + } + if ((end = strrchr(++pwd, '"')) == NULL) { + return NULL; + } + ftp->pwd = estrndup(pwd, end - pwd); + + return ftp->pwd; +} +/* }}} */ + +/* {{{ ftp_exec + */ +int +ftp_exec(ftpbuf_t *ftp, const char *cmd) +{ + if (ftp == NULL) { + return 0; + } + if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 200) { + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_raw + */ +void +ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value) +{ + if (ftp == NULL || cmd == NULL) { + RETURN_NULL(); + } + if (!ftp_putcmd(ftp, cmd, NULL)) { + RETURN_NULL(); + } + array_init(return_value); + while (ftp_readline(ftp)) { + add_next_index_string(return_value, ftp->inbuf, 1); + if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') { + return; + } + } +} +/* }}} */ + +/* {{{ ftp_chdir + */ +int +ftp_chdir(ftpbuf_t *ftp, const char *dir) +{ + if (ftp == NULL) { + return 0; + } + + if (ftp->pwd) { + efree(ftp->pwd); + ftp->pwd = NULL; + } + + if (!ftp_putcmd(ftp, "CWD", dir)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 250) { + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ ftp_cdup + */ +int +ftp_cdup(ftpbuf_t *ftp) +{ + if (ftp == NULL) { + return 0; + } + + if (ftp->pwd) { + efree(ftp->pwd); + ftp->pwd = NULL; + } + + if (!ftp_putcmd(ftp, "CDUP", NULL)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 250) { + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ ftp_mkdir + */ +char* +ftp_mkdir(ftpbuf_t *ftp, const char *dir) +{ + char *mkd, *end; + + if (ftp == NULL) { + return NULL; + } + if (!ftp_putcmd(ftp, "MKD", dir)) { + return NULL; + } + if (!ftp_getresp(ftp) || ftp->resp != 257) { + return NULL; + } + /* copy out the dir from response */ + if ((mkd = strchr(ftp->inbuf, '"')) == NULL) { + mkd = estrdup(dir); + return mkd; + } + if ((end = strrchr(++mkd, '"')) == NULL) { + return NULL; + } + *end = 0; + mkd = estrdup(mkd); + *end = '"'; + + return mkd; +} +/* }}} */ + +/* {{{ ftp_rmdir + */ +int +ftp_rmdir(ftpbuf_t *ftp, const char *dir) +{ + if (ftp == NULL) { + return 0; + } + if (!ftp_putcmd(ftp, "RMD", dir)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 250) { + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ ftp_chmod + */ +int +ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len) +{ + char *buffer; + + if (ftp == NULL || filename_len <= 0) { + return 0; + } + + if (!(buffer = emalloc(32 + filename_len + 1))) { + return 0; + } + + sprintf(buffer, "CHMOD %o %s", mode, filename); + + if (!ftp_putcmd(ftp, "SITE", buffer)) { + efree(buffer); + return 0; + } + + efree(buffer); + + if (!ftp_getresp(ftp) || ftp->resp != 200) { + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_alloc + */ +int +ftp_alloc(ftpbuf_t *ftp, const int size, char **response) +{ + char buffer[64]; + + if (ftp == NULL || size <= 0) { + return 0; + } + + snprintf(buffer, sizeof(buffer) - 1, "%d", size); + + if (!ftp_putcmd(ftp, "ALLO", buffer)) { + return 0; + } + + if (!ftp_getresp(ftp)) { + return 0; + } + + if (response && ftp->inbuf) { + *response = estrdup(ftp->inbuf); + } + + if (ftp->resp < 200 || ftp->resp >= 300) { + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_nlist + */ +char** +ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC) +{ + return ftp_genlist(ftp, "NLST", path TSRMLS_CC); +} +/* }}} */ + +/* {{{ ftp_list + */ +char** +ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC) +{ + return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC); +} +/* }}} */ + +/* {{{ ftp_type + */ +int +ftp_type(ftpbuf_t *ftp, ftptype_t type) +{ + char typechar[2] = "?"; + + if (ftp == NULL) { + return 0; + } + if (type == ftp->type) { + return 1; + } + if (type == FTPTYPE_ASCII) { + typechar[0] = 'A'; + } else if (type == FTPTYPE_IMAGE) { + typechar[0] = 'I'; + } else { + return 0; + } + if (!ftp_putcmd(ftp, "TYPE", typechar)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 200) { + return 0; + } + ftp->type = type; + + return 1; +} +/* }}} */ + +/* {{{ ftp_pasv + */ +int +ftp_pasv(ftpbuf_t *ftp, int pasv) +{ + char *ptr; + union ipbox ipbox; + unsigned long b[6]; + socklen_t n; + struct sockaddr *sa; + struct sockaddr_in *sin; + + if (ftp == NULL) { + return 0; + } + if (pasv && ftp->pasv == 2) { + return 1; + } + ftp->pasv = 0; + if (!pasv) { + return 1; + } + n = sizeof(ftp->pasvaddr); + memset(&ftp->pasvaddr, 0, n); + sa = (struct sockaddr *) &ftp->pasvaddr; + +#if HAVE_IPV6 + if (getpeername(ftp->fd, sa, &n) < 0) { + return 0; + } + if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; + char *endptr, delimiter; + + /* try EPSV first */ + if (!ftp_putcmd(ftp, "EPSV", NULL)) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + if (ftp->resp == 229) { + /* parse out the port */ + for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++); + if (!*ptr) { + return 0; + } + delimiter = *++ptr; + for (n = 0; *ptr && n < 3; ptr++) { + if (*ptr == delimiter) { + n++; + } + } + + sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10)); + if (ptr == endptr || *endptr != delimiter) { + return 0; + } + ftp->pasv = 2; + return 1; + } + } + + /* fall back to PASV */ +#endif + + if (!ftp_putcmd(ftp, "PASV", NULL)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 227) { + return 0; + } + /* parse out the IP and port */ + for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++); + n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]); + if (n != 6) { + return 0; + } + for (n = 0; n < 6; n++) { + ipbox.c[n] = (unsigned char) b[n]; + } + sin = (struct sockaddr_in *) sa; + sin->sin_family = AF_INET; + sin->sin_addr = ipbox.ia[0]; + sin->sin_port = ipbox.s[2]; + + ftp->pasv = 2; + + return 1; +} +/* }}} */ + +/* {{{ ftp_get + */ +int +ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC) +{ + databuf_t *data = NULL; + int lastch; + size_t rcvd; + char arg[11]; + + if (ftp == NULL) { + return 0; + } + if (!ftp_type(ftp, type)) { + goto bail; + } + + if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) { + goto bail; + } + + ftp->data = data; + + if (resumepos > 0) { + if (resumepos > 2147483647) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater then 2147483647 bytes."); + goto bail; + } + sprintf(arg, "%u", resumepos); + if (!ftp_putcmd(ftp, "REST", arg)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 350)) { + goto bail; + } + } + + if (!ftp_putcmd(ftp, "RETR", path)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) { + goto bail; + } + + if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { + goto bail; + } + + lastch = 0; + while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) { + if (rcvd == -1) { + goto bail; + } + + if (type == FTPTYPE_ASCII) { +#ifndef PHP_WIN32 + char *s; +#endif + char *ptr = data->buf; + char *e = ptr + rcvd; + /* logic depends on the OS EOL + * Win32 -> \r\n + * Everything Else \n + */ +#ifdef PHP_WIN32 + php_stream_write(outstream, ptr, (e - ptr)); + ptr = e; +#else + while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) { + php_stream_write(outstream, ptr, (s - ptr)); + if (*(s + 1) == '\n') { + s++; + php_stream_putc(outstream, '\n'); + } + ptr = s + 1; + } +#endif + if (ptr < e) { + php_stream_write(outstream, ptr, (e - ptr)); + } + } else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) { + goto bail; + } + } + + ftp->data = data = data_close(ftp, data); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + goto bail; + } + + return 1; +bail: + ftp->data = data_close(ftp, data); + return 0; +} +/* }}} */ + +/* {{{ ftp_put + */ +int +ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC) +{ + databuf_t *data = NULL; + int size; + char *ptr; + int ch; + char arg[11]; + + if (ftp == NULL) { + return 0; + } + if (!ftp_type(ftp, type)) { + goto bail; + } + if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) { + goto bail; + } + ftp->data = data; + + if (startpos > 0) { + if (startpos > 2147483647) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater then 2147483647 bytes."); + goto bail; + } + sprintf(arg, "%u", startpos); + if (!ftp_putcmd(ftp, "REST", arg)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 350)) { + goto bail; + } + } + + if (!ftp_putcmd(ftp, "STOR", path)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) { + goto bail; + } + if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { + goto bail; + } + + size = 0; + ptr = data->buf; + while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) { + /* flush if necessary */ + if (FTP_BUFSIZE - size < 2) { + if (my_send(ftp, data->fd, data->buf, size) != size) { + goto bail; + } + ptr = data->buf; + size = 0; + } + + if (ch == '\n' && type == FTPTYPE_ASCII) { + *ptr++ = '\r'; + size++; + } + + *ptr++ = ch; + size++; + } + + if (size && my_send(ftp, data->fd, data->buf, size) != size) { + goto bail; + } + ftp->data = data = data_close(ftp, data); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + goto bail; + } + return 1; +bail: + ftp->data = data_close(ftp, data); + return 0; +} +/* }}} */ + +/* {{{ ftp_size + */ +int +ftp_size(ftpbuf_t *ftp, const char *path) +{ + if (ftp == NULL) { + return -1; + } + if (!ftp_type(ftp, FTPTYPE_IMAGE)) { + return -1; + } + if (!ftp_putcmd(ftp, "SIZE", path)) { + return -1; + } + if (!ftp_getresp(ftp) || ftp->resp != 213) { + return -1; + } + return atoi(ftp->inbuf); +} +/* }}} */ + +/* {{{ ftp_mdtm + */ +time_t +ftp_mdtm(ftpbuf_t *ftp, const char *path) +{ + time_t stamp; + struct tm *gmt, tmbuf; + struct tm tm; + char *ptr; + int n; + + if (ftp == NULL) { + return -1; + } + if (!ftp_putcmd(ftp, "MDTM", path)) { + return -1; + } + if (!ftp_getresp(ftp) || ftp->resp != 213) { + return -1; + } + /* parse out the timestamp */ + for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++); + n = sscanf(ptr, "%4u%2u%2u%2u%2u%2u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); + if (n != 6) { + return -1; + } + tm.tm_year -= 1900; + tm.tm_mon--; + tm.tm_isdst = -1; + + /* figure out the GMT offset */ + stamp = time(NULL); + gmt = php_gmtime_r(&stamp, &tmbuf); + gmt->tm_isdst = -1; + + /* apply the GMT offset */ + tm.tm_sec += stamp - mktime(gmt); + tm.tm_isdst = gmt->tm_isdst; + + stamp = mktime(&tm); + + return stamp; +} +/* }}} */ + +/* {{{ ftp_delete + */ +int +ftp_delete(ftpbuf_t *ftp, const char *path) +{ + if (ftp == NULL) { + return 0; + } + if (!ftp_putcmd(ftp, "DELE", path)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 250) { + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_rename + */ +int +ftp_rename(ftpbuf_t *ftp, const char *src, const char *dest) +{ + if (ftp == NULL) { + return 0; + } + if (!ftp_putcmd(ftp, "RNFR", src)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 350) { + return 0; + } + if (!ftp_putcmd(ftp, "RNTO", dest)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 250) { + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ ftp_site + */ +int +ftp_site(ftpbuf_t *ftp, const char *cmd) +{ + if (ftp == NULL) { + return 0; + } + if (!ftp_putcmd(ftp, "SITE", cmd)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) { + return 0; + } + + return 1; +} +/* }}} */ + +/* static functions */ + +/* {{{ ftp_putcmd + */ +int +ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const char *args) +{ + int size; + char *data; + + /* build the output buffer */ + if (args && args[0]) { + /* "cmd args\r\n\0" */ + if (strlen(cmd) + strlen(args) + 4 > FTP_BUFSIZE) { + return 0; + } + size = sprintf(ftp->outbuf, "%s %s\r\n", cmd, args); + } else { + /* "cmd\r\n\0" */ + if (strlen(cmd) + 3 > FTP_BUFSIZE) { + return 0; + } + size = sprintf(ftp->outbuf, "%s\r\n", cmd); + } + + data = ftp->outbuf; + + if (my_send(ftp, ftp->fd, data, size) != size) { + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ ftp_readline + */ +int +ftp_readline(ftpbuf_t *ftp) +{ + int size, rcvd; + char *data, *eol; + + /* shift the extra to the front */ + size = FTP_BUFSIZE; + rcvd = 0; + if (ftp->extra) { + memmove(ftp->inbuf, ftp->extra, ftp->extralen); + rcvd = ftp->extralen; + } + + data = ftp->inbuf; + + do { + size -= rcvd; + for (eol = data; rcvd; rcvd--, eol++) { + if (*eol == '\r') { + *eol = 0; + ftp->extra = eol + 1; + if (rcvd > 1 && *(eol + 1) == '\n') { + ftp->extra++; + rcvd--; + } + if ((ftp->extralen = --rcvd) == 0) { + ftp->extra = NULL; + } + return 1; + } else if (*eol == '\n') { + *eol = 0; + ftp->extra = eol + 1; + if ((ftp->extralen = --rcvd) == 0) { + ftp->extra = NULL; + } + return 1; + } + } + + data = eol; + if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) { + return 0; + } + } while (size); + + return 0; +} +/* }}} */ + +/* {{{ ftp_getresp + */ +int +ftp_getresp(ftpbuf_t *ftp) +{ + char *buf; + + if (ftp == NULL) { + return 0; + } + buf = ftp->inbuf; + ftp->resp = 0; + + while (1) { + + if (!ftp_readline(ftp)) { + return 0; + } + + /* Break out when the end-tag is found */ + if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') { + break; + } + } + + /* translate the tag */ + if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) { + return 0; + } + + ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0'); + + memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4); + + if (ftp->extra) { + ftp->extra -= 4; + } + return 1; +} +/* }}} */ + +/* {{{ my_send + */ +int +my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len) +{ + int n, size, sent; + + size = len; + while (size) { + n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000); + + if (n < 1) { + +#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK)) + if (n == 0) { + errno = ETIMEDOUT; + } +#endif + return -1; + } + +#if HAVE_OPENSSL_EXT + if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) { + sent = SSL_write(ftp->ssl_handle, buf, size); + } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) { + sent = SSL_write(ftp->data->ssl_handle, buf, size); + } else { +#endif + sent = send(s, buf, size, 0); +#if HAVE_OPENSSL_EXT + } +#endif + if (sent == -1) { + return -1; + } + + buf = (char*) buf + sent; + size -= sent; + } + + return len; +} +/* }}} */ + +/* {{{ my_recv + */ +int +my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len) +{ + int n, nr_bytes; + + n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000); + if (n < 1) { +#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK)) + if (n == 0) { + errno = ETIMEDOUT; + } +#endif + return -1; + } + +#if HAVE_OPENSSL_EXT + if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) { + nr_bytes = SSL_read(ftp->ssl_handle, buf, len); + } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) { + nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len); + } else { +#endif + nr_bytes = recv(s, buf, len, 0); +#if HAVE_OPENSSL_EXT + } +#endif + return (nr_bytes); +} +/* }}} */ + +/* {{{ data_available + */ +int +data_available(ftpbuf_t *ftp, php_socket_t s) +{ + int n; + + n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000); + if (n < 1) { +#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK)) + if (n == 0) { + errno = ETIMEDOUT; + } +#endif + return 0; + } + + return 1; +} +/* }}} */ +/* {{{ data_writeable + */ +int +data_writeable(ftpbuf_t *ftp, php_socket_t s) +{ + int n; + + n = php_pollfd_for_ms(s, POLLOUT, 1000); + if (n < 1) { +#ifndef PHP_WIN32 + if (n == 0) { + errno = ETIMEDOUT; + } +#endif + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ my_accept + */ +int +my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen) +{ + int n; + + n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000); + if (n < 1) { +#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK)) + if (n == 0) { + errno = ETIMEDOUT; + } +#endif + return -1; + } + + return accept(s, addr, addrlen); +} +/* }}} */ + +/* {{{ ftp_getdata + */ +databuf_t* +ftp_getdata(ftpbuf_t *ftp TSRMLS_DC) +{ + int fd = -1; + databuf_t *data; + php_sockaddr_storage addr; + struct sockaddr *sa; + socklen_t size; + union ipbox ipbox; + char arg[sizeof("255, 255, 255, 255, 255, 255")]; + struct timeval tv; + + + /* ask for a passive connection if we need one */ + if (ftp->pasv && !ftp_pasv(ftp, 1)) { + return NULL; + } + /* alloc the data structure */ + data = ecalloc(1, sizeof(*data)); + data->listener = -1; + data->fd = -1; + data->type = ftp->type; + + sa = (struct sockaddr *) &ftp->localaddr; + /* bind/listen */ + if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + /* passive connection handler */ + if (ftp->pasv) { + /* clear the ready status */ + ftp->pasv = 1; + + /* connect */ + /* Win 95/98 seems not to like size > sizeof(sockaddr_in) */ + size = php_sockaddr_size(&ftp->pasvaddr); + tv.tv_sec = ftp->timeout_sec; + tv.tv_usec = 0; + if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + data->fd = fd; + + ftp->data = data; + return data; + } + + + /* active (normal) connection */ + + /* bind to a local address */ + php_any_addr(sa->sa_family, &addr, 0); + size = php_sockaddr_size(&addr); + + if (bind(fd, (struct sockaddr*) &addr, size) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + if (getsockname(fd, (struct sockaddr*) &addr, &size) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + if (listen(fd, 5) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + data->listener = fd; + +#if HAVE_IPV6 + if (sa->sa_family == AF_INET6) { + /* need to use EPRT */ + char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")]; + char out[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out)); + sprintf(eprtarg, "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port)); + + if (!ftp_putcmd(ftp, "EPRT", eprtarg)) { + goto bail; + } + + if (!ftp_getresp(ftp) || ftp->resp != 200) { + goto bail; + } + + ftp->data = data; + return data; + } +#endif + + /* send the PORT */ + ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr; + ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port; + sprintf(arg, "%u,%u,%u,%u,%u,%u", ipbox.c[0], ipbox.c[1], ipbox.c[2], ipbox.c[3], ipbox.c[4], ipbox.c[5]); + + if (!ftp_putcmd(ftp, "PORT", arg)) { + goto bail; + } + if (!ftp_getresp(ftp) || ftp->resp != 200) { + goto bail; + } + + ftp->data = data; + return data; + +bail: + if (fd != -1) { + closesocket(fd); + } + efree(data); + return NULL; +} +/* }}} */ + +/* {{{ data_accept + */ +databuf_t* +data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC) +{ + php_sockaddr_storage addr; + socklen_t size; + +#if HAVE_OPENSSL_EXT + SSL_CTX *ctx; +#endif + + if (data->fd != -1) { + goto data_accepted; + } + size = sizeof(addr); + data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size); + closesocket(data->listener); + data->listener = -1; + + if (data->fd == -1) { + efree(data); + return NULL; + } + +data_accepted: +#if HAVE_OPENSSL_EXT + + /* now enable ssl if we need to */ + if (ftp->use_ssl && ftp->use_ssl_for_data) { + ctx = SSL_CTX_new(SSLv23_client_method()); + if (ctx == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL context"); + return 0; + } + + data->ssl_handle = SSL_new(ctx); + if (data->ssl_handle == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL handle"); + SSL_CTX_free(ctx); + return 0; + } + + + SSL_set_fd(data->ssl_handle, data->fd); + + if (ftp->old_ssl) { + SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle); + } + + if (SSL_connect(data->ssl_handle) <= 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed"); + SSL_shutdown(data->ssl_handle); + return 0; + } + + data->ssl_active = 1; + } + +#endif + + return data; +} +/* }}} */ + +/* {{{ data_close + */ +databuf_t* +data_close(ftpbuf_t *ftp, databuf_t *data) +{ + if (data == NULL) { + return NULL; + } + if (data->listener != -1) { +#if HAVE_OPENSSL_EXT + if (data->ssl_active) { + SSL_shutdown(data->ssl_handle); + data->ssl_active = 0; + } +#endif + closesocket(data->listener); + } + if (data->fd != -1) { +#if HAVE_OPENSSL_EXT + if (data->ssl_active) { + SSL_shutdown(data->ssl_handle); + data->ssl_active = 0; + } +#endif + closesocket(data->fd); + } + if (ftp) { + ftp->data = NULL; + } + efree(data); + return NULL; +} +/* }}} */ + +/* {{{ ftp_genlist + */ +char** +ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC) +{ + php_stream *tmpstream = NULL; + databuf_t *data = NULL; + char *ptr; + int ch, lastch; + int size, rcvd; + int lines; + char **ret = NULL; + char **entry; + char *text; + + + if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create temporary file. Check permissions in temporary files directory."); + return NULL; + } + + if (!ftp_type(ftp, FTPTYPE_ASCII)) { + goto bail; + } + + if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) { + goto bail; + } + ftp->data = data; + + if (!ftp_putcmd(ftp, cmd, path)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) { + goto bail; + } + + /* pull data buffer into tmpfile */ + if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { + goto bail; + } + size = 0; + lines = 0; + lastch = 0; + while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) { + if (rcvd == -1) { + goto bail; + } + + php_stream_write(tmpstream, data->buf, rcvd); + + size += rcvd; + for (ptr = data->buf; rcvd; rcvd--, ptr++) { + if (*ptr == '\n' && lastch == '\r') { + lines++; + } else { + size++; + } + lastch = *ptr; + } + } + + ftp->data = data = data_close(ftp, data); + + php_stream_rewind(tmpstream); + + ret = safe_emalloc((lines + 1), sizeof(char**), size * sizeof(char*)); + + entry = ret; + text = (char*) (ret + lines + 1); + *entry = text; + lastch = 0; + while ((ch = php_stream_getc(tmpstream)) != EOF) { + if (ch == '\n' && lastch == '\r') { + *(text - 1) = 0; + *++entry = text; + } else { + *text++ = ch; + } + lastch = ch; + } + *entry = NULL; + + php_stream_close(tmpstream); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + efree(ret); + return NULL; + } + + return ret; +bail: + ftp->data = data_close(ftp, data); + php_stream_close(tmpstream); + if (ret) + efree(ret); + return NULL; +} +/* }}} */ + +/* {{{ ftp_nb_get + */ +int +ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC) +{ + databuf_t *data = NULL; + char arg[11]; + + if (ftp == NULL) { + goto bail; + } + + if (!ftp_type(ftp, type)) { + goto bail; + } + + if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) { + goto bail; + } + + if (resumepos>0) { + /* We are working on an architecture that supports 64-bit integers + * since php is 32 bit by design, we bail out with warning + */ + if (resumepos > 2147483647) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater then 2147483648 bytes."); + goto bail; + } + sprintf(arg, "%u", resumepos); + if (!ftp_putcmd(ftp, "REST", arg)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 350)) { + goto bail; + } + } + + if (!ftp_putcmd(ftp, "RETR", path)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) { + goto bail; + } + + if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { + goto bail; + } + + ftp->data = data; + ftp->stream = outstream; + ftp->lastch = 0; + ftp->nb = 1; + + return (ftp_nb_continue_read(ftp TSRMLS_CC)); + +bail: + ftp->data = data_close(ftp, data); + return PHP_FTP_FAILED; +} +/* }}} */ + +/* {{{ ftp_nb_continue_read + */ +int +ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC) +{ + databuf_t *data = NULL; + char *ptr; + int lastch; + size_t rcvd; + ftptype_t type; + + data = ftp->data; + + /* check if there is already more data */ + if (!data_available(ftp, data->fd)) { + return PHP_FTP_MOREDATA; + } + + type = ftp->type; + + lastch = ftp->lastch; + if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) { + if (rcvd == -1) { + goto bail; + } + + if (type == FTPTYPE_ASCII) { + for (ptr = data->buf; rcvd; rcvd--, ptr++) { + if (lastch == '\r' && *ptr != '\n') { + php_stream_putc(ftp->stream, '\r'); + } + if (*ptr != '\r') { + php_stream_putc(ftp->stream, *ptr); + } + lastch = *ptr; + } + } else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) { + goto bail; + } + + ftp->lastch = lastch; + return PHP_FTP_MOREDATA; + } + + if (type == FTPTYPE_ASCII && lastch == '\r') { + php_stream_putc(ftp->stream, '\r'); + } + + ftp->data = data = data_close(ftp, data); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + goto bail; + } + + ftp->nb = 0; + return PHP_FTP_FINISHED; +bail: + ftp->nb = 0; + ftp->data = data_close(ftp, data); + return PHP_FTP_FAILED; +} +/* }}} */ + +/* {{{ ftp_nb_put + */ +int +ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC) +{ + databuf_t *data = NULL; + char arg[11]; + + if (ftp == NULL) { + return 0; + } + if (!ftp_type(ftp, type)) { + goto bail; + } + if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) { + goto bail; + } + if (startpos > 0) { + if (startpos > 2147483647) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater then 2147483647 bytes."); + goto bail; + } + sprintf(arg, "%u", startpos); + if (!ftp_putcmd(ftp, "REST", arg)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 350)) { + goto bail; + } + } + + if (!ftp_putcmd(ftp, "STOR", path)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) { + goto bail; + } + if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { + goto bail; + } + ftp->data = data; + ftp->stream = instream; + ftp->lastch = 0; + ftp->nb = 1; + + return (ftp_nb_continue_write(ftp TSRMLS_CC)); + +bail: + ftp->data = data_close(ftp, data); + return PHP_FTP_FAILED; +} +/* }}} */ + + +/* {{{ ftp_nb_continue_write + */ +int +ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC) +{ + int size; + char *ptr; + int ch; + + /* check if we can write more data */ + if (!data_writeable(ftp, ftp->data->fd)) { + return PHP_FTP_MOREDATA; + } + + size = 0; + ptr = ftp->data->buf; + while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) { + + if (ch == '\n' && ftp->type == FTPTYPE_ASCII) { + *ptr++ = '\r'; + size++; + } + + *ptr++ = ch; + size++; + + /* flush if necessary */ + if (FTP_BUFSIZE - size < 2) { + if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) { + goto bail; + } + return PHP_FTP_MOREDATA; + } + } + + if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) { + goto bail; + } + ftp->data = data_close(ftp, ftp->data); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + goto bail; + } + ftp->nb = 0; + return PHP_FTP_FINISHED; +bail: + ftp->data = data_close(ftp, ftp->data); + ftp->nb = 0; + return PHP_FTP_FAILED; +} +/* }}} */ + +#endif /* HAVE_FTP */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_c71358_283565_main#main.c b/src/main/resource/testFiles/php-src/prevFiles/prev_c71358_283565_main#main.c new file mode 100644 index 0000000..62d3df0 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_c71358_283565_main#main.c @@ -0,0 +1,2363 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2009 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Rasmus Lerdorf | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* {{{ includes + */ + +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS + +#include "php.h" +#include +#include +#ifdef PHP_WIN32 +#include "win32/time.h" +#include "win32/signal.h" +#include "win32/php_win32_globals.h" +#include +#elif defined(NETWARE) +#include +#ifdef USE_WINSOCK +#include +#endif +#endif +#if HAVE_SYS_TIME_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SIGNAL_H +#include +#endif +#if HAVE_SETLOCALE +#include +#endif +#include "zend.h" +#include "zend_extensions.h" +#include "php_ini.h" +#include "php_globals.h" +#include "php_main.h" +#include "fopen_wrappers.h" +#include "ext/standard/php_standard.h" +#include "ext/standard/php_string.h" +#include "ext/date/php_date.h" +#include "php_variables.h" +#include "ext/standard/credits.h" +#ifdef PHP_WIN32 +#include +#include "win32/php_registry.h" +#include "ext/standard/flock_compat.h" +#endif +#include "php_syslog.h" +#include "Zend/zend_exceptions.h" + +#if PHP_SIGCHILD +#include +#include +#endif + +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_highlight.h" +#include "zend_indent.h" +#include "zend_extensions.h" +#include "zend_ini.h" + +#include "php_content_types.h" +#include "php_ticks.h" +#include "php_logos.h" +#include "php_streams.h" +#include "php_open_temporary_file.h" + +#include "SAPI.h" +#include "rfc1867.h" + +#if HAVE_SYS_MMAN_H +# include +# ifndef PAGE_SIZE +# define PAGE_SIZE 4096 +# endif +#endif +#ifdef PHP_WIN32 +# define PAGE_SIZE 4096 +#endif +/* }}} */ + +PHPAPI int (*php_register_internal_extensions_func)(TSRMLS_D) = php_register_internal_extensions; + +#ifndef ZTS +php_core_globals core_globals; +#else +PHPAPI int core_globals_id; +#endif + +#ifdef PHP_WIN32 +#include "win32_internal_function_disabled.h" + +static php_win32_disable_functions() { + int i; + TSRMLS_FETCH(); + + if (EG(windows_version_info).dwMajorVersion < 5) { + for (i = 0; i < function_name_cnt_5; i++) { + if (zend_hash_del(CG(function_table), function_name_5[i], strlen(function_name_5[i]) + 1)==FAILURE) { + php_printf("Unable to disable function '%s'\n", function_name_5[i]); + return FAILURE; + } + } + } + + if (EG(windows_version_info).dwMajorVersion < 6) { + for (i = 0; i < function_name_cnt_6; i++) { + if (zend_hash_del(CG(function_table), function_name_6[i], strlen(function_name_6[i]) + 1)==FAILURE) { + php_printf("Unable to disable function '%s'\n", function_name_6[i]); + return FAILURE; + } + } + } + return SUCCESS; +} +#endif + +#define SAFE_FILENAME(f) ((f)?(f):"-") + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnSetPrecision) +{ + int i = atoi(new_value); + if (i >= 0) { + EG(precision) = i; + return SUCCESS; + } else { + return FAILURE; + } +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnChangeMemoryLimit) +{ + if (new_value) { + PG(memory_limit) = zend_atol(new_value, new_value_length); + } else { + PG(memory_limit) = 1<<30; /* effectively, no limit */ + } + return zend_set_memory_limit(PG(memory_limit)); +} +/* }}} */ + + +/* {{{ php_disable_functions + */ +static void php_disable_functions(TSRMLS_D) +{ + char *s = NULL, *e; + + if (!*(INI_STR("disable_functions"))) { + return; + } + + e = PG(disable_functions) = strdup(INI_STR("disable_functions")); + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_disable_function(s, e-s TSRMLS_CC); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_disable_function(s, e-s TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ php_disable_classes + */ +static void php_disable_classes(TSRMLS_D) +{ + char *s = NULL, *e; + + if (!*(INI_STR("disable_classes"))) { + return; + } + + e = PG(disable_classes) = strdup(INI_STR("disable_classes")); + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_disable_class(s, e-s TSRMLS_CC); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_disable_class(s, e-s TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnUpdateTimeout) +{ + EG(timeout_seconds) = atoi(new_value); + if (stage==PHP_INI_STAGE_STARTUP) { + /* Don't set a timeout on startup, only per-request */ + return SUCCESS; + } + zend_unset_timeout(TSRMLS_C); + zend_set_timeout(EG(timeout_seconds), 0); + return SUCCESS; +} +/* }}} */ + +/* {{{ php_get_display_errors_mode() helper function + */ +static int php_get_display_errors_mode(char *value, int value_length) +{ + int mode; + + if (!value) { + return PHP_DISPLAY_ERRORS_STDOUT; + } + + if (value_length == 2 && !strcasecmp("on", value)) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else if (value_length == 3 && !strcasecmp("yes", value)) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else if (value_length == 4 && !strcasecmp("true", value)) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else if (value_length == 6 && !strcasecmp(value, "stderr")) { + mode = PHP_DISPLAY_ERRORS_STDERR; + } else if (value_length == 6 && !strcasecmp(value, "stdout")) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else { + mode = atoi(value); + if (mode && mode != PHP_DISPLAY_ERRORS_STDOUT && mode != PHP_DISPLAY_ERRORS_STDERR) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } + } + + return mode; +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnUpdateDisplayErrors) +{ + PG(display_errors) = (zend_bool) php_get_display_errors_mode(new_value, new_value_length); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_INI_DISP + */ +static PHP_INI_DISP(display_errors_mode) +{ + int mode, tmp_value_length, cgi_or_cli; + char *tmp_value; + TSRMLS_FETCH(); + + if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) { + tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL ); + tmp_value_length = ini_entry->orig_value_length; + } else if (ini_entry->value) { + tmp_value = ini_entry->value; + tmp_value_length = ini_entry->value_length; + } else { + tmp_value = NULL; + tmp_value_length = 0; + } + + mode = php_get_display_errors_mode(tmp_value, tmp_value_length); + + /* Display 'On' for other SAPIs instead of STDOUT or STDERR */ + cgi_or_cli = (!strcmp(sapi_module.name, "cli") || !strcmp(sapi_module.name, "cgi")); + + switch (mode) { + case PHP_DISPLAY_ERRORS_STDERR: + if (cgi_or_cli ) { + PUTS("STDERR"); + } else { + PUTS("On"); + } + break; + + case PHP_DISPLAY_ERRORS_STDOUT: + if (cgi_or_cli ) { + PUTS("STDOUT"); + } else { + PUTS("On"); + } + break; + + default: + PUTS("Off"); + break; + } +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnUpdateErrorLog) +{ + /* Only do the safemode/open_basedir check at runtime */ + if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && + strcmp(new_value, "syslog")) { + if (PG(safe_mode) && (!php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { + return FAILURE; + } + + if (PG(open_basedir) && php_check_open_basedir(new_value TSRMLS_CC)) { + return FAILURE; + } + + } + OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnUpdateMailLog) +{ + /* Only do the safemode/open_basedir check at runtime */ + if (stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) { + if (PG(safe_mode) && (!php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { + return FAILURE; + } + + if (PG(open_basedir) && php_check_open_basedir(new_value TSRMLS_CC)) { + return FAILURE; + } + + } + OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnChangeMailForceExtra) +{ + /* Don't allow changing it in htaccess */ + if (stage == PHP_INI_STAGE_HTACCESS) { + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + + +/* Need to convert to strings and make use of: + * PHP_SAFE_MODE + * + * Need to be read from the environment (?): + * PHP_AUTO_PREPEND_FILE + * PHP_AUTO_APPEND_FILE + * PHP_DOCUMENT_ROOT + * PHP_USER_DIR + * PHP_INCLUDE_PATH + */ + +#ifndef PHP_SAFE_MODE_EXEC_DIR +# define PHP_SAFE_MODE_EXEC_DIR "" +#endif + + /* Windows and Netware use the internal mail */ +#if defined(PHP_WIN32) || defined(NETWARE) +# define DEFAULT_SENDMAIL_PATH NULL +#elif defined(PHP_PROG_SENDMAIL) +# define DEFAULT_SENDMAIL_PATH PHP_PROG_SENDMAIL " -t -i " +#else +# define DEFAULT_SENDMAIL_PATH "/usr/sbin/sendmail -t -i" +#endif + +/* {{{ PHP_INI + */ +PHP_INI_BEGIN() + PHP_INI_ENTRY_EX("define_syslog_variables", "0", PHP_INI_ALL, NULL, php_ini_boolean_displayer_cb) + PHP_INI_ENTRY_EX("highlight.bg", HL_BG_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + PHP_INI_ENTRY_EX("highlight.comment", HL_COMMENT_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + PHP_INI_ENTRY_EX("highlight.default", HL_DEFAULT_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + PHP_INI_ENTRY_EX("highlight.html", HL_HTML_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + PHP_INI_ENTRY_EX("highlight.keyword", HL_KEYWORD_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + PHP_INI_ENTRY_EX("highlight.string", HL_STRING_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + + STD_PHP_INI_BOOLEAN("allow_call_time_pass_reference", "1", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, allow_call_time_pass_reference, zend_compiler_globals, compiler_globals) + STD_PHP_INI_BOOLEAN("asp_tags", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, asp_tags, zend_compiler_globals, compiler_globals) + STD_PHP_INI_ENTRY_EX("display_errors", "1", PHP_INI_ALL, OnUpdateDisplayErrors, display_errors, php_core_globals, core_globals, display_errors_mode) + STD_PHP_INI_BOOLEAN("display_startup_errors", "0", PHP_INI_ALL, OnUpdateBool, display_startup_errors, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("enable_dl", "1", PHP_INI_SYSTEM, OnUpdateBool, enable_dl, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("expose_php", "1", PHP_INI_SYSTEM, OnUpdateBool, expose_php, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("docref_root", "", PHP_INI_ALL, OnUpdateString, docref_root, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("docref_ext", "", PHP_INI_ALL, OnUpdateString, docref_ext, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("html_errors", "1", PHP_INI_ALL, OnUpdateBool, html_errors, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("xmlrpc_errors", "0", PHP_INI_SYSTEM, OnUpdateBool, xmlrpc_errors, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("xmlrpc_error_number", "0", PHP_INI_ALL, OnUpdateLong, xmlrpc_error_number, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("max_input_time", "-1", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, max_input_time, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("ignore_user_abort", "0", PHP_INI_ALL, OnUpdateBool, ignore_user_abort, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("implicit_flush", "0", PHP_INI_ALL, OnUpdateBool, implicit_flush, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("log_errors", "0", PHP_INI_ALL, OnUpdateBool, log_errors, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("log_errors_max_len", "1024", PHP_INI_ALL, OnUpdateLong, log_errors_max_len, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("ignore_repeated_errors", "0", PHP_INI_ALL, OnUpdateBool, ignore_repeated_errors, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("ignore_repeated_source", "0", PHP_INI_ALL, OnUpdateBool, ignore_repeated_source, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("report_memleaks", "1", PHP_INI_ALL, OnUpdateBool, report_memleaks, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("report_zend_debug", "1", PHP_INI_ALL, OnUpdateBool, report_zend_debug, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("magic_quotes_gpc", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, magic_quotes_gpc, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("magic_quotes_runtime", "0", PHP_INI_ALL, OnUpdateBool, magic_quotes_runtime, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("magic_quotes_sybase", "0", PHP_INI_ALL, OnUpdateBool, magic_quotes_sybase, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("output_buffering", "0", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateLong, output_buffering, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("output_handler", NULL, PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateString, output_handler, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("register_argc_argv", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, register_argc_argv, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("register_globals", "0", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, register_globals, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("register_long_arrays", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, register_long_arrays, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("auto_globals_jit", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, auto_globals_jit, php_core_globals, core_globals) +#if PHP_SAFE_MODE + STD_PHP_INI_BOOLEAN("safe_mode", "1", PHP_INI_SYSTEM, OnUpdateBool, safe_mode, php_core_globals, core_globals) +#else + STD_PHP_INI_BOOLEAN("safe_mode", "0", PHP_INI_SYSTEM, OnUpdateBool, safe_mode, php_core_globals, core_globals) +#endif + STD_PHP_INI_ENTRY("safe_mode_include_dir", NULL, PHP_INI_SYSTEM, OnUpdateString, safe_mode_include_dir, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("safe_mode_gid", "0", PHP_INI_SYSTEM, OnUpdateBool, safe_mode_gid, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("short_open_tag", DEFAULT_SHORT_OPEN_TAG, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, short_tags, zend_compiler_globals, compiler_globals) + STD_PHP_INI_BOOLEAN("sql.safe_mode", "0", PHP_INI_SYSTEM, OnUpdateBool, sql_safe_mode, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("track_errors", "0", PHP_INI_ALL, OnUpdateBool, track_errors, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("y2k_compliance", "1", PHP_INI_ALL, OnUpdateBool, y2k_compliance, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("unserialize_callback_func", NULL, PHP_INI_ALL, OnUpdateString, unserialize_callback_func, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("serialize_precision", "100", PHP_INI_ALL, OnUpdateLongGEZero, serialize_precision, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("arg_separator.output", "&", PHP_INI_ALL, OnUpdateStringUnempty, arg_separator.output, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("arg_separator.input", "&", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateStringUnempty, arg_separator.input, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("auto_append_file", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, auto_append_file, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("auto_prepend_file", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, auto_prepend_file, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("doc_root", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, doc_root, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("default_charset", SAPI_DEFAULT_CHARSET, PHP_INI_ALL, OnUpdateString, default_charset, sapi_globals_struct,sapi_globals) + STD_PHP_INI_ENTRY("default_mimetype", SAPI_DEFAULT_MIMETYPE, PHP_INI_ALL, OnUpdateString, default_mimetype, sapi_globals_struct,sapi_globals) + STD_PHP_INI_ENTRY("error_log", NULL, PHP_INI_ALL, OnUpdateErrorLog, error_log, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("extension_dir", PHP_EXTENSION_DIR, PHP_INI_SYSTEM, OnUpdateStringUnempty, extension_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("include_path", PHP_INCLUDE_PATH, PHP_INI_ALL, OnUpdateStringUnempty, include_path, php_core_globals, core_globals) + PHP_INI_ENTRY("max_execution_time", "30", PHP_INI_ALL, OnUpdateTimeout) + STD_PHP_INI_ENTRY("open_basedir", NULL, PHP_INI_ALL, OnUpdateBaseDir, open_basedir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("safe_mode_exec_dir", PHP_SAFE_MODE_EXEC_DIR, PHP_INI_SYSTEM, OnUpdateString, safe_mode_exec_dir, php_core_globals, core_globals) + + STD_PHP_INI_BOOLEAN("file_uploads", "1", PHP_INI_SYSTEM, OnUpdateBool, file_uploads, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("upload_max_filesize", "2M", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, upload_max_filesize, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("post_max_size", "8M", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, post_max_size, sapi_globals_struct,sapi_globals) + STD_PHP_INI_ENTRY("upload_tmp_dir", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, upload_tmp_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("max_input_nesting_level", "64", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLongGEZero, max_input_nesting_level, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("user_dir", NULL, PHP_INI_SYSTEM, OnUpdateString, user_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("variables_order", "EGPCS", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateStringUnempty, variables_order, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("request_order", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, request_order, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("error_append_string", NULL, PHP_INI_ALL, OnUpdateString, error_append_string, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("error_prepend_string", NULL, PHP_INI_ALL, OnUpdateString, error_prepend_string, php_core_globals, core_globals) + + PHP_INI_ENTRY("SMTP", "localhost",PHP_INI_ALL, NULL) + PHP_INI_ENTRY("smtp_port", "25", PHP_INI_ALL, NULL) + STD_PHP_INI_BOOLEAN("mail.add_x_header", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, mail_x_header, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("mail.log", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateMailLog, mail_log, php_core_globals, core_globals) + PHP_INI_ENTRY("browscap", NULL, PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("memory_limit", "128M", PHP_INI_ALL, OnChangeMemoryLimit) + PHP_INI_ENTRY("precision", "14", PHP_INI_ALL, OnSetPrecision) + PHP_INI_ENTRY("sendmail_from", NULL, PHP_INI_ALL, NULL) + PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("mail.force_extra_parameters",NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnChangeMailForceExtra) + PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL) + + STD_PHP_INI_BOOLEAN("allow_url_fopen", "1", PHP_INI_SYSTEM, OnUpdateBool, allow_url_fopen, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("allow_url_include", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_url_include, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("always_populate_raw_post_data", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, always_populate_raw_post_data, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("realpath_cache_size", "16K", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_size_limit, virtual_cwd_globals, cwd_globals) + STD_PHP_INI_ENTRY("realpath_cache_ttl", "120", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_ttl, virtual_cwd_globals, cwd_globals) + + STD_PHP_INI_ENTRY("user_ini.filename", ".user.ini", PHP_INI_SYSTEM, OnUpdateString, user_ini_filename, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("user_ini.cache_ttl", "300", PHP_INI_SYSTEM, OnUpdateLong, user_ini_cache_ttl, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("exit_on_timeout", "0", PHP_INI_ALL, OnUpdateBool, exit_on_timeout, php_core_globals, core_globals) +PHP_INI_END() +/* }}} */ + +/* True globals (no need for thread safety */ +/* But don't make them a single int bitfield */ +static int module_initialized = 0; +static int module_startup = 1; +static int module_shutdown = 0; + +/* {{{ php_during_module_startup */ +static int php_during_module_startup(void) +{ + return module_startup; +} +/* }}} */ + +/* {{{ php_during_module_shutdown */ +static int php_during_module_shutdown(void) +{ + return module_shutdown; +} +/* }}} */ + +/* {{{ php_log_err + */ +PHPAPI void php_log_err(char *log_message TSRMLS_DC) +{ + int fd = -1; + time_t error_time; + + /* Try to use the specified logging location. */ + if (PG(error_log) != NULL) { +#ifdef HAVE_SYSLOG_H + if (!strcmp(PG(error_log), "syslog")) { + php_syslog(LOG_NOTICE, "%.500s", log_message); + return; + } +#endif + fd = VCWD_OPEN_MODE(PG(error_log), O_CREAT | O_APPEND | O_WRONLY, 0644); + if (fd != -1) { + char *tmp; + int len; + char *error_time_str; + + time(&error_time); + error_time_str = php_format_date("d-M-Y H:i:s", 11, error_time, php_during_module_startup() TSRMLS_CC); + len = spprintf(&tmp, 0, "[%s] %s%s", error_time_str, log_message, PHP_EOL); +#ifdef PHP_WIN32 + php_flock(fd, 2); +#endif + write(fd, tmp, len); + efree(tmp); + efree(error_time_str); + close(fd); + return; + } + } + + /* Otherwise fall back to the default logging location, if we have one */ + + if (sapi_module.log_message) { + sapi_module.log_message(log_message); + } +} +/* }}} */ + +/* {{{ php_write + wrapper for modules to use PHPWRITE */ +PHPAPI int php_write(void *buf, uint size TSRMLS_DC) +{ + return PHPWRITE(buf, size); +} +/* }}} */ + +/* {{{ php_printf + */ +PHPAPI int php_printf(const char *format, ...) +{ + va_list args; + int ret; + char *buffer; + int size; + TSRMLS_FETCH(); + + va_start(args, format); + size = vspprintf(&buffer, 0, format, args); + ret = PHPWRITE(buffer, size); + efree(buffer); + va_end(args); + + return ret; +} +/* }}} */ + +/* {{{ php_verror */ +/* php_verror is called from php_error_docref functions. + * Its purpose is to unify error messages and automatically generate clickable + * html error messages if correcponding ini setting (html_errors) is activated. + * See: CODING_STANDARDS for details. + */ +PHPAPI void php_verror(const char *docref, const char *params, int type, const char *format, va_list args TSRMLS_DC) +{ + char *buffer = NULL, *docref_buf = NULL, *target = NULL; + char *docref_target = "", *docref_root = ""; + char *p; + int buffer_len = 0; + char *space = ""; + char *class_name = ""; + char *function; + int origin_len; + char *origin; + char *message; + int is_function = 0; + + /* get error text into buffer and escape for html if necessary */ + buffer_len = vspprintf(&buffer, 0, format, args); + if (PG(html_errors)) { + int len; + char *replace = php_escape_html_entities(buffer, buffer_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); + efree(buffer); + buffer = replace; + buffer_len = len; + } + + /* which function caused the problem if any at all */ + if (php_during_module_startup()) { + function = "PHP Startup"; + } else if (php_during_module_shutdown()) { + function = "PHP Shutdown"; + } else if (EG(current_execute_data) && + EG(current_execute_data)->opline && + EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL + ) { + switch (EG(current_execute_data)->opline->op2.u.constant.value.lval) { + case ZEND_EVAL: + function = "eval"; + is_function = 1; + break; + case ZEND_INCLUDE: + function = "include"; + is_function = 1; + break; + case ZEND_INCLUDE_ONCE: + function = "include_once"; + is_function = 1; + break; + case ZEND_REQUIRE: + function = "require"; + is_function = 1; + break; + case ZEND_REQUIRE_ONCE: + function = "require_once"; + is_function = 1; + break; + default: + function = "Unknown"; + } + } else { + function = get_active_function_name(TSRMLS_C); + if (!function || !strlen(function)) { + function = "Unknown"; + } else { + is_function = 1; + class_name = get_active_class_name(&space TSRMLS_CC); + } + } + + /* if we still have memory then format the origin */ + if (is_function) { + origin_len = spprintf(&origin, 0, "%s%s%s(%s)", class_name, space, function, params); + } else { + origin_len = spprintf(&origin, 0, "%s", function); + } + + if (PG(html_errors)) { + int len; + char *replace = php_escape_html_entities(origin, origin_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); + efree(origin); + origin = replace; + } + + /* origin and buffer available, so lets come up with the error message */ + if (docref && docref[0] == '#') { + docref_target = strchr(docref, '#'); + docref = NULL; + } + + /* no docref given but function is known (the default) */ + if (!docref && is_function) { + int doclen; + if (space[0] == '\0') { + doclen = spprintf(&docref_buf, 0, "function.%s", function); + } else { + doclen = spprintf(&docref_buf, 0, "%s.%s", class_name, function); + } + while((p = strchr(docref_buf, '_')) != NULL) { + *p = '-'; + } + docref = php_strtolower(docref_buf, doclen); + } + + /* we have a docref for a function AND + * - we show erroes in html mode OR + * - the user wants to see the links anyway + */ + if (docref && is_function && (PG(html_errors) || strlen(PG(docref_root)))) { + if (strncmp(docref, "http://", 7)) { + /* We don't have 'http://' so we use docref_root */ + + char *ref; /* temp copy for duplicated docref */ + + docref_root = PG(docref_root); + + ref = estrdup(docref); + if (docref_buf) { + efree(docref_buf); + } + docref_buf = ref; + /* strip of the target if any */ + p = strrchr(ref, '#'); + if (p) { + target = estrdup(p); + if (target) { + docref_target = target; + *p = '\0'; + } + } + /* add the extension if it is set in ini */ + if (PG(docref_ext) && strlen(PG(docref_ext))) { + spprintf(&docref_buf, 0, "%s%s", ref, PG(docref_ext)); + efree(ref); + } + docref = docref_buf; + } + /* display html formatted or only show the additional links */ + if (PG(html_errors)) { + spprintf(&message, 0, "%s [%s]: %s", origin, docref_root, docref, docref_target, docref, buffer); + } else { + spprintf(&message, 0, "%s [%s%s%s]: %s", origin, docref_root, docref, docref_target, buffer); + } + if (target) { + efree(target); + } + } else { + spprintf(&message, 0, "%s: %s", origin, buffer); + } + efree(origin); + if (docref_buf) { + efree(docref_buf); + } + + if (PG(track_errors) && module_initialized && + (!EG(user_error_handler) || !(EG(user_error_handler_error_reporting) & type))) { + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + if (EG(active_symbol_table)) { + zval *tmp; + ALLOC_INIT_ZVAL(tmp); + ZVAL_STRINGL(tmp, buffer, buffer_len, 1); + zend_hash_update(EG(active_symbol_table), "php_errormsg", sizeof("php_errormsg"), (void **) &tmp, sizeof(zval *), NULL); + } + } + efree(buffer); + + php_error(type, "%s", message); + efree(message); +} +/* }}} */ + +/* {{{ php_error_docref0 */ +/* See: CODING_STANDARDS for details. */ +PHPAPI void php_error_docref0(const char *docref TSRMLS_DC, int type, const char *format, ...) +{ + va_list args; + + va_start(args, format); + php_verror(docref, "", type, format, args TSRMLS_CC); + va_end(args); +} +/* }}} */ + +/* {{{ php_error_docref1 */ +/* See: CODING_STANDARDS for details. */ +PHPAPI void php_error_docref1(const char *docref TSRMLS_DC, const char *param1, int type, const char *format, ...) +{ + va_list args; + + va_start(args, format); + php_verror(docref, param1, type, format, args TSRMLS_CC); + va_end(args); +} +/* }}} */ + +/* {{{ php_error_docref2 */ +/* See: CODING_STANDARDS for details. */ +PHPAPI void php_error_docref2(const char *docref TSRMLS_DC, const char *param1, const char *param2, int type, const char *format, ...) +{ + char *params; + va_list args; + + spprintf(¶ms, 0, "%s,%s", param1, param2); + va_start(args, format); + php_verror(docref, params ? params : "...", type, format, args TSRMLS_CC); + va_end(args); + if (params) { + efree(params); + } +} +/* }}} */ + +/* {{{ php_html_puts */ +PHPAPI void php_html_puts(const char *str, uint size TSRMLS_DC) +{ + zend_html_puts(str, size TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_error_cb + extended error handling function */ +static void php_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) +{ + char *buffer; + int buffer_len, display; + TSRMLS_FETCH(); + + buffer_len = vspprintf(&buffer, PG(log_errors_max_len), format, args); + + /* check for repeated errors to be ignored */ + if (PG(ignore_repeated_errors) && PG(last_error_message)) { + /* no check for PG(last_error_file) is needed since it cannot + * be NULL if PG(last_error_message) is not NULL */ + if (strcmp(PG(last_error_message), buffer) + || (!PG(ignore_repeated_source) + && ((PG(last_error_lineno) != (int)error_lineno) + || strcmp(PG(last_error_file), error_filename)))) { + display = 1; + } else { + display = 0; + } + } else { + display = 1; + } + + /* store the error if it has changed */ + if (display) { + if (PG(last_error_message)) { + free(PG(last_error_message)); + } + if (PG(last_error_file)) { + free(PG(last_error_file)); + } + PG(last_error_type) = type; + PG(last_error_message) = strdup(buffer); + PG(last_error_file) = strdup(error_filename); + PG(last_error_lineno) = error_lineno; + } + + /* according to error handling mode, suppress error, throw exception or show it */ + if (EG(error_handling) != EH_NORMAL) { + switch (type) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + case E_PARSE: + /* fatal errors are real errors and cannot be made exceptions */ + break; + case E_STRICT: + case E_DEPRECATED: + case E_USER_DEPRECATED: + /* for the sake of BC to old damaged code */ + break; + case E_NOTICE: + case E_USER_NOTICE: + /* notices are no errors and are not treated as such like E_WARNINGS */ + break; + default: + /* throw an exception if we are in EH_THROW mode + * but DO NOT overwrite a pending exception + */ + if (EG(error_handling) == EH_THROW && !EG(exception)) { + zend_throw_error_exception(EG(exception_class), buffer, 0, type TSRMLS_CC); + } + efree(buffer); + return; + } + } + + /* display/log the error if necessary */ + if (display && (EG(error_reporting) & type || (type & E_CORE)) + && (PG(log_errors) || PG(display_errors) || (!module_initialized))) { + char *error_type_str; + + switch (type) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + error_type_str = "Fatal error"; + break; + case E_RECOVERABLE_ERROR: + error_type_str = "Catchable fatal error"; + break; + case E_WARNING: + case E_CORE_WARNING: + case E_COMPILE_WARNING: + case E_USER_WARNING: + error_type_str = "Warning"; + break; + case E_PARSE: + error_type_str = "Parse error"; + break; + case E_NOTICE: + case E_USER_NOTICE: + error_type_str = "Notice"; + break; + case E_STRICT: + error_type_str = "Strict Standards"; + break; + case E_DEPRECATED: + case E_USER_DEPRECATED: + error_type_str = "Deprecated"; + break; + default: + error_type_str = "Unknown error"; + break; + } + + if (!module_initialized || PG(log_errors)) { + char *log_buffer; +#ifdef PHP_WIN32 + if ((type == E_CORE_ERROR || type == E_CORE_WARNING) && PG(display_startup_errors)) { + MessageBox(NULL, buffer, error_type_str, MB_OK|ZEND_SERVICE_MB_STYLE); + } +#endif + spprintf(&log_buffer, 0, "PHP %s: %s in %s on line %d", error_type_str, buffer, error_filename, error_lineno); + php_log_err(log_buffer TSRMLS_CC); + efree(log_buffer); + } + if (PG(display_errors) + && ((module_initialized && !PG(during_request_startup)) + || (PG(display_startup_errors) + && (OG(php_body_write)==php_default_output_func || OG(php_body_write)==php_ub_body_write_no_header || OG(php_body_write)==php_ub_body_write) + ) + ) + ) { + + if (PG(xmlrpc_errors)) { + php_printf("faultCode%ldfaultString%s:%s in %s on line %d", PG(xmlrpc_error_number), error_type_str, buffer, error_filename, error_lineno); + } else { + char *prepend_string = INI_STR("error_prepend_string"); + char *append_string = INI_STR("error_append_string"); + + if (PG(html_errors)) { + if (type == E_ERROR) { + int len; + char *buf = php_escape_html_entities(buffer, buffer_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); + php_printf("%s
\n%s: %s in %s on line %d
\n%s", STR_PRINT(prepend_string), error_type_str, buf, error_filename, error_lineno, STR_PRINT(append_string)); + efree(buf); + } else { + php_printf("%s
\n%s: %s in %s on line %d
\n%s", STR_PRINT(prepend_string), error_type_str, buffer, error_filename, error_lineno, STR_PRINT(append_string)); + } + } else { + /* Write CLI/CGI errors to stderr if display_errors = "stderr" */ + if ((!strcmp(sapi_module.name, "cli") || !strcmp(sapi_module.name, "cgi")) && + PG(display_errors) == PHP_DISPLAY_ERRORS_STDERR + ) { + fprintf(stderr, "%s: %s in %s on line %d\n", error_type_str, buffer, error_filename, error_lineno); + } else { + php_printf("%s\n%s: %s in %s on line %d\n%s", STR_PRINT(prepend_string), error_type_str, buffer, error_filename, error_lineno, STR_PRINT(append_string)); + } + } + } + } +#if ZEND_DEBUG + if (PG(report_zend_debug)) { + zend_bool trigger_break; + + switch (type) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + trigger_break=1; + break; + default: + trigger_break=0; + break; + } + zend_output_debug_string(trigger_break, "%s(%d) : %s - %s", error_filename, error_lineno, error_type_str, buffer); + } +#endif + } + + /* Bail out if we can't recover */ + switch (type) { + case E_CORE_ERROR: + if(!module_initialized) { + /* bad error in module startup - no way we can live with this */ + exit(-2); + } + /* no break - intentionally */ + case E_ERROR: + case E_RECOVERABLE_ERROR: + case E_PARSE: + case E_COMPILE_ERROR: + case E_USER_ERROR: + EG(exit_status) = 255; + if (module_initialized) { + if (!PG(display_errors) && + !SG(headers_sent) && + SG(sapi_headers).http_response_code == 200 + ) { + sapi_header_line ctr = {0}; + + ctr.line = "HTTP/1.0 500 Internal Server Error"; + ctr.line_len = strlen(ctr.line); + sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); + } + /* the parser would return 1 (failure), we can bail out nicely */ + if (type != E_PARSE) { + /* restore memory limit */ + zend_set_memory_limit(PG(memory_limit)); + efree(buffer); + zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC); + zend_bailout(); + return; + } + } + break; + } + + /* Log if necessary */ + if (!display) { + efree(buffer); + return; + } + + if (PG(track_errors) && module_initialized) { + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + if (EG(active_symbol_table)) { + zval *tmp; + ALLOC_INIT_ZVAL(tmp); + ZVAL_STRINGL(tmp, buffer, buffer_len, 1); + zend_hash_update(EG(active_symbol_table), "php_errormsg", sizeof("php_errormsg"), (void **) & tmp, sizeof(zval *), NULL); + } + } + + efree(buffer); +} +/* }}} */ + +/* {{{ proto bool set_time_limit(int seconds) + Sets the maximum time a script can run */ +PHP_FUNCTION(set_time_limit) +{ + long new_timeout; + char *new_timeout_str; + int new_timeout_strlen; + + if (PG(safe_mode)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot set time limit in safe mode"); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &new_timeout) == FAILURE) { + return; + } + + new_timeout_strlen = zend_spprintf(&new_timeout_str, 0, "%ld", new_timeout); + + if (zend_alter_ini_entry_ex("max_execution_time", sizeof("max_execution_time"), new_timeout_str, new_timeout_strlen, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == SUCCESS) { + RETVAL_TRUE; + } else { + RETVAL_FALSE; + } + efree(new_timeout_str); +} +/* }}} */ + +/* {{{ php_fopen_wrapper_for_zend + */ +static FILE *php_fopen_wrapper_for_zend(const char *filename, char **opened_path TSRMLS_DC) +{ + return php_stream_open_wrapper_as_file((char *)filename, "rb", ENFORCE_SAFE_MODE|USE_PATH|IGNORE_URL_WIN|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE, opened_path); +} +/* }}} */ + +static void php_zend_stream_closer(void *handle TSRMLS_DC) /* {{{ */ +{ + php_stream_close((php_stream*)handle); +} +/* }}} */ + +static void php_zend_stream_mmap_closer(void *handle TSRMLS_DC) /* {{{ */ +{ + php_stream_mmap_unmap((php_stream*)handle); + php_zend_stream_closer(handle TSRMLS_CC); +} +/* }}} */ + +static size_t php_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */ +{ + php_stream_statbuf ssb; + if (php_stream_stat((php_stream*)handle, &ssb) == 0) { + return ssb.sb.st_size; + } + return 0; +} +/* }}} */ + +static int php_stream_open_for_zend(const char *filename, zend_file_handle *handle TSRMLS_DC) /* {{{ */ +{ + return php_stream_open_for_zend_ex(filename, handle, ENFORCE_SAFE_MODE|USE_PATH|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); +} +/* }}} */ + +PHPAPI int php_stream_open_for_zend_ex(const char *filename, zend_file_handle *handle, int mode TSRMLS_DC) /* {{{ */ +{ + char *p; + size_t len, mapped_len; + php_stream *stream = php_stream_open_wrapper((char *)filename, "rb", mode, &handle->opened_path); + + if (stream) { + handle->filename = (char*)filename; + handle->free_filename = 0; + handle->handle.stream.handle = stream; + handle->handle.stream.reader = (zend_stream_reader_t)_php_stream_read; + handle->handle.stream.fsizer = php_zend_stream_fsizer; + handle->handle.stream.isatty = 0; + /* can we mmap immeadiately? */ + memset(&handle->handle.stream.mmap, 0, sizeof(handle->handle.stream.mmap)); + len = php_zend_stream_fsizer(stream TSRMLS_CC); + if (len != 0 + && ((len - 1) % PAGE_SIZE) <= PAGE_SIZE - ZEND_MMAP_AHEAD + && php_stream_mmap_possible(stream) + && (p = php_stream_mmap_range(stream, 0, len, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped_len)) != NULL) { + handle->handle.stream.closer = php_zend_stream_mmap_closer; + handle->handle.stream.mmap.buf = p; + handle->handle.stream.mmap.len = mapped_len; + handle->type = ZEND_HANDLE_MAPPED; + } else { + handle->handle.stream.closer = php_zend_stream_closer; + handle->type = ZEND_HANDLE_STREAM; + } + /* suppress warning if this stream is not explicitly closed */ + php_stream_auto_cleanup(stream); + + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +static char *php_resolve_path_for_zend(const char *filename, int filename_len TSRMLS_DC) /* {{{ */ +{ + return php_resolve_path(filename, filename_len, PG(include_path) TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_get_configuration_directive_for_zend + */ +static int php_get_configuration_directive_for_zend(const char *name, uint name_length, zval *contents) +{ + zval *retval = cfg_get_entry(name, name_length); + + if (retval) { + *contents = *retval; + return SUCCESS; + } else { + return FAILURE; + } +} +/* }}} */ + +/* {{{ php_message_handler_for_zend + */ +static void php_message_handler_for_zend(long message, void *data TSRMLS_DC) +{ + switch (message) { + case ZMSG_FAILED_INCLUDE_FOPEN: + php_error_docref("function.include" TSRMLS_CC, E_WARNING, "Failed opening '%s' for inclusion (include_path='%s')", php_strip_url_passwd((char *) data), STR_PRINT(PG(include_path))); + break; + case ZMSG_FAILED_REQUIRE_FOPEN: + php_error_docref("function.require" TSRMLS_CC, E_COMPILE_ERROR, "Failed opening required '%s' (include_path='%s')", php_strip_url_passwd((char *) data), STR_PRINT(PG(include_path))); + break; + case ZMSG_FAILED_HIGHLIGHT_FOPEN: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed opening '%s' for highlighting", php_strip_url_passwd((char *) data)); + break; + case ZMSG_MEMORY_LEAK_DETECTED: + case ZMSG_MEMORY_LEAK_REPEATED: +#if ZEND_DEBUG + if (EG(error_reporting) & E_WARNING) { + char memory_leak_buf[1024]; + + if (message==ZMSG_MEMORY_LEAK_DETECTED) { + zend_leak_info *t = (zend_leak_info *) data; + + snprintf(memory_leak_buf, 512, "%s(%d) : Freeing 0x%.8lX (%zu bytes), script=%s\n", t->filename, t->lineno, (zend_uintptr_t)t->addr, t->size, SAFE_FILENAME(SG(request_info).path_translated)); + if (t->orig_filename) { + char relay_buf[512]; + + snprintf(relay_buf, 512, "%s(%d) : Actual location (location was relayed)\n", t->orig_filename, t->orig_lineno); + strlcat(memory_leak_buf, relay_buf, sizeof(memory_leak_buf)); + } + } else { + unsigned long leak_count = (zend_uintptr_t) data; + + snprintf(memory_leak_buf, 512, "Last leak repeated %ld time%s\n", leak_count, (leak_count>1?"s":"")); + } +# if defined(PHP_WIN32) + OutputDebugString(memory_leak_buf); +# else + fprintf(stderr, "%s", memory_leak_buf); +# endif + } +#endif + break; + case ZMSG_MEMORY_LEAKS_GRAND_TOTAL: +#if ZEND_DEBUG + if (EG(error_reporting) & E_WARNING) { + char memory_leak_buf[512]; + + snprintf(memory_leak_buf, 512, "=== Total %d memory leaks detected ===\n", *((zend_uint *) data)); +# if defined(PHP_WIN32) + OutputDebugString(memory_leak_buf); +# else + fprintf(stderr, "%s", memory_leak_buf); +# endif + } +#endif + break; + case ZMSG_LOG_SCRIPT_NAME: { + struct tm *ta, tmbuf; + time_t curtime; + char *datetime_str, asctimebuf[52]; + char memory_leak_buf[4096]; + + time(&curtime); + ta = php_localtime_r(&curtime, &tmbuf); + datetime_str = php_asctime_r(ta, asctimebuf); + if (datetime_str) { + datetime_str[strlen(datetime_str)-1]=0; /* get rid of the trailing newline */ + snprintf(memory_leak_buf, sizeof(memory_leak_buf), "[%s] Script: '%s'\n", datetime_str, SAFE_FILENAME(SG(request_info).path_translated)); + } else { + snprintf(memory_leak_buf, sizeof(memory_leak_buf), "[null] Script: '%s'\n", SAFE_FILENAME(SG(request_info).path_translated)); + } +# if defined(PHP_WIN32) + OutputDebugString(memory_leak_buf); +# else + fprintf(stderr, "%s", memory_leak_buf); +# endif + } + break; + } +} +/* }}} */ + + +void php_on_timeout(int seconds TSRMLS_DC) +{ + PG(connection_status) |= PHP_CONNECTION_TIMEOUT; + zend_set_timeout(EG(timeout_seconds), 1); + if(PG(exit_on_timeout)) sapi_terminate_process(TSRMLS_C); +} + +#if PHP_SIGCHILD +/* {{{ sigchld_handler + */ +static void sigchld_handler(int apar) +{ + while (waitpid(-1, NULL, WNOHANG) > 0); + signal(SIGCHLD, sigchld_handler); +} +/* }}} */ +#endif + +/* {{{ php_start_sapi() + */ +static int php_start_sapi(TSRMLS_D) +{ + int retval = SUCCESS; + + if(!SG(sapi_started)) { + zend_try { + PG(during_request_startup) = 1; + + /* initialize global variables */ + PG(modules_activated) = 0; + PG(header_is_being_sent) = 0; + PG(connection_status) = PHP_CONNECTION_NORMAL; + + zend_activate(TSRMLS_C); + zend_set_timeout(EG(timeout_seconds), 1); + zend_activate_modules(TSRMLS_C); + PG(modules_activated)=1; + } zend_catch { + retval = FAILURE; + } zend_end_try(); + + SG(sapi_started) = 1; + } + return retval; +} + +/* }}} */ + +/* {{{ php_request_startup + */ +#ifndef APACHE_HOOKS +int php_request_startup(TSRMLS_D) +{ + int retval = SUCCESS; + +#ifdef PHP_WIN32 + PG(com_initialized) = 0; +#endif + +#if PHP_SIGCHILD + signal(SIGCHLD, sigchld_handler); +#endif + + zend_try { + PG(during_request_startup) = 1; + + php_output_activate(TSRMLS_C); + + /* initialize global variables */ + PG(modules_activated) = 0; + PG(header_is_being_sent) = 0; + PG(connection_status) = PHP_CONNECTION_NORMAL; + PG(in_user_include) = 0; + + zend_activate(TSRMLS_C); + sapi_activate(TSRMLS_C); + + if (PG(max_input_time) == -1) { + zend_set_timeout(EG(timeout_seconds), 1); + } else { + zend_set_timeout(PG(max_input_time), 1); + } + + /* Disable realpath cache if safe_mode or open_basedir are set */ + if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) { + CWDG(realpath_cache_size_limit) = 0; + } + + if (PG(expose_php)) { + sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); + } + + if (PG(output_handler) && PG(output_handler)[0]) { + php_start_ob_buffer_named(PG(output_handler), 0, 1 TSRMLS_CC); + } else if (PG(output_buffering)) { + if (PG(output_buffering)>1) { + php_start_ob_buffer(NULL, PG(output_buffering), 1 TSRMLS_CC); + } else { + php_start_ob_buffer(NULL, 0, 1 TSRMLS_CC); + } + } else if (PG(implicit_flush)) { + php_start_implicit_flush(TSRMLS_C); + } + + /* We turn this off in php_execute_script() */ + /* PG(during_request_startup) = 0; */ + + php_hash_environment(TSRMLS_C); + zend_activate_modules(TSRMLS_C); + PG(modules_activated)=1; + } zend_catch { + retval = FAILURE; + } zend_end_try(); + + SG(sapi_started) = 1; + + return retval; +} +# else +int php_request_startup(TSRMLS_D) +{ + int retval = SUCCESS; + +#if PHP_SIGCHILD + signal(SIGCHLD, sigchld_handler); +#endif + + if (php_start_sapi() == FAILURE) { + return FAILURE; + } + + php_output_activate(TSRMLS_C); + sapi_activate(TSRMLS_C); + php_hash_environment(TSRMLS_C); + + zend_try { + PG(during_request_startup) = 1; + php_output_activate(TSRMLS_C); + if (PG(expose_php)) { + sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); + } + } zend_catch { + retval = FAILURE; + } zend_end_try(); + + return retval; +} +# endif +/* }}} */ + +/* {{{ php_request_startup_for_hook + */ +int php_request_startup_for_hook(TSRMLS_D) +{ + int retval = SUCCESS; + +#if PHP_SIGCHLD + signal(SIGCHLD, sigchld_handler); +#endif + + if (php_start_sapi(TSRMLS_C) == FAILURE) { + return FAILURE; + } + + php_output_activate(TSRMLS_C); + sapi_activate_headers_only(TSRMLS_C); + php_hash_environment(TSRMLS_C); + + return retval; +} +/* }}} */ + +/* {{{ php_request_shutdown_for_exec + */ +void php_request_shutdown_for_exec(void *dummy) +{ + TSRMLS_FETCH(); + + /* used to close fd's in the 3..255 range here, but it's problematic + */ + shutdown_memory_manager(1, 1 TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_request_shutdown_for_hook + */ +void php_request_shutdown_for_hook(void *dummy) +{ + TSRMLS_FETCH(); + + if (PG(modules_activated)) zend_try { + php_call_shutdown_functions(TSRMLS_C); + } zend_end_try(); + + if (PG(modules_activated)) { + zend_deactivate_modules(TSRMLS_C); + php_free_shutdown_functions(TSRMLS_C); + } + + zend_try { + int i; + + for (i = 0; i < NUM_TRACK_VARS; i++) { + if (PG(http_globals)[i]) { + zval_ptr_dtor(&PG(http_globals)[i]); + } + } + } zend_end_try(); + + zend_deactivate(TSRMLS_C); + + zend_try { + sapi_deactivate(TSRMLS_C); + } zend_end_try(); + + zend_try { + php_shutdown_stream_hashes(TSRMLS_C); + } zend_end_try(); + + zend_try { + shutdown_memory_manager(CG(unclean_shutdown), 0 TSRMLS_CC); + } zend_end_try(); + + zend_try { + zend_unset_timeout(TSRMLS_C); + } zend_end_try(); +} + +/* }}} */ + +/* {{{ php_request_shutdown + */ +void php_request_shutdown(void *dummy) +{ + zend_bool report_memleaks; + TSRMLS_FETCH(); + + report_memleaks = PG(report_memleaks); + + /* EG(opline_ptr) points into nirvana and therefore cannot be safely accessed + * inside zend_executor callback functions. + */ + EG(opline_ptr) = NULL; + EG(active_op_array) = NULL; + + php_deactivate_ticks(TSRMLS_C); + + /* 1. Call all possible shutdown functions registered with register_shutdown_function() */ + if (PG(modules_activated)) zend_try { + php_call_shutdown_functions(TSRMLS_C); + } zend_end_try(); + + /* 2. Call all possible __destruct() functions */ + zend_try { + zend_call_destructors(TSRMLS_C); + } zend_end_try(); + + /* 3. Flush all output buffers */ + zend_try { + zend_bool send_buffer = SG(request_info).headers_only ? 0 : 1; + if (CG(unclean_shutdown) && PG(last_error_type) == E_ERROR && + OG(ob_nesting_level) && !OG(active_ob_buffer).chunk_size && PG(memory_limit) < zend_memory_usage(1 TSRMLS_CC)) { + send_buffer = 0; + } + php_end_ob_buffers(send_buffer TSRMLS_CC); + } zend_end_try(); + + /* 4. Send the set HTTP headers (note: This must be done AFTER php_end_ob_buffers() !!) */ + zend_try { + sapi_send_headers(TSRMLS_C); + } zend_end_try(); + + /* 5. Call all extensions RSHUTDOWN functions */ + if (PG(modules_activated)) { + zend_deactivate_modules(TSRMLS_C); + php_free_shutdown_functions(TSRMLS_C); + } + + /* 6. Destroy super-globals */ + zend_try { + int i; + + for (i=0; ilast_error_message) { + free(core_globals->last_error_message); + } + if (core_globals->last_error_file) { + free(core_globals->last_error_file); + } + if (core_globals->disable_functions) { + free(core_globals->disable_functions); + } + if (core_globals->disable_classes) { + free(core_globals->disable_classes); + } + + php_shutdown_ticks(TSRMLS_C); +} +/* }}} */ + +PHP_MINFO_FUNCTION(php_core) { /* {{{ */ + php_info_print_table_start(); + php_info_print_table_row(2, "PHP Version", PHP_VERSION); + php_info_print_table_end(); + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +/* {{{ php_register_extensions + */ +int php_register_extensions(zend_module_entry **ptr, int count TSRMLS_DC) +{ + zend_module_entry **end = ptr + count; + + while (ptr < end) { + if (*ptr) { + if (zend_register_internal_module(*ptr TSRMLS_CC)==NULL) { + return FAILURE; + } + } + ptr++; + } + return SUCCESS; +} +/* }}} */ + +#if defined(PHP_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1400) +static _invalid_parameter_handler old_invalid_parameter_handler; + +void dummy_invalid_parameter_handler( + const wchar_t *expression, + const wchar_t *function, + const wchar_t *file, + unsigned int line, + uintptr_t pEwserved) +{ + static int called = 0; + char buf[1024]; + int len; + + if (!called) { + called = 1; + if (function) { + if (file) { + len = _snprintf(buf, sizeof(buf)-1, "Invalid parameter detected in CRT function '%ws' (%ws:%d)", function, file, line); + } else { + len = _snprintf(buf, sizeof(buf)-1, "Invalid parameter detected in CRT function '%ws'", function); + } + } else { + len = _snprintf(buf, sizeof(buf)-1, "Invalid CRT parameters detected"); + } + zend_error(E_WARNING, "%s", buf); + called = 0; + } +} +#endif + +/* {{{ php_module_startup + */ +int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additional_modules) +{ + zend_utility_functions zuf; + zend_utility_values zuv; + int module_number=0; /* for REGISTER_INI_ENTRIES() */ + char *php_os; + zend_module_entry *module; +#ifdef ZTS + zend_executor_globals *executor_globals; + void ***tsrm_ls; + php_core_globals *core_globals; +#endif +#if defined(PHP_WIN32) || (defined(NETWARE) && defined(USE_WINSOCK)) + WORD wVersionRequested = MAKEWORD(2, 0); + WSADATA wsaData; +#endif +#ifdef PHP_WIN32 + DWORD dwVersion = GetVersion(); + /* Get build numbers for Windows NT or Win95 */ + if (dwVersion < 0x80000000){ + php_os="WINNT"; + } else { + php_os="WIN32"; + } +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + old_invalid_parameter_handler = + _set_invalid_parameter_handler(dummy_invalid_parameter_handler); + if (old_invalid_parameter_handler != NULL) { + _set_invalid_parameter_handler(old_invalid_parameter_handler); + } + + /* Disable the message box for assertions.*/ + _CrtSetReportMode(_CRT_ASSERT, 0); +#endif +#else + php_os=PHP_OS; +#endif + +#ifdef ZTS + tsrm_ls = ts_resource(0); +#endif + + module_shutdown = 0; + module_startup = 1; + sapi_initialize_empty_request(TSRMLS_C); + sapi_activate(TSRMLS_C); + + if (module_initialized) { + return SUCCESS; + } + + sapi_module = *sf; + + php_output_startup(); + + zuf.error_function = php_error_cb; + zuf.printf_function = php_printf; + zuf.write_function = php_body_write_wrapper; + zuf.fopen_function = php_fopen_wrapper_for_zend; + zuf.message_handler = php_message_handler_for_zend; + zuf.block_interruptions = sapi_module.block_interruptions; + zuf.unblock_interruptions = sapi_module.unblock_interruptions; + zuf.get_configuration_directive = php_get_configuration_directive_for_zend; + zuf.ticks_function = php_run_ticks; + zuf.on_timeout = php_on_timeout; + zuf.stream_open_function = php_stream_open_for_zend; + zuf.vspprintf_function = vspprintf; + zuf.getenv_function = sapi_getenv; + zuf.resolve_path_function = php_resolve_path_for_zend; + zend_startup(&zuf, NULL TSRMLS_CC); + +#ifdef ZTS + executor_globals = ts_resource(executor_globals_id); + ts_allocate_id(&core_globals_id, sizeof(php_core_globals), (ts_allocate_ctor) core_globals_ctor, (ts_allocate_dtor) core_globals_dtor); + core_globals = ts_resource(core_globals_id); +#ifdef PHP_WIN32 + ts_allocate_id(&php_win32_core_globals_id, sizeof(php_win32_core_globals), (ts_allocate_ctor) php_win32_core_globals_ctor, (ts_allocate_dtor) php_win32_core_globals_dtor); +#endif +#else + php_startup_ticks(TSRMLS_C); +#endif + gc_globals_ctor(TSRMLS_C); + +#ifdef PHP_WIN32 + { + OSVERSIONINFOEX *osvi = &EG(windows_version_info); + + ZeroMemory(osvi, sizeof(OSVERSIONINFOEX)); + osvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if( !GetVersionEx((OSVERSIONINFO *) osvi)) { + php_printf("\nGetVersionEx unusable. %d\n", GetLastError()); + return FAILURE; + } + } +#endif + EG(bailout) = NULL; + EG(error_reporting) = E_ALL & ~E_NOTICE; + + PG(header_is_being_sent) = 0; + SG(request_info).headers_only = 0; + SG(request_info).argv0 = NULL; + SG(request_info).argc=0; + SG(request_info).argv=(char **)NULL; + PG(connection_status) = PHP_CONNECTION_NORMAL; + PG(during_request_startup) = 0; + PG(last_error_message) = NULL; + PG(last_error_file) = NULL; + PG(last_error_lineno) = 0; + EG(error_handling) = EH_NORMAL; + EG(exception_class) = NULL; + PG(disable_functions) = NULL; + PG(disable_classes) = NULL; + +#if HAVE_SETLOCALE + setlocale(LC_CTYPE, ""); + zend_update_current_locale(); +#endif + +#if HAVE_TZSET + tzset(); +#endif + +#if defined(PHP_WIN32) || (defined(NETWARE) && defined(USE_WINSOCK)) + /* start up winsock services */ + if (WSAStartup(wVersionRequested, &wsaData) != 0) { + php_printf("\nwinsock.dll unusable. %d\n", WSAGetLastError()); + return FAILURE; + } +#endif + + le_index_ptr = zend_register_list_destructors_ex(NULL, NULL, "index pointer", 0); + + /* Register constants */ + REGISTER_MAIN_STRINGL_CONSTANT("PHP_VERSION", PHP_VERSION, sizeof(PHP_VERSION)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_MAJOR_VERSION", PHP_MAJOR_VERSION, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_MINOR_VERSION", PHP_MINOR_VERSION, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_RELEASE_VERSION", PHP_RELEASE_VERSION, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_EXTRA_VERSION", PHP_EXTRA_VERSION, sizeof(PHP_EXTRA_VERSION) - 1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_VERSION_ID", PHP_VERSION_ID, CONST_PERSISTENT | CONST_CS); +#ifdef ZTS + REGISTER_MAIN_LONG_CONSTANT("PHP_ZTS", 1, CONST_PERSISTENT | CONST_CS); +#else + REGISTER_MAIN_LONG_CONSTANT("PHP_ZTS", 0, CONST_PERSISTENT | CONST_CS); +#endif + REGISTER_MAIN_LONG_CONSTANT("PHP_DEBUG", PHP_DEBUG, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_OS", php_os, strlen(php_os), CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_SAPI", sapi_module.name, strlen(sapi_module.name), CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("DEFAULT_INCLUDE_PATH", PHP_INCLUDE_PATH, sizeof(PHP_INCLUDE_PATH)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PEAR_INSTALL_DIR", PEAR_INSTALLDIR, sizeof(PEAR_INSTALLDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PEAR_EXTENSION_DIR", PHP_EXTENSION_DIR, sizeof(PHP_EXTENSION_DIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_EXTENSION_DIR", PHP_EXTENSION_DIR, sizeof(PHP_EXTENSION_DIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_PREFIX", PHP_PREFIX, sizeof(PHP_PREFIX)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_BINDIR", PHP_BINDIR, sizeof(PHP_BINDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_LIBDIR", PHP_LIBDIR, sizeof(PHP_LIBDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_DATADIR", PHP_DATADIR, sizeof(PHP_DATADIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_SYSCONFDIR", PHP_SYSCONFDIR, sizeof(PHP_SYSCONFDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_LOCALSTATEDIR", PHP_LOCALSTATEDIR, sizeof(PHP_LOCALSTATEDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_PATH", PHP_CONFIG_FILE_PATH, strlen(PHP_CONFIG_FILE_PATH), CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_SCAN_DIR", PHP_CONFIG_FILE_SCAN_DIR, sizeof(PHP_CONFIG_FILE_SCAN_DIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_SHLIB_SUFFIX", PHP_SHLIB_SUFFIX, sizeof(PHP_SHLIB_SUFFIX)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_EOL", PHP_EOL, sizeof(PHP_EOL)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_MAXPATHLEN", MAXPATHLEN, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_INT_MAX", LONG_MAX, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_INT_SIZE", sizeof(long), CONST_PERSISTENT | CONST_CS); + +#ifdef PHP_WIN32 + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_MAJOR", EG(windows_version_info).dwMajorVersion, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_MINOR", EG(windows_version_info).dwMinorVersion, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_BUILD", EG(windows_version_info).dwBuildNumber, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_PLATFORM", EG(windows_version_info).dwPlatformId, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SP_MAJOR", EG(windows_version_info).wServicePackMajor, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SP_MINOR", EG(windows_version_info).wServicePackMinor, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SUITEMASK", EG(windows_version_info).wSuiteMask, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_PRODUCTTYPE", EG(windows_version_info).wProductType, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_DOMAIN_CONTROLLER", VER_NT_DOMAIN_CONTROLLER, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_SERVER", VER_NT_SERVER, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_WORKSTATION", VER_NT_WORKSTATION, CONST_PERSISTENT | CONST_CS); +#endif + + php_output_register_constants(TSRMLS_C); + php_rfc1867_register_constants(TSRMLS_C); + + /* this will read in php.ini, set up the configuration parameters, + load zend extensions and register php function extensions + to be loaded later */ + if (php_init_config(TSRMLS_C) == FAILURE) { + return FAILURE; + } + + /* Check for deprecated directives */ + { + static const char *directives[] = { + "define_syslog_variables", + "register_globals", + "register_long_arrays", + "safe_mode", + "magic_quotes_gpc", + "magic_quotes_runtime", + "magic_quotes_sybase", + NULL}; + const char **p = directives; + long val; + + while (*p) { + if (cfg_get_long((char*)*p, &val) == SUCCESS && val) { + zend_error(E_WARNING, "Directive '%s' is deprecated in PHP 5.3 and greater", *p); + } + ++p; + } + + /* This is not too nice, but since its the only one theres no need for extra stuff here */ + if (cfg_get_long("zend.ze1_compatibility_mode", &val) == SUCCESS && val) { + zend_error(E_ERROR, "zend.ze1_compatibility_mode is no longer supported in PHP 5.3 and greater"); + } + } + + /* Register PHP core ini entries */ + REGISTER_INI_ENTRIES(); + + /* Register Zend ini entries */ + zend_register_standard_ini_entries(TSRMLS_C); + + /* Disable realpath cache if safe_mode or open_basedir are set */ + if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) { + CWDG(realpath_cache_size_limit) = 0; + } + + /* initialize stream wrappers registry + * (this uses configuration parameters from php.ini) + */ + if (php_init_stream_wrappers(module_number TSRMLS_CC) == FAILURE) { + php_printf("PHP: Unable to initialize stream url wrappers.\n"); + return FAILURE; + } + + /* initialize registry for images to be used in phpinfo() + (this uses configuration parameters from php.ini) + */ + if (php_init_info_logos() == FAILURE) { + php_printf("PHP: Unable to initialize info phpinfo logos.\n"); + return FAILURE; + } + + zuv.html_errors = 1; + zuv.import_use_extension = ".php"; + php_startup_auto_globals(TSRMLS_C); + zend_set_utility_values(&zuv); + php_startup_sapi_content_types(TSRMLS_C); + + /* startup extensions staticly compiled in */ + if (php_register_internal_extensions_func(TSRMLS_C) == FAILURE) { + php_printf("Unable to start builtin modules\n"); + return FAILURE; + } + + /* start additional PHP extensions */ + php_register_extensions(&additional_modules, num_additional_modules TSRMLS_CC); + + /* load and startup extensions compiled as shared objects (aka DLLs) + as requested by php.ini entries + theese are loaded after initialization of internal extensions + as extensions *might* rely on things from ext/standard + which is always an internal extension and to be initialized + ahead of all other internals + */ + php_ini_register_extensions(TSRMLS_C); + zend_startup_modules(TSRMLS_C); + + /* disable certain classes and functions as requested by php.ini */ + php_disable_functions(TSRMLS_C); + php_disable_classes(TSRMLS_C); + + /* start Zend extensions */ + zend_startup_extensions(); + + /* register additional functions */ + if (sapi_module.additional_functions) { + if (zend_hash_find(&module_registry, "standard", sizeof("standard"), (void**)&module)==SUCCESS) { + EG(current_module) = module; + zend_register_functions(NULL, sapi_module.additional_functions, NULL, MODULE_PERSISTENT TSRMLS_CC); + EG(current_module) = NULL; + } + } + + /* make core report what it should */ + if (zend_hash_find(&module_registry, "core", sizeof("core"), (void**)&module)==SUCCESS) { + module->version = PHP_VERSION; + module->info_func = PHP_MINFO(php_core); + } + +#ifdef PHP_WIN32 + /* Disable incompatible functions for the running platform */ + if (php_win32_disable_functions() == FAILURE) { + php_printf("Unable to disable unsupported functions\n"); + return FAILURE; + } +#endif + +#ifdef ZTS + zend_post_startup(TSRMLS_C); +#endif + + module_initialized = 1; + sapi_deactivate(TSRMLS_C); + module_startup = 0; + + shutdown_memory_manager(1, 0 TSRMLS_CC); + + /* we're done */ + return SUCCESS; +} +/* }}} */ + +void php_module_shutdown_for_exec(void) +{ + /* used to close fd's in the range 3.255 here, but it's problematic */ +} + +/* {{{ php_module_shutdown_wrapper + */ +int php_module_shutdown_wrapper(sapi_module_struct *sapi_globals) +{ + TSRMLS_FETCH(); + php_module_shutdown(TSRMLS_C); + return SUCCESS; +} +/* }}} */ + +/* {{{ php_module_shutdown + */ +void php_module_shutdown(TSRMLS_D) +{ + int module_number=0; /* for UNREGISTER_INI_ENTRIES() */ + + module_shutdown = 1; + + if (!module_initialized) { + return; + } + +#ifdef ZTS + ts_free_worker_threads(); +#endif + +#if defined(PHP_WIN32) || (defined(NETWARE) && defined(USE_WINSOCK)) + /*close winsock */ + WSACleanup(); +#endif + + sapi_flush(TSRMLS_C); + + zend_shutdown(TSRMLS_C); + + /* Destroys filter & transport registries too */ + php_shutdown_stream_wrappers(module_number TSRMLS_CC); + + php_shutdown_info_logos(); + UNREGISTER_INI_ENTRIES(); + + /* close down the ini config */ + php_shutdown_config(); + +#ifndef ZTS + zend_ini_shutdown(TSRMLS_C); + shutdown_memory_manager(CG(unclean_shutdown), 1 TSRMLS_CC); + core_globals_dtor(&core_globals TSRMLS_CC); + gc_globals_dtor(TSRMLS_C); +#else + zend_ini_global_shutdown(TSRMLS_C); + ts_free_id(core_globals_id); +#endif + + php_shutdown_temporary_directory(); + + module_initialized = 0; + +#if defined(PHP_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1400) + if (old_invalid_parameter_handler == NULL) { + _set_invalid_parameter_handler(old_invalid_parameter_handler); + } +#endif +} +/* }}} */ + +/* {{{ php_execute_script + */ +PHPAPI int php_execute_script(zend_file_handle *primary_file TSRMLS_DC) +{ + zend_file_handle *prepend_file_p, *append_file_p; + zend_file_handle prepend_file = {0}, append_file = {0}; +#if HAVE_BROKEN_GETCWD + int old_cwd_fd = -1; +#else + char *old_cwd; + ALLOCA_FLAG(use_heap) +#endif + int retval = 0; + + EG(exit_status) = 0; + if (php_handle_special_queries(TSRMLS_C)) { + zend_file_handle_dtor(primary_file TSRMLS_CC); + return 0; + } +#ifndef HAVE_BROKEN_GETCWD +# define OLD_CWD_SIZE 4096 + old_cwd = do_alloca(OLD_CWD_SIZE, use_heap); + old_cwd[0] = '\0'; +#endif + + zend_try { + char realfile[MAXPATHLEN]; + +#ifdef PHP_WIN32 + UpdateIniFromRegistry(primary_file->filename TSRMLS_CC); +#endif + + PG(during_request_startup) = 0; + + if (primary_file->filename && !(SG(options) & SAPI_OPTION_NO_CHDIR)) { +#if HAVE_BROKEN_GETCWD + /* this looks nasty to me */ + old_cwd_fd = open(".", 0); +#else + VCWD_GETCWD(old_cwd, OLD_CWD_SIZE-1); +#endif + VCWD_CHDIR_FILE(primary_file->filename); + } + + /* Only lookup the real file path and add it to the included_files list if already opened + * otherwise it will get opened and added to the included_files list in zend_execute_scripts + */ + if (primary_file->filename && + primary_file->opened_path == NULL && + primary_file->type != ZEND_HANDLE_FILENAME + ) { + int realfile_len; + int dummy = 1; + + if (expand_filepath(primary_file->filename, realfile TSRMLS_CC)) { + realfile_len = strlen(realfile); + zend_hash_add(&EG(included_files), realfile, realfile_len+1, (void *)&dummy, sizeof(int), NULL); + primary_file->opened_path = estrndup(realfile, realfile_len); + } + } + + if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) { + prepend_file.filename = PG(auto_prepend_file); + prepend_file.opened_path = NULL; + prepend_file.free_filename = 0; + prepend_file.type = ZEND_HANDLE_FILENAME; + prepend_file_p = &prepend_file; + } else { + prepend_file_p = NULL; + } + + if (PG(auto_append_file) && PG(auto_append_file)[0]) { + append_file.filename = PG(auto_append_file); + append_file.opened_path = NULL; + append_file.free_filename = 0; + append_file.type = ZEND_HANDLE_FILENAME; + append_file_p = &append_file; + } else { + append_file_p = NULL; + } + if (PG(max_input_time) != -1) { +#ifdef PHP_WIN32 + zend_unset_timeout(TSRMLS_C); +#endif + zend_set_timeout(EG(timeout_seconds), 0); + } + retval = (zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, NULL, 3, prepend_file_p, primary_file, append_file_p) == SUCCESS); + + } zend_end_try(); + +#if HAVE_BROKEN_GETCWD + if (old_cwd_fd != -1) { + fchdir(old_cwd_fd); + close(old_cwd_fd); + } +#else + if (old_cwd[0] != '\0') { + VCWD_CHDIR(old_cwd); + } + free_alloca(old_cwd, use_heap); +#endif + return retval; +} +/* }}} */ + +/* {{{ php_execute_simple_script + */ +PHPAPI int php_execute_simple_script(zend_file_handle *primary_file, zval **ret TSRMLS_DC) +{ + char *old_cwd; + ALLOCA_FLAG(use_heap) + + EG(exit_status) = 0; +#define OLD_CWD_SIZE 4096 + old_cwd = do_alloca(OLD_CWD_SIZE, use_heap); + old_cwd[0] = '\0'; + + zend_try { +#ifdef PHP_WIN32 + UpdateIniFromRegistry(primary_file->filename TSRMLS_CC); +#endif + + PG(during_request_startup) = 0; + + if (primary_file->filename && !(SG(options) & SAPI_OPTION_NO_CHDIR)) { + VCWD_GETCWD(old_cwd, OLD_CWD_SIZE-1); + VCWD_CHDIR_FILE(primary_file->filename); + } + zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, ret, 1, primary_file); + } zend_end_try(); + + if (old_cwd[0] != '\0') { + VCWD_CHDIR(old_cwd); + } + + free_alloca(old_cwd, use_heap); + return EG(exit_status); +} +/* }}} */ + +/* {{{ php_handle_aborted_connection + */ +PHPAPI void php_handle_aborted_connection(void) +{ + TSRMLS_FETCH(); + + PG(connection_status) = PHP_CONNECTION_ABORTED; + php_output_set_status(0 TSRMLS_CC); + + if (!PG(ignore_user_abort)) { + zend_bailout(); + } +} +/* }}} */ + +/* {{{ php_handle_auth_data + */ +PHPAPI int php_handle_auth_data(const char *auth TSRMLS_DC) +{ + int ret = -1; + + if (auth && auth[0] != '\0' && strncmp(auth, "Basic ", 6) == 0) { + char *pass; + char *user; + + user = php_base64_decode(auth + 6, strlen(auth) - 6, NULL); + if (user) { + pass = strchr(user, ':'); + if (pass) { + *pass++ = '\0'; + SG(request_info).auth_user = user; + SG(request_info).auth_password = estrdup(pass); + ret = 0; + } else { + efree(user); + } + } + } + + if (ret == -1) { + SG(request_info).auth_user = SG(request_info).auth_password = NULL; + } else { + SG(request_info).auth_digest = NULL; + } + + if (ret == -1 && auth && auth[0] != '\0' && strncmp(auth, "Digest ", 7) == 0) { + SG(request_info).auth_digest = estrdup(auth + 7); + ret = 0; + } + + if (ret == -1) { + SG(request_info).auth_digest = NULL; + } + + return ret; +} +/* }}} */ + +/* {{{ php_lint_script + */ +PHPAPI int php_lint_script(zend_file_handle *file TSRMLS_DC) +{ + zend_op_array *op_array; + int retval = FAILURE; + + zend_try { + op_array = zend_compile_file(file, ZEND_INCLUDE TSRMLS_CC); + zend_destroy_file_handle(file TSRMLS_CC); + + if (op_array) { + destroy_op_array(op_array TSRMLS_CC); + efree(op_array); + retval = SUCCESS; + } + } zend_end_try(); + + return retval; +} +/* }}} */ + +#ifdef PHP_WIN32 +/* {{{ dummy_indent + just so that this symbol gets exported... */ +PHPAPI void dummy_indent(void) +{ + zend_indent(); +} +/* }}} */ +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_ea27fd_5645de_ext#fileinfo#libmagic#cdf.c b/src/main/resource/testFiles/php-src/prevFiles/prev_ea27fd_5645de_ext#fileinfo#libmagic#cdf.c new file mode 100644 index 0000000..9d8d042 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_ea27fd_5645de_ext#fileinfo#libmagic#cdf.c @@ -0,0 +1,1111 @@ +/*- + * Copyright (c) 2008 Christos Zoulas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Parse composite document files, the format used in Microsoft Office + * document files before they switched to zipped xml. + * Info from: http://sc.openoffice.org/compdocfileformat.pdf + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: cdf.c,v 1.17 2009/02/03 20:27:51 christos Exp $") +#endif + +#include +#ifdef CDF_DEBUG +#include +#endif +#include + +#ifdef PHP_WIN32 +#include "win32/unistd.h" +#else +#include +#endif + +#include +#include +#include + +#ifndef EFTYPE +#define EFTYPE EINVAL +#endif + +#include "cdf.h" + +#ifndef __arraycount +#define __arraycount(a) (sizeof(a) / sizeof(a[0])) +#endif + +#ifdef CDF_DEBUG +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif + +static union { + char s[4]; + uint32_t u; +} cdf_bo; + +#define NEED_SWAP (cdf_bo.u == (uint32_t)0x01020304) + +#define CDF_TOLE8(x) (NEED_SWAP ? cdf_tole8(x) : (uint64_t)(x)) +#define CDF_TOLE4(x) (NEED_SWAP ? cdf_tole4(x) : (uint32_t)(x)) +#define CDF_TOLE2(x) (NEED_SWAP ? cdf_tole2(x) : (uint16_t)(x)) + +/* + * swap a short + */ +uint16_t +cdf_tole2(uint16_t sv) +{ + uint16_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[1]; + d[1] = s[0]; + return rv; +} + +/* + * swap an int + */ +uint32_t +cdf_tole4(uint32_t sv) +{ + uint32_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + return rv; +} + +/* + * swap a quad + */ +uint64_t +cdf_tole8(uint64_t sv) +{ + uint64_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[7]; + d[1] = s[6]; + d[2] = s[5]; + d[3] = s[4]; + d[4] = s[3]; + d[5] = s[2]; + d[6] = s[1]; + d[7] = s[0]; + return rv; +} + +#define CDF_UNPACK(a) \ + (void)memcpy(&(a), &buf[len], sizeof(a)), len += sizeof(a) +#define CDF_UNPACKA(a) \ + (void)memcpy((a), &buf[len], sizeof(a)), len += sizeof(a) + +void +cdf_swap_header(cdf_header_t *h) +{ + size_t i; + + h->h_magic = CDF_TOLE8(h->h_magic); + h->h_uuid[0] = CDF_TOLE8(h->h_uuid[0]); + h->h_uuid[1] = CDF_TOLE8(h->h_uuid[1]); + h->h_revision = CDF_TOLE2(h->h_revision); + h->h_version = CDF_TOLE2(h->h_version); + h->h_byte_order = CDF_TOLE2(h->h_byte_order); + h->h_sec_size_p2 = CDF_TOLE2(h->h_sec_size_p2); + h->h_short_sec_size_p2 = CDF_TOLE2(h->h_short_sec_size_p2); + h->h_num_sectors_in_sat = CDF_TOLE4(h->h_num_sectors_in_sat); + h->h_secid_first_directory = CDF_TOLE4(h->h_secid_first_directory); + h->h_min_size_standard_stream = + CDF_TOLE4(h->h_min_size_standard_stream); + h->h_secid_first_sector_in_short_sat = + CDF_TOLE4(h->h_secid_first_sector_in_short_sat); + h->h_num_sectors_in_short_sat = + CDF_TOLE4(h->h_num_sectors_in_short_sat); + h->h_secid_first_sector_in_master_sat = + CDF_TOLE4(h->h_secid_first_sector_in_master_sat); + h->h_num_sectors_in_master_sat = + CDF_TOLE4(h->h_num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) + h->h_master_sat[i] = CDF_TOLE4(h->h_master_sat[i]); +} + +void +cdf_unpack_header(cdf_header_t *h, char *buf) +{ + size_t i; + size_t len = 0; + + CDF_UNPACK(h->h_magic); + CDF_UNPACKA(h->h_uuid); + CDF_UNPACK(h->h_revision); + CDF_UNPACK(h->h_version); + CDF_UNPACK(h->h_byte_order); + CDF_UNPACK(h->h_sec_size_p2); + CDF_UNPACK(h->h_short_sec_size_p2); + CDF_UNPACKA(h->h_unused0); + CDF_UNPACK(h->h_num_sectors_in_sat); + CDF_UNPACK(h->h_secid_first_directory); + CDF_UNPACKA(h->h_unused1); + CDF_UNPACK(h->h_min_size_standard_stream); + CDF_UNPACK(h->h_secid_first_sector_in_short_sat); + CDF_UNPACK(h->h_num_sectors_in_short_sat); + CDF_UNPACK(h->h_secid_first_sector_in_master_sat); + CDF_UNPACK(h->h_num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) + CDF_UNPACK(h->h_master_sat[i]); +} + +void +cdf_swap_dir(cdf_directory_t *d) +{ + d->d_namelen = CDF_TOLE2(d->d_namelen); + d->d_left_child = CDF_TOLE4(d->d_left_child); + d->d_right_child = CDF_TOLE4(d->d_right_child); + d->d_storage = CDF_TOLE4(d->d_storage); + d->d_storage_uuid[0] = CDF_TOLE8(d->d_storage_uuid[0]); + d->d_storage_uuid[1] = CDF_TOLE8(d->d_storage_uuid[1]); + d->d_flags = CDF_TOLE4(d->d_flags); + d->d_created = CDF_TOLE8(d->d_created); + d->d_modified = CDF_TOLE8(d->d_modified); + d->d_stream_first_sector = CDF_TOLE4(d->d_stream_first_sector); + d->d_size = CDF_TOLE4(d->d_size); +} + +void +cdf_swap_class(cdf_classid_t *d) +{ + d->cl_dword = CDF_TOLE4(d->cl_dword); + d->cl_word[0] = CDF_TOLE2(d->cl_word[0]); + d->cl_word[1] = CDF_TOLE2(d->cl_word[1]); +} + +void +cdf_unpack_dir(cdf_directory_t *d, char *buf) +{ + size_t len = 0; + + CDF_UNPACKA(d->d_name); + CDF_UNPACK(d->d_namelen); + CDF_UNPACK(d->d_type); + CDF_UNPACK(d->d_color); + CDF_UNPACK(d->d_left_child); + CDF_UNPACK(d->d_right_child); + CDF_UNPACK(d->d_storage); + CDF_UNPACKA(d->d_storage_uuid); + CDF_UNPACK(d->d_flags); + CDF_UNPACK(d->d_created); + CDF_UNPACK(d->d_modified); + CDF_UNPACK(d->d_stream_first_sector); + CDF_UNPACK(d->d_size); + CDF_UNPACK(d->d_unused0); +} + +int +cdf_read_header(int fd, cdf_header_t *h) +{ + char buf[512]; + (void)memcpy(cdf_bo.s, "\01\02\03\04", 4); + if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) + return -1; + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) + return -1; + cdf_unpack_header(h, buf); + cdf_swap_header(h); + if (h->h_magic != CDF_MAGIC) { + DPRINTF(("Bad magic 0x%x != 0x$x\n", h->h_magic, CDF_MAGIC)); + errno = EFTYPE; + return -1; + } + return 0; +} + + +ssize_t +cdf_read_sector(int fd, void *buf, size_t offs, size_t len, + const cdf_header_t *h, cdf_secid_t id) +{ + assert((size_t)CDF_SEC_SIZE(h) == len); + if (lseek(fd, (off_t)CDF_SEC_POS(h, id), SEEK_SET) == (off_t)-1) + return -1; + return read(fd, ((char *)buf) + offs, len); +} + +ssize_t +cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs, + size_t len, const cdf_header_t *h, cdf_secid_t id) +{ + assert((size_t)CDF_SHORT_SEC_SIZE(h) == len); + (void)memcpy(((char *)buf) + offs, + ((const char *)sst->sst_tab) + CDF_SHORT_SEC_POS(h, id), len); + return len; +} + +/* + * Read the sector allocation table. + */ +int +cdf_read_sat(int fd, cdf_header_t *h, cdf_sat_t *sat) +{ + size_t i, j, k; + size_t ss = CDF_SEC_SIZE(h); + cdf_secid_t *msa, mid; + + for (i = 0; i < __arraycount(h->h_master_sat); i++) + if (h->h_master_sat[i] == CDF_SECID_FREE) + break; + + sat->sat_len = (h->h_num_sectors_in_master_sat + i); + if ((sat->sat_tab = calloc(sat->sat_len, ss)) == NULL) + return -1; + + for (i = 0; i < __arraycount(h->h_master_sat); i++) { + if (h->h_master_sat[i] < 0) + break; + if (cdf_read_sector(fd, sat->sat_tab, ss * i, ss, h, + h->h_master_sat[i]) != (ssize_t)ss) { + DPRINTF(("Reading sector %d", h->h_master_sat[i])); + goto out1; + } + } + + if ((msa = calloc(1, ss)) == NULL) + goto out1; + + mid = h->h_secid_first_sector_in_master_sat; + for (j = 0; j < h->h_num_sectors_in_master_sat; j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Reading master sector loop limit")); + errno = EFTYPE; + goto out2; + } + if (cdf_read_sector(fd, msa, 0, ss, h, mid) != (ssize_t)ss) { + DPRINTF(("Reading master sector %d", mid)); + goto out2; + } + for (k = 0; k < (ss / sizeof(mid)) - 1; k++, i++) + if (cdf_read_sector(fd, sat->sat_tab, ss * i, ss, h, + CDF_TOLE4(msa[k])) != (ssize_t)ss) { + DPRINTF(("Reading sector %d", + CDF_TOLE4(msa[k]))); + goto out2; + } + mid = CDF_TOLE4(msa[(ss / sizeof(mid)) - 1]); + } + free(msa); + return 0; +out2: + free(msa); +out1: + free(sat->sat_tab); + return -1; +} + +size_t +cdf_count_chain(const cdf_header_t *h, const cdf_sat_t *sat, + cdf_secid_t sid) +{ + size_t i, j, s = CDF_SEC_SIZE(h) / sizeof(cdf_secid_t); + cdf_secid_t maxsector = (cdf_secid_t)(sat->sat_len * s); + + DPRINTF(("Chain:")); + for (j = i = 0; sid >= 0; i++, j++) { + DPRINTF((" %d", sid)); + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Counting chain loop limit")); + errno = EFTYPE; + return (size_t)-1; + } + if (sid > maxsector) { + DPRINTF(("Sector %d > %d\n", sid, maxsector)); + errno = EFTYPE; + return (size_t)-1; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + DPRINTF(("\n")); + return i; +} + +int +cdf_read_long_sector_chain(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + size_t ss = CDF_SEC_SIZE(h), i, j; + ssize_t nr; + scn->sst_len = cdf_count_chain(h, sat, sid); + scn->sst_dirlen = len; + + if (scn->sst_len == (size_t)-1) + return -1; + + scn->sst_tab = calloc(scn->sst_len, ss); + if (scn->sst_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if ((nr = cdf_read_sector(fd, scn->sst_tab, i * ss, ss, h, + sid)) != (ssize_t)ss) { + if (i == scn->sst_len - 1 && nr > 0) { + /* Last sector might be truncated */ + return 0; + } + DPRINTF(("Reading long sector chain %d", sid)); + goto out; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read long sector chain loop limit")); + errno = EFTYPE; + goto out; + } + } + return 0; +out: + free(scn->sst_tab); + return (size_t)-1; +} + +int +cdf_read_short_sector_chain(const cdf_header_t *h, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + size_t ss = CDF_SHORT_SEC_SIZE(h), i, j; + scn->sst_len = cdf_count_chain(h, ssat, sid); + scn->sst_dirlen = len; + + if (scn->sst_len == (size_t)-1) + return -1; + + scn->sst_tab = calloc(scn->sst_len, ss); + if (scn->sst_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read short sector chain loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h, + sid) != (ssize_t)ss) { + DPRINTF(("Reading short sector chain %d", sid)); + goto out; + } + sid = CDF_TOLE4(ssat->sat_tab[sid]); + } + return 0; +out: + free(scn->sst_tab); + return (size_t)-1; +} + +int +cdf_read_sector_chain(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + + if (len < h->h_min_size_standard_stream) + return cdf_read_short_sector_chain(h, ssat, sst, sid, len, + scn); + else + return cdf_read_long_sector_chain(fd, h, sat, sid, len, scn); +} + +int +cdf_read_dir(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_dir_t *dir) +{ + size_t i, j; + size_t ss = CDF_SEC_SIZE(h), ns, nd; + char *buf; + cdf_secid_t sid = h->h_secid_first_directory; + + ns = cdf_count_chain(h, sat, sid); + if (ns == (size_t)-1) + return -1; + + nd = ss / CDF_DIRECTORY_SIZE; + + dir->dir_len = ns * nd; + dir->dir_tab = calloc(dir->dir_len, sizeof(dir->dir_tab[0])); + if (dir->dir_tab == NULL) + return -1; + + if ((buf = malloc(ss)) == NULL) { + free(dir->dir_tab); + return -1; + } + + for (j = i = 0; i < ns; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read dir loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_sector(fd, buf, 0, ss, h, sid) != (ssize_t)ss) { + DPRINTF(("Reading directory sector %d", sid)); + goto out; + } + for (j = 0; j < nd; j++) { + cdf_unpack_dir(&dir->dir_tab[i * nd + j], + &buf[j * CDF_DIRECTORY_SIZE]); + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + if (NEED_SWAP) + for (i = 0; i < dir->dir_len; i++) + cdf_swap_dir(&dir->dir_tab[i]); + free(buf); + return 0; +out: + free(dir->dir_tab); + free(buf); + return -1; +} + + +int +cdf_read_ssat(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_sat_t *ssat) +{ + size_t i, j; + size_t ss = CDF_SEC_SIZE(h); + cdf_secid_t sid = h->h_secid_first_sector_in_short_sat; + + ssat->sat_len = cdf_count_chain(h, sat, sid); + if (ssat->sat_len == (size_t)-1) + return -1; + + ssat->sat_tab = calloc(ssat->sat_len, ss); + if (ssat->sat_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read short sat sector loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_sector(fd, ssat->sat_tab, i * ss, ss, h, sid) != + (ssize_t)ss) { + DPRINTF(("Reading short sat sector %d", sid)); + goto out; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + return 0; +out: + free(ssat->sat_tab); + return -1; +} + +int +cdf_read_short_stream(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_dir_t *dir, cdf_stream_t *scn) +{ + size_t i; + const cdf_directory_t *d; + + for (i = 0; i < dir->dir_len; i++) + if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE) + break; + + if (i == dir->dir_len) { + DPRINTF(("Cannot find root storage node\n")); + errno = EFTYPE; + return -1; + } + d = &dir->dir_tab[i]; + + /* If the it is not there, just fake it; some docs don't have it */ + if (d->d_stream_first_sector < 0) { + scn->sst_tab = NULL; + scn->sst_len = 0; + return 0; + } + + return cdf_read_long_sector_chain(fd, h, sat, + d->d_stream_first_sector, d->d_size, scn); +} + +static int +cdf_namecmp(const char *d, const uint16_t *s, size_t l) +{ + for (; l--; d++, s++) + if (*d != CDF_TOLE2(*s)) + return (unsigned char)*d - CDF_TOLE2(*s); + return 0; +} + +int +cdf_read_summary_info(int fd, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir, cdf_stream_t *scn) +{ + size_t i; + const cdf_directory_t *d; + static const char name[] = "\05SummaryInformation"; + + for (i = 0; i < dir->dir_len; i++) + if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_USER_STREAM && + cdf_namecmp(name, dir->dir_tab[i].d_name, sizeof(name)) + == 0) + break; + + if (i == dir->dir_len) { + DPRINTF(("Cannot find summary information section\n")); + errno = EFTYPE; + return -1; + } + d = &dir->dir_tab[i]; + return cdf_read_sector_chain(fd, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, scn); +} + +int +cdf_read_property_info(const cdf_stream_t *sst, uint32_t offs, + cdf_property_info_t **info, size_t *count, size_t *maxcount) +{ + const cdf_section_header_t *shp; + cdf_section_header_t sh; + const uint32_t *p, *q, *e; + int16_t s16; + int32_t s32; + uint32_t u32; + int64_t s64; + uint64_t u64; + cdf_timestamp_t tp; + size_t i, o, nelements, j; + cdf_property_info_t *inp; + + shp = (const void *)((const char *)sst->sst_tab + offs); + sh.sh_len = CDF_TOLE4(shp->sh_len); + sh.sh_properties = CDF_TOLE4(shp->sh_properties); + DPRINTF(("section len: %d properties %d\n", sh.sh_len, + sh.sh_properties)); + if (*maxcount) { + *maxcount += sh.sh_properties; + inp = realloc(*info, *maxcount * sizeof(*inp)); + } else { + *maxcount = sh.sh_properties; + inp = malloc(*maxcount * sizeof(*inp)); + } + if (inp == NULL) + goto out; + *info = inp; + inp += *count; + *count += sh.sh_properties; + p = (const void *)((const char *)sst->sst_tab + offs + sizeof(sh)); + e = (const void *)(((const char *)shp) + sh.sh_len); + for (i = 0; i < sh.sh_properties; i++) { + q = (const uint32_t *)((const char *)p + + CDF_TOLE4(p[(i << 1) + 1])) - 2; + if (q > e) { + DPRINTF(("Ran of the end %p > %p\n", q, e)); + goto out; + } + inp[i].pi_id = CDF_TOLE4(p[i << 1]); + inp[i].pi_type = CDF_TOLE4(q[0]); + DPRINTF(("%d) id=%x type=%x offs=%x\n", i, inp[i].pi_id, + inp[i].pi_type, (const char *)q - (const char *)p)); + if (inp[i].pi_type & CDF_VECTOR) { + nelements = CDF_TOLE4(q[1]); + o = 2; + } else { + nelements = 1; + o = 1; + } + if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED)) + goto unknown; + switch (inp[i].pi_type & CDF_TYPEMASK) { + case CDF_EMPTY: + break; + case CDF_SIGNED16: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s16, &q[o], sizeof(s16)); + inp[i].pi_s16 = CDF_TOLE2(s16); + break; + case CDF_SIGNED32: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s32, &q[o], sizeof(s32)); + inp[i].pi_s32 = CDF_TOLE4(s32); + break; + case CDF_BOOL: + case CDF_UNSIGNED32: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u32, &q[o], sizeof(u32)); + inp[i].pi_u32 = CDF_TOLE4(u32); + break; + case CDF_SIGNED64: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s64, &q[o], sizeof(s64)); + inp[i].pi_s64 = CDF_TOLE4(s64); + break; + case CDF_UNSIGNED64: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u64, &q[o], sizeof(u64)); + inp[i].pi_u64 = CDF_TOLE4(u64); + break; + case CDF_LENGTH32_STRING: + if (nelements > 1) { + size_t nelem = inp - *info; + *maxcount += nelements; + inp = realloc(*info, *maxcount * sizeof(*inp)); + if (inp == NULL) + goto out; + *info = inp; + inp = *info + nelem; + } + DPRINTF(("nelements = %d\n", nelements)); + for (j = 0; j < nelements; j++, i++) { + uint32_t l = CDF_TOLE4(q[o]); + inp[i].pi_str.s_len = l; + inp[i].pi_str.s_buf = (const char *)(&q[o+1]); + DPRINTF(("l = %d, r = %d, s = %s\n", l, + CDF_ROUND(l, sizeof(l)), + inp[i].pi_str.s_buf)); + l = 4 + CDF_ROUND(l, sizeof(l)); + o += l >> 2; + } + i--; + break; + case CDF_FILETIME: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&tp, &q[o], sizeof(tp)); + inp[i].pi_tp = CDF_TOLE8(tp); + break; + case CDF_CLIPBOARD: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + break; + default: + unknown: + DPRINTF(("Don't know how to deal with %x\n", + inp[i].pi_type)); + goto out; + } + } + return 0; +out: + free(*info); + return -1; +} + +int +cdf_unpack_summary_info(const cdf_stream_t *sst, cdf_summary_info_header_t *ssi, + cdf_property_info_t **info, size_t *count) +{ + size_t i, maxcount; + const cdf_summary_info_header_t *si = sst->sst_tab; + const cdf_section_declaration_t *sd = (const void *) + ((const char *)sst->sst_tab + CDF_SECTION_DECLARATION_OFFSET); + + ssi->si_byte_order = CDF_TOLE2(si->si_byte_order); + ssi->si_os_version = CDF_TOLE2(si->si_os_version); + ssi->si_os = CDF_TOLE2(si->si_os); + ssi->si_class = si->si_class; + cdf_swap_class(&ssi->si_class); + ssi->si_count = CDF_TOLE2(si->si_count); + *count = 0; + maxcount = 0; + *info = NULL; + for (i = 0; i < CDF_TOLE4(si->si_count); i++) { + if (i >= CDF_LOOP_LIMIT) { + DPRINTF(("Unpack summary info loop limit")); + errno = EFTYPE; + return -1; + } + if (cdf_read_property_info(sst, CDF_TOLE4(sd->sd_offset), + info, count, &maxcount) == -1) + return -1; + } + return 0; +} + + + +int +cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id) +{ + return snprintf(buf, buflen, "%.8x-%.4x-%.4x-%.2x%.2x-" + "%.2x%.2x%.2x%.2x%.2x%.2x", id->cl_dword, id->cl_word[0], + id->cl_word[1], id->cl_two[0], id->cl_two[1], id->cl_six[0], + id->cl_six[1], id->cl_six[2], id->cl_six[3], id->cl_six[4], + id->cl_six[5]); +} + +static const struct { + uint32_t v; + const char *n; +} vn[] = { + { CDF_PROPERTY_CODE_PAGE, "Code page" }, + { CDF_PROPERTY_TITLE, "Title" }, + { CDF_PROPERTY_SUBJECT, "Subject" }, + { CDF_PROPERTY_AUTHOR, "Author" }, + { CDF_PROPERTY_KEYWORDS, "Keywords" }, + { CDF_PROPERTY_COMMENTS, "Comments" }, + { CDF_PROPERTY_TEMPLATE, "Template" }, + { CDF_PROPERTY_LAST_SAVED_BY, "Last Saved By" }, + { CDF_PROPERTY_REVISION_NUMBER, "Revision Number" }, + { CDF_PROPERTY_TOTAL_EDITING_TIME, "Total Editing Time" }, + { CDF_PROPERTY_LAST_PRINTED, "Last Printed" }, + { CDF_PROPERTY_CREATE_TIME, "Create Time/Date" }, + { CDF_PROPERTY_LAST_SAVED_TIME, "Last Saved Time/Date" }, + { CDF_PROPERTY_NUMBER_OF_PAGES, "Number of Pages" }, + { CDF_PROPERTY_NUMBER_OF_WORDS, "Number of Words" }, + { CDF_PROPERTY_NUMBER_OF_CHARACTERS, "Number of Characters" }, + { CDF_PROPERTY_THUMBNAIL, "Thumbnail" }, + { CDF_PROPERTY_NAME_OF_APPLICATION, "Name of Creating Application" }, + { CDF_PROPERTY_SECURITY, "Security" }, + { CDF_PROPERTY_LOCALE_ID, "Locale ID" }, +}; + +int +cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p) +{ + size_t i; + + for (i = 0; i < __arraycount(vn); i++) + if (vn[i].v == p) + return snprintf(buf, bufsiz, "%s", vn[i].n); + return snprintf(buf, bufsiz, "0x%x", p); +} + +int +cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts) +{ + size_t len = 0; + int days, hours, mins, secs; + + ts /= CDF_TIME_PREC; + secs = ts % 60; + ts /= 60; + mins = ts % 60; + ts /= 60; + hours = ts % 24; + ts /= 24; + days = ts; + + if (days) { + len += snprintf(buf + len, bufsiz - len, "%dd+", days); + if (len >= bufsiz) + return len; + } + + if (days || hours) { + len += snprintf(buf + len, bufsiz - len, "%.2d:", hours); + if (len >= bufsiz) + return len; + } + + len += snprintf(buf + len, bufsiz - len, "%.2d:", mins); + if (len >= bufsiz) + return len; + + len += snprintf(buf + len, bufsiz - len, "%.2d", secs); + return len; +} + + +#ifdef CDF_DEBUG +void +cdf_dump_header(const cdf_header_t *h) +{ + size_t i; + +#define DUMP(a, b) printf("%40.40s = " a "\n", # b, h->h_ ## b) + DUMP("%d", revision); + DUMP("%d", version); + DUMP("0x%x", byte_order); + DUMP("%d", sec_size_p2); + DUMP("%d", short_sec_size_p2); + DUMP("%d", num_sectors_in_sat); + DUMP("%d", secid_first_directory); + DUMP("%d", min_size_standard_stream); + DUMP("%d", secid_first_sector_in_short_sat); + DUMP("%d", num_sectors_in_short_sat); + DUMP("%d", secid_first_sector_in_master_sat); + DUMP("%d", num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) { + if (h->h_master_sat[i] == CDF_SECID_FREE) + break; + printf("%35.35s[%.3zu] = %d\n", + "master_sat", i, h->h_master_sat[i]); + } +} + +void +cdf_dump_sat(const char *prefix, const cdf_header_t *h, const cdf_sat_t *sat) +{ + size_t i, j, s = CDF_SEC_SIZE(h) / sizeof(cdf_secid_t); + + for (i = 0; i < sat->sat_len; i++) { + printf("%s[%zu]:\n", prefix, i); + for (j = 0; j < s; j++) { + printf("%5d, ", CDF_TOLE4(sat->sat_tab[s * i + j])); + if ((j + 1) % 10 == 0) + printf("\n"); + } + printf("\n"); + } +} + +void +cdf_dump(void *v, size_t len) +{ + size_t i, j; + unsigned char *p = v; + char abuf[16]; + printf("%.4x: ", 0); + for (i = 0, j = 0; i < len; i++, p++) { + printf("%.2x ", *p); + abuf[j++] = isprint(*p) ? *p : '.'; + if (j == 16) { + j = 0; + abuf[15] = '\0'; + printf("%s\n%.4x: ", abuf, i + 1); + } + } + printf("\n"); +} + +void +cdf_dump_stream(const cdf_header_t *h, const cdf_stream_t *sst) +{ + size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? + CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); + cdf_dump(sst->sst_tab, ss * sst->sst_len); +} + +void +cdf_dump_dir(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir) +{ + size_t i, j; + cdf_directory_t *d; + char name[__arraycount(d->d_name)]; + cdf_stream_t scn; + struct timeval ts; + + static const char *types[] = { "empty", "user storage", + "user stream", "lockbytes", "property", "root storage" }; + + for (i = 0; i < dir->dir_len; i++) { + d = &dir->dir_tab[i]; + for (j = 0; j < sizeof(name); j++) + name[j] = (char)CDF_TOLE2(d->d_name[j]); + printf("Directory %zu: %s\n", i, name); + if (d->d_type < __arraycount(types)) + printf("Type: %s\n", types[d->d_type]); + else + printf("Type: %d\n", d->d_type); + printf("Color: %s\n", d->d_color ? "black" : "red"); + printf("Left child: %d\n", d->d_left_child); + printf("Right child: %d\n", d->d_right_child); + printf("Flags: 0x%x\n", d->d_flags); + cdf_timestamp_to_timespec(&ts, d->d_created); + printf("Created %s", ctime(&ts.tv_sec)); + cdf_timestamp_to_timespec(&ts, d->d_modified); + printf("Modified %s", ctime(&ts.tv_sec)); + printf("Stream %d\n", d->d_stream_first_sector); + printf("Size %d\n", d->d_size); + switch (d->d_type) { + case CDF_DIR_TYPE_USER_STORAGE: + printf("Storage: %d\n", d->d_storage); + break; + case CDF_DIR_TYPE_USER_STREAM: + if (sst == NULL) + break; + if (cdf_read_sector_chain(fd, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, &scn) == -1) { + warn("Can't read stream for %s at %d len %d", + name, d->d_stream_first_sector, d->d_size); + break; + } + cdf_dump_stream(h, &scn); + free(scn.sst_tab); + break; + default: + break; + } + + } +} + +void +cdf_dump_property_info(const cdf_property_info_t *info, size_t count) +{ + cdf_timestamp_t tp; + struct timeval ts; + char buf[64]; + size_t i; + + for (i = 0; i < count; i++) { + cdf_print_property_name(buf, sizeof(buf), info[i].pi_id); + printf("%zu) %s: ", i, buf); + switch (info[i].pi_type) { + case CDF_SIGNED16: + printf("signed 16 [%hd]\n", info[i].pi_s16); + break; + case CDF_SIGNED32: + printf("signed 32 [%d]\n", info[i].pi_s32); + break; + case CDF_UNSIGNED32: + printf("unsigned 32 [%u]\n", info[i].pi_u32); + break; + case CDF_LENGTH32_STRING: + printf("string %u [%.*s]\n", info[i].pi_str.s_len, + info[i].pi_str.s_len, info[i].pi_str.s_buf); + break; + case CDF_FILETIME: + tp = info[i].pi_tp; + if (tp < 1000000000000000LL) { + cdf_print_elapsed_time(buf, sizeof(buf), tp); + printf("timestamp %s\n", buf); + } else { + cdf_timestamp_to_timespec(&ts, tp); + printf("timestamp %s", ctime(&ts.tv_sec)); + } + break; + case CDF_CLIPBOARD: + printf("CLIPBOARD %u\n", info[i].pi_u32); + break; + default: + DPRINTF(("Don't know how to deal with %x\n", + info[i].pi_type)); + break; + } + } +} + + +void +cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst) +{ + char buf[128]; + cdf_summary_info_header_t ssi; + cdf_property_info_t *info; + size_t count; + + (void)&h; + if (cdf_unpack_summary_info(sst, &ssi, &info, &count) == -1) + return; + printf("Endian: %x\n", ssi.si_byte_order); + printf("Os Version %d.%d\n", ssi.si_os_version & 0xff, + ssi.si_os_version >> 8); + printf("Os %d\n", ssi.si_os); + cdf_print_classid(buf, sizeof(buf), &ssi.si_class); + printf("Class %s\n", buf); + printf("Count %d\n", ssi.si_count); + cdf_dump_property_info(info, count); + free(info); +} + +#endif + +#ifdef TEST +int +main(int argc, char *argv[]) +{ + int fd, i; + cdf_header_t h; + cdf_sat_t sat, ssat; + cdf_stream_t sst, scn; + cdf_dir_t dir; + + if (argc < 2) { + (void)fprintf(stderr, "Usage: %s \n", getprogname()); + return -1; + } + + for (i = 1; i < argc; i++) { + if ((fd = open(argv[1], O_RDONLY)) == -1) + err(1, "Cannot open `%s'", argv[1]); + + if (cdf_read_header(fd, &h) == -1) + err(1, "Cannot read header"); +#ifdef CDF_DEBUG + cdf_dump_header(&h); +#endif + + if (cdf_read_sat(fd, &h, &sat) == -1) + err(1, "Cannot read sat"); +#ifdef CDF_DEBUG + cdf_dump_sat("SAT", &h, &sat); +#endif + + if (cdf_read_ssat(fd, &h, &sat, &ssat) == -1) + err(1, "Cannot read ssat"); +#ifdef CDF_DEBUG + cdf_dump_sat("SSAT", &h, &ssat); +#endif + + if (cdf_read_dir(fd, &h, &sat, &dir) == -1) + err(1, "Cannot read dir"); + + if (cdf_read_short_stream(fd, &h, &sat, &dir, &sst) == -1) + err(1, "Cannot read short stream"); +#ifdef CDF_DEBUG + cdf_dump_stream(&h, &sst); +#endif + +#ifdef CDF_DEBUG + cdf_dump_dir(fd, &h, &sat, &ssat, &sst, &dir); +#endif + + + if (cdf_read_summary_info(fd, &h, &sat, &ssat, &sst, &dir, + &scn) == -1) + err(1, "Cannot read summary info"); +#ifdef CDF_DEBUG + cdf_dump_summary_info(&h, &scn); +#endif + + (void)close(fd); + } + + return 0; +} +#endif diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_f91b3d_5f1bff_ext#ereg#ereg.c b/src/main/resource/testFiles/php-src/prevFiles/prev_f91b3d_5f1bff_ext#ereg#ereg.c new file mode 100644 index 0000000..9b8513e --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_f91b3d_5f1bff_ext#ereg#ereg.c @@ -0,0 +1,660 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2002 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jim Winstead | + | Jaakko Hyvätti | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include +#include "php.h" +#include "php_string.h" +#include "reg.h" +#include "ext/standard/info.h" + +#ifdef ZTS +int reg_globals_id; +#else +static php_reg_globals reg_globals; +#endif + +typedef struct { + regex_t preg; + int cflags; +} reg_cache; + +/* {{{ _php_regcomp + */ +static int _php_regcomp(regex_t *preg, const char *pattern, int cflags) +{ + int r = 0; + int patlen = strlen(pattern); + reg_cache *rc = NULL; + TSRMLS_FETCH(); + + if(zend_hash_find(®(ht_rc), (char *) pattern, patlen+1, (void **) &rc) == FAILURE || + rc->cflags != cflags) { + r = regcomp(preg, pattern, cflags); + if(!r) { + reg_cache rcp; + + rcp.cflags = cflags; + memcpy(&rcp.preg, preg, sizeof(*preg)); + zend_hash_update(®(ht_rc), (char *) pattern, patlen+1, + (void *) &rcp, sizeof(rcp), NULL); + } + } else { + memcpy(preg, &rc->preg, sizeof(*preg)); + } + + return r; +} +/* }}} */ + +static void _free_reg_cache(reg_cache *rc) +{ + regfree(&rc->preg); +} + +#undef regfree +#define regfree(a); +#undef regcomp +#define regcomp(a, b, c) _php_regcomp(a, b, c) + +static void php_reg_init_globals(php_reg_globals *reg_globals TSRMLS_DC) +{ + zend_hash_init(®_globals->ht_rc, 0, NULL, (void (*)(void *)) _free_reg_cache, 1); +} + +PHP_MINIT_FUNCTION(regex) +{ +#ifdef ZTS + ts_allocate_id(®_globals_id, sizeof(php_reg_globals), (ts_allocate_ctor) php_reg_init_globals, NULL); +#else + php_reg_init_globals(®_globals TSRMLS_CC); +#endif + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(regex) +{ + zend_hash_destroy(®(ht_rc)); + return SUCCESS; +} + +PHP_MINFO_FUNCTION(regex) +{ +#if HSREGEX + php_info_print_table_row(2, "Regex Library", "Bundled library enabled"); +#else + php_info_print_table_row(2, "Regex Library", "System library enabled"); +#endif +} + + +/* {{{ php_reg_eprint + * php_reg_eprint - convert error number to name + */ +static void php_reg_eprint(int err, regex_t *re) { + char *buf = NULL, *message = NULL; + size_t len; + size_t buf_len; + +#ifdef REG_ITOA + /* get the length of the message */ + buf_len = regerror(REG_ITOA | err, re, NULL, 0); + if (buf_len) { + buf = (char *)emalloc(buf_len * sizeof(char)); + if (!buf) return; /* fail silently */ + /* finally, get the error message */ + regerror(REG_ITOA | err, re, buf, buf_len); + } +#else + buf_len = 0; +#endif + len = regerror(err, re, NULL, 0); + if (len) { + message = (char *)emalloc((buf_len + len + 2) * sizeof(char)); + if (!message) { + return; /* fail silently */ + } + if (buf_len) { + snprintf(message, buf_len, "%s: ", buf); + buf_len += 1; /* so pointer math below works */ + } + /* drop the message into place */ + regerror(err, re, message + buf_len, len); + + php_error(E_WARNING, "%s", message); + } + + STR_FREE(buf); + STR_FREE(message); +} +/* }}} */ + +/* {{{ php_ereg + */ +static void php_ereg(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval **regex, /* Regular expression */ + **findin, /* String to apply expression to */ + **array = NULL; /* Optional register array */ + regex_t re; + regmatch_t *subs; + int err, match_len, string_len; + uint i; + int copts = 0; + off_t start, end; + char *buf = NULL; + char *string = NULL; + int argc = ZEND_NUM_ARGS(); + + if (argc < 2 || argc > 3 || + zend_get_parameters_ex(argc, ®ex, &findin, &array) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (icase) + copts |= REG_ICASE; + + if (argc == 2) + copts |= REG_NOSUB; + + /* compile the regular expression from the supplied regex */ + if (Z_TYPE_PP(regex) == IS_STRING) { + err = regcomp(&re, Z_STRVAL_PP(regex), REG_EXTENDED | copts); + } else { + /* we convert numbers to integers and treat them as a string */ + if (Z_TYPE_PP(regex) == IS_DOUBLE) + convert_to_long_ex(regex); /* get rid of decimal places */ + convert_to_string_ex(regex); + /* don't bother doing an extended regex with just a number */ + err = regcomp(&re, Z_STRVAL_PP(regex), copts); + } + + if (err) { + php_reg_eprint(err, &re); + RETURN_FALSE; + } + + /* make a copy of the string we're looking in */ + convert_to_string_ex(findin); + string = estrndup(Z_STRVAL_PP(findin), Z_STRLEN_PP(findin)); + + /* allocate storage for (sub-)expression-matches */ + subs = (regmatch_t *)ecalloc(sizeof(regmatch_t),re.re_nsub+1); + if (!subs) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg"); + RETURN_FALSE; + } + + /* actually execute the regular expression */ + err = regexec(&re, string, re.re_nsub+1, subs, 0); + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + regfree(&re); + efree(subs); + RETURN_FALSE; + } + match_len = 1; + + if (array && err != REG_NOMATCH) { + match_len = (int) (subs[0].rm_eo - subs[0].rm_so); + string_len = Z_STRLEN_PP(findin) + 1; + + buf = emalloc(string_len); + if (!buf) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg"); + regfree(&re); + efree(subs); + RETURN_FALSE; + } + + zval_dtor(*array); /* start with clean array */ + array_init(*array); + + for (i = 0; i <= re.re_nsub; i++) { + start = subs[i].rm_so; + end = subs[i].rm_eo; + if (start != -1 && end > 0 && start < string_len && end < string_len && start < end) { + add_index_stringl(*array, i, string+start, end-start, 1); + } else { + add_index_bool(*array, i, 0); + } + } + efree(buf); + } + + efree(subs); + efree(string); + if (err == REG_NOMATCH) { + RETVAL_FALSE; + } else { + if (match_len == 0) + match_len = 1; + RETVAL_LONG(match_len); + } + regfree(&re); +} +/* }}} */ + +/* {{{ proto int ereg(string pattern, string string [, array registers]) + Regular expression match */ +PHP_FUNCTION(ereg) +{ + php_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto int eregi(string pattern, string string [, array registers]) + Case-insensitive regular expression match */ +PHP_FUNCTION(eregi) +{ + php_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ php_reg_replace + * this is the meat and potatoes of regex replacement! */ +PHPAPI char *php_reg_replace(const char *pattern, const char *replace, const char *string, int icase, int extended) +{ + regex_t re; + regmatch_t *subs; + + char *buf, /* buf is where we build the replaced string */ + *nbuf, /* nbuf is used when we grow the buffer */ + *walkbuf; /* used to walk buf when replacing backrefs */ + const char *walk; /* used to walk replacement string for backrefs */ + int buf_len; + int pos, tmp, string_len, new_l; + int err, copts = 0; + + string_len = strlen(string); + + if (icase) + copts = REG_ICASE; + if (extended) + copts |= REG_EXTENDED; + + err = regcomp(&re, pattern, copts); + if (err) { + php_reg_eprint(err, &re); + return ((char *) -1); + } + + + /* allocate storage for (sub-)expression-matches */ + subs = (regmatch_t *)ecalloc(sizeof(regmatch_t),re.re_nsub+1); + if (!subs) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg_replace"); + return ((char *) -1); + } + + /* start with a buffer that is twice the size of the stringo + we're doing replacements in */ + buf_len = 2 * string_len + 1; + buf = emalloc(buf_len * sizeof(char)); + if (!buf) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg_replace"); + efree(subs); + regfree(&re); + return ((char *) -1); + } + + err = pos = 0; + buf[0] = '\0'; + while (!err) { + err = regexec(&re, &string[pos], re.re_nsub+1, subs, (pos ? REG_NOTBOL : 0)); + + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + efree(subs); + efree(buf); + regfree(&re); + return ((char *) -1); + } + + if (!err) { + /* backref replacement is done in two passes: + 1) find out how long the string will be, and allocate buf + 2) copy the part before match, replacement and backrefs to buf + + Jaakko Hyvätti + */ + + new_l = strlen(buf) + subs[0].rm_so; /* part before the match */ + walk = replace; + while (*walk) + if ('\\' == *walk + && '0' <= walk[1] && '9' >= walk[1] + && walk[1] - '0' <= ((char) re.re_nsub) + && subs[walk[1] - '0'].rm_so > -1 + && subs[walk[1] - '0'].rm_eo > -1) { + new_l += subs[walk[1] - '0'].rm_eo + - subs[walk[1] - '0'].rm_so; + walk += 2; + } else { + new_l++; + walk++; + } + + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + tmp = strlen(buf); + /* copy the part of the string before the match */ + strncat(buf, &string[pos], subs[0].rm_so); + + /* copy replacement and backrefs */ + walkbuf = &buf[tmp + subs[0].rm_so]; + walk = replace; + while (*walk) + if ('\\' == *walk + && '0' <= walk[1] && '9' >= walk[1] + && walk[1] - '0' <= re.re_nsub + && subs[walk[1] - '0'].rm_so > -1 + && subs[walk[1] - '0'].rm_eo > -1 + /* this next case shouldn't happen. it does. */ + && subs[walk[1] - '0'].rm_so <= subs[walk[1] - '0'].rm_eo) { + tmp = subs[walk[1] - '0'].rm_eo + - subs[walk[1] - '0'].rm_so; + memcpy (walkbuf, + &string[pos + subs[walk[1] - '0'].rm_so], + tmp); + walkbuf += tmp; + walk += 2; + } else + *walkbuf++ = *walk++; + *walkbuf = '\0'; + + /* and get ready to keep looking for replacements */ + if (subs[0].rm_so == subs[0].rm_eo) { + if (subs[0].rm_so + pos >= string_len) + break; + new_l = strlen (buf) + 1; + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + pos += subs[0].rm_eo + 1; + buf [new_l-1] = string [pos-1]; + buf [new_l] = '\0'; + } else { + pos += subs[0].rm_eo; + } + } else { /* REG_NOMATCH */ + new_l = strlen(buf) + strlen(&string[pos]); + if (new_l + 1 > buf_len) { + buf_len = new_l + 1; /* now we know exactly how long it is */ + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + /* stick that last bit of string on our output */ + strcat(buf, &string[pos]); + } + } + + /* don't want to leak memory .. */ + efree(subs); + regfree(&re); + + /* whew. */ + return (buf); +} +/* }}} */ + +/* {{{ php_ereg_replace + */ +static void php_ereg_replace(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval **arg_pattern, + **arg_replace, + **arg_string; + char *pattern; + char *string; + char *replace; + char *ret; + + if (ZEND_NUM_ARGS() != 3 || + zend_get_parameters_ex(3, &arg_pattern, &arg_replace, &arg_string) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (Z_TYPE_PP(arg_pattern) == IS_STRING) { + if (Z_STRVAL_PP(arg_pattern) && Z_STRLEN_PP(arg_pattern)) + pattern = estrndup(Z_STRVAL_PP(arg_pattern), Z_STRLEN_PP(arg_pattern)); + else + pattern = empty_string; + } else { + convert_to_long_ex(arg_pattern); + pattern = emalloc(2); + pattern[0] = (char) Z_LVAL_PP(arg_pattern); + pattern[1] = '\0'; + } + + if (Z_TYPE_PP(arg_replace) == IS_STRING) { + if (Z_STRVAL_PP(arg_replace) && Z_STRLEN_PP(arg_replace)) + replace = estrndup(Z_STRVAL_PP(arg_replace), Z_STRLEN_PP(arg_replace)); + else + replace = empty_string; + } else { + convert_to_long_ex(arg_replace); + replace = emalloc(2); + replace[0] = (char) Z_LVAL_PP(arg_replace); + replace[1] = '\0'; + } + + convert_to_string_ex(arg_string); + if (Z_STRVAL_PP(arg_string) && Z_STRLEN_PP(arg_string)) + string = estrndup(Z_STRVAL_PP(arg_string), Z_STRLEN_PP(arg_string)); + else + string = empty_string; + + /* do the actual work */ + ret = php_reg_replace(pattern, replace, string, icase, 1); + if (ret == (char *) -1) { + RETVAL_FALSE; + } else { + RETVAL_STRING(ret, 1); + STR_FREE(ret); + } + + STR_FREE(string); + STR_FREE(replace); + STR_FREE(pattern); +} +/* }}} */ + +/* {{{ proto string ereg_replace(string pattern, string replacement, string string) + Replace regular expression */ +PHP_FUNCTION(ereg_replace) +{ + php_ereg_replace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto string eregi_replace(string pattern, string replacement, string string) + Case insensitive replace regular expression */ +PHP_FUNCTION(eregi_replace) +{ + php_ereg_replace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ php_split + */ +static void php_split(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + zval **spliton, **str, **arg_count = NULL; + regex_t re; + regmatch_t subs[1]; + char *strp, *endp; + int err, size, count = -1, copts = 0; + int argc = ZEND_NUM_ARGS(); + + if (argc < 2 || argc > 3 || + zend_get_parameters_ex(argc, &spliton, &str, &arg_count) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (argc > 2) { + convert_to_long_ex(arg_count); + count = Z_LVAL_PP(arg_count); + } + + if (icase) + copts = REG_ICASE; + + convert_to_string_ex(spliton); + convert_to_string_ex(str); + + strp = Z_STRVAL_PP(str); + endp = strp + Z_STRLEN_PP(str); + + err = regcomp(&re, Z_STRVAL_PP(spliton), REG_EXTENDED | copts); + if (err) { + php_reg_eprint(err, &re); + RETURN_FALSE; + } + + if (array_init(return_value) == FAILURE) { + regfree(&re); + RETURN_FALSE; + } + + /* churn through str, generating array entries as we go */ + while ((count == -1 || count > 1) && !(err = regexec(&re, strp, 1, subs, 0))) { + if (subs[0].rm_so == 0 && subs[0].rm_eo) { + /* match is at start of string, return empty string */ + add_next_index_stringl(return_value, empty_string, 0, 1); + /* skip ahead the length of the regex match */ + strp += subs[0].rm_eo; + } else if (subs[0].rm_so == 0 && subs[0].rm_eo == 0) { + /* No more matches */ + regfree(&re); + php_error(E_WARNING, "Invalid Regular Expression to split()"); + zend_hash_destroy(Z_ARRVAL_P(return_value)); + efree(Z_ARRVAL_P(return_value)); + RETURN_FALSE; + } else { + /* On a real match */ + + /* make a copy of the substring */ + size = subs[0].rm_so; + + /* add it to the array */ + add_next_index_stringl(return_value, strp, size, 1); + + /* point at our new starting point */ + strp = strp + subs[0].rm_eo; + } + + /* if we're only looking for a certain number of points, + stop looking once we hit it */ + if (count != -1) { + count--; + } + } + + /* see if we encountered an error */ + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + regfree(&re); + zend_hash_destroy(Z_ARRVAL_P(return_value)); + efree(Z_ARRVAL_P(return_value)); + RETURN_FALSE; + } + + /* otherwise we just have one last element to add to the array */ + size = endp - strp; + + add_next_index_stringl(return_value, strp, size, 1); + + regfree(&re); +} +/* }}} */ + +/* {{{ proto array split(string pattern, string string [, int limit]) + Split string into array by regular expression */ +PHP_FUNCTION(split) +{ + php_split(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto array spliti(string pattern, string string [, int limit]) + Split string into array by regular expression case-insensitive */ + +PHP_FUNCTION(spliti) +{ + php_split(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} + +/* }}} */ + +/* {{{ proto string sql_regcase(string string) + Make regular expression for case insensitive match */ +PHPAPI PHP_FUNCTION(sql_regcase) +{ + zval **string; + char *tmp; + unsigned char c; + register int i, j; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &string)==FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_string_ex(string); + + tmp = emalloc((Z_STRLEN_PP(string) * 4) + 1); + + for (i = j = 0; i < Z_STRLEN_PP(string); i++) { + c = (unsigned char) Z_STRVAL_PP(string)[i]; + if(isalpha(c)) { + tmp[j++] = '['; + tmp[j++] = toupper(c); + tmp[j++] = tolower(c); + tmp[j++] = ']'; + } else { + tmp[j++] = c; + } + } + tmp[j] = 0; + + RETVAL_STRINGL(tmp, j, 1); + efree(tmp); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/395434_d3a0db_ext#date#lib#parse_tz.c b/src/main/resource/testFiles/php-src/revFiles/395434_d3a0db_ext#date#lib#parse_tz.c new file mode 100644 index 0000000..73e293f --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/395434_d3a0db_ext#date#lib#parse_tz.c @@ -0,0 +1,384 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2008 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Derick Rethans | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "timelib.h" + +#include + +#ifdef HAVE_STRING_H +#include +#else +#include +#endif +#include "timezonedb.h" + +#if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__)) +# if defined(__LITTLE_ENDIAN__) +# undef WORDS_BIGENDIAN +# else +# if defined(__BIG_ENDIAN__) +# define WORDS_BIGENDIAN +# endif +# endif +#endif + +#ifdef WORDS_BIGENDIAN +#define timelib_conv_int(l) (l) +#else +#define timelib_conv_int(l) ((l & 0x000000ff) << 24) + ((l & 0x0000ff00) << 8) + ((l & 0x00ff0000) >> 8) + ((l & 0xff000000) >> 24) +#endif + +static void read_header(char **tzf, timelib_tzinfo *tz) +{ + uint32_t buffer[6]; + + memcpy(&buffer, *tzf, sizeof(buffer)); + tz->ttisgmtcnt = timelib_conv_int(buffer[0]); + tz->ttisstdcnt = timelib_conv_int(buffer[1]); + tz->leapcnt = timelib_conv_int(buffer[2]); + tz->timecnt = timelib_conv_int(buffer[3]); + tz->typecnt = timelib_conv_int(buffer[4]); + tz->charcnt = timelib_conv_int(buffer[5]); + *tzf += sizeof(buffer); +} + +static void read_transistions(char **tzf, timelib_tzinfo *tz) +{ + int32_t *buffer = NULL; + uint32_t i; + unsigned char *cbuffer = NULL; + + if (tz->timecnt) { + buffer = (int32_t*) malloc(tz->timecnt * sizeof(int32_t)); + if (!buffer) { + return; + } + memcpy(buffer, *tzf, sizeof(int32_t) * tz->timecnt); + *tzf += (sizeof(int32_t) * tz->timecnt); + for (i = 0; i < tz->timecnt; i++) { + buffer[i] = timelib_conv_int(buffer[i]); + } + + cbuffer = (unsigned char*) malloc(tz->timecnt * sizeof(unsigned char)); + if (!cbuffer) { + return; + } + memcpy(cbuffer, *tzf, sizeof(unsigned char) * tz->timecnt); + *tzf += sizeof(unsigned char) * tz->timecnt; + } + + tz->trans = buffer; + tz->trans_idx = cbuffer; +} + +static void read_types(char **tzf, timelib_tzinfo *tz) +{ + unsigned char *buffer; + int32_t *leap_buffer; + unsigned int i, j; + + buffer = (unsigned char*) malloc(tz->typecnt * sizeof(unsigned char) * 6); + if (!buffer) { + return; + } + memcpy(buffer, *tzf, sizeof(unsigned char) * 6 * tz->typecnt); + *tzf += sizeof(unsigned char) * 6 * tz->typecnt; + + tz->type = (ttinfo*) malloc(tz->typecnt * sizeof(struct ttinfo)); + if (!tz->type) { + return; + } + + for (i = 0; i < tz->typecnt; i++) { + j = i * 6; + tz->type[i].offset = (buffer[j] * 16777216) + (buffer[j + 1] * 65536) + (buffer[j + 2] * 256) + buffer[j + 3]; + tz->type[i].isdst = buffer[j + 4]; + tz->type[i].abbr_idx = buffer[j + 5]; + } + free(buffer); + + tz->timezone_abbr = (char*) malloc(tz->charcnt); + if (!tz->timezone_abbr) { + return; + } + memcpy(tz->timezone_abbr, *tzf, sizeof(char) * tz->charcnt); + *tzf += sizeof(char) * tz->charcnt; + + leap_buffer = (int32_t *) malloc(tz->leapcnt * 2 * sizeof(int32_t)); + if (!leap_buffer) { + return; + } + memcpy(leap_buffer, *tzf, sizeof(int32_t) * tz->leapcnt * 2); + *tzf += sizeof(int32_t) * tz->leapcnt * 2; + + tz->leap_times = (tlinfo*) malloc(tz->leapcnt * sizeof(tlinfo)); + if (!tz->leap_times) { + return; + } + for (i = 0; i < tz->leapcnt; i++) { + tz->leap_times[i].trans = timelib_conv_int(leap_buffer[i * 2]); + tz->leap_times[i].offset = timelib_conv_int(leap_buffer[i * 2 + 1]); + } + free(leap_buffer); + + buffer = (unsigned char*) malloc(tz->ttisstdcnt * sizeof(unsigned char)); + if (!buffer) { + return; + } + memcpy(buffer, *tzf, sizeof(unsigned char) * tz->ttisstdcnt); + *tzf += sizeof(unsigned char) * tz->ttisstdcnt; + + for (i = 0; i < tz->ttisstdcnt; i++) { + tz->type[i].isstdcnt = buffer[i]; + } + free(buffer); + + buffer = (unsigned char*) malloc(tz->ttisgmtcnt * sizeof(unsigned char)); + if (!buffer) { + return; + } + memcpy(buffer, *tzf, sizeof(unsigned char) * tz->ttisgmtcnt); + *tzf += sizeof(unsigned char) * tz->ttisgmtcnt; + + for (i = 0; i < tz->ttisgmtcnt; i++) { + tz->type[i].isgmtcnt = buffer[i]; + } + free(buffer); +} + +void timelib_dump_tzinfo(timelib_tzinfo *tz) +{ + uint32_t i; + + printf("UTC/Local count: %lu\n", (unsigned long) tz->ttisgmtcnt); + printf("Std/Wall count: %lu\n", (unsigned long) tz->ttisstdcnt); + printf("Leap.sec. count: %lu\n", (unsigned long) tz->leapcnt); + printf("Trans. count: %lu\n", (unsigned long) tz->timecnt); + printf("Local types count: %lu\n", (unsigned long) tz->typecnt); + printf("Zone Abbr. count: %lu\n", (unsigned long) tz->charcnt); + + printf ("%8s (%12s) = %3d [%5ld %1d %3d '%s' (%d,%d)]\n", + "", "", 0, + (long int) tz->type[0].offset, + tz->type[0].isdst, + tz->type[0].abbr_idx, + &tz->timezone_abbr[tz->type[0].abbr_idx], + tz->type[0].isstdcnt, + tz->type[0].isgmtcnt + ); + for (i = 0; i < tz->timecnt; i++) { + printf ("%08X (%12d) = %3d [%5ld %1d %3d '%s' (%d,%d)]\n", + tz->trans[i], tz->trans[i], tz->trans_idx[i], + (long int) tz->type[tz->trans_idx[i]].offset, + tz->type[tz->trans_idx[i]].isdst, + tz->type[tz->trans_idx[i]].abbr_idx, + &tz->timezone_abbr[tz->type[tz->trans_idx[i]].abbr_idx], + tz->type[tz->trans_idx[i]].isstdcnt, + tz->type[tz->trans_idx[i]].isgmtcnt + ); + } + for (i = 0; i < tz->leapcnt; i++) { + printf ("%08X (%12ld) = %d\n", + tz->leap_times[i].trans, + (long) tz->leap_times[i].trans, + tz->leap_times[i].offset); + } +} + +static int seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb) +{ + int left = 0, right = tzdb->index_size - 1; + + do { + int mid = ((unsigned)left + right) >> 1; + int cmp = strcasecmp(timezone, tzdb->index[mid].id); + + if (cmp < 0) { + right = mid - 1; + } else if (cmp > 0) { + left = mid + 1; + } else { /* (cmp == 0) */ + (*tzf) = &(tzdb->data[tzdb->index[mid].pos + 20]); + return 1; + } + + } while (left <= right); + + return 0; +} + +const timelib_tzdb *timelib_builtin_db(void) +{ + return &timezonedb_builtin; +} + +const timelib_tzdb_index_entry *timelib_timezone_builtin_identifiers_list(int *count) +{ + *count = sizeof(timezonedb_idx_builtin) / sizeof(*timezonedb_idx_builtin); + return timezonedb_idx_builtin; +} + +int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb) +{ + const unsigned char *tzf; + return (seek_to_tz_position(&tzf, timezone, tzdb)); +} + +timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb) +{ + const unsigned char *tzf; + timelib_tzinfo *tmp; + + if (seek_to_tz_position(&tzf, timezone, tzdb)) { + tmp = timelib_tzinfo_ctor(timezone); + + read_header((char**) &tzf, tmp); + read_transistions((char**) &tzf, tmp); + read_types((char**) &tzf, tmp); + } else { + tmp = NULL; + } + + return tmp; +} + +static ttinfo* fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time) +{ + uint32_t i; + + /* If there is no transistion time, we pick the first one, if that doesn't + * exist we return NULL */ + if (!tz->timecnt || !tz->trans) { + *transition_time = 0; + if (tz->typecnt == 1) { + return &(tz->type[0]); + } + return NULL; + } + + /* If the TS is lower than the first transistion time, then we scan over + * all the transistion times to find the first non-DST one, or the first + * one in case there are only DST entries. Not sure which smartass came up + * with this idea in the first though :) */ + if (ts < tz->trans[0]) { + uint32_t j; + + *transition_time = 0; + j = 0; + while (j < tz->timecnt && tz->type[j].isdst) { + ++j; + } + if (j == tz->timecnt) { + j = 0; + } + return &(tz->type[j]); + } + + /* In all other cases we loop through the available transtion times to find + * the correct entry */ + for (i = 0; i < tz->timecnt; i++) { + if (ts < tz->trans[i]) { + *transition_time = tz->trans[i - 1]; + return &(tz->type[tz->trans_idx[i - 1]]); + } + } + *transition_time = tz->trans[tz->timecnt - 1]; + return &(tz->type[tz->trans_idx[tz->timecnt - 1]]); +} + +static tlinfo* fetch_leaptime_offset(timelib_tzinfo *tz, timelib_sll ts) +{ + int i; + + if (!tz->leapcnt || !tz->leap_times) { + return NULL; + } + + for (i = tz->leapcnt - 1; i > 0; i--) { + if (ts > tz->leap_times[i].trans) { + return &(tz->leap_times[i]); + } + } + return NULL; +} + +int timelib_timestamp_is_in_dst(timelib_sll ts, timelib_tzinfo *tz) +{ + ttinfo *to; + timelib_sll dummy; + + if ((to = fetch_timezone_offset(tz, ts, &dummy))) { + return to->isdst; + } + return -1; +} + +timelib_time_offset *timelib_get_time_zone_info(timelib_sll ts, timelib_tzinfo *tz) +{ + ttinfo *to; + tlinfo *tl; + int32_t offset = 0, leap_secs = 0; + char *abbr; + timelib_time_offset *tmp = timelib_time_offset_ctor(); + timelib_sll transistion_time; + + if ((to = fetch_timezone_offset(tz, ts, &transistion_time))) { + offset = to->offset; + abbr = &(tz->timezone_abbr[to->abbr_idx]); + tmp->is_dst = to->isdst; + tmp->transistion_time = transistion_time; + } else { + offset = 0; + abbr = tz->timezone_abbr; + tmp->is_dst = 0; + tmp->transistion_time = 0; + } + + if ((tl = fetch_leaptime_offset(tz, ts))) { + leap_secs = -tl->offset; + } + + tmp->offset = offset; + tmp->leap_secs = leap_secs; + tmp->abbr = abbr ? strdup(abbr) : strdup("GMT"); + + return tmp; +} + +timelib_sll timelib_get_current_offset(timelib_time *t) +{ + timelib_time_offset *gmt_offset; + timelib_sll retval; + + switch (t->zone_type) { + case TIMELIB_ZONETYPE_ABBR: + case TIMELIB_ZONETYPE_OFFSET: + return (t->z + t->dst) * -60; + + case TIMELIB_ZONETYPE_ID: + gmt_offset = timelib_get_time_zone_info(t->sse, t->tz_info); + retval = gmt_offset->offset; + timelib_time_offset_dtor(gmt_offset); + return retval; + + default: + return 0; + } +} diff --git a/src/main/resource/testFiles/php-src/revFiles/470a39_b38730_Zend#zend_stream.c b/src/main/resource/testFiles/php-src/revFiles/470a39_b38730_Zend#zend_stream.c new file mode 100644 index 0000000..1998aa8 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/470a39_b38730_Zend#zend_stream.c @@ -0,0 +1,343 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2008 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Wez Furlong | + | Scott MacVicar | + | Nuno Lopes | + | Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + + +#include "zend.h" +#include "zend_compile.h" + +#include +#include +#if HAVE_SYS_MMAN_H +# include +#endif + +ZEND_DLIMPORT int isatty(int fd); + +static size_t zend_stream_stdio_reader(void *handle, char *buf, size_t len TSRMLS_DC) /* {{{ */ +{ + return fread(buf, 1, len, (FILE*)handle); +} +/* }}} */ + +static void zend_stream_stdio_closer(void *handle TSRMLS_DC) /* {{{ */ +{ + if (handle && (FILE*)handle != stdin) { + fclose((FILE*)handle); + } +} +/* }}} */ + +static size_t zend_stream_stdio_fsizer(void *handle TSRMLS_DC) /* {{{ */ +{ + struct stat buf; + if (handle && fstat(fileno((FILE*)handle), &buf) == 0) { +#ifdef S_ISREG + if (!S_ISREG(buf.st_mode)) { + return 0; + } +#endif + return buf.st_size; + } + return 0; +} +/* }}} */ + +static void zend_stream_unmap(zend_stream *stream TSRMLS_DC) { /* {{{ */ +#if HAVE_MMAP + if (stream->mmap.map) { + munmap(stream->mmap.map, stream->mmap.len); + } else +#endif + if (stream->mmap.buf) { + efree(stream->mmap.buf); + } + stream->mmap.len = 0; + stream->mmap.pos = 0; + stream->mmap.map = 0; + stream->mmap.buf = 0; + stream->handle = stream->mmap.old_handle; +} /* }}} */ + +static void zend_stream_mmap_closer(zend_stream *stream TSRMLS_DC) /* {{{ */ +{ + zend_stream_unmap(stream TSRMLS_CC); + if (stream->mmap.old_closer && stream->handle) { + stream->mmap.old_closer(stream->handle TSRMLS_CC); + } +} /* }}} */ + +static inline int zend_stream_is_mmap(zend_file_handle *file_handle) { /* {{{ */ + return file_handle->type == ZEND_HANDLE_MAPPED; +} /* }}} */ + +static size_t zend_stream_fsize(zend_file_handle *file_handle TSRMLS_DC) /* {{{ */ +{ + struct stat buf; + + if (zend_stream_is_mmap(file_handle)) { + return file_handle->handle.stream.mmap.len; + } + if (file_handle->type == ZEND_HANDLE_STREAM || file_handle->type == ZEND_HANDLE_MAPPED) { + return file_handle->handle.stream.fsizer(file_handle->handle.stream.handle TSRMLS_CC); + } + if (file_handle->handle.fp && fstat(fileno(file_handle->handle.fp), &buf) == 0) { +#ifdef S_ISREG + if (!S_ISREG(buf.st_mode)) { + return 0; + } +#endif + return buf.st_size; + } + + return -1; +} /* }}} */ + +ZEND_API int zend_stream_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* {{{ */ +{ + if (zend_stream_open_function) { + return zend_stream_open_function(filename, handle TSRMLS_CC); + } + handle->type = ZEND_HANDLE_FP; + handle->opened_path = NULL; + handle->handle.fp = zend_fopen(filename, &handle->opened_path TSRMLS_CC); + handle->filename = (char *)filename; + handle->free_filename = 0; + memset(&handle->handle.stream.mmap, 0, sizeof(zend_mmap)); + + return (handle->handle.fp) ? SUCCESS : FAILURE; +} +/* }}} */ + +ZEND_API int zend_stream_getc(zend_file_handle *file_handle TSRMLS_DC) /* {{{ */ +{ + char buf; + if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf) TSRMLS_CC)) { + return (int)buf; + } + return EOF; +} +/* }}} */ + +static size_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len TSRMLS_DC) /* {{{ */ +{ + if (!zend_stream_is_mmap(file_handle) && file_handle->handle.stream.isatty) { + int c = '*'; + size_t n; + +#ifdef NETWARE + /* + c != 4 check is there as fread of a character in NetWare LibC gives 4 upon ^D character. + Ascii value 4 is actually EOT character which is not defined anywhere in the LibC + or else we can use instead of hardcoded 4. + */ + for (n = 0; n < len && (c = zend_stream_getc(file_handle TSRMLS_CC)) != EOF && c != 4 && c != '\n'; ++n) { +#else + for (n = 0; n < len && (c = zend_stream_getc(file_handle TSRMLS_CC)) != EOF && c != '\n'; ++n) { +#endif + buf[n] = (char)c; + } + if (c == '\n') { + buf[n++] = (char)c; + } + + return n; + } + return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len TSRMLS_CC); +} +/* }}} */ + +ZEND_API int zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len TSRMLS_DC) /* {{{ */ +{ + size_t size; + zend_stream_type old_type; + + if (file_handle->type == ZEND_HANDLE_FILENAME) { + if (zend_stream_open(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) { + return FAILURE; + } + } + + switch (file_handle->type) { + case ZEND_HANDLE_FD: + file_handle->type = ZEND_HANDLE_FP; + file_handle->handle.fp = fdopen(file_handle->handle.fd, "rb"); + /* no break; */ + case ZEND_HANDLE_FP: + if (!file_handle->handle.fp) { + return FAILURE; + } + memset(&file_handle->handle.stream.mmap, 0, sizeof(zend_mmap)); + file_handle->handle.stream.isatty = isatty(fileno((FILE *)file_handle->handle.stream.handle)) ? 1 : 0; + file_handle->handle.stream.reader = (zend_stream_reader_t)zend_stream_stdio_reader; + file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_stdio_closer; + file_handle->handle.stream.fsizer = (zend_stream_fsizer_t)zend_stream_stdio_fsizer; + memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap)); + /* no break; */ + case ZEND_HANDLE_STREAM: + /* nothing to do */ + break; + + case ZEND_HANDLE_MAPPED: + file_handle->handle.stream.mmap.pos = 0; + *buf = file_handle->handle.stream.mmap.buf; + *len = file_handle->handle.stream.mmap.len; + return SUCCESS; + + default: + return FAILURE; + } + + size = zend_stream_fsize(file_handle TSRMLS_CC); + if (size == (size_t)-1) { + return FAILURE; + } + + old_type = file_handle->type; + file_handle->type = ZEND_HANDLE_STREAM; /* we might still be _FP but we need fsize() work */ + + if (old_type == ZEND_HANDLE_FP && !file_handle->handle.stream.isatty && size) { +#if HAVE_MMAP + if (file_handle->handle.fp) { + /* *buf[size] is zeroed automatically by the kernel */ + *buf = mmap(0, size + ZEND_MMAP_AHEAD, PROT_READ, MAP_PRIVATE, fileno(file_handle->handle.fp), 0); + if (*buf != MAP_FAILED) { + file_handle->handle.stream.mmap.len = size; + file_handle->handle.stream.mmap.map = *buf; + file_handle->handle.stream.mmap.buf = *buf; + goto return_mapped; + } + } +#endif + file_handle->handle.stream.mmap.map = 0; + file_handle->handle.stream.mmap.buf = *buf = safe_emalloc(1, size, ZEND_MMAP_AHEAD); + file_handle->handle.stream.mmap.len = zend_stream_read(file_handle, *buf, size TSRMLS_CC); + } else { + size_t read, remain = 4*1024; + *buf = emalloc(remain); + size = 0; + + while ((read = zend_stream_read(file_handle, *buf + size, remain TSRMLS_CC)) > 0) { + size += read; + remain -= read; + + if (remain == 0) { + *buf = safe_erealloc(*buf, size, 2, 0); + remain = size; + } + } + file_handle->handle.stream.mmap.map = 0; + file_handle->handle.stream.mmap.len = size; + if (size && remain < ZEND_MMAP_AHEAD) { + *buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD); + } + file_handle->handle.stream.mmap.buf = *buf; + } + + if (file_handle->handle.stream.mmap.len == 0) { + *buf = erealloc(*buf, ZEND_MMAP_AHEAD); + file_handle->handle.stream.mmap.buf = *buf; + } + + if (ZEND_MMAP_AHEAD) { + memset(file_handle->handle.stream.mmap.buf + file_handle->handle.stream.mmap.len, 0, ZEND_MMAP_AHEAD); + } + +return_mapped: + file_handle->type = ZEND_HANDLE_MAPPED; + file_handle->handle.stream.mmap.pos = 0; + file_handle->handle.stream.mmap.old_handle = file_handle->handle.stream.handle; + file_handle->handle.stream.mmap.old_closer = file_handle->handle.stream.closer; + file_handle->handle.stream.handle = &file_handle->handle.stream; + file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_mmap_closer; + + *buf = file_handle->handle.stream.mmap.buf; + *len = file_handle->handle.stream.mmap.len; + + return SUCCESS; +} +/* }}} */ + +ZEND_API void zend_file_handle_dtor(zend_file_handle *fh TSRMLS_DC) /* {{{ */ + { + switch (fh->type) { + case ZEND_HANDLE_FD: + /* nothing to do */ + break; + case ZEND_HANDLE_FP: + fclose(fh->handle.fp); + break; + case ZEND_HANDLE_STREAM: + case ZEND_HANDLE_MAPPED: + if (fh->handle.stream.closer && fh->handle.stream.handle) { + fh->handle.stream.closer(fh->handle.stream.handle TSRMLS_CC); + } + fh->handle.stream.handle = NULL; + break; + case ZEND_HANDLE_FILENAME: + /* We're only supposed to get here when destructing the used_files hash, + * which doesn't really contain open files, but references to their names/paths + */ + break; + } + if (fh->opened_path) { + efree(fh->opened_path); + fh->opened_path = NULL; + } + if (fh->free_filename && fh->filename) { + efree(fh->filename); + fh->filename = NULL; + } +} +/* }}} */ + +ZEND_API int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */ + { + if (fh1->type != fh2->type) { + return 0; + } + switch (fh1->type) { + case ZEND_HANDLE_FD: + return fh1->handle.fd == fh2->handle.fd; + case ZEND_HANDLE_FP: + return fh1->handle.fp == fh2->handle.fp; + case ZEND_HANDLE_STREAM: + return fh1->handle.stream.handle == fh2->handle.stream.handle; + case ZEND_HANDLE_MAPPED: + return (fh1->handle.stream.handle == &fh1->handle.stream && + fh2->handle.stream.handle == &fh2->handle.stream && + fh1->handle.stream.mmap.old_handle == fh2->handle.stream.mmap.old_handle) + || fh1->handle.stream.handle == fh2->handle.stream.handle; + default: + return 0; + } + return 0; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/499b55_595741_Zend#zend_execute_API.c b/src/main/resource/testFiles/php-src/revFiles/499b55_595741_Zend#zend_execute_API.c new file mode 100644 index 0000000..425a047 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/499b55_595741_Zend#zend_execute_API.c @@ -0,0 +1,1748 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2014 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include +#include + +#include "zend.h" +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_API.h" +#include "zend_stack.h" +#include "zend_constants.h" +#include "zend_extensions.h" +#include "zend_exceptions.h" +#include "zend_closures.h" +#include "zend_generators.h" +#include "zend_vm.h" +#include "zend_float.h" +#ifdef HAVE_SYS_TIME_H +#include +#endif + +ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data TSRMLS_DC); +ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data_ptr, zend_fcall_info *fci, int return_value_used TSRMLS_DC); + +/* true globals */ +ZEND_API const zend_fcall_info empty_fcall_info = { 0, NULL, {{0},0}, NULL, NULL, 0, NULL, NULL, 0 }; +ZEND_API const zend_fcall_info_cache empty_fcall_info_cache = { 0, NULL, NULL, NULL, NULL }; + +#ifdef ZEND_WIN32 +#include +static WNDCLASS wc; +static HWND timeout_window; +static HANDLE timeout_thread_event; +static HANDLE timeout_thread_handle; +static DWORD timeout_thread_id; +static int timeout_thread_initialized=0; +#endif + +#if 0&&ZEND_DEBUG +static void (*original_sigsegv_handler)(int); +static void zend_handle_sigsegv(int dummy) /* {{{ */ +{ + fflush(stdout); + fflush(stderr); + if (original_sigsegv_handler == zend_handle_sigsegv) { + signal(SIGSEGV, original_sigsegv_handler); + } else { + signal(SIGSEGV, SIG_DFL); + } + { + TSRMLS_FETCH(); + + fprintf(stderr, "SIGSEGV caught on opcode %d on opline %d of %s() at %s:%d\n\n", + active_opline->opcode, + active_opline-EG(active_op_array)->opcodes, + get_active_function_name(TSRMLS_C), + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C)); +/* See http://support.microsoft.com/kb/190351 */ +#ifdef PHP_WIN32 + fflush(stderr); +#endif + } + if (original_sigsegv_handler!=zend_handle_sigsegv) { + original_sigsegv_handler(dummy); + } +} +/* }}} */ +#endif + +static void zend_extension_activator(zend_extension *extension TSRMLS_DC) /* {{{ */ +{ + if (extension->activate) { + extension->activate(); + } +} +/* }}} */ + +static void zend_extension_deactivator(zend_extension *extension TSRMLS_DC) /* {{{ */ +{ + if (extension->deactivate) { + extension->deactivate(); + } +} +/* }}} */ + +static int clean_non_persistent_function(zend_function *function TSRMLS_DC) /* {{{ */ +{ + return (function->type == ZEND_INTERNAL_FUNCTION) ? ZEND_HASH_APPLY_STOP : ZEND_HASH_APPLY_REMOVE; +} +/* }}} */ + +static int clean_non_persistent_function_full(zend_function *function TSRMLS_DC) /* {{{ */ +{ + return (function->type == ZEND_INTERNAL_FUNCTION) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; +} +/* }}} */ + +static int clean_non_persistent_class(zend_class_entry **ce TSRMLS_DC) /* {{{ */ +{ + return ((*ce)->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY_STOP : ZEND_HASH_APPLY_REMOVE; +} +/* }}} */ + +static int clean_non_persistent_class_full(zend_class_entry **ce TSRMLS_DC) /* {{{ */ +{ + return ((*ce)->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; +} +/* }}} */ + +void init_executor(TSRMLS_D) /* {{{ */ +{ + zval tmp; + zend_init_fpu(TSRMLS_C); + + ZVAL_NULL(&EG(uninitialized_zval)); + /* trick to make uninitialized_zval never be modified, passed by ref, etc. */ + ZVAL_NULL(&EG(error_zval)); +/* destroys stack frame, therefore makes core dumps worthless */ +#if 0&&ZEND_DEBUG + original_sigsegv_handler = signal(SIGSEGV, zend_handle_sigsegv); +#endif + + EG(symtable_cache_ptr) = EG(symtable_cache) - 1; + EG(symtable_cache_limit) = EG(symtable_cache) + SYMTABLE_CACHE_SIZE - 1; + EG(no_extensions) = 0; + + EG(function_table) = CG(function_table); + EG(class_table) = CG(class_table); + + EG(in_execution) = 0; + EG(in_autoload) = NULL; + EG(autoload_func) = NULL; + EG(error_handling) = EH_NORMAL; + + zend_vm_stack_init(TSRMLS_C); + ZVAL_LONG(&tmp, 0); + zend_vm_stack_push(&tmp TSRMLS_CC); + + zend_hash_init(&EG(symbol_table).ht, 50, NULL, ZVAL_PTR_DTOR, 0); + EG(active_symbol_table) = &EG(symbol_table).ht; + + zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_activator TSRMLS_CC); + EG(opline_ptr) = NULL; + + zend_hash_init(&EG(included_files), 5, NULL, NULL, 0); + + EG(ticks_count) = 0; + + ZVAL_UNDEF(&EG(user_error_handler)); + + EG(current_execute_data) = NULL; + + zend_stack_init(&EG(user_error_handlers_error_reporting)); + zend_stack_init(&EG(user_error_handlers)); + zend_stack_init(&EG(user_exception_handlers)); + + zend_objects_store_init(&EG(objects_store), 1024); + + EG(full_tables_cleanup) = 0; +#ifdef ZEND_WIN32 + EG(timed_out) = 0; +#endif + + EG(exception) = NULL; + EG(prev_exception) = NULL; + + EG(scope) = NULL; + EG(called_scope) = NULL; + + ZVAL_UNDEF(&EG(This)); + + EG(active_op_array) = NULL; + + EG(active) = 1; + EG(start_op) = NULL; +} +/* }}} */ + +static int zval_call_destructor(zval *zv TSRMLS_DC) /* {{{ */ +{ + if (Z_TYPE_P(zv) == IS_OBJECT && Z_REFCOUNT_P(zv) == 1) { + return ZEND_HASH_APPLY_REMOVE; + } else { + return ZEND_HASH_APPLY_KEEP; + } +} +/* }}} */ + +void shutdown_destructors(TSRMLS_D) /* {{{ */ +{ + zend_try { + int symbols; + do { + symbols = zend_hash_num_elements(&EG(symbol_table).ht); + zend_hash_reverse_apply(&EG(symbol_table).ht, (apply_func_t) zval_call_destructor TSRMLS_CC); + } while (symbols != zend_hash_num_elements(&EG(symbol_table).ht)); + zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC); + } zend_catch { + /* if we couldn't destruct cleanly, mark all objects as destructed anyway */ + zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC); + } zend_end_try(); +} +/* }}} */ + +void shutdown_executor(TSRMLS_D) /* {{{ */ +{ + zend_try { + +/* Removed because this can not be safely done, e.g. in this situation: + Object 1 creates object 2 + Object 3 holds reference to object 2. + Now when 1 and 2 are destroyed, 3 can still access 2 in its destructor, with + very problematic results */ +/* zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC); */ + +/* Moved after symbol table cleaners, because some of the cleaners can call + destructors, which would use EG(symtable_cache_ptr) and thus leave leaks */ +/* while (EG(symtable_cache_ptr)>=EG(symtable_cache)) { + zend_hash_destroy(*EG(symtable_cache_ptr)); + efree(*EG(symtable_cache_ptr)); + EG(symtable_cache_ptr)--; + } +*/ + zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_deactivator TSRMLS_CC); + zend_hash_graceful_reverse_destroy(&EG(symbol_table).ht); + } zend_end_try(); + + zend_try { + zval *zeh; + /* remove error handlers before destroying classes and functions, + * so that if handler used some class, crash would not happen */ + if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { + zeh = &EG(user_error_handler); + zval_ptr_dtor(zeh); + ZVAL_UNDEF(&EG(user_error_handler)); + } + + if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { + zeh = &EG(user_exception_handler); + zval_ptr_dtor(zeh); + ZVAL_UNDEF(&EG(user_exception_handler)); + } + + zend_stack_destroy(&EG(user_error_handlers_error_reporting)); + zend_stack_init(&EG(user_error_handlers_error_reporting)); + zend_stack_clean(&EG(user_error_handlers), (void (*)(void *))ZVAL_DESTRUCTOR, 1); + zend_stack_clean(&EG(user_exception_handlers), (void (*)(void *))ZVAL_DESTRUCTOR, 1); + } zend_end_try(); + + zend_try { + /* Cleanup static data for functions and arrays. + * We need a separate cleanup stage because of the following problem: + * Suppose we destroy class X, which destroys the class's function table, + * and in the function table we have function foo() that has static $bar. + * Now if an object of class X is assigned to $bar, its destructor will be + * called and will fail since X's function table is in mid-destruction. + * So we want first of all to clean up all data and then move to tables destruction. + * Note that only run-time accessed data need to be cleaned up, pre-defined data can + * not contain objects and thus are not probelmatic */ + if (EG(full_tables_cleanup)) { + zend_hash_apply(EG(function_table), (apply_func_t) zend_cleanup_function_data_full TSRMLS_CC); + zend_hash_apply(EG(class_table), (apply_func_t) zend_cleanup_class_data TSRMLS_CC); + } else { + zend_hash_reverse_apply(EG(function_table), (apply_func_t) zend_cleanup_function_data TSRMLS_CC); + zend_hash_reverse_apply(EG(class_table), (apply_func_t) zend_cleanup_user_class_data TSRMLS_CC); + zend_cleanup_internal_classes(TSRMLS_C); + } + } zend_end_try(); + + zend_try { + zend_objects_store_free_object_storage(&EG(objects_store) TSRMLS_CC); + + zend_vm_stack_destroy(TSRMLS_C); + + /* Destroy all op arrays */ + if (EG(full_tables_cleanup)) { + zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC); + zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC); + } else { + zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function TSRMLS_CC); + zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class TSRMLS_CC); + } + + while (EG(symtable_cache_ptr)>=EG(symtable_cache)) { + zend_hash_destroy(*EG(symtable_cache_ptr)); + FREE_HASHTABLE(*EG(symtable_cache_ptr)); + EG(symtable_cache_ptr)--; + } + } zend_end_try(); + + zend_try { + clean_non_persistent_constants(TSRMLS_C); + } zend_end_try(); + + zend_try { +#if 0&&ZEND_DEBUG + signal(SIGSEGV, original_sigsegv_handler); +#endif + + zend_hash_destroy(&EG(included_files)); + + zend_stack_destroy(&EG(user_error_handlers_error_reporting)); + zend_stack_destroy(&EG(user_error_handlers)); + zend_stack_destroy(&EG(user_exception_handlers)); + zend_objects_store_destroy(&EG(objects_store)); + if (EG(in_autoload)) { + zend_hash_destroy(EG(in_autoload)); + FREE_HASHTABLE(EG(in_autoload)); + } + } zend_end_try(); + + zend_shutdown_fpu(TSRMLS_C); + + EG(active) = 0; +} +/* }}} */ + +/* return class name and "::" or "". */ +ZEND_API const char *get_active_class_name(const char **space TSRMLS_DC) /* {{{ */ +{ + if (!zend_is_executing(TSRMLS_C)) { + if (space) { + *space = ""; + } + return ""; + } + switch (EG(current_execute_data)->function_state.function->type) { + case ZEND_USER_FUNCTION: + case ZEND_INTERNAL_FUNCTION: + { + zend_class_entry *ce = EG(current_execute_data)->function_state.function->common.scope; + + if (space) { + *space = ce ? "::" : ""; + } + return ce ? ce->name->val : ""; + } + default: + if (space) { + *space = ""; + } + return ""; + } +} +/* }}} */ + +ZEND_API const char *get_active_function_name(TSRMLS_D) /* {{{ */ +{ + if (!zend_is_executing(TSRMLS_C)) { + return NULL; + } + switch (EG(current_execute_data)->function_state.function->type) { + case ZEND_USER_FUNCTION: { + zend_string *function_name = ((zend_op_array *) EG(current_execute_data)->function_state.function)->function_name; + + if (function_name) { + return function_name->val; + } else { + return "main"; + } + } + break; + case ZEND_INTERNAL_FUNCTION: + return ((zend_internal_function *) EG(current_execute_data)->function_state.function)->function_name->val; + break; + default: + return NULL; + } +} +/* }}} */ + +ZEND_API const char *zend_get_executed_filename(TSRMLS_D) /* {{{ */ +{ + if (EG(active_op_array)) { + return EG(active_op_array)->filename->val; + } else { + return "[no active file]"; + } +} +/* }}} */ + +ZEND_API uint zend_get_executed_lineno(TSRMLS_D) /* {{{ */ +{ + if(EG(exception) && EG(opline_ptr) && active_opline->opcode == ZEND_HANDLE_EXCEPTION && + active_opline->lineno == 0 && EG(opline_before_exception)) { + return EG(opline_before_exception)->lineno; + } + if (EG(opline_ptr)) { + return active_opline->lineno; + } else { + return 0; + } +} +/* }}} */ + +ZEND_API zend_bool zend_is_executing(TSRMLS_D) /* {{{ */ +{ + return EG(in_execution); +} +/* }}} */ + +ZEND_API void _zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) /* {{{ */ +{ + TSRMLS_FETCH(); + i_zval_ptr_dtor(zval_ptr ZEND_FILE_LINE_RELAY_CC TSRMLS_CC); +} +/* }}} */ + +ZEND_API void _zval_internal_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) /* {{{ */ +{ + if (IS_REFCOUNTED(Z_TYPE_P(zval_ptr))) { + Z_DELREF_P(zval_ptr); + if (Z_REFCOUNT_P(zval_ptr) == 0) { + _zval_internal_dtor_for_ptr(zval_ptr ZEND_FILE_LINE_CC); + } else if (Z_REFCOUNT_P(zval_ptr) == 1) { + if (Z_ISREF_P(zval_ptr)) { +//??? zend_reference *ref = Z_REF_P(zval_ptr); +//??? ZVAL_COPY_VALUE(zval_ptr, Z_REFVAL_P(zval_ptr)); +//??? efree(ref); + } + } + } +} +/* }}} */ + +ZEND_API int zend_is_true(zval *op TSRMLS_DC) /* {{{ */ +{ + return i_zend_is_true(op TSRMLS_CC); +} +/* }}} */ + +#include "../TSRM/tsrm_strtok_r.h" + +#define IS_VISITED_CONSTANT 0x080 //??? IS_CONSTANT_INDEX +#define IS_CONSTANT_VISITED(p) (Z_TYPE_P(p) & IS_VISITED_CONSTANT) +#define Z_REAL_TYPE_P(p) (Z_TYPE_P(p) & ~IS_VISITED_CONSTANT) +#define MARK_CONSTANT_VISITED(p) Z_TYPE_P(p) |= IS_VISITED_CONSTANT + +static void zval_deep_copy(zval *p) +{ + zval value; + + ZVAL_DUP(&value, p); +//??? Z_TYPE(value) &= ~IS_CONSTANT_INDEX; +//??? zval_copy_ctor(&value); +//??? Z_TYPE(value) = Z_TYPE_P(p); + ZVAL_COPY_VALUE(p, &value); +} + +ZEND_API int zval_update_constant_ex(zval *p, void *arg, zend_class_entry *scope TSRMLS_DC) /* {{{ */ +{ + zend_bool inline_change = (zend_bool) (zend_uintptr_t) arg; + zval const_value; + char *colon; + + if (IS_CONSTANT_VISITED(p)) { + zend_error(E_ERROR, "Cannot declare self-referencing constant '%s'", Z_STRVAL_P(p)); + } else if ((Z_TYPE_P(p) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT) { + int refcount; + zend_uchar is_ref; + + SEPARATE_ZVAL_IF_NOT_REF(p); + + MARK_CONSTANT_VISITED(p); + + refcount = Z_REFCOUNT_P(p); +//??? is_ref = Z_ISREF_P(p); + + if (!zend_get_constant_ex(Z_STRVAL_P(p), Z_STRLEN_P(p), &const_value, scope, Z_REAL_TYPE_P(p) TSRMLS_CC)) { + char *actual = Z_STRVAL_P(p); + + if ((colon = (char*)zend_memrchr(Z_STRVAL_P(p), ':', Z_STRLEN_P(p)))) { + int len; + + zend_error(E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(p)); + len = Z_STRLEN_P(p) - ((colon - Z_STRVAL_P(p)) + 1); + if (inline_change) { + zend_string *tmp = STR_INIT(colon + 1, len, 0); + STR_RELEASE(Z_STR_P(p)); + Z_STR_P(p) = tmp; + } else { +//??? Z_STRVAL_P(p) = colon + 1; + Z_STR_P(p) = STR_INIT(colon + 1, len, 0); + } + } else { + zend_string *save = Z_STR_P(p); + char *slash; + int actual_len = Z_STRLEN_P(p); + if ((Z_TYPE_P(p) & IS_CONSTANT_UNQUALIFIED) && (slash = (char *)zend_memrchr(actual, '\\', actual_len))) { + actual = slash + 1; + actual_len -= (actual - Z_STRVAL_P(p)); + if (inline_change) { + zend_string *s = STR_INIT(actual, actual_len, 0); +//??? STR_RELEASE(Z_STR_P(p)); + Z_STR_P(p) = s; + } + } + if (actual[0] == '\\') { + if (inline_change) { + memmove(Z_STRVAL_P(p), Z_STRVAL_P(p)+1, Z_STRLEN_P(p)); + --Z_STRLEN_P(p); + } else { + ++actual; + } + --actual_len; + } + if ((Z_TYPE_P(p) & IS_CONSTANT_UNQUALIFIED) == 0) { + if (save->val[0] == '\\') { + zend_error(E_ERROR, "Undefined constant '%s'", save->val + 1); + } else { + zend_error(E_ERROR, "Undefined constant '%s'", save->val); + } + if (inline_change) { +//??? STR_RELEASE(save); + } + save = NULL; + } + if (inline_change && save && save->val != actual) { +//??? STR_RELEASE(save); + } + zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'", actual, actual); + p->type = IS_STRING; + if (!inline_change) { + ZVAL_STRINGL(p, actual, actual_len); + } + } + } else { + if (inline_change) { + STR_RELEASE(Z_STR_P(p)); + } + ZVAL_COPY_VALUE(p, &const_value); + } + + if (IS_REFCOUNTED(Z_TYPE_P(p))) Z_SET_REFCOUNT_P(p, refcount); +//??? Z_SET_ISREF_TO_P(p, is_ref); + } else if (Z_TYPE_P(p) == IS_CONSTANT_ARRAY) { + zval *element, new_val; + zend_string *str_index; + ulong num_index; + int ret; + + SEPARATE_ZVAL_IF_NOT_REF(p); + + Z_TYPE_P(p) = IS_ARRAY; + if (!inline_change) { + HashTable *ht = Z_ARRVAL_P(p); + ZVAL_NEW_ARR(p); + zend_hash_init(Z_ARRVAL_P(p), zend_hash_num_elements(ht), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(Z_ARRVAL_P(p), ht, zval_deep_copy); + } + + /* First go over the array and see if there are any constant indices */ + zend_hash_internal_pointer_reset(Z_ARRVAL_P(p)); + while ((element = zend_hash_get_current_data(Z_ARRVAL_P(p))) != NULL) { + if (zend_hash_get_current_key_ex(Z_ARRVAL_P(p), &str_index, &num_index, 0, NULL) != HASH_KEY_IS_STRING) { + zend_hash_move_forward(Z_ARRVAL_P(p)); + continue; + } + if (!(str_index->gc.u.v.flags & (IS_STR_CONSTANT | IS_STR_AST))) { + zend_hash_move_forward(Z_ARRVAL_P(p)); + continue; + } + + if (str_index->gc.u.v.flags & IS_STR_AST) { + zend_ast_evaluate(&const_value, (zend_ast *)str_index->val, scope TSRMLS_CC); + zend_ast_destroy((zend_ast *)str_index->val); +//??? + } else if (!zend_get_constant_ex(str_index->val, str_index->len, &const_value, scope, str_index->gc.u.v.flags TSRMLS_CC)) { + char *actual, *str; + const char *save = str_index->val; + int len; + + str = str_index->val; + len = str_index->len; + if ((colon = (char*)zend_memrchr(str, ':', len))) { + zend_error(E_ERROR, "Undefined class constant '%s'", str); + len -= ((colon - str) + 1); + str = colon; + } else { + if (str_index->gc.u.v.flags & IS_STR_CONSTANT_UNQUALIFIED) { + if ((actual = (char *)zend_memrchr(str, '\\', len))) { + actual++; + len -= (actual - str); + str = actual; + } + } + if (str[0] == '\\') { + ++str; + --len; + } + if (save[0] == '\\') { + ++save; + } + if (!(str_index->gc.u.v.flags & IS_STR_CONSTANT_UNQUALIFIED)) { + zend_error(E_ERROR, "Undefined constant '%s'", save); + } + zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'", str, str); + } + if (str == str_index->val && len == str_index->len) { + ZVAL_STR(&const_value, str_index); + } else { + ZVAL_STRINGL(&const_value, str, len); + } + } + + if (Z_REFCOUNT_P(element) > 1) { + ZVAL_DUP(&new_val, element); + + /* preserve this bit for inheritance */ +//??? Z_TYPE_P(element) |= IS_CONSTANT_INDEX; + zval_ptr_dtor(element); + ZVAL_COPY_VALUE(element, &new_val); + } + + switch (Z_TYPE(const_value)) { + case IS_STRING: + ret = zend_symtable_update_current_key(Z_ARRVAL_P(p), Z_STR(const_value), HASH_UPDATE_KEY_IF_BEFORE); + break; + case IS_BOOL: + case IS_LONG: + ret = zend_hash_update_current_key_ex(Z_ARRVAL_P(p), HASH_KEY_IS_LONG, NULL, Z_LVAL(const_value), HASH_UPDATE_KEY_IF_BEFORE, NULL); + break; + case IS_DOUBLE: + ret = zend_hash_update_current_key_ex(Z_ARRVAL_P(p), HASH_KEY_IS_LONG, NULL, zend_dval_to_lval(Z_DVAL(const_value)), HASH_UPDATE_KEY_IF_BEFORE, NULL); + break; + case IS_NULL: + ret = zend_hash_update_current_key_ex(Z_ARRVAL_P(p), HASH_KEY_IS_STRING, STR_EMPTY_ALLOC(), 0, HASH_UPDATE_KEY_IF_BEFORE, NULL); + break; + default: + ret = SUCCESS; + break; + } + if (ret == SUCCESS) { + zend_hash_move_forward(Z_ARRVAL_P(p)); + } + zval_dtor(&const_value); + } + zend_hash_apply_with_argument(Z_ARRVAL_P(p), (apply_func_arg_t) zval_update_constant_inline_change, (void *) scope TSRMLS_CC); + zend_hash_internal_pointer_reset(Z_ARRVAL_P(p)); + } else if (Z_TYPE_P(p) == IS_CONSTANT_AST) { + SEPARATE_ZVAL_IF_NOT_REF(p); + + zend_ast_evaluate(&const_value, Z_ASTVAL_P(p), scope TSRMLS_CC); + if (inline_change) { + zend_ast_destroy(Z_ASTVAL_P(p)); + } + ZVAL_COPY_VALUE(p, &const_value); + } + return 0; +} +/* }}} */ + +ZEND_API int zval_update_constant_inline_change(zval *pp, void *scope TSRMLS_DC) /* {{{ */ +{ + return zval_update_constant_ex(pp, (void*)1, scope TSRMLS_CC); +} +/* }}} */ + +ZEND_API int zval_update_constant_no_inline_change(zval *pp, void *scope TSRMLS_DC) /* {{{ */ +{ + return zval_update_constant_ex(pp, (void*)0, scope TSRMLS_CC); +} +/* }}} */ + +ZEND_API int zval_update_constant(zval *pp, void *arg TSRMLS_DC) /* {{{ */ +{ + return zval_update_constant_ex(pp, arg, NULL TSRMLS_CC); +} +/* }}} */ + +int call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, zend_uint param_count, zval params[] TSRMLS_DC) /* {{{ */ +{ + zval *params_array; + zend_uint i; + int ex_retval; + + if (param_count) { + params_array = (zval *) emalloc(sizeof(zval) * param_count); + for (i=0; iretval); + + if (!EG(active)) { + return FAILURE; /* executor is already inactive */ + } + + if (EG(exception)) { + return FAILURE; /* we would result in an instable executor otherwise */ + } + + switch (fci->size) { + case sizeof(zend_fcall_info): + break; /* nothing to do currently */ + default: + zend_error(E_ERROR, "Corrupted fcall_info provided to zend_call_function()"); + break; + } + + /* Initialize execute_data */ + if (EG(current_execute_data)) { + execute_data = *EG(current_execute_data); + EX(op_array) = NULL; + EX(opline) = NULL; + ZVAL_UNDEF(&EX(object)); + } else { + /* This only happens when we're called outside any execute()'s + * It shouldn't be strictly necessary to NULL execute_data out, + * but it may make bugs easier to spot + */ + memset(&execute_data, 0, sizeof(zend_execute_data)); + } + + if (!fci_cache || !fci_cache->initialized) { + char *callable_name; + char *error = NULL; + + if (!fci_cache) { + fci_cache = &fci_cache_local; + } + + if (!zend_is_callable_ex(&fci->function_name, fci->object_ptr, IS_CALLABLE_CHECK_SILENT, &callable_name, NULL, fci_cache, &error TSRMLS_CC)) { + if (error) { + zend_error(E_WARNING, "Invalid callback %s, %s", callable_name, error); + efree(error); + } + if (callable_name) { + efree(callable_name); + } + return FAILURE; + } else if (error) { + /* Capitalize the first latter of the error message */ + if (error[0] >= 'a' && error[0] <= 'z') { + error[0] += ('A' - 'a'); + } + zend_error(E_STRICT, "%s", error); + efree(error); + } + efree(callable_name); + } + + EX(function_state).function = fci_cache->function_handler; + calling_scope = fci_cache->calling_scope; + called_scope = fci_cache->called_scope; + fci->object_ptr = fci_cache->object_ptr; + if (fci->object_ptr) { + ZVAL_COPY_VALUE(&EX(object), fci->object_ptr); + } else { + ZVAL_UNDEF(&EX(object)); + } + if (fci->object_ptr && Z_TYPE_P(fci->object_ptr) == IS_OBJECT && + (!EG(objects_store).object_buckets || + !IS_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(fci->object_ptr)]))) { + return FAILURE; + } + + if (EX(function_state).function->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) { + if (EX(function_state).function->common.fn_flags & ZEND_ACC_ABSTRACT) { + zend_error_noreturn(E_ERROR, "Cannot call abstract method %s::%s()", EX(function_state).function->common.scope->name->val, EX(function_state).function->common.function_name->val); + } + if (EX(function_state).function->common.fn_flags & ZEND_ACC_DEPRECATED) { + zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated", + EX(function_state).function->common.scope ? EX(function_state).function->common.scope->name->val : "", + EX(function_state).function->common.scope ? "::" : "", + EX(function_state).function->common.function_name->val); + } + } + + ZEND_VM_STACK_GROW_IF_NEEDED(fci->param_count + 1); + + for (i=0; iparam_count; i++) { + zval *param; + + if (ARG_SHOULD_BE_SENT_BY_REF(EX(function_state).function, i + 1)) { + if (Z_REFCOUNTED(fci->params[i])) { + if (!Z_ISREF(fci->params[i]) && Z_REFCOUNT(fci->params[i]) > 1) { + zval new_zval; + + if (fci->no_separation && + !ARG_MAY_BE_SENT_BY_REF(EX(function_state).function, i + 1)) { + if (i || UNEXPECTED(ZEND_VM_STACK_ELEMETS(EG(argument_stack)) == (EG(argument_stack)->top))) { + /* hack to clean up the stack */ + ZVAL_LONG(&tmp, i); + zend_vm_stack_push(&tmp TSRMLS_CC); + zend_vm_stack_clear_multiple(0 TSRMLS_CC); + } + + zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given", + i+1, + EX(function_state).function->common.scope ? EX(function_state).function->common.scope->name->val : "", + EX(function_state).function->common.scope ? "::" : "", + EX(function_state).function->common.function_name->val); + return FAILURE; + } + + ZVAL_DUP(&new_zval, &fci->params[i]); + Z_DELREF(fci->params[i]); + ZVAL_COPY_VALUE(&fci->params[i], &new_zval); + } + Z_ADDREF(fci->params[i]); + } +//??? Z_SET_ISREF_PP(fci->params[i]); + param = &fci->params[i]; + } else if (Z_ISREF(fci->params[i]) && + /* don't separate references for __call */ + (EX(function_state).function->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0 ) { + param = &tmp; + ZVAL_DUP(param, &fci->params[i]); + } else { + param = &tmp; + ZVAL_COPY(param, &fci->params[i]); + } + zend_vm_stack_push(param TSRMLS_CC); + } + + EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C); + ZVAL_LONG(&tmp, fci->param_count); + zend_vm_stack_push(&tmp TSRMLS_CC); + + current_scope = EG(scope); + EG(scope) = calling_scope; + + ZVAL_COPY_VALUE(¤t_this, &EG(This)); + + current_called_scope = EG(called_scope); + if (called_scope) { + EG(called_scope) = called_scope; + } else if (EX(function_state).function->type != ZEND_INTERNAL_FUNCTION) { + EG(called_scope) = NULL; + } + + if (fci->object_ptr) { + if ((EX(function_state).function->common.fn_flags & ZEND_ACC_STATIC)) { + ZVAL_UNDEF(&EG(This)); + } else { + ZVAL_COPY(&EG(This), fci->object_ptr); + } + } else { + ZVAL_UNDEF(&EG(This)); + } + + EX(prev_execute_data) = EG(current_execute_data); + EG(current_execute_data) = &execute_data; + + if (EX(function_state).function->type == ZEND_USER_FUNCTION) { + calling_symbol_table = EG(active_symbol_table); + EG(scope) = EX(function_state).function->common.scope; + if (fci->symbol_table) { + EG(active_symbol_table) = fci->symbol_table; + } else { + EG(active_symbol_table) = NULL; + } + + original_op_array = EG(active_op_array); + EG(active_op_array) = (zend_op_array *) EX(function_state).function; + original_opline_ptr = EG(opline_ptr); + + if (EG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { + zend_generator_create_zval(EG(active_op_array), fci->retval TSRMLS_CC); + } else { + zend_execute(EG(active_op_array), fci->retval TSRMLS_CC); + } + + if (!fci->symbol_table && EG(active_symbol_table)) { + zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); + } + EG(active_symbol_table) = calling_symbol_table; + EG(active_op_array) = original_op_array; + EG(opline_ptr) = original_opline_ptr; + } else if (EX(function_state).function->type == ZEND_INTERNAL_FUNCTION) { + int call_via_handler = (EX(function_state).function->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0; +//??? ALLOC_INIT_ZVAL(*fci->retval_ptr_ptr); + if (EX(function_state).function->common.scope) { + EG(scope) = EX(function_state).function->common.scope; + } + if (EXPECTED(zend_execute_internal == NULL)) { + /* saves one function call if zend_execute_internal is not used */ + EX(function_state).function->internal_function.handler(fci->param_count, fci->retval, fci->object_ptr, 1 TSRMLS_CC); + } else { + zend_execute_internal(&execute_data, fci, 1 TSRMLS_CC); + } + /* We shouldn't fix bad extensions here, + because it can break proper ones (Bug #34045) + if (!EX(function_state).function->common.return_reference) + { + INIT_PZVAL(*fci->retval_ptr_ptr); + }*/ + if (EG(exception) && fci->retval) { + zval_ptr_dtor(fci->retval); + ZVAL_UNDEF(fci->retval); + } + + if (call_via_handler) { + /* We must re-initialize function again */ + fci_cache->initialized = 0; + } + } else { /* ZEND_OVERLOADED_FUNCTION */ +//??? ALLOC_INIT_ZVAL(*fci->retval_ptr_ptr); + + /* Not sure what should be done here if it's a static method */ + if (fci->object_ptr) { + Z_OBJ_HT_P(fci->object_ptr)->call_method(EX(function_state).function->common.function_name, fci->param_count, fci->retval, fci->object_ptr, 1 TSRMLS_CC); + } else { + zend_error_noreturn(E_ERROR, "Cannot call overloaded function for non-object"); + } + + if (EX(function_state).function->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { + efree((char*)EX(function_state).function->common.function_name); + } + efree(EX(function_state).function); + + if (EG(exception) && fci->retval) { + zval_ptr_dtor(fci->retval); + ZVAL_UNDEF(fci->retval); + } + } + zend_vm_stack_clear_multiple(0 TSRMLS_CC); + + zval_ptr_dtor(&EG(This)); + EG(called_scope) = current_called_scope; + EG(scope) = current_scope; + ZVAL_COPY_VALUE(&EG(This), ¤t_this); + EG(current_execute_data) = EX(prev_execute_data); + + if (EG(exception)) { + zend_throw_exception_internal(NULL TSRMLS_CC); + } + return SUCCESS; +} +/* }}} */ + +ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zend_literal *key, int use_autoload TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *ce = NULL; + zval args[1]; + zval local_retval; + int retval; + zend_string *lc_name; + zend_fcall_info fcall_info; + zend_fcall_info_cache fcall_cache; + + if (key) { + lc_name = Z_STR(key->constant); + } else { + if (name == NULL || !name->len) { + return NULL; + } + + if (name->val[0] == '\\') { + lc_name = STR_ALLOC(name->len - 1, 0); + zend_str_tolower_copy(lc_name->val, name->val + 1, name->len - 1); + } else { + lc_name = STR_ALLOC(name->len, 0); + zend_str_tolower_copy(lc_name->val, name->val, name->len); + } + } + + ce = zend_hash_find_ptr(EG(class_table), lc_name); + if (ce) { + if (!key) { + STR_FREE(lc_name); + } + return ce; + } + + /* The compiler is not-reentrant. Make sure we __autoload() only during run-time + * (doesn't impact functionality of __autoload() + */ + if (!use_autoload || zend_is_compiling(TSRMLS_C)) { + if (!key) { + STR_FREE(lc_name); + } + return NULL; + } + + /* Verify class name before passing it to __autoload() */ + if (strspn(name->val, "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\") != name->len) { + if (!key) { + STR_FREE(lc_name); + } + return NULL; + } + + if (EG(in_autoload) == NULL) { + ALLOC_HASHTABLE(EG(in_autoload)); + zend_hash_init(EG(in_autoload), 0, NULL, NULL, 0); + } + + if (zend_hash_add_empty_element(EG(in_autoload), lc_name) == NULL) { + if (!key) { + STR_FREE(lc_name); + } + return NULL; + } + + ZVAL_UNDEF(&local_retval); + + if (name->val[0] == '\\') { + ZVAL_STRINGL(&args[0], name->val + 1, name->len - 1); + } else { + ZVAL_STR(&args[0], STR_COPY(name)); + } + + fcall_info.size = sizeof(fcall_info); + fcall_info.function_table = EG(function_table); + ZVAL_STRINGL(&fcall_info.function_name, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1); + fcall_info.symbol_table = NULL; + fcall_info.retval = &local_retval; + fcall_info.param_count = 1; + fcall_info.params = args; + fcall_info.object_ptr = NULL; + fcall_info.no_separation = 1; + + fcall_cache.initialized = EG(autoload_func) ? 1 : 0; + fcall_cache.function_handler = EG(autoload_func); + fcall_cache.calling_scope = NULL; + fcall_cache.called_scope = NULL; + fcall_cache.object_ptr = NULL; + + zend_exception_save(TSRMLS_C); + retval = zend_call_function(&fcall_info, &fcall_cache TSRMLS_CC); + zend_exception_restore(TSRMLS_C); + + EG(autoload_func) = fcall_cache.function_handler; + + zval_ptr_dtor(&args[0]); + zval_dtor(&fcall_info.function_name); + + zend_hash_del(EG(in_autoload), lc_name); + + zval_ptr_dtor(&local_retval); + + if (retval == SUCCESS) { + ce = zend_hash_find_ptr(EG(class_table), lc_name); + } + if (!key) { + STR_FREE(lc_name); + } + return ce; +} +/* }}} */ + +ZEND_API zend_class_entry *zend_lookup_class(zend_string *name TSRMLS_DC) /* {{{ */ +{ + return zend_lookup_class_ex(name, NULL, 1 TSRMLS_CC); +} +/* }}} */ + +ZEND_API int zend_eval_stringl(char *str, int str_len, zval *retval_ptr, char *string_name TSRMLS_DC) /* {{{ */ +{ + zval pv; + zend_op_array *new_op_array; + zend_op_array *original_active_op_array = EG(active_op_array); + zend_uint original_compiler_options; + int retval; + + if (retval_ptr) { + ZVAL_STR(&pv, STR_ALLOC(str_len + sizeof("return ;")-1, 1)); + memcpy(Z_STRVAL(pv), "return ", sizeof("return ") - 1); + memcpy(Z_STRVAL(pv) + sizeof("return ") - 1, str, str_len); + Z_STRVAL(pv)[Z_STRLEN(pv) - 1] = ';'; + Z_STRVAL(pv)[Z_STRLEN(pv)] = '\0'; + } else { + ZVAL_STRINGL(&pv, str, str_len); + } + + /*printf("Evaluating '%s'\n", pv.value.str.val);*/ + + original_compiler_options = CG(compiler_options); + CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL; + new_op_array = zend_compile_string(&pv, string_name TSRMLS_CC); + CG(compiler_options) = original_compiler_options; + + if (new_op_array) { + zval local_retval; + zend_op **original_opline_ptr = EG(opline_ptr); + int orig_interactive = CG(interactive); + + EG(active_op_array) = new_op_array; + EG(no_extensions)=1; + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + CG(interactive) = 0; + + zend_try { + ZVAL_UNDEF(&local_retval); + zend_execute(new_op_array, &local_retval TSRMLS_CC); + } zend_catch { + destroy_op_array(new_op_array TSRMLS_CC); + efree(new_op_array); + zend_bailout(); + } zend_end_try(); + + CG(interactive) = orig_interactive; + if (Z_TYPE(local_retval) != IS_UNDEF) { + if (retval_ptr) { + COPY_PZVAL_TO_ZVAL(*retval_ptr, &local_retval); + } else { + zval_ptr_dtor(&local_retval); + } + } else { + if (retval_ptr) { + ZVAL_NULL(retval_ptr); + } + } + + EG(no_extensions)=0; + EG(opline_ptr) = original_opline_ptr; + EG(active_op_array) = original_active_op_array; + destroy_op_array(new_op_array TSRMLS_CC); + efree(new_op_array); + retval = SUCCESS; + } else { + retval = FAILURE; + } + zval_dtor(&pv); + return retval; +} +/* }}} */ + +ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name TSRMLS_DC) /* {{{ */ +{ + return zend_eval_stringl(str, strlen(str), retval_ptr, string_name TSRMLS_CC); +} +/* }}} */ + +ZEND_API int zend_eval_stringl_ex(char *str, int str_len, zval *retval_ptr, char *string_name, int handle_exceptions TSRMLS_DC) /* {{{ */ +{ + int result; + + result = zend_eval_stringl(str, str_len, retval_ptr, string_name TSRMLS_CC); + if (handle_exceptions && EG(exception)) { + zend_exception_error(EG(exception), E_ERROR TSRMLS_CC); + result = FAILURE; + } + return result; +} +/* }}} */ + +ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name, int handle_exceptions TSRMLS_DC) /* {{{ */ +{ + return zend_eval_stringl_ex(str, strlen(str), retval_ptr, string_name, handle_exceptions TSRMLS_CC); +} +/* }}} */ + +void execute_new_code(TSRMLS_D) /* {{{ */ +{ + zend_op *opline, *end; + zend_op *ret_opline; + int orig_interactive; + + if (!(CG(active_op_array)->fn_flags & ZEND_ACC_INTERACTIVE) + || CG(context).backpatch_count>0 + || CG(active_op_array)->function_name + || CG(active_op_array)->type!=ZEND_USER_FUNCTION) { + return; + } + + ret_opline = get_next_op(CG(active_op_array) TSRMLS_CC); + ret_opline->opcode = ZEND_RETURN; + ret_opline->op1_type = IS_CONST; + ret_opline->op1.constant = zend_add_literal(CG(active_op_array), &EG(uninitialized_zval) TSRMLS_CC); + SET_UNUSED(ret_opline->op2); + + if (!EG(start_op)) { + EG(start_op) = CG(active_op_array)->opcodes; + } + + opline=EG(start_op); + end=CG(active_op_array)->opcodes+CG(active_op_array)->last; + + while (oplineop1_type == IS_CONST) { + opline->op1.zv = &CG(active_op_array)->literals[opline->op1.constant].constant; + } + if (opline->op2_type == IS_CONST) { + opline->op2.zv = &CG(active_op_array)->literals[opline->op2.constant].constant; + } + switch (opline->opcode) { + case ZEND_GOTO: + if (Z_TYPE_P(opline->op2.zv) != IS_LONG) { + zend_resolve_goto_label(CG(active_op_array), opline, 1 TSRMLS_CC); + } + /* break omitted intentionally */ + case ZEND_JMP: + opline->op1.jmp_addr = &CG(active_op_array)->opcodes[opline->op1.opline_num]; + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_JMP_SET: + case ZEND_JMP_SET_VAR: + opline->op2.jmp_addr = &CG(active_op_array)->opcodes[opline->op2.opline_num]; + break; + } + ZEND_VM_SET_OPCODE_HANDLER(opline); + opline++; + } + + zend_release_labels(1 TSRMLS_CC); + + EG(active_op_array) = CG(active_op_array); + orig_interactive = CG(interactive); + CG(interactive) = 0; + zend_execute(CG(active_op_array), NULL TSRMLS_CC); + CG(interactive) = orig_interactive; + + if (EG(exception)) { + zend_exception_error(EG(exception), E_ERROR TSRMLS_CC); + } + + CG(active_op_array)->last -= 1; /* get rid of that ZEND_RETURN */ + EG(start_op) = CG(active_op_array)->opcodes+CG(active_op_array)->last; +} +/* }}} */ + +ZEND_API void zend_timeout(int dummy) /* {{{ */ +{ + TSRMLS_FETCH(); + + if (zend_on_timeout) { +#ifdef ZEND_SIGNALS + /* + We got here because we got a timeout signal, so we are in a signal handler + at this point. However, we want to be able to timeout any user-supplied + shutdown functions, so pretend we are not in a signal handler while we are + calling these + */ + SIGG(running) = 0; +#endif + zend_on_timeout(EG(timeout_seconds) TSRMLS_CC); + } + + zend_error(E_ERROR, "Maximum execution time of %d second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s"); +} +/* }}} */ + +#ifdef ZEND_WIN32 +static LRESULT CALLBACK zend_timeout_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) /* {{{ */ +{ + switch (message) { + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_REGISTER_ZEND_TIMEOUT: + /* wParam is the thread id pointer, lParam is the timeout amount in seconds */ + if (lParam == 0) { + KillTimer(timeout_window, wParam); + } else { +#ifdef ZTS + void ***tsrm_ls; +#endif + SetTimer(timeout_window, wParam, lParam*1000, NULL); +#ifdef ZTS + tsrm_ls = ts_resource_ex(0, &wParam); + if (!tsrm_ls) { + /* shouldn't normally happen */ + break; + } +#endif + EG(timed_out) = 0; + } + break; + case WM_UNREGISTER_ZEND_TIMEOUT: + /* wParam is the thread id pointer */ + KillTimer(timeout_window, wParam); + break; + case WM_TIMER: { +#ifdef ZTS + void ***tsrm_ls; + + tsrm_ls = ts_resource_ex(0, &wParam); + if (!tsrm_ls) { + /* Thread died before receiving its timeout? */ + break; + } +#endif + KillTimer(timeout_window, wParam); + EG(timed_out) = 1; + } + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} +/* }}} */ + +static unsigned __stdcall timeout_thread_proc(void *pArgs) /* {{{ */ +{ + MSG message; + + wc.style=0; + wc.lpfnWndProc = zend_timeout_WndProc; + wc.cbClsExtra=0; + wc.cbWndExtra=0; + wc.hInstance=NULL; + wc.hIcon=NULL; + wc.hCursor=NULL; + wc.hbrBackground=(HBRUSH)(COLOR_BACKGROUND + 5); + wc.lpszMenuName=NULL; + wc.lpszClassName = "Zend Timeout Window"; + if (!RegisterClass(&wc)) { + return -1; + } + timeout_window = CreateWindow(wc.lpszClassName, wc.lpszClassName, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL); + SetEvent(timeout_thread_event); + while (GetMessage(&message, NULL, 0, 0)) { + SendMessage(timeout_window, message.message, message.wParam, message.lParam); + if (message.message == WM_QUIT) { + break; + } + } + DestroyWindow(timeout_window); + UnregisterClass(wc.lpszClassName, NULL); + SetEvent(timeout_thread_handle); + return 0; +} +/* }}} */ + +void zend_init_timeout_thread(void) /* {{{ */ +{ + timeout_thread_event = CreateEvent(NULL, FALSE, FALSE, NULL); + timeout_thread_handle = CreateEvent(NULL, FALSE, FALSE, NULL); + _beginthreadex(NULL, 0, timeout_thread_proc, NULL, 0, &timeout_thread_id); + WaitForSingleObject(timeout_thread_event, INFINITE); +} +/* }}} */ + +void zend_shutdown_timeout_thread(void) /* {{{ */ +{ + if (!timeout_thread_initialized) { + return; + } + PostThreadMessage(timeout_thread_id, WM_QUIT, 0, 0); + + /* Wait for thread termination */ + WaitForSingleObject(timeout_thread_handle, 5000); + CloseHandle(timeout_thread_handle); + timeout_thread_initialized = 0; +} +/* }}} */ + +#endif + +/* This one doesn't exists on QNX */ +#ifndef SIGPROF +#define SIGPROF 27 +#endif + +void zend_set_timeout(long seconds, int reset_signals) /* {{{ */ +{ + TSRMLS_FETCH(); + + EG(timeout_seconds) = seconds; + +#ifdef ZEND_WIN32 + if(!seconds) { + return; + } + if (timeout_thread_initialized == 0 && InterlockedIncrement(&timeout_thread_initialized) == 1) { + /* We start up this process-wide thread here and not in zend_startup(), because if Zend + * is initialized inside a DllMain(), you're not supposed to start threads from it. + */ + zend_init_timeout_thread(); + } + PostThreadMessage(timeout_thread_id, WM_REGISTER_ZEND_TIMEOUT, (WPARAM) GetCurrentThreadId(), (LPARAM) seconds); +#else +# ifdef HAVE_SETITIMER + { + struct itimerval t_r; /* timeout requested */ + int signo; + + if(seconds) { + t_r.it_value.tv_sec = seconds; + t_r.it_value.tv_usec = t_r.it_interval.tv_sec = t_r.it_interval.tv_usec = 0; + +# ifdef __CYGWIN__ + setitimer(ITIMER_REAL, &t_r, NULL); + } + signo = SIGALRM; +# else + setitimer(ITIMER_PROF, &t_r, NULL); + } + signo = SIGPROF; +# endif + + if (reset_signals) { +# ifdef ZEND_SIGNALS + zend_signal(signo, zend_timeout TSRMLS_CC); +# else + sigset_t sigset; + + signal(signo, zend_timeout); + sigemptyset(&sigset); + sigaddset(&sigset, signo); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); +# endif + } + } +# endif /* HAVE_SETITIMER */ +#endif +} +/* }}} */ + +void zend_unset_timeout(TSRMLS_D) /* {{{ */ +{ +#ifdef ZEND_WIN32 + if(timeout_thread_initialized) { + PostThreadMessage(timeout_thread_id, WM_UNREGISTER_ZEND_TIMEOUT, (WPARAM) GetCurrentThreadId(), (LPARAM) 0); + } +#else +# ifdef HAVE_SETITIMER + if (EG(timeout_seconds)) { + struct itimerval no_timeout; + + no_timeout.it_value.tv_sec = no_timeout.it_value.tv_usec = no_timeout.it_interval.tv_sec = no_timeout.it_interval.tv_usec = 0; + +#ifdef __CYGWIN__ + setitimer(ITIMER_REAL, &no_timeout, NULL); +#else + setitimer(ITIMER_PROF, &no_timeout, NULL); +#endif + } +# endif +#endif +} +/* }}} */ + +zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *ce; + int use_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) == 0; + int silent = (fetch_type & ZEND_FETCH_CLASS_SILENT) != 0; + + fetch_type &= ZEND_FETCH_CLASS_MASK; + +check_fetch_type: + switch (fetch_type) { + case ZEND_FETCH_CLASS_SELF: + if (!EG(scope)) { + zend_error(E_ERROR, "Cannot access self:: when no class scope is active"); + } + return EG(scope); + case ZEND_FETCH_CLASS_PARENT: + if (!EG(scope)) { + zend_error(E_ERROR, "Cannot access parent:: when no class scope is active"); + } + if (!EG(scope)->parent) { + zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent"); + } + return EG(scope)->parent; + case ZEND_FETCH_CLASS_STATIC: + if (!EG(called_scope)) { + zend_error(E_ERROR, "Cannot access static:: when no class scope is active"); + } + return EG(called_scope); + case ZEND_FETCH_CLASS_AUTO: { + fetch_type = zend_get_class_fetch_type(class_name->val, class_name->len); + if (fetch_type!=ZEND_FETCH_CLASS_DEFAULT) { + goto check_fetch_type; + } + } + break; + } + + if ((ce = zend_lookup_class_ex(class_name, NULL, use_autoload TSRMLS_CC)) == NULL) { + if (use_autoload) { + if (!silent && !EG(exception)) { + if (fetch_type == ZEND_FETCH_CLASS_INTERFACE) { + zend_error(E_ERROR, "Interface '%s' not found", class_name->val); + } else if (fetch_type == ZEND_FETCH_CLASS_TRAIT) { + zend_error(E_ERROR, "Trait '%s' not found", class_name->val); + } else { + zend_error(E_ERROR, "Class '%s' not found", class_name->val); + } + } + } + return NULL; + } + return ce; +} +/* }}} */ + +zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, const zend_literal *key, int fetch_type TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *ce; + int use_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) == 0; + + if ((ce = zend_lookup_class_ex(class_name, key, use_autoload TSRMLS_CC)) == NULL) { + if (use_autoload) { + if ((fetch_type & ZEND_FETCH_CLASS_SILENT) == 0 && !EG(exception)) { + if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_INTERFACE) { + zend_error(E_ERROR, "Interface '%s' not found", class_name->val); + } else if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_TRAIT) { + zend_error(E_ERROR, "Trait '%s' not found", class_name->val); + } else { + zend_error(E_ERROR, "Class '%s' not found", class_name->val); + } + } + } + return NULL; + } + return ce; +} +/* }}} */ + +#define MAX_ABSTRACT_INFO_CNT 3 +#define MAX_ABSTRACT_INFO_FMT "%s%s%s%s" +#define DISPLAY_ABSTRACT_FN(idx) \ + ai.afn[idx] ? ZEND_FN_SCOPE_NAME(ai.afn[idx]) : "", \ + ai.afn[idx] ? "::" : "", \ + ai.afn[idx] ? ai.afn[idx]->common.function_name->val : "", \ + ai.afn[idx] && ai.afn[idx + 1] ? ", " : (ai.afn[idx] && ai.cnt > MAX_ABSTRACT_INFO_CNT ? ", ..." : "") + +typedef struct _zend_abstract_info { + zend_function *afn[MAX_ABSTRACT_INFO_CNT + 1]; + int cnt; + int ctor; +} zend_abstract_info; + +static int zend_verify_abstract_class_function(zval *zv, zend_abstract_info *ai TSRMLS_DC) /* {{{ */ +{ + zend_function *fn = Z_PTR_P(zv); + + if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { + if (ai->cnt < MAX_ABSTRACT_INFO_CNT) { + ai->afn[ai->cnt] = fn; + } + if (fn->common.fn_flags & ZEND_ACC_CTOR) { + if (!ai->ctor) { + ai->cnt++; + ai->ctor = 1; + } else { + ai->afn[ai->cnt] = NULL; + } + } else { + ai->cnt++; + } + } + return 0; +} +/* }}} */ + +void zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC) /* {{{ */ +{ + zend_abstract_info ai; + + if ((ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) && !(ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) { + memset(&ai, 0, sizeof(ai)); + + zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t) zend_verify_abstract_class_function, &ai TSRMLS_CC); + + if (ai.cnt) { + zend_error(E_ERROR, "Class %s contains %d abstract method%s and must therefore be declared abstract or implement the remaining methods (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")", + ce->name->val, ai.cnt, + ai.cnt > 1 ? "s" : "", + DISPLAY_ABSTRACT_FN(0), + DISPLAY_ABSTRACT_FN(1), + DISPLAY_ABSTRACT_FN(2) + ); + } + } +} +/* }}} */ + +ZEND_API void zend_reset_all_cv(HashTable *symbol_table TSRMLS_DC) /* {{{ */ +{ + zend_execute_data *ex; + int i; + + for (ex = EG(current_execute_data); ex; ex = ex->prev_execute_data) { + if (ex->op_array && ex->symbol_table == symbol_table) { + for (i = 0; i < ex->op_array->last_var; i++) { + ZVAL_UNDEF(EX_VAR_NUM_2(ex, i)); + } + } + } +} +/* }}} */ + +ZEND_API void zend_delete_variable(zend_execute_data *ex, HashTable *ht, zend_string *name TSRMLS_DC) /* {{{ */ +{ + if (zend_hash_del(ht, name) == SUCCESS) { + while (ex && ex->symbol_table == ht) { + int i; + + if (ex->op_array) { + for (i = 0; i < ex->op_array->last_var; i++) { + if (ex->op_array->vars[i]->h == name->h && + ex->op_array->vars[i]->len == name->len && + !memcmp(ex->op_array->vars[i]->val, name->val, name->len)) { + ZVAL_UNDEF(EX_VAR_NUM_2(ex, i)); + break; + } + } + } + ex = ex->prev_execute_data; + } + } +} +/* }}} */ + +ZEND_API int zend_delete_global_variable(zend_string *name TSRMLS_DC) /* {{{ */ +{ + zend_execute_data *ex; + + if (zend_hash_del(&EG(symbol_table).ht, name) == SUCCESS) { + for (ex = EG(current_execute_data); ex; ex = ex->prev_execute_data) { + if (ex->op_array && ex->symbol_table == &EG(symbol_table).ht) { + int i; + for (i = 0; i < ex->op_array->last_var; i++) { + if (ex->op_array->vars[i]->h == name->h && + ex->op_array->vars[i]->len == name->len && + !memcmp(ex->op_array->vars[i]->val, name->val, name->len) + ) { + ZVAL_UNDEF(EX_VAR_NUM_2(ex, i)); + break; + } + } + } + } + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +ZEND_API void zend_rebuild_symbol_table(TSRMLS_D) /* {{{ */ +{ + zend_uint i; + zend_execute_data *ex; + + if (!EG(active_symbol_table)) { + + /* Search for last called user function */ + ex = EG(current_execute_data); + while (ex && !ex->op_array) { + ex = ex->prev_execute_data; + } + if (ex && ex->symbol_table) { + EG(active_symbol_table) = ex->symbol_table; + return; + } + + if (ex && ex->op_array) { + if (EG(symtable_cache_ptr)>=EG(symtable_cache)) { + /*printf("Cache hit! Reusing %x\n", symtable_cache[symtable_cache_ptr]);*/ + EG(active_symbol_table) = *(EG(symtable_cache_ptr)--); + } else { + ALLOC_HASHTABLE(EG(active_symbol_table)); + zend_hash_init(EG(active_symbol_table), ex->op_array->last_var, NULL, ZVAL_PTR_DTOR, 0); + /*printf("Cache miss! Initialized %x\n", EG(active_symbol_table));*/ + } + ex->symbol_table = EG(active_symbol_table); + + if (ex->op_array->this_var != -1 && + Z_TYPE_P(EX_VAR_NUM_2(ex, ex->op_array->this_var)) == IS_UNDEF && + Z_TYPE(EG(This)) != IS_UNDEF) { + ZVAL_COPY_VALUE(EX_VAR_NUM_2(ex, ex->op_array->this_var), &EG(This)); + } + for (i = 0; i < ex->op_array->last_var; i++) { + if (Z_TYPE_P(EX_VAR_NUM_2(ex, i)) != IS_UNDEF) { + zval *zv = zend_hash_update(EG(active_symbol_table), + ex->op_array->vars[i], + EX_VAR_NUM_2(ex, i)); + ZVAL_INDIRECT(EX_VAR_NUM_2(ex, i), zv); + } + } + } + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/4f7339_317bcb_ext#standard#dns.c b/src/main/resource/testFiles/php-src/revFiles/4f7339_317bcb_ext#standard#dns.c new file mode 100644 index 0000000..214a7dc --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/4f7339_317bcb_ext#standard#dns.c @@ -0,0 +1,1052 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: The typical suspects | + | Pollita | + | Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* {{{ includes */ +#include "php.h" +#include "php_network.h" + +#if HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef PHP_WIN32 +# include "win32/inet.h" +# include +# include +# include +#else /* This holds good for NetWare too, both for Winsock and Berkeley sockets */ +#include +#if HAVE_ARPA_INET_H +#include +#endif +#include +#ifdef _OSD_POSIX +#undef STATUS +#undef T_UNSPEC +#endif +#if HAVE_ARPA_NAMESER_H +#ifdef DARWIN +# define BIND_8_COMPAT 1 +#endif +#include +#endif +#if HAVE_RESOLV_H +#include +#endif +#ifdef HAVE_DNS_H +#include +#endif +#endif + +/* Borrowed from SYS/SOCKET.H */ +#if defined(NETWARE) && defined(USE_WINSOCK) +#define AF_INET 2 /* internetwork: UDP, TCP, etc. */ +#endif + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 255 +#endif + +/* For the local hostname obtained via gethostname which is different from the + dns-related MAXHOSTNAMELEN constant above */ +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 255 +#endif + +#include "php_dns.h" + +/* type compat */ +#ifndef DNS_T_A +#define DNS_T_A 1 +#endif +#ifndef DNS_T_NS +#define DNS_T_NS 2 +#endif +#ifndef DNS_T_CNAME +#define DNS_T_CNAME 5 +#endif +#ifndef DNS_T_SOA +#define DNS_T_SOA 6 +#endif +#ifndef DNS_T_PTR +#define DNS_T_PTR 12 +#endif +#ifndef DNS_T_HINFO +#define DNS_T_HINFO 13 +#endif +#ifndef DNS_T_MINFO +#define DNS_T_MINFO 14 +#endif +#ifndef DNS_T_MX +#define DNS_T_MX 15 +#endif +#ifndef DNS_T_TXT +#define DNS_T_TXT 16 +#endif +#ifndef DNS_T_AAAA +#define DNS_T_AAAA 28 +#endif +#ifndef DNS_T_SRV +#define DNS_T_SRV 33 +#endif +#ifndef DNS_T_NAPTR +#define DNS_T_NAPTR 35 +#endif +#ifndef DNS_T_A6 +#define DNS_T_A6 38 +#endif + +#ifndef DNS_T_ANY +#define DNS_T_ANY 255 +#endif +/* }}} */ + +static char *php_gethostbyaddr(char *ip); +static char *php_gethostbyname(char *name); + +#ifdef HAVE_GETHOSTNAME +/* {{{ proto string gethostname() + Get the host name of the current machine */ +PHP_FUNCTION(gethostname) +{ + char buf[HOST_NAME_MAX]; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (gethostname(buf, sizeof(buf) - 1)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to fetch host [%d]: %s", errno, strerror(errno)); + RETURN_FALSE; + } + + RETURN_STRING(buf, 1); +} +/* }}} */ +#endif + +/* TODO: Reimplement the gethostby* functions using the new winxp+ API, in dns_win32.c, then + we can have a dns.c, dns_unix.c and dns_win32.c instead of a messy dns.c full of #ifdef +*/ + +/* {{{ proto string gethostbyaddr(string ip_address) + Get the Internet host name corresponding to a given IP address */ +PHP_FUNCTION(gethostbyaddr) +{ + char *addr; + int addr_len; + char *hostname; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &addr, &addr_len) == FAILURE) { + return; + } + + hostname = php_gethostbyaddr(addr); + + if (hostname == NULL) { +#if HAVE_IPV6 && HAVE_INET_PTON + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Address is not a valid IPv4 or IPv6 address"); +#else + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Address is not in a.b.c.d form"); +#endif + RETVAL_FALSE; + } else { + RETVAL_STRING(hostname, 0); + } +} +/* }}} */ + +/* {{{ php_gethostbyaddr */ +static char *php_gethostbyaddr(char *ip) +{ +#if HAVE_IPV6 && HAVE_INET_PTON + struct in6_addr addr6; +#endif + struct in_addr addr; + struct hostent *hp; + +#if HAVE_IPV6 && HAVE_INET_PTON + if (inet_pton(AF_INET6, ip, &addr6)) { + hp = gethostbyaddr((char *) &addr6, sizeof(addr6), AF_INET6); + } else if (inet_pton(AF_INET, ip, &addr)) { + hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); + } else { + return NULL; + } +#else + addr.s_addr = inet_addr(ip); + + if (addr.s_addr == -1) { + return NULL; + } + + hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); +#endif + + if (!hp || hp->h_name == NULL || hp->h_name[0] == '\0') { + return estrdup(ip); + } + + return estrdup(hp->h_name); +} +/* }}} */ + +/* {{{ proto string gethostbyname(string hostname) + Get the IP address corresponding to a given Internet host name */ +PHP_FUNCTION(gethostbyname) +{ + char *hostname; + int hostname_len; + char *addr; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &hostname_len) == FAILURE) { + return; + } + + addr = php_gethostbyname(hostname); + + RETVAL_STRING(addr, 0); +} +/* }}} */ + +/* {{{ proto array gethostbynamel(string hostname) + Return a list of IP addresses that a given hostname resolves to. */ +PHP_FUNCTION(gethostbynamel) +{ + char *hostname; + int hostname_len; + struct hostent *hp; + struct in_addr in; + int i; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &hostname_len) == FAILURE) { + return; + } + + hp = gethostbyname(hostname); + if (hp == NULL || hp->h_addr_list == NULL) { + RETURN_FALSE; + } + + array_init(return_value); + + for (i = 0 ; hp->h_addr_list[i] != 0 ; i++) { + in = *(struct in_addr *) hp->h_addr_list[i]; + add_next_index_string(return_value, inet_ntoa(in), 1); + } +} +/* }}} */ + +/* {{{ php_gethostbyname */ +static char *php_gethostbyname(char *name) +{ + struct hostent *hp; + struct in_addr in; + + hp = gethostbyname(name); + + if (!hp || !*(hp->h_addr_list)) { + return estrdup(name); + } + + memcpy(&in.s_addr, *(hp->h_addr_list), sizeof(in.s_addr)); + + return estrdup(inet_ntoa(in)); +} +/* }}} */ + +#if HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32) +# define PHP_DNS_NUM_TYPES 12 /* Number of DNS Types Supported by PHP currently */ + +# define PHP_DNS_A 0x00000001 +# define PHP_DNS_NS 0x00000002 +# define PHP_DNS_CNAME 0x00000010 +# define PHP_DNS_SOA 0x00000020 +# define PHP_DNS_PTR 0x00000800 +# define PHP_DNS_HINFO 0x00001000 +# define PHP_DNS_MX 0x00004000 +# define PHP_DNS_TXT 0x00008000 +# define PHP_DNS_A6 0x01000000 +# define PHP_DNS_SRV 0x02000000 +# define PHP_DNS_NAPTR 0x04000000 +# define PHP_DNS_AAAA 0x08000000 +# define PHP_DNS_ANY 0x10000000 +# define PHP_DNS_ALL (PHP_DNS_A|PHP_DNS_NS|PHP_DNS_CNAME|PHP_DNS_SOA|PHP_DNS_PTR|PHP_DNS_HINFO|PHP_DNS_MX|PHP_DNS_TXT|PHP_DNS_A6|PHP_DNS_SRV|PHP_DNS_NAPTR|PHP_DNS_AAAA) +#endif /* HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32) */ + +/* Note: These functions are defined in ext/standard/dns_win32.c for Windows! */ +#if !defined(PHP_WIN32) && (HAVE_DNS_SEARCH_FUNC && !(defined(__BEOS__) || defined(NETWARE))) + +#ifndef HFIXEDSZ +#define HFIXEDSZ 12 /* fixed data in header */ +#endif /* HFIXEDSZ */ + +#ifndef QFIXEDSZ +#define QFIXEDSZ 4 /* fixed data in query */ +#endif /* QFIXEDSZ */ + +#undef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 1024 + +#ifndef MAXRESOURCERECORDS +#define MAXRESOURCERECORDS 64 +#endif /* MAXRESOURCERECORDS */ + +typedef union { + HEADER qb1; + u_char qb2[65536]; +} querybuf; + +/* just a hack to free resources allocated by glibc in __res_nsend() + * See also: + * res_thread_freeres() in glibc/resolv/res_init.c + * __libc_res_nsend() in resolv/res_send.c + * */ + +#if defined(__GLIBC__) && !defined(HAVE_DEPRECATED_DNS_FUNCS) +#define php_dns_free_res(__res__) _php_dns_free_res(__res__) +static void _php_dns_free_res(struct __res_state res) { /* {{{ */ + int ns; + for (ns = 0; ns < MAXNS; ns++) { + if (res._u._ext.nsaddrs[ns] != NULL) { + free (res._u._ext.nsaddrs[ns]); + res._u._ext.nsaddrs[ns] = NULL; + } + } +} /* }}} */ +#else +#define php_dns_free_res(__res__) +#endif + +/* {{{ proto bool dns_check_record(string host [, string type]) + Check DNS records corresponding to a given Internet host name or IP address */ +PHP_FUNCTION(dns_check_record) +{ +#ifndef MAXPACKET +#define MAXPACKET 8192 /* max packet size used internally by BIND */ +#endif + u_char ans[MAXPACKET]; + char *hostname, *rectype = NULL; + int hostname_len, rectype_len = 0; + int type = T_MX, i; +#if defined(HAVE_DNS_SEARCH) + struct sockaddr_storage from; + uint32_t fromsize = sizeof(from); + dns_handle_t handle; +#elif defined(HAVE_RES_NSEARCH) + struct __res_state state; + struct __res_state *handle = &state; +#endif + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &hostname, &hostname_len, &rectype, &rectype_len) == FAILURE) { + return; + } + + if (hostname_len == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host cannot be empty"); + RETURN_FALSE; + } + + if (rectype) { + if (!strcasecmp("A", rectype)) type = T_A; + else if (!strcasecmp("NS", rectype)) type = DNS_T_NS; + else if (!strcasecmp("MX", rectype)) type = DNS_T_MX; + else if (!strcasecmp("PTR", rectype)) type = DNS_T_PTR; + else if (!strcasecmp("ANY", rectype)) type = DNS_T_ANY; + else if (!strcasecmp("SOA", rectype)) type = DNS_T_SOA; + else if (!strcasecmp("TXT", rectype)) type = DNS_T_TXT; + else if (!strcasecmp("CNAME", rectype)) type = DNS_T_CNAME; + else if (!strcasecmp("AAAA", rectype)) type = DNS_T_AAAA; + else if (!strcasecmp("SRV", rectype)) type = DNS_T_SRV; + else if (!strcasecmp("NAPTR", rectype)) type = DNS_T_NAPTR; + else if (!strcasecmp("A6", rectype)) type = DNS_T_A6; + else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type '%s' not supported", rectype); + RETURN_FALSE; + } + } + +#if defined(HAVE_DNS_SEARCH) + handle = dns_open(NULL); + if (handle == NULL) { + RETURN_FALSE; + } +#elif defined(HAVE_RES_NSEARCH) + memset(&state, 0, sizeof(state)); + if (res_ninit(handle)) { + RETURN_FALSE; + } +#else + res_init(); +#endif + + RETVAL_TRUE; + i = php_dns_search(handle, hostname, C_IN, type, ans, sizeof(ans)); + + if (i < 0) { + RETVAL_FALSE; + } + + php_dns_free_handle(handle); +} +/* }}} */ + +#if HAVE_FULL_DNS_FUNCS + +/* {{{ php_parserr */ +static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int store, int raw, zval **subarray) +{ + u_short type, class, dlen; + u_long ttl; + long n, i; + u_short s; + u_char *tp, *p; + char name[MAXHOSTNAMELEN]; + int have_v6_break = 0, in_v6_break = 0; + + *subarray = NULL; + + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, sizeof(name) - 2); + if (n < 0) { + return NULL; + } + cp += n; + + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + if (type_to_fetch != T_ANY && type != type_to_fetch) { + cp += dlen; + return cp; + } + + if (!store) { + cp += dlen; + return cp; + } + + ALLOC_INIT_ZVAL(*subarray); + array_init(*subarray); + + add_assoc_string(*subarray, "host", name, 1); + add_assoc_string(*subarray, "class", "IN", 1); + add_assoc_long(*subarray, "ttl", ttl); + + if (raw) { + add_assoc_long(*subarray, "type", type); + add_assoc_stringl(*subarray, "data", (char*) cp, (uint) dlen, 1); + cp += dlen; + return cp; + } + + switch (type) { + case DNS_T_A: + add_assoc_string(*subarray, "type", "A", 1); + snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); + add_assoc_string(*subarray, "ip", name, 1); + cp += dlen; + break; + case DNS_T_MX: + add_assoc_string(*subarray, "type", "MX", 1); + GETSHORT(n, cp); + add_assoc_long(*subarray, "pri", n); + /* no break; */ + case DNS_T_CNAME: + if (type == DNS_T_CNAME) { + add_assoc_string(*subarray, "type", "CNAME", 1); + } + /* no break; */ + case DNS_T_NS: + if (type == DNS_T_NS) { + add_assoc_string(*subarray, "type", "NS", 1); + } + /* no break; */ + case DNS_T_PTR: + if (type == DNS_T_PTR) { + add_assoc_string(*subarray, "type", "PTR", 1); + } + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "target", name, 1); + break; + case DNS_T_HINFO: + /* See RFC 1010 for values */ + add_assoc_string(*subarray, "type", "HINFO", 1); + n = *cp & 0xFF; + cp++; + add_assoc_stringl(*subarray, "cpu", (char*)cp, n, 1); + cp += n; + n = *cp & 0xFF; + cp++; + add_assoc_stringl(*subarray, "os", (char*)cp, n, 1); + cp += n; + break; + case DNS_T_TXT: + { + int ll = 0; + zval *entries = NULL; + + add_assoc_string(*subarray, "type", "TXT", 1); + tp = emalloc(dlen + 1); + + MAKE_STD_ZVAL(entries); + array_init(entries); + + while (ll < dlen) { + n = cp[ll]; + if ((ll + n) >= dlen) { + // Invalid chunk length, truncate + n = dlen - (ll + 1); + } + memcpy(tp + ll , cp + ll + 1, n); + add_next_index_stringl(entries, cp + ll + 1, n, 1); + ll = ll + n + 1; + } + tp[dlen] = '\0'; + cp += dlen; + + add_assoc_stringl(*subarray, "txt", tp, (dlen>0)?dlen - 1:0, 0); + add_assoc_zval(*subarray, "entries", entries); + } + break; + case DNS_T_SOA: + add_assoc_string(*subarray, "type", "SOA", 1); + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "mname", name, 1); + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "rname", name, 1); + GETLONG(n, cp); + add_assoc_long(*subarray, "serial", n); + GETLONG(n, cp); + add_assoc_long(*subarray, "refresh", n); + GETLONG(n, cp); + add_assoc_long(*subarray, "retry", n); + GETLONG(n, cp); + add_assoc_long(*subarray, "expire", n); + GETLONG(n, cp); + add_assoc_long(*subarray, "minimum-ttl", n); + break; + case DNS_T_AAAA: + tp = (u_char*)name; + for(i=0; i < 8; i++) { + GETSHORT(s, cp); + if (s != 0) { + if (tp > (u_char *)name) { + in_v6_break = 0; + tp[0] = ':'; + tp++; + } + tp += sprintf((char*)tp,"%x",s); + } else { + if (!have_v6_break) { + have_v6_break = 1; + in_v6_break = 1; + tp[0] = ':'; + tp++; + } else if (!in_v6_break) { + tp[0] = ':'; + tp++; + tp[0] = '0'; + tp++; + } + } + } + if (have_v6_break && in_v6_break) { + tp[0] = ':'; + tp++; + } + tp[0] = '\0'; + add_assoc_string(*subarray, "type", "AAAA", 1); + add_assoc_string(*subarray, "ipv6", name, 1); + break; + case DNS_T_A6: + p = cp; + add_assoc_string(*subarray, "type", "A6", 1); + n = ((int)cp[0]) & 0xFF; + cp++; + add_assoc_long(*subarray, "masklen", n); + tp = (u_char*)name; + if (n > 15) { + have_v6_break = 1; + in_v6_break = 1; + tp[0] = ':'; + tp++; + } + if (n % 16 > 8) { + /* Partial short */ + if (cp[0] != 0) { + if (tp > (u_char *)name) { + in_v6_break = 0; + tp[0] = ':'; + tp++; + } + sprintf((char*)tp, "%x", cp[0] & 0xFF); + } else { + if (!have_v6_break) { + have_v6_break = 1; + in_v6_break = 1; + tp[0] = ':'; + tp++; + } else if (!in_v6_break) { + tp[0] = ':'; + tp++; + tp[0] = '0'; + tp++; + } + } + cp++; + } + for (i = (n + 8) / 16; i < 8; i++) { + GETSHORT(s, cp); + if (s != 0) { + if (tp > (u_char *)name) { + in_v6_break = 0; + tp[0] = ':'; + tp++; + } + tp += sprintf((char*)tp,"%x",s); + } else { + if (!have_v6_break) { + have_v6_break = 1; + in_v6_break = 1; + tp[0] = ':'; + tp++; + } else if (!in_v6_break) { + tp[0] = ':'; + tp++; + tp[0] = '0'; + tp++; + } + } + } + if (have_v6_break && in_v6_break) { + tp[0] = ':'; + tp++; + } + tp[0] = '\0'; + add_assoc_string(*subarray, "ipv6", name, 1); + if (cp < p + dlen) { + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "chain", name, 1); + } + break; + case DNS_T_SRV: + add_assoc_string(*subarray, "type", "SRV", 1); + GETSHORT(n, cp); + add_assoc_long(*subarray, "pri", n); + GETSHORT(n, cp); + add_assoc_long(*subarray, "weight", n); + GETSHORT(n, cp); + add_assoc_long(*subarray, "port", n); + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "target", name, 1); + break; + case DNS_T_NAPTR: + add_assoc_string(*subarray, "type", "NAPTR", 1); + GETSHORT(n, cp); + add_assoc_long(*subarray, "order", n); + GETSHORT(n, cp); + add_assoc_long(*subarray, "pref", n); + n = (cp[0] & 0xFF); + add_assoc_stringl(*subarray, "flags", (char*)++cp, n, 1); + cp += n; + n = (cp[0] & 0xFF); + add_assoc_stringl(*subarray, "services", (char*)++cp, n, 1); + cp += n; + n = (cp[0] & 0xFF); + add_assoc_stringl(*subarray, "regex", (char*)++cp, n, 1); + cp += n; + n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + if (n < 0) { + return NULL; + } + cp += n; + add_assoc_string(*subarray, "replacement", name, 1); + break; + default: + zval_ptr_dtor(subarray); + *subarray = NULL; + cp += dlen; + break; + } + + return cp; +} +/* }}} */ + +/* {{{ proto array|false dns_get_record(string hostname [, int type[, array authns, array addtl]]) + Get any Resource Record corresponding to a given Internet host name */ +PHP_FUNCTION(dns_get_record) +{ + char *hostname; + int hostname_len; + long type_param = PHP_DNS_ANY; + zval *authns = NULL, *addtl = NULL; + int type_to_fetch; +#if defined(HAVE_DNS_SEARCH) + struct sockaddr_storage from; + uint32_t fromsize = sizeof(from); + dns_handle_t handle; +#elif defined(HAVE_RES_NSEARCH) + struct __res_state state; + struct __res_state *handle = &state; +#endif + HEADER *hp; + querybuf answer; + u_char *cp = NULL, *end = NULL; + int n, qd, an, ns = 0, ar = 0; + int type, first_query = 1, store_results = 1; + zend_bool raw = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz!z!b", + &hostname, &hostname_len, &type_param, &authns, &addtl, &raw) == FAILURE) { + return; + } + + if (authns) { + zval_dtor(authns); + array_init(authns); + } + if (addtl) { + zval_dtor(addtl); + array_init(addtl); + } + + if (!raw) { + if ((type_param & ~PHP_DNS_ALL) && (type_param != PHP_DNS_ANY)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type '%ld' not supported", type_param); + RETURN_FALSE; + } + } else { + if ((type_param < 1) || (type_param > 0xFFFF)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Numeric DNS record type must be between 1 and 65535, '%ld' given", type_param); + RETURN_FALSE; + } + } + + /* Initialize the return array */ + array_init(return_value); + + /* - We emulate an or'ed type mask by querying type by type. (Steps 0 - NUMTYPES-1 ) + * If additional info is wanted we check again with DNS_T_ANY (step NUMTYPES / NUMTYPES+1 ) + * store_results is used to skip storing the results retrieved in step + * NUMTYPES+1 when results were already fetched. + * - In case of PHP_DNS_ANY we use the directly fetch DNS_T_ANY. (step NUMTYPES+1 ) + * - In case of raw mode, we query only the requestd type instead of looping type by type + * before going with the additional info stuff. + */ + + if (raw) { + type = -1; + } else if (type_param == PHP_DNS_ANY) { + type = PHP_DNS_NUM_TYPES + 1; + } else { + type = 0; + } + + for ( ; + type < (addtl ? (PHP_DNS_NUM_TYPES + 2) : PHP_DNS_NUM_TYPES) || first_query; + type++ + ) { + first_query = 0; + switch (type) { + case -1: /* raw */ + type_to_fetch = type_param; + /* skip over the rest and go directly to additional records */ + type = PHP_DNS_NUM_TYPES - 1; + break; + case 0: + type_to_fetch = type_param&PHP_DNS_A ? DNS_T_A : 0; + break; + case 1: + type_to_fetch = type_param&PHP_DNS_NS ? DNS_T_NS : 0; + break; + case 2: + type_to_fetch = type_param&PHP_DNS_CNAME ? DNS_T_CNAME : 0; + break; + case 3: + type_to_fetch = type_param&PHP_DNS_SOA ? DNS_T_SOA : 0; + break; + case 4: + type_to_fetch = type_param&PHP_DNS_PTR ? DNS_T_PTR : 0; + break; + case 5: + type_to_fetch = type_param&PHP_DNS_HINFO ? DNS_T_HINFO : 0; + break; + case 6: + type_to_fetch = type_param&PHP_DNS_MX ? DNS_T_MX : 0; + break; + case 7: + type_to_fetch = type_param&PHP_DNS_TXT ? DNS_T_TXT : 0; + break; + case 8: + type_to_fetch = type_param&PHP_DNS_AAAA ? DNS_T_AAAA : 0; + break; + case 9: + type_to_fetch = type_param&PHP_DNS_SRV ? DNS_T_SRV : 0; + break; + case 10: + type_to_fetch = type_param&PHP_DNS_NAPTR ? DNS_T_NAPTR : 0; + break; + case 11: + type_to_fetch = type_param&PHP_DNS_A6 ? DNS_T_A6 : 0; + break; + case PHP_DNS_NUM_TYPES: + store_results = 0; + continue; + default: + case (PHP_DNS_NUM_TYPES + 1): + type_to_fetch = DNS_T_ANY; + break; + } + + if (type_to_fetch) { +#if defined(HAVE_DNS_SEARCH) + handle = dns_open(NULL); + if (handle == NULL) { + zval_dtor(return_value); + RETURN_FALSE; + } +#elif defined(HAVE_RES_NSEARCH) + memset(&state, 0, sizeof(state)); + if (res_ninit(handle)) { + zval_dtor(return_value); + RETURN_FALSE; + } +#else + res_init(); +#endif + + n = php_dns_search(handle, hostname, C_IN, type_to_fetch, answer.qb2, sizeof answer); + + if (n < 0) { + php_dns_free_handle(handle); + continue; + } + + cp = answer.qb2 + HFIXEDSZ; + end = answer.qb2 + n; + hp = (HEADER *)&answer; + qd = ntohs(hp->qdcount); + an = ntohs(hp->ancount); + ns = ntohs(hp->nscount); + ar = ntohs(hp->arcount); + + /* Skip QD entries, they're only used by dn_expand later on */ + while (qd-- > 0) { + n = dn_skipname(cp, end); + if (n < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse DNS data received"); + zval_dtor(return_value); + php_dns_free_handle(handle); + RETURN_FALSE; + } + cp += n + QFIXEDSZ; + } + + /* YAY! Our real answers! */ + while (an-- && cp && cp < end) { + zval *retval; + + cp = php_parserr(cp, &answer, type_to_fetch, store_results, raw, &retval); + if (retval != NULL && store_results) { + add_next_index_zval(return_value, retval); + } + } + + if (authns || addtl) { + /* List of Authoritative Name Servers + * Process when only requesting addtl so that we can skip through the section + */ + while (ns-- > 0 && cp && cp < end) { + zval *retval = NULL; + + cp = php_parserr(cp, &answer, DNS_T_ANY, authns != NULL, raw, &retval); + if (retval != NULL) { + add_next_index_zval(authns, retval); + } + } + } + + if (addtl) { + /* Additional records associated with authoritative name servers */ + while (ar-- > 0 && cp && cp < end) { + zval *retval = NULL; + + cp = php_parserr(cp, &answer, DNS_T_ANY, 1, raw, &retval); + if (retval != NULL) { + add_next_index_zval(addtl, retval); + } + } + } + php_dns_free_handle(handle); + } + } +} +/* }}} */ + +/* {{{ proto bool dns_get_mx(string hostname, array mxhosts [, array weight]) + Get MX records corresponding to a given Internet host name */ +PHP_FUNCTION(dns_get_mx) +{ + char *hostname; + int hostname_len; + zval *mx_list, *weight_list = NULL; + int count, qdc; + u_short type, weight; + u_char ans[MAXPACKET]; + char buf[MAXHOSTNAMELEN]; + HEADER *hp; + u_char *cp, *end; + int i; +#if defined(HAVE_DNS_SEARCH) + struct sockaddr_storage from; + uint32_t fromsize = sizeof(from); + dns_handle_t handle; +#elif defined(HAVE_RES_NSEARCH) + struct __res_state state; + struct __res_state *handle = &state; +#endif + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) { + return; + } + + zval_dtor(mx_list); + array_init(mx_list); + + if (weight_list) { + zval_dtor(weight_list); + array_init(weight_list); + } + +#if defined(HAVE_DNS_SEARCH) + handle = dns_open(NULL); + if (handle == NULL) { + RETURN_FALSE; + } +#elif defined(HAVE_RES_NSEARCH) + memset(&state, 0, sizeof(state)); + if (res_ninit(handle)) { + RETURN_FALSE; + } +#else + res_init(); +#endif + + i = php_dns_search(handle, hostname, C_IN, DNS_T_MX, (u_char *)&ans, sizeof(ans)); + if (i < 0) { + RETURN_FALSE; + } + if (i > (int)sizeof(ans)) { + i = sizeof(ans); + } + hp = (HEADER *)&ans; + cp = (u_char *)&ans + HFIXEDSZ; + end = (u_char *)&ans +i; + for (qdc = ntohs((unsigned short)hp->qdcount); qdc--; cp += i + QFIXEDSZ) { + if ((i = dn_skipname(cp, end)) < 0 ) { + php_dns_free_handle(handle); + RETURN_FALSE; + } + } + count = ntohs((unsigned short)hp->ancount); + while (--count >= 0 && cp < end) { + if ((i = dn_skipname(cp, end)) < 0 ) { + php_dns_free_handle(handle); + RETURN_FALSE; + } + cp += i; + GETSHORT(type, cp); + cp += INT16SZ + INT32SZ; + GETSHORT(i, cp); + if (type != DNS_T_MX) { + cp += i; + continue; + } + GETSHORT(weight, cp); + if ((i = dn_expand(ans, end, cp, buf, sizeof(buf)-1)) < 0) { + php_dns_free_handle(handle); + RETURN_FALSE; + } + cp += i; + add_next_index_string(mx_list, buf, 1); + if (weight_list) { + add_next_index_long(weight_list, weight); + } + } + php_dns_free_handle(handle); + RETURN_TRUE; +} +/* }}} */ +#endif /* HAVE_FULL_DNS_FUNCS */ +#endif /* !defined(PHP_WIN32) && (HAVE_DNS_SEARCH_FUNC && !(defined(__BEOS__) || defined(NETWARE))) */ + +#if HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32) +PHP_MINIT_FUNCTION(dns) { + REGISTER_LONG_CONSTANT("DNS_A", PHP_DNS_A, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_NS", PHP_DNS_NS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_CNAME", PHP_DNS_CNAME, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_SOA", PHP_DNS_SOA, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_PTR", PHP_DNS_PTR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_HINFO", PHP_DNS_HINFO, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_MX", PHP_DNS_MX, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_TXT", PHP_DNS_TXT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_SRV", PHP_DNS_SRV, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_NAPTR", PHP_DNS_NAPTR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_AAAA", PHP_DNS_AAAA, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_A6", PHP_DNS_A6, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_ANY", PHP_DNS_ANY, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DNS_ALL", PHP_DNS_ALL, CONST_CS | CONST_PERSISTENT); + return SUCCESS; +} +#endif /* HAVE_FULL_DNS_FUNCS */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/52ffe2_9b50cf_ext#fileinfo#libmagic#cdf.c b/src/main/resource/testFiles/php-src/revFiles/52ffe2_9b50cf_ext#fileinfo#libmagic#cdf.c new file mode 100644 index 0000000..152e6a5 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/52ffe2_9b50cf_ext#fileinfo#libmagic#cdf.c @@ -0,0 +1,1111 @@ +/*- + * Copyright (c) 2008 Christos Zoulas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Parse composite document files, the format used in Microsoft Office + * document files before they switched to zipped xml. + * Info from: http://sc.openoffice.org/compdocfileformat.pdf + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: cdf.c,v 1.17 2009/02/03 20:27:51 christos Exp $") +#endif + +#include +#ifdef CDF_DEBUG +#include +#endif +#include + +#ifdef PHP_WIN32 +#include "win32/unistd.h" +#else +#include +#endif + +#include +#include +#include + +#ifndef EFTYPE +#define EFTYPE EINVAL +#endif + +#include "cdf.h" + +#ifndef __arraycount +#define __arraycount(a) (sizeof(a) / sizeof(a[0])) +#endif + +#ifdef CDF_DEBUG +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif + +static union { + char s[4]; + uint32_t u; +} cdf_bo; + +#define NEED_SWAP (cdf_bo.u == (uint32_t)0x01020304) + +#define CDF_TOLE8(x) (NEED_SWAP ? cdf_tole8(x) : (uint64_t)(x)) +#define CDF_TOLE4(x) (NEED_SWAP ? cdf_tole4(x) : (uint32_t)(x)) +#define CDF_TOLE2(x) (NEED_SWAP ? cdf_tole2(x) : (uint16_t)(x)) + +/* + * swap a short + */ +uint16_t +cdf_tole2(uint16_t sv) +{ + uint16_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[1]; + d[1] = s[0]; + return rv; +} + +/* + * swap an int + */ +uint32_t +cdf_tole4(uint32_t sv) +{ + uint32_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + return rv; +} + +/* + * swap a quad + */ +uint64_t +cdf_tole8(uint64_t sv) +{ + uint64_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[7]; + d[1] = s[6]; + d[2] = s[5]; + d[3] = s[4]; + d[4] = s[3]; + d[5] = s[2]; + d[6] = s[1]; + d[7] = s[0]; + return rv; +} + +#define CDF_UNPACK(a) \ + (void)memcpy(&(a), &buf[len], sizeof(a)), len += sizeof(a) +#define CDF_UNPACKA(a) \ + (void)memcpy((a), &buf[len], sizeof(a)), len += sizeof(a) + +void +cdf_swap_header(cdf_header_t *h) +{ + size_t i; + + h->h_magic = CDF_TOLE8(h->h_magic); + h->h_uuid[0] = CDF_TOLE8(h->h_uuid[0]); + h->h_uuid[1] = CDF_TOLE8(h->h_uuid[1]); + h->h_revision = CDF_TOLE2(h->h_revision); + h->h_version = CDF_TOLE2(h->h_version); + h->h_byte_order = CDF_TOLE2(h->h_byte_order); + h->h_sec_size_p2 = CDF_TOLE2(h->h_sec_size_p2); + h->h_short_sec_size_p2 = CDF_TOLE2(h->h_short_sec_size_p2); + h->h_num_sectors_in_sat = CDF_TOLE4(h->h_num_sectors_in_sat); + h->h_secid_first_directory = CDF_TOLE4(h->h_secid_first_directory); + h->h_min_size_standard_stream = + CDF_TOLE4(h->h_min_size_standard_stream); + h->h_secid_first_sector_in_short_sat = + CDF_TOLE4(h->h_secid_first_sector_in_short_sat); + h->h_num_sectors_in_short_sat = + CDF_TOLE4(h->h_num_sectors_in_short_sat); + h->h_secid_first_sector_in_master_sat = + CDF_TOLE4(h->h_secid_first_sector_in_master_sat); + h->h_num_sectors_in_master_sat = + CDF_TOLE4(h->h_num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) + h->h_master_sat[i] = CDF_TOLE4(h->h_master_sat[i]); +} + +void +cdf_unpack_header(cdf_header_t *h, char *buf) +{ + size_t i; + size_t len = 0; + + CDF_UNPACK(h->h_magic); + CDF_UNPACKA(h->h_uuid); + CDF_UNPACK(h->h_revision); + CDF_UNPACK(h->h_version); + CDF_UNPACK(h->h_byte_order); + CDF_UNPACK(h->h_sec_size_p2); + CDF_UNPACK(h->h_short_sec_size_p2); + CDF_UNPACKA(h->h_unused0); + CDF_UNPACK(h->h_num_sectors_in_sat); + CDF_UNPACK(h->h_secid_first_directory); + CDF_UNPACKA(h->h_unused1); + CDF_UNPACK(h->h_min_size_standard_stream); + CDF_UNPACK(h->h_secid_first_sector_in_short_sat); + CDF_UNPACK(h->h_num_sectors_in_short_sat); + CDF_UNPACK(h->h_secid_first_sector_in_master_sat); + CDF_UNPACK(h->h_num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) + CDF_UNPACK(h->h_master_sat[i]); +} + +void +cdf_swap_dir(cdf_directory_t *d) +{ + d->d_namelen = CDF_TOLE2(d->d_namelen); + d->d_left_child = CDF_TOLE4(d->d_left_child); + d->d_right_child = CDF_TOLE4(d->d_right_child); + d->d_storage = CDF_TOLE4(d->d_storage); + d->d_storage_uuid[0] = CDF_TOLE8(d->d_storage_uuid[0]); + d->d_storage_uuid[1] = CDF_TOLE8(d->d_storage_uuid[1]); + d->d_flags = CDF_TOLE4(d->d_flags); + d->d_created = CDF_TOLE8(d->d_created); + d->d_modified = CDF_TOLE8(d->d_modified); + d->d_stream_first_sector = CDF_TOLE4(d->d_stream_first_sector); + d->d_size = CDF_TOLE4(d->d_size); +} + +void +cdf_swap_class(cdf_classid_t *d) +{ + d->cl_dword = CDF_TOLE4(d->cl_dword); + d->cl_word[0] = CDF_TOLE2(d->cl_word[0]); + d->cl_word[1] = CDF_TOLE2(d->cl_word[1]); +} + +void +cdf_unpack_dir(cdf_directory_t *d, char *buf) +{ + size_t len = 0; + + CDF_UNPACKA(d->d_name); + CDF_UNPACK(d->d_namelen); + CDF_UNPACK(d->d_type); + CDF_UNPACK(d->d_color); + CDF_UNPACK(d->d_left_child); + CDF_UNPACK(d->d_right_child); + CDF_UNPACK(d->d_storage); + CDF_UNPACKA(d->d_storage_uuid); + CDF_UNPACK(d->d_flags); + CDF_UNPACK(d->d_created); + CDF_UNPACK(d->d_modified); + CDF_UNPACK(d->d_stream_first_sector); + CDF_UNPACK(d->d_size); + CDF_UNPACK(d->d_unused0); +} + +int +cdf_read_header(int fd, cdf_header_t *h) +{ + char buf[512]; + (void)memcpy(cdf_bo.s, "\01\02\03\04", 4); + if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) + return -1; + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) + return -1; + cdf_unpack_header(h, buf); + cdf_swap_header(h); + if (h->h_magic != CDF_MAGIC) { + DPRINTF(("Bad magic 0x%x != 0x$x\n", h->h_magic, CDF_MAGIC)); + errno = EFTYPE; + return -1; + } + return 0; +} + + +ssize_t +cdf_read_sector(int fd, void *buf, size_t offs, size_t len, + const cdf_header_t *h, cdf_secid_t id) +{ + assert((size_t)CDF_SEC_SIZE(h) == len); + if (lseek(fd, (off_t)CDF_SEC_POS(h, id), SEEK_SET) == (off_t)-1) + return -1; + return read(fd, ((char *)buf) + offs, len); +} + +ssize_t +cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs, + size_t len, const cdf_header_t *h, cdf_secid_t id) +{ + assert((size_t)CDF_SHORT_SEC_SIZE(h) == len); + (void)memcpy(((char *)buf) + offs, + ((const char *)sst->sst_tab) + CDF_SHORT_SEC_POS(h, id), len); + return len; +} + +/* + * Read the sector allocation table. + */ +int +cdf_read_sat(int fd, cdf_header_t *h, cdf_sat_t *sat) +{ + size_t i, j, k; + size_t ss = CDF_SEC_SIZE(h); + cdf_secid_t *msa, mid; + + for (i = 0; i < __arraycount(h->h_master_sat); i++) + if (h->h_master_sat[i] == CDF_SECID_FREE) + break; + + sat->sat_len = (h->h_num_sectors_in_master_sat + i); + if ((sat->sat_tab = calloc(sat->sat_len, ss)) == NULL) + return -1; + + for (i = 0; i < __arraycount(h->h_master_sat); i++) { + if (h->h_master_sat[i] < 0) + break; + if (cdf_read_sector(fd, sat->sat_tab, ss * i, ss, h, + h->h_master_sat[i]) != (ssize_t)ss) { + DPRINTF(("Reading sector %d", h->h_master_sat[i])); + goto out1; + } + } + + if ((msa = calloc(1, ss)) == NULL) + goto out1; + + mid = h->h_secid_first_sector_in_master_sat; + for (j = 0; j < h->h_num_sectors_in_master_sat; j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Reading master sector loop limit")); + errno = EFTYPE; + goto out2; + } + if (cdf_read_sector(fd, msa, 0, ss, h, mid) != (ssize_t)ss) { + DPRINTF(("Reading master sector %d", mid)); + goto out2; + } + for (k = 0; k < (ss / sizeof(mid)) - 1; k++, i++) + if (cdf_read_sector(fd, sat->sat_tab, ss * i, ss, h, + CDF_TOLE4(msa[k])) != (ssize_t)ss) { + DPRINTF(("Reading sector %d", + CDF_TOLE4(msa[k]))); + goto out2; + } + mid = CDF_TOLE4(msa[(ss / sizeof(mid)) - 1]); + } + free(msa); + return 0; +out2: + free(msa); +out1: + free(sat->sat_tab); + return -1; +} + +size_t +cdf_count_chain(const cdf_header_t *h, const cdf_sat_t *sat, + cdf_secid_t sid) +{ + size_t i, j, s = CDF_SEC_SIZE(h) / sizeof(cdf_secid_t); + cdf_secid_t maxsector = (cdf_secid_t)(sat->sat_len * s); + + DPRINTF(("Chain:")); + for (j = i = 0; sid >= 0; i++, j++) { + DPRINTF((" %d", sid)); + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Counting chain loop limit")); + errno = EFTYPE; + return (size_t)-1; + } + if (sid > maxsector) { + DPRINTF(("Sector %d > %d\n", sid, maxsector)); + errno = EFTYPE; + return (size_t)-1; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + DPRINTF(("\n")); + return i; +} + +int +cdf_read_long_sector_chain(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + size_t ss = CDF_SEC_SIZE(h), i, j; + ssize_t nr; + scn->sst_len = cdf_count_chain(h, sat, sid); + scn->sst_dirlen = len; + + if (scn->sst_len == (size_t)-1) + return -1; + + scn->sst_tab = calloc(scn->sst_len, ss); + if (scn->sst_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if ((nr = cdf_read_sector(fd, scn->sst_tab, i * ss, ss, h, + sid)) != (ssize_t)ss) { + if (i == scn->sst_len - 1 && nr > 0) { + /* Last sector might be truncated */ + return 0; + } + DPRINTF(("Reading long sector chain %d", sid)); + goto out; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read long sector chain loop limit")); + errno = EFTYPE; + goto out; + } + } + return 0; +out: + free(scn->sst_tab); + return -1; +} + +int +cdf_read_short_sector_chain(const cdf_header_t *h, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + size_t ss = CDF_SHORT_SEC_SIZE(h), i, j; + scn->sst_len = cdf_count_chain(h, ssat, sid); + scn->sst_dirlen = len; + + if (scn->sst_len == (size_t)-1) + return -1; + + scn->sst_tab = calloc(scn->sst_len, ss); + if (scn->sst_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read short sector chain loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h, + sid) != (ssize_t)ss) { + DPRINTF(("Reading short sector chain %d", sid)); + goto out; + } + sid = CDF_TOLE4(ssat->sat_tab[sid]); + } + return 0; +out: + free(scn->sst_tab); + return -1; +} + +int +cdf_read_sector_chain(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + + if (len < h->h_min_size_standard_stream) + return cdf_read_short_sector_chain(h, ssat, sst, sid, len, + scn); + else + return cdf_read_long_sector_chain(fd, h, sat, sid, len, scn); +} + +int +cdf_read_dir(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_dir_t *dir) +{ + size_t i, j; + size_t ss = CDF_SEC_SIZE(h), ns, nd; + char *buf; + cdf_secid_t sid = h->h_secid_first_directory; + + ns = cdf_count_chain(h, sat, sid); + if (ns == (size_t)-1) + return -1; + + nd = ss / CDF_DIRECTORY_SIZE; + + dir->dir_len = ns * nd; + dir->dir_tab = calloc(dir->dir_len, sizeof(dir->dir_tab[0])); + if (dir->dir_tab == NULL) + return -1; + + if ((buf = malloc(ss)) == NULL) { + free(dir->dir_tab); + return -1; + } + + for (j = i = 0; i < ns; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read dir loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_sector(fd, buf, 0, ss, h, sid) != (ssize_t)ss) { + DPRINTF(("Reading directory sector %d", sid)); + goto out; + } + for (j = 0; j < nd; j++) { + cdf_unpack_dir(&dir->dir_tab[i * nd + j], + &buf[j * CDF_DIRECTORY_SIZE]); + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + if (NEED_SWAP) + for (i = 0; i < dir->dir_len; i++) + cdf_swap_dir(&dir->dir_tab[i]); + free(buf); + return 0; +out: + free(dir->dir_tab); + free(buf); + return -1; +} + + +int +cdf_read_ssat(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_sat_t *ssat) +{ + size_t i, j; + size_t ss = CDF_SEC_SIZE(h); + cdf_secid_t sid = h->h_secid_first_sector_in_short_sat; + + ssat->sat_len = cdf_count_chain(h, sat, sid); + if (ssat->sat_len == (size_t)-1) + return -1; + + ssat->sat_tab = calloc(ssat->sat_len, ss); + if (ssat->sat_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read short sat sector loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_sector(fd, ssat->sat_tab, i * ss, ss, h, sid) != + (ssize_t)ss) { + DPRINTF(("Reading short sat sector %d", sid)); + goto out; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + return 0; +out: + free(ssat->sat_tab); + return -1; +} + +int +cdf_read_short_stream(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_dir_t *dir, cdf_stream_t *scn) +{ + size_t i; + const cdf_directory_t *d; + + for (i = 0; i < dir->dir_len; i++) + if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE) + break; + + if (i == dir->dir_len) { + DPRINTF(("Cannot find root storage node\n")); + errno = EFTYPE; + return -1; + } + d = &dir->dir_tab[i]; + + /* If the it is not there, just fake it; some docs don't have it */ + if (d->d_stream_first_sector < 0) { + scn->sst_tab = NULL; + scn->sst_len = 0; + return 0; + } + + return cdf_read_long_sector_chain(fd, h, sat, + d->d_stream_first_sector, d->d_size, scn); +} + +static int +cdf_namecmp(const char *d, const uint16_t *s, size_t l) +{ + for (; l--; d++, s++) + if (*d != CDF_TOLE2(*s)) + return (unsigned char)*d - CDF_TOLE2(*s); + return 0; +} + +int +cdf_read_summary_info(int fd, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir, cdf_stream_t *scn) +{ + size_t i; + const cdf_directory_t *d; + static const char name[] = "\05SummaryInformation"; + + for (i = 0; i < dir->dir_len; i++) + if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_USER_STREAM && + cdf_namecmp(name, dir->dir_tab[i].d_name, sizeof(name)) + == 0) + break; + + if (i == dir->dir_len) { + DPRINTF(("Cannot find summary information section\n")); + errno = EFTYPE; + return -1; + } + d = &dir->dir_tab[i]; + return cdf_read_sector_chain(fd, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, scn); +} + +int +cdf_read_property_info(const cdf_stream_t *sst, uint32_t offs, + cdf_property_info_t **info, size_t *count, size_t *maxcount) +{ + const cdf_section_header_t *shp; + cdf_section_header_t sh; + const uint32_t *p, *q, *e; + int16_t s16; + int32_t s32; + uint32_t u32; + int64_t s64; + uint64_t u64; + cdf_timestamp_t tp; + size_t i, o, nelements, j; + cdf_property_info_t *inp; + + shp = (const void *)((const char *)sst->sst_tab + offs); + sh.sh_len = CDF_TOLE4(shp->sh_len); + sh.sh_properties = CDF_TOLE4(shp->sh_properties); + DPRINTF(("section len: %d properties %d\n", sh.sh_len, + sh.sh_properties)); + if (*maxcount) { + *maxcount += sh.sh_properties; + inp = realloc(*info, *maxcount * sizeof(*inp)); + } else { + *maxcount = sh.sh_properties; + inp = malloc(*maxcount * sizeof(*inp)); + } + if (inp == NULL) + goto out; + *info = inp; + inp += *count; + *count += sh.sh_properties; + p = (const void *)((const char *)sst->sst_tab + offs + sizeof(sh)); + e = (const void *)(((const char *)shp) + sh.sh_len); + for (i = 0; i < sh.sh_properties; i++) { + q = (const uint32_t *)((const char *)p + + CDF_TOLE4(p[(i << 1) + 1])) - 2; + if (q > e) { + DPRINTF(("Ran of the end %p > %p\n", q, e)); + goto out; + } + inp[i].pi_id = CDF_TOLE4(p[i << 1]); + inp[i].pi_type = CDF_TOLE4(q[0]); + DPRINTF(("%d) id=%x type=%x offs=%x\n", i, inp[i].pi_id, + inp[i].pi_type, (const char *)q - (const char *)p)); + if (inp[i].pi_type & CDF_VECTOR) { + nelements = CDF_TOLE4(q[1]); + o = 2; + } else { + nelements = 1; + o = 1; + } + if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED)) + goto unknown; + switch (inp[i].pi_type & CDF_TYPEMASK) { + case CDF_EMPTY: + break; + case CDF_SIGNED16: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s16, &q[o], sizeof(s16)); + inp[i].pi_s16 = CDF_TOLE2(s16); + break; + case CDF_SIGNED32: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s32, &q[o], sizeof(s32)); + inp[i].pi_s32 = CDF_TOLE4(s32); + break; + case CDF_BOOL: + case CDF_UNSIGNED32: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u32, &q[o], sizeof(u32)); + inp[i].pi_u32 = CDF_TOLE4(u32); + break; + case CDF_SIGNED64: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s64, &q[o], sizeof(s64)); + inp[i].pi_s64 = CDF_TOLE4(s64); + break; + case CDF_UNSIGNED64: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u64, &q[o], sizeof(u64)); + inp[i].pi_u64 = CDF_TOLE4(u64); + break; + case CDF_LENGTH32_STRING: + if (nelements > 1) { + size_t nelem = inp - *info; + *maxcount += nelements; + inp = realloc(*info, *maxcount * sizeof(*inp)); + if (inp == NULL) + goto out; + *info = inp; + inp = *info + nelem; + } + DPRINTF(("nelements = %d\n", nelements)); + for (j = 0; j < nelements; j++, i++) { + uint32_t l = CDF_TOLE4(q[o]); + inp[i].pi_str.s_len = l; + inp[i].pi_str.s_buf = (const char *)(&q[o+1]); + DPRINTF(("l = %d, r = %d, s = %s\n", l, + CDF_ROUND(l, sizeof(l)), + inp[i].pi_str.s_buf)); + l = 4 + CDF_ROUND(l, sizeof(l)); + o += l >> 2; + } + i--; + break; + case CDF_FILETIME: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&tp, &q[o], sizeof(tp)); + inp[i].pi_tp = CDF_TOLE8(tp); + break; + case CDF_CLIPBOARD: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + break; + default: + unknown: + DPRINTF(("Don't know how to deal with %x\n", + inp[i].pi_type)); + goto out; + } + } + return 0; +out: + free(*info); + return -1; +} + +int +cdf_unpack_summary_info(const cdf_stream_t *sst, cdf_summary_info_header_t *ssi, + cdf_property_info_t **info, size_t *count) +{ + size_t i, maxcount; + const cdf_summary_info_header_t *si = sst->sst_tab; + const cdf_section_declaration_t *sd = (const void *) + ((const char *)sst->sst_tab + CDF_SECTION_DECLARATION_OFFSET); + + ssi->si_byte_order = CDF_TOLE2(si->si_byte_order); + ssi->si_os_version = CDF_TOLE2(si->si_os_version); + ssi->si_os = CDF_TOLE2(si->si_os); + ssi->si_class = si->si_class; + cdf_swap_class(&ssi->si_class); + ssi->si_count = CDF_TOLE2(si->si_count); + *count = 0; + maxcount = 0; + *info = NULL; + for (i = 0; i < CDF_TOLE4(si->si_count); i++) { + if (i >= CDF_LOOP_LIMIT) { + DPRINTF(("Unpack summary info loop limit")); + errno = EFTYPE; + return -1; + } + if (cdf_read_property_info(sst, CDF_TOLE4(sd->sd_offset), + info, count, &maxcount) == -1) + return -1; + } + return 0; +} + + + +int +cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id) +{ + return snprintf(buf, buflen, "%.8x-%.4x-%.4x-%.2x%.2x-" + "%.2x%.2x%.2x%.2x%.2x%.2x", id->cl_dword, id->cl_word[0], + id->cl_word[1], id->cl_two[0], id->cl_two[1], id->cl_six[0], + id->cl_six[1], id->cl_six[2], id->cl_six[3], id->cl_six[4], + id->cl_six[5]); +} + +static const struct { + uint32_t v; + const char *n; +} vn[] = { + { CDF_PROPERTY_CODE_PAGE, "Code page" }, + { CDF_PROPERTY_TITLE, "Title" }, + { CDF_PROPERTY_SUBJECT, "Subject" }, + { CDF_PROPERTY_AUTHOR, "Author" }, + { CDF_PROPERTY_KEYWORDS, "Keywords" }, + { CDF_PROPERTY_COMMENTS, "Comments" }, + { CDF_PROPERTY_TEMPLATE, "Template" }, + { CDF_PROPERTY_LAST_SAVED_BY, "Last Saved By" }, + { CDF_PROPERTY_REVISION_NUMBER, "Revision Number" }, + { CDF_PROPERTY_TOTAL_EDITING_TIME, "Total Editing Time" }, + { CDF_PROPERTY_LAST_PRINTED, "Last Printed" }, + { CDF_PROPERTY_CREATE_TIME, "Create Time/Date" }, + { CDF_PROPERTY_LAST_SAVED_TIME, "Last Saved Time/Date" }, + { CDF_PROPERTY_NUMBER_OF_PAGES, "Number of Pages" }, + { CDF_PROPERTY_NUMBER_OF_WORDS, "Number of Words" }, + { CDF_PROPERTY_NUMBER_OF_CHARACTERS, "Number of Characters" }, + { CDF_PROPERTY_THUMBNAIL, "Thumbnail" }, + { CDF_PROPERTY_NAME_OF_APPLICATION, "Name of Creating Application" }, + { CDF_PROPERTY_SECURITY, "Security" }, + { CDF_PROPERTY_LOCALE_ID, "Locale ID" }, +}; + +int +cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p) +{ + size_t i; + + for (i = 0; i < __arraycount(vn); i++) + if (vn[i].v == p) + return snprintf(buf, bufsiz, "%s", vn[i].n); + return snprintf(buf, bufsiz, "0x%x", p); +} + +int +cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts) +{ + size_t len = 0; + int days, hours, mins, secs; + + ts /= CDF_TIME_PREC; + secs = ts % 60; + ts /= 60; + mins = ts % 60; + ts /= 60; + hours = ts % 24; + ts /= 24; + days = ts; + + if (days) { + len += snprintf(buf + len, bufsiz - len, "%dd+", days); + if (len >= bufsiz) + return len; + } + + if (days || hours) { + len += snprintf(buf + len, bufsiz - len, "%.2d:", hours); + if (len >= bufsiz) + return len; + } + + len += snprintf(buf + len, bufsiz - len, "%.2d:", mins); + if (len >= bufsiz) + return len; + + len += snprintf(buf + len, bufsiz - len, "%.2d", secs); + return len; +} + + +#ifdef CDF_DEBUG +void +cdf_dump_header(const cdf_header_t *h) +{ + size_t i; + +#define DUMP(a, b) printf("%40.40s = " a "\n", # b, h->h_ ## b) + DUMP("%d", revision); + DUMP("%d", version); + DUMP("0x%x", byte_order); + DUMP("%d", sec_size_p2); + DUMP("%d", short_sec_size_p2); + DUMP("%d", num_sectors_in_sat); + DUMP("%d", secid_first_directory); + DUMP("%d", min_size_standard_stream); + DUMP("%d", secid_first_sector_in_short_sat); + DUMP("%d", num_sectors_in_short_sat); + DUMP("%d", secid_first_sector_in_master_sat); + DUMP("%d", num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) { + if (h->h_master_sat[i] == CDF_SECID_FREE) + break; + printf("%35.35s[%.3zu] = %d\n", + "master_sat", i, h->h_master_sat[i]); + } +} + +void +cdf_dump_sat(const char *prefix, const cdf_header_t *h, const cdf_sat_t *sat) +{ + size_t i, j, s = CDF_SEC_SIZE(h) / sizeof(cdf_secid_t); + + for (i = 0; i < sat->sat_len; i++) { + printf("%s[%zu]:\n", prefix, i); + for (j = 0; j < s; j++) { + printf("%5d, ", CDF_TOLE4(sat->sat_tab[s * i + j])); + if ((j + 1) % 10 == 0) + printf("\n"); + } + printf("\n"); + } +} + +void +cdf_dump(void *v, size_t len) +{ + size_t i, j; + unsigned char *p = v; + char abuf[16]; + printf("%.4x: ", 0); + for (i = 0, j = 0; i < len; i++, p++) { + printf("%.2x ", *p); + abuf[j++] = isprint(*p) ? *p : '.'; + if (j == 16) { + j = 0; + abuf[15] = '\0'; + printf("%s\n%.4x: ", abuf, i + 1); + } + } + printf("\n"); +} + +void +cdf_dump_stream(const cdf_header_t *h, const cdf_stream_t *sst) +{ + size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? + CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); + cdf_dump(sst->sst_tab, ss * sst->sst_len); +} + +void +cdf_dump_dir(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir) +{ + size_t i, j; + cdf_directory_t *d; + char name[__arraycount(d->d_name)]; + cdf_stream_t scn; + struct timeval ts; + + static const char *types[] = { "empty", "user storage", + "user stream", "lockbytes", "property", "root storage" }; + + for (i = 0; i < dir->dir_len; i++) { + d = &dir->dir_tab[i]; + for (j = 0; j < sizeof(name); j++) + name[j] = (char)CDF_TOLE2(d->d_name[j]); + printf("Directory %zu: %s\n", i, name); + if (d->d_type < __arraycount(types)) + printf("Type: %s\n", types[d->d_type]); + else + printf("Type: %d\n", d->d_type); + printf("Color: %s\n", d->d_color ? "black" : "red"); + printf("Left child: %d\n", d->d_left_child); + printf("Right child: %d\n", d->d_right_child); + printf("Flags: 0x%x\n", d->d_flags); + cdf_timestamp_to_timespec(&ts, d->d_created); + printf("Created %s", ctime(&ts.tv_sec)); + cdf_timestamp_to_timespec(&ts, d->d_modified); + printf("Modified %s", ctime(&ts.tv_sec)); + printf("Stream %d\n", d->d_stream_first_sector); + printf("Size %d\n", d->d_size); + switch (d->d_type) { + case CDF_DIR_TYPE_USER_STORAGE: + printf("Storage: %d\n", d->d_storage); + break; + case CDF_DIR_TYPE_USER_STREAM: + if (sst == NULL) + break; + if (cdf_read_sector_chain(fd, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, &scn) == -1) { + warn("Can't read stream for %s at %d len %d", + name, d->d_stream_first_sector, d->d_size); + break; + } + cdf_dump_stream(h, &scn); + free(scn.sst_tab); + break; + default: + break; + } + + } +} + +void +cdf_dump_property_info(const cdf_property_info_t *info, size_t count) +{ + cdf_timestamp_t tp; + struct timeval ts; + char buf[64]; + size_t i; + + for (i = 0; i < count; i++) { + cdf_print_property_name(buf, sizeof(buf), info[i].pi_id); + printf("%zu) %s: ", i, buf); + switch (info[i].pi_type) { + case CDF_SIGNED16: + printf("signed 16 [%hd]\n", info[i].pi_s16); + break; + case CDF_SIGNED32: + printf("signed 32 [%d]\n", info[i].pi_s32); + break; + case CDF_UNSIGNED32: + printf("unsigned 32 [%u]\n", info[i].pi_u32); + break; + case CDF_LENGTH32_STRING: + printf("string %u [%.*s]\n", info[i].pi_str.s_len, + info[i].pi_str.s_len, info[i].pi_str.s_buf); + break; + case CDF_FILETIME: + tp = info[i].pi_tp; + if (tp < 1000000000000000LL) { + cdf_print_elapsed_time(buf, sizeof(buf), tp); + printf("timestamp %s\n", buf); + } else { + cdf_timestamp_to_timespec(&ts, tp); + printf("timestamp %s", ctime(&ts.tv_sec)); + } + break; + case CDF_CLIPBOARD: + printf("CLIPBOARD %u\n", info[i].pi_u32); + break; + default: + DPRINTF(("Don't know how to deal with %x\n", + info[i].pi_type)); + break; + } + } +} + + +void +cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst) +{ + char buf[128]; + cdf_summary_info_header_t ssi; + cdf_property_info_t *info; + size_t count; + + (void)&h; + if (cdf_unpack_summary_info(sst, &ssi, &info, &count) == -1) + return; + printf("Endian: %x\n", ssi.si_byte_order); + printf("Os Version %d.%d\n", ssi.si_os_version & 0xff, + ssi.si_os_version >> 8); + printf("Os %d\n", ssi.si_os); + cdf_print_classid(buf, sizeof(buf), &ssi.si_class); + printf("Class %s\n", buf); + printf("Count %d\n", ssi.si_count); + cdf_dump_property_info(info, count); + free(info); +} + +#endif + +#ifdef TEST +int +main(int argc, char *argv[]) +{ + int fd, i; + cdf_header_t h; + cdf_sat_t sat, ssat; + cdf_stream_t sst, scn; + cdf_dir_t dir; + + if (argc < 2) { + (void)fprintf(stderr, "Usage: %s \n", getprogname()); + return -1; + } + + for (i = 1; i < argc; i++) { + if ((fd = open(argv[1], O_RDONLY)) == -1) + err(1, "Cannot open `%s'", argv[1]); + + if (cdf_read_header(fd, &h) == -1) + err(1, "Cannot read header"); +#ifdef CDF_DEBUG + cdf_dump_header(&h); +#endif + + if (cdf_read_sat(fd, &h, &sat) == -1) + err(1, "Cannot read sat"); +#ifdef CDF_DEBUG + cdf_dump_sat("SAT", &h, &sat); +#endif + + if (cdf_read_ssat(fd, &h, &sat, &ssat) == -1) + err(1, "Cannot read ssat"); +#ifdef CDF_DEBUG + cdf_dump_sat("SSAT", &h, &ssat); +#endif + + if (cdf_read_dir(fd, &h, &sat, &dir) == -1) + err(1, "Cannot read dir"); + + if (cdf_read_short_stream(fd, &h, &sat, &dir, &sst) == -1) + err(1, "Cannot read short stream"); +#ifdef CDF_DEBUG + cdf_dump_stream(&h, &sst); +#endif + +#ifdef CDF_DEBUG + cdf_dump_dir(fd, &h, &sat, &ssat, &sst, &dir); +#endif + + + if (cdf_read_summary_info(fd, &h, &sat, &ssat, &sst, &dir, + &scn) == -1) + err(1, "Cannot read summary info"); +#ifdef CDF_DEBUG + cdf_dump_summary_info(&h, &scn); +#endif + + (void)close(fd); + } + + return 0; +} +#endif diff --git a/src/main/resource/testFiles/php-src/revFiles/5317e8_9068c2_main#php_variables.c b/src/main/resource/testFiles/php-src/revFiles/5317e8_9068c2_main#php_variables.c new file mode 100644 index 0000000..bffcc78 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/5317e8_9068c2_main#php_variables.c @@ -0,0 +1,1074 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2008 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Zeev Suraski | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include +#include "php.h" +#include "ext/standard/php_standard.h" +#include "ext/standard/credits.h" +#include "php_variables.h" +#include "php_globals.h" +#include "php_content_types.h" +#include "SAPI.h" +#include "php_logos.h" +#include "zend_globals.h" + +/* for systems that need to override reading of environment variables */ +void _php_import_environment_variables(zval *array_ptr TSRMLS_DC); +PHPAPI void (*php_import_environment_variables)(zval *array_ptr TSRMLS_DC) = _php_import_environment_variables; + +PHPAPI void php_register_variable(char *var, char *strval, zval *track_vars_array TSRMLS_DC) +{ + php_register_variable_safe(var, strval, strlen(strval), track_vars_array TSRMLS_CC); +} + +/* binary-safe version */ +PHPAPI void php_register_variable_safe(char *var, char *strval, int str_len, zval *track_vars_array TSRMLS_DC) +{ + zval new_entry; + assert(strval != NULL); + + /* Prepare value */ + Z_STRLEN(new_entry) = str_len; + Z_STRVAL(new_entry) = estrndup(strval, Z_STRLEN(new_entry)); + Z_TYPE(new_entry) = IS_STRING; + + php_register_variable_ex(var, &new_entry, track_vars_array TSRMLS_CC); +} + +PHPAPI void php_u_register_variable_safe(UChar *var, UChar *strval, int str_len, zval *track_vars_array TSRMLS_DC) +{ + zval new_entry; + assert(strval != NULL); + + /* Prepare value */ + Z_USTRLEN(new_entry) = str_len; + Z_USTRVAL(new_entry) = eustrndup(strval, Z_USTRLEN(new_entry)); + Z_TYPE(new_entry) = IS_UNICODE; + + php_u_register_variable_ex(var, &new_entry, track_vars_array TSRMLS_CC); +} + +PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars_array TSRMLS_DC) +{ + char *p = NULL; + char *ip; /* index pointer */ + char *index; + char *var, *var_orig; + int var_len, index_len; + zval *gpc_element, **gpc_element_p; + zend_bool is_array = 0; + HashTable *symtable1 = NULL; + + assert(var_name != NULL); + + if (track_vars_array) { + symtable1 = Z_ARRVAL_P(track_vars_array); + } + + if (!symtable1) { + /* Nothing to do */ + zval_dtor(val); + return; + } + + /* + * Prepare variable name + */ + + var_orig = estrdup(var_name); + var = var_orig; + /* ignore leading spaces in the variable name */ + while (*var && *var==' ') { + var++; + } + + /* ensure that we don't have spaces or dots in the variable name (not binary safe) */ + for (p = var; *p; p++) { + if (*p == ' ' || *p == '.') { + *p='_'; + } else if (*p == '[') { + is_array = 1; + ip = p; + *p = 0; + break; + } + } + var_len = p - var; + + if (var_len==0) { /* empty variable name, or variable name with a space in it */ + zval_dtor(val); + efree(var_orig); + return; + } + + /* GLOBALS hijack attempt, reject parameter */ + if (symtable1 == EG(active_symbol_table) && + var_len == sizeof("GLOBALS")-1 && + !memcmp(var, "GLOBALS", sizeof("GLOBALS")-1)) { + zval_dtor(val); + efree(var_orig); + return; + } + + index = var; + index_len = var_len; + + if (is_array) { + int nest_level = 0; + while (1) { + char *index_s; + int new_idx_len = 0; + + if(++nest_level > PG(max_input_nesting_level)) { + HashTable *ht; + /* too many levels of nesting */ + + ht = Z_ARRVAL_P(track_vars_array); + + zend_hash_del(ht, var, var_len + 1); + zval_dtor(val); + + /* do not output the error message to the screen, + this helps us to to avoid "information disclosure" */ + if (!PG(display_errors)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variable nesting level exceeded %ld. To increase the limit change max_input_nesting_level in php.ini.", PG(max_input_nesting_level)); + } + efree(var_orig); + return; + } + + ip++; + index_s = ip; + if (isspace(*ip)) { + ip++; + } + if (*ip==']') { + index_s = NULL; + } else { + ip = strchr(ip, ']'); + if (!ip) { + /* PHP variables cannot contain '[' in their names, so we replace the character with a '_' */ + *(index_s - 1) = '_'; + + index_len = 0; + if (index) { + index_len = strlen(index); + } + goto plain_var; + return; + } + *ip = 0; + new_idx_len = strlen(index_s); + } + + if (!index) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } else { + if (zend_rt_symtable_find(symtable1, index, index_len + 1, (void **) &gpc_element_p) == FAILURE + || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_rt_symtable_update(symtable1, index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } + } + symtable1 = Z_ARRVAL_PP(gpc_element_p); + /* ip pointed to the '[' character, now obtain the key */ + index = index_s; + index_len = new_idx_len; + + ip++; + if (*ip == '[') { + is_array = 1; + *ip = 0; + } else { + goto plain_var; + } + } + } else { +plain_var: + MAKE_STD_ZVAL(gpc_element); + gpc_element->value = val->value; + Z_TYPE_P(gpc_element) = Z_TYPE_P(val); + if (!index) { + zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } else { + /* + * According to rfc2965, more specific paths are listed above the less specific ones. + * If we encounter a duplicate cookie name, we should skip it, since it is not possible + * to have the same (plain text) cookie name for the same path and we should not overwrite + * more specific cookies with the less specific ones. + */ + if (PG(http_globals)[TRACK_VARS_COOKIE] && + symtable1 == Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_COOKIE]) && + zend_rt_symtable_exists(symtable1, index, index_len+1)) { + zval_ptr_dtor(&gpc_element); + } else { + zend_rt_symtable_update(symtable1, index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } + } + } + efree(var_orig); +} + +PHPAPI void php_u_register_variable_ex(UChar *var, zval *val, zval *track_vars_array TSRMLS_DC) +{ + UChar *p = NULL; + UChar *ip; /* index pointer */ + UChar *index; + int var_len, index_len; + zval *gpc_element, **gpc_element_p; + zend_bool is_array; + HashTable *symtable1=NULL; + + assert(var != NULL); + + if (track_vars_array) { + symtable1 = Z_ARRVAL_P(track_vars_array); + } + + if (!symtable1) { + /* Nothing to do */ + zval_dtor(val); + return; + } + + /* + * Prepare variable name + */ + ip = u_strchr(var, 0x5b /*'['*/); + if (ip) { + is_array = 1; + *ip = 0; + } else { + is_array = 0; + } + /* ignore leading spaces in the variable name */ + while (*var && *var==0x20 /*' '*/) { + var++; + } + var_len = u_strlen(var); + if (var_len==0) { /* empty variable name, or variable name with a space in it */ + zval_dtor(val); + return; + } + /* ensure that we don't have spaces or dots in the variable name (not binary safe) */ + for (p=var; *p; p++) { + switch(*p) { + case 0x20: /*' '*/ + case 0x2e: /*'.'*/ + *p=0x5f; /*'_'*/ + break; + } + } + + index = var; + index_len = var_len; + + if (is_array) { + int nest_level = 0; + while (1) { + zstr escaped_index = NULL_ZSTR; + UChar *index_s; + int new_idx_len = 0; + + if(++nest_level > PG(max_input_nesting_level)) { + HashTable *ht; + zstr tmp_var; + /* too many levels of nesting */ + + ht = Z_ARRVAL_P(track_vars_array); + + tmp_var.u = var; + zend_u_hash_del(ht, IS_UNICODE, tmp_var, var_len + 1); + zval_dtor(val); + + /* do not output the error message to the screen, + this helps us to to avoid "information disclosure" */ + if (!PG(display_errors)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variable nesting level exceeded %ld. To increase the limit change max_input_nesting_level in php.ini.", PG(max_input_nesting_level)); + } + return; + } + + ip++; + index_s = ip; + if (u_isspace(*ip)) { + ip++; + } + if (*ip==0x5d /*']'*/) { + index_s = NULL; + } else { + ip = u_strchr(ip, 0x5d /*']'*/); + if (!ip) { + /* PHP variables cannot contain '[' in their names, so we replace the character with a '_' */ + *(index_s - 1) = 0x5f; /*'_'*/ + + index_len = var_len = 0; + if (index) { + index_len = var_len = u_strlen(index); + } + goto plain_var; + return; + } + *ip = 0; + new_idx_len = u_strlen(index_s); + } + + if (!index) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } else { + escaped_index.u = index; + + if (zend_u_symtable_find(symtable1, IS_UNICODE, escaped_index, index_len+1, (void **) &gpc_element_p)==FAILURE + || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_u_symtable_update(symtable1, IS_UNICODE, escaped_index, index_len+1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } + if (index!=escaped_index.u) { + efree(escaped_index.u); + } + } + symtable1 = Z_ARRVAL_PP(gpc_element_p); + /* ip pointed to the '[' character, now obtain the key */ + index = index_s; + index_len = new_idx_len; + + ip++; + if (*ip==0x5b /*'['*/) { + is_array = 1; + *ip = 0; + } else { + goto plain_var; + } + } + } else { +plain_var: + MAKE_STD_ZVAL(gpc_element); + gpc_element->value = val->value; + Z_TYPE_P(gpc_element) = Z_TYPE_P(val); + if (!index) { + zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } else { + /* UTODO fix for php_addslashes case */ + /* char *escaped_index = php_addslashes(index, index_len, &index_len, 0 TSRMLS_CC); */ + zstr escaped_index; + + escaped_index.u = index; + zend_u_symtable_update(symtable1, IS_UNICODE, escaped_index, index_len+1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + /* efree(escaped_index); */ + } + } +} + +SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler) +{ + char *var, *val, *e, *s, *p; + zval *array_ptr = (zval *) arg; + UConverter *input_conv = UG(http_input_encoding_conv); + + if (SG(request_info).post_data == NULL) { + return; + } + + if (!input_conv) { + input_conv = ZEND_U_CONVERTER(UG(output_encoding_conv)); + } + + s = SG(request_info).post_data; + e = s + SG(request_info).post_data_length; + + while (s < e && (p = memchr(s, '&', (e - s)))) { +last_value: + if ((val = memchr(s, '=', (p - s)))) { /* have a value */ + if (UG(unicode)) { + UChar *u_var, *u_val; + int u_var_len, u_val_len; + int var_len; + int val_len; + UErrorCode status1 = U_ZERO_ERROR, status2 = U_ZERO_ERROR; + + var = s; + var_len = val - s; + + php_url_decode(var, var_len); + val++; + val_len = php_url_decode(val, (p - val)); + zend_string_to_unicode_ex(input_conv, &u_var, &u_var_len, var, var_len, &status1); + zend_string_to_unicode_ex(input_conv, &u_val, &u_val_len, val, val_len, &status2); + if (U_SUCCESS(status1) && U_SUCCESS(status2)) { + /* UTODO add input filtering */ + php_u_register_variable_safe(u_var, u_val, u_val_len, array_ptr TSRMLS_CC); + } else { + /* UTODO set a user-accessible flag to indicate that conversion failed? */ + } + efree(u_var); + efree(u_val); + } else { + unsigned int val_len, new_val_len; + + var = s; + + php_url_decode(var, (val - s)); + val++; + val_len = php_url_decode(val, (p - val)); + val = estrndup(val, val_len); + if (sapi_module.input_filter(PARSE_POST, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); + } + } + s = p + 1; + } + if (s < e) { + p = e; + goto last_value; + } +} + +SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter) +{ + /* TODO: check .ini setting here and apply user-defined input filter */ + if(new_val_len) *new_val_len = val_len; + return 1; +} + +SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data) +{ + char *res = NULL, *var, *val, *separator = NULL; + const char *c_var; + zval *array_ptr; + int free_buffer = 0; + char *strtok_buf = NULL; + UConverter *input_conv = UG(http_input_encoding_conv); + + switch (arg) { + case PARSE_POST: + case PARSE_GET: + case PARSE_COOKIE: + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + switch (arg) { + case PARSE_POST: + if (PG(http_globals)[TRACK_VARS_POST]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_POST]); + } + PG(http_globals)[TRACK_VARS_POST] = array_ptr; + break; + case PARSE_GET: + if (PG(http_globals)[TRACK_VARS_GET]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_GET]); + } + PG(http_globals)[TRACK_VARS_GET] = array_ptr; + break; + case PARSE_COOKIE: + if (PG(http_globals)[TRACK_VARS_COOKIE]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_COOKIE]); + } + PG(http_globals)[TRACK_VARS_COOKIE] = array_ptr; + break; + } + break; + default: + array_ptr = destArray; + break; + } + + if (arg == PARSE_POST) { + sapi_handle_post(array_ptr TSRMLS_CC); + return; + } + + if (arg == PARSE_GET) { /* GET data */ + c_var = SG(request_info).query_string; + if (c_var && *c_var) { + res = (char *) estrdup(c_var); + free_buffer = 1; + } else { + free_buffer = 0; + } + } else if (arg == PARSE_COOKIE) { /* Cookie data */ + c_var = SG(request_info).cookie_data; + if (c_var && *c_var) { + res = (char *) estrdup(c_var); + free_buffer = 1; + } else { + free_buffer = 0; + } + } else if (arg == PARSE_STRING) { /* String data */ + res = str; + free_buffer = 1; + } + + if (!res) { + return; + } + + switch (arg) { + case PARSE_GET: + case PARSE_STRING: + separator = (char *) estrdup(PG(arg_separator).input); + break; + case PARSE_COOKIE: + separator = ";\0"; + break; + } + + if (!input_conv) { + input_conv = ZEND_U_CONVERTER(UG(output_encoding_conv)); + } + + var = php_strtok_r(res, separator, &strtok_buf); + + while (var) { + int var_len; + + val = strchr(var, '='); + + if (arg == PARSE_COOKIE) { + /* Remove leading spaces from cookie names, needed for multi-cookie header where ; can be followed by a space */ + while (isspace(*var)) { + var++; + } + if (var == val || *var == '\0') { + goto next_var; + } + } + + if (val) { + *val++ = '\0'; + } + var_len = strlen(var); + php_url_decode(var, var_len); + + if (UG(unicode)) { + UChar *u_var, *u_val; + int u_var_len, u_val_len; + UErrorCode status = U_ZERO_ERROR; + + zend_string_to_unicode_ex(input_conv, &u_var, &u_var_len, var, var_len, &status); + if (U_FAILURE(status)) { + /* UTODO set a user-accessible flag to indicate that conversion failed? */ + efree(u_var); + goto next_var; + } + + if (val) { /* have a value */ + int val_len; + /* unsigned int new_val_len; see below */ + + val_len = php_url_decode(val, strlen(val)); + zend_string_to_unicode_ex(input_conv, &u_val, &u_val_len, val, val_len, &status); + if (U_FAILURE(status)) { + /* UTODO set a user-accessible flag to indicate that conversion failed? */ + efree(u_var); + efree(u_val); + goto next_var; + } + php_u_register_variable_safe(u_var, u_val, u_val_len, array_ptr TSRMLS_CC); + /* UTODO need to make input_filter Unicode aware */ + /* + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + */ + efree(u_var); + efree(u_val); + } else { + u_val_len = 0; + u_val = USTR_MAKE(""); + php_u_register_variable_safe(u_var, u_val, u_val_len, array_ptr TSRMLS_CC); + /* + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + */ + efree(u_var); + efree(u_val); + } + } else { + if (val) { /* have a value */ + int val_len; + unsigned int new_val_len; + + val_len = php_url_decode(val, strlen(val)); + val = estrndup(val, val_len); + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); + } else { + int val_len; + unsigned int new_val_len; + + val_len = 0; + val = estrndup("", val_len); + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); + } + } +next_var: + var = php_strtok_r(NULL, separator, &strtok_buf); + } + + if (arg != PARSE_COOKIE) { + efree(separator); + } + + if (free_buffer) { + efree(res); + } +} + +void _php_import_environment_variables(zval *array_ptr TSRMLS_DC) +{ + char buf[128]; + char **env, *p, *t = buf; + size_t alloc_size = sizeof(buf); + unsigned long nlen; /* ptrdiff_t is not portable */ + + for (env = environ; env != NULL && *env != NULL; env++) { + p = strchr(*env, '='); + if (!p) { /* malformed entry? */ + continue; + } + nlen = p - *env; + if (nlen >= alloc_size) { + alloc_size = nlen + 64; + t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size)); + } + memcpy(t, *env, nlen); + t[nlen] = '\0'; + php_register_variable(t, p + 1, array_ptr TSRMLS_CC); + } + if (t != buf && t != NULL) { + efree(t); + } +} + +zend_bool php_std_auto_global_callback(char *name, uint name_len TSRMLS_DC) +{ + zend_printf("%s\n", name); + return 0; /* don't rearm */ +} + +/* {{{ php_build_argv + */ +static void php_build_argv(char *s, zval *track_vars_array TSRMLS_DC) +{ + zval *arr, *argc, *tmp; + int count = 0; + char *ss, *space; + + if (!(SG(request_info).argc || track_vars_array)) { + return; + } + + ALLOC_INIT_ZVAL(arr); + array_init(arr); + + /* Prepare argv */ + if (SG(request_info).argc) { /* are we in cli sapi? */ + int i; + for (i = 0; i < SG(request_info).argc; i++) { + ALLOC_ZVAL(tmp); + /* leave args as binary, since the encoding is not known */ + ZVAL_STRING(tmp, SG(request_info).argv[i], 1); + INIT_PZVAL(tmp); + if (zend_hash_next_index_insert(Z_ARRVAL_P(arr), &tmp, sizeof(zval *), NULL) == FAILURE) { + if (Z_TYPE_P(tmp) == IS_STRING) { + efree(Z_STRVAL_P(tmp)); + } + } + } + } else if (s && *s) { + ss = s; + while (ss) { + space = strchr(ss, '+'); + if (space) { + *space = '\0'; + } + /* auto-type */ + ALLOC_ZVAL(tmp); + ZVAL_RT_STRING(tmp, ss, 1); + INIT_PZVAL(tmp); + count++; + if (zend_hash_next_index_insert(Z_ARRVAL_P(arr), &tmp, sizeof(zval *), NULL) == FAILURE) { + efree(Z_STRVAL_P(tmp)); + } + if (space) { + *space = '+'; + ss = space + 1; + } else { + ss = space; + } + } + } + + /* prepare argc */ + ALLOC_INIT_ZVAL(argc); + if (SG(request_info).argc) { + Z_LVAL_P(argc) = SG(request_info).argc; + } else { + Z_LVAL_P(argc) = count; + } + Z_TYPE_P(argc) = IS_LONG; + + if (SG(request_info).argc) { + Z_ADDREF_P(arr); + Z_ADDREF_P(argc); + zend_ascii_hash_update(&EG(symbol_table), "argv", sizeof("argv"), &arr, sizeof(zval *), NULL); + zend_ascii_hash_add(&EG(symbol_table), "argc", sizeof("argc"), &argc, sizeof(zval *), NULL); + } + if (track_vars_array) { + Z_ADDREF_P(arr); + Z_ADDREF_P(argc); + zend_ascii_hash_update(Z_ARRVAL_P(track_vars_array), "argv", sizeof("argv"), &arr, sizeof(zval *), NULL); + zend_ascii_hash_update(Z_ARRVAL_P(track_vars_array), "argc", sizeof("argc"), &argc, sizeof(zval *), NULL); + } + zval_ptr_dtor(&arr); + zval_ptr_dtor(&argc); +} +/* }}} */ + +/* {{{ php_handle_special_queries + */ +PHPAPI int php_handle_special_queries(TSRMLS_D) +{ + if (PG(expose_php) && SG(request_info).query_string && SG(request_info).query_string[0] == '=') { + if (php_info_logos(SG(request_info).query_string + 1 TSRMLS_CC)) { + return 1; + } else if (!strcmp(SG(request_info).query_string + 1, PHP_CREDITS_GUID)) { + php_print_credits(PHP_CREDITS_ALL TSRMLS_CC); + return 1; + } + } + return 0; +} +/* }}} */ + +/* {{{ php_register_server_variables + */ +static inline void php_register_server_variables(TSRMLS_D) +{ + zval *array_ptr = NULL; + + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + if (PG(http_globals)[TRACK_VARS_SERVER]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_SERVER]); + } + PG(http_globals)[TRACK_VARS_SERVER] = array_ptr; + + /* Server variables */ + if (sapi_module.register_server_variables) { + sapi_module.register_server_variables(array_ptr TSRMLS_CC); + } + + /* PHP Authentication support */ + if (SG(request_info).auth_user) { + php_register_variable("PHP_AUTH_USER", SG(request_info).auth_user, array_ptr TSRMLS_CC); + } + if (SG(request_info).auth_password) { + php_register_variable("PHP_AUTH_PW", SG(request_info).auth_password, array_ptr TSRMLS_CC); + } + if (SG(request_info).auth_digest) { + php_register_variable("PHP_AUTH_DIGEST", SG(request_info).auth_digest, array_ptr TSRMLS_CC); + } + /* store request init time */ + { + zval new_entry; + Z_TYPE(new_entry) = IS_LONG; + Z_LVAL(new_entry) = sapi_get_request_time(TSRMLS_C); + php_register_variable_ex("REQUEST_TIME", &new_entry, array_ptr TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ php_autoglobal_merge + */ +static void php_autoglobal_merge(HashTable *dest, HashTable *src TSRMLS_DC) +{ + zval **src_entry, **dest_entry; + zstr string_key; + uint string_key_len; + ulong num_key; + HashPosition pos; + int key_type; + + zend_hash_internal_pointer_reset_ex(src, &pos); + while (zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS) { + key_type = zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos); + if (Z_TYPE_PP(src_entry) != IS_ARRAY + || ((key_type == HASH_KEY_IS_UNICODE || key_type == HASH_KEY_IS_STRING) && zend_u_hash_find(dest, key_type, string_key, string_key_len, (void **) &dest_entry) != SUCCESS) + || (key_type == HASH_KEY_IS_LONG && zend_hash_index_find(dest, num_key, (void **)&dest_entry) != SUCCESS) + || Z_TYPE_PP(dest_entry) != IS_ARRAY + ) { + Z_ADDREF_PP(src_entry); + if (key_type == HASH_KEY_IS_STRING || key_type == HASH_KEY_IS_UNICODE) { + zend_u_hash_update(dest, key_type, string_key, string_key_len, src_entry, sizeof(zval *), NULL); + } else { + zend_hash_index_update(dest, num_key, src_entry, sizeof(zval *), NULL); + } + } else { + SEPARATE_ZVAL(dest_entry); + php_autoglobal_merge(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry) TSRMLS_CC); + } + zend_hash_move_forward_ex(src, &pos); + } +} +/* }}} */ + +static zend_bool php_auto_globals_create_server(char *name, uint name_len TSRMLS_DC); +static zend_bool php_auto_globals_create_env(char *name, uint name_len TSRMLS_DC); +static zend_bool php_auto_globals_create_request(char *name, uint name_len TSRMLS_DC); + +/* {{{ php_hash_environment + */ +int php_hash_environment(TSRMLS_D) +{ + char *p; + unsigned char _gpc_flags[5] = {0, 0, 0, 0, 0}; + zend_bool jit_initialization = PG(auto_globals_jit); + struct auto_global_record { + char *name; + uint name_len; + zend_bool jit_initialization; + } auto_global_records[] = { + { "_POST", sizeof("_POST"), 0 }, + { "_GET", sizeof("_GET"), 0 }, + { "_COOKIE", sizeof("_COOKIE"), 0 }, + { "_SERVER", sizeof("_SERVER"), 1 }, + { "_ENV", sizeof("_ENV"), 1 }, + { "_FILES", sizeof("_FILES"), 0 }, + }; + size_t num_track_vars = sizeof(auto_global_records)/sizeof(struct auto_global_record); + size_t i; + + /* jit_initialization = 0; */ + for (i=0; i | + | Rob Richards | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#if HAVE_LIBXML && HAVE_DOM +#include "php_dom.h" +#include +#ifdef LIBXML_SCHEMAS_ENABLED +#include +#include +#endif + +typedef struct _idsIterator idsIterator; +struct _idsIterator { + xmlChar *elementId; + xmlNode *element; +}; + +#define DOM_LOAD_STRING 0 +#define DOM_LOAD_FILE 1 + +static void idsHashScanner(void *payload, void *data, xmlChar *name) +{ + idsIterator *priv = (idsIterator *) data; + + if (priv->element == NULL && xmlStrEqual (name, priv->elementId)) { + priv->element = ((xmlNode *)((xmlID *)payload)->attr)->parent; + } +} + +/* +* class domdocument extends domnode +* +* URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-i-Document +* Since: +*/ + +zend_function_entry php_dom_document_class_functions[] = { + PHP_FALIAS(createElement, dom_document_create_element, NULL) + PHP_FALIAS(createDocumentFragment, dom_document_create_document_fragment, NULL) + PHP_FALIAS(createTextNode, dom_document_create_text_node, NULL) + PHP_FALIAS(createComment, dom_document_create_comment, NULL) + PHP_FALIAS(createCDATASection, dom_document_create_cdatasection, NULL) + PHP_FALIAS(createProcessingInstruction, dom_document_create_processing_instruction, NULL) + PHP_FALIAS(createAttribute, dom_document_create_attribute, NULL) + PHP_FALIAS(createEntityReference, dom_document_create_entity_reference, NULL) + PHP_FALIAS(getElementsByTagName, dom_document_get_elements_by_tag_name, NULL) + PHP_FALIAS(importNode, dom_document_import_node, NULL) + PHP_FALIAS(createElementNS, dom_document_create_element_ns, NULL) + PHP_FALIAS(createAttributeNS, dom_document_create_attribute_ns, NULL) + PHP_FALIAS(getElementsByTagNameNS, dom_document_get_elements_by_tag_name_ns, NULL) + PHP_FALIAS(getElementById, dom_document_get_element_by_id, NULL) + PHP_FALIAS(adoptNode, dom_document_adopt_node, NULL) + PHP_FALIAS(normalizeDocument, dom_document_normalize_document, NULL) + PHP_FALIAS(renameNode, dom_document_rename_node, NULL) + PHP_FALIAS(load, dom_document_load, NULL) + PHP_FALIAS(save, dom_document_save, NULL) + PHP_FALIAS(loadXML, dom_document_loadxml, NULL) + PHP_FALIAS(saveXML, dom_document_savexml, NULL) + PHP_FALIAS(domdocument, dom_document_document, NULL) + PHP_FALIAS(validate, dom_document_validate, NULL) + PHP_FALIAS(xinclude, dom_document_xinclude, NULL) +#if defined(LIBXML_HTML_ENABLED) + PHP_FALIAS(loadHTML, dom_document_load_html, NULL) + PHP_FALIAS(loadHTMLFile, dom_document_load_html_file, NULL) + PHP_FALIAS(saveHTML, dom_document_save_html, NULL) + PHP_FALIAS(saveHTMLFile, dom_document_save_html_file, NULL) +#endif /* defined(LIBXML_HTML_ENABLED) */ +#if defined(LIBXML_SCHEMAS_ENABLED) + PHP_FALIAS(schemaValidate, dom_document_schema_validate_file, NULL) + PHP_FALIAS(schemaValidateSource, dom_document_schema_validate_xml, NULL) + PHP_FALIAS(relaxNGValidate, dom_document_relaxNG_validate_file, NULL) + PHP_FALIAS(relaxNGValidateSource, dom_document_relaxNG_validate_xml, NULL) +#endif + {NULL, NULL, NULL} +}; + +/* {{{ proto doctype documenttype +readonly=yes +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-B63ED1A31 +Since: +*/ +int dom_document_doctype_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + xmlDtdPtr dtdptr; + int ret; + + docp = (xmlDocPtr) dom_object_get_node(obj); + + dtdptr = xmlGetIntSubset(docp); + if (!dtdptr) { + return FAILURE; + } + + ALLOC_ZVAL(*retval); + if (NULL == (*retval = php_dom_create_object((xmlNodePtr) dtdptr, &ret, NULL, *retval, obj TSRMLS_CC))) { + php_error(E_WARNING, "Cannot create required DOM object"); + return FAILURE; + } + return SUCCESS; + +} + +/* }}} */ + + + +/* {{{ proto implementation domimplementation +readonly=yes +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1B793EBA +Since: +*/ +int dom_document_implementation_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + ALLOC_ZVAL(*retval); + php_dom_create_implementation(retval TSRMLS_CC); + return SUCCESS; +} + +/* }}} */ + + + +/* {{{ proto domelement document_element documentElement +readonly=yes +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-87CD092 +Since: +*/ +int dom_document_document_element_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + xmlNode *root; + int ret; + + docp = (xmlDocPtr) dom_object_get_node(obj); + + root = xmlDocGetRootElement(docp); + if (!root) { + return FAILURE; + } + + ALLOC_ZVAL(*retval); + if (NULL == (*retval = php_dom_create_object(root, &ret, NULL, *retval, obj TSRMLS_CC))) { + php_error(E_WARNING, "Cannot create required DOM object"); + return FAILURE; + } + return SUCCESS; +} + +/* }}} */ + + +/* {{{ proto actual_encoding string +readonly=no +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-actualEncoding +Since: DOM Level 3 +*/ +/* READ ONLY FOR NOW USING ENCODING PROPERTY +int dom_document_actual_encoding_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + ALLOC_ZVAL(*retval); + ZVAL_NULL(*retval); + return SUCCESS; +} + +int dom_document_actual_encoding_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + return SUCCESS; +} +*/ + +/* }}} */ + +/* {{{ proto encoding string +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-encoding +Since: DOM Level 3 +*/ +int dom_document_encoding_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + char *encoding; + + docp = (xmlDocPtr) dom_object_get_node(obj); + encoding = (char *) docp->encoding; + ALLOC_ZVAL(*retval); + + if (encoding != NULL) { + ZVAL_STRING(*retval, encoding, 1); + } else { + ZVAL_NULL(*retval); + } + + return SUCCESS; +} + +int dom_document_encoding_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + xmlDoc *docp; + xmlCharEncodingHandlerPtr handler; + + docp = (xmlDocPtr) dom_object_get_node(obj); + + handler = xmlFindCharEncodingHandler(Z_STRVAL_P(newval)); + + if (handler != NULL) { + xmlCharEncCloseFunc(handler); + if (docp->encoding != NULL) { + xmlFree((xmlChar *)docp->encoding); + } + docp->encoding = xmlStrdup((const xmlChar *) Z_STRVAL_P(newval)); + return SUCCESS; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Document Encoding"); + } + + return SUCCESS; +} + +/* }}} */ + + + +/* {{{ proto standalone boolean +readonly=no +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-standalone +Since: DOM Level 3 +*/ +int dom_document_standalone_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + int standalone; + + docp = (xmlDocPtr) dom_object_get_node(obj); + ALLOC_ZVAL(*retval); + standalone = docp->standalone; + ZVAL_BOOL(*retval, standalone); + + return SUCCESS; +} + +int dom_document_standalone_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + xmlDoc *docp; + int standalone; + + docp = (xmlDocPtr) dom_object_get_node(obj); + standalone = Z_LVAL_P(newval); + if (standalone > 0) { + docp->standalone = 1; + } + else if (standalone < 0) { + docp->standalone = -1; + } + else { + docp->standalone = 0; + } + + return SUCCESS; +} + +/* }}} */ + + + +/* {{{ proto version string +readonly=no +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-version +Since: DOM Level 3 +*/ +int dom_document_version_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + char *version; + + docp = (xmlDocPtr) dom_object_get_node(obj); + version = (char *) docp->version; + ALLOC_ZVAL(*retval); + + if (version != NULL) { + ZVAL_STRING(*retval, version, 1); + } else { + ZVAL_NULL(*retval); + } + + return SUCCESS; +} + +int dom_document_version_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + xmlDoc *docp; + + docp = (xmlDocPtr) dom_object_get_node(obj); + if (docp->version != NULL) { + xmlFree((xmlChar *) docp->version ); + } + + docp->version = xmlStrdup((const xmlChar *) Z_STRVAL_P(newval)); + + return SUCCESS; +} + +/* }}} */ + +/* {{{ proto strict_error_checking boolean +readonly=no +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-strictErrorChecking +Since: DOM Level 3 +*/ +int dom_document_strict_error_checking_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->stricterror); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_strict_error_checking_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->stricterror = Z_LVAL_P(newval); + } + + return SUCCESS; +} + +/* }}} */ + +/* {{{ proto formatOutput boolean +readonly=no +*/ +int dom_document_format_output_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->formatoutput); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_format_output_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->formatoutput = Z_LVAL_P(newval); + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ proto validateonParse boolean +readonly=no +*/ +int dom_document_validate_on_parse_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->validateonparse); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_validate_on_parse_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->validateonparse = Z_LVAL_P(newval); + } + + return SUCCESS; +} +/* }}} */ + + +/* {{{ proto resolveExternals boolean +readonly=no +*/ +int dom_document_resolve_externals_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->resolveexternals); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_resolve_externals_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->resolveexternals = Z_LVAL_P(newval); + } + + return SUCCESS; +} +/* }}} */ + + +/* {{{ proto preserveWhiteSpace boolean +readonly=no +*/ +int dom_document_preserve_whitespace_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->preservewhitespace); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_preserve_whitespace_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->preservewhitespace = Z_LVAL_P(newval); + } + + return SUCCESS; +} +/* }}} */ + + +/* {{{ proto substituteEntities boolean +readonly=no +*/ +int dom_document_substitue_entities_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + ALLOC_ZVAL(*retval); + if (obj->document) { + doc_prop = dom_get_doc_props(obj->document); + ZVAL_BOOL(*retval, doc_prop->substituteentities); + } else { + ZVAL_FALSE(*retval); + } + return SUCCESS; +} + +int dom_document_substitue_entities_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + dom_doc_props *doc_prop; + + if (obj->document && newval->type == IS_BOOL) { + doc_prop = dom_get_doc_props(obj->document); + doc_prop->substituteentities = Z_LVAL_P(newval); + } + + return SUCCESS; +} +/* }}} */ + + +/* {{{ proto document_uri string +readonly=no +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-documentURI +Since: DOM Level 3 +*/ +int dom_document_document_uri_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp; + char *url; + + docp = (xmlDocPtr) dom_object_get_node(obj); + + ALLOC_ZVAL(*retval); + url = (char *) docp->URL; + if (url != NULL) { + ZVAL_STRING(*retval, url, 1); + } else { + ZVAL_NULL(*retval); + } + + return SUCCESS; +} + +int dom_document_document_uri_write(dom_object *obj, zval *newval TSRMLS_DC) +{ + xmlDoc *docp; + + docp = (xmlDocPtr) dom_object_get_node(obj); + if (docp->URL != NULL) { + xmlFree((xmlChar *) docp->URL); + } + + docp->URL = xmlStrdup((const xmlChar *) Z_STRVAL_P(newval)); + + return SUCCESS; +} + +/* }}} */ + + + +/* {{{ proto config domconfiguration +readonly=yes +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-config +Since: DOM Level 3 +*/ +int dom_document_config_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + ALLOC_ZVAL(*retval); + ZVAL_NULL(*retval); + return SUCCESS; +} + +/* }}} */ + + + +/* {{{ proto domelement dom_document_create_element(string tagName); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-2141741547 +Since: +*/ +PHP_FUNCTION(dom_document_create_element) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + dom_object *intern; + int ret, name_len, value_len; + char *name, *value = NULL; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &name, &name_len, &value, &value_len) == FAILURE) { + return; + } + + node = xmlNewDocNode(docp, NULL, name, value); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_element */ + + +/* {{{ proto domdocumentfragment dom_document_create_document_fragment(); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-35CB04B5 +Since: +*/ +PHP_FUNCTION(dom_document_create_document_fragment) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + dom_object *intern; + int ret; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + node = xmlNewDocFragment(docp); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_document_fragment */ + + +/* {{{ proto domtext dom_document_create_text_node(string data); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1975348127 +Since: +*/ +PHP_FUNCTION(dom_document_create_text_node) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + int ret, value_len; + dom_object *intern; + char *value; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) == FAILURE) { + return; + } + + node = xmlNewDocText(docp, (xmlChar *) value); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_text_node */ + + +/* {{{ proto domcomment dom_document_create_comment(string data); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1334481328 +Since: +*/ +PHP_FUNCTION(dom_document_create_comment) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + int ret, value_len; + dom_object *intern; + char *value; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) == FAILURE) { + return; + } + + node = xmlNewDocComment(docp, (xmlChar *) value); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_comment */ + + +/* {{{ proto domcdatasection dom_document_create_cdatasection(string data); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D26C0AF8 +Since: +*/ +PHP_FUNCTION(dom_document_create_cdatasection) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + int ret, value_len; + dom_object *intern; + char *value; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) == FAILURE) { + return; + } + + node = xmlNewCDataBlock(docp, (xmlChar *) value, value_len); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_cdatasection */ + + +/* {{{ proto domprocessinginstruction dom_document_create_processing_instruction(string target, string data); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-135944439 +Since: +*/ +PHP_FUNCTION(dom_document_create_processing_instruction) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp; + int ret, value_len, name_len; + dom_object *intern; + char *name, *value = NULL; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &name, &name_len, &value, &value_len) == FAILURE) { + return; + } + + node = xmlNewPI((xmlChar *) name, (xmlChar *) value); + if (!node) { + RETURN_FALSE; + } + + node->doc = docp; + + DOM_RET_OBJ(rv, node, &ret, intern); +} +/* }}} end dom_document_create_processing_instruction */ + + +/* {{{ proto domattr dom_document_create_attribute(string name); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1084891198 +Since: +*/ +PHP_FUNCTION(dom_document_create_attribute) +{ + zval *id, *rv = NULL; + xmlAttrPtr node; + xmlDocPtr docp; + int ret, name_len; + dom_object *intern; + char *name; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { + return; + } + + if (name_len == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Attribute name is required"); + RETURN_FALSE; + } + + node = xmlNewDocProp(docp, (xmlChar *) name, NULL); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, (xmlNodePtr) node, &ret, intern); + +} +/* }}} end dom_document_create_attribute */ + + +/* {{{ proto domentityreference dom_document_create_entity_reference(string name); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-392B75AE +Since: +*/ +PHP_FUNCTION(dom_document_create_entity_reference) +{ + zval *id, *rv = NULL; + xmlNode *node; + xmlDocPtr docp = NULL; + dom_object *intern; + int ret, name_len; + char *name; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { + return; + } + + node = xmlNewReference(docp, name); + if (!node) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, (xmlNodePtr) node, &ret, intern); +} +/* }}} end dom_document_create_entity_reference */ + + +/* {{{ proto domnodelist dom_document_get_elements_by_tag_name(string tagname); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C9094 +Since: +*/ +PHP_FUNCTION(dom_document_get_elements_by_tag_name) +{ + zval *id; + xmlDocPtr docp; + int name_len; + dom_object *intern, *namednode; + char *name; + xmlChar *local; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { + return; + } + + php_dom_create_interator(return_value, DOM_NODELIST TSRMLS_CC); + namednode = (dom_object *)zend_objects_get_address(return_value TSRMLS_CC); + local = xmlCharStrndup(name, name_len); + dom_namednode_iter(intern, 0, namednode, NULL, local, NULL); +} +/* }}} end dom_document_get_elements_by_tag_name */ + + +/* {{{ proto domnode dom_document_import_node(node importedNode, boolean deep); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Core-Document-importNode +Since: DOM Level 2 +*/ +PHP_FUNCTION(dom_document_import_node) +{ + zval *rv = NULL; + zval *id, *node; + xmlDocPtr docp; + xmlNodePtr nodep, retnodep; + dom_object *intern, *nodeobj; + int ret; + long recursive = 0; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|l", &node, &recursive) == FAILURE) { + return; + } + + DOM_GET_OBJ(nodep, node, xmlNodePtr, nodeobj); + + if (nodep->type == XML_HTML_DOCUMENT_NODE || nodep->type == XML_DOCUMENT_NODE + || nodep->type == XML_DOCUMENT_TYPE_NODE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot import: Node Type Not Supported"); + RETURN_FALSE; + } + + if (nodep->doc == docp) { + retnodep = nodep; + } else { + retnodep = xmlDocCopyNode(nodep, docp, recursive); + if (!retnodep) { + RETURN_FALSE; + } + } + + DOM_RET_OBJ(rv, (xmlNodePtr) retnodep, &ret, intern); +} +/* }}} end dom_document_import_node */ + + +/* {{{ proto domelement dom_document_create_element_ns(string namespaceURI, string qualifiedName); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNS +Since: DOM Level 2 +*/ +PHP_FUNCTION(dom_document_create_element_ns) +{ + zval *id, *rv = NULL; + xmlDocPtr docp; + xmlNodePtr nodep = NULL; + xmlNsPtr nsptr = NULL; + int ret, uri_len = 0, name_len = 0; + char *uri, *name; + char *localname = NULL, *prefix = NULL; + int errorcode; + dom_object *intern; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) { + return; + } + + errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len); + + if (errorcode == 0) { + nodep = xmlNewDocNode (docp, NULL, localname, NULL); + if (nodep != NULL && uri != NULL) { + nsptr = xmlSearchNsByHref (nodep->doc, nodep, uri); + if (nsptr == NULL) { + nsptr = dom_get_ns(nodep, uri, &errorcode, prefix); + } + xmlSetNs(nodep, nsptr); + } + } + + xmlFree(localname); + if (prefix != NULL) { + xmlFree(prefix); + } + + if (errorcode != 0) { + if (nodep != NULL) { + xmlFreeNode(nodep); + } + php_dom_throw_error(errorcode, dom_get_strict_error(intern->document) TSRMLS_CC); + RETURN_FALSE; + } + + if (nodep == NULL) { + RETURN_FALSE; + } + + + nodep->ns = nsptr; + + DOM_RET_OBJ(rv, nodep, &ret, intern); +} +/* }}} end dom_document_create_element_ns */ + + +/* {{{ proto domattr dom_document_create_attribute_ns(string namespaceURI, string qualifiedName); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrAttrNS +Since: DOM Level 2 +*/ +PHP_FUNCTION(dom_document_create_attribute_ns) +{ + zval *id, *rv = NULL; + xmlDocPtr docp; + xmlNodePtr nodep = NULL, root; + xmlNsPtr nsptr; + int ret, uri_len = 0, name_len = 0; + char *uri, *name; + char *localname = NULL, *prefix = NULL; + dom_object *intern; + int errorcode; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) { + return; + } + + root = xmlDocGetRootElement(docp); + if (root != NULL) { + errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len); + if (errorcode == 0) { + nodep = (xmlNodePtr) xmlNewDocProp(docp, localname, NULL); + if (nodep != NULL && uri_len > 0) { + nsptr = xmlSearchNsByHref (nodep->doc, root, uri); + if (nsptr == NULL) { + nsptr = dom_get_ns(root, uri, &errorcode, prefix); + } + xmlSetNs(nodep, nsptr); + } + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Document Missing Root Element"); + RETURN_FALSE; + } + + xmlFree(localname); + if (prefix != NULL) { + xmlFree(prefix); + } + + if (errorcode != 0) { + if (nodep != NULL) { + xmlFreeProp((xmlAttrPtr) nodep); + } + php_dom_throw_error(errorcode, dom_get_strict_error(intern->document) TSRMLS_CC); + RETURN_FALSE; + } + + if (nodep == NULL) { + RETURN_FALSE; + } + + DOM_RET_OBJ(rv, nodep, &ret, intern); +} +/* }}} end dom_document_create_attribute_ns */ + + +/* {{{ proto domnodelist dom_document_get_elements_by_tag_name_ns(string namespaceURI, string localName); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBTNNS +Since: DOM Level 2 +*/ +PHP_FUNCTION(dom_document_get_elements_by_tag_name_ns) +{ + zval *id; + xmlDocPtr docp; + int uri_len, name_len; + dom_object *intern, *namednode; + char *uri, *name; + xmlChar *local, *nsuri; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &uri, &uri_len, &name, &name_len) == FAILURE) { + return; + } + + php_dom_create_interator(return_value, DOM_NODELIST TSRMLS_CC); + namednode = (dom_object *)zend_objects_get_address(return_value TSRMLS_CC); + local = xmlCharStrndup(name, name_len); + nsuri = xmlCharStrndup(uri, uri_len); + dom_namednode_iter(intern, 0, namednode, NULL, local, nsuri); +} +/* }}} end dom_document_get_elements_by_tag_name_ns */ + + +/* {{{ proto domelement dom_document_get_element_by_id(string elementId); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBId +Since: DOM Level 2 +*/ +PHP_FUNCTION(dom_document_get_element_by_id) +{ + zval *id, *rv = NULL; + xmlDocPtr docp; + idsIterator iter; + xmlHashTable *ids = NULL; + int ret,idname_len; + dom_object *intern; + char *idname; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &idname, &idname_len) == FAILURE) { + return; + } + + ids = (xmlHashTable *) docp->ids; + if (ids) { + iter.elementId = (xmlChar *) idname; + iter.element = NULL; + xmlHashScan(ids, (void *)idsHashScanner, &iter); + DOM_RET_OBJ(rv, (xmlNodePtr) iter.element, &ret, intern); + } else { + RETVAL_NULL(); + } +} +/* }}} end dom_document_get_element_by_id */ + + +/* {{{ proto domnode dom_document_adopt_node(node source); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-adoptNode +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_adopt_node) +{ + DOM_NOT_IMPLEMENTED(); +} +/* }}} end dom_document_adopt_node */ + + +/* {{{ proto dom_void dom_document_normalize_document(); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-normalizeDocument +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_normalize_document) +{ + zval *id; + xmlDocPtr docp; + dom_object *intern; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + DOM_NO_ARGS(); + + dom_normalize((xmlNodePtr) docp TSRMLS_CC); +} +/* }}} end dom_document_normalize_document */ + + +/* {{{ proto domnode dom_document_rename_node(node n, string namespaceURI, string qualifiedName); +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-renameNode +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_rename_node) +{ + DOM_NOT_IMPLEMENTED(); +} +/* }}} end dom_document_rename_node */ + +/* {{{ proto domnode dom_document_document([string version], [string encoding]); */ +PHP_FUNCTION(dom_document_document) +{ + + zval *id; + xmlDoc *docp = NULL, *olddoc; + dom_object *intern; + char *encoding, *version = NULL; + int encoding_len = 0, version_len = 0, refcount; + + id = getThis(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ss", &version, &version_len, &encoding, &encoding_len) == FAILURE) { + return; + } + + docp = xmlNewDoc(version); + if (!docp) + RETURN_FALSE; + + if (encoding_len > 0) { + docp->encoding = (const xmlChar*)xmlStrdup(encoding); + } + + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + if (intern != NULL) { + olddoc = (xmlDocPtr) dom_object_get_node(intern); + if (olddoc != NULL) { + php_libxml_decrement_node_ptr((php_libxml_node_object *) intern TSRMLS_CC); + refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern TSRMLS_CC); + if (refcount != 0) { + olddoc->_private = NULL; + } + } + intern->document = NULL; + php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp TSRMLS_CC); + php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)docp, (void *)intern TSRMLS_CC); + } +} +/* }}} end dom_document_document */ + +char *_dom_get_valid_file_path(char *source, char *resolved_path, int resolved_path_len TSRMLS_DC) { + xmlURI *uri; + xmlChar *escsource; + char *file_dest; + int isFileUri = 0; + + uri = xmlCreateURI(); + escsource = xmlURIEscapeStr(source, ":"); + xmlParseURIReference(uri, escsource); + xmlFree(escsource); + + if (uri->scheme != NULL) { + /* absolute file uris - libxml only supports localhost or empty host */ + if (strncasecmp(source, "file:///",8) == 0) { + isFileUri = 1; +#ifdef PHP_WIN32 + source += 8; +#else + source += 7; +#endif + } else if (strncasecmp(source, "file://localhost/",17) == 0) { + isFileUri = 1; +#ifdef PHP_WIN32 + source += 17; +#else + source += 16; +#endif + } + } + + file_dest = source; + + if ((uri->scheme == NULL || isFileUri)) { + /* XXX possible buffer overflow if VCWD_REALPATH does not know size of resolved_path */ + if (! VCWD_REALPATH(source, resolved_path)) { + expand_filepath(source, resolved_path TSRMLS_CC); + } + file_dest = resolved_path; + } + + xmlFreeURI(uri); + + return file_dest; +} + + +/* {{{ */ +static xmlDocPtr dom_document_parser(zval *id, int mode, char *source TSRMLS_DC) { + xmlDocPtr ret; + xmlParserCtxtPtr ctxt = NULL; + dom_doc_props *doc_props; + dom_object *intern; + php_libxml_ref_obj *document = NULL; + int validate, resolve_externals, keep_blanks, substitute_ent; + int resolved_path_len; + char *directory=NULL, resolved_path[MAXPATHLEN]; + + if (id != NULL) { + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + document = intern->document; + } + + doc_props = dom_get_doc_props(document); + validate = doc_props->validateonparse; + resolve_externals = doc_props->resolveexternals; + keep_blanks = doc_props->preservewhitespace; + substitute_ent = doc_props->substituteentities; + + if (document == NULL) { + efree(doc_props); + } + + xmlInitParser(); + + keep_blanks = xmlKeepBlanksDefault(keep_blanks); + + if (mode == DOM_LOAD_FILE) { + char *file_dest = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC); + if (file_dest) { + ctxt = xmlCreateFileParserCtxt(file_dest); + } + + } else { + ctxt = xmlCreateDocParserCtxt(source); + } + + xmlKeepBlanksDefault(keep_blanks); + /* xmlIndentTreeOutput default is changed in xmlKeepBlanksDefault + reset back to 1 which is default value */ + + xmlIndentTreeOutput = 1; + + if (ctxt == NULL) { + return(NULL); + } + + /* If loading from memory, we need to set the base directory for the document */ + if (mode != DOM_LOAD_FILE) { +#if HAVE_GETCWD + directory = VCWD_GETCWD(resolved_path, MAXPATHLEN); +#elif HAVE_GETWD + directory = VCWD_GETWD(resolved_path); +#endif + if (directory) { + if(ctxt->directory != NULL) { + xmlFree((char *) ctxt->directory); + } + resolved_path_len = strlen(resolved_path); + if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) { + resolved_path[resolved_path_len] = DEFAULT_SLASH; + resolved_path[++resolved_path_len] = '\0'; + } + ctxt->directory = (char *) xmlCanonicPath((const xmlChar *) resolved_path); + } + } + + ctxt->recovery = 0; + ctxt->validate = validate; + ctxt->loadsubset = (resolve_externals * XML_COMPLETE_ATTRS); + ctxt->replaceEntities = substitute_ent; + + ctxt->vctxt.error = php_libxml_ctx_error; + ctxt->vctxt.warning = php_libxml_ctx_warning; + + if (ctxt->sax != NULL) { + ctxt->sax->error = php_libxml_ctx_error; + ctxt->sax->warning = php_libxml_ctx_warning; + } + + xmlParseDocument(ctxt); + + if (ctxt->wellFormed) { + ret = ctxt->myDoc; + /* If loading from memory, set the base reference uri for the document */ + if (ret->URL == NULL && ctxt->directory != NULL) { + ret->URL = xmlStrdup(ctxt->directory); + } + } else { + ret = NULL; + xmlFreeDoc(ctxt->myDoc); + ctxt->myDoc = NULL; + } + + xmlFreeParserCtxt(ctxt); + + return(ret); +} +/* }}} */ + +/* {{{ static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) */ +static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) { + zval *id, *rv = NULL; + xmlDoc *docp = NULL, *newdoc; + dom_doc_props *doc_prop; + dom_object *intern; + char *source; + int source_len, refcount, ret; + + id = getThis(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &source, &source_len) == FAILURE) { + return; + } + + newdoc = dom_document_parser(id, mode, source TSRMLS_CC); + + if (!newdoc) + RETURN_FALSE; + + if (id != NULL) { + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + if (intern != NULL) { + docp = (xmlDocPtr) dom_object_get_node(intern); + doc_prop = NULL; + if (docp != NULL) { + php_libxml_decrement_node_ptr((php_libxml_node_object *) intern TSRMLS_CC); + doc_prop = intern->document->doc_props; + intern->document->doc_props = NULL; + refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern TSRMLS_CC); + if (refcount != 0) { + docp->_private = NULL; + } + } + intern->document = NULL; + php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc TSRMLS_CC); + intern->document->doc_props = doc_prop; + } + + php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern TSRMLS_CC); + + RETURN_TRUE; + } else { + DOM_RET_OBJ(rv, (xmlNodePtr) newdoc, &ret, NULL); + } +} +/* }}} end dom_parser_document */ + +/* {{{ proto boolean domnode dom_document_load(string source); +URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-load +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_load) +{ + dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); +} +/* }}} end dom_document_load */ + +/* {{{ proto boolean domnode dom_document_loadxml(string source); +URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-loadXML +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_loadxml) +{ + dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); +} +/* }}} end dom_document_loadxml */ + +/* {{{ proto long domnode dom_document_save(string file); +Convenience method to save to file +*/ +PHP_FUNCTION(dom_document_save) +{ + zval *id; + xmlDoc *docp; + int file_len, bytes, format; + dom_object *intern; + dom_doc_props *doc_props; + char *file; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) { + return; + } + + /* encoding handled by property on doc */ + + doc_props = dom_get_doc_props(intern->document); + format = doc_props->formatoutput; + bytes = xmlSaveFormatFileEnc(file, docp, NULL, format); + + if (bytes == -1) { + RETURN_FALSE; + } + RETURN_LONG(bytes); +} +/* }}} end dom_document_save */ + +/* {{{ proto string domnode dom_document_savexml([node n]); +URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML +Since: DOM Level 3 +*/ +PHP_FUNCTION(dom_document_savexml) +{ + zval *id, *nodep = NULL; + xmlDoc *docp; + xmlNode *node; + xmlBufferPtr buf; + xmlChar *mem; + dom_object *intern, *nodeobj; + dom_doc_props *doc_props; + int size, format; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|o", &nodep) == FAILURE) { + return; + } + + doc_props = dom_get_doc_props(intern->document); + format = doc_props->formatoutput; + + if (nodep != NULL) { + /* Dump contents of Node */ + DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj); + if (node->doc != docp) { + php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document) TSRMLS_CC); + RETURN_FALSE; + } + buf = xmlBufferCreate(); + if (!buf) { + php_error(E_WARNING, "Could not fetch buffer"); + RETURN_FALSE; + } + + xmlNodeDump(buf, docp, node, 0, format); + mem = (xmlChar*) xmlBufferContent(buf); + if (!mem) { + xmlBufferFree(buf); + RETURN_FALSE; + } + RETVAL_STRING(mem, 1); + xmlBufferFree(buf); + } else { + /* Encoding is handled from the encoding property set on the document */ + xmlDocDumpFormatMemory(docp, &mem, &size, format); + if (!size) { + RETURN_FALSE; + } + RETVAL_STRINGL(mem, size, 1); + xmlFree(mem); + } +} +/* }}} end dom_document_savexml */ + +/* {{{ proto int dom_document_xinclude() + Substitutues xincludes in a DomDocument */ +PHP_FUNCTION(dom_document_xinclude) +{ + zval *id; + xmlDoc *docp; + int err; + dom_object *intern; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + err = xmlXIncludeProcess (docp); + + if (err) { + RETVAL_LONG(err); + } else { + RETVAL_FALSE; + } + +} + + +/* {{{ proto string domnode dom_document_validate(); +Since: DOM extended +*/ +PHP_FUNCTION(dom_document_validate) +{ + zval *id; + xmlDoc *docp; + dom_object *intern; + xmlValidCtxt cvp; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (docp->intSubset == NULL) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "No DTD given in XML-Document"); + } + + cvp.userData = NULL; + cvp.error = (xmlValidityErrorFunc) php_libxml_error_handler; + cvp.warning = (xmlValidityErrorFunc) php_libxml_error_handler; + + if (xmlValidateDocument(&cvp, docp)) { + RETVAL_TRUE; + } else { + RETVAL_FALSE; + } +} +/* }}} end dom_document_validate */ + +#if defined(LIBXML_SCHEMAS_ENABLED) +static void +_dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type) +{ + zval *id; + xmlDoc *docp; + dom_object *intern; + char *source = NULL, *valid_file = NULL; + int source_len = 0; + xmlSchemaParserCtxtPtr parser; + xmlSchemaPtr sptr; + xmlSchemaValidCtxtPtr vptr; + int is_valid; + char resolved_path[MAXPATHLEN + 1]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &source, &source_len) == FAILURE) { + return; + } + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + switch (type) { + case DOM_LOAD_FILE: + valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC); + if (!valid_file) { + php_error(E_WARNING, "Invalid Schema file source"); + RETURN_FALSE; + } + parser = xmlSchemaNewParserCtxt(valid_file); + break; + case DOM_LOAD_STRING: + parser = xmlSchemaNewMemParserCtxt(source, source_len); + /* If loading from memory, we need to set the base directory for the document + but it is not apparent how to do that for schema's */ + break; + default: + return; + } + + xmlSchemaSetParserErrors(parser, + (xmlSchemaValidityErrorFunc) php_libxml_error_handler, + (xmlSchemaValidityWarningFunc) php_libxml_error_handler, + parser); + sptr = xmlSchemaParse(parser); + xmlSchemaFreeParserCtxt(parser); + if (!sptr) { + php_error(E_WARNING, "Invalid Schema"); + RETURN_FALSE; + } + + docp = (xmlDocPtr) dom_object_get_node(intern); + + vptr = xmlSchemaNewValidCtxt(sptr); + if (!vptr) { + xmlSchemaFree(sptr); + php_error(E_ERROR, "Invalid Schema Validation Context"); + RETURN_FALSE; + } + + xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr); + is_valid = xmlSchemaValidateDoc(vptr, docp); + xmlSchemaFree(sptr); + xmlSchemaFreeValidCtxt(vptr); + + if (is_valid == 0) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} + +/* {{{ proto boolean domnode _dom_document_schema_validate(string filename); */ +PHP_FUNCTION(dom_document_schema_validate_file) +{ + _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); +} +/* }}} end _dom_document_schema_validate */ + +/* {{{ proto boolean domnode _dom_document_schema_validate(string source); */ +PHP_FUNCTION(dom_document_schema_validate_xml) +{ + _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); +} +/* }}} end _dom_document_schema_validate */ + + +static void +_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int type) +{ + zval *id; + xmlDoc *docp; + dom_object *intern; + char *source = NULL, *valid_file = NULL; + int source_len = 0; + xmlRelaxNGParserCtxtPtr parser; + xmlRelaxNGPtr sptr; + xmlRelaxNGValidCtxtPtr vptr; + int is_valid; + char resolved_path[MAXPATHLEN + 1]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &source, &source_len) == FAILURE) { + return; + } + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + switch (type) { + case DOM_LOAD_FILE: + valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC); + if (!valid_file) { + php_error(E_WARNING, "Invalid RelaxNG file source"); + RETURN_FALSE; + } + parser = xmlRelaxNGNewParserCtxt(valid_file); + break; + case DOM_LOAD_STRING: + parser = xmlRelaxNGNewMemParserCtxt(source, source_len); + /* If loading from memory, we need to set the base directory for the document + but it is not apparent how to do that for schema's */ + break; + default: + return; + } + + xmlRelaxNGSetParserErrors(parser, + (xmlRelaxNGValidityErrorFunc) php_libxml_error_handler, + (xmlRelaxNGValidityWarningFunc) php_libxml_error_handler, + parser); + sptr = xmlRelaxNGParse(parser); + xmlRelaxNGFreeParserCtxt(parser); + if (!sptr) { + php_error(E_WARNING, "Invalid RelaxNG"); + RETURN_FALSE; + } + + docp = (xmlDocPtr) dom_object_get_node(intern); + + vptr = xmlRelaxNGNewValidCtxt(sptr); + if (!vptr) { + xmlRelaxNGFree(sptr); + php_error(E_ERROR, "Invalid RelaxNG Validation Context"); + RETURN_FALSE; + } + + xmlRelaxNGSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr); + is_valid = xmlRelaxNGValidateDoc(vptr, docp); + xmlRelaxNGFree(sptr); + xmlRelaxNGFreeValidCtxt(vptr); + + if (is_valid == 0) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} + +/* {{{ proto boolean domnode dom_document_relaxNG_validate_file(string filename); */ +PHP_FUNCTION(dom_document_relaxNG_validate_file) +{ + _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); +} +/* }}} end dom_document_relaxNG_validate_file */ + +/* {{{ proto boolean domnode dom_document_relaxNG_validate_xml(string source); */ +PHP_FUNCTION(dom_document_relaxNG_validate_xml) +{ + _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); +} +/* }}} end dom_document_relaxNG_validate_xml */ + +#endif + +#if defined(LIBXML_HTML_ENABLED) + +static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) +{ + zval *id, *rv = NULL; + xmlDoc *docp = NULL, *newdoc; + dom_object *intern; + dom_doc_props *doc_prop; + char *source; + int source_len, refcount, ret; + htmlParserCtxtPtr ctxt; + + id = getThis(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &source, &source_len) == FAILURE) { + return; + } + + if (mode == DOM_LOAD_FILE) { + ctxt = htmlCreateFileParserCtxt(source, NULL); + } else { + source_len = xmlStrlen(source); + ctxt = htmlCreateMemoryParserCtxt(source, source_len); + } + ctxt->vctxt.error = php_libxml_ctx_error; + ctxt->vctxt.warning = php_libxml_ctx_warning; + if (ctxt->sax != NULL) { + ctxt->sax->error = php_libxml_ctx_error; + ctxt->sax->warning = php_libxml_ctx_warning; + } + htmlParseDocument(ctxt); + newdoc = ctxt->myDoc; + htmlFreeParserCtxt(ctxt); + + if (!newdoc) + RETURN_FALSE; + + if (id != NULL) { + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + if (intern != NULL) { + docp = (xmlDocPtr) dom_object_get_node(intern); + doc_prop = NULL; + if (docp != NULL) { + php_libxml_decrement_node_ptr((php_libxml_node_object *) intern TSRMLS_CC); + doc_prop = intern->document->doc_props; + intern->document->doc_props = NULL; + refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern TSRMLS_CC); + if (refcount != 0) { + docp->_private = NULL; + } + } + intern->document = NULL; + php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc TSRMLS_CC); + intern->document->doc_props = doc_prop; + } + + php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern TSRMLS_CC); + + RETURN_TRUE; + } else { + DOM_RET_OBJ(rv, (xmlNodePtr) newdoc, &ret, NULL); + } +} + +/* {{{ proto boolean domnode dom_document_load_html_file(string source); +Since: DOM extended +*/ +PHP_FUNCTION(dom_document_load_html_file) +{ + dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); +} +/* }}} end dom_document_load_html_file */ + +/* {{{ proto boolean domnode dom_document_load_html(string source); +Since: DOM extended +*/ +PHP_FUNCTION(dom_document_load_html) +{ + dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); +} +/* }}} end dom_document_load_html */ + +/* {{{ proto long dom_document_save_html_file(string file); +Convenience method to save to file as html +*/ +PHP_FUNCTION(dom_document_save_html_file) +{ + zval *id; + xmlDoc *docp; + int file_len, bytes, format; + dom_object *intern; + dom_doc_props *doc_props; + char *file; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) { + return; + } + + /* encoding handled by property on doc */ + + doc_props = dom_get_doc_props(intern->document); + format = doc_props->formatoutput; + bytes = htmlSaveFileFormat(file, docp, NULL, format); + + if (bytes == -1) { + RETURN_FALSE; + } + RETURN_LONG(bytes); +} +/* }}} end dom_document_save_html_file */ + +/* {{{ proto string dom_document_save_html(); +Convenience method to output as html +*/ +PHP_FUNCTION(dom_document_save_html) +{ + zval *id; + xmlDoc *docp; + dom_object *intern; + xmlChar *mem; + int size; + + DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern); + + htmlDocDumpMemory(docp, &mem, &size); + if (!size) { + if (mem) + xmlFree(mem); + RETURN_FALSE; + } + RETVAL_STRINGL(mem, size, 1); + xmlFree(mem); +} +/* }}} end dom_document_save_html */ + +#endif /* defined(LIBXML_HTML_ENABLED) */ + +#endif /* HAVE_LIBXML && HAVE_DOM */ diff --git a/src/main/resource/testFiles/php-src/revFiles/81f05c1_b1d8f1_ext#mysqli#mysqli_nonapi.c b/src/main/resource/testFiles/php-src/revFiles/81f05c1_b1d8f1_ext#mysqli#mysqli_nonapi.c new file mode 100644 index 0000000..e010402 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/81f05c1_b1d8f1_ext#mysqli#mysqli_nonapi.c @@ -0,0 +1,269 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Georg Richter | + +----------------------------------------------------------------------+ + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_mysqli.h" + +/* {{{ proto object mysqli_connect([string hostname [,string username [,string passwd [,string dbname [,int port [,string socket]]]]]]) + Open a connection to a mysql server */ +PHP_FUNCTION(mysqli_connect) +{ + MYSQL *mysql = NULL; + MYSQLI_RESOURCE *mysqli_resource; + zval *object = getThis(); + char *hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL; + unsigned int hostname_len, username_len, passwd_len, dbname_len, socket_len; + long port=0; + + + if (getThis() && !ZEND_NUM_ARGS()) { + RETURN_NULL(); + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ssssls", &hostname, &hostname_len, &username, &username_len, + &passwd, &passwd_len, &dbname, &dbname_len, &port, &socket, &socket_len) == FAILURE) { + return; + } + + /* TODO: safe mode handling */ + if (PG(sql_safe_mode)){ + } else { + if (!passwd) { + passwd = MyG(default_pw); + if (!username){ + username = MyG(default_user); + if (!hostname) { + hostname = MyG(default_host); + } + } + } + } + + mysql = mysql_init(NULL); + + if (mysql_real_connect(mysql,hostname,username,passwd,dbname,port,socket,0) == NULL) { + /* Save error messages */ + + MYSQLI_REPORT_MYSQL_ERROR(mysql); + php_mysqli_set_error(mysql_errno(mysql), (char *) mysql_error(mysql) TSRMLS_CC); + + if (!(MyG(report_mode) & MYSQLI_REPORT_ERROR)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysql_error(mysql)); + } + /* free mysql structure */ + mysql_close(mysql); + RETURN_FALSE; + } + + /* clear error */ + php_mysqli_set_error(mysql_errno(mysql), (char *) mysql_error(mysql) TSRMLS_CC); + + mysql->reconnect = 0; + + mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); + mysqli_resource->ptr = (void *)mysql; + + if (!object) { + MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_link_class_entry); + } else { + ((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->ptr = mysqli_resource; + ((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->valid = 1; + } +} +/* }}} */ + +#ifdef HAVE_EMBEDDED_MYSQLI +/* {{{ proto object mysqli_embedded_connect(void) + Open a connection to a embedded mysql server */ +PHP_FUNCTION(mysqli_embedded_connect) +{ + MYSQL *mysql; + MYSQLI_RESOURCE *mysqli_resource; + zval *object = getThis(); + char *dbname = NULL; + int dblen = 0; + + if (!MyG(embedded)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Embedded server was not initialized."); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &dbname, &dblen) == FAILURE) { + return; + } + + mysql = mysql_init(NULL); + + if (mysql_real_connect(mysql, NULL, NULL, NULL, dbname, 0, NULL, 0) == NULL) { + MYSQLI_REPORT_MYSQL_ERROR(mysql); + php_mysqli_set_error(mysql_errno(mysql), (char *) mysql_error(mysql) TSRMLS_CC); + + if (!(MyG(report_mode) & MYSQLI_REPORT_ERROR)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysql_error(mysql)); + } + /* free mysql structure */ + mysql_close(mysql); + RETURN_FALSE; + } + + php_mysqli_set_error(mysql_errno(mysql), (char *) mysql_error(mysql) TSRMLS_CC); + + mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); + mysqli_resource->ptr = (void *)mysql; + + if (!object) { + MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_link_class_entry); + } else { + ((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->ptr = mysqli_resource; + } +} +/* }}} */ +#endif + +/* {{{ proto int mysqli_connect_errno(void) + Returns the numerical value of the error message from last connect command */ +PHP_FUNCTION(mysqli_connect_errno) +{ + RETURN_LONG(MyG(error_no)); +} +/* }}} */ + +/* {{{ proto string mysqli_connect_error(void) + Returns the text of the error message from previous MySQL operation */ +PHP_FUNCTION(mysqli_connect_error) +{ + if (MyG(error_msg)) { + RETURN_STRING(MyG(error_msg),1); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto mixed mysqli_fetch_array (object result [,int resulttype]) + Fetch a result row as an associative array, a numeric array, or both */ +PHP_FUNCTION(mysqli_fetch_array) +{ + php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); +} +/* }}} */ + +/* {{{ proto mixed mysqli_fetch_assoc (object result) + Fetch a result row as an associative array */ +PHP_FUNCTION(mysqli_fetch_assoc) +{ + php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 0); +} +/* }}} */ + +/* {{{ proto mixed mysqli_fetch_object (object result [, string class_name [, NULL|array ctor_params]]) + Fetch a result row as an object */ +PHP_FUNCTION(mysqli_fetch_object) +{ + php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 1); +} +/* }}} */ + +/* {{{ proto bool mysqli_multi_query(object link, string query) + Binary-safe version of mysql_query() */ +PHP_FUNCTION(mysqli_multi_query) +{ + MYSQL *mysql; + zval *mysql_link; + char *query = NULL; + unsigned int query_len; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &mysql_link, mysqli_link_class_entry, &query, &query_len) == FAILURE) { + return; + } + MYSQLI_FETCH_RESOURCE(mysql, MYSQL *, &mysql_link, "mysqli_link"); + + MYSQLI_ENABLE_MQ; + if (mysql_real_query(mysql, query, query_len)) { + MYSQLI_DISABLE_MQ; + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto mixed mysqli_query(object link, string query [,int resultmode]) */ +PHP_FUNCTION(mysqli_query) +{ + MYSQL *mysql; + zval *mysql_link; + MYSQLI_RESOURCE *mysqli_resource; + MYSQL_RES *result; + char *query = NULL; + unsigned int query_len; + unsigned int resultmode = 0; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", &mysql_link, mysqli_link_class_entry, &query, &query_len, &resultmode) == FAILURE) { + return; + } + MYSQLI_FETCH_RESOURCE(mysql, MYSQL*, &mysql_link, "mysqli_link"); + + MYSQLI_DISABLE_MQ; + + if (mysql_real_query(mysql, query, query_len)) { + MYSQLI_REPORT_MYSQL_ERROR(mysql); + RETURN_FALSE; + } + + + if (!mysql_field_count(mysql)) { + if (MyG(report_mode) & MYSQLI_REPORT_INDEX) { + php_mysqli_report_index(query, mysql->server_status TSRMLS_CC); + } + RETURN_TRUE; + } + + result = (resultmode == MYSQLI_USE_RESULT) ? mysql_use_result(mysql) : mysql_store_result(mysql); + + if (!result) { + RETURN_FALSE; + } + + if (MyG(report_mode) & MYSQLI_REPORT_INDEX) { + php_mysqli_report_index(query, mysql->server_status TSRMLS_CC); + } + + mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); + mysqli_resource->ptr = (void *)result; + MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/820983_90ee88_ext#standard#html.c b/src/main/resource/testFiles/php-src/revFiles/820983_90ee88_ext#standard#html.c new file mode 100644 index 0000000..a50f1fd --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/820983_90ee88_ext#standard#html.c @@ -0,0 +1,1370 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jaakko Hyvätti | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* + * HTML entity resources: + * + * http://msdn.microsoft.com/workshop/author/dhtml/reference/charsets/charset2.asp + * http://msdn.microsoft.com/workshop/author/dhtml/reference/charsets/charset3.asp + * http://www.unicode.org/Public/MAPPINGS/OBSOLETE/UNI2SGML.TXT + * + * http://www.w3.org/TR/2002/REC-xhtml1-20020801/dtds.html#h-A2 + * + */ + +#include "php.h" +#if PHP_WIN32 +#include "config.w32.h" +#elif defined NETWARE +#include "config.nw.h" +#else +#include +#endif +#include "reg.h" +#include "html.h" +#include "php_string.h" +#include "SAPI.h" +#if HAVE_LOCALE_H +#include +#endif +#if HAVE_LANGINFO_H +#include +#endif + +#if HAVE_MBSTRING +# include "ext/mbstring/mbstring.h" +ZEND_EXTERN_MODULE_GLOBALS(mbstring) +#endif + +enum entity_charset { cs_terminator, cs_8859_1, cs_cp1252, + cs_8859_15, cs_utf_8, cs_big5, cs_gb2312, + cs_big5hkscs, cs_sjis, cs_eucjp, cs_koi8r, + cs_cp1251, cs_8859_5, cs_cp866, cs_macroman + }; +typedef const char *entity_table_t; + +/* codepage 1252 is a Windows extension to iso-8859-1. */ +static entity_table_t ent_cp_1252[] = { + "euro", NULL, "sbquo", "fnof", "bdquo", "hellip", "dagger", + "Dagger", "circ", "permil", "Scaron", "lsaquo", "OElig", + NULL, NULL, NULL, NULL, "lsquo", "rsquo", "ldquo", "rdquo", + "bull", "ndash", "mdash", "tilde", "trade", "scaron", "rsaquo", + "oelig", NULL, NULL, "Yuml" +}; + +static entity_table_t ent_iso_8859_1[] = { + "nbsp", "iexcl", "cent", "pound", "curren", "yen", "brvbar", + "sect", "uml", "copy", "ordf", "laquo", "not", "shy", "reg", + "macr", "deg", "plusmn", "sup2", "sup3", "acute", "micro", + "para", "middot", "cedil", "sup1", "ordm", "raquo", "frac14", + "frac12", "frac34", "iquest", "Agrave", "Aacute", "Acirc", + "Atilde", "Auml", "Aring", "AElig", "Ccedil", "Egrave", + "Eacute", "Ecirc", "Euml", "Igrave", "Iacute", "Icirc", + "Iuml", "ETH", "Ntilde", "Ograve", "Oacute", "Ocirc", "Otilde", + "Ouml", "times", "Oslash", "Ugrave", "Uacute", "Ucirc", "Uuml", + "Yacute", "THORN", "szlig", "agrave", "aacute", "acirc", + "atilde", "auml", "aring", "aelig", "ccedil", "egrave", + "eacute", "ecirc", "euml", "igrave", "iacute", "icirc", + "iuml", "eth", "ntilde", "ograve", "oacute", "ocirc", "otilde", + "ouml", "divide", "oslash", "ugrave", "uacute", "ucirc", + "uuml", "yacute", "thorn", "yuml" +}; + +static entity_table_t ent_iso_8859_15[] = { + "nbsp", "iexcl", "cent", "pound", "euro", "yen", "Scaron", + "sect", "scaron", "copy", "ordf", "laquo", "not", "shy", "reg", + "macr", "deg", "plusmn", "sup2", "sup3", NULL, /* Zcaron */ + "micro", "para", "middot", NULL, /* zcaron */ "sup1", "ordm", + "raquo", "OElig", "oelig", "Yuml", "iquest", "Agrave", "Aacute", + "Acirc", "Atilde", "Auml", "Aring", "AElig", "Ccedil", "Egrave", + "Eacute", "Ecirc", "Euml", "Igrave", "Iacute", "Icirc", + "Iuml", "ETH", "Ntilde", "Ograve", "Oacute", "Ocirc", "Otilde", + "Ouml", "times", "Oslash", "Ugrave", "Uacute", "Ucirc", "Uuml", + "Yacute", "THORN", "szlig", "agrave", "aacute", "acirc", + "atilde", "auml", "aring", "aelig", "ccedil", "egrave", + "eacute", "ecirc", "euml", "igrave", "iacute", "icirc", + "iuml", "eth", "ntilde", "ograve", "oacute", "ocirc", "otilde", + "ouml", "divide", "oslash", "ugrave", "uacute", "ucirc", + "uuml", "yacute", "thorn", "yuml" +}; + +static entity_table_t ent_uni_338_402[] = { + /* 338 (0x0152) */ + "OElig", "oelig", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 352 (0x0160) */ + "Scaron", "scaron", NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 376 (0x0178) + "Yuml", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, */ + /* 400 (0x0190)*/ + NULL, NULL, "fnof" +}; + +static entity_table_t ent_uni_spacing[] = { + /* 710 */ + "circ", + /* 711 - 730 */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 731 - 732 */ + NULL, "tilde" +}; + +static entity_table_t ent_uni_greek[] = { + /* 913 */ + "Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", + "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", + NULL, "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega", + /* 938 - 944 are not mapped */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", + "iota", "kappa", "lambda", "mu", "nu", "xi", "omicron", "pi", "rho", + "sigmaf", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega", + /* 970 - 976 are not mapped */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "thetasym", "upsih", + NULL, NULL, NULL, + "piv" +}; + +static entity_table_t ent_uni_punct[] = { + /* 8194 */ + "ensp", "emsp", NULL, NULL, NULL, NULL, NULL, + "thinsp", NULL, NULL, "zwnj", "zwj", "lrm", "rlm", + NULL, NULL, NULL, "ndash", "mdash", NULL, NULL, NULL, + /* 8216 */ + "lsquo", "rsquo", "sbquo", NULL, "ldquo", "rdquo", "bdquo", NULL, + "dagger", "Dagger", "bull", NULL, NULL, NULL, "hellip", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "permil", NULL, + /* 8242 */ + "prime", "Prime", NULL, NULL, NULL, NULL, NULL, "lsaquo", "rsaquo", NULL, + NULL, NULL, "oline", NULL, NULL, NULL, NULL, NULL, + "frasl" +}; + +static entity_table_t ent_uni_euro[] = { + "euro" +}; + +static entity_table_t ent_uni_8465_8501[] = { + /* 8465 */ + "image", NULL, NULL, NULL, NULL, NULL, NULL, + /* 8472 */ + "weierp", NULL, NULL, NULL, + /* 8476 */ + "real", NULL, NULL, NULL, NULL, NULL, + /* 8482 */ + "trade", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8501 */ + "alefsym", +}; + +static entity_table_t ent_uni_8592_9002[] = { + /* 8592 (0x2190) */ + "larr", "uarr", "rarr", "darr", "harr", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8608 (0x21a0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8624 (0x21b0) */ + NULL, NULL, NULL, NULL, NULL, "crarr", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8640 (0x21c0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8656 (0x21d0) */ + "lArr", "uArr", "rArr", "dArr", "hArr", "vArr", NULL, NULL, + NULL, NULL, "lAarr", "rAarr", NULL, "rarrw", NULL, NULL, + /* 8672 (0x21e0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8704 (0x2200) */ + "forall", "comp", "part", "exist", "nexist", "empty", NULL, "nabla", + "isin", "notin", "epsis", "ni", "notni", "bepsi", NULL, "prod", + /* 8720 (0x2210) */ + "coprod", "sum", "minus", "mnplus", "plusdo", NULL, "setmn", "lowast", + "compfn", NULL, "radic", NULL, NULL, "prop", "infin", "ang90", + /* 8736 (0x2220) */ + "ang", "angmsd", "angsph", "mid", "nmid", "par", "npar", "and", + "or", "cap", "cup", "int", NULL, NULL, "conint", NULL, + /* 8752 (0x2230) */ + NULL, NULL, NULL, NULL, "there4", "becaus", NULL, NULL, + NULL, NULL, NULL, NULL, "sim", "bsim", NULL, NULL, + /* 8768 (0x2240) */ + "wreath", "nsim", NULL, "sime", "nsime", "cong", NULL, "ncong", + "asymp", "nap", "ape", NULL, "bcong", "asymp", "bump", "bumpe", + /* 8784 (0x2250) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8800 (0x2260) */ + "ne", "equiv", NULL, NULL, "le", "ge", "lE", "gE", + "lnE", "gnE", "Lt", "Gt", "twixt", NULL, "nlt", "ngt", + /* 8816 (0x2270) */ + "nles", "nges", "lsim", "gsim", NULL, NULL, "lg", "gl", + NULL, NULL, "pr", "sc", "cupre", "sscue", "prsim", "scsim", + /* 8832 (0x2280) */ + "npr", "nsc", "sub", "sup", "nsub", "nsup", "sube", "supe", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8848 (0x2290) */ + NULL, NULL, NULL, NULL, NULL, "oplus", NULL, "otimes", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8864 (0x22a0) */ + NULL, NULL, NULL, NULL, NULL, "perp", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8880 (0x22b0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8896 (0x22c0) */ + NULL, NULL, NULL, NULL, NULL, "sdot", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8912 (0x22d0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8928 (0x22e0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8944 (0x22f0) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8960 (0x2300) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "lceil", "rceil", "lfloor", "rfloor", NULL, NULL, NULL, NULL, + /* 8976 (0x2310) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 8992 (0x2320) */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, "lang", "rang" +}; + +static entity_table_t ent_uni_9674[] = { + /* 9674 */ + "loz" +}; + +static entity_table_t ent_uni_9824_9830[] = { + /* 9824 */ + "spades", NULL, NULL, "clubs", NULL, "hearts", "diams" +}; + +static entity_table_t ent_koi8r[] = { + "#1105", /* "jo "*/ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, "#1025", /* "JO" */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "#1102", "#1072", "#1073", "#1094", "#1076", "#1077", "#1092", + "#1075", "#1093", "#1080", "#1081", "#1082", "#1083", "#1084", + "#1085", "#1086", "#1087", "#1103", "#1088", "#1089", "#1090", + "#1091", "#1078", "#1074", "#1100", "#1099", "#1079", "#1096", + "#1101", "#1097", "#1095", "#1098", "#1070", "#1040", "#1041", + "#1062", "#1044", "#1045", "#1060", "#1043", "#1061", "#1048", + "#1049", "#1050", "#1051", "#1052", "#1053", "#1054", "#1055", + "#1071", "#1056", "#1057", "#1058", "#1059", "#1046", "#1042", + "#1068", "#1067", "#1047", "#1064", "#1069", "#1065", "#1063", + "#1066" +}; + +static entity_table_t ent_cp_1251[] = { + "#1026", "#1027", "#8218", "#1107", "#8222", "hellip", "dagger", + "Dagger", "euro", "permil", "#1033", "#8249", "#1034", "#1036", + "#1035", "#1039", "#1106", "#8216", "#8217", "#8219", "#8220", + "bull", "ndash", "mdash", NULL, "trade", "#1113", "#8250", + "#1114", "#1116", "#1115", "#1119", "nbsp", "#1038", "#1118", + "#1032", "curren", "#1168", "brvbar", "sect", "#1025", "copy", + "#1028", "laquo", "not", "shy", "reg", "#1031", "deg", "plusmn", + "#1030", "#1110", "#1169", "micro", "para", "middot", "#1105", + "#8470", "#1108", "raquo", "#1112", "#1029", "#1109", "#1111", + "#1040", "#1041", "#1042", "#1043", "#1044", "#1045", "#1046", + "#1047", "#1048", "#1049", "#1050", "#1051", "#1052", "#1053", + "#1054", "#1055", "#1056", "#1057", "#1058", "#1059", "#1060", + "#1061", "#1062", "#1063", "#1064", "#1065", "#1066", "#1067", + "#1068", "#1069", "#1070", "#1071", "#1072", "#1073", "#1074", + "#1075", "#1076", "#1077", "#1078", "#1079", "#1080", "#1081", + "#1082", "#1083", "#1084", "#1085", "#1086", "#1087", "#1088", + "#1089", "#1090", "#1091", "#1092", "#1093", "#1094", "#1095", + "#1096", "#1097", "#1098", "#1099", "#1100", "#1101", "#1102", + "#1103" +}; + +static entity_table_t ent_iso_8859_5[] = { + "#1056", "#1057", "#1058", "#1059", "#1060", "#1061", "#1062", + "#1063", "#1064", "#1065", "#1066", "#1067", "#1068", "#1069", + "#1070", "#1071", "#1072", "#1073", "#1074", "#1075", "#1076", + "#1077", "#1078", "#1079", "#1080", "#1081", "#1082", "#1083", + "#1084", "#1085", "#1086", "#1087", "#1088", "#1089", "#1090", + "#1091", "#1092", "#1093", "#1094", "#1095", "#1096", "#1097", + "#1098", "#1099", "#1100", "#1101", "#1102", "#1103", "#1104", + "#1105", "#1106", "#1107", "#1108", "#1109", "#1110", "#1111", + "#1112", "#1113", "#1114", "#1115", "#1116", "#1117", "#1118", + "#1119" +}; + +static entity_table_t ent_cp_866[] = { + + "#9492", "#9524", "#9516", "#9500", "#9472", "#9532", "#9566", + "#9567", "#9562", "#9556", "#9577", "#9574", "#9568", "#9552", + "#9580", "#9575", "#9576", "#9572", "#9573", "#9561", "#9560", + "#9554", "#9555", "#9579", "#9578", "#9496", "#9484", "#9608", + "#9604", "#9612", "#9616", "#9600", "#1088", "#1089", "#1090", + "#1091", "#1092", "#1093", "#1094", "#1095", "#1096", "#1097", + "#1098", "#1099", "#1100", "#1101", "#1102", "#1103", "#1025", + "#1105", "#1028", "#1108", "#1031", "#1111", "#1038", "#1118", + "#176", "#8729", "#183", "#8730", "#8470", "#164", "#9632", + "#160" +}; + +/* MacRoman has a couple of low-ascii chars that need mapping too */ +/* Vertical tab (ASCII 11) is often used to store line breaks inside */ +/* DB exports, this mapping changes it to a space */ +static entity_table_t ent_macroman[] = { + "sp", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, "quot", NULL, + NULL, NULL, "amp", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "lt", NULL, "gt", NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, "Auml", "Aring", "Ccedil", "Eacute", "Ntilde", "Ouml", + "Uuml", "aacute", "agrave", "acirc", "auml", "atilde", "aring", + "ccedil", "eacute", "egrave", "ecirc", "euml", "iacute", "igrave", + "icirc", "iuml", "ntilde", "oacute", "ograve", "ocirc", "ouml", + "otilde", "uacute", "ugrave", "ucirc", "uuml", "dagger", "deg", + "cent", "pound", "sect", "bull", "para", "szlig", "reg", + "copy", "trade", "acute", "uml", "ne", "AElig", "Oslash", + "infin", "plusmn", "le", "ge", "yen", "micro", "part", + "sum", "prod", "pi", "int", "ordf", "ordm", "Omega", + "aelig", "oslash", "iquest", "iexcl", "not", "radic", "fnof", + "asymp", "#8710", "laquo", "raquo", "hellip", "nbsp", "Agrave", + "Atilde", "Otilde", "OElig", "oelig", "ndash", "mdash", "ldquo", + "rdquo", "lsquo", "rsquo", "divide", "loz", "yuml", "Yuml", + "frasl", "euro", "lsaquo", "rsaquo", "#xFB01", "#xFB02", "Dagger", + "middot", "sbquo", "bdquo", "permil", "Acirc", "Ecirc", "Aacute", + "Euml", "Egrave", "Iacute", "Icirc", "Iuml", "Igrave", "Oacute", + "Ocirc", "#xF8FF", "Ograve", "Uacute", "Ucirc", "Ugrave", "#305", + "circ", "tilde", "macr", "#728", "#729", "#730", "cedil", + "#733", "#731", "#711" +}; + +struct html_entity_map { + enum entity_charset charset; /* charset identifier */ + unsigned short basechar; /* char code at start of table */ + unsigned short endchar; /* last char code in the table */ + entity_table_t *table; /* the table of mappings */ +}; + +static const struct html_entity_map entity_map[] = { + { cs_cp1252, 0x80, 0x9f, ent_cp_1252 }, + { cs_cp1252, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_8859_1, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_8859_15, 0xa0, 0xff, ent_iso_8859_15 }, + { cs_utf_8, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_utf_8, 338, 402, ent_uni_338_402 }, + { cs_utf_8, 710, 732, ent_uni_spacing }, + { cs_utf_8, 913, 982, ent_uni_greek }, + { cs_utf_8, 8194, 8260, ent_uni_punct }, + { cs_utf_8, 8364, 8364, ent_uni_euro }, + { cs_utf_8, 8465, 8501, ent_uni_8465_8501 }, + { cs_utf_8, 8592, 9002, ent_uni_8592_9002 }, + { cs_utf_8, 9674, 9674, ent_uni_9674 }, + { cs_utf_8, 9824, 9830, ent_uni_9824_9830 }, + { cs_big5, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_gb2312, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_big5hkscs, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_sjis, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_eucjp, 0xa0, 0xff, ent_iso_8859_1 }, + { cs_koi8r, 0xa3, 0xff, ent_koi8r }, + { cs_cp1251, 0x80, 0xff, ent_cp_1251 }, + { cs_8859_5, 0xc0, 0xff, ent_iso_8859_5 }, + { cs_cp866, 0xc0, 0xff, ent_cp_866 }, + { cs_macroman, 0x0b, 0xff, ent_macroman }, + { cs_terminator } +}; + +static const struct { + const char *codeset; + enum entity_charset charset; +} charset_map[] = { + { "ISO-8859-1", cs_8859_1 }, + { "ISO8859-1", cs_8859_1 }, + { "ISO-8859-15", cs_8859_15 }, + { "ISO8859-15", cs_8859_15 }, + { "utf-8", cs_utf_8 }, + { "cp1252", cs_cp1252 }, + { "Windows-1252", cs_cp1252 }, + { "1252", cs_cp1252 }, + { "BIG5", cs_big5 }, + { "950", cs_big5 }, + { "GB2312", cs_gb2312 }, + { "936", cs_gb2312 }, + { "BIG5-HKSCS", cs_big5hkscs }, + { "Shift_JIS", cs_sjis }, + { "SJIS", cs_sjis }, + { "932", cs_sjis }, + { "EUCJP", cs_eucjp }, + { "EUC-JP", cs_eucjp }, + { "KOI8-R", cs_koi8r }, + { "koi8-ru", cs_koi8r }, + { "koi8r", cs_koi8r }, + { "cp1251", cs_cp1251 }, + { "Windows-1251", cs_cp1251 }, + { "win-1251", cs_cp1251 }, + { "iso8859-5", cs_8859_5 }, + { "iso-8859-5", cs_8859_5 }, + { "cp866", cs_cp866 }, + { "866", cs_cp866 }, + { "ibm866", cs_cp866 }, + { "MacRoman", cs_macroman }, + { NULL } +}; + +static const struct { + unsigned short charcode; + char *entity; + int entitylen; + int flags; +} basic_entities[] = { + { '"', """, 6, ENT_HTML_QUOTE_DOUBLE }, + { '\'', "'", 6, ENT_HTML_QUOTE_SINGLE }, + { '\'', "'", 5, ENT_HTML_QUOTE_SINGLE }, + { '<', "<", 4, 0 }, + { '>', ">", 4, 0 }, + { 0, NULL, 0, 0 } +}; + +struct basic_entities_dec { + unsigned short charcode; + char entity[8]; + int entitylen; +}; + +#define MB_RETURN { \ + *newpos = pos; \ + mbseq[mbpos] = '\0'; \ + *mbseqlen = mbpos; \ + return this_char; } + +#define MB_WRITE(mbchar) { \ + mbspace--; \ + if (mbspace == 0) { \ + MB_RETURN; \ + } \ + mbseq[mbpos++] = (mbchar); } + +/* {{{ get_next_char + */ +inline static unsigned short get_next_char(enum entity_charset charset, + unsigned char * str, + int * newpos, + unsigned char * mbseq, + int * mbseqlen) +{ + int pos = *newpos; + int mbpos = 0; + int mbspace = *mbseqlen; + unsigned short this_char = str[pos++]; + + if (mbspace <= 0) { + *mbseqlen = 0; + return this_char; + } + + MB_WRITE((unsigned char)this_char); + + switch (charset) { + case cs_utf_8: + { + unsigned long utf = 0; + int stat = 0; + int more = 1; + + /* unpack utf-8 encoding into a wide char. + * Code stolen from the mbstring extension */ + + do { + if (this_char < 0x80) { + more = 0; + break; + } else if (this_char < 0xc0) { + switch (stat) { + case 0x10: /* 2, 2nd */ + case 0x21: /* 3, 3rd */ + case 0x32: /* 4, 4th */ + case 0x43: /* 5, 5th */ + case 0x54: /* 6, 6th */ + /* last byte in sequence */ + more = 0; + utf |= (this_char & 0x3f); + this_char = (unsigned short)utf; + break; + case 0x20: /* 3, 2nd */ + case 0x31: /* 4, 3rd */ + case 0x42: /* 5, 4th */ + case 0x53: /* 6, 5th */ + /* penultimate char */ + utf |= ((this_char & 0x3f) << 6); + stat++; + break; + case 0x30: /* 4, 2nd */ + case 0x41: /* 5, 3rd */ + case 0x52: /* 6, 4th */ + utf |= ((this_char & 0x3f) << 12); + stat++; + break; + case 0x40: /* 5, 2nd */ + case 0x51: + utf |= ((this_char & 0x3f) << 18); + stat++; + break; + case 0x50: /* 6, 2nd */ + utf |= ((this_char & 0x3f) << 24); + stat++; + break; + default: + /* invalid */ + more = 0; + } + } + /* lead byte */ + else if (this_char < 0xe0) { + stat = 0x10; /* 2 byte */ + utf = (this_char & 0x1f) << 6; + } else if (this_char < 0xf0) { + stat = 0x20; /* 3 byte */ + utf = (this_char & 0xf) << 12; + } else if (this_char < 0xf8) { + stat = 0x30; /* 4 byte */ + utf = (this_char & 0x7) << 18; + } else if (this_char < 0xfc) { + stat = 0x40; /* 5 byte */ + utf = (this_char & 0x3) << 24; + } else if (this_char < 0xfe) { + stat = 0x50; /* 6 byte */ + utf = (this_char & 0x1) << 30; + } else { + /* invalid; bail */ + more = 0; + break; + } + + if (more) { + this_char = str[pos++]; + MB_WRITE((unsigned char)this_char); + } + } while (more); + } + break; + case cs_big5: + case cs_gb2312: + case cs_big5hkscs: + { + /* check if this is the first of a 2-byte sequence */ + if (this_char >= 0xa1 && this_char <= 0xfe) { + /* peek at the next char */ + unsigned char next_char = str[pos]; + if ((next_char >= 0x40 && next_char <= 0x7e) || + (next_char >= 0xa1 && next_char <= 0xfe)) { + /* yes, this a wide char */ + this_char <<= 8; + MB_WRITE(next_char); + this_char |= next_char; + pos++; + } + + } + break; + } + case cs_sjis: + { + /* check if this is the first of a 2-byte sequence */ + if ( (this_char >= 0x81 && this_char <= 0x9f) || + (this_char >= 0xe0 && this_char <= 0xef) + ) { + /* peek at the next char */ + unsigned char next_char = str[pos]; + if ((next_char >= 0x40 && next_char <= 0x7e) || + (next_char >= 0x80 && next_char <= 0xfc)) + { + /* yes, this a wide char */ + this_char <<= 8; + MB_WRITE(next_char); + this_char |= next_char; + pos++; + } + + } + break; + } + case cs_eucjp: + { + /* check if this is the first of a multi-byte sequence */ + if (this_char >= 0xa1 && this_char <= 0xfe) { + /* peek at the next char */ + unsigned char next_char = str[pos]; + if (next_char >= 0xa1 && next_char <= 0xfe) { + /* yes, this a jis kanji char */ + this_char <<= 8; + MB_WRITE(next_char); + this_char |= next_char; + pos++; + } + + } else if (this_char == 0x8e) { + /* peek at the next char */ + unsigned char next_char = str[pos]; + if (next_char >= 0xa1 && next_char <= 0xdf) { + /* JIS X 0201 kana */ + this_char <<= 8; + MB_WRITE(next_char); + this_char |= next_char; + pos++; + } + + } else if (this_char == 0x8f) { + /* peek at the next two char */ + unsigned char next_char = str[pos]; + unsigned char next2_char = str[pos+1]; + if ((next_char >= 0xa1 && next_char <= 0xfe) && + (next2_char >= 0xa1 && next2_char <= 0xfe)) { + /* JIS X 0212 hojo-kanji */ + this_char <<= 8; + MB_WRITE(next_char); + this_char |= next_char; + pos++; + this_char <<= 8; + MB_WRITE(next2_char); + this_char |= next2_char; + pos++; + } + + } + break; + } + default: + break; + } + MB_RETURN; +} +/* }}} */ + +/* {{{ entity_charset determine_charset + * returns the charset identifier based on current locale or a hint. + * defaults to iso-8859-1 */ +static enum entity_charset determine_charset(char *charset_hint TSRMLS_DC) +{ + int i; + enum entity_charset charset = cs_8859_1; + int len = 0; + zval *uf_result = NULL; + + /* Guarantee default behaviour for backwards compatibility */ + if (charset_hint == NULL) + return cs_8859_1; + + if ((len = strlen(charset_hint)) != 0) { + goto det_charset; + } +#if HAVE_MBSTRING +#if !defined(COMPILE_DL_MBSTRING) + /* XXX: Ugly things. Why don't we look for a more sophisticated way? */ + switch (MBSTRG(current_internal_encoding)) { + case mbfl_no_encoding_8859_1: + return cs_8859_1; + + case mbfl_no_encoding_utf8: + return cs_utf_8; + + case mbfl_no_encoding_euc_jp: + case mbfl_no_encoding_eucjp_win: + return cs_eucjp; + + case mbfl_no_encoding_sjis: + case mbfl_no_encoding_sjis_win: + case mbfl_no_encoding_sjis_mac: + return cs_sjis; + + case mbfl_no_encoding_cp1252: + return cs_cp1252; + + case mbfl_no_encoding_8859_15: + return cs_8859_15; + + case mbfl_no_encoding_big5: + return cs_big5; + + case mbfl_no_encoding_euc_cn: + case mbfl_no_encoding_hz: + case mbfl_no_encoding_cp936: + return cs_gb2312; + + case mbfl_no_encoding_koi8r: + return cs_koi8r; + + case mbfl_no_encoding_cp866: + return cs_cp866; + + case mbfl_no_encoding_cp1251: + return cs_cp1251; + + case mbfl_no_encoding_8859_5: + return cs_8859_5; + + default: + ; + } +#else + { + zval nm_mb_internal_encoding; + + ZVAL_STRING(&nm_mb_internal_encoding, "mb_internal_encoding", 0); + + if (call_user_function_ex(CG(function_table), NULL, &nm_mb_internal_encoding, &uf_result, 0, NULL, 1, NULL TSRMLS_CC) != FAILURE) { + + charset_hint = Z_STRVAL_P(uf_result); + len = Z_STRLEN_P(uf_result); + + goto det_charset; + } + } +#endif +#endif + + charset_hint = SG(default_charset); + if (charset_hint != NULL && (len=strlen(charset_hint)) != 0) { + goto det_charset; + } + + /* try to detect the charset for the locale */ +#if HAVE_NL_LANGINFO && HAVE_LOCALE_H && defined(CODESET) + charset_hint = nl_langinfo(CODESET); + if (charset_hint != NULL && (len=strlen(charset_hint)) != 0) { + goto det_charset; + } +#endif + +#if HAVE_LOCALE_H + /* try to figure out the charset from the locale */ + { + char *localename; + char *dot, *at; + + /* lang[_territory][.codeset][@modifier] */ + localename = setlocale(LC_CTYPE, NULL); + + dot = strchr(localename, '.'); + if (dot) { + dot++; + /* locale specifies a codeset */ + at = strchr(dot, '@'); + if (at) + len = at - dot; + else + len = strlen(dot); + charset_hint = dot; + } else { + /* no explicit name; see if the name itself + * is the charset */ + charset_hint = localename; + len = strlen(charset_hint); + } + } +#endif + +det_charset: + + if (charset_hint) { + int found = 0; + + /* now walk the charset map and look for the codeset */ + for (i = 0; charset_map[i].codeset; i++) { + if (strncasecmp(charset_hint, charset_map[i].codeset, len) == 0) { + charset = charset_map[i].charset; + found = 1; + break; + } + } + if (!found) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "charset `%s' not supported, assuming iso-8859-1", + charset_hint); + } + } + if (uf_result != NULL) { + zval_ptr_dtor(&uf_result); + } + return charset; +} +/* }}} */ + +/* {{{ php_utf32_utf8 */ +size_t php_utf32_utf8(unsigned char *buf, int k) +{ + size_t retval = 0; + + if (k < 0x80) { + buf[0] = k; + retval = 1; + } else if (k < 0x800) { + buf[0] = 0xc0 | (k >> 6); + buf[1] = 0x80 | (k & 0x3f); + retval = 2; + } else if (k < 0x10000) { + buf[0] = 0xe0 | (k >> 12); + buf[1] = 0x80 | ((k >> 6) & 0x3f); + buf[2] = 0x80 | (k & 0x3f); + retval = 3; + } else if (k < 0x200000) { + buf[0] = 0xf0 | (k >> 18); + buf[1] = 0x80 | ((k >> 12) & 0x3f); + buf[2] = 0x80 | ((k >> 6) & 0x3f); + buf[3] = 0x80 | (k & 0x3f); + retval = 4; + } else if (k < 0x4000000) { + buf[0] = 0xf8 | (k >> 24); + buf[1] = 0x80 | ((k >> 18) & 0x3f); + buf[2] = 0x80 | ((k >> 12) & 0x3f); + buf[3] = 0x80 | ((k >> 6) & 0x3f); + buf[4] = 0x80 | (k & 0x3f); + retval = 5; + } else { + buf[0] = 0xfc | (k >> 30); + buf[1] = 0x80 | ((k >> 24) & 0x3f); + buf[2] = 0x80 | ((k >> 18) & 0x3f); + buf[3] = 0x80 | ((k >> 12) & 0x3f); + buf[4] = 0x80 | ((k >> 6) & 0x3f); + buf[5] = 0x80 | (k & 0x3f); + retval = 6; + } + buf[retval] = '\0'; + + return retval; +} +/* }}} */ + +/* {{{ php_unescape_html_entities + */ +PHPAPI char *php_unescape_html_entities(unsigned char *old, int oldlen, int *newlen, int all, int quote_style, char *hint_charset TSRMLS_DC) +{ + int retlen; + int j, k; + char *replaced, *ret, *p, *q, *lim, *next; + enum entity_charset charset = determine_charset(hint_charset TSRMLS_CC); + unsigned char replacement[15]; + int replacement_len; + + ret = estrdup(old); + retlen = oldlen; + if (!retlen) { + goto empty_source; + } + + if (all) { + /* look for a match in the maps for this charset */ + for (j = 0; entity_map[j].charset != cs_terminator; j++) { + if (entity_map[j].charset != charset) + continue; + + for (k = entity_map[j].basechar; k <= entity_map[j].endchar; k++) { + unsigned char entity[32]; + int entity_length = 0; + + if (entity_map[j].table[k - entity_map[j].basechar] == NULL) + continue; + + entity[0] = '&'; + entity_length = strlen(entity_map[j].table[k - entity_map[j].basechar]); + strncpy(&entity[1], entity_map[j].table[k - entity_map[j].basechar], sizeof(entity) - 2); + entity[entity_length+1] = ';'; + entity[entity_length+2] = '\0'; + entity_length += 2; + + /* When we have MBCS entities in the tables above, this will need to handle it */ + replacement_len = 0; + switch (charset) { + case cs_8859_1: + case cs_cp1252: + case cs_8859_15: + case cs_cp1251: + case cs_8859_5: + case cs_cp866: + replacement[0] = k; + replacement[1] = '\0'; + replacement_len = 1; + break; + + case cs_big5: + case cs_gb2312: + case cs_big5hkscs: + case cs_sjis: + case cs_eucjp: + /* we cannot properly handle those multibyte encodings + * with php_str_to_str. skip it. */ + continue; + + case cs_utf_8: + replacement_len = php_utf32_utf8(replacement, k); + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot yet handle MBCS!"); + return 0; + } + + if (php_memnstr(ret, entity, entity_length, ret+retlen)) { + replaced = php_str_to_str(ret, retlen, entity, entity_length, replacement, replacement_len, &retlen); + efree(ret); + ret = replaced; + } + } + } + } + + for (j = 0; basic_entities[j].charcode != 0; j++) { + + if (basic_entities[j].flags && (quote_style & basic_entities[j].flags) == 0) + continue; + + replacement[0] = (unsigned char)basic_entities[j].charcode; + replacement[1] = '\0'; + + if (php_memnstr(ret, basic_entities[j].entity, basic_entities[j].entitylen, ret+retlen)) { + replaced = php_str_to_str(ret, retlen, basic_entities[j].entity, basic_entities[j].entitylen, replacement, 1, &retlen); + efree(ret); + ret = replaced; + } + } + + /* replace numeric entities & "&" */ + lim = ret + retlen; + for (p = ret, q = ret; p < lim;) { + int code; + + if (p[0] == '&') { + if (p + 2 < lim) { + if (p[1] == '#') { + int invalid_code = 0; + + if (p[2] == 'x' || p[2] == 'X') { + code = strtol(p + 3, &next, 16); + } else { + code = strtol(p + 2, &next, 10); + } + + if (next != NULL && *next == ';') { + switch (charset) { + case cs_utf_8: + q += php_utf32_utf8(q, code); + break; + + case cs_8859_1: + case cs_8859_5: + case cs_8859_15: + if ((code >= 0x80 && code < 0xa0) || code > 0xff) { + invalid_code = 1; + } else { + if (code == 39 || !quote_style) { + invalid_code = 1; + } else { + *(q++) = code; + } + } + break; + + case cs_cp1252: + case cs_cp1251: + case cs_cp866: + if (code > 0xff) { + invalid_code = 1; + } else { + *(q++) = code; + } + break; + + case cs_big5: + case cs_big5hkscs: + case cs_sjis: + case cs_eucjp: + if (code >= 0x80) { + invalid_code = 1; + } else { + *(q++) = code; + } + break; + + case cs_gb2312: + if (code >= 0x81) { + invalid_code = 1; + } else { + *(q++) = code; + } + break; + + default: + /* for backwards compatilibity */ + invalid_code = 1; + break; + } + if (invalid_code) { + for (; p <= next; p++) { + *(q++) = *p; + } + } + p = next + 1; + } else { + *(q++) = *(p++); + *(q++) = *(p++); + } + } else if (p + 4 < lim && + p[1] == 'a' && p[2] == 'm' &&p[3] == 'p' && + p[4] == ';') { + *(q++) = '&'; + p += 5; + } else { + *(q++) = *(p++); + *(q++) = *(p++); + } + } else { + *(q++) = *(p++); + } + } else { + *(q++) = *(p++); + } + } + *q = '\0'; + retlen = (size_t)(q - ret); +empty_source: + *newlen = retlen; + return ret; +} +/* }}} */ + + + + +/* {{{ php_escape_html_entities + */ +PHPAPI char *php_escape_html_entities(unsigned char *old, int oldlen, int *newlen, int all, int quote_style, char *hint_charset TSRMLS_DC) +{ + int i, j, maxlen, len; + char *replaced; + enum entity_charset charset = determine_charset(hint_charset TSRMLS_CC); + int matches_map; + + maxlen = 2 * oldlen; + if (maxlen < 128) + maxlen = 128; + replaced = emalloc (maxlen); + len = 0; + + i = 0; + while (i < oldlen) { + unsigned char mbsequence[16]; /* allow up to 15 characters in a multibyte sequence */ + int mbseqlen = sizeof(mbsequence); + unsigned short this_char = get_next_char(charset, old, &i, mbsequence, &mbseqlen); + + matches_map = 0; + + if (len + 9 > maxlen) + replaced = erealloc (replaced, maxlen += 128); + + if (all) { + /* look for a match in the maps for this charset */ + unsigned char *rep = NULL; + + + for (j = 0; entity_map[j].charset != cs_terminator; j++) { + if (entity_map[j].charset == charset + && this_char >= entity_map[j].basechar + && this_char <= entity_map[j].endchar) { + rep = (unsigned char*)entity_map[j].table[this_char - entity_map[j].basechar]; + if (rep == NULL) { + /* there is no entity for this position; fall through and + * just output the character itself */ + break; + } + + matches_map = 1; + break; + } + } + + if (matches_map) { + replaced[len++] = '&'; + strcpy(replaced + len, rep); + len += strlen(rep); + replaced[len++] = ';'; + } + } + if (!matches_map) { + int is_basic = 0; + + if (this_char == '&') { + memcpy(replaced + len, "&", sizeof("&") - 1); + len += sizeof("&") - 1; + is_basic = 1; + } else { + for (j = 0; basic_entities[j].charcode != 0; j++) { + if ((basic_entities[j].charcode != this_char) || + (basic_entities[j].flags && + (quote_style & basic_entities[j].flags) == 0)) { + continue; + } + + memcpy(replaced + len, basic_entities[j].entity, basic_entities[j].entitylen); + len += basic_entities[j].entitylen; + + is_basic = 1; + break; + } + } + + if (!is_basic) { + /* a wide char without a named entity; pass through the original sequence */ + if (mbseqlen > 1) { + memcpy(replaced + len, mbsequence, mbseqlen); + len += mbseqlen; + } else { + replaced[len++] = (unsigned char)this_char; + } + } + } + } + replaced[len] = '\0'; + *newlen = len; + + return replaced; + + +} +/* }}} */ + +/* {{{ php_html_entities + */ +static void php_html_entities(INTERNAL_FUNCTION_PARAMETERS, int all) +{ + char *str, *hint_charset = NULL; + int str_len, hint_charset_len = 0; + int len; + long quote_style = ENT_COMPAT; + char *replaced; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len, "e_style, &hint_charset, &hint_charset_len) == FAILURE) { + return; + } + + replaced = php_escape_html_entities(str, str_len, &len, all, quote_style, hint_charset TSRMLS_CC); + RETVAL_STRINGL(replaced, len, 0); +} +/* }}} */ + +#define HTML_SPECIALCHARS 0 +#define HTML_ENTITIES 1 + +/* {{{ register_html_constants + */ +void register_html_constants(INIT_FUNC_ARGS) +{ + REGISTER_LONG_CONSTANT("HTML_SPECIALCHARS", HTML_SPECIALCHARS, CONST_PERSISTENT|CONST_CS); + REGISTER_LONG_CONSTANT("HTML_ENTITIES", HTML_ENTITIES, CONST_PERSISTENT|CONST_CS); + REGISTER_LONG_CONSTANT("ENT_COMPAT", ENT_COMPAT, CONST_PERSISTENT|CONST_CS); + REGISTER_LONG_CONSTANT("ENT_QUOTES", ENT_QUOTES, CONST_PERSISTENT|CONST_CS); + REGISTER_LONG_CONSTANT("ENT_NOQUOTES", ENT_NOQUOTES, CONST_PERSISTENT|CONST_CS); +} +/* }}} */ + +/* {{{ proto string htmlspecialchars(string string [, int quote_style][, string charset]) + Convert special characters to HTML entities */ +PHP_FUNCTION(htmlspecialchars) +{ + php_html_entities(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto string htmlspecialchars(string string [, int quote_style]) + Convert special HTML entities back to characters */ +PHP_FUNCTION(htmlspecialchars_decode) +{ + char *str, *new_str, *e, *p; + int len, j, i, new_len; + long quote_style = ENT_COMPAT; + struct basic_entities_dec basic_entities_dec[8]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, "e_style) == FAILURE) { + return; + } + + new_str = estrndup(str, len); + new_len = len; + e = new_str + new_len; + + if (!(p = memchr(new_str, '&', new_len))) { + RETURN_STRINGL(new_str, new_len, 0); + } + + for (j = 0, i = 0; basic_entities[i].charcode != 0; i++) { + if (basic_entities[i].flags && !(quote_style & basic_entities[i].flags)) { + continue; + } + basic_entities_dec[j].charcode = basic_entities[i].charcode; + memcpy(basic_entities_dec[j].entity, basic_entities[i].entity, basic_entities[i].entitylen + 1); + basic_entities_dec[j].entitylen = basic_entities[i].entitylen; + j++; + } + basic_entities_dec[j].charcode = '&'; + basic_entities_dec[j].entitylen = sizeof("&") - 1; + memcpy(basic_entities_dec[j].entity, "&", sizeof("&")); + i = j + 1; + + do { + int l = e - p; + + for (j = 0; j < i; j++) { + if (basic_entities_dec[j].entitylen > l) { + continue; + } + if (!memcmp(p, basic_entities_dec[j].entity, basic_entities_dec[j].entitylen)) { + int e_len = basic_entities_dec[j].entitylen - 1; + + *p++ = basic_entities_dec[j].charcode; + memmove(p, p + e_len, (e - p - e_len)); + e -= e_len; + goto done; + } + } + p++; + +done: + if (p >= e) { + break; + } + } while ((p = memchr(p, '&', (e - p)))); + + new_len = e - new_str; + + new_str[new_len] = '\0'; + RETURN_STRINGL(new_str, new_len, 0); +} +/* }}} */ + +/* {{{ proto string html_entity_decode(string string [, int quote_style][, string charset]) + Convert all HTML entities to their applicable characters */ +PHP_FUNCTION(html_entity_decode) +{ + char *str, *hint_charset = NULL; + int str_len, hint_charset_len, len; + long quote_style = ENT_COMPAT; + char *replaced; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len, + "e_style, &hint_charset, &hint_charset_len) == FAILURE) { + return; + } + + replaced = php_unescape_html_entities(str, str_len, &len, 1, quote_style, hint_charset TSRMLS_CC); + RETVAL_STRINGL(replaced, len, 0); +} +/* }}} */ + + +/* {{{ proto string htmlentities(string string [, int quote_style][, string charset]) + Convert all applicable characters to HTML entities */ +PHP_FUNCTION(htmlentities) +{ + php_html_entities(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto array get_html_translation_table([int table [, int quote_style]]) + Returns the internal translation table used by htmlspecialchars and htmlentities */ +PHP_FUNCTION(get_html_translation_table) +{ + long which = HTML_SPECIALCHARS, quote_style = ENT_COMPAT; + int i, j; + char ind[2]; + enum entity_charset charset = determine_charset(NULL TSRMLS_CC); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll", &which, "e_style) == FAILURE) { + return; + } + + array_init(return_value); + + ind[1] = 0; + + switch (which) { + case HTML_ENTITIES: + for (j=0; entity_map[j].charset != cs_terminator; j++) { + if (entity_map[j].charset != charset) + continue; + for (i = 0; i <= entity_map[j].endchar - entity_map[j].basechar; i++) { + char buffer[16]; + + if (entity_map[j].table[i] == NULL) + continue; + /* what about wide chars here ?? */ + ind[0] = i + entity_map[j].basechar; + sprintf(buffer, "&%s;", entity_map[j].table[i]); + add_assoc_string(return_value, ind, buffer, 1); + + } + } + /* break thru */ + + case HTML_SPECIALCHARS: + for (j = 0; basic_entities[j].charcode != 0; j++) { + + if (basic_entities[j].flags && (quote_style & basic_entities[j].flags) == 0) + continue; + + ind[0] = (unsigned char)basic_entities[j].charcode; + add_assoc_stringl(return_value, ind, basic_entities[j].entity, basic_entities[j].entitylen, 1); + } + add_assoc_stringl(return_value, "&", "&", sizeof("&") - 1, 1); + + break; + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/920358_bd306c_ext#dom#xpath.c b/src/main/resource/testFiles/php-src/revFiles/920358_bd306c_ext#dom#xpath.c new file mode 100644 index 0000000..76767fa --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/920358_bd306c_ext#dom#xpath.c @@ -0,0 +1,269 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Christian Stocker | + | Rob Richards | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#if HAVE_LIBXML && HAVE_DOM +#include "php_dom.h" + + +/* +* class domxpath +*/ + +#if defined(LIBXML_XPATH_ENABLED) + +zend_function_entry php_dom_xpath_class_functions[] = { + PHP_FALIAS(domxpath, dom_xpath_xpath, NULL) + PHP_FALIAS(register_namespace, dom_xpath_register_ns, NULL) + PHP_FALIAS(query, dom_xpath_query, NULL) + {NULL, NULL, NULL} +}; + +/* {{{ proto domxpath dom_xpath_xpath(domDocument doc); */ +PHP_FUNCTION(dom_xpath_xpath) +{ + zval *id, *doc; + xmlDocPtr docp = NULL; + dom_object *docobj, *intern; + xmlXPathContextPtr ctx, oldctx; + + id = getThis(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &doc) == FAILURE) { + return; + } + + DOM_GET_OBJ(docp, doc, xmlDocPtr, docobj); + + ctx = xmlXPathNewContext(docp); + if (ctx == NULL) { + RETURN_FALSE; + } + + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + if (intern != NULL) { + oldctx = (xmlXPathContextPtr)intern->ptr; + if (oldctx != NULL) { + php_libxml_decrement_doc_ref((php_libxml_node_object *)intern TSRMLS_CC); + xmlXPathFreeContext(oldctx); + } + intern->ptr = ctx; + intern->document = docobj->document; + php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp TSRMLS_CC); + } +} +/* }}} end dom_xpath_xpath */ + +/* {{{ proto domdocument document document */ +int dom_xpath_document_read(dom_object *obj, zval **retval TSRMLS_DC) +{ + xmlDoc *docp = NULL; + xmlXPathContextPtr ctx; + int ret; + + ctx = (xmlXPathContextPtr) obj->ptr; + + if (ctx) { + docp = (xmlDocPtr) ctx->doc; + } else { + printf("NONE"); + } + + ALLOC_ZVAL(*retval); + if (NULL == (*retval = php_dom_create_object((xmlNodePtr) docp, &ret, NULL, *retval, obj TSRMLS_CC))) { + php_error(E_WARNING, "Cannot create required DOM object"); + return FAILURE; + } + return SUCCESS; +} + +/* {{{ proto boolean dom_xpath_register_ns(string prefix, string uri); */ +PHP_FUNCTION(dom_xpath_register_ns) +{ + zval *id; + xmlXPathContextPtr ctxp; + int prefix_len, ns_uri_len; + dom_object *intern; + unsigned char *prefix, *ns_uri; + + DOM_GET_THIS(id); + + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + + ctxp = (xmlXPathContextPtr) intern->ptr; + if (ctxp == NULL) { + php_error(E_WARNING, "Invalid XPath Context"); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &prefix, &prefix_len, &ns_uri, &ns_uri_len) == FAILURE) { + RETURN_FALSE; + } + + if (xmlXPathRegisterNs(ctxp, prefix, ns_uri) != 0) { + RETURN_FALSE + } + RETURN_TRUE; +} + +static void dom_xpath_iter(zval *baseobj, dom_object *intern) +{ + dom_nnodemap_object *mapptr; + + mapptr = (dom_nnodemap_object *)intern->ptr; + mapptr->baseobjptr = baseobj; + mapptr->nodetype = DOM_NODESET; + +} + +/* {{{ proto domnodelist dom_xpath_query(string expr [,domNode context]); */ +PHP_FUNCTION(dom_xpath_query) +{ + zval *id, *retval, *context = NULL; + xmlXPathContextPtr ctxp; + xmlNodePtr nodep = NULL; + xmlXPathObjectPtr xpathobjp; + int expr_len, ret, nsnbr = 0; + dom_object *intern, *nodeobj; + char *expr; + xmlDoc *docp = NULL; + xmlNsPtr *ns; + + DOM_GET_THIS(id); + + intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC); + + ctxp = (xmlXPathContextPtr) intern->ptr; + if (ctxp == NULL) { + php_error(E_WARNING, "Invalid XPath Context"); + RETURN_FALSE; + } + + docp = (xmlDocPtr) ctxp->doc; + if (docp == NULL) { + php_error(E_WARNING, "Invalid XPath Document Pointer"); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|o", &expr, &expr_len, &context) == FAILURE) { + RETURN_FALSE; + } + + if (context != NULL) { + DOM_GET_OBJ(nodep, context, xmlNodePtr, nodeobj); + } + + if (!nodep) { + nodep = xmlDocGetRootElement(docp); + } + + if (nodep && docp != nodep->doc) { + php_error(E_WARNING, "Node From Wrong Document"); + RETURN_FALSE; + } + + ctxp->node = nodep; + + /* Register namespaces in the node */ + ns = xmlGetNsList(docp, nodep); + + if (ns != NULL) { + while (ns[nsnbr] != NULL) + nsnbr++; + } + + + ctxp->namespaces = ns; + ctxp->nsNr = nsnbr; + + xpathobjp = xmlXPathEvalExpression(expr, ctxp); + ctxp->node = NULL; + + if (ns != NULL) { + xmlFree(ns); + ctxp->namespaces = NULL; + ctxp->nsNr = 0; + } + + if (! xpathobjp) { + RETURN_FALSE; + } + + + if (xpathobjp->type == XPATH_NODESET) { + int i; + xmlNodeSetPtr nodesetp; + + if (NULL == (nodesetp = xpathobjp->nodesetval)) { + xmlXPathFreeObject (xpathobjp); + RETURN_FALSE; + } + + MAKE_STD_ZVAL(retval); + array_init(retval); + + for (i = 0; i < nodesetp->nodeNr; i++) { + xmlNodePtr node = nodesetp->nodeTab[i]; + zval *child; + + MAKE_STD_ZVAL(child); + + if (node->type == XML_NAMESPACE_DECL) { + xmlNsPtr curns; + xmlNodePtr nsparent; + + nsparent = node->_private; + curns = xmlNewNs(NULL, node->name, NULL); + if (node->children) { + curns->prefix = xmlStrdup((char *) node->children); + } + if (node->children) { + node = xmlNewDocNode(docp, NULL, (char *) node->children, node->name); + } else { + node = xmlNewDocNode(docp, NULL, "xmlns", node->name); + } + node->type = XML_NAMESPACE_DECL; + node->parent = nsparent; + node->ns = curns; + } + child = php_dom_create_object(node, &ret, NULL, child, intern TSRMLS_CC); + add_next_index_zval(retval, child); + } + } else { + RETURN_FALSE; + } + + php_dom_create_interator(return_value, DOM_NODELIST TSRMLS_CC); + intern = (dom_object *)zend_objects_get_address(return_value TSRMLS_CC); + dom_xpath_iter(retval, intern); + + xmlXPathFreeObject(xpathobjp); +} +/* }}} end dom_xpath_query */ + +#endif /* LIBXML_XPATH_ENABLED */ + +/* }}} */ +#endif diff --git a/src/main/resource/testFiles/php-src/revFiles/9baa92_f9c232_sapi#apache2handler#sapi_apache2.c b/src/main/resource/testFiles/php-src/revFiles/9baa92_f9c232_sapi#apache2handler#sapi_apache2.c new file mode 100644 index 0000000..87e0bf1 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/9baa92_f9c232_sapi#apache2handler#sapi_apache2.c @@ -0,0 +1,593 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Sascha Schumann | + | Parts based on Apache 1.3 SAPI module by | + | Rasmus Lerdorf and Zeev Suraski | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS + +#include "php.h" +#include "php_main.h" +#include "php_ini.h" +#include "php_variables.h" +#include "SAPI.h" + +#include + +#include "ext/standard/php_smart_str.h" +#ifndef NETWARE +#include "ext/standard/php_standard.h" +#else +#include "ext/standard/basic_functions.h" +#endif + +#include "apr_strings.h" +#include "ap_config.h" +#include "util_filter.h" +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#include "http_core.h" +#include "ap_mpm.h" + +#include "php_apache.h" + +/* UnixWare and Netware define shutdown to _shutdown, which causes problems later + * on when using a structure member named shutdown. Since this source + * file does not use the system call shutdown, it is safe to #undef it.K + */ +#undef shutdown + +#define PHP_MAGIC_TYPE "application/x-httpd-php" +#define PHP_SOURCE_MAGIC_TYPE "application/x-httpd-php-source" +#define PHP_SCRIPT "php5-script" + +/* A way to specify the location of the php.ini dir in an apache directive */ +char *apache2_php_ini_path_override = NULL; + +static int +php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC) +{ + request_rec *r; + php_struct *ctx; + + ctx = SG(server_context); + r = ctx->r; + + if (ap_rwrite(str, str_length, r) < 0) { + php_handle_aborted_connection(); + } + + return str_length; /* we always consume all the data passed to us. */ +} + +static int +php_apache_sapi_header_handler(sapi_header_struct *sapi_header,sapi_headers_struct *sapi_headers TSRMLS_DC) +{ + php_struct *ctx; + char *val; + + ctx = SG(server_context); + + val = strchr(sapi_header->header, ':'); + + if (!val) { + sapi_free_header(sapi_header); + return 0; + } + + *val = '\0'; + + do { + val++; + } while (*val == ' '); + + if (!strcasecmp(sapi_header->header, "content-type")) { + val = apr_pstrdup(ctx->r->pool, val); + ap_set_content_type(ctx->r, val); + } else if (sapi_header->replace) { + apr_table_set(ctx->r->headers_out, sapi_header->header, val); + } else { + apr_table_add(ctx->r->headers_out, sapi_header->header, val); + } + + sapi_free_header(sapi_header); + + return 0; +} + +static int +php_apache_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) +{ + php_struct *ctx = SG(server_context); + + ctx->r->status = SG(sapi_headers).http_response_code; + + return SAPI_HEADER_SENT_SUCCESSFULLY; +} + +static int +php_apache_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC) +{ + apr_size_t len, tlen=0; + php_struct *ctx = SG(server_context); + request_rec *r; + apr_bucket_brigade *brigade; + + r = ctx->r; + brigade = ctx->brigade; + len = count_bytes; + + /* + * This loop is needed because ap_get_brigade() can return us partial data + * which would cause premature termination of request read. Therefor we + * need to make sure that if data is avaliable we fill the buffer completely. + */ + + while (ap_get_brigade(r->input_filters, brigade, AP_MODE_READBYTES, APR_BLOCK_READ, len) == APR_SUCCESS) { + apr_brigade_flatten(brigade, buf, &len); + apr_brigade_cleanup(brigade); + tlen += len; + if (tlen == count_bytes || !len) { + break; + } + buf += len; + len = count_bytes - tlen; + } + + return tlen; +} + +static struct stat* +php_apache_sapi_get_stat(TSRMLS_D) +{ + php_struct *ctx = SG(server_context); + + ctx->finfo.st_uid = ctx->r->finfo.user; + ctx->finfo.st_gid = ctx->r->finfo.group; + ctx->finfo.st_dev = ctx->r->finfo.device; + ctx->finfo.st_ino = ctx->r->finfo.inode; +#if defined(NETWARE) && defined(CLIB_STAT_PATCH) + ctx->finfo.st_atime.tv_sec = apr_time_sec(ctx->r->finfo.atime); + ctx->finfo.st_mtime.tv_sec = apr_time_sec(ctx->r->finfo.mtime); + ctx->finfo.st_ctime.tv_sec = apr_time_sec(ctx->r->finfo.ctime); +#else + ctx->finfo.st_atime = apr_time_sec(ctx->r->finfo.atime); + ctx->finfo.st_mtime = apr_time_sec(ctx->r->finfo.mtime); + ctx->finfo.st_ctime = apr_time_sec(ctx->r->finfo.ctime); +#endif + + ctx->finfo.st_size = ctx->r->finfo.size; + ctx->finfo.st_nlink = ctx->r->finfo.nlink; + + return &ctx->finfo; +} + +static char * +php_apache_sapi_read_cookies(TSRMLS_D) +{ + php_struct *ctx = SG(server_context); + const char *http_cookie; + + http_cookie = apr_table_get(ctx->r->headers_in, "cookie"); + + /* The SAPI interface should use 'const char *' */ + return (char *) http_cookie; +} + +static char * +php_apache_sapi_getenv(char *name, size_t name_len TSRMLS_DC) +{ + php_struct *ctx = SG(server_context); + const char *env_var; + + env_var = apr_table_get(ctx->r->subprocess_env, name); + + return (char *) env_var; +} + +static void +php_apache_sapi_register_variables(zval *track_vars_array TSRMLS_DC) +{ + php_struct *ctx = SG(server_context); + const apr_array_header_t *arr = apr_table_elts(ctx->r->subprocess_env); + char *key, *val; + + APR_ARRAY_FOREACH_OPEN(arr, key, val) + if (!val) val = ""; + php_register_variable(key, val, track_vars_array TSRMLS_CC); + APR_ARRAY_FOREACH_CLOSE() + + php_register_variable("PHP_SELF", ctx->r->uri, track_vars_array TSRMLS_CC); +} + +static void +php_apache_sapi_flush(void *server_context) +{ + php_struct *ctx; + request_rec *r; + TSRMLS_FETCH(); + + ctx = server_context; + + /* If we haven't registered a server_context yet, + * then don't bother flushing. */ + if (!server_context) { + return; + } + + r = ctx->r; + + sapi_send_headers(TSRMLS_C); + + r->status = SG(sapi_headers).http_response_code; + SG(headers_sent) = 1; + + if (ap_rflush(r) < 0 || r->connection->aborted) { + php_handle_aborted_connection(); + } +} + +static void php_apache_sapi_log_message(char *msg) +{ + php_struct *ctx; + TSRMLS_FETCH(); + + ctx = SG(server_context); + + /* We use APLOG_STARTUP because it keeps us from printing the + * data and time information at the beginning of the error log + * line. Not sure if this is correct, but it mirrors what happens + * with Apache 1.3 -- rbb + */ + if (ctx == NULL) { /* we haven't initialized our ctx yet, oh well */ + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL, "%s", msg); + } else { + ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, ctx->r, "%s", msg); + } +} + +static void php_apache_sapi_log_message_ex(char *msg, request_rec *r) +{ + if (r) { + ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, r, msg, r->filename); + } else { + php_apache_sapi_log_message(msg); + } +} + +static time_t php_apache_sapi_get_request_time(TSRMLS_D) { + php_struct *ctx = SG(server_context); + return apr_time_sec(ctx->r->request_time); +} + +extern zend_module_entry php_apache_module; + +static int php_apache2_startup(sapi_module_struct *sapi_module) +{ + if (php_module_startup(sapi_module, &php_apache_module, 1)==FAILURE) { + return FAILURE; + } + return SUCCESS; +} + +static sapi_module_struct apache2_sapi_module = { + "apache2handler", + "Apache 2.0 Handler", + + php_apache2_startup, /* startup */ + php_module_shutdown_wrapper, /* shutdown */ + + NULL, /* activate */ + NULL, /* deactivate */ + + php_apache_sapi_ub_write, /* unbuffered write */ + php_apache_sapi_flush, /* flush */ + php_apache_sapi_get_stat, /* get uid */ + php_apache_sapi_getenv, /* getenv */ + + php_error, /* error handler */ + + php_apache_sapi_header_handler, /* header handler */ + php_apache_sapi_send_headers, /* send headers handler */ + NULL, /* send header handler */ + + php_apache_sapi_read_post, /* read POST data */ + php_apache_sapi_read_cookies, /* read Cookies */ + + php_apache_sapi_register_variables, + php_apache_sapi_log_message, /* Log message */ + php_apache_sapi_get_request_time, /* Request Time */ + + STANDARD_SAPI_MODULE_PROPERTIES +}; + +static apr_status_t +php_apache_server_shutdown(void *tmp) +{ + apache2_sapi_module.shutdown(&apache2_sapi_module); + sapi_shutdown(); +#ifdef ZTS + tsrm_shutdown(); +#endif + return APR_SUCCESS; +} + +static void php_apache_add_version(apr_pool_t *p) +{ + TSRMLS_FETCH(); + if (PG(expose_php)) { + ap_add_version_component(p, "PHP/" PHP_VERSION); + } +} + +static int php_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) +{ +#ifndef ZTS + int threaded_mpm; + + ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm); + if(threaded_mpm) { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, 0, "Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe. You need to recompile PHP."); + return DONE; + } +#endif + /* When this is NULL, apache won't override the hard-coded default + * php.ini path setting. */ + apache2_php_ini_path_override = NULL; + return OK; +} + +static int +php_apache_server_startup(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) +{ + void *data = NULL; + const char *userdata_key = "apache2hook_post_config"; + + /* Apache will load, unload and then reload a DSO module. This + * prevents us from starting PHP until the second load. */ + apr_pool_userdata_get(&data, userdata_key, s->process->pool); + if (data == NULL) { + /* We must use set() here and *not* setn(), otherwise the + * static string pointed to by userdata_key will be mapped + * to a different location when the DSO is reloaded and the + * pointers won't match, causing get() to return NULL when + * we expected it to return non-NULL. */ + apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool); + return OK; + } + + /* Set up our overridden path. */ + if (apache2_php_ini_path_override) { + apache2_sapi_module.php_ini_path_override = apache2_php_ini_path_override; + } +#ifdef ZTS + tsrm_startup(1, 1, 0, NULL); +#endif + sapi_startup(&apache2_sapi_module); + apache2_sapi_module.startup(&apache2_sapi_module); + apr_pool_cleanup_register(pconf, NULL, php_apache_server_shutdown, apr_pool_cleanup_null); + php_apache_add_version(pconf); + + return OK; +} + +static apr_status_t php_server_context_cleanup(void *data_) +{ + void **data = data_; + *data = NULL; + return APR_SUCCESS; +} + +static int php_apache_request_ctor(request_rec *r, php_struct *ctx TSRMLS_DC) +{ + char *content_type; + char *content_length; + const char *auth; + + SG(sapi_headers).http_response_code = !r->status ? HTTP_OK : r->status; + SG(request_info).content_type = apr_table_get(r->headers_in, "Content-Type"); + SG(request_info).query_string = apr_pstrdup(r->pool, r->args); + SG(request_info).request_method = r->method; + SG(request_info).request_uri = apr_pstrdup(r->pool, r->uri); + SG(request_info).path_translated = apr_pstrdup(r->pool, r->filename); + r->no_local_copy = 1; + + content_type = sapi_get_default_content_type(TSRMLS_C); + ap_set_content_type(r, apr_pstrdup(r->pool, content_type)); + efree(content_type); + + content_length = (char *) apr_table_get(r->headers_in, "Content-Length"); + SG(request_info).content_length = (content_length ? atoi(content_length) : 0); + + apr_table_unset(r->headers_out, "Content-Length"); + apr_table_unset(r->headers_out, "Last-Modified"); + apr_table_unset(r->headers_out, "Expires"); + apr_table_unset(r->headers_out, "ETag"); + if (!PG(safe_mode) || (PG(safe_mode) && !ap_auth_type(r))) { + auth = apr_table_get(r->headers_in, "Authorization"); + php_handle_auth_data(auth TSRMLS_CC); + ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user); + } else { + SG(request_info).auth_user = NULL; + SG(request_info).auth_password = NULL; + } + return php_request_startup(TSRMLS_C); +} + +static void php_apache_request_dtor(request_rec *r TSRMLS_DC) +{ + php_request_shutdown(NULL); +} + +static int php_handler(request_rec *r) +{ + php_struct *ctx; + void *conf; + apr_bucket_brigade *brigade; + apr_bucket *bucket; + apr_status_t rv; + request_rec *parent_req = NULL; + TSRMLS_FETCH(); + + conf = ap_get_module_config(r->per_dir_config, &php5_module); + apply_config(conf); + + if (strcmp(r->handler, PHP_MAGIC_TYPE) && strcmp(r->handler, PHP_SOURCE_MAGIC_TYPE) && strcmp(r->handler, PHP_SCRIPT)) { + /* Check for xbithack in this case. */ + if (!AP2(xbithack) || strcmp(r->handler, "text/html") || !(r->finfo.protection & APR_UEXECUTE)) { + zend_try { + zend_ini_deactivate(TSRMLS_C); + } zend_end_try(); + return DECLINED; + } + } + + /* handle situations where user turns the engine off */ + if (!AP2(engine)) { + zend_try { + zend_ini_deactivate(TSRMLS_C); + } zend_end_try(); + return DECLINED; + } + + if (r->finfo.filetype == 0) { + php_apache_sapi_log_message_ex("script '%s' not found or unable to stat", r); + zend_try { + zend_ini_deactivate(TSRMLS_C); + } zend_end_try(); + return HTTP_NOT_FOUND; + } + if (r->finfo.filetype == APR_DIR) { + php_apache_sapi_log_message_ex("attempt to invoke directory '%s' as script", r); + zend_try { + zend_ini_deactivate(TSRMLS_C); + } zend_end_try(); + return HTTP_FORBIDDEN; + } + + /* Setup the CGI variables if this is the main request */ + if (r->main == NULL || + /* .. or if the sub-request envinronment differs from the main-request. */ + r->subprocess_env != r->main->subprocess_env + ) { + /* setup standard CGI variables */ + ap_add_common_vars(r); + ap_add_cgi_vars(r); + } + +zend_first_try { + + ctx = SG(server_context); + if (ctx == NULL) { + ctx = SG(server_context) = apr_pcalloc(r->pool, sizeof(*ctx)); + /* register a cleanup so we clear out the SG(server_context) + * after each request. Note: We pass in the pointer to the + * server_context in case this is handled by a different thread. + */ + apr_pool_cleanup_register(r->pool, (void *)&SG(server_context), php_server_context_cleanup, apr_pool_cleanup_null); + + ctx->r = r; + brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc); + ctx->brigade = brigade; + + if (php_apache_request_ctor(r, ctx TSRMLS_CC)!=SUCCESS) { + zend_bailout(); + } + } else { + parent_req = ctx->r; + ctx->r = r; + brigade = ctx->brigade; + } + + if (AP2(last_modified)) { + ap_update_mtime(r, r->finfo.mtime); + ap_set_last_modified(r); + } + + /* Determine if we need to parse the file or show the source */ + if (strncmp(r->handler, PHP_SOURCE_MAGIC_TYPE, sizeof(PHP_SOURCE_MAGIC_TYPE) - 1) == 0) { + zend_syntax_highlighter_ini syntax_highlighter_ini; + php_get_highlight_struct(&syntax_highlighter_ini); + highlight_file((char *)r->filename, &syntax_highlighter_ini TSRMLS_CC); + } else { + zend_file_handle zfd; + + zfd.type = ZEND_HANDLE_FILENAME; + zfd.filename = (char *) r->filename; + zfd.free_filename = 0; + zfd.opened_path = NULL; + + if (!parent_req) { + php_execute_script(&zfd TSRMLS_CC); + } else { + zend_execute_scripts(ZEND_INCLUDE TSRMLS_CC, NULL, 1, &zfd); + } +#if MEMORY_LIMIT + { + char *mem_usage; + + mem_usage = apr_psprintf(ctx->r->pool, "%u", AG(allocated_memory_peak)); + apr_table_set(r->notes, "mod_php_memory_usage", mem_usage); + } +#endif + } + +} zend_end_try(); + + if (!parent_req) { + php_apache_request_dtor(r TSRMLS_CC); + ctx->request_processed = 1; + bucket = apr_bucket_eos_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(brigade, bucket); + + rv = ap_pass_brigade(r->output_filters, brigade); + if (rv != APR_SUCCESS || r->connection->aborted) { +zend_first_try { + php_handle_aborted_connection(); +} zend_end_try(); + } + apr_brigade_cleanup(brigade); + } else { + ctx->r = parent_req; + } + + return OK; +} + +void php_ap2_register_hook(apr_pool_t *p) +{ + ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/a0a56e_7afbb7_ext#com_dotnet#com_extension.c b/src/main/resource/testFiles/php-src/revFiles/a0a56e_7afbb7_ext#com_dotnet#com_extension.c new file mode 100644 index 0000000..d0ec3bb --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/a0a56e_7afbb7_ext#com_dotnet#com_extension.c @@ -0,0 +1,363 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_com_dotnet.h" +#include "php_com_dotnet_internal.h" +#include "Zend/zend_exceptions.h" + +ZEND_DECLARE_MODULE_GLOBALS(com_dotnet) +static PHP_GINIT_FUNCTION(com_dotnet); + +TsHashTable php_com_typelibraries; + +zend_class_entry + *php_com_variant_class_entry, + *php_com_exception_class_entry, + *php_com_saproxy_class_entry; + +zend_function_entry com_dotnet_functions[] = { + PHP_FE(variant_set, NULL) + PHP_FE(variant_add, NULL) + PHP_FE(variant_cat, NULL) + PHP_FE(variant_sub, NULL) + PHP_FE(variant_mul, NULL) + PHP_FE(variant_and, NULL) + PHP_FE(variant_div, NULL) + PHP_FE(variant_eqv, NULL) + PHP_FE(variant_idiv, NULL) + PHP_FE(variant_imp, NULL) + PHP_FE(variant_mod, NULL) + PHP_FE(variant_or, NULL) + PHP_FE(variant_pow, NULL) + PHP_FE(variant_xor, NULL) + PHP_FE(variant_abs, NULL) + PHP_FE(variant_fix, NULL) + PHP_FE(variant_int, NULL) + PHP_FE(variant_neg, NULL) + PHP_FE(variant_not, NULL) + PHP_FE(variant_round, NULL) + PHP_FE(variant_cmp, NULL) + PHP_FE(variant_date_to_timestamp, NULL) + PHP_FE(variant_date_from_timestamp, NULL) + PHP_FE(variant_get_type, NULL) + PHP_FE(variant_set_type, NULL) + PHP_FE(variant_cast, NULL) + /* com_com.c */ + PHP_FE(com_create_guid, NULL) + PHP_FE(com_event_sink, NULL) + PHP_FE(com_print_typeinfo, NULL) + PHP_FE(com_message_pump, NULL) + PHP_FE(com_load_typelib, NULL) + PHP_FE(com_get_active_object, NULL) + { NULL, NULL, NULL } +}; + +/* {{{ com_dotnet_module_entry + */ +zend_module_entry com_dotnet_module_entry = { + STANDARD_MODULE_HEADER, + "com_dotnet", + com_dotnet_functions, + PHP_MINIT(com_dotnet), + PHP_MSHUTDOWN(com_dotnet), + PHP_RINIT(com_dotnet), + PHP_RSHUTDOWN(com_dotnet), + PHP_MINFO(com_dotnet), + "0.1", + PHP_MODULE_GLOBALS(com_dotnet), + PHP_GINIT(com_dotnet), + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX +}; +/* }}} */ + +#ifdef COMPILE_DL_COM_DOTNET +ZEND_GET_MODULE(com_dotnet) +#endif + +/* {{{ PHP_INI + */ + +/* com.typelib_file is the path to a file containing a + * list of typelibraries to register *persistently*. + * lines starting with ; are comments + * append #cis to end of typelib name to cause its constants + * to be loaded case insensitively */ +static PHP_INI_MH(OnTypeLibFileUpdate) +{ + FILE *typelib_file; + char *typelib_name_buffer; + char *strtok_buf = NULL; + int cached; + + if (!new_value || !new_value[0] || (typelib_file = VCWD_FOPEN(new_value, "r"))==NULL) { + return FAILURE; + } + + typelib_name_buffer = (char *) emalloc(sizeof(char)*1024); + + while (fgets(typelib_name_buffer, 1024, typelib_file)) { + ITypeLib *pTL; + char *typelib_name; + char *modifier, *ptr; + int mode = CONST_CS | CONST_PERSISTENT; /* CONST_PERSISTENT is ok here */ + + if (typelib_name_buffer[0]==';') { + continue; + } + typelib_name = php_strtok_r(typelib_name_buffer, "\r\n", &strtok_buf); /* get rid of newlines */ + if (typelib_name == NULL) { + continue; + } + typelib_name = php_strtok_r(typelib_name, "#", &strtok_buf); + modifier = php_strtok_r(NULL, "#", &strtok_buf); + if (modifier != NULL) { + if (!strcmp(modifier, "cis") || !strcmp(modifier, "case_insensitive")) { + mode &= ~CONST_CS; + } + } + + /* Remove leading/training white spaces on search_string */ + while (isspace(*typelib_name)) {/* Ends on '\0' in worst case */ + typelib_name ++; + } + ptr = typelib_name + strlen(typelib_name) - 1; + while ((ptr != typelib_name) && isspace(*ptr)) { + *ptr = '\0'; + ptr--; + } + + if ((pTL = php_com_load_typelib_via_cache(typelib_name, COMG(code_page), &cached TSRMLS_CC)) != NULL) { + if (!cached) { + php_com_import_typelib(pTL, mode, COMG(code_page) TSRMLS_CC); + } + ITypeLib_Release(pTL); + } + } + + efree(typelib_name_buffer); + fclose(typelib_file); + + return SUCCESS; +} + +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("com.allow_dcom", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_dcom, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.autoregister_verbose", "0", PHP_INI_ALL, OnUpdateBool, autoreg_verbose, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.autoregister_typelib", "0", PHP_INI_ALL, OnUpdateBool, autoreg_on, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.autoregister_casesensitive", "1", PHP_INI_ALL, OnUpdateBool, autoreg_case_sensitive, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.code_page", "", PHP_INI_ALL, OnUpdateLong, code_page, zend_com_dotnet_globals, com_dotnet_globals) + PHP_INI_ENTRY("com.typelib_file", "", PHP_INI_SYSTEM, OnTypeLibFileUpdate) +PHP_INI_END() +/* }}} */ + +/* {{{ PHP_GINIT_FUNCTION + */ +static PHP_GINIT_FUNCTION(com_dotnet) +{ + memset(com_dotnet_globals, 0, sizeof(*com_dotnet_globals)); + com_dotnet_globals->code_page = CP_ACP; +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(com_dotnet) +{ + zend_class_entry ce, *tmp; + + php_com_wrapper_minit(INIT_FUNC_ARGS_PASSTHRU); + php_com_persist_minit(INIT_FUNC_ARGS_PASSTHRU); + + INIT_CLASS_ENTRY(ce, "com_exception", NULL); + php_com_exception_class_entry = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC); + php_com_exception_class_entry->ce_flags |= ZEND_ACC_FINAL; +/* php_com_exception_class_entry->constructor->common.fn_flags |= ZEND_ACC_PROTECTED; */ + + INIT_CLASS_ENTRY(ce, "com_safearray_proxy", NULL); + php_com_saproxy_class_entry = zend_register_internal_class(&ce TSRMLS_CC); + php_com_saproxy_class_entry->ce_flags |= ZEND_ACC_FINAL; +/* php_com_saproxy_class_entry->constructor->common.fn_flags |= ZEND_ACC_PROTECTED; */ + php_com_saproxy_class_entry->get_iterator = php_com_saproxy_iter_get; + + INIT_CLASS_ENTRY(ce, "variant", NULL); + ce.create_object = php_com_object_new; + php_com_variant_class_entry = zend_register_internal_class(&ce TSRMLS_CC); + php_com_variant_class_entry->get_iterator = php_com_iter_get; + + INIT_CLASS_ENTRY(ce, "com", NULL); + ce.create_object = php_com_object_new; + tmp = zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC); + tmp->get_iterator = php_com_iter_get; + + zend_ts_hash_init(&php_com_typelibraries, 0, NULL, php_com_typelibrary_dtor, 1); + +#if HAVE_MSCOREE_H + INIT_CLASS_ENTRY(ce, "dotnet", NULL); + ce.create_object = php_com_object_new; + tmp = zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC); + tmp->get_iterator = php_com_iter_get; +#endif + + REGISTER_INI_ENTRIES(); + +#define COM_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT) + + COM_CONST(CLSCTX_INPROC_SERVER); + COM_CONST(CLSCTX_INPROC_HANDLER); + COM_CONST(CLSCTX_LOCAL_SERVER); + COM_CONST(CLSCTX_REMOTE_SERVER); + COM_CONST(CLSCTX_SERVER); + COM_CONST(CLSCTX_ALL); + +#if 0 + COM_CONST(DISPATCH_METHOD); + COM_CONST(DISPATCH_PROPERTYGET); + COM_CONST(DISPATCH_PROPERTYPUT); +#endif + + COM_CONST(VT_NULL); + COM_CONST(VT_EMPTY); + COM_CONST(VT_UI1); + COM_CONST(VT_I1); + COM_CONST(VT_UI2); + COM_CONST(VT_I2); + COM_CONST(VT_UI4); + COM_CONST(VT_I4); + COM_CONST(VT_R4); + COM_CONST(VT_R8); + COM_CONST(VT_BOOL); + COM_CONST(VT_ERROR); + COM_CONST(VT_CY); + COM_CONST(VT_DATE); + COM_CONST(VT_BSTR); + COM_CONST(VT_DECIMAL); + COM_CONST(VT_UNKNOWN); + COM_CONST(VT_DISPATCH); + COM_CONST(VT_VARIANT); + COM_CONST(VT_INT); + COM_CONST(VT_UINT); + COM_CONST(VT_ARRAY); + COM_CONST(VT_BYREF); + + COM_CONST(CP_ACP); + COM_CONST(CP_MACCP); + COM_CONST(CP_OEMCP); + COM_CONST(CP_UTF7); + COM_CONST(CP_UTF8); + COM_CONST(CP_SYMBOL); + COM_CONST(CP_THREAD_ACP); + + COM_CONST(VARCMP_LT); + COM_CONST(VARCMP_EQ); + COM_CONST(VARCMP_GT); + COM_CONST(VARCMP_NULL); + + COM_CONST(NORM_IGNORECASE); + COM_CONST(NORM_IGNORENONSPACE); + COM_CONST(NORM_IGNORESYMBOLS); + COM_CONST(NORM_IGNOREWIDTH); + COM_CONST(NORM_IGNOREKANATYPE); +#ifdef NORM_IGNOREKASHIDA + COM_CONST(NORM_IGNOREKASHIDA); +#endif + COM_CONST(DISP_E_DIVBYZERO); + COM_CONST(DISP_E_OVERFLOW); + COM_CONST(DISP_E_BADINDEX); + COM_CONST(MK_E_UNAVAILABLE); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(com_dotnet) +{ + UNREGISTER_INI_ENTRIES(); +#if HAVE_MSCOREE_H + if (COMG(dotnet_runtime_stuff)) { + php_com_dotnet_mshutdown(TSRMLS_C); + } +#endif + + zend_ts_hash_destroy(&php_com_typelibraries); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RINIT_FUNCTION + */ +PHP_RINIT_FUNCTION(com_dotnet) +{ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +PHP_RSHUTDOWN_FUNCTION(com_dotnet) +{ +#if HAVE_MSCOREE_H + if (COMG(dotnet_runtime_stuff)) { + php_com_dotnet_rshutdown(TSRMLS_C); + } +#endif + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(com_dotnet) +{ + php_info_print_table_start(); + + php_info_print_table_header(2, "COM support", "enabled"); + php_info_print_table_header(2, "DCOM support", COMG(allow_dcom) ? "enabled" : "disabled"); + +#if HAVE_MSCOREE_H + php_info_print_table_header(2, ".Net support", "enabled"); +#else + php_info_print_table_header(2, ".Net support", "not present in this build"); +#endif + + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/a10e77_634012_ext#phar#tar.c b/src/main/resource/testFiles/php-src/revFiles/a10e77_634012_ext#phar#tar.c new file mode 100644 index 0000000..b914db1 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/a10e77_634012_ext#phar#tar.c @@ -0,0 +1,1322 @@ +/* + +----------------------------------------------------------------------+ + | TAR archive support for Phar | + +----------------------------------------------------------------------+ + | Copyright (c) 2005-2012 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Dmitry Stogov | + | Gregory Beaver | + +----------------------------------------------------------------------+ +*/ + +#include "phar_internal.h" + +static php_uint32 phar_tar_number(char *buf, int len) /* {{{ */ +{ + php_uint32 num = 0; + int i = 0; + + while (i < len && buf[i] == ' ') { + ++i; + } + + while (i < len && buf[i] >= '0' && buf[i] <= '7') { + num = num * 8 + (buf[i] - '0'); + ++i; + } + + return num; +} +/* }}} */ + +/* adapted from format_octal() in libarchive + * + * Copyright (c) 2003-2009 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +static int phar_tar_octal(char *buf, php_uint32 val, int len) /* {{{ */ +{ + char *p = buf; + int s = len; + + p += len; /* Start at the end and work backwards. */ + while (s-- > 0) { + *--p = (char)('0' + (val & 7)); + val >>= 3; + } + + if (val == 0) + return SUCCESS; + + /* If it overflowed, fill field with max value. */ + while (len-- > 0) + *p++ = '7'; + + return FAILURE; +} +/* }}} */ + +static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */ +{ + php_uint32 sum = 0; + char *end = buf + len; + + while (buf != end) { + sum += (unsigned char)*buf; + ++buf; + } + return sum; +} +/* }}} */ + +int phar_is_tar(char *buf, char *fname) /* {{{ */ +{ + tar_header *header = (tar_header *) buf; + php_uint32 checksum = phar_tar_number(header->checksum, sizeof(header->checksum)); + php_uint32 ret; + char save[sizeof(header->checksum)]; + + /* assume that the first filename in a tar won't begin with checksum, sizeof(header->checksum)); + memset(header->checksum, ' ', sizeof(header->checksum)); + ret = (checksum == phar_tar_checksum(buf, 512)); + memcpy(header->checksum, save, sizeof(header->checksum)); + if (!ret && strstr(fname, ".tar")) { + /* probably a corrupted tar - so we will pretend it is one */ + return 1; + } + return ret; +} +/* }}} */ + +int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */ +{ + phar_archive_data *phar; + int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error TSRMLS_CC); + + if (FAILURE == ret) { + return FAILURE; + } + + if (pphar) { + *pphar = phar; + } + + phar->is_data = is_data; + + if (phar->is_tar) { + return ret; + } + + if (phar->is_brandnew) { + phar->is_tar = 1; + phar->is_zip = 0; + phar->internal_file_start = 0; + return SUCCESS; + } + + /* we've reached here - the phar exists and is a regular phar */ + if (error) { + spprintf(error, 4096, "phar tar error: \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a tar-based phar", fname); + } + return FAILURE; +} +/* }}} */ + +static int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp TSRMLS_DC) /* {{{ */ +{ + char *metadata; + size_t save = php_stream_tell(fp), read; + phar_entry_info *mentry; + + metadata = (char *) emalloc(entry->uncompressed_filesize + 1); + + read = php_stream_read(fp, metadata, entry->uncompressed_filesize); + if (read != entry->uncompressed_filesize) { + efree(metadata); + php_stream_seek(fp, save, SEEK_SET); + return FAILURE; + } + + if (phar_parse_metadata(&metadata, &entry->metadata, entry->uncompressed_filesize TSRMLS_CC) == FAILURE) { + /* if not valid serialized data, it is a regular string */ + efree(metadata); + php_stream_seek(fp, save, SEEK_SET); + return FAILURE; + } + + if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) { + entry->phar->metadata = entry->metadata; + entry->metadata = NULL; + } else if (entry->filename_len >= sizeof(".phar/.metadata/") + sizeof("/.metadata.bin") - 1 && SUCCESS == zend_hash_find(&(entry->phar->manifest), entry->filename + sizeof(".phar/.metadata/") - 1, entry->filename_len - (sizeof("/.metadata.bin") - 1 + sizeof(".phar/.metadata/") - 1), (void *)&mentry)) { + /* transfer this metadata to the entry it refers */ + mentry->metadata = entry->metadata; + entry->metadata = NULL; + } + + efree(metadata); + php_stream_seek(fp, save, SEEK_SET); + return SUCCESS; +} +/* }}} */ + +int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */ +{ + char buf[512], *actual_alias = NULL, *p; + phar_entry_info entry = {0}; + size_t pos = 0, read, totalsize; + tar_header *hdr; + php_uint32 sum1, sum2, size, old; + phar_archive_data *myphar, **actual; + int last_was_longlink = 0; + + if (error) { + *error = NULL; + } + + php_stream_seek(fp, 0, SEEK_END); + totalsize = php_stream_tell(fp); + php_stream_seek(fp, 0, SEEK_SET); + read = php_stream_read(fp, buf, sizeof(buf)); + + if (read != sizeof(buf)) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is not a tar file or is truncated", fname); + } + php_stream_close(fp); + return FAILURE; + } + + hdr = (tar_header*)buf; + old = (memcmp(hdr->magic, "ustar", sizeof("ustar")-1) != 0); + + myphar = (phar_archive_data *) pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist)); + myphar->is_persistent = PHAR_G(persist); + /* estimate number of entries, can't be certain with tar files */ + zend_hash_init(&myphar->manifest, 2 + (totalsize >> 12), + zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)myphar->is_persistent); + zend_hash_init(&myphar->mounted_dirs, 5, + zend_get_hash_value, NULL, (zend_bool)myphar->is_persistent); + zend_hash_init(&myphar->virtual_dirs, 4 + (totalsize >> 11), + zend_get_hash_value, NULL, (zend_bool)myphar->is_persistent); + myphar->is_tar = 1; + /* remember whether this entire phar was compressed with gz/bzip2 */ + myphar->flags = compression; + + entry.is_tar = 1; + entry.is_crc_checked = 1; + entry.phar = myphar; + pos += sizeof(buf); + + do { + phar_entry_info *newentry; + + pos = php_stream_tell(fp); + hdr = (tar_header*) buf; + sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum)); + if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) { + break; + } + memset(hdr->checksum, ' ', sizeof(hdr->checksum)); + sum2 = phar_tar_checksum(buf, old?sizeof(old_tar_header):sizeof(tar_header)); + + size = entry.uncompressed_filesize = entry.compressed_filesize = + phar_tar_number(hdr->size, sizeof(hdr->size)); + + if (((!old && hdr->prefix[0] == 0) || old) && strlen(hdr->name) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) { + off_t curloc; + + if (size > 511) { + if (error) { + spprintf(error, 4096, "phar error: tar-based phar \"%s\" has signature that is larger than 511 bytes, cannot process", fname); + } +bail: + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + curloc = php_stream_tell(fp); + read = php_stream_read(fp, buf, size); + if (read != size) { + if (error) { + spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be read", fname); + } + goto bail; + } +#ifdef WORDS_BIGENDIAN +# define PHAR_GET_32(buffer) \ + (((((unsigned char*)(buffer))[3]) << 24) \ + | ((((unsigned char*)(buffer))[2]) << 16) \ + | ((((unsigned char*)(buffer))[1]) << 8) \ + | (((unsigned char*)(buffer))[0])) +#else +# define PHAR_GET_32(buffer) (php_uint32) *(buffer) +#endif + myphar->sig_flags = PHAR_GET_32(buf); + if (FAILURE == phar_verify_signature(fp, php_stream_tell(fp) - size - 512, myphar->sig_flags, buf + 8, size - 8, fname, &myphar->signature, &myphar->sig_len, error TSRMLS_CC)) { + if (error) { + char *save = *error; + spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be verified: %s", fname, save); + efree(save); + } + goto bail; + } + php_stream_seek(fp, curloc + 512, SEEK_SET); + /* signature checked out, let's ensure this is the last file in the phar */ + if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) { + /* this is not good enough - seek succeeds even on truncated tars */ + php_stream_seek(fp, 512, SEEK_CUR); + if ((uint)php_stream_tell(fp) > totalsize) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } + + read = php_stream_read(fp, buf, sizeof(buf)); + + if (read != sizeof(buf)) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + hdr = (tar_header*) buf; + sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum)); + + if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) { + break; + } + + if (error) { + spprintf(error, 4096, "phar error: \"%s\" has entries after signature, invalid phar", fname); + } + + goto bail; + } + + if (!last_was_longlink && hdr->typeflag == 'L') { + last_was_longlink = 1; + /* support the ././@LongLink system for storing long filenames */ + entry.filename_len = entry.uncompressed_filesize; + + /* Check for overflow - bug 61065 */ + if (entry.filename_len == UINT_MAX) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (invalid entry size)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + entry.filename = pemalloc(entry.filename_len+1, myphar->is_persistent); + + read = php_stream_read(fp, entry.filename, entry.filename_len); + if (read != entry.filename_len) { + efree(entry.filename); + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + entry.filename[entry.filename_len] = '\0'; + + /* skip blank stuff */ + size = ((size+511)&~511) - size; + + /* this is not good enough - seek succeeds even on truncated tars */ + php_stream_seek(fp, size, SEEK_CUR); + if ((uint)php_stream_tell(fp) > totalsize) { + efree(entry.filename); + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + read = php_stream_read(fp, buf, sizeof(buf)); + + if (read != sizeof(buf)) { + efree(entry.filename); + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + continue; + } else if (!last_was_longlink && !old && hdr->prefix[0] != 0) { + char name[256]; + int i, j; + + for (i = 0; i < 155; i++) { + name[i] = hdr->prefix[i]; + if (name[i] == '\0') { + break; + } + } + name[i++] = '/'; + for (j = 0; j < 100; j++) { + name[i+j] = hdr->name[j]; + if (name[i+j] == '\0') { + break; + } + } + + entry.filename_len = i+j; + + if (name[entry.filename_len - 1] == '/') { + /* some tar programs store directories with trailing slash */ + entry.filename_len--; + } + entry.filename = pestrndup(name, entry.filename_len, myphar->is_persistent); + } else if (!last_was_longlink) { + int i; + + /* calculate strlen, which can be no longer than 100 */ + for (i = 0; i < 100; i++) { + if (hdr->name[i] == '\0') { + break; + } + } + entry.filename_len = i; + entry.filename = pestrndup(hdr->name, i, myphar->is_persistent); + + if (entry.filename[entry.filename_len - 1] == '/') { + /* some tar programs store directories with trailing slash */ + entry.filename[entry.filename_len - 1] = '\0'; + entry.filename_len--; + } + } + last_was_longlink = 0; + + phar_add_virtual_dirs(myphar, entry.filename, entry.filename_len TSRMLS_CC); + + if (sum1 != sum2) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename); + } + pefree(entry.filename, myphar->is_persistent); + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + entry.tar_type = ((old & (hdr->typeflag == '\0')) ? TAR_FILE : hdr->typeflag); + entry.offset = entry.offset_abs = pos; /* header_offset unused in tar */ + entry.fp_type = PHAR_FP; + entry.flags = phar_tar_number(hdr->mode, sizeof(hdr->mode)) & PHAR_ENT_PERM_MASK; + entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime)); + entry.is_persistent = myphar->is_persistent; +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) +#endif + if (old && entry.tar_type == TAR_FILE && S_ISDIR(entry.flags)) { + entry.tar_type = TAR_DIR; + } + + if (entry.tar_type == TAR_DIR) { + entry.is_dir = 1; + } else { + entry.is_dir = 0; + } + + entry.link = NULL; + + if (entry.tar_type == TAR_LINK) { + if (!zend_hash_exists(&myphar->manifest, hdr->linkname, strlen(hdr->linkname))) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file \"%s\"", fname, hdr->linkname); + } + pefree(entry.filename, entry.is_persistent); + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + entry.link = estrdup(hdr->linkname); + } else if (entry.tar_type == TAR_SYMLINK) { + entry.link = estrdup(hdr->linkname); + } + phar_set_inode(&entry TSRMLS_CC); + zend_hash_add(&myphar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), (void **) &newentry); + + if (entry.is_persistent) { + ++entry.manifest_pos; + } + + if (entry.filename_len >= sizeof(".phar/.metadata")-1 && !memcmp(entry.filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) { + if (FAILURE == phar_tar_process_metadata(newentry, fp TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: tar-based phar \"%s\" has invalid metadata in magic file \"%s\"", fname, entry.filename); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } + + if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { + /* found explicit alias */ + if (size > 511) { + if (error) { + spprintf(error, 4096, "phar error: tar-based phar \"%s\" has alias that is larger than 511 bytes, cannot process", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + read = php_stream_read(fp, buf, size); + + if (read == size) { + buf[size] = '\0'; + if (!phar_validate_alias(buf, size)) { + if (size > 50) { + buf[50] = '.'; + buf[51] = '.'; + buf[52] = '.'; + buf[53] = '\0'; + } + + if (error) { + spprintf(error, 4096, "phar error: invalid alias \"%s\" in tar-based phar \"%s\"", buf, fname); + } + + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + actual_alias = pestrndup(buf, size, myphar->is_persistent); + myphar->alias = actual_alias; + myphar->alias_len = size; + php_stream_seek(fp, pos, SEEK_SET); + } else { + if (error) { + spprintf(error, 4096, "phar error: Unable to read alias from tar-based phar \"%s\"", fname); + } + + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } + + size = (size+511)&~511; + + if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) { + /* this is not good enough - seek succeeds even on truncated tars */ + php_stream_seek(fp, size, SEEK_CUR); + if ((uint)php_stream_tell(fp) > totalsize) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } + + read = php_stream_read(fp, buf, sizeof(buf)); + + if (read != sizeof(buf)) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } while (read != 0); + + if (zend_hash_exists(&(myphar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) { + myphar->is_data = 0; + } else { + myphar->is_data = 1; + } + + /* ensure signature set */ + if (!myphar->is_data && PHAR_G(require_hash) && !myphar->signature) { + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + if (error) { + spprintf(error, 0, "tar-based phar \"%s\" does not have a signature", fname); + } + return FAILURE; + } + + myphar->fname = pestrndup(fname, fname_len, myphar->is_persistent); +#ifdef PHP_WIN32 + phar_unixify_path_separators(myphar->fname, fname_len); +#endif + myphar->fname_len = fname_len; + myphar->fp = fp; + p = strrchr(myphar->fname, '/'); + + if (p) { + myphar->ext = memchr(p, '.', (myphar->fname + fname_len) - p); + if (myphar->ext == p) { + myphar->ext = memchr(p + 1, '.', (myphar->fname + fname_len) - p - 1); + } + if (myphar->ext) { + myphar->ext_len = (myphar->fname + fname_len) - myphar->ext; + } + } + + phar_request_initialize(TSRMLS_C); + + if (SUCCESS != zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len, (void*)&myphar, sizeof(phar_archive_data*), (void **)&actual)) { + if (error) { + spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\" to phar registry", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + myphar = *actual; + + if (actual_alias) { + phar_archive_data **fd_ptr; + + myphar->is_temporary_alias = 0; + + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, myphar->alias_len TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname); + } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len); + return FAILURE; + } + } + + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); + } else { + phar_archive_data **fd_ptr; + + if (alias_len) { + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname); + } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len); + return FAILURE; + } + } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); + myphar->alias = pestrndup(alias, alias_len, myphar->is_persistent); + myphar->alias_len = alias_len; + } else { + myphar->alias = pestrndup(myphar->fname, fname_len, myphar->is_persistent); + myphar->alias_len = fname_len; + } + + myphar->is_temporary_alias = 1; + } + + if (pphar) { + *pphar = myphar; + } + + return SUCCESS; +} +/* }}} */ + +struct _phar_pass_tar_info { + php_stream *old; + php_stream *new; + int free_fp; + int free_ufp; + char **error; +}; + +static int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */ +{ + tar_header header; + size_t pos; + phar_entry_info *entry = (phar_entry_info *) pDest; + struct _phar_pass_tar_info *fp = (struct _phar_pass_tar_info *)argument; + char padding[512]; + + if (entry->is_mounted) { + return ZEND_HASH_APPLY_KEEP; + } + + if (entry->is_deleted) { + if (entry->fp_refcount <= 0) { + return ZEND_HASH_APPLY_REMOVE; + } else { + /* we can't delete this in-memory until it is closed */ + return ZEND_HASH_APPLY_KEEP; + } + } + + phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len TSRMLS_CC); + memset((char *) &header, 0, sizeof(header)); + + if (entry->filename_len > 100) { + char *boundary; + if (entry->filename_len > 256) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too long for tar file format", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + boundary = entry->filename + entry->filename_len - 101; + while (*boundary && *boundary != '/') { + ++boundary; + } + if (!*boundary || ((boundary - entry->filename) > 155)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too long for tar file format", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + memcpy(header.prefix, entry->filename, boundary - entry->filename); + memcpy(header.name, boundary + 1, entry->filename_len - (boundary + 1 - entry->filename)); + } else { + memcpy(header.name, entry->filename, entry->filename_len); + } + + phar_tar_octal(header.mode, entry->flags & PHAR_ENT_PERM_MASK, sizeof(header.mode)-1); + + if (FAILURE == phar_tar_octal(header.size, entry->uncompressed_filesize, sizeof(header.size)-1)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + if (FAILURE == phar_tar_octal(header.mtime, entry->timestamp, sizeof(header.mtime)-1)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, file modification time of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + /* calc checksum */ + header.typeflag = entry->tar_type; + + if (entry->link) { + strncpy(header.linkname, entry->link, strlen(entry->link)); + } + + strncpy(header.magic, "ustar", sizeof("ustar")-1); + strncpy(header.version, "00", sizeof("00")-1); + strncpy(header.checksum, " ", sizeof(" ")-1); + entry->crc32 = phar_tar_checksum((char *)&header, sizeof(header)); + + if (FAILURE == phar_tar_octal(header.checksum, entry->crc32, sizeof(header.checksum)-1)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, checksum of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + /* write header */ + entry->header_offset = php_stream_tell(fp->new); + + if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for file \"%s\" could not be written", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + pos = php_stream_tell(fp->new); /* save start of file within tar */ + + /* write contents */ + if (entry->uncompressed_filesize) { + if (FAILURE == phar_open_entry_fp(entry, fp->error, 0 TSRMLS_CC)) { + return ZEND_HASH_APPLY_STOP; + } + + if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written, seek failed", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + if (SUCCESS != phar_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), fp->new, entry->uncompressed_filesize, NULL)) { + if (fp->error) { + spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename); + } + return ZEND_HASH_APPLY_STOP; + } + + memset(padding, 0, 512); + php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize); + } + + if (!entry->is_modified && entry->fp_refcount) { + /* open file pointers refer to this fp, do not free the stream */ + switch (entry->fp_type) { + case PHAR_FP: + fp->free_fp = 0; + break; + case PHAR_UFP: + fp->free_ufp = 0; + default: + break; + } + } + + entry->is_modified = 0; + + if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) { + if (!entry->fp_refcount) { + php_stream_close(entry->fp); + } + entry->fp = NULL; + } + + entry->fp_type = PHAR_FP; + + /* note new location within tar */ + entry->offset = entry->offset_abs = pos; + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +int phar_tar_setmetadata(zval *metadata, phar_entry_info *entry, char **error TSRMLS_DC) /* {{{ */ +{ + php_serialize_data_t metadata_hash; + + if (entry->metadata_str.c) { + smart_str_free(&entry->metadata_str); + } + + entry->metadata_str.c = 0; + entry->metadata_str.len = 0; + PHP_VAR_SERIALIZE_INIT(metadata_hash); + php_var_serialize(&entry->metadata_str, &metadata, &metadata_hash TSRMLS_CC); + PHP_VAR_SERIALIZE_DESTROY(metadata_hash); + entry->uncompressed_filesize = entry->compressed_filesize = entry->metadata_str.len; + + if (entry->fp && entry->fp_type == PHAR_MOD) { + php_stream_close(entry->fp); + } + + entry->fp_type = PHAR_MOD; + entry->is_modified = 1; + entry->fp = php_stream_fopen_tmpfile(); + entry->offset = entry->offset_abs = 0; + + if (entry->metadata_str.len != php_stream_write(entry->fp, entry->metadata_str.c, entry->metadata_str.len)) { + spprintf(error, 0, "phar tar error: unable to write metadata to magic metadata file \"%s\"", entry->filename); + zend_hash_del(&(entry->phar->manifest), entry->filename, entry->filename_len); + return ZEND_HASH_APPLY_STOP; + } + + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +static int phar_tar_setupmetadata(void *pDest, void *argument TSRMLS_DC) /* {{{ */ +{ + int lookfor_len; + struct _phar_pass_tar_info *i = (struct _phar_pass_tar_info *)argument; + char *lookfor, **error = i->error; + phar_entry_info *entry = (phar_entry_info *)pDest, *metadata, newentry = {0}; + + if (entry->filename_len >= sizeof(".phar/.metadata") && !memcmp(entry->filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) { + if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) { + return phar_tar_setmetadata(entry->phar->metadata, entry, error TSRMLS_CC); + } + /* search for the file this metadata entry references */ + if (entry->filename_len >= sizeof(".phar/.metadata/") + sizeof("/.metadata.bin") - 1 && !zend_hash_exists(&(entry->phar->manifest), entry->filename + sizeof(".phar/.metadata/") - 1, entry->filename_len - (sizeof("/.metadata.bin") - 1 + sizeof(".phar/.metadata/") - 1))) { + /* this is orphaned metadata, erase it */ + return ZEND_HASH_APPLY_REMOVE; + } + /* we can keep this entry, the file that refers to it exists */ + return ZEND_HASH_APPLY_KEEP; + } + + if (!entry->is_modified) { + return ZEND_HASH_APPLY_KEEP; + } + + /* now we are dealing with regular files, so look for metadata */ + lookfor_len = spprintf(&lookfor, 0, ".phar/.metadata/%s/.metadata.bin", entry->filename); + + if (!entry->metadata) { + zend_hash_del(&(entry->phar->manifest), lookfor, lookfor_len); + efree(lookfor); + return ZEND_HASH_APPLY_KEEP; + } + + if (SUCCESS == zend_hash_find(&(entry->phar->manifest), lookfor, lookfor_len, (void **)&metadata)) { + int ret; + ret = phar_tar_setmetadata(entry->metadata, metadata, error TSRMLS_CC); + efree(lookfor); + return ret; + } + + newentry.filename = lookfor; + newentry.filename_len = lookfor_len; + newentry.phar = entry->phar; + newentry.tar_type = TAR_FILE; + newentry.is_tar = 1; + + if (SUCCESS != zend_hash_add(&(entry->phar->manifest), lookfor, lookfor_len, (void *)&newentry, sizeof(phar_entry_info), (void **)&metadata)) { + efree(lookfor); + spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for file \"%s\"", entry->filename); + return ZEND_HASH_APPLY_STOP; + } + + return phar_tar_setmetadata(entry->metadata, metadata, error TSRMLS_CC); +} +/* }}} */ + +int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */ +{ + phar_entry_info entry = {0}; + static const char newstub[] = "is_persistent) { + if (error) { + spprintf(error, 0, "internal error: attempt to flush cached tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + + if (phar->is_data) { + goto nostub; + } + + /* set alias */ + if (!phar->is_temporary_alias && phar->alias_len) { + entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1); + entry.filename_len = sizeof(".phar/alias.txt")-1; + entry.fp = php_stream_fopen_tmpfile(); + + if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) { + if (error) { + spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + + entry.uncompressed_filesize = phar->alias_len; + + if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { + if (error) { + spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + } else { + zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1); + } + + /* set stub */ + if (user_stub && !defaultstub) { + char *pos; + if (len < 0) { + /* resource passed in */ + if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) { + if (error) { + spprintf(error, 0, "unable to access resource to copy stub to new tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + if (len == -1) { + len = PHP_STREAM_COPY_ALL; + } else { + len = -len; + } + user_stub = 0; +#if PHP_MAJOR_VERSION >= 6 + if (!(len = php_stream_copy_to_mem(stubfile, (void **) &user_stub, len, 0)) || !user_stub) { +#else + if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) { +#endif + if (error) { + spprintf(error, 0, "unable to read resource to copy stub to new tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + free_user_stub = 1; + } else { + free_user_stub = 0; + } + + tmp = estrndup(user_stub, len); + if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) { + efree(tmp); + if (error) { + spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", phar->fname); + } + if (free_user_stub) { + efree(user_stub); + } + return EOF; + } + pos = user_stub + (pos - tmp); + efree(tmp); + + len = pos - user_stub + 18; + entry.fp = php_stream_fopen_tmpfile(); + entry.uncompressed_filesize = len + 5; + + if ((size_t)len != php_stream_write(entry.fp, user_stub, len) + || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) { + if (error) { + spprintf(error, 0, "unable to create stub from string in new tar-based phar \"%s\"", phar->fname); + } + if (free_user_stub) { + efree(user_stub); + } + php_stream_close(entry.fp); + return EOF; + } + + entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1); + entry.filename_len = sizeof(".phar/stub.php")-1; + zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); + + if (free_user_stub) { + efree(user_stub); + } + } else { + /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */ + entry.fp = php_stream_fopen_tmpfile(); + + if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) { + php_stream_close(entry.fp); + if (error) { + spprintf(error, 0, "unable to %s stub in%star-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname); + } + return EOF; + } + + entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1; + entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1); + entry.filename_len = sizeof(".phar/stub.php")-1; + + if (!defaultstub) { + if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) { + if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { + php_stream_close(entry.fp); + efree(entry.filename); + if (error) { + spprintf(error, 0, "unable to create stub in tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + } else { + php_stream_close(entry.fp); + efree(entry.filename); + } + } else { + if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { + php_stream_close(entry.fp); + efree(entry.filename); + if (error) { + spprintf(error, 0, "unable to overwrite stub in tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + } + } +nostub: + if (phar->fp && !phar->is_brandnew) { + oldfile = phar->fp; + closeoldfile = 0; + php_stream_rewind(oldfile); + } else { + oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL); + closeoldfile = oldfile != NULL; + } + + newfile = php_stream_fopen_tmpfile(); + + if (!newfile) { + if (error) { + spprintf(error, 0, "unable to create temporary file"); + } + if (closeoldfile) { + php_stream_close(oldfile); + } + return EOF; + } + + pass.old = oldfile; + pass.new = newfile; + pass.error = error; + pass.free_fp = 1; + pass.free_ufp = 1; + + if (phar->metadata) { + phar_entry_info *mentry; + if (SUCCESS == zend_hash_find(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void **)&mentry)) { + if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error TSRMLS_CC)) { + if (closeoldfile) { + php_stream_close(oldfile); + } + return EOF; + } + } else { + phar_entry_info newentry = {0}; + + newentry.filename = estrndup(".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1); + newentry.filename_len = sizeof(".phar/.metadata.bin")-1; + newentry.phar = phar; + newentry.tar_type = TAR_FILE; + newentry.is_tar = 1; + + if (SUCCESS != zend_hash_add(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void *)&newentry, sizeof(phar_entry_info), (void **)&mentry)) { + spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for phar archive \"%s\"", phar->fname); + if (closeoldfile) { + php_stream_close(oldfile); + } + return EOF; + } + + if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error TSRMLS_CC)) { + zend_hash_del(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1); + if (closeoldfile) { + php_stream_close(oldfile); + } + return EOF; + } + } + } + + zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_setupmetadata, (void *) &pass TSRMLS_CC); + + if (error && *error) { + if (closeoldfile) { + php_stream_close(oldfile); + } + + /* on error in the hash iterator above, error is set */ + php_stream_close(newfile); + return EOF; + } + + zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_writeheaders, (void *) &pass TSRMLS_CC); + + /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */ + if (!phar->is_data || phar->sig_flags) { + if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, error TSRMLS_CC)) { + if (error) { + char *save = *error; + spprintf(error, 0, "phar error: unable to write signature to tar-based phar: %s", save); + efree(save); + } + + if (closeoldfile) { + php_stream_close(oldfile); + } + + php_stream_close(newfile); + return EOF; + } + + entry.filename = ".phar/signature.bin"; + entry.filename_len = sizeof(".phar/signature.bin")-1; + entry.fp = php_stream_fopen_tmpfile(); + +#ifdef WORDS_BIGENDIAN +# define PHAR_SET_32(var, buffer) \ + *(php_uint32 *)(var) = (((((unsigned char*)&(buffer))[3]) << 24) \ + | ((((unsigned char*)&(buffer))[2]) << 16) \ + | ((((unsigned char*)&(buffer))[1]) << 8) \ + | (((unsigned char*)&(buffer))[0])) +#else +# define PHAR_SET_32(var, buffer) *(php_uint32 *)(var) = (php_uint32) (buffer) +#endif + PHAR_SET_32(sigbuf, phar->sig_flags); + PHAR_SET_32(sigbuf + 4, signature_length); + + if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) { + efree(signature); + if (error) { + spprintf(error, 0, "phar error: unable to write signature to tar-based phar %s", phar->fname); + } + + if (closeoldfile) { + php_stream_close(oldfile); + } + php_stream_close(newfile); + return EOF; + } + + efree(signature); + entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8; + /* throw out return value and write the signature */ + entry.filename_len = phar_tar_writeheaders((void *)&entry, (void *)&pass TSRMLS_CC); + + if (error && *error) { + if (closeoldfile) { + php_stream_close(oldfile); + } + /* error is set by writeheaders */ + php_stream_close(newfile); + return EOF; + } + } /* signature */ + + /* add final zero blocks */ + buf = (char *) ecalloc(1024, 1); + php_stream_write(newfile, buf, 1024); + efree(buf); + + if (closeoldfile) { + php_stream_close(oldfile); + } + + /* on error in the hash iterator above, error is set */ + if (error && *error) { + php_stream_close(newfile); + return EOF; + } + + if (phar->fp && pass.free_fp) { + php_stream_close(phar->fp); + } + + if (phar->ufp) { + if (pass.free_ufp) { + php_stream_close(phar->ufp); + } + phar->ufp = NULL; + } + + phar->is_brandnew = 0; + php_stream_rewind(newfile); + + if (phar->donotflush) { + /* deferred flush */ + phar->fp = newfile; + } else { + phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); + if (!phar->fp) { + phar->fp = newfile; + if (error) { + spprintf(error, 0, "unable to open new phar \"%s\" for writing", phar->fname); + } + return EOF; + } + + if (phar->flags & PHAR_FILE_COMPRESSED_GZ) { + php_stream_filter *filter; + /* to properly compress, we have to tell zlib to add a zlib header */ + zval filterparams; + + array_init(&filterparams); +/* this is defined in zlib's zconf.h */ +#ifndef MAX_WBITS +#define MAX_WBITS 15 +#endif + add_assoc_long(&filterparams, "window", MAX_WBITS + 16); + filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC); + zval_dtor(&filterparams); + + if (!filter) { + /* copy contents uncompressed rather than lose them */ + phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL); + php_stream_close(newfile); + if (error) { + spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname); + } + return EOF; + } + + php_stream_filter_append(&phar->fp->writefilters, filter); + phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL); + php_stream_filter_flush(filter, 1); + php_stream_filter_remove(filter, 1 TSRMLS_CC); + php_stream_close(phar->fp); + /* use the temp stream as our base */ + phar->fp = newfile; + } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) { + php_stream_filter *filter; + + filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC); + php_stream_filter_append(&phar->fp->writefilters, filter); + phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL); + php_stream_filter_flush(filter, 1); + php_stream_filter_remove(filter, 1 TSRMLS_CC); + php_stream_close(phar->fp); + /* use the temp stream as our base */ + phar->fp = newfile; + } else { + phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL); + /* we could also reopen the file in "rb" mode but there is no need for that */ + php_stream_close(newfile); + } + } + return EOF; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/a29791_796ff1_Zend#zend_API.c b/src/main/resource/testFiles/php-src/revFiles/a29791_796ff1_Zend#zend_API.c new file mode 100644 index 0000000..6d6cb07 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/a29791_796ff1_Zend#zend_API.c @@ -0,0 +1,978 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + + +#include "zend.h" +#include "zend_execute.h" +#include "zend_API.h" +#include "modules.h" +#include "zend_constants.h" + +#ifdef HAVE_STDARG_H +#include +#endif + +/* these variables are true statics/globals, and have to be mutex'ed on every access */ +static int module_count=0; +HashTable module_registry; + +/* this function doesn't check for too many parameters */ +ZEND_API int zend_get_parameters(int ht, int param_count, ...) +{ + void **p; + int arg_count; + va_list ptr; + zval **param, *param_ptr; + ELS_FETCH(); + + p = EG(argument_stack).top_element-2; + arg_count = (ulong) *p; + + if (param_count>arg_count) { + return FAILURE; + } + + va_start(ptr, param_count); + + while (param_count-->0) { + param = va_arg(ptr, zval **); + param_ptr = *(p-arg_count); + if (!PZVAL_IS_REF(param_ptr) && param_ptr->refcount>1) { + zval *new_tmp; + + ALLOC_ZVAL(new_tmp); + *new_tmp = *param_ptr; + zval_copy_ctor(new_tmp); + INIT_PZVAL(new_tmp); + param_ptr = new_tmp; + ((zval *) *(p-arg_count))->refcount--; + *(p-param_count) = param_ptr; + } + *param = param_ptr; + arg_count--; + } + va_end(ptr); + + return SUCCESS; +} + + +ZEND_API int zend_get_parameters_array(int ht, int param_count, zval **argument_array) +{ + void **p; + int arg_count; + zval *param_ptr; + ELS_FETCH(); + + p = EG(argument_stack).top_element-2; + arg_count = (ulong) *p; + + if (param_count>arg_count) { + return FAILURE; + } + + + while (param_count-->0) { + param_ptr = *(p-arg_count); + if (!PZVAL_IS_REF(param_ptr) && param_ptr->refcount>1) { + zval *new_tmp; + + ALLOC_ZVAL(new_tmp); + *new_tmp = *param_ptr; + zval_copy_ctor(new_tmp); + INIT_PZVAL(new_tmp); + param_ptr = new_tmp; + ((zval *) *(p-arg_count))->refcount--; + *(p-arg_count) = param_ptr; + } + *(argument_array++) = param_ptr; + arg_count--; + } + + return SUCCESS; +} + + + + +/* Zend-optimized Extended functions */ +/* this function doesn't check for too many parameters */ +ZEND_API int zend_get_parameters_ex(int param_count, ...) +{ + void **p; + int arg_count; + va_list ptr; + zval ***param; + ELS_FETCH(); + + p = EG(argument_stack).top_element-2; + arg_count = (ulong) *p; + + if (param_count>arg_count) { + return FAILURE; + } + + va_start(ptr, param_count); + while (param_count-->0) { + param = va_arg(ptr, zval ***); + *param = (zval **) p-(arg_count--); + } + va_end(ptr); + + return SUCCESS; +} + + +ZEND_API int zend_get_parameters_array_ex(int param_count, zval ***argument_array) +{ + void **p; + int arg_count; + ELS_FETCH(); + + p = EG(argument_stack).top_element-2; + arg_count = (ulong) *p; + + if (param_count>arg_count) { + return FAILURE; + } + + while (param_count-->0) { + *(argument_array++) = (zval **) p-(arg_count--); + } + + return SUCCESS; +} + + +ZEND_API int ParameterPassedByReference(int ht, uint n) +{ + void **p; + ulong arg_count; + zval *arg; + ELS_FETCH(); + + p = EG(argument_stack).elements+EG(argument_stack).top-2; + arg_count = (ulong) *p; + + if (n>arg_count) { + return FAILURE; + } + arg = (zval *) *(p-arg_count+n-1); + return PZVAL_IS_REF(arg); +} + + +ZEND_API void wrong_param_count() +{ + zend_error(E_WARNING,"Wrong parameter count for %s()",get_active_function_name()); +} + + +ZEND_API inline int _array_init(zval *arg ZEND_FILE_LINE_DC) +{ + ALLOC_HASHTABLE_REL(arg->value.ht); + + if (!arg->value.ht || zend_hash_init(arg->value.ht, 0, NULL, ZVAL_PTR_DTOR, 0)) { + zend_error(E_CORE_ERROR, "Cannot allocate memory for array"); + return FAILURE; + } + arg->type = IS_ARRAY; + return SUCCESS; +} + + +ZEND_API inline int _object_init_ex(zval *arg, zend_class_entry *class_type ZEND_FILE_LINE_DC) +{ + zval *tmp; + + if (!class_type->constants_updated) { + zend_hash_apply_with_argument(&class_type->default_properties, (int (*)(void *,void *)) zval_update_constant, (void *) 1); + class_type->constants_updated = 1; + } + + ALLOC_HASHTABLE_REL(arg->value.obj.properties); + zend_hash_init(arg->value.obj.properties, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(arg->value.obj.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + arg->type = IS_OBJECT; + arg->value.obj.ce = class_type; + return SUCCESS; +} + + +ZEND_API inline int _object_init(zval *arg ZEND_FILE_LINE_DC) +{ + return _object_init_ex(arg, &zend_standard_class_def ZEND_FILE_LINE_CC); +} + + +ZEND_API inline int add_assoc_function(zval *arg, char *key,void (*function_ptr)(INTERNAL_FUNCTION_PARAMETERS)) +{ + zend_error(E_WARNING, "add_assoc_function() is no longer supported"); + return FAILURE; +} + + +ZEND_API inline int add_assoc_long(zval *arg, char *key, long n) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_LONG; + tmp->value.lval = n; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_assoc_unset(zval *arg, char *key) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_NULL; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + +ZEND_API inline int add_assoc_bool(zval *arg, char *key, int b) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_BOOL; + tmp->value.lval = b; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_assoc_resource(zval *arg, char *key, int r) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_RESOURCE; + tmp->value.lval = r; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_assoc_double(zval *arg, char *key, double d) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_DOUBLE; + tmp->value.dval = d; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_assoc_string(zval *arg, char *key, char *str, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_assoc_stringl(zval *arg, char *key, char *str, uint length, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_index_long(zval *arg, uint index, long n) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_LONG; + tmp->value.lval = n; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_index_unset(zval *arg, uint index) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_NULL; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_index_bool(zval *arg, uint index, int b) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_BOOL; + tmp->value.lval = b; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_index_resource(zval *arg, uint index, int r) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_RESOURCE; + tmp->value.lval = r; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_index_double(zval *arg, uint index, double d) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_DOUBLE; + tmp->value.dval = d; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_index_string(zval *arg, uint index, char *str, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_index_stringl(zval *arg, uint index, char *str, uint length, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_next_index_long(zval *arg, long n) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_LONG; + tmp->value.lval = n; + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_next_index_unset(zval *arg) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_NULL; + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_next_index_bool(zval *arg, int b) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_BOOL; + tmp->value.lval = b; + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_next_index_resource(zval *arg, int r) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_RESOURCE; + tmp->value.lval = r; + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_next_index_double(zval *arg, double d) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_DOUBLE; + tmp->value.dval = d; + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_next_index_string(zval *arg, char *str, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_next_index_stringl(zval *arg, char *str, uint length, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_next_index_insert(arg->value.ht, &tmp, sizeof(zval *),NULL); +} + + +ZEND_API inline int add_get_assoc_string(zval *arg, char *key, char *str, void **dest, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), dest); +} + + +ZEND_API inline int add_get_assoc_stringl(zval *arg, char *key, char *str, uint length, void **dest, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.ht, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), dest); +} + + +ZEND_API inline int add_get_index_long(zval *arg, uint index, long l, void **dest) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_LONG; + tmp->value.lval = l; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),dest); +} + + +ZEND_API inline int add_get_index_double(zval *arg, uint index, double d, void **dest) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_DOUBLE; + tmp->value.dval= d; + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),dest); +} + + +ZEND_API inline int add_get_index_string(zval *arg, uint index, char *str, void **dest, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),dest); +} + + +ZEND_API inline int add_get_index_stringl(zval *arg, uint index, char *str, uint length, void **dest, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_index_update(arg->value.ht, index, (void *) &tmp, sizeof(zval *),dest); +} + + +ZEND_API inline int add_property_long(zval *arg, char *key, long n) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_LONG; + tmp->value.lval = n; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + +ZEND_API inline int add_property_bool(zval *arg, char *key, int b) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_BOOL; + tmp->value.lval = b; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + +ZEND_API inline int add_property_unset(zval *arg, char *key) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_NULL; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + +ZEND_API inline int add_property_resource(zval *arg, char *key, long n) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_RESOURCE; + tmp->value.lval = n; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_property_double(zval *arg, char *key, double d) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_DOUBLE; + tmp->value.dval = d; + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_property_string(zval *arg, char *key, char *str, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = strlen(str); + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API inline int add_property_stringl(zval *arg, char *key, char *str, uint length, int duplicate) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->type = IS_STRING; + tmp->value.str.len = length; + if (duplicate) { + tmp->value.str.val = estrndup(str,tmp->value.str.len); + } else { + tmp->value.str.val = str; + } + INIT_PZVAL(tmp); + return zend_hash_update(arg->value.obj.properties, key, strlen(key)+1, (void *) &tmp, sizeof(zval *), NULL); +} + + +ZEND_API int zend_startup_module(zend_module_entry *module) +{ + if (module) { + module->module_number = zend_next_free_module(); + if (module->module_startup_func) { + ELS_FETCH(); + + if (module->module_startup_func(MODULE_PERSISTENT, module->module_number ELS_CC)==FAILURE) { + zend_error(E_CORE_ERROR,"Unable to start %s module",module->name); + return FAILURE; + } + } + module->type = MODULE_PERSISTENT; + zend_register_module(module); + } + return SUCCESS; +} + + +/* registers all functions in *library_functions in the function hash */ +int zend_register_functions(zend_function_entry *functions, HashTable *function_table) +{ + zend_function_entry *ptr = functions; + zend_function function; + zend_internal_function *internal_function = (zend_internal_function *)&function; + int count=0,unload=0; + HashTable *target_function_table = function_table; + CLS_FETCH(); + + if (!target_function_table) { + target_function_table = CG(function_table); + } + internal_function->type = ZEND_INTERNAL_FUNCTION; + + while (ptr->fname) { + internal_function->handler = ptr->handler; + internal_function->arg_types = ptr->func_arg_types; + internal_function->function_name = ptr->fname; + if (!internal_function->handler) { + zend_error(E_CORE_WARNING,"Null function defined as active function"); + zend_unregister_functions(functions, count, target_function_table); + return FAILURE; + } + if (zend_hash_add(target_function_table, ptr->fname, strlen(ptr->fname)+1, &function, sizeof(zend_function), NULL) == FAILURE) { + unload=1; + break; + } + ptr++; + count++; + } + if (unload) { /* before unloading, display all remaining bad function in the module */ + while (ptr->fname) { + if (zend_hash_exists(target_function_table, ptr->fname, strlen(ptr->fname)+1)) { + zend_error(E_CORE_WARNING, "Function registration failed - duplicate name - %s",ptr->fname); + } + ptr++; + } + zend_unregister_functions(functions, count, target_function_table); + return FAILURE; + } + return SUCCESS; +} + +/* count=-1 means erase all functions, otherwise, + * erase the first count functions + */ +void zend_unregister_functions(zend_function_entry *functions, int count, HashTable *function_table) +{ + zend_function_entry *ptr = functions; + int i=0; + HashTable *target_function_table = function_table; + CLS_FETCH(); + + if (!target_function_table) { + target_function_table = CG(function_table); + } + while (ptr->fname) { + if (count!=-1 && i>=count) { + break; + } +#if 0 + zend_printf("Unregistering %s()\n", ptr->fname); +#endif + zend_hash_del(target_function_table, ptr->fname, strlen(ptr->fname)+1); + ptr++; + i++; + } +} + + +ZEND_API int zend_register_module(zend_module_entry *module) +{ +#if 0 + zend_printf("%s: Registering module %d\n",module->name, module->module_number); +#endif + if (module->functions && zend_register_functions(module->functions, NULL)==FAILURE) { + zend_error(E_CORE_WARNING,"%s: Unable to register functions, unable to load",module->name); + return FAILURE; + } + module->module_started=1; + return zend_hash_add(&module_registry, module->name,strlen(module->name)+1,(void *)module,sizeof(zend_module_entry),NULL); +} + + +void module_destructor(zend_module_entry *module) +{ + if (module->type == MODULE_TEMPORARY) { + zend_clean_module_rsrc_dtors(module->module_number); + clean_module_constants(module->module_number); + if (module->request_shutdown_func) + module->request_shutdown_func(module->type, module->module_number); + } + + if (module->module_started && module->module_shutdown_func) { +#if 0 + zend_printf("%s: Module shutdown\n",module->name); +#endif + module->module_shutdown_func(module->type, module->module_number); + } + module->module_started=0; + if (module->functions) { + zend_unregister_functions(module->functions, -1, NULL); + } + +#if HAVE_LIBDL + if (module->handle) { + dlclose(module->handle); + } +#endif +} + + +/* call request startup for all modules */ +int module_registry_request_startup(zend_module_entry *module) +{ + if (module->request_startup_func) { + ELS_FETCH(); + +#if 0 + zend_printf("%s: Request startup\n",module->name); +#endif + if (module->request_startup_func(module->type, module->module_number ELS_CC)==FAILURE) { + zend_error(E_WARNING, "request_startup() for %s module failed", module->name); + exit(1); + } + } + return 0; +} + + +/* for persistent modules - call request shutdown and flag NOT to erase + * for temporary modules - do nothing, and flag to erase + */ +int module_registry_cleanup(zend_module_entry *module) +{ + switch(module->type) { + case MODULE_PERSISTENT: + if (module->request_shutdown_func) { +#if 0 + zend_printf("%s: Request shutdown\n",module->name); +#endif + module->request_shutdown_func(module->type, module->module_number); + } + return 0; + break; + case MODULE_TEMPORARY: + return 1; + break; + } + return 0; +} + + +/* return the next free module number */ +int zend_next_free_module(void) +{ + return ++module_count; +} + +/* If parent_ce is not NULL then it inherits from parent_ce + * If parent_ce is NULL and parent_name isn't then it looks for the parent and inherits from it + * If both parent_ce and parent_name are NULL it does a regular class registration + * If parent_name is specified but not found NULL is returned + */ +ZEND_API zend_class_entry *zend_register_internal_class_ex(zend_class_entry *class_entry, zend_class_entry *parent_ce, char *parent_name) +{ + zend_class_entry *register_class; + CLS_FETCH(); + + if (!parent_ce && parent_name) { + if (zend_hash_find(CG(class_table), parent_name, strlen(parent_name)+1, (void **) &parent_ce)==FAILURE) { + return NULL; + } + } + + register_class = zend_register_internal_class(class_entry); + + if (parent_ce) { + do_inheritance(register_class, parent_ce); + } + return register_class; +} + +ZEND_API zend_class_entry *zend_register_internal_class(zend_class_entry *class_entry) +{ + zend_class_entry *register_class; + char *lowercase_name = zend_strndup(class_entry->name, class_entry->name_length); + CLS_FETCH(); + + zend_str_tolower(lowercase_name, class_entry->name_length); + + class_entry->type = ZEND_INTERNAL_CLASS; + class_entry->parent = NULL; + class_entry->refcount = (int *) malloc(sizeof(int)); + *class_entry->refcount = 1; + class_entry->constants_updated = 0; + zend_hash_init(&class_entry->default_properties, 0, NULL, ZVAL_PTR_DTOR, 1); + zend_hash_init(&class_entry->function_table, 0, NULL, ZEND_FUNCTION_DTOR, 1); + + + if (class_entry->builtin_functions) { + zend_register_functions(class_entry->builtin_functions, &class_entry->function_table); + } + + zend_hash_update(CG(class_table), lowercase_name, class_entry->name_length+1, class_entry, sizeof(zend_class_entry), (void **) ®ister_class); + free(lowercase_name); + return register_class; +} + + +ZEND_API zend_module_entry *zend_get_module(int module_number) +{ + zend_module_entry *module; + + if (zend_hash_index_find(&module_registry, module_number, (void **) &module)==SUCCESS) { + return module; + } else { + return NULL; + } +} + +ZEND_API int zend_set_hash_symbol(zval *symbol, char *name, int name_length, + int is_ref, int num_symbol_tables, ...) +{ + HashTable *symbol_table; + va_list symbol_table_list; + + if (num_symbol_tables <= 0) return FAILURE; + + symbol->is_ref = is_ref; + + va_start(symbol_table_list, num_symbol_tables); + while(num_symbol_tables-- > 0) { + symbol_table = va_arg(symbol_table_list, HashTable *); + zend_hash_update(symbol_table, name, name_length + 1, &symbol, sizeof(zval *), NULL); + zval_add_ref(&symbol); + } + va_end(symbol_table_list); + return SUCCESS; +} + + + + +/* Disabled functions support */ + +static ZEND_FUNCTION(display_disabled_function) +{ + zend_error(E_WARNING, "%s() has been disabled for security reasons.", get_active_function_name()); +} + + +static zend_function_entry disabled_function[] = { + ZEND_FE(display_disabled_function, NULL) + { NULL, NULL, NULL } +}; + + +ZEND_API int zend_disable_function(char *function_name, uint function_name_length) +{ + CLS_FETCH(); + + if (zend_hash_del(CG(function_table), function_name, function_name_length+1)==FAILURE) { + return FAILURE; + } + disabled_function[0].fname = function_name; + return zend_register_functions(disabled_function, CG(function_table)); +} diff --git a/src/main/resource/testFiles/php-src/revFiles/a347ed_d2b435_ext#ereg#ereg.c b/src/main/resource/testFiles/php-src/revFiles/a347ed_d2b435_ext#ereg#ereg.c new file mode 100644 index 0000000..13e06ba --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/a347ed_d2b435_ext#ereg#ereg.c @@ -0,0 +1,660 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2002 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jim Winstead | + | Jaakko Hyvätti | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include +#include "php.h" +#include "php_string.h" +#include "reg.h" +#include "ext/standard/info.h" + +#ifdef ZTS +int reg_globals_id; +#else +static php_reg_globals reg_globals; +#endif + +typedef struct { + regex_t preg; + int cflags; +} reg_cache; + +/* {{{ _php_regcomp + */ +static int _php_regcomp(regex_t *preg, const char *pattern, int cflags) +{ + int r = 0; + int patlen = strlen(pattern); + reg_cache *rc = NULL; + TSRMLS_FETCH(); + + if(zend_hash_find(®(ht_rc), (char *) pattern, patlen+1, (void **) &rc) == FAILURE || + rc->cflags != cflags) { + r = regcomp(preg, pattern, cflags); + if(!r) { + reg_cache rcp; + + rcp.cflags = cflags; + memcpy(&rcp.preg, preg, sizeof(*preg)); + zend_hash_update(®(ht_rc), (char *) pattern, patlen+1, + (void *) &rcp, sizeof(rcp), NULL); + } + } else { + memcpy(preg, &rc->preg, sizeof(*preg)); + } + + return r; +} +/* }}} */ + +static void _free_reg_cache(reg_cache *rc) +{ + regfree(&rc->preg); +} + +#undef regfree +#define regfree(a); +#undef regcomp +#define regcomp(a, b, c) _php_regcomp(a, b, c) + +static void php_reg_init_globals(php_reg_globals *reg_globals TSRMLS_DC) +{ + zend_hash_init(®_globals->ht_rc, 0, NULL, (void (*)(void *)) _free_reg_cache, 1); +} + +PHP_MINIT_FUNCTION(regex) +{ +#ifdef ZTS + ts_allocate_id(®_globals_id, sizeof(php_reg_globals), (ts_allocate_ctor) php_reg_init_globals, NULL); +#else + php_reg_init_globals(®_globals TSRMLS_CC); +#endif + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(regex) +{ + zend_hash_destroy(®(ht_rc)); + return SUCCESS; +} + +PHP_MINFO_FUNCTION(regex) +{ +#if HSREGEX + php_info_print_table_row(2, "Regex Library", "Bundled library enabled"); +#else + php_info_print_table_row(2, "Regex Library", "System library enabled"); +#endif +} + + +/* {{{ php_reg_eprint + * php_reg_eprint - convert error number to name + */ +static void php_reg_eprint(int err, regex_t *re) { + char *buf = NULL, *message = NULL; + size_t len; + size_t buf_len; + +#ifdef REG_ITOA + /* get the length of the message */ + buf_len = regerror(REG_ITOA | err, re, NULL, 0); + if (buf_len) { + buf = (char *)emalloc(buf_len * sizeof(char)); + if (!buf) return; /* fail silently */ + /* finally, get the error message */ + regerror(REG_ITOA | err, re, buf, buf_len); + } +#else + buf_len = 0; +#endif + len = regerror(err, re, NULL, 0); + if (len) { + message = (char *)emalloc((buf_len + len + 2) * sizeof(char)); + if (!message) { + return; /* fail silently */ + } + if (buf_len) { + snprintf(message, buf_len, "%s: ", buf); + buf_len += 1; /* so pointer math below works */ + } + /* drop the message into place */ + regerror(err, re, message + buf_len, len); + + php_error(E_WARNING, "%s", message); + } + + STR_FREE(buf); + STR_FREE(message); +} +/* }}} */ + +/* {{{ php_ereg + */ +static void php_ereg(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval **regex, /* Regular expression */ + **findin, /* String to apply expression to */ + **array = NULL; /* Optional register array */ + regex_t re; + regmatch_t *subs; + int err, match_len, string_len; + uint i; + int copts = 0; + off_t start, end; + char *buf = NULL; + char *string = NULL; + int argc = ZEND_NUM_ARGS(); + + if (argc < 2 || argc > 3 || + zend_get_parameters_ex(argc, ®ex, &findin, &array) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (icase) + copts |= REG_ICASE; + + if (argc == 2) + copts |= REG_NOSUB; + + /* compile the regular expression from the supplied regex */ + if (Z_TYPE_PP(regex) == IS_STRING) { + err = regcomp(&re, Z_STRVAL_PP(regex), REG_EXTENDED | copts); + } else { + /* we convert numbers to integers and treat them as a string */ + if (Z_TYPE_PP(regex) == IS_DOUBLE) + convert_to_long_ex(regex); /* get rid of decimal places */ + convert_to_string_ex(regex); + /* don't bother doing an extended regex with just a number */ + err = regcomp(&re, Z_STRVAL_PP(regex), copts); + } + + if (err) { + php_reg_eprint(err, &re); + RETURN_FALSE; + } + + /* make a copy of the string we're looking in */ + convert_to_string_ex(findin); + string = estrndup(Z_STRVAL_PP(findin), Z_STRLEN_PP(findin)); + + /* allocate storage for (sub-)expression-matches */ + subs = (regmatch_t *)ecalloc(sizeof(regmatch_t),re.re_nsub+1); + if (!subs) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg"); + RETURN_FALSE; + } + + /* actually execute the regular expression */ + err = regexec(&re, string, re.re_nsub+1, subs, 0); + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + regfree(&re); + efree(subs); + RETURN_FALSE; + } + match_len = 1; + + if (array && err != REG_NOMATCH) { + match_len = (int) (subs[0].rm_eo - subs[0].rm_so); + string_len = Z_STRLEN_PP(findin) + 1; + + buf = emalloc(string_len); + if (!buf) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg"); + regfree(&re); + efree(subs); + RETURN_FALSE; + } + + zval_dtor(*array); /* start with clean array */ + array_init(*array); + + for (i = 0; i <= re.re_nsub; i++) { + start = subs[i].rm_so; + end = subs[i].rm_eo; + if (start != -1 && end > 0 && start < string_len && end < string_len && start < end) { + add_index_stringl(*array, i, string+start, end-start, 1); + } else { + add_index_bool(*array, i, 0); + } + } + efree(buf); + } + + efree(subs); + efree(string); + if (err == REG_NOMATCH) { + RETVAL_FALSE; + } else { + if (match_len == 0) + match_len = 1; + RETVAL_LONG(match_len); + } + regfree(&re); +} +/* }}} */ + +/* {{{ proto int ereg(string pattern, string string [, array registers]) + Regular expression match */ +PHP_FUNCTION(ereg) +{ + php_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto int eregi(string pattern, string string [, array registers]) + Case-insensitive regular expression match */ +PHP_FUNCTION(eregi) +{ + php_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ php_reg_replace + * this is the meat and potatoes of regex replacement! */ +PHPAPI char *php_reg_replace(const char *pattern, const char *replace, const char *string, int icase, int extended) +{ + regex_t re; + regmatch_t *subs; + + char *buf, /* buf is where we build the replaced string */ + *nbuf, /* nbuf is used when we grow the buffer */ + *walkbuf; /* used to walk buf when replacing backrefs */ + const char *walk; /* used to walk replacement string for backrefs */ + int buf_len; + int pos, tmp, string_len, new_l; + int err, copts = 0; + + string_len = strlen(string); + + if (icase) + copts = REG_ICASE; + if (extended) + copts |= REG_EXTENDED; + + err = regcomp(&re, pattern, copts); + if (err) { + php_reg_eprint(err, &re); + return ((char *) -1); + } + + + /* allocate storage for (sub-)expression-matches */ + subs = (regmatch_t *)ecalloc(sizeof(regmatch_t),re.re_nsub+1); + if (!subs) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg_replace"); + return ((char *) -1); + } + + /* start with a buffer that is twice the size of the stringo + we're doing replacements in */ + buf_len = 2 * string_len + 1; + buf = emalloc(buf_len * sizeof(char)); + if (!buf) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg_replace"); + efree(subs); + regfree(&re); + return ((char *) -1); + } + + err = pos = 0; + buf[0] = '\0'; + while (!err) { + err = regexec(&re, &string[pos], re.re_nsub+1, subs, (pos ? REG_NOTBOL : 0)); + + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + efree(subs); + efree(buf); + regfree(&re); + return ((char *) -1); + } + + if (!err) { + /* backref replacement is done in two passes: + 1) find out how long the string will be, and allocate buf + 2) copy the part before match, replacement and backrefs to buf + + Jaakko Hyvätti + */ + + new_l = strlen(buf) + subs[0].rm_so; /* part before the match */ + walk = replace; + while (*walk) + if ('\\' == *walk + && '0' <= walk[1] && '9' >= walk[1] + && walk[1] - '0' <= re.re_nsub + && subs[walk[1] - '0'].rm_so > -1 + && subs[walk[1] - '0'].rm_eo > -1) { + new_l += subs[walk[1] - '0'].rm_eo + - subs[walk[1] - '0'].rm_so; + walk += 2; + } else { + new_l++; + walk++; + } + + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + tmp = strlen(buf); + /* copy the part of the string before the match */ + strncat(buf, &string[pos], subs[0].rm_so); + + /* copy replacement and backrefs */ + walkbuf = &buf[tmp + subs[0].rm_so]; + walk = replace; + while (*walk) + if ('\\' == *walk + && '0' <= walk[1] && '9' >= walk[1] + && walk[1] - '0' <= re.re_nsub + && subs[walk[1] - '0'].rm_so > -1 + && subs[walk[1] - '0'].rm_eo > -1 + /* this next case shouldn't happen. it does. */ + && subs[walk[1] - '0'].rm_so <= subs[walk[1] - '0'].rm_eo) { + tmp = subs[walk[1] - '0'].rm_eo + - subs[walk[1] - '0'].rm_so; + memcpy (walkbuf, + &string[pos + subs[walk[1] - '0'].rm_so], + tmp); + walkbuf += tmp; + walk += 2; + } else + *walkbuf++ = *walk++; + *walkbuf = '\0'; + + /* and get ready to keep looking for replacements */ + if (subs[0].rm_so == subs[0].rm_eo) { + if (subs[0].rm_so + pos >= string_len) + break; + new_l = strlen (buf) + 1; + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + pos += subs[0].rm_eo + 1; + buf [new_l-1] = string [pos-1]; + buf [new_l] = '\0'; + } else { + pos += subs[0].rm_eo; + } + } else { /* REG_NOMATCH */ + new_l = strlen(buf) + strlen(&string[pos]); + if (new_l + 1 > buf_len) { + buf_len = new_l + 1; /* now we know exactly how long it is */ + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + /* stick that last bit of string on our output */ + strcat(buf, &string[pos]); + } + } + + /* don't want to leak memory .. */ + efree(subs); + regfree(&re); + + /* whew. */ + return (buf); +} +/* }}} */ + +/* {{{ php_ereg_replace + */ +static void php_ereg_replace(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval **arg_pattern, + **arg_replace, + **arg_string; + char *pattern; + char *string; + char *replace; + char *ret; + + if (ZEND_NUM_ARGS() != 3 || + zend_get_parameters_ex(3, &arg_pattern, &arg_replace, &arg_string) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (Z_TYPE_PP(arg_pattern) == IS_STRING) { + if (Z_STRVAL_PP(arg_pattern) && Z_STRLEN_PP(arg_pattern)) + pattern = estrndup(Z_STRVAL_PP(arg_pattern), Z_STRLEN_PP(arg_pattern)); + else + pattern = empty_string; + } else { + convert_to_long_ex(arg_pattern); + pattern = emalloc(2); + pattern[0] = (char) Z_LVAL_PP(arg_pattern); + pattern[1] = '\0'; + } + + if (Z_TYPE_PP(arg_replace) == IS_STRING) { + if (Z_STRVAL_PP(arg_replace) && Z_STRLEN_PP(arg_replace)) + replace = estrndup(Z_STRVAL_PP(arg_replace), Z_STRLEN_PP(arg_replace)); + else + replace = empty_string; + } else { + convert_to_long_ex(arg_replace); + replace = emalloc(2); + replace[0] = (char) Z_LVAL_PP(arg_replace); + replace[1] = '\0'; + } + + convert_to_string_ex(arg_string); + if (Z_STRVAL_PP(arg_string) && Z_STRLEN_PP(arg_string)) + string = estrndup(Z_STRVAL_PP(arg_string), Z_STRLEN_PP(arg_string)); + else + string = empty_string; + + /* do the actual work */ + ret = php_reg_replace(pattern, replace, string, icase, 1); + if (ret == (char *) -1) { + RETVAL_FALSE; + } else { + RETVAL_STRING(ret, 1); + STR_FREE(ret); + } + + STR_FREE(string); + STR_FREE(replace); + STR_FREE(pattern); +} +/* }}} */ + +/* {{{ proto string ereg_replace(string pattern, string replacement, string string) + Replace regular expression */ +PHP_FUNCTION(ereg_replace) +{ + php_ereg_replace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto string eregi_replace(string pattern, string replacement, string string) + Case insensitive replace regular expression */ +PHP_FUNCTION(eregi_replace) +{ + php_ereg_replace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ php_split + */ +static void php_split(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + zval **spliton, **str, **arg_count = NULL; + regex_t re; + regmatch_t subs[1]; + char *strp, *endp; + int err, size, count = -1, copts = 0; + int argc = ZEND_NUM_ARGS(); + + if (argc < 2 || argc > 3 || + zend_get_parameters_ex(argc, &spliton, &str, &arg_count) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (argc > 2) { + convert_to_long_ex(arg_count); + count = Z_LVAL_PP(arg_count); + } + + if (icase) + copts = REG_ICASE; + + convert_to_string_ex(spliton); + convert_to_string_ex(str); + + strp = Z_STRVAL_PP(str); + endp = strp + Z_STRLEN_PP(str); + + err = regcomp(&re, Z_STRVAL_PP(spliton), REG_EXTENDED | copts); + if (err) { + php_reg_eprint(err, &re); + RETURN_FALSE; + } + + if (array_init(return_value) == FAILURE) { + regfree(&re); + RETURN_FALSE; + } + + /* churn through str, generating array entries as we go */ + while ((count == -1 || count > 1) && !(err = regexec(&re, strp, 1, subs, 0))) { + if (subs[0].rm_so == 0 && subs[0].rm_eo) { + /* match is at start of string, return empty string */ + add_next_index_stringl(return_value, empty_string, 0, 1); + /* skip ahead the length of the regex match */ + strp += subs[0].rm_eo; + } else if (subs[0].rm_so == 0 && subs[0].rm_eo == 0) { + /* No more matches */ + regfree(&re); + php_error(E_WARNING, "Invalid Regular Expression to split()"); + zend_hash_destroy(Z_ARRVAL_P(return_value)); + efree(Z_ARRVAL_P(return_value)); + RETURN_FALSE; + } else { + /* On a real match */ + + /* make a copy of the substring */ + size = subs[0].rm_so; + + /* add it to the array */ + add_next_index_stringl(return_value, strp, size, 1); + + /* point at our new starting point */ + strp = strp + subs[0].rm_eo; + } + + /* if we're only looking for a certain number of points, + stop looking once we hit it */ + if (count != -1) { + count--; + } + } + + /* see if we encountered an error */ + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + regfree(&re); + zend_hash_destroy(Z_ARRVAL_P(return_value)); + efree(Z_ARRVAL_P(return_value)); + RETURN_FALSE; + } + + /* otherwise we just have one last element to add to the array */ + size = endp - strp; + + add_next_index_stringl(return_value, strp, size, 1); + + regfree(&re); +} +/* }}} */ + +/* {{{ proto array split(string pattern, string string [, int limit]) + Split string into array by regular expression */ +PHP_FUNCTION(split) +{ + php_split(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto array spliti(string pattern, string string [, int limit]) + Split string into array by regular expression case-insensitive */ + +PHP_FUNCTION(spliti) +{ + php_split(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} + +/* }}} */ + +/* {{{ proto string sql_regcase(string string) + Make regular expression for case insensitive match */ +PHPAPI PHP_FUNCTION(sql_regcase) +{ + zval **string; + char *tmp; + unsigned char c; + register int i, j; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &string)==FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_string_ex(string); + + tmp = emalloc((Z_STRLEN_PP(string) * 4) + 1); + + for (i = j = 0; i < Z_STRLEN_PP(string); i++) { + c = (unsigned char) Z_STRVAL_PP(string)[i]; + if(isalpha(c)) { + tmp[j++] = '['; + tmp[j++] = toupper(c); + tmp[j++] = tolower(c); + tmp[j++] = ']'; + } else { + tmp[j++] = c; + } + } + tmp[j] = 0; + + RETVAL_STRINGL(tmp, j, 1); + efree(tmp); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/b88698_34b3dc_ext#standard#reg.c b/src/main/resource/testFiles/php-src/revFiles/b88698_34b3dc_ext#standard#reg.c new file mode 100644 index 0000000..1373933 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/b88698_34b3dc_ext#standard#reg.c @@ -0,0 +1,664 @@ +/* + +----------------------------------------------------------------------+ + | PHP HTML Embedded Scripting Language Version 3.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997,1998 PHP Development Team (See Credits file) | + +----------------------------------------------------------------------+ + | This program is free software; you can redistribute it and/or modify | + | it under the terms of one of the following licenses: | + | | + | A) the GNU General Public License as published by the Free Software | + | Foundation; either version 2 of the License, or (at your option) | + | any later version. | + | | + | B) the PHP License as published by the PHP Development Team and | + | included in the distribution in the file: LICENSE | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + | | + | You should have received a copy of both licenses referred to here. | + | If you did not, or have any questions about PHP licensing, please | + | contact core@php.net. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jim Winstead | + | Jaakko Hyvätti | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include +#include "php.h" +#include "php3_string.h" +#include "php_regex.h" +#include "reg.h" + +unsigned char third_argument_force_ref[] = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE }; + +function_entry reg_functions[] = { + {"ereg", php3_ereg, third_argument_force_ref }, + {"ereg_replace", php3_eregreplace, NULL }, + {"eregi", php3_eregi, third_argument_force_ref }, + {"eregi_replace", php3_eregireplace, NULL }, + {"split", php3_split, NULL}, + {"join", php3_implode, NULL}, + {"sql_regcase", php3_sql_regcase, NULL}, + {NULL, NULL, NULL} +}; + + +static int php_minit_regex(INIT_FUNC_ARGS); +static int php_mshutdown_regex(SHUTDOWN_FUNC_ARGS); +static void php_info_regex(ZEND_MODULE_INFO_FUNC_ARGS); + +zend_module_entry regexp_module_entry = { + "Regular Expressions", + reg_functions, + php_minit_regex, php_mshutdown_regex, + NULL, NULL, php_info_regex, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef ZTS +int reg_globals_id; +#else +static php_reg_globals reg_globals; +#endif + +typedef struct { + regex_t preg; + int cflags; +} reg_cache; + +static int _php_regcomp(regex_t *preg, const char *pattern, int cflags) +{ + int r = 0; + int patlen = strlen(pattern); + reg_cache *rc = NULL; + REGLS_FETCH(); + + if(_php3_hash_find(®(ht_rc), (char *) pattern, patlen+1, (void **) &rc) == FAILURE || + rc->cflags != cflags) { + r = regcomp(preg, pattern, cflags); + if(!r) { + reg_cache rcp; + + rcp.cflags = cflags; + memcpy(&rcp.preg, preg, sizeof(*preg)); + _php3_hash_update(®(ht_rc), (char *) pattern, patlen+1, + (void *) &rcp, sizeof(rcp), NULL); + } + } else { + memcpy(preg, &rc->preg, sizeof(*preg)); + } + + return r; +} + +static void _free_reg_cache(reg_cache *rc) +{ + regfree(&rc->preg); +} + +#define regfree(a); +#define regcomp(a,b,c) _php_regcomp(a,b,c) + +static void php_reg_init_globals(php_reg_globals *reg_globals) +{ + _php3_hash_init(®_globals->ht_rc, 0, NULL, (void (*)(void *)) _free_reg_cache, 1); +} + +static int php_minit_regex(INIT_FUNC_ARGS) +{ +#ifdef ZTS + reg_globals_id = ts_allocate_id(sizeof(php_reg_globals), php_reg_init_globals, NULL); +#else + php_reg_init_globals(®_globals); +#endif + + return SUCCESS; +} + +static int php_mshutdown_regex(SHUTDOWN_FUNC_ARGS) +{ + REGLS_FETCH(); + + _php3_hash_destroy(®(ht_rc)); + return SUCCESS; +} + +static void php_info_regex(ZEND_MODULE_INFO_FUNC_ARGS) +{ +#if HSREGEX + PUTS("Bundled regex library enabled\n"); +#else + PUTS("System regex library enabled\n"); +#endif +} + + +/* This is the maximum number of (..) constructs we'll generate from a + call to ereg() or eregi() with the optional third argument. */ +#define NS 10 + +/* + * _php3_reg_eprint - convert error number to name + */ +static void _php3_reg_eprint(int err, regex_t *re) { + char *buf = NULL, *message = NULL; + size_t len; + size_t buf_len; + +#ifdef REG_ITOA + /* get the length of the message */ + buf_len = regerror(REG_ITOA | err, re, NULL, 0); + if (buf_len) { + buf = (char *)emalloc(buf_len * sizeof(char)); + if (!buf) return; /* fail silently */ + /* finally, get the error message */ + regerror(REG_ITOA | err, re, buf, buf_len); + } +#else + buf_len = 0; +#endif + len = regerror(err, re, NULL, 0); + if (len) { + message = (char *)emalloc((buf_len + len + 2) * sizeof(char)); + if (!message) { + return; /* fail silently */ + } + if (buf_len) { + snprintf(message, buf_len, "%s: ", buf); + buf_len += 1; /* so pointer math below works */ + } + /* drop the message into place */ + regerror(err, re, message + buf_len, len); + + php3_error(E_WARNING, "%s", message); + } + + STR_FREE(buf); + STR_FREE(message); +} + +static void _php3_ereg(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval *regex, /* Regular expression */ + *findin, /* String to apply expression to */ + *array = NULL; /* Optional register array */ + regex_t re; + regmatch_t subs[NS]; + int err, i, match_len, string_len; + int copts = 0; + off_t start, end; + char *buf = NULL; + char *string = NULL; + + if (icase) + copts |= REG_ICASE; + + switch(ARG_COUNT(ht)) { + case 2: + if (getParameters(ht, 2, ®ex, &findin) == FAILURE) { + WRONG_PARAM_COUNT; + } + /* don't bother doing substring matching if we're not going + to make use of the information */ + copts |= REG_NOSUB; + break; + case 3: + if (getParameters(ht, 3, ®ex, &findin, &array) == FAILURE) { + WRONG_PARAM_COUNT; + } + if (!ParameterPassedByReference(ht, 3)) { + php3_error(E_WARNING, "Array to be filled with values must be passed by reference."); + RETURN_FALSE; + } + break; + default: + WRONG_PARAM_COUNT; + } + + + /* compile the regular expression from the supplied regex */ + if (regex->type == IS_STRING) { + err = regcomp(&re, regex->value.str.val, REG_EXTENDED | copts); + } else { + /* we convert numbers to integers and treat them as a string */ + if (regex->type == IS_DOUBLE) + convert_to_long(regex); /* get rid of decimal places */ + convert_to_string(regex); + /* don't bother doing an extended regex with just a number */ + err = regcomp(&re, regex->value.str.val, copts); + } + + if (err) { + _php3_reg_eprint(err, &re); + RETURN_FALSE; + } + + /* make a copy of the string we're looking in */ + convert_to_string(findin); + string = estrndup(findin->value.str.val, findin->value.str.len); + + /* actually execute the regular expression */ + err = regexec(&re, string, (size_t) NS, subs, 0); + if (err && err != REG_NOMATCH) { + _php3_reg_eprint(err, &re); + regfree(&re); + RETURN_FALSE; + } + match_len = 1; + + if (array && err != REG_NOMATCH) { + match_len = (int) (subs[0].rm_eo - subs[0].rm_so); + string_len = strlen(string) + 1; + + buf = emalloc(string_len); + if (!buf) { + php3_error(E_WARNING, "Unable to allocate memory in _php3_ereg"); + RETURN_FALSE; + } + + pval_destructor(array); /* start with clean array */ + array_init(array); + + for (i = 0; i < NS; i++) { + start = subs[i].rm_so; + end = subs[i].rm_eo; + if (start != -1 && end > 0 && start < string_len && end < string_len && start < end) { + add_index_stringl(array, i, string+start, end-start, 1); + } + } + efree(buf); + } + + efree(string); + if (err == REG_NOMATCH) { + RETVAL_FALSE; + } else { + if (match_len == 0) + match_len = 1; + RETVAL_LONG(match_len); + } + regfree(&re); +} + +/* {{{ proto int ereg(string pattern, string string [, array registers]) + Regular expression match */ +PHP_FUNCTION(ereg) +{ + _php3_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto int eregi(string pattern, string string [, array registers]) + Case-insensitive regular expression match */ +PHP_FUNCTION(eregi) +{ + _php3_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* this is the meat and potatoes of regex replacement! */ +char *_php3_regreplace(const char *pattern, const char *replace, const char *string, int icase, int extended) +{ + regex_t re; + regmatch_t subs[NS]; + + char *buf, /* buf is where we build the replaced string */ + *nbuf, /* nbuf is used when we grow the buffer */ + *walkbuf; /* used to walk buf when replacing backrefs */ + const char *walk; /* used to walk replacement string for backrefs */ + int buf_len; + int pos, tmp, string_len, new_l; + int err, copts = 0; + + string_len = strlen(string); + if (!string_len) + return estrndup("", 0); + + if (icase) + copts = REG_ICASE; + if (extended) + copts |= REG_EXTENDED; + err = regcomp(&re, pattern, copts); + if (err) { + _php3_reg_eprint(err, &re); + return ((char *) -1); + } + + /* start with a buffer that is twice the size of the stringo + we're doing replacements in */ + buf_len = 2 * string_len + 1; + buf = emalloc(buf_len * sizeof(char)); + if (!buf) { + php3_error(E_WARNING, "Unable to allocate memory in _php3_regreplace"); + regfree(&re); + return ((char *) -1); + } + + err = pos = 0; + buf[0] = '\0'; + + while (!err) { + err = regexec(&re, &string[pos], (size_t) NS, subs, (pos ? REG_NOTBOL : 0)); + + if (err && err != REG_NOMATCH) { + _php3_reg_eprint(err, &re); + regfree(&re); + return ((char *) -1); + } + if (!err) { + /* backref replacement is done in two passes: + 1) find out how long the string will be, and allocate buf + 2) copy the part before match, replacement and backrefs to buf + + Jaakko Hyvätti + */ + + new_l = strlen(buf) + subs[0].rm_so; /* part before the match */ + walk = replace; + while (*walk) + if ('\\' == *walk + && '0' <= walk[1] && '9' >= walk[1] + && subs[walk[1] - '0'].rm_so > -1 + && subs[walk[1] - '0'].rm_eo > -1) { + new_l += subs[walk[1] - '0'].rm_eo + - subs[walk[1] - '0'].rm_so; + walk += 2; + } else { + new_l++; + walk++; + } + + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + tmp = strlen(buf); + /* copy the part of the string before the match */ + strncat(buf, &string[pos], subs[0].rm_so); + + /* copy replacement and backrefs */ + walkbuf = &buf[tmp + subs[0].rm_so]; + walk = replace; + while (*walk) + if ('\\' == *walk + && '0' <= walk[1] && '9' >= walk[1] + && subs[walk[1] - '0'].rm_so > -1 + && subs[walk[1] - '0'].rm_eo > -1) { + tmp = subs[walk[1] - '0'].rm_eo + - subs[walk[1] - '0'].rm_so; + memcpy (walkbuf, + &string[pos + subs[walk[1] - '0'].rm_so], + tmp); + walkbuf += tmp; + walk += 2; + } else + *walkbuf++ = *walk++; + *walkbuf = '\0'; + + /* and get ready to keep looking for replacements */ + if (subs[0].rm_so == subs[0].rm_eo) { + if (subs[0].rm_so + pos >= string_len) + break; + new_l = strlen (buf) + 1; + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + pos += subs[0].rm_eo + 1; + buf [new_l-1] = string [pos-1]; + buf [new_l] = '\0'; + } else { + pos += subs[0].rm_eo; + } + } else { /* REG_NOMATCH */ + new_l = strlen(buf) + strlen(&string[pos]); + if (new_l + 1 > buf_len) { + buf_len = new_l + 1; /* now we know exactly how long it is */ + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + /* stick that last bit of string on our output */ + strcat(buf, &string[pos]); + } + } + + /* don't want to leak memory .. */ + regfree(&re); + + /* whew. */ + return (buf); +} + +static void _php3_eregreplace(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval *arg_pattern, + *arg_replace, + *arg_string; + char *pattern; + char *string; + char *replace; + char *ret; + + if (ARG_COUNT(ht) != 3 || getParameters(ht, 3, &arg_pattern, &arg_replace, &arg_string) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (arg_pattern->type == IS_STRING) { + if (arg_pattern->value.str.val && arg_pattern->value.str.len) + pattern = estrndup(arg_pattern->value.str.val,arg_pattern->value.str.len); + else + pattern = empty_string; + } else { + convert_to_long(arg_pattern); + pattern = emalloc(2); + pattern[0] = (char) arg_pattern->value.lval; + pattern[1] = '\0'; + } + + if (arg_replace->type == IS_STRING) { + if (arg_replace->value.str.val && arg_replace->value.str.len) + replace = estrndup(arg_replace->value.str.val, arg_replace->value.str.len); + else + replace = empty_string; + } else { + convert_to_long(arg_replace); + replace = emalloc(2); + replace[0] = (char) arg_replace->value.lval; + replace[1] = '\0'; + } + + convert_to_string(arg_string); + if (arg_string->value.str.val && arg_string->value.str.len) + string = estrndup(arg_string->value.str.val, arg_string->value.str.len); + else + string = empty_string; + + /* do the actual work */ + ret = _php3_regreplace(pattern, replace, string, icase, 1); + if (ret == (char *) -1) { + RETVAL_FALSE; + } else { + RETVAL_STRING(ret,1); + STR_FREE(ret); + } + STR_FREE(string); + STR_FREE(replace); + STR_FREE(pattern); +} + +/* {{{ proto string ereg_replace(string pattern, string string [, array registers]) + Replace regular expression */ +PHP_FUNCTION(eregreplace) +{ + _php3_eregreplace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto string eregi_replace(string pattern, string string [, array registers]) + Case insensitive replace regular expression */ +PHP_FUNCTION(eregireplace) +{ + _php3_eregreplace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* ("root", "passwd", "uid", "gid", "other:stuff:like:/bin/sh") + = split(":", $passwd_file, 5); */ +/* {{{ proto array split(string pattern, string string [, int limit]) + split string into array by regular expression */ +PHP_FUNCTION(split) +{ + pval *spliton, *str, *arg_count = NULL; + regex_t re; + regmatch_t subs[1]; + char *strp, *endp; + int err, size, count; + + switch (ARG_COUNT(ht)) { + case 2: + if (getParameters(ht, 2, &spliton, &str) == FAILURE) + WRONG_PARAM_COUNT; + count = -1; + break; + case 3: + if (getParameters(ht, 3, &spliton, &str, &arg_count) == FAILURE) + WRONG_PARAM_COUNT; + convert_to_long(arg_count); + count = arg_count->value.lval; + break; + default: + WRONG_PARAM_COUNT; + } + + convert_to_string(spliton); + convert_to_string(str); + + strp = str->value.str.val; + endp = str->value.str.val + strlen(str->value.str.val); + + err = regcomp(&re, spliton->value.str.val, REG_EXTENDED); + if (err) { + php3_error(E_WARNING, "unexpected regex error (%d)", err); + RETURN_FALSE; + } + + if (array_init(return_value) == FAILURE) { + regfree(&re); + RETURN_FALSE; + } + + /* churn through str, generating array entries as we go */ + while ((count == -1 || count > 0) && !(err = regexec(&re, strp, 1, subs, 0))) { + if (subs[0].rm_so == 0 && subs[0].rm_eo) { + /* match is at start of string, return empty string */ + add_next_index_stringl(return_value, empty_string, 0, 1); + /* skip ahead the length of the regex match */ + strp+=subs[0].rm_eo; + } else if (subs[0].rm_so==0 && subs[0].rm_eo==0) { + /* No more matches */ + regfree(&re); + php3_error(E_WARNING, "bad regular expression for split()"); + _php3_hash_destroy(return_value->value.ht); + efree(return_value->value.ht); + RETURN_FALSE; + } else { + /* On a real match */ + + /* make a copy of the substring */ + size = subs[0].rm_so; + + /* add it to the array */ + add_next_index_stringl(return_value, strp, size, 1); + + /* point at our new starting point */ + strp = strp + subs[0].rm_eo; + } + + /* if we're only looking for a certain number of points, + stop looking once we hit it */ + if (count != -1) { + count--; + } + } + + /* see if we encountered an error */ + if (err && err != REG_NOMATCH) { + php3_error(E_WARNING, "unexpected regex error (%d)", err); + regfree(&re); + _php3_hash_destroy(return_value->value.ht); + efree(return_value->value.ht); + RETURN_FALSE; + } + + /* otherwise we just have one last element to add to the array */ + if (count == -1 || err==REG_NOMATCH) { + size = endp - strp; + + add_next_index_stringl(return_value, strp, size, 1); + } + + regfree(&re); + + return; +} +/* }}} */ + +/* {{{ proto string sql_regcase(string string) + Make regular expression for case insensitive match */ +PHPAPI PHP_FUNCTION(sql_regcase) +{ + pval *string; + char *tmp; + unsigned char c; + register int i, j; + + if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &string)==FAILURE) { + WRONG_PARAM_COUNT; + } + + convert_to_string(string); + + tmp = (char *) emalloc(string->value.str.len*4+1); + + for (i=j=0; ivalue.str.len; i++) { + c = (unsigned char) string->value.str.val[i]; + if(isalpha(c)) { + tmp[j++] = '['; + tmp[j++] = toupper(c); + tmp[j++] = tolower(c); + tmp[j++] = ']'; + } else { + tmp[j++] = c; + } + } + tmp[j]=0; + + tmp = erealloc(tmp, j + 1); + + RETVAL_STRINGL(tmp, j, 0); +} +/* }}} */ + + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/bb5faa_c5152b_TSRM#tsrm_virtual_cwd.c b/src/main/resource/testFiles/php-src/revFiles/bb5faa_c5152b_TSRM#tsrm_virtual_cwd.c new file mode 100644 index 0000000..b88bae9 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/bb5faa_c5152b_TSRM#tsrm_virtual_cwd.c @@ -0,0 +1,1942 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Sascha Schumann | + | Pierre Joye | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tsrm_virtual_cwd.h" +#include "tsrm_strtok_r.h" + +#ifdef TSRM_WIN32 +#include +#include "tsrm_win32.h" +# ifndef IO_REPARSE_TAG_SYMLINK +# define IO_REPARSE_TAG_SYMLINK 0xA000000C +# endif + +# ifndef VOLUME_NAME_NT +# define VOLUME_NAME_NT 0x2 +# endif + +# ifndef VOLUME_NAME_DOS +# define VOLUME_NAME_DOS 0x0 +# endif +#endif + +#ifndef S_IFLNK +# define S_IFLNK 0120000 +#endif + +#ifdef NETWARE +#include +#endif + +#ifndef HAVE_REALPATH +#define realpath(x,y) strcpy(y,x) +#endif + +#define VIRTUAL_CWD_DEBUG 0 + +#include "TSRM.h" + +/* Only need mutex for popen() in Windows and NetWare because it doesn't chdir() on UNIX */ +#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS) +MUTEX_T cwd_mutex; +#endif + +#ifdef ZTS +ts_rsrc_id cwd_globals_id; +#else +virtual_cwd_globals cwd_globals; +#endif + +cwd_state main_cwd_state; /* True global */ + +#ifndef TSRM_WIN32 +#include +#else +#include +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) ((mode) & _S_IFDIR) +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) ((mode) & _S_IFREG) +#endif + +#ifdef TSRM_WIN32 +#include +#define tsrm_strtok_r(a,b,c) _tcstok((a),(b)) +#define TOKENIZER_STRING "/\\" + +static int php_check_dots(const char *element, int n) +{ + while (n-- > 0) if (element[n] != '.') break; + + return (n != -1); +} + +#define IS_DIRECTORY_UP(element, len) \ + (len >= 2 && !php_check_dots(element, len)) + +#define IS_DIRECTORY_CURRENT(element, len) \ + (len == 1 && element[0] == '.') + +#elif defined(NETWARE) +/* NetWare has strtok() (in LibC) and allows both slashes in paths, like Windows -- + but rest of the stuff is like Unix */ +/* strtok() call in LibC is abending when used in a different address space -- hence using + PHP's version itself for now */ +/*#define tsrm_strtok_r(a,b,c) strtok((a),(b))*/ +#define TOKENIZER_STRING "/\\" + +#else +#define TOKENIZER_STRING "/" +#endif + + +/* default macros */ + +#ifndef IS_DIRECTORY_UP +#define IS_DIRECTORY_UP(element, len) \ + (len == 2 && element[0] == '.' && element[1] == '.') +#endif + +#ifndef IS_DIRECTORY_CURRENT +#define IS_DIRECTORY_CURRENT(element, len) \ + (len == 1 && element[0] == '.') +#endif + +/* define this to check semantics */ +#define IS_DIR_OK(s) (1) + +#ifndef IS_DIR_OK +#define IS_DIR_OK(state) (php_is_dir_ok(state) == 0) +#endif + + +#define CWD_STATE_COPY(d, s) \ + (d)->cwd_length = (s)->cwd_length; \ + (d)->cwd = (char *) malloc((s)->cwd_length+1); \ + memcpy((d)->cwd, (s)->cwd, (s)->cwd_length+1); + +#define CWD_STATE_FREE(s) \ + free((s)->cwd); + +#ifdef TSRM_WIN32 + +#ifdef CTL_CODE +#undef CTL_CODE +#endif +#define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define METHOD_BUFFERED 0 +#define FILE_ANY_ACCESS 0 +#define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE ( 16 * 1024 ) + +typedef struct { + unsigned long ReparseTag; + unsigned short ReparseDataLength; + unsigned short Reserved; + union { + struct { + unsigned short SubstituteNameOffset; + unsigned short SubstituteNameLength; + unsigned short PrintNameOffset; + unsigned short PrintNameLength; + unsigned long Flags; + wchar_t ReparseTarget[1]; + } SymbolicLinkReparseBuffer; + struct { + unsigned short SubstituteNameOffset; + unsigned short SubstituteNameLength; + unsigned short PrintNameOffset; + unsigned short PrintNameLength; + wchar_t ReparseTarget[1]; + } MountPointReparseBuffer; + struct { + unsigned char ReparseTarget[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER; + +#define SECS_BETWEEN_EPOCHS (__int64)11644473600 +#define SECS_TO_100NS (__int64)10000000 +static inline time_t FileTimeToUnixTime(const FILETIME FileTime) +{ + __int64 UnixTime; + long *nsec = NULL; + SYSTEMTIME SystemTime; + FileTimeToSystemTime(&FileTime, &SystemTime); + + UnixTime = ((__int64)FileTime.dwHighDateTime << 32) + + FileTime.dwLowDateTime; + + UnixTime -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS); + + if (nsec) { + *nsec = (UnixTime % SECS_TO_100NS) * (__int64)100; + } + + UnixTime /= SECS_TO_100NS; /* now convert to seconds */ + + if ((time_t)UnixTime != UnixTime) { + UnixTime = 0; + } + return (time_t)UnixTime; +} + +CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len){ /* {{{ */ + HINSTANCE kernel32; + HANDLE hFile; + DWORD dwRet; + + typedef BOOL (WINAPI *gfpnh_func)(HANDLE, LPTSTR, DWORD, DWORD); + gfpnh_func pGetFinalPathNameByHandle; + + kernel32 = LoadLibrary("kernel32.dll"); + + if (kernel32) { + pGetFinalPathNameByHandle = (gfpnh_func)GetProcAddress(kernel32, "GetFinalPathNameByHandleA"); + if (pGetFinalPathNameByHandle == NULL) { + return -1; + } + } else { + return -1; + } + + hFile = CreateFile(link, // file to open + GENERIC_READ, // open for reading + FILE_SHARE_READ, // share for reading + NULL, // default security + OPEN_EXISTING, // existing file only + FILE_FLAG_BACKUP_SEMANTICS, // normal file + NULL); // no attr. template + + if( hFile == INVALID_HANDLE_VALUE) { + return -1; + } + + dwRet = pGetFinalPathNameByHandle(hFile, target, MAXPATHLEN, VOLUME_NAME_DOS); + if(dwRet >= MAXPATHLEN) { + return -1; + } + + CloseHandle(hFile); + + if(dwRet > 4) { + /* Skip first 4 characters if they are "\??\" */ + if(target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] == '\\') { + char tmp[MAXPATHLEN]; + unsigned int offset = 4; + dwRet -= 4; + + /* \??\UNC\ */ + if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') { + offset += 2; + dwRet -= 2; + target[offset] = '\\'; + } + + memcpy(tmp, target + offset, dwRet); + memcpy(target, tmp, dwRet); + } + } + + target[dwRet] = '\0'; + return dwRet; +} +/* }}} */ + +CWD_API int php_sys_stat_ex(const char *path, struct stat *buf, int lstat) /* {{{ */ +{ + WIN32_FILE_ATTRIBUTE_DATA data; + __int64 t; + const size_t path_len = strlen(path); + + if (!GetFileAttributesEx(path, GetFileExInfoStandard, &data)) { + return stat(path, buf); + } + + if (path_len >= 1 && path[1] == ':') { + if (path[0] >= 'A' && path[0] <= 'Z') { + buf->st_dev = buf->st_rdev = path[0] - 'A'; + } else { + buf->st_dev = buf->st_rdev = path[0] - 'a'; + } + } else if (IS_UNC_PATH(path, path_len)) { + buf->st_dev = buf->st_rdev = 0; + } else { + char cur_path[MAXPATHLEN+1]; + DWORD len = sizeof(cur_path); + char *tmp = cur_path; + + while(1) { + DWORD r = GetCurrentDirectory(len, tmp); + if (r < len) { + if (tmp[1] == ':') { + if (path[0] >= 'A' && path[0] <= 'Z') { + buf->st_dev = buf->st_rdev = path[0] - 'A'; + } else { + buf->st_dev = buf->st_rdev = path[0] - 'a'; + } + } else { + buf->st_dev = buf->st_rdev = -1; + } + break; + } else if (!r) { + buf->st_dev = buf->st_rdev = -1; + break; + } else { + len = r+1; + tmp = (char*)malloc(len); + } + } + if (tmp != cur_path) { + free(tmp); + } + } + + buf->st_uid = buf->st_gid = buf->st_ino = 0; + + if (lstat && data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + /* File is a reparse point. Get the target */ + HANDLE hLink = NULL; + REPARSE_DATA_BUFFER * pbuffer; + unsigned int retlength = 0; + TSRM_ALLOCA_FLAG(use_heap_large); + + hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); + if(hLink == INVALID_HANDLE_VALUE) { + return -1; + } + + pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large); + if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) { + tsrm_free_alloca(pbuffer, use_heap_large); + CloseHandle(hLink); + return -1; + } + + CloseHandle(hLink); + + if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + buf->st_mode = S_IFLNK; + buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6)); + } + +#if 0 /* Not used yet */ + else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { + buf->st_mode |=; + } +#endif + tsrm_free_alloca(pbuffer, use_heap_large); + } else { + buf->st_mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) : S_IFREG; + buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6)); + } + + if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + int len = strlen(path); + + if (path[len-4] == '.') { + if (_memicmp(path+len-3, "exe", 3) == 0 || + _memicmp(path+len-3, "com", 3) == 0 || + _memicmp(path+len-3, "bat", 3) == 0 || + _memicmp(path+len-3, "cmd", 3) == 0) { + buf->st_mode |= (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)); + } + } + } + + buf->st_nlink = 1; + t = data.nFileSizeHigh; + t = t << 32; + t |= data.nFileSizeLow; + buf->st_size = t; + buf->st_atime = FileTimeToUnixTime(data.ftLastAccessTime); + buf->st_ctime = FileTimeToUnixTime(data.ftCreationTime); + buf->st_mtime = FileTimeToUnixTime(data.ftLastWriteTime); + return 0; +} +/* }}} */ +#endif + +static int php_is_dir_ok(const cwd_state *state) /* {{{ */ +{ + struct stat buf; + + if (php_sys_stat(state->cwd, &buf) == 0 && S_ISDIR(buf.st_mode)) + return (0); + + return (1); +} +/* }}} */ + +static int php_is_file_ok(const cwd_state *state) /* {{{ */ +{ + struct stat buf; + + if (php_sys_stat(state->cwd, &buf) == 0 && S_ISREG(buf.st_mode)) + return (0); + + return (1); +} +/* }}} */ + +static void cwd_globals_ctor(virtual_cwd_globals *cwd_g TSRMLS_DC) /* {{{ */ +{ + CWD_STATE_COPY(&cwd_g->cwd, &main_cwd_state); + cwd_g->realpath_cache_size = 0; + cwd_g->realpath_cache_size_limit = REALPATH_CACHE_SIZE; + cwd_g->realpath_cache_ttl = REALPATH_CACHE_TTL; + memset(cwd_g->realpath_cache, 0, sizeof(cwd_g->realpath_cache)); +} +/* }}} */ + +static void cwd_globals_dtor(virtual_cwd_globals *cwd_g TSRMLS_DC) /* {{{ */ +{ + CWD_STATE_FREE(&cwd_g->cwd); + realpath_cache_clean(TSRMLS_C); +} +/* }}} */ + +CWD_API void virtual_cwd_startup(void) /* {{{ */ +{ + char cwd[MAXPATHLEN]; + char *result; + +#ifdef NETWARE + result = getcwdpath(cwd, NULL, 1); + if(result) + { + char *c=cwd; + while(c = strchr(c, '\\')) + { + *c='/'; + ++c; + } + } +#else + result = getcwd(cwd, sizeof(cwd)); +#endif + if (!result) { + cwd[0] = '\0'; + } + + main_cwd_state.cwd_length = strlen(cwd); +#ifdef TSRM_WIN32 + if (main_cwd_state.cwd_length >= 2 && cwd[1] == ':') { + cwd[0] = toupper(cwd[0]); + } +#endif + main_cwd_state.cwd = strdup(cwd); + +#ifdef ZTS + ts_allocate_id(&cwd_globals_id, sizeof(virtual_cwd_globals), (ts_allocate_ctor) cwd_globals_ctor, (ts_allocate_dtor) cwd_globals_dtor); +#else + cwd_globals_ctor(&cwd_globals TSRMLS_CC); +#endif + +#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS) + cwd_mutex = tsrm_mutex_alloc(); +#endif +} +/* }}} */ + +CWD_API void virtual_cwd_shutdown(void) /* {{{ */ +{ +#ifndef ZTS + cwd_globals_dtor(&cwd_globals TSRMLS_CC); +#endif +#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS) + tsrm_mutex_free(cwd_mutex); +#endif + + free(main_cwd_state.cwd); /* Don't use CWD_STATE_FREE because the non global states will probably use emalloc()/efree() */ +} +/* }}} */ + +CWD_API char *virtual_getcwd_ex(size_t *length TSRMLS_DC) /* {{{ */ +{ + cwd_state *state; + + state = &CWDG(cwd); + + if (state->cwd_length == 0) { + char *retval; + + *length = 1; + retval = (char *) malloc(2); + if (retval == NULL) { + return NULL; + } + retval[0] = DEFAULT_SLASH; + retval[1] = '\0'; + return retval; + } + +#ifdef TSRM_WIN32 + /* If we have something like C: */ + if (state->cwd_length == 2 && state->cwd[state->cwd_length-1] == ':') { + char *retval; + + *length = state->cwd_length+1; + retval = (char *) malloc(*length+1); + if (retval == NULL) { + return NULL; + } + memcpy(retval, state->cwd, *length); + retval[0] = toupper(retval[0]); + retval[*length-1] = DEFAULT_SLASH; + retval[*length] = '\0'; + return retval; + } +#endif + *length = state->cwd_length; + return strdup(state->cwd); +} +/* }}} */ + +/* Same semantics as UNIX getcwd() */ +CWD_API char *virtual_getcwd(char *buf, size_t size TSRMLS_DC) /* {{{ */ +{ + size_t length; + char *cwd; + + cwd = virtual_getcwd_ex(&length TSRMLS_CC); + + if (buf == NULL) { + return cwd; + } + if (length > size-1) { + free(cwd); + errno = ERANGE; /* Is this OK? */ + return NULL; + } + memcpy(buf, cwd, length+1); + free(cwd); + return buf; +} +/* }}} */ + +#ifdef PHP_WIN32 +static inline unsigned long realpath_cache_key(const char *path, int path_len TSRMLS_DC) /* {{{ */ +{ + register unsigned long h; + char *bucket_key_start = tsrm_win32_get_path_sid_key(path TSRMLS_CC); + char *bucket_key = (char *)bucket_key_start; + const char *e = bucket_key + strlen(bucket_key); + + if (!bucket_key) { + return 0; + } + + for (h = 2166136261U; bucket_key < e;) { + h *= 16777619; + h ^= *bucket_key++; + } + HeapFree(GetProcessHeap(), 0, (LPVOID)bucket_key_start); + return h; +} +/* }}} */ +#else +static inline unsigned long realpath_cache_key(const char *path, int path_len) /* {{{ */ +{ + register unsigned long h; + const char *e = path + path_len; + + for (h = 2166136261U; path < e;) { + h *= 16777619; + h ^= *path++; + } + + return h; +} +/* }}} */ +#endif /* defined(PHP_WIN32) */ + +CWD_API void realpath_cache_clean(TSRMLS_D) /* {{{ */ +{ + int i; + + for (i = 0; i < sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]); i++) { + realpath_cache_bucket *p = CWDG(realpath_cache)[i]; + while (p != NULL) { + realpath_cache_bucket *r = p; + p = p->next; + free(r); + } + CWDG(realpath_cache)[i] = NULL; + } + CWDG(realpath_cache_size) = 0; +} +/* }}} */ + +CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC) /* {{{ */ +{ +#ifdef PHP_WIN32 + unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC); +#else + unsigned long key = realpath_cache_key(path, path_len); +#endif + unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); + realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n]; + + while (*bucket != NULL) { + if (key == (*bucket)->key && path_len == (*bucket)->path_len && + memcmp(path, (*bucket)->path, path_len) == 0) { + realpath_cache_bucket *r = *bucket; + *bucket = (*bucket)->next; + CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1; + free(r); + return; + } else { + bucket = &(*bucket)->next; + } + } +} +/* }}} */ + +static inline void realpath_cache_add(const char *path, int path_len, const char *realpath, int realpath_len, int is_dir, time_t t TSRMLS_DC) /* {{{ */ +{ + long size = sizeof(realpath_cache_bucket) + path_len + 1; + int same = 1; + + if (realpath_len != path_len || + memcmp(path, realpath, path_len) != 0) { + size += realpath_len + 1; + same = 0; + } + + if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) { + realpath_cache_bucket *bucket = malloc(size); + unsigned long n; + + if (bucket == NULL) { + return; + } + +#ifdef PHP_WIN32 + bucket->key = realpath_cache_key(path, path_len TSRMLS_CC); +#else + bucket->key = realpath_cache_key(path, path_len); +#endif + bucket->path = (char*)bucket + sizeof(realpath_cache_bucket); + memcpy(bucket->path, path, path_len+1); + bucket->path_len = path_len; + if (same) { + bucket->realpath = bucket->path; + } else { + bucket->realpath = bucket->path + (path_len + 1); + memcpy(bucket->realpath, realpath, realpath_len+1); + } + bucket->realpath_len = realpath_len; + bucket->is_dir = is_dir; +#ifdef PHP_WIN32 + bucket->is_rvalid = 0; + bucket->is_readable = 0; + bucket->is_wvalid = 0; + bucket->is_writable = 0; +#endif + bucket->expires = t + CWDG(realpath_cache_ttl); + n = bucket->key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); + bucket->next = CWDG(realpath_cache)[n]; + CWDG(realpath_cache)[n] = bucket; + CWDG(realpath_cache_size) += size; + } +} +/* }}} */ + +static inline realpath_cache_bucket* realpath_cache_find(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */ +{ +#ifdef PHP_WIN32 + unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC); +#else + unsigned long key = realpath_cache_key(path, path_len); +#endif + + unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); + realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n]; + + while (*bucket != NULL) { + if (CWDG(realpath_cache_ttl) && (*bucket)->expires < t) { + realpath_cache_bucket *r = *bucket; + *bucket = (*bucket)->next; + CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1; + free(r); + } else if (key == (*bucket)->key && path_len == (*bucket)->path_len && + memcmp(path, (*bucket)->path, path_len) == 0) { + return *bucket; + } else { + bucket = &(*bucket)->next; + } + } + return NULL; +} +/* }}} */ + +CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */ +{ + return realpath_cache_find(path, path_len, t TSRMLS_CC); +} +/* }}} */ + +CWD_API int realpath_cache_size(TSRMLS_D) +{ + return CWDG(realpath_cache_size); +} + +CWD_API int realpath_cache_max_buckets(TSRMLS_D) +{ + return (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); +} + +CWD_API realpath_cache_bucket** realpath_cache_get_buckets(TSRMLS_D) +{ + return CWDG(realpath_cache); +} + + +#undef LINK_MAX +#define LINK_MAX 32 + +static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, int use_realpath, int is_dir, int *link_is_dir TSRMLS_DC) /* {{{ */ +{ + int i, j, save; + int directory = 0; +#ifdef TSRM_WIN32 + WIN32_FIND_DATA data; + HANDLE hFind; + TSRM_ALLOCA_FLAG(use_heap_large) +#else + struct stat st; +#endif + realpath_cache_bucket *bucket; + char *tmp; + TSRM_ALLOCA_FLAG(use_heap) + + while (1) { + if (len <= start) { + return start; + } + + i = len; + while (i > start && !IS_SLASH(path[i-1])) { + i--; + } + + if (i == len || + (i == len - 1 && path[i] == '.')) { + /* remove double slashes and '.' */ + len = i - 1; + is_dir = 1; + continue; + } else if (i == len - 2 && path[i] == '.' && path[i+1] == '.') { + /* remove '..' and previous directory */ + if (i - 1 <= start) { + return start ? start : len; + } + j = tsrm_realpath_r(path, start, i-1, ll, t, use_realpath, 1, NULL TSRMLS_CC); + if (j > start) { + j--; + while (j > start && !IS_SLASH(path[j])) { + j--; + } + if (!start) { + /* leading '..' must not be removed in case of relative path */ + if (j == 0 && path[0] == '.' && path[1] == '.' && + IS_SLASH(path[2])) { + path[3] = '.'; + path[4] = '.'; + path[5] = DEFAULT_SLASH; + j = 5; + } else if (j > 0 && + path[j+1] == '.' && path[j+2] == '.' && + IS_SLASH(path[j+3])) { + j += 4; + path[j++] = '.'; + path[j++] = '.'; + path[j] = DEFAULT_SLASH; + } + } + } else if (!start && !j) { + /* leading '..' must not be removed in case of relative path */ + path[0] = '.'; + path[1] = '.'; + path[2] = DEFAULT_SLASH; + j = 2; + } + return j; + } + + path[len] = 0; + + save = (use_realpath != CWD_EXPAND); + + if (start && save && CWDG(realpath_cache_size_limit)) { + /* cache lookup for absolute path */ + if (!*t) { + *t = time(0); + } + if ((bucket = realpath_cache_find(path, len, *t TSRMLS_CC)) != NULL) { + if (is_dir && !bucket->is_dir) { + /* not a directory */ + return -1; + } else { + if (link_is_dir) { + *link_is_dir = bucket->is_dir; + } + memcpy(path, bucket->realpath, bucket->realpath_len + 1); + return bucket->realpath_len; + } + } + } + +#ifdef TSRM_WIN32 + if (save && (hFind = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE) { + if (use_realpath == CWD_REALPATH) { + /* file not found */ + return -1; + } + /* continue resolution anyway but don't save result in the cache */ + save = 0; + } + + if (save) { + FindClose(hFind); + } + + tmp = tsrm_do_alloca(len+1, use_heap); + memcpy(tmp, path, len+1); + + if(save && + !(IS_UNC_PATH(path, len) && len >= 3 && path[2] != '?') && + (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + /* File is a reparse point. Get the target */ + HANDLE hLink = NULL; + REPARSE_DATA_BUFFER * pbuffer; + unsigned int retlength = 0; + int bufindex = 0, isabsolute = 0; + wchar_t * reparsetarget; + BOOL isVolume = FALSE; + char printname[MAX_PATH]; + char substitutename[MAX_PATH]; + int printname_len, substitutename_len; + int substitutename_off = 0; + + if(++(*ll) > LINK_MAX) { + return -1; + } + + hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); + if(hLink == INVALID_HANDLE_VALUE) { + return -1; + } + + pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large); + if (pbuffer == NULL) { + return -1; + } + if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) { + tsrm_free_alloca(pbuffer, use_heap_large); + CloseHandle(hLink); + return -1; + } + + CloseHandle(hLink); + + if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + reparsetarget = pbuffer->SymbolicLinkReparseBuffer.ReparseTarget; + printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR); + isabsolute = (pbuffer->SymbolicLinkReparseBuffer.Flags == 0) ? 1 : 0; + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR), + printname_len + 1, + printname, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR); + printname[printname_len] = 0; + + substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR); + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), + substitutename_len + 1, + substitutename, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + substitutename[substitutename_len] = 0; + } + else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { + isabsolute = 1; + reparsetarget = pbuffer->MountPointReparseBuffer.ReparseTarget; + printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR); + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR), + printname_len + 1, + printname, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + printname[pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)] = 0; + + substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR); + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), + substitutename_len + 1, + substitutename, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + substitutename[substitutename_len] = 0; + } else { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + } + + if(isabsolute && substitutename_len > 4) { + /* Do not resolve volumes (for now). A mounted point can + target a volume without a drive, it is not certain that + all IO functions we use in php and its deps support + path with volume GUID instead of the DOS way, like: + d:\test\mnt\foo + \\?\Volume{62d1c3f8-83b9-11de-b108-806e6f6e6963}\foo + */ + if (strncmp(substitutename, "\\??\\Volume{",11) == 0 + || strncmp(substitutename, "\\\\?\\Volume{",11) == 0 + || strncmp(substitutename, "\\??\\UNC\\", 8) == 0 + ) { + isVolume = TRUE; + substitutename_off = 0; + } else + /* do not use the \??\ and \\?\ prefix*/ + if (strncmp(substitutename, "\\??\\", 4) == 0 + || strncmp(substitutename, "\\\\?\\", 4) == 0) { + substitutename_off = 4; + } + } + + if (!isVolume) { + char * tmp2 = substitutename + substitutename_off; + for(bufindex = 0; bufindex < (substitutename_len - substitutename_off); bufindex++) { + *(path + bufindex) = *(tmp2 + bufindex); + } + + *(path + bufindex) = 0; + j = bufindex; + } else { + j = len; + } + + +#if VIRTUAL_CWD_DEBUG + fprintf(stderr, "reparse: print: %s ", printname); + fprintf(stderr, "sub: %s ", substitutename); + fprintf(stderr, "resolved: %s ", path); +#endif + tsrm_free_alloca(pbuffer, use_heap_large); + + if(isabsolute == 1) { + if (!((j == 3) && (path[1] == ':') && (path[2] == '\\'))) { + /* use_realpath is 0 in the call below coz path is absolute*/ + j = tsrm_realpath_r(path, 0, j, ll, t, 0, is_dir, &directory TSRMLS_CC); + if(j < 0) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + } + } + else { + if(i + j >= MAXPATHLEN - 1) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + + memmove(path+i, path, j+1); + memcpy(path, tmp, i-1); + path[i-1] = DEFAULT_SLASH; + j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC); + if(j < 0) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + } + directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + + if(link_is_dir) { + *link_is_dir = directory; + } + } + else { + if (save) { + directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + if (is_dir && !directory) { + /* not a directory */ + return -1; + } + } + +#elif defined(NETWARE) + save = 0; + tmp = tsrm_do_alloca(len+1, use_heap); + memcpy(tmp, path, len+1); +#else + if (save && php_sys_lstat(path, &st) < 0) { + if (use_realpath == CWD_REALPATH) { + /* file not found */ + return -1; + } + /* continue resolution anyway but don't save result in the cache */ + save = 0; + } + + tmp = tsrm_do_alloca(len+1, use_heap); + memcpy(tmp, path, len+1); + + if (save && S_ISLNK(st.st_mode)) { + if (++(*ll) > LINK_MAX || (j = php_sys_readlink(tmp, path, MAXPATHLEN)) < 0) { + /* too many links or broken symlinks */ + tsrm_free_alloca(tmp, use_heap); + return -1; + } + path[j] = 0; + if (IS_ABSOLUTE_PATH(path, j)) { + j = tsrm_realpath_r(path, 1, j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC); + if (j < 0) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + } else { + if (i + j >= MAXPATHLEN-1) { + tsrm_free_alloca(tmp, use_heap); + return -1; /* buffer overflow */ + } + memmove(path+i, path, j+1); + memcpy(path, tmp, i-1); + path[i-1] = DEFAULT_SLASH; + j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC); + if (j < 0) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + } + if (link_is_dir) { + *link_is_dir = directory; + } + } else { + if (save) { + directory = S_ISDIR(st.st_mode); + if (link_is_dir) { + *link_is_dir = directory; + } + if (is_dir && !directory) { + /* not a directory */ + tsrm_free_alloca(tmp, use_heap); + return -1; + } + } +#endif + if (i - 1 <= start) { + j = start; + } else { + /* some leading directories may be unaccessable */ + j = tsrm_realpath_r(path, start, i-1, ll, t, save ? CWD_FILEPATH : use_realpath, 1, NULL TSRMLS_CC); + if (j > start) { + path[j++] = DEFAULT_SLASH; + } + } +#ifdef TSRM_WIN32 + if (j < 0 || j + len - i >= MAXPATHLEN-1) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + if (save) { + i = strlen(data.cFileName); + memcpy(path+j, data.cFileName, i+1); + j += i; + } else { + /* use the original file or directory name as it wasn't found */ + memcpy(path+j, tmp+i, len-i+1); + j += (len-i); + } + } +#else + if (j < 0 || j + len - i >= MAXPATHLEN-1) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + memcpy(path+j, tmp+i, len-i+1); + j += (len-i); + } +#endif + + if (save && start && CWDG(realpath_cache_size_limit)) { + /* save absolute path in the cache */ + realpath_cache_add(tmp, len, path, j, directory, *t TSRMLS_CC); + } + + tsrm_free_alloca(tmp, use_heap); + return j; + } +} +/* }}} */ + +/* Resolve path relatively to state and put the real path into state */ +/* returns 0 for ok, 1 for error */ +CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath TSRMLS_DC) /* {{{ */ +{ + int path_length = strlen(path); + char resolved_path[MAXPATHLEN]; + int start = 1; + int ll = 0; + time_t t; + int ret; + int add_slash; + void *tmp; + + if (path_length == 0 || path_length >= MAXPATHLEN-1) { +#ifdef TSRM_WIN32 +# if _MSC_VER < 1300 + errno = EINVAL; +# else + _set_errno(EINVAL); +# endif +#else + errno = EINVAL; +#endif + return 1; + } + +#if VIRTUAL_CWD_DEBUG + fprintf(stderr,"cwd = %s path = %s\n", state->cwd, path); +#endif + + /* cwd_length can be 0 when getcwd() fails. + * This can happen under solaris when a dir does not have read permissions + * but *does* have execute permissions */ + if (!IS_ABSOLUTE_PATH(path, path_length)) { + if (state->cwd_length == 0) { + /* resolve relative path */ + start = 0; + memcpy(resolved_path , path, path_length + 1); + } else { + int state_cwd_length = state->cwd_length; + +#ifdef TSRM_WIN32 + if (IS_SLASH(path[0])) { + if (state->cwd[1] == ':') { + /* Copy only the drive name */ + state_cwd_length = 2; + } else if (IS_UNC_PATH(state->cwd, state->cwd_length)) { + /* Copy only the share name */ + state_cwd_length = 2; + while (IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (state->cwd[state_cwd_length] && + !IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (state->cwd[state_cwd_length] && + !IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + } + } +#endif + if (path_length + state_cwd_length + 1 >= MAXPATHLEN-1) { + return 1; + } + memcpy(resolved_path, state->cwd, state_cwd_length); + resolved_path[state_cwd_length] = DEFAULT_SLASH; + memcpy(resolved_path + state_cwd_length + 1, path, path_length + 1); + path_length += state_cwd_length + 1; + } + } else { +#ifdef TSRM_WIN32 + if (path_length > 2 && path[1] == ':' && !IS_SLASH(path[2])) { + resolved_path[0] = path[0]; + resolved_path[1] = ':'; + resolved_path[2] = DEFAULT_SLASH; + memcpy(resolved_path + 3, path + 2, path_length - 1); + path_length++; + } else +#endif + memcpy(resolved_path, path, path_length + 1); + } + +#ifdef TSRM_WIN32 + if (memchr(resolved_path, '*', path_length) || + memchr(resolved_path, '?', path_length)) { + return 1; + } +#endif + +#ifdef TSRM_WIN32 + if (IS_UNC_PATH(resolved_path, path_length)) { + /* skip UNC name */ + resolved_path[0] = DEFAULT_SLASH; + resolved_path[1] = DEFAULT_SLASH; + start = 2; + while (!IS_SLASH(resolved_path[start])) { + if (resolved_path[start] == 0) { + goto verify; + } + resolved_path[start] = toupper(resolved_path[start]); + start++; + } + resolved_path[start++] = DEFAULT_SLASH; + while (!IS_SLASH(resolved_path[start])) { + if (resolved_path[start] == 0) { + goto verify; + } + resolved_path[start] = toupper(resolved_path[start]); + start++; + } + resolved_path[start++] = DEFAULT_SLASH; + } else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) { + /* skip DRIVE name */ + resolved_path[0] = toupper(resolved_path[0]); + resolved_path[2] = DEFAULT_SLASH; + start = 3; + } +#elif defined(NETWARE) + if (IS_ABSOLUTE_PATH(resolved_path, path_length)) { + /* skip VOLUME name */ + start = 0; + while (start != ':') { + if (resolved_path[start] == 0) return -1; + start++; + } + start++; + if (!IS_SLASH(resolved_path[start])) return -1; + resolved_path[start++] = DEFAULT_SLASH; + } +#endif + + add_slash = (use_realpath != CWD_REALPATH) && path_length > 0 && IS_SLASH(resolved_path[path_length-1]); + t = CWDG(realpath_cache_ttl) ? 0 : -1; + path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL TSRMLS_CC); + + if (path_length < 0) { + errno = ENOENT; + return 1; + } + + if (!start && !path_length) { + resolved_path[path_length++] = '.'; + } + if (add_slash && path_length && !IS_SLASH(resolved_path[path_length-1])) { + if (path_length >= MAXPATHLEN-1) { + return -1; + } + resolved_path[path_length++] = DEFAULT_SLASH; + } + resolved_path[path_length] = 0; + +#ifdef TSRM_WIN32 +verify: +#endif + if (verify_path) { + cwd_state old_state; + + CWD_STATE_COPY(&old_state, state); + state->cwd_length = path_length; + + tmp = realloc(state->cwd, state->cwd_length+1); + if (tmp == NULL) { +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "Out of memory\n"); +#endif + return 1; + } + state->cwd = (char *) tmp; + + memcpy(state->cwd, resolved_path, state->cwd_length+1); + if (verify_path(state)) { + CWD_STATE_FREE(state); + *state = old_state; + ret = 1; + } else { + CWD_STATE_FREE(&old_state); + ret = 0; + } + } else { + state->cwd_length = path_length; + tmp = realloc(state->cwd, state->cwd_length+1); + if (tmp == NULL) { +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "Out of memory\n"); +#endif + return 1; + } + state->cwd = (char *) tmp; + + memcpy(state->cwd, resolved_path, state->cwd_length+1); + ret = 0; + } + +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "virtual_file_ex() = %s\n",state->cwd); +#endif + return (ret); +} +/* }}} */ + +CWD_API int virtual_chdir(const char *path TSRMLS_DC) /* {{{ */ +{ + return virtual_file_ex(&CWDG(cwd), path, php_is_dir_ok, CWD_REALPATH TSRMLS_CC)?-1:0; +} +/* }}} */ + +CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path TSRMLS_DC) TSRMLS_DC) /* {{{ */ +{ + int length = strlen(path); + char *temp; + int retval; + TSRM_ALLOCA_FLAG(use_heap) + + if (length == 0) { + return 1; /* Can't cd to empty string */ + } + while(--length >= 0 && !IS_SLASH(path[length])) { + } + + if (length == -1) { + /* No directory only file name */ + errno = ENOENT; + return -1; + } + + if (length == COPY_WHEN_ABSOLUTE(path) && IS_ABSOLUTE_PATH(path, length+1)) { /* Also use trailing slash if this is absolute */ + length++; + } + temp = (char *) tsrm_do_alloca(length+1, use_heap); + memcpy(temp, path, length); + temp[length] = 0; +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "Changing directory to %s\n", temp); +#endif + retval = p_chdir(temp TSRMLS_CC); + tsrm_free_alloca(temp, use_heap); + return retval; +} +/* }}} */ + +CWD_API char *virtual_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + char *retval; + char cwd[MAXPATHLEN]; + + /* realpath("") returns CWD */ + if (!*path) { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + retval = NULL; + goto end; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + if (VCWD_GETCWD(cwd, MAXPATHLEN)) { + path = cwd; + } + } else if (!IS_ABSOLUTE_PATH(path, strlen(path))) { + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + } else { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + retval = NULL; + goto end; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + } + + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)==0) { + int len = new_state.cwd_length>MAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length; + + memcpy(real_path, new_state.cwd, len); + real_path[len] = '\0'; + retval = real_path; + } else { + retval = NULL; + } + + CWD_STATE_FREE(&new_state); +end: + return retval; +} +/* }}} */ + +CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + retval = virtual_file_ex(&new_state, path, verify_path, CWD_FILEPATH TSRMLS_CC); + + *filepath = new_state.cwd; + + return retval; + +} +/* }}} */ + +CWD_API int virtual_filepath(const char *path, char **filepath TSRMLS_DC) /* {{{ */ +{ + return virtual_filepath_ex(path, filepath, php_is_file_ok TSRMLS_CC); +} +/* }}} */ + +CWD_API FILE *virtual_fopen(const char *path, const char *mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + FILE *f; + + if (path[0] == '\0') { /* Fail to open empty path */ + return NULL; + } + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return NULL; + } + + f = fopen(new_state.cwd, mode); + + CWD_STATE_FREE(&new_state); + return f; +} +/* }}} */ + +CWD_API int virtual_access(const char *pathname, int mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + +#if defined(TSRM_WIN32) + ret = tsrm_win32_access(new_state.cwd, mode TSRMLS_CC); +#else + ret = access(new_state.cwd, mode); +#endif + + CWD_STATE_FREE(&new_state); + + return ret; +} +/* }}} */ + +#if HAVE_UTIME +#ifdef TSRM_WIN32 +static void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */ +{ + // Note that LONGLONG is a 64-bit value + LONGLONG ll; + + ll = Int32x32To64(t, 10000000) + 116444736000000000; + pft->dwLowDateTime = (DWORD)ll; + pft->dwHighDateTime = ll >> 32; +} +/* }}} */ + +TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */ +{ + FILETIME mtime, atime; + HANDLE hFile; + + hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, + OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL); + + /* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but + the CreateFile operation succeeds */ + if (GetLastError() == ERROR_ALREADY_EXISTS) { + SetLastError(0); + } + + if ( hFile == INVALID_HANDLE_VALUE ) { + return -1; + } + + if (!buf) { + SYSTEMTIME st; + GetSystemTime(&st); + SystemTimeToFileTime(&st, &mtime); + atime = mtime; + } else { + UnixTimeToFileTime(buf->modtime, &mtime); + UnixTimeToFileTime(buf->actime, &atime); + } + if (!SetFileTime(hFile, NULL, &atime, &mtime)) { + CloseHandle(hFile); + return -1; + } + CloseHandle(hFile); + return 1; +} +/* }}} */ +#endif + +CWD_API int virtual_utime(const char *filename, struct utimbuf *buf TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + +#ifdef TSRM_WIN32 + ret = win32_utime(new_state.cwd, buf); +#else + ret = utime(new_state.cwd, buf); +#endif + + CWD_STATE_FREE(&new_state); + return ret; +} +/* }}} */ +#endif + +CWD_API int virtual_chmod(const char *filename, mode_t mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + ret = chmod(new_state.cwd, mode); + + CWD_STATE_FREE(&new_state); + return ret; +} +/* }}} */ + +#if !defined(TSRM_WIN32) && !defined(NETWARE) +CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + if (link) { +#if HAVE_LCHOWN + ret = lchown(new_state.cwd, owner, group); +#else + ret = -1; +#endif + } else { + ret = chown(new_state.cwd, owner, group); + } + + CWD_STATE_FREE(&new_state); + return ret; +} +/* }}} */ +#endif + +CWD_API int virtual_open(const char *path TSRMLS_DC, int flags, ...) /* {{{ */ +{ + cwd_state new_state; + int f; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + if (flags & O_CREAT) { + mode_t mode; + va_list arg; + + va_start(arg, flags); + mode = (mode_t) va_arg(arg, int); + va_end(arg); + + f = open(new_state.cwd, flags, mode); + } else { + f = open(new_state.cwd, flags); + } + CWD_STATE_FREE(&new_state); + return f; +} +/* }}} */ + +CWD_API int virtual_creat(const char *path, mode_t mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int f; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + f = creat(new_state.cwd, mode); + + CWD_STATE_FREE(&new_state); + return f; +} +/* }}} */ + +CWD_API int virtual_rename(char *oldname, char *newname TSRMLS_DC) /* {{{ */ +{ + cwd_state old_state; + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&old_state, &CWDG(cwd)); + if (virtual_file_ex(&old_state, oldname, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&old_state); + return -1; + } + oldname = old_state.cwd; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, newname, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&old_state); + CWD_STATE_FREE(&new_state); + return -1; + } + newname = new_state.cwd; + + /* rename on windows will fail if newname already exists. + MoveFileEx has to be used */ +#ifdef TSRM_WIN32 + /* MoveFileEx returns 0 on failure, other way 'round for this function */ + retval = (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED) == 0) ? -1 : 0; +#else + retval = rename(oldname, newname); +#endif + + CWD_STATE_FREE(&old_state); + CWD_STATE_FREE(&new_state); + + return retval; +} +/* }}} */ + +CWD_API int virtual_stat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = php_sys_stat(new_state.cwd, buf); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_lstat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = php_sys_lstat(new_state.cwd, buf); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_unlink(const char *path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = unlink(new_state.cwd); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_mkdir(const char *pathname, mode_t mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + +#ifdef TSRM_WIN32 + retval = mkdir(new_state.cwd); +#else + retval = mkdir(new_state.cwd, mode); +#endif + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_rmdir(const char *pathname TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = rmdir(new_state.cwd); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +#ifdef TSRM_WIN32 +DIR *opendir(const char *name); +#endif + +CWD_API DIR *virtual_opendir(const char *pathname TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + DIR *retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return NULL; + } + + retval = opendir(new_state.cwd); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +#ifdef TSRM_WIN32 +CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */ +{ + return popen_ex(command, type, CWDG(cwd).cwd, NULL TSRMLS_CC); +} +/* }}} */ +#elif defined(NETWARE) +/* On NetWare, the trick of prepending "cd cwd; " doesn't work so we need to perform + a VCWD_CHDIR() and mutex it + */ +CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */ +{ + char prev_cwd[MAXPATHLEN]; + char *getcwd_result; + FILE *retval; + + getcwd_result = VCWD_GETCWD(prev_cwd, MAXPATHLEN); + if (!getcwd_result) { + return NULL; + } + +#ifdef ZTS + tsrm_mutex_lock(cwd_mutex); +#endif + + VCWD_CHDIR(CWDG(cwd).cwd); + retval = popen(command, type); + VCWD_CHDIR(prev_cwd); + +#ifdef ZTS + tsrm_mutex_unlock(cwd_mutex); +#endif + + return retval; +} +/* }}} */ +#else /* Unix */ +CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */ +{ + int command_length; + int dir_length, extra = 0; + char *command_line; + char *ptr, *dir; + FILE *retval; + + command_length = strlen(command); + + dir_length = CWDG(cwd).cwd_length; + dir = CWDG(cwd).cwd; + while (dir_length > 0) { + if (*dir == '\'') extra+=3; + dir++; + dir_length--; + } + dir_length = CWDG(cwd).cwd_length; + dir = CWDG(cwd).cwd; + + ptr = command_line = (char *) malloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1); + if (!command_line) { + return NULL; + } + memcpy(ptr, "cd ", sizeof("cd ")-1); + ptr += sizeof("cd ")-1; + + if (CWDG(cwd).cwd_length == 0) { + *ptr++ = DEFAULT_SLASH; + } else { + *ptr++ = '\''; + while (dir_length > 0) { + switch (*dir) { + case '\'': + *ptr++ = '\''; + *ptr++ = '\\'; + *ptr++ = '\''; + /* fall-through */ + default: + *ptr++ = *dir; + } + dir++; + dir_length--; + } + *ptr++ = '\''; + } + + *ptr++ = ' '; + *ptr++ = ';'; + *ptr++ = ' '; + + memcpy(ptr, command, command_length+1); + retval = popen(command_line, type); + + free(command_line); + return retval; +} +/* }}} */ +#endif + +CWD_API char *tsrm_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + char cwd[MAXPATHLEN]; + + /* realpath("") returns CWD */ + if (!*path) { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + return NULL; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + if (VCWD_GETCWD(cwd, MAXPATHLEN)) { + path = cwd; + } + } else if (!IS_ABSOLUTE_PATH(path, strlen(path)) && + VCWD_GETCWD(cwd, MAXPATHLEN)) { + new_state.cwd = strdup(cwd); + new_state.cwd_length = strlen(cwd); + } else { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + return NULL; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + } + + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)) { + free(new_state.cwd); + return NULL; + } + + if (real_path) { + int copy_len = new_state.cwd_length>MAXPATHLEN-1 ? MAXPATHLEN-1 : new_state.cwd_length; + memcpy(real_path, new_state.cwd, copy_len); + real_path[copy_len] = '\0'; + free(new_state.cwd); + return real_path; + } else { + return new_state.cwd; + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/c3c87e_c9c279_ext#zip#php_zip.c b/src/main/resource/testFiles/php-src/revFiles/c3c87e_c9c279_ext#zip#php_zip.c new file mode 100644 index 0000000..1dd76fc --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/c3c87e_c9c279_ext#zip#php_zip.c @@ -0,0 +1,3146 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Piere-Alain Joye | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/standard/file.h" +#include "ext/standard/php_string.h" +#include "ext/pcre/php_pcre.h" +#include "ext/standard/php_filestat.h" +#include "php_zip.h" + +/* zip_open is a macro for renaming libzip zipopen, so we need to use PHP_NAMED_FUNCTION */ +static PHP_NAMED_FUNCTION(zif_zip_open); +static PHP_NAMED_FUNCTION(zif_zip_read); +static PHP_NAMED_FUNCTION(zif_zip_close); +static PHP_NAMED_FUNCTION(zif_zip_entry_read); +static PHP_NAMED_FUNCTION(zif_zip_entry_filesize); +static PHP_NAMED_FUNCTION(zif_zip_entry_name); +static PHP_NAMED_FUNCTION(zif_zip_entry_compressedsize); +static PHP_NAMED_FUNCTION(zif_zip_entry_compressionmethod); +static PHP_NAMED_FUNCTION(zif_zip_entry_open); +static PHP_NAMED_FUNCTION(zif_zip_entry_close); + +#ifdef HAVE_GLOB +#ifndef PHP_WIN32 +#include +#else +#include "win32/glob.h" +#endif +#endif + +/* {{{ Resource le */ +static int le_zip_dir; +#define le_zip_dir_name "Zip Directory" +static int le_zip_entry; +#define le_zip_entry_name "Zip Entry" +/* }}} */ + +/* {{{ PHP_ZIP_STAT_INDEX(za, index, flags, sb) */ +#define PHP_ZIP_STAT_INDEX(za, index, flags, sb) \ + if (zip_stat_index(za, index, flags, &sb) != 0) { \ + RETURN_FALSE; \ + } +/* }}} */ + +/* {{{ PHP_ZIP_STAT_PATH(za, path, path_len, flags, sb) */ +#define PHP_ZIP_STAT_PATH(za, path, path_len, flags, sb) \ + if (path_len < 1) { \ + php_error_docref(NULL, E_NOTICE, "Empty string as entry name"); \ + RETURN_FALSE; \ + } \ + if (zip_stat(za, path, flags, &sb) != 0) { \ + RETURN_FALSE; \ + } +/* }}} */ + +/* {{{ PHP_ZIP_SET_FILE_COMMENT(za, index, comment, comment_len) */ +#define PHP_ZIP_SET_FILE_COMMENT(za, index, comment, comment_len) \ + if (comment_len == 0) { \ + /* Passing NULL remove the existing comment */ \ + if (zip_set_file_comment(za, index, NULL, 0) < 0) { \ + RETURN_FALSE; \ + } \ + } else if (zip_set_file_comment(za, index, comment, comment_len) < 0) { \ + RETURN_FALSE; \ + } \ + RETURN_TRUE; +/* }}} */ + +# define add_ascii_assoc_string add_assoc_string +# define add_ascii_assoc_long add_assoc_long + +/* Flatten a path by making a relative path (to .)*/ +static char * php_zip_make_relative_path(char *path, size_t path_len) /* {{{ */ +{ + char *path_begin = path; + size_t i; + + if (path_len < 1 || path == NULL) { + return NULL; + } + + if (IS_SLASH(path[0])) { + return path + 1; + } + + i = path_len; + + while (1) { + while (i > 0 && !IS_SLASH(path[i])) { + i--; + } + + if (!i) { + return path; + } + + if (i >= 2 && (path[i -1] == '.' || path[i -1] == ':')) { + /* i is the position of . or :, add 1 for / */ + path_begin = path + i + 1; + break; + } + i--; + } + + return path_begin; +} +/* }}} */ + +# define CWD_STATE_ALLOC(l) emalloc(l) +# define CWD_STATE_FREE(s) efree(s) + +/* {{{ php_zip_extract_file */ +static int php_zip_extract_file(struct zip * za, char *dest, char *file, int file_len) +{ + php_stream_statbuf ssb; + struct zip_file *zf; + struct zip_stat sb; + char b[8192]; + int n, len, ret; + php_stream *stream; + char *fullpath; + char *file_dirname_fullpath; + char file_dirname[MAXPATHLEN]; + size_t dir_len; + int is_dir_only = 0; + char *path_cleaned; + size_t path_cleaned_len; + cwd_state new_state; + zend_string *file_basename; + + new_state.cwd = CWD_STATE_ALLOC(1); + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + + /* Clean/normlize the path and then transform any path (absolute or relative) + to a path relative to cwd (../../mydir/foo.txt > mydir/foo.txt) + */ + virtual_file_ex(&new_state, file, NULL, CWD_EXPAND); + path_cleaned = php_zip_make_relative_path(new_state.cwd, new_state.cwd_length); + if(!path_cleaned) { + return 0; + } + path_cleaned_len = strlen(path_cleaned); + + if (path_cleaned_len >= MAXPATHLEN || zip_stat(za, file, 0, &sb) != 0) { + return 0; + } + + /* it is a directory only, see #40228 */ + if (path_cleaned_len > 1 && IS_SLASH(path_cleaned[path_cleaned_len - 1])) { + len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file); + is_dir_only = 1; + } else { + memcpy(file_dirname, path_cleaned, path_cleaned_len); + dir_len = php_dirname(file_dirname, path_cleaned_len); + + if (dir_len <= 0 || (dir_len == 1 && file_dirname[0] == '.')) { + len = spprintf(&file_dirname_fullpath, 0, "%s", dest); + } else { + len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file_dirname); + } + + file_basename = php_basename(path_cleaned, path_cleaned_len, NULL, 0); + + if (ZIP_OPENBASEDIR_CHECKPATH(file_dirname_fullpath)) { + efree(file_dirname_fullpath); + zend_string_release(file_basename); + CWD_STATE_FREE(new_state.cwd); + return 0; + } + } + + /* let see if the path already exists */ + if (php_stream_stat_path_ex(file_dirname_fullpath, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL) < 0) { + ret = php_stream_mkdir(file_dirname_fullpath, 0777, PHP_STREAM_MKDIR_RECURSIVE|REPORT_ERRORS, NULL); + if (!ret) { + efree(file_dirname_fullpath); + if (!is_dir_only) { + zend_string_release(file_basename); + CWD_STATE_FREE(new_state.cwd); + } + return 0; + } + } + + /* it is a standalone directory, job done */ + if (is_dir_only) { + efree(file_dirname_fullpath); + CWD_STATE_FREE(new_state.cwd); + return 1; + } + + len = spprintf(&fullpath, 0, "%s/%s", file_dirname_fullpath, file_basename->val); + if (!len) { + efree(file_dirname_fullpath); + zend_string_release(file_basename); + CWD_STATE_FREE(new_state.cwd); + return 0; + } else if (len > MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "Full extraction path exceed MAXPATHLEN (%i)", MAXPATHLEN); + efree(file_dirname_fullpath); + zend_string_release(file_basename); + CWD_STATE_FREE(new_state.cwd); + return 0; + } + + /* check again the full path, not sure if it + * is required, does a file can have a different + * safemode status as its parent folder? + */ + if (ZIP_OPENBASEDIR_CHECKPATH(fullpath)) { + efree(fullpath); + efree(file_dirname_fullpath); + zend_string_release(file_basename); + CWD_STATE_FREE(new_state.cwd); + return 0; + } + + stream = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS, NULL); + + if (stream == NULL) { + n = -1; + goto done; + } + + zf = zip_fopen(za, file, 0); + if (zf == NULL) { + n = -1; + php_stream_close(stream); + goto done; + } + + n = 0; + + while ((n=zip_fread(zf, b, sizeof(b))) > 0) { + php_stream_write(stream, b, n); + } + + php_stream_close(stream); + n = zip_fclose(zf); + +done: + efree(fullpath); + zend_string_release(file_basename); + efree(file_dirname_fullpath); + CWD_STATE_FREE(new_state.cwd); + + if (n<0) { + return 0; + } else { + return 1; + } +} +/* }}} */ + +static int php_zip_add_file(struct zip *za, const char *filename, size_t filename_len, + char *entry_name, size_t entry_name_len, long offset_start, long offset_len) /* {{{ */ +{ + struct zip_source *zs; + char resolved_path[MAXPATHLEN]; + zval exists_flag; + + + if (ZIP_OPENBASEDIR_CHECKPATH(filename)) { + return -1; + } + + if (!expand_filepath(filename, resolved_path)) { + return -1; + } + + php_stat(resolved_path, strlen(resolved_path), FS_EXISTS, &exists_flag); + if (Z_TYPE(exists_flag) == IS_FALSE) { + return -1; + } + + zs = zip_source_file(za, resolved_path, offset_start, offset_len); + if (!zs) { + return -1; + } + if (zip_file_add(za, entry_name, zs, ZIP_FL_OVERWRITE) < 0) { + zip_source_free(zs); + return -1; + } else { + zip_error_clear(za); + return 1; + } +} +/* }}} */ + +static int php_zip_parse_options(zval *options, zend_long *remove_all_path, char **remove_path, size_t *remove_path_len, char **add_path, size_t *add_path_len) /* {{{ */ +{ + zval *option; + if ((option = zend_hash_str_find(HASH_OF(options), "remove_all_path", sizeof("remove_all_path") - 1)) != NULL) { + *remove_all_path = zval_get_long(option); + } + + /* If I add more options, it would make sense to create a nice static struct and loop over it. */ + if ((option = zend_hash_str_find(HASH_OF(options), "remove_path", sizeof("remove_path") - 1)) != NULL) { + if (Z_TYPE_P(option) != IS_STRING) { + php_error_docref(NULL, E_WARNING, "remove_path option expected to be a string"); + return -1; + } + + if (Z_STRLEN_P(option) < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string given as remove_path option"); + return -1; + } + + if (Z_STRLEN_P(option) >= MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "remove_path string is too long (max: %i, %i given)", + MAXPATHLEN - 1, Z_STRLEN_P(option)); + return -1; + } + *remove_path_len = Z_STRLEN_P(option); + *remove_path = Z_STRVAL_P(option); + } + + if ((option = zend_hash_str_find(HASH_OF(options), "add_path", sizeof("add_path") - 1)) != NULL) { + if (Z_TYPE_P(option) != IS_STRING) { + php_error_docref(NULL, E_WARNING, "add_path option expected to be a string"); + return -1; + } + + if (Z_STRLEN_P(option) < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string given as the add_path option"); + return -1; + } + + if (Z_STRLEN_P(option) >= MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "add_path string too long (max: %i, %i given)", + MAXPATHLEN - 1, Z_STRLEN_P(option)); + return -1; + } + *add_path_len = Z_STRLEN_P(option); + *add_path = Z_STRVAL_P(option); + } + return 1; +} +/* }}} */ + +/* {{{ REGISTER_ZIP_CLASS_CONST_LONG */ +#define REGISTER_ZIP_CLASS_CONST_LONG(const_name, value) \ + zend_declare_class_constant_long(zip_class_entry, const_name, sizeof(const_name)-1, (zend_long)value); +/* }}} */ + +/* {{{ ZIP_FROM_OBJECT */ +#define ZIP_FROM_OBJECT(intern, object) \ + { \ + ze_zip_object *obj = Z_ZIP_P(object); \ + intern = obj->za; \ + if (!intern) { \ + php_error_docref(NULL, E_WARNING, "Invalid or uninitialized Zip object"); \ + RETURN_FALSE; \ + } \ + } +/* }}} */ + +/* {{{ RETURN_SB(sb) */ +#define RETURN_SB(sb) \ + { \ + array_init(return_value); \ + add_ascii_assoc_string(return_value, "name", (char *)(sb)->name); \ + add_ascii_assoc_long(return_value, "index", (zend_long) (sb)->index); \ + add_ascii_assoc_long(return_value, "crc", (zend_long) (sb)->crc); \ + add_ascii_assoc_long(return_value, "size", (zend_long) (sb)->size); \ + add_ascii_assoc_long(return_value, "mtime", (zend_long) (sb)->mtime); \ + add_ascii_assoc_long(return_value, "comp_size", (zend_long) (sb)->comp_size); \ + add_ascii_assoc_long(return_value, "comp_method", (zend_long) (sb)->comp_method); \ + } +/* }}} */ + +static int php_zip_status(struct zip *za) /* {{{ */ +{ +#if LIBZIP_VERSION_MAJOR < 1 + int zep, syp; + + zip_error_get(za, &zep, &syp); +#else + int zep; + zip_error_t *err; + + err = zip_get_error(za); + zep = zip_error_code_zip(err); + zip_error_fini(err); +#endif + return zep; +} +/* }}} */ + +static int php_zip_status_sys(struct zip *za) /* {{{ */ +{ +#if LIBZIP_VERSION_MAJOR < 1 + int zep, syp; + + zip_error_get(za, &zep, &syp); +#else + int syp; + zip_error_t *err; + + err = zip_get_error(za); + syp = zip_error_code_system(err); + zip_error_fini(err); +#endif + return syp; +} +/* }}} */ + +static int php_zip_get_num_files(struct zip *za) /* {{{ */ +{ + return zip_get_num_files(za); +} +/* }}} */ + +static char * php_zipobj_get_filename(ze_zip_object *obj) /* {{{ */ +{ + + if (!obj) { + return NULL; + } + + if (obj->filename) { + return obj->filename; + } + return NULL; +} +/* }}} */ + +static char * php_zipobj_get_zip_comment(struct zip *za, int *len) /* {{{ */ +{ + if (za) { + return (char *)zip_get_archive_comment(za, len, 0); + } + return NULL; +} +/* }}} */ + +#ifdef HAVE_GLOB /* {{{ */ +#ifndef GLOB_ONLYDIR +#define GLOB_ONLYDIR (1<<30) +#define GLOB_EMULATE_ONLYDIR +#define GLOB_FLAGMASK (~GLOB_ONLYDIR) +#else +#define GLOB_FLAGMASK (~0) +#endif +#ifndef GLOB_BRACE +# define GLOB_BRACE 0 +#endif +#ifndef GLOB_MARK +# define GLOB_MARK 0 +#endif +#ifndef GLOB_NOSORT +# define GLOB_NOSORT 0 +#endif +#ifndef GLOB_NOCHECK +# define GLOB_NOCHECK 0 +#endif +#ifndef GLOB_NOESCAPE +# define GLOB_NOESCAPE 0 +#endif +#ifndef GLOB_ERR +# define GLOB_ERR 0 +#endif + +/* This is used for checking validity of passed flags (passing invalid flags causes segfault in glob()!! */ +#define GLOB_AVAILABLE_FLAGS (0 | GLOB_BRACE | GLOB_MARK | GLOB_NOSORT | GLOB_NOCHECK | GLOB_NOESCAPE | GLOB_ERR | GLOB_ONLYDIR) + +#endif /* }}} */ + +int php_zip_glob(char *pattern, int pattern_len, zend_long flags, zval *return_value) /* {{{ */ +{ +#ifdef HAVE_GLOB + char cwd[MAXPATHLEN]; + int cwd_skip = 0; +#ifdef ZTS + char work_pattern[MAXPATHLEN]; + char *result; +#endif + glob_t globbuf; + int n; + int ret; + + if (pattern_len >= MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN); + return -1; + } + + if ((GLOB_AVAILABLE_FLAGS & flags) != flags) { + php_error_docref(NULL, E_WARNING, "At least one of the passed flags is invalid or not supported on this platform"); + return -1; + } + +#ifdef ZTS + if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) { + result = VCWD_GETCWD(cwd, MAXPATHLEN); + if (!result) { + cwd[0] = '\0'; + } +#ifdef PHP_WIN32 + if (IS_SLASH(*pattern)) { + cwd[2] = '\0'; + } +#endif + cwd_skip = strlen(cwd)+1; + + snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern); + pattern = work_pattern; + } +#endif + + globbuf.gl_offs = 0; + if (0 != (ret = glob(pattern, flags & GLOB_FLAGMASK, NULL, &globbuf))) { +#ifdef GLOB_NOMATCH + if (GLOB_NOMATCH == ret) { + /* Some glob implementation simply return no data if no matches + were found, others return the GLOB_NOMATCH error code. + We don't want to treat GLOB_NOMATCH as an error condition + so that PHP glob() behaves the same on both types of + implementations and so that 'foreach (glob() as ...' + can be used for simple glob() calls without further error + checking. + */ + array_init(return_value); + return 0; + } +#endif + return 0; + } + + /* now catch the FreeBSD style of "no matches" */ + if (!globbuf.gl_pathc || !globbuf.gl_pathv) { + array_init(return_value); + return 0; + } + + /* we assume that any glob pattern will match files from one directory only + so checking the dirname of the first match should be sufficient */ + strncpy(cwd, globbuf.gl_pathv[0], MAXPATHLEN); + if (ZIP_OPENBASEDIR_CHECKPATH(cwd)) { + return -1; + } + + array_init(return_value); + for (n = 0; n < globbuf.gl_pathc; n++) { + /* we need to do this every time since GLOB_ONLYDIR does not guarantee that + * all directories will be filtered. GNU libc documentation states the + * following: + * If the information about the type of the file is easily available + * non-directories will be rejected but no extra work will be done to + * determine the information for each file. I.e., the caller must still be + * able to filter directories out. + */ + if (flags & GLOB_ONLYDIR) { + zend_stat_t s; + + if (0 != VCWD_STAT(globbuf.gl_pathv[n], &s)) { + continue; + } + + if (S_IFDIR != (s.st_mode & S_IFMT)) { + continue; + } + } + add_next_index_string(return_value, globbuf.gl_pathv[n]+cwd_skip); + } + + globfree(&globbuf); + return globbuf.gl_pathc; +#else + php_error_docref(NULL, E_ERROR, "Glob support is not available"); + return 0; +#endif /* HAVE_GLOB */ +} +/* }}} */ + +int php_zip_pcre(zend_string *regexp, char *path, int path_len, zval *return_value) /* {{{ */ +{ +#ifdef ZTS + char cwd[MAXPATHLEN]; + int cwd_skip = 0; + char work_path[MAXPATHLEN]; + char *result; +#endif + int files_cnt; + zend_string **namelist; + +#ifdef ZTS + if (!IS_ABSOLUTE_PATH(path, path_len)) { + result = VCWD_GETCWD(cwd, MAXPATHLEN); + if (!result) { + cwd[0] = '\0'; + } +#ifdef PHP_WIN32 + if (IS_SLASH(*path)) { + cwd[2] = '\0'; + } +#endif + cwd_skip = strlen(cwd)+1; + + snprintf(work_path, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, path); + path = work_path; + } +#endif + + if (ZIP_OPENBASEDIR_CHECKPATH(path)) { + return -1; + } + + files_cnt = php_stream_scandir(path, &namelist, NULL, (void *) php_stream_dirent_alphasort); + + if (files_cnt > 0) { + pcre *re = NULL; + pcre_extra *pcre_extra = NULL; + int preg_options = 0, i; + + re = pcre_get_compiled_regex(regexp, &pcre_extra, &preg_options); + if (!re) { + php_error_docref(NULL, E_WARNING, "Invalid expression"); + return -1; + } + + array_init(return_value); + + /* only the files, directories are ignored */ + for (i = 0; i < files_cnt; i++) { + zend_stat_t s; + char fullpath[MAXPATHLEN]; + int ovector[3]; + int matches; + int namelist_len = namelist[i]->len; + + if ((namelist_len == 1 && namelist[i]->val[0] == '.') || + (namelist_len == 2 && namelist[i]->val[0] == '.' && namelist[i]->val[1] == '.')) { + zend_string_release(namelist[i]); + continue; + } + + if ((path_len + namelist_len + 1) >= MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "add_path string too long (max: %i, %i given)", + MAXPATHLEN - 1, (path_len + namelist_len + 1)); + zend_string_release(namelist[i]); + break; + } + + snprintf(fullpath, MAXPATHLEN, "%s%c%s", path, DEFAULT_SLASH, namelist[i]->val); + + if (0 != VCWD_STAT(fullpath, &s)) { + php_error_docref(NULL, E_WARNING, "Cannot read <%s>", fullpath); + zend_string_release(namelist[i]); + continue; + } + + if (S_IFDIR == (s.st_mode & S_IFMT)) { + zend_string_release(namelist[i]); + continue; + } + + matches = pcre_exec(re, NULL, namelist[i]->val, namelist[i]->len, 0, 0, ovector, 3); + /* 0 means that the vector is too small to hold all the captured substring offsets */ + if (matches < 0) { + zend_string_release(namelist[i]); + continue; + } + + add_next_index_string(return_value, fullpath); + zend_string_release(namelist[i]); + } + efree(namelist); + } + return files_cnt; +} +/* }}} */ + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_open, 0, 0, 1) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_close, 0, 0, 1) + ZEND_ARG_INFO(0, zip) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_read, 0, 0, 1) + ZEND_ARG_INFO(0, zip) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_open, 0, 0, 2) + ZEND_ARG_INFO(0, zip_dp) + ZEND_ARG_INFO(0, zip_entry) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_close, 0, 0, 1) + ZEND_ARG_INFO(0, zip_ent) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_read, 0, 0, 1) + ZEND_ARG_INFO(0, zip_entry) + ZEND_ARG_INFO(0, len) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_name, 0, 0, 1) + ZEND_ARG_INFO(0, zip_entry) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_compressedsize, 0, 0, 1) + ZEND_ARG_INFO(0, zip_entry) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_filesize, 0, 0, 1) + ZEND_ARG_INFO(0, zip_entry) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_entry_compressionmethod, 0, 0, 1) + ZEND_ARG_INFO(0, zip_entry) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ zend_function_entry */ +static const zend_function_entry zip_functions[] = { + ZEND_RAW_FENTRY("zip_open", zif_zip_open, arginfo_zip_open, 0) + ZEND_RAW_FENTRY("zip_close", zif_zip_close, arginfo_zip_close, 0) + ZEND_RAW_FENTRY("zip_read", zif_zip_read, arginfo_zip_read, 0) + PHP_FE(zip_entry_open, arginfo_zip_entry_open) + PHP_FE(zip_entry_close, arginfo_zip_entry_close) + PHP_FE(zip_entry_read, arginfo_zip_entry_read) + PHP_FE(zip_entry_filesize, arginfo_zip_entry_filesize) + PHP_FE(zip_entry_name, arginfo_zip_entry_name) + PHP_FE(zip_entry_compressedsize, arginfo_zip_entry_compressedsize) + PHP_FE(zip_entry_compressionmethod, arginfo_zip_entry_compressionmethod) +#ifdef PHP_FE_END + PHP_FE_END +#else + {NULL,NULL,NULL} +#endif +}; +/* }}} */ + +/* {{{ ZE2 OO definitions */ +static zend_class_entry *zip_class_entry; +static zend_object_handlers zip_object_handlers; + +static HashTable zip_prop_handlers; + +typedef int (*zip_read_int_t)(struct zip *za); +typedef char *(*zip_read_const_char_t)(struct zip *za, int *len); +typedef char *(*zip_read_const_char_from_ze_t)(ze_zip_object *obj); + +typedef struct _zip_prop_handler { + zip_read_int_t read_int_func; + zip_read_const_char_t read_const_char_func; + zip_read_const_char_from_ze_t read_const_char_from_obj_func; + + int type; +} zip_prop_handler; +/* }}} */ + +static void php_zip_register_prop_handler(HashTable *prop_handler, char *name, zip_read_int_t read_int_func, zip_read_const_char_t read_char_func, zip_read_const_char_from_ze_t read_char_from_obj_func, int rettype) /* {{{ */ +{ + zip_prop_handler hnd; + + hnd.read_const_char_func = read_char_func; + hnd.read_int_func = read_int_func; + hnd.read_const_char_from_obj_func = read_char_from_obj_func; + hnd.type = rettype; + zend_hash_str_add_mem(prop_handler, name, strlen(name), &hnd, sizeof(zip_prop_handler)); +} +/* }}} */ + +static zval *php_zip_property_reader(ze_zip_object *obj, zip_prop_handler *hnd, zval *rv) /* {{{ */ +{ + const char *retchar = NULL; + int retint = 0; + int len = 0; + + if (obj && obj->za != NULL) { + if (hnd->read_const_char_func) { + retchar = hnd->read_const_char_func(obj->za, &len); + } else { + if (hnd->read_int_func) { + retint = hnd->read_int_func(obj->za); + if (retint == -1) { + php_error_docref(NULL, E_WARNING, "Internal zip error returned"); + return NULL; + } + } else { + if (hnd->read_const_char_from_obj_func) { + retchar = hnd->read_const_char_from_obj_func(obj); + len = strlen(retchar); + } + } + } + } + + switch (hnd->type) { + case IS_STRING: + if (retchar) { + ZVAL_STRINGL(rv, (char *) retchar, len); + } else { + ZVAL_EMPTY_STRING(rv); + } + break; + /* case IS_TRUE */ + case IS_FALSE: + ZVAL_BOOL(rv, (long)retint); + break; + case IS_LONG: + ZVAL_LONG(rv, (long)retint); + break; + default: + ZVAL_NULL(rv); + } + + return rv; +} +/* }}} */ + +static zval *php_zip_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) /* {{{ */ +{ + ze_zip_object *obj; + zval tmp_member; + zval *retval = NULL; + zip_prop_handler *hnd = NULL; + zend_object_handlers *std_hnd; + + if (Z_TYPE_P(member) != IS_STRING) { + ZVAL_DUP(&tmp_member, member); + convert_to_string(&tmp_member); + member = &tmp_member; + cache_slot = NULL; + } + + obj = Z_ZIP_P(object); + + if (obj->prop_handler != NULL) { + hnd = zend_hash_find_ptr(obj->prop_handler, Z_STR_P(member)); + } + + if (hnd == NULL) { + std_hnd = zend_get_std_object_handlers(); + retval = std_hnd->get_property_ptr_ptr(object, member, type, cache_slot); + } + + if (member == &tmp_member) { + zval_dtor(member); + } + + return retval; +} +/* }}} */ + +static zval *php_zip_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */ +{ + ze_zip_object *obj; + zval tmp_member; + zval *retval = NULL; + zip_prop_handler *hnd = NULL; + zend_object_handlers *std_hnd; + + if (Z_TYPE_P(member) != IS_STRING) { + ZVAL_DUP(&tmp_member, member); + convert_to_string(&tmp_member); + member = &tmp_member; + cache_slot = NULL; + } + + obj = Z_ZIP_P(object); + + if (obj->prop_handler != NULL) { + hnd = zend_hash_find_ptr(obj->prop_handler, Z_STR_P(member)); + } + + if (hnd != NULL) { + retval = php_zip_property_reader(obj, hnd, rv); + if (retval == NULL) { + retval = &EG(uninitialized_zval); + } + } else { + std_hnd = zend_get_std_object_handlers(); + retval = std_hnd->read_property(object, member, type, cache_slot, rv); + } + + if (member == &tmp_member) { + zval_dtor(member); + } + + return retval; +} +/* }}} */ + +static int php_zip_has_property(zval *object, zval *member, int type, void **cache_slot) /* {{{ */ +{ + ze_zip_object *obj; + zval tmp_member; + zip_prop_handler *hnd = NULL; + zend_object_handlers *std_hnd; + int retval = 0; + + if (Z_TYPE_P(member) != IS_STRING) { + ZVAL_DUP(&tmp_member, member); + convert_to_string(&tmp_member); + member = &tmp_member; + cache_slot = NULL; + } + + obj = Z_ZIP_P(object); + + if (obj->prop_handler != NULL) { + hnd = zend_hash_find_ptr(obj->prop_handler, Z_STR_P(member)); + } + + if (hnd != NULL) { + zval tmp, *prop; + + if (type == 2) { + retval = 1; + } else if ((prop = php_zip_property_reader(obj, hnd, &tmp)) != NULL) { + if (type == 1) { + retval = zend_is_true(&tmp); + } else if (type == 0) { + retval = (Z_TYPE(tmp) != IS_NULL); + } + } + + zval_ptr_dtor(&tmp); + } else { + std_hnd = zend_get_std_object_handlers(); + retval = std_hnd->has_property(object, member, type, cache_slot); + } + + if (member == &tmp_member) { + zval_dtor(member); + } + + return retval; +} +/* }}} */ + +static HashTable *php_zip_get_properties(zval *object)/* {{{ */ +{ + ze_zip_object *obj; + HashTable *props; + zip_prop_handler *hnd; + zend_string *key; + + obj = Z_ZIP_P(object); + props = zend_std_get_properties(object); + + if (obj->prop_handler == NULL) { + return NULL; + } + + ZEND_HASH_FOREACH_STR_KEY_PTR(obj->prop_handler, key, hnd) { + zval *ret, val; + ret = php_zip_property_reader(obj, hnd, &val); + if (ret == NULL) { + ret = &EG(uninitialized_zval); + } + zend_hash_update(props, key, ret); + } ZEND_HASH_FOREACH_END(); + + return props; +} +/* }}} */ + +static void php_zip_object_free_storage(zend_object *object) /* {{{ */ +{ + ze_zip_object * intern = php_zip_fetch_object(object); + int i; + + if (!intern) { + return; + } + if (intern->za) { + if (zip_close(intern->za) != 0) { + php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context"); + return; + } + intern->za = NULL; + } + + if (intern->buffers_cnt>0) { + for (i=0; ibuffers_cnt; i++) { + efree(intern->buffers[i]); + } + efree(intern->buffers); + } + + intern->za = NULL; + zend_object_std_dtor(&intern->zo); + + if (intern->filename) { + efree(intern->filename); + } +} +/* }}} */ + +static zend_object *php_zip_object_new(zend_class_entry *class_type) /* {{{ */ +{ + ze_zip_object *intern; + + intern = ecalloc(1, sizeof(ze_zip_object) + zend_object_properties_size(class_type)); + intern->prop_handler = &zip_prop_handlers; + zend_object_std_init(&intern->zo, class_type); + object_properties_init(&intern->zo, class_type); + intern->zo.handlers = &zip_object_handlers; + + return &intern->zo; +} +/* }}} */ + +/* {{{ Resource dtors */ + +/* {{{ php_zip_free_dir */ +static void php_zip_free_dir(zend_resource *rsrc) +{ + zip_rsrc * zip_int = (zip_rsrc *) rsrc->ptr; + + if (zip_int) { + if (zip_int->za) { + if (zip_close(zip_int->za) != 0) { + php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context"); + } + zip_int->za = NULL; + } + + efree(rsrc->ptr); + + rsrc->ptr = NULL; + } +} +/* }}} */ + +/* {{{ php_zip_free_entry */ +static void php_zip_free_entry(zend_resource *rsrc) +{ + zip_read_rsrc *zr_rsrc = (zip_read_rsrc *) rsrc->ptr; + + if (zr_rsrc) { + if (zr_rsrc->zf) { + zip_fclose(zr_rsrc->zf); + zr_rsrc->zf = NULL; + } + efree(zr_rsrc); + rsrc->ptr = NULL; + } +} +/* }}} */ + +/* }}}*/ + +/* reset macro */ + +/* {{{ function prototypes */ +static PHP_MINIT_FUNCTION(zip); +static PHP_MSHUTDOWN_FUNCTION(zip); +static PHP_MINFO_FUNCTION(zip); +/* }}} */ + +/* {{{ zip_module_entry + */ +zend_module_entry zip_module_entry = { + STANDARD_MODULE_HEADER, + "zip", + zip_functions, + PHP_MINIT(zip), + PHP_MSHUTDOWN(zip), + NULL, + NULL, + PHP_MINFO(zip), + PHP_ZIP_VERSION, + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_ZIP +ZEND_GET_MODULE(zip) +#endif +/* set macro */ + +/* {{{ proto resource zip_open(string filename) +Create new zip using source uri for output */ +static PHP_NAMED_FUNCTION(zif_zip_open) +{ + char resolved_path[MAXPATHLEN + 1]; + zip_rsrc *rsrc_int; + int err = 0; + zend_string *filename; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &filename) == FAILURE) { + return; + } + + if (filename->len == 0) { + php_error_docref(NULL, E_WARNING, "Empty string as source"); + RETURN_FALSE; + } + + if (ZIP_OPENBASEDIR_CHECKPATH(filename->val)) { + RETURN_FALSE; + } + + if(!expand_filepath(filename->val, resolved_path)) { + RETURN_FALSE; + } + + rsrc_int = (zip_rsrc *)emalloc(sizeof(zip_rsrc)); + + rsrc_int->za = zip_open(resolved_path, 0, &err); + if (rsrc_int->za == NULL) { + efree(rsrc_int); + RETURN_LONG((zend_long)err); + } + + rsrc_int->index_current = 0; + rsrc_int->num_files = zip_get_num_files(rsrc_int->za); + + RETURN_RES(zend_register_resource(rsrc_int, le_zip_dir)); +} +/* }}} */ + +/* {{{ proto void zip_close(resource zip) + Close a Zip archive */ +static PHP_NAMED_FUNCTION(zif_zip_close) +{ + zval * zip; + zip_rsrc *z_rsrc = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip) == FAILURE) { + return; + } + + if ((z_rsrc = (zip_rsrc *)zend_fetch_resource(Z_RES_P(zip), le_zip_dir_name, le_zip_dir)) == NULL) { + RETURN_FALSE; + } + + /* really close the zip will break BC :-D */ + zend_list_close(Z_RES_P(zip)); +} +/* }}} */ + +/* {{{ proto resource zip_read(resource zip) + Returns the next file in the archive */ +static PHP_NAMED_FUNCTION(zif_zip_read) +{ + zval *zip_dp; + zip_read_rsrc *zr_rsrc; + int ret; + zip_rsrc *rsrc_int; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip_dp) == FAILURE) { + return; + } + + if ((rsrc_int = (zip_rsrc *)zend_fetch_resource(Z_RES_P(zip_dp), le_zip_dir_name, le_zip_dir)) == NULL) { + RETURN_FALSE; + } + + if (rsrc_int && rsrc_int->za) { + if (rsrc_int->index_current >= rsrc_int->num_files) { + RETURN_FALSE; + } + + zr_rsrc = emalloc(sizeof(zip_read_rsrc)); + + ret = zip_stat_index(rsrc_int->za, rsrc_int->index_current, 0, &zr_rsrc->sb); + + if (ret != 0) { + efree(zr_rsrc); + RETURN_FALSE; + } + + zr_rsrc->zf = zip_fopen_index(rsrc_int->za, rsrc_int->index_current, 0); + if (zr_rsrc->zf) { + rsrc_int->index_current++; + RETURN_RES(zend_register_resource(zr_rsrc, le_zip_entry)); + } else { + efree(zr_rsrc); + RETURN_FALSE; + } + + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool zip_entry_open(resource zip_dp, resource zip_entry [, string mode]) + Open a Zip File, pointed by the resource entry */ +/* Dummy function to follow the old API */ +static PHP_NAMED_FUNCTION(zif_zip_entry_open) +{ + zval * zip; + zval * zip_entry; + char *mode = NULL; + size_t mode_len = 0; + zip_read_rsrc * zr_rsrc; + zip_rsrc *z_rsrc; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|s", &zip, &zip_entry, &mode, &mode_len) == FAILURE) { + return; + } + + if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) { + RETURN_FALSE; + } + + if ((z_rsrc = (zip_rsrc *)zend_fetch_resource(Z_RES_P(zip), le_zip_dir_name, le_zip_dir)) == NULL) { + RETURN_FALSE; + } + + if (zr_rsrc->zf != NULL) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool zip_entry_close(resource zip_ent) + Close a zip entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_close) +{ + zval * zip_entry; + zip_read_rsrc * zr_rsrc; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip_entry) == FAILURE) { + return; + } + + if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) { + RETURN_FALSE; + } + + RETURN_BOOL(SUCCESS == zend_list_close(Z_RES_P(zip_entry))); +} +/* }}} */ + +/* {{{ proto mixed zip_entry_read(resource zip_entry [, int len]) + Read from an open directory entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_read) +{ + zval * zip_entry; + zend_long len = 0; + zip_read_rsrc * zr_rsrc; + zend_string *buffer; + int n = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &zip_entry, &len) == FAILURE) { + return; + } + + if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) { + RETURN_FALSE; + } + + if (len <= 0) { + len = 1024; + } + + if (zr_rsrc->zf) { + buffer = zend_string_alloc(len, 0); + n = zip_fread(zr_rsrc->zf, buffer->val, buffer->len); + if (n > 0) { + buffer->val[n] = '\0'; + buffer->len = n; + RETURN_NEW_STR(buffer); + } else { + zend_string_free(buffer); + RETURN_EMPTY_STRING() + } + } else { + RETURN_FALSE; + } +} +/* }}} */ + +static void php_zip_entry_get_info(INTERNAL_FUNCTION_PARAMETERS, int opt) /* {{{ */ +{ + zval * zip_entry; + zip_read_rsrc * zr_rsrc; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip_entry) == FAILURE) { + return; + } + + if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) { + RETURN_FALSE; + } + + if (!zr_rsrc->zf) { + RETURN_FALSE; + } + + switch (opt) { + case 0: + RETURN_STRING((char *)zr_rsrc->sb.name); + break; + case 1: + RETURN_LONG((zend_long) (zr_rsrc->sb.comp_size)); + break; + case 2: + RETURN_LONG((zend_long) (zr_rsrc->sb.size)); + break; + case 3: + switch (zr_rsrc->sb.comp_method) { + case 0: + RETURN_STRING("stored"); + break; + case 1: + RETURN_STRING("shrunk"); + break; + case 2: + case 3: + case 4: + case 5: + RETURN_STRING("reduced"); + break; + case 6: + RETURN_STRING("imploded"); + break; + case 7: + RETURN_STRING("tokenized"); + break; + case 8: + RETURN_STRING("deflated"); + break; + case 9: + RETURN_STRING("deflatedX"); + break; + case 10: + RETURN_STRING("implodedX"); + break; + default: + RETURN_FALSE; + } + RETURN_LONG((zend_long) (zr_rsrc->sb.comp_method)); + break; + } + +} +/* }}} */ + +/* {{{ proto string zip_entry_name(resource zip_entry) + Return the name given a ZZip entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_name) +{ + php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto int zip_entry_compressedsize(resource zip_entry) + Return the compressed size of a ZZip entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_compressedsize) +{ + php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto int zip_entry_filesize(resource zip_entry) + Return the actual filesize of a ZZip entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_filesize) +{ + php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2); +} +/* }}} */ + +/* {{{ proto string zip_entry_compressionmethod(resource zip_entry) + Return a string containing the compression method used on a particular entry */ +static PHP_NAMED_FUNCTION(zif_zip_entry_compressionmethod) +{ + php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3); +} +/* }}} */ + +/* {{{ proto mixed ZipArchive::open(string source [, int flags]) +Create new zip using source uri for output, return TRUE on success or the error code */ +static ZIPARCHIVE_METHOD(open) +{ + struct zip *intern; + int err = 0; + zend_long flags = 0; + char *resolved_path; + zend_string *filename; + zval *self = getThis(); + ze_zip_object *ze_obj = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &filename, &flags) == FAILURE) { + return; + } + + if (self) { + /* We do not use ZIP_FROM_OBJECT, zip init function here */ + ze_obj = Z_ZIP_P(self); + } + + if (filename->len == 0) { + php_error_docref(NULL, E_WARNING, "Empty string as source"); + RETURN_FALSE; + } + + if (ZIP_OPENBASEDIR_CHECKPATH(filename->val)) { + RETURN_FALSE; + } + + if (!(resolved_path = expand_filepath(filename->val, NULL))) { + RETURN_FALSE; + } + + if (ze_obj->za) { + /* we already have an opened zip, free it */ + if (zip_close(ze_obj->za) != 0) { + php_error_docref(NULL, E_WARNING, "Empty string as source"); + efree(resolved_path); + RETURN_FALSE; + } + ze_obj->za = NULL; + } + if (ze_obj->filename) { + efree(ze_obj->filename); + ze_obj->filename = NULL; + } + + intern = zip_open(resolved_path, flags, &err); + if (!intern || err) { + efree(resolved_path); + RETURN_LONG((zend_long)err); + } + ze_obj->filename = resolved_path; + ze_obj->filename_len = strlen(resolved_path); + ze_obj->za = intern; + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto resource ZipArchive::setPassword(string password) +Set the password for the active archive */ +static ZIPARCHIVE_METHOD(setPassword) +{ + struct zip *intern; + zval *self = getThis(); + char *password; + size_t password_len; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &password, &password_len) == FAILURE) { + return; + } + + if (password_len < 1) { + RETURN_FALSE; + } else { + int res = zip_set_default_password(intern, (const char *)password); + if (res == 0) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::close() +close the zip archive */ +static ZIPARCHIVE_METHOD(close) +{ + struct zip *intern; + zval *self = getThis(); + ze_zip_object *ze_obj; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + ze_obj = Z_ZIP_P(self); + + if (zip_close(intern)) { + zip_discard(intern); + } + + efree(ze_obj->filename); + ze_obj->filename = NULL; + ze_obj->filename_len = 0; + ze_obj->za = NULL; + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string ZipArchive::getStatusString() + * Returns the status error message, system and/or zip messages */ +static ZIPARCHIVE_METHOD(getStatusString) +{ + struct zip *intern; + zval *self = getThis(); +#if LIBZIP_VERSION_MAJOR < 1 + int zep, syp, len; + char error_string[128]; +#else + zip_error_t *err; +#endif + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + +#if LIBZIP_VERSION_MAJOR < 1 + zip_error_get(intern, &zep, &syp); + + len = zip_error_to_str(error_string, 128, zep, syp); + RETVAL_STRINGL(error_string, len); +#else + err = zip_get_error(intern); + RETVAL_STRING(zip_error_strerror(err)); + zip_error_fini(err); +#endif +} +/* }}} */ + +/* {{{ proto bool ZipArchive::createEmptyDir(string dirname) +Returns the index of the entry named filename in the archive */ +static ZIPARCHIVE_METHOD(addEmptyDir) +{ + struct zip *intern; + zval *self = getThis(); + char *dirname; + size_t dirname_len; + int idx; + struct zip_stat sb; + char *s; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &dirname, &dirname_len) == FAILURE) { + return; + } + + if (dirname_len<1) { + RETURN_FALSE; + } + + if (dirname[dirname_len-1] != '/') { + s=(char *)emalloc(dirname_len+2); + strcpy(s, dirname); + s[dirname_len] = '/'; + s[dirname_len+1] = '\0'; + } else { + s = dirname; + } + + idx = zip_stat(intern, s, 0, &sb); + if (idx >= 0) { + RETVAL_FALSE; + } else { + if (zip_add_dir(intern, (const char *)s) == -1) { + RETVAL_FALSE; + } + zip_error_clear(intern); + RETVAL_TRUE; + } + + if (s != dirname) { + efree(s); + } +} +/* }}} */ + +static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ +{ + struct zip *intern; + zval *self = getThis(); + char *path = NULL; + char *remove_path = NULL; + char *add_path = NULL; + size_t add_path_len, remove_path_len = 0, path_len = 0; + zend_long remove_all_path = 0; + zend_long flags = 0; + zval *options = NULL; + int found; + zend_string *pattern; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + /* 1 == glob, 2 == pcre */ + if (type == 1) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|la", + &pattern, &flags, &options) == FAILURE) { + return; + } + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|sa", + &pattern, &path, &path_len, &options) == FAILURE) { + return; + } + } + + if (pattern->len == 0) { + php_error_docref(NULL, E_NOTICE, "Empty string as pattern"); + RETURN_FALSE; + } + if (options && (php_zip_parse_options(options, &remove_all_path, &remove_path, &remove_path_len, + &add_path, &add_path_len) < 0)) { + RETURN_FALSE; + } + + if (remove_path && remove_path_len > 1) { + size_t real_len = strlen(remove_path); + if ((real_len > 1) && ((remove_path[real_len - 1] == '/') || (remove_path[real_len - 1] == '\\'))) { + remove_path[real_len - 1] = '\0'; + } + } + + if (type == 1) { + found = php_zip_glob(pattern->val, pattern->len, flags, return_value); + } else { + found = php_zip_pcre(pattern, path, path_len, return_value); + } + + if (found > 0) { + int i; + zval *zval_file; + + for (i = 0; i < found; i++) { + char *file_stripped, *entry_name; + size_t entry_name_len, file_stripped_len; + char entry_name_buf[MAXPATHLEN]; + zend_string *basename = NULL; + + if ((zval_file = zend_hash_index_find(Z_ARRVAL_P(return_value), i)) != NULL) { + if (remove_all_path) { + basename = php_basename(Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), NULL, 0); + file_stripped = basename->val; + file_stripped_len = basename->len; + } else if (remove_path && strstr(Z_STRVAL_P(zval_file), remove_path) != NULL) { + file_stripped = Z_STRVAL_P(zval_file) + remove_path_len + 1; + file_stripped_len = Z_STRLEN_P(zval_file) - remove_path_len - 1; + } else { + file_stripped = Z_STRVAL_P(zval_file); + file_stripped_len = Z_STRLEN_P(zval_file); + } + + if (add_path) { + if ((add_path_len + file_stripped_len) > MAXPATHLEN) { + php_error_docref(NULL, E_WARNING, "Entry name too long (max: %d, %pd given)", + MAXPATHLEN - 1, (add_path_len + file_stripped_len)); + zval_ptr_dtor(return_value); + RETURN_FALSE; + } + + snprintf(entry_name_buf, MAXPATHLEN, "%s%s", add_path, file_stripped); + entry_name = entry_name_buf; + entry_name_len = strlen(entry_name); + } else { + entry_name = Z_STRVAL_P(zval_file); + entry_name_len = Z_STRLEN_P(zval_file); + } + if (basename) { + zend_string_release(basename); + basename = NULL; + } + if (php_zip_add_file(intern, Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), + entry_name, entry_name_len, 0, 0) < 0) { + zval_dtor(return_value); + RETURN_FALSE; + } + } + } + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::addGlob(string pattern[,int flags [, array options]]) +Add files matching the glob pattern. See php's glob for the pattern syntax. */ +static ZIPARCHIVE_METHOD(addGlob) +{ + php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto bool ZipArchive::addPattern(string pattern[, string path [, array options]]) +Add files matching the pcre pattern. See php's pcre for the pattern syntax. */ +static ZIPARCHIVE_METHOD(addPattern) +{ + php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2); +} +/* }}} */ + +/* {{{ proto bool ZipArchive::addFile(string filepath[, string entryname[, int start [, int length]]]) +Add a file in a Zip archive using its path and the name to use. */ +static ZIPARCHIVE_METHOD(addFile) +{ + struct zip *intern; + zval *self = getThis(); + char *entry_name = NULL; + size_t entry_name_len = 0; + zend_long offset_start = 0, offset_len = 0; + zend_string *filename; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|sll", + &filename, &entry_name, &entry_name_len, &offset_start, &offset_len) == FAILURE) { + return; + } + + if (filename->len == 0) { + php_error_docref(NULL, E_NOTICE, "Empty string as filename"); + RETURN_FALSE; + } + + if (entry_name_len == 0) { + entry_name = filename->val; + entry_name_len = filename->len; + } + + if (php_zip_add_file(intern, filename->val, filename->len, entry_name, entry_name_len, 0, 0) < 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::addFromString(string name, string content) +Add a file using content and the entry name */ +static ZIPARCHIVE_METHOD(addFromString) +{ + struct zip *intern; + zval *self = getThis(); + zend_string *buffer; + char *name; + size_t name_len; + ze_zip_object *ze_obj; + struct zip_source *zs; + int pos = 0; + int cur_idx; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS", + &name, &name_len, &buffer) == FAILURE) { + return; + } + + ze_obj = Z_ZIP_P(self); + if (ze_obj->buffers_cnt) { + ze_obj->buffers = (char **)erealloc(ze_obj->buffers, sizeof(char *) * (ze_obj->buffers_cnt+1)); + pos = ze_obj->buffers_cnt++; + } else { + ze_obj->buffers = (char **)emalloc(sizeof(char *)); + ze_obj->buffers_cnt++; + pos = 0; + } + ze_obj->buffers[pos] = (char *)emalloc(buffer->len + 1); + memcpy(ze_obj->buffers[pos], buffer->val, buffer->len + 1); + + zs = zip_source_buffer(intern, ze_obj->buffers[pos], buffer->len, 0); + + if (zs == NULL) { + RETURN_FALSE; + } + + cur_idx = zip_name_locate(intern, (const char *)name, 0); + /* TODO: fix _zip_replace */ + if (cur_idx >= 0) { + if (zip_delete(intern, cur_idx) == -1) { + zip_source_free(zs); + RETURN_FALSE; + } + } + + if (zip_add(intern, name, zs) == -1) { + zip_source_free(zs); + RETURN_FALSE; + } else { + zip_error_clear(intern); + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto array ZipArchive::statName(string filename[, int flags]) +Returns the information about a the zip entry filename */ +static ZIPARCHIVE_METHOD(statName) +{ + struct zip *intern; + zval *self = getThis(); + zend_long flags = 0; + struct zip_stat sb; + zend_string *name; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &name, &flags) == FAILURE) { + return; + } + + PHP_ZIP_STAT_PATH(intern, name->val, name->len, flags, sb); + + RETURN_SB(&sb); +} +/* }}} */ + +/* {{{ proto resource ZipArchive::statIndex(int index[, int flags]) +Returns the zip entry informations using its index */ +static ZIPARCHIVE_METHOD(statIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index, flags = 0; + + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", + &index, &flags) == FAILURE) { + return; + } + + if (zip_stat_index(intern, index, flags, &sb) != 0) { + RETURN_FALSE; + } + RETURN_SB(&sb); +} +/* }}} */ + +/* {{{ proto int ZipArchive::locateName(string filename[, int flags]) +Returns the index of the entry named filename in the archive */ +static ZIPARCHIVE_METHOD(locateName) +{ + struct zip *intern; + zval *self = getThis(); + zend_long flags = 0; + zend_long idx = -1; + zend_string *name; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &name, &flags) == FAILURE) { + return; + } + + if (name->len < 1) { + RETURN_FALSE; + } + + idx = (zend_long)zip_name_locate(intern, (const char *)name->val, flags); + + if (idx >= 0) { + RETURN_LONG(idx); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string ZipArchive::getNameIndex(int index [, int flags]) +Returns the name of the file at position index */ +static ZIPARCHIVE_METHOD(getNameIndex) +{ + struct zip *intern; + zval *self = getThis(); + const char *name; + zend_long flags = 0, index = 0; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", + &index, &flags) == FAILURE) { + return; + } + + name = zip_get_name(intern, (int) index, flags); + + if (name) { + RETVAL_STRING((char *)name); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setArchiveComment(string comment) +Set or remove (NULL/'') the comment of the archive */ +static ZIPARCHIVE_METHOD(setArchiveComment) +{ + struct zip *intern; + zval *self = getThis(); + size_t comment_len; + char * comment; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &comment, &comment_len) == FAILURE) { + return; + } + if (zip_set_archive_comment(intern, (const char *)comment, (int)comment_len)) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto string ZipArchive::getArchiveComment([int flags]) +Returns the comment of an entry using its index */ +static ZIPARCHIVE_METHOD(getArchiveComment) +{ + struct zip *intern; + zval *self = getThis(); + zend_long flags = 0; + const char * comment; + int comment_len = 0; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) { + return; + } + + comment = zip_get_archive_comment(intern, &comment_len, (int)flags); + if(comment==NULL) { + RETURN_FALSE; + } + RETURN_STRINGL((char *)comment, (zend_long)comment_len); +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setCommentName(string name, string comment) +Set or remove (NULL/'') the comment of an entry using its Name */ +static ZIPARCHIVE_METHOD(setCommentName) +{ + struct zip *intern; + zval *self = getThis(); + size_t comment_len, name_len; + char * comment, *name; + int idx; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", + &name, &name_len, &comment, &comment_len) == FAILURE) { + return; + } + + if (name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as entry name"); + } + + idx = zip_name_locate(intern, name, 0); + if (idx < 0) { + RETURN_FALSE; + } + PHP_ZIP_SET_FILE_COMMENT(intern, idx, comment, comment_len); +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setCommentIndex(int index, string comment) +Set or remove (NULL/'') the comment of an entry using its index */ +static ZIPARCHIVE_METHOD(setCommentIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index; + size_t comment_len; + char * comment; + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", + &index, &comment, &comment_len) == FAILURE) { + return; + } + + PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + PHP_ZIP_SET_FILE_COMMENT(intern, index, comment, comment_len); +} +/* }}} */ + +/* those constants/functions are only available in libzip since 0.11.2 */ +#ifdef ZIP_OPSYS_DEFAULT + +/* {{{ proto bool ZipArchive::setExternalAttributesName(string name, int opsys, int attr [, int flags]) +Set external attributes for file in zip, using its name */ +static ZIPARCHIVE_METHOD(setExternalAttributesName) +{ + struct zip *intern; + zval *self = getThis(); + size_t name_len; + char *name; + zend_long flags=0, opsys, attr; + zip_int64_t idx; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|l", + &name, &name_len, &opsys, &attr, &flags) == FAILURE) { + return; + } + + if (name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as entry name"); + } + + idx = zip_name_locate(intern, name, 0); + if (idx < 0) { + RETURN_FALSE; + } + if (zip_file_set_external_attributes(intern, idx, (zip_flags_t)flags, + (zip_uint8_t)(opsys&0xff), (zip_uint32_t)attr) < 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setExternalAttributesIndex(int index, int opsys, int attr [, int flags]) +Set external attributes for file in zip, using its index */ +static ZIPARCHIVE_METHOD(setExternalAttributesIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index, flags=0, opsys, attr; + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll|l", + &index, &opsys, &attr, &flags) == FAILURE) { + return; + } + + PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + if (zip_file_set_external_attributes(intern, (zip_uint64_t)index, + (zip_flags_t)flags, (zip_uint8_t)(opsys&0xff), (zip_uint32_t)attr) < 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::getExternalAttributesName(string name, int &opsys, int &attr [, int flags]) +Get external attributes for file in zip, using its name */ +static ZIPARCHIVE_METHOD(getExternalAttributesName) +{ + struct zip *intern; + zval *self = getThis(), *z_opsys, *z_attr; + size_t name_len; + char *name; + zend_long flags=0; + zip_uint8_t opsys; + zip_uint32_t attr; + zip_int64_t idx; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/z/|l", + &name, &name_len, &z_opsys, &z_attr, &flags) == FAILURE) { + return; + } + + if (name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as entry name"); + } + + idx = zip_name_locate(intern, name, 0); + if (idx < 0) { + RETURN_FALSE; + } + if (zip_file_get_external_attributes(intern, idx, + (zip_flags_t)flags, &opsys, &attr) < 0) { + RETURN_FALSE; + } + zval_ptr_dtor(z_opsys); + ZVAL_LONG(z_opsys, opsys); + zval_ptr_dtor(z_attr); + ZVAL_LONG(z_attr, attr); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::getExternalAttributesIndex(int index, int &opsys, int &attr [, int flags]) +Get external attributes for file in zip, using its index */ +static ZIPARCHIVE_METHOD(getExternalAttributesIndex) +{ + struct zip *intern; + zval *self = getThis(), *z_opsys, *z_attr; + zend_long index, flags=0; + zip_uint8_t opsys; + zip_uint32_t attr; + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz/z/|l", + &index, &z_opsys, &z_attr, &flags) == FAILURE) { + return; + } + + PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + if (zip_file_get_external_attributes(intern, (zip_uint64_t)index, + (zip_flags_t)flags, &opsys, &attr) < 0) { + RETURN_FALSE; + } + zval_dtor(z_opsys); + ZVAL_LONG(z_opsys, opsys); + zval_dtor(z_attr); + ZVAL_LONG(z_attr, attr); + RETURN_TRUE; +} +/* }}} */ +#endif /* ifdef ZIP_OPSYS_DEFAULT */ + +/* {{{ proto string ZipArchive::getCommentName(string name[, int flags]) +Returns the comment of an entry using its name */ +static ZIPARCHIVE_METHOD(getCommentName) +{ + struct zip *intern; + zval *self = getThis(); + size_t name_len; + int idx; + zend_long flags = 0; + int comment_len = 0; + const char * comment; + char *name; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", + &name, &name_len, &flags) == FAILURE) { + return; + } + if (name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as entry name"); + RETURN_FALSE; + } + + idx = zip_name_locate(intern, name, 0); + if (idx < 0) { + RETURN_FALSE; + } + + comment = zip_get_file_comment(intern, idx, &comment_len, (int)flags); + RETURN_STRINGL((char *)comment, (zend_long)comment_len); +} +/* }}} */ + +/* {{{ proto string ZipArchive::getCommentIndex(int index[, int flags]) +Returns the comment of an entry using its index */ +static ZIPARCHIVE_METHOD(getCommentIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index, flags = 0; + const char * comment; + int comment_len = 0; + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", + &index, &flags) == FAILURE) { + return; + } + + PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + comment = zip_get_file_comment(intern, index, &comment_len, (int)flags); + RETURN_STRINGL((char *)comment, (zend_long)comment_len); +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setCompressionName(string name, int comp_method[, int comp_flags]) +Set the compression of a file in zip, using its name */ +static ZIPARCHIVE_METHOD(setCompressionName) + { + struct zip *intern; + zval *this = getThis(); + size_t name_len; + char *name; + zip_int64_t idx; + zend_long comp_method, comp_flags = 0; + + if (!this) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", + &name, &name_len, &comp_method, &comp_flags) == FAILURE) { + return; + } + + if (name_len < 1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Empty string as entry name"); + } + + idx = zip_name_locate(intern, name, 0); + if (idx < 0) { + RETURN_FALSE; + } + + if (zip_set_file_compression(intern, (zip_uint64_t)idx, + (zip_int32_t)comp_method, (zip_uint32_t)comp_flags) != 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::setCompressionIndex(int index, int comp_method[, int comp_flags]) +Set the compression of a file in zip, using its index */ +static ZIPARCHIVE_METHOD(setCompressionIndex) +{ + struct zip *intern; + zval *this = getThis(); + zend_long index; + zend_long comp_method, comp_flags = 0; + + if (!this) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|l", + &index, &comp_method, &comp_flags) == FAILURE) { + return; + } + + if (zip_set_file_compression(intern, (zip_uint64_t)index, + (zip_int32_t)comp_method, (zip_uint32_t)comp_flags) != 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::deleteIndex(int index) +Delete a file using its index */ +static ZIPARCHIVE_METHOD(deleteIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { + return; + } + + if (index < 0) { + RETURN_FALSE; + } + + if (zip_delete(intern, index) < 0) { + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::deleteName(string name) +Delete a file using its index */ +static ZIPARCHIVE_METHOD(deleteName) +{ + struct zip *intern; + zval *self = getThis(); + size_t name_len; + char *name; + struct zip_stat sb; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) { + return; + } + if (name_len < 1) { + RETURN_FALSE; + } + + PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb); + if (zip_delete(intern, sb.index)) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::renameIndex(int index, string new_name) +Rename an entry selected by its index to new_name */ +static ZIPARCHIVE_METHOD(renameIndex) +{ + struct zip *intern; + zval *self = getThis(); + + char *new_name; + size_t new_name_len; + zend_long index; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &index, &new_name, &new_name_len) == FAILURE) { + return; + } + + if (index < 0) { + RETURN_FALSE; + } + + if (new_name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as new entry name"); + RETURN_FALSE; + } + if (zip_rename(intern, index, (const char *)new_name) != 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::renameName(string name, string new_name) +Rename an entry selected by its name to new_name */ +static ZIPARCHIVE_METHOD(renameName) +{ + struct zip *intern; + zval *self = getThis(); + struct zip_stat sb; + char *name, *new_name; + size_t name_len, new_name_len; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &new_name, &new_name_len) == FAILURE) { + return; + } + + if (new_name_len < 1) { + php_error_docref(NULL, E_NOTICE, "Empty string as new entry name"); + RETURN_FALSE; + } + + PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb); + + if (zip_rename(intern, sb.index, (const char *)new_name)) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ZipArchive::unchangeIndex(int index) +Changes to the file at position index are reverted */ +static ZIPARCHIVE_METHOD(unchangeIndex) +{ + struct zip *intern; + zval *self = getThis(); + zend_long index; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { + return; + } + + if (index < 0) { + RETURN_FALSE; + } + + if (zip_unchange(intern, index) != 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::unchangeName(string name) +Changes to the file named 'name' are reverted */ +static ZIPARCHIVE_METHOD(unchangeName) +{ + struct zip *intern; + zval *self = getThis(); + struct zip_stat sb; + char *name; + size_t name_len; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) { + return; + } + + if (name_len < 1) { + RETURN_FALSE; + } + + PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb); + + if (zip_unchange(intern, sb.index) != 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::unchangeAll() +All changes to files and global information in archive are reverted */ +static ZIPARCHIVE_METHOD(unchangeAll) +{ + struct zip *intern; + zval *self = getThis(); + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zip_unchange_all(intern) != 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::unchangeArchive() +Revert all global changes to the archive archive. For now, this only reverts archive comment changes. */ +static ZIPARCHIVE_METHOD(unchangeArchive) +{ + struct zip *intern; + zval *self = getThis(); + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zip_unchange_archive(intern) != 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ZipArchive::extractTo(string pathto[, mixed files]) +Extract one or more file from a zip archive */ +/* TODO: + * - allow index or array of indeces + * - replace path + * - patterns + */ +static ZIPARCHIVE_METHOD(extractTo) +{ + struct zip *intern; + + zval *self = getThis(); + zval *zval_files = NULL; + zval *zval_file = NULL; + php_stream_statbuf ssb; + char *pathto; + size_t pathto_len; + int ret, i; + + int nelems; + + if (!self) { + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|z", &pathto, &pathto_len, &zval_files) == FAILURE) { + return; + } + + if (pathto_len < 1) { + RETURN_FALSE; + } + + if (php_stream_stat_path_ex(pathto, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL) < 0) { + ret = php_stream_mkdir(pathto, 0777, PHP_STREAM_MKDIR_RECURSIVE, NULL); + if (!ret) { + RETURN_FALSE; + } + } + + ZIP_FROM_OBJECT(intern, self); + if (zval_files && (Z_TYPE_P(zval_files) != IS_NULL)) { + switch (Z_TYPE_P(zval_files)) { + case IS_STRING: + if (!php_zip_extract_file(intern, pathto, Z_STRVAL_P(zval_files), Z_STRLEN_P(zval_files))) { + RETURN_FALSE; + } + break; + case IS_ARRAY: + nelems = zend_hash_num_elements(Z_ARRVAL_P(zval_files)); + if (nelems == 0 ) { + RETURN_FALSE; + } + for (i = 0; i < nelems; i++) { + if ((zval_file = zend_hash_index_find(Z_ARRVAL_P(zval_files), i)) != NULL) { + switch (Z_TYPE_P(zval_file)) { + case IS_LONG: + break; + case IS_STRING: + if (!php_zip_extract_file(intern, pathto, Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file))) { + RETURN_FALSE; + } + break; + } + } + } + break; + case IS_LONG: + default: + php_error_docref(NULL, E_WARNING, "Invalid argument, expect string or array of strings"); + break; + } + } else { + /* Extract all files */ + int filecount = zip_get_num_files(intern); + + if (filecount == -1) { + php_error_docref(NULL, E_WARNING, "Illegal archive"); + RETURN_FALSE; + } + + for (i = 0; i < filecount; i++) { + char *file = (char*)zip_get_name(intern, i, ZIP_FL_UNCHANGED); + if (!php_zip_extract_file(intern, pathto, file, strlen(file))) { + RETURN_FALSE; + } + } + } + RETURN_TRUE; +} +/* }}} */ + +static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ +{ + struct zip *intern; + zval *self = getThis(); + + struct zip_stat sb; + struct zip_file *zf; + + zend_long index = -1; + zend_long flags = 0; + zend_long len = 0; + + zend_string *filename; + zend_string *buffer; + + int n = 0; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (type == 1) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|ll", &filename, &len, &flags) == FAILURE) { + return; + } + PHP_ZIP_STAT_PATH(intern, filename->val, filename->len, flags, sb); + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ll", &index, &len, &flags) == FAILURE) { + return; + } + PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + } + + if (sb.size < 1) { + RETURN_EMPTY_STRING(); + } + + if (len < 1) { + len = sb.size; + } + if (index >= 0) { + zf = zip_fopen_index(intern, index, flags); + } else { + zf = zip_fopen(intern, filename->val, flags); + } + + if (zf == NULL) { + RETURN_FALSE; + } + + buffer = zend_string_alloc(len, 0); + n = zip_fread(zf, buffer->val, buffer->len); + if (n < 1) { + zend_string_free(buffer); + RETURN_EMPTY_STRING(); + } + + zip_fclose(zf); + buffer->val[n] = '\0'; + buffer->len = n; + RETURN_NEW_STR(buffer); +} +/* }}} */ + +/* {{{ proto string ZipArchive::getFromName(string entryname[, int len [, int flags]]) +get the contents of an entry using its name */ +static ZIPARCHIVE_METHOD(getFromName) +{ + php_zip_get_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto string ZipArchive::getFromIndex(int index[, int len [, int flags]]) +get the contents of an entry using its index */ +static ZIPARCHIVE_METHOD(getFromIndex) +{ + php_zip_get_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto resource ZipArchive::getStream(string entryname) +get a stream for an entry using its name */ +static ZIPARCHIVE_METHOD(getStream) +{ + struct zip *intern; + zval *self = getThis(); + struct zip_stat sb; + char *mode = "rb"; + zend_string *filename; + php_stream *stream; + ze_zip_object *obj; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &filename) == FAILURE) { + return; + } + + if (zip_stat(intern, filename->val, 0, &sb) != 0) { + RETURN_FALSE; + } + + obj = Z_ZIP_P(self); + + stream = php_stream_zip_open(obj->filename, filename->val, mode STREAMS_CC); + if (stream) { + php_stream_to_zval(stream, return_value); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_open, 0, 0, 1) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setpassword, 0, 0, 1) + ZEND_ARG_INFO(0, password) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_ziparchive__void, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_addemptydir, 0, 0, 1) + ZEND_ARG_INFO(0, dirname) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_addglob, 0, 0, 1) + ZEND_ARG_INFO(0, pattern) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_addpattern, 0, 0, 1) + ZEND_ARG_INFO(0, pattern) + ZEND_ARG_INFO(0, path) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_addfile, 0, 0, 1) + ZEND_ARG_INFO(0, filepath) + ZEND_ARG_INFO(0, entryname) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, length) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_addfromstring, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, content) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_statname, 0, 0, 1) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_statindex, 0, 0, 1) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setarchivecomment, 0, 0, 1) + ZEND_ARG_INFO(0, comment) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setcommentindex, 0, 0, 2) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, comment) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getcommentname, 0, 0, 1) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getcommentindex, 0, 0, 1) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_renameindex, 0, 0, 2) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, new_name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_renamename, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, new_name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_unchangeindex, 0, 0, 1) + ZEND_ARG_INFO(0, index) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_unchangename, 0, 0, 1) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_extractto, 0, 0, 1) + ZEND_ARG_INFO(0, pathto) + ZEND_ARG_INFO(0, files) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getfromname, 0, 0, 1) + ZEND_ARG_INFO(0, entryname) + ZEND_ARG_INFO(0, len) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getfromindex, 0, 0, 1) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, len) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getarchivecomment, 0, 0, 0) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setcommentname, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, comment) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getstream, 0, 0, 1) + ZEND_ARG_INFO(0, entryname) +ZEND_END_ARG_INFO() + +#ifdef ZIP_OPSYS_DEFAULT +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setextattrname, 0, 0, 3) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, opsys) + ZEND_ARG_INFO(0, attr) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setextattrindex, 0, 0, 3) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, opsys) + ZEND_ARG_INFO(0, attr) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getextattrname, 0, 0, 3) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(1, opsys) + ZEND_ARG_INFO(1, attr) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_getextattrindex, 0, 0, 3) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(1, opsys) + ZEND_ARG_INFO(1, attr) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() +#endif /* ifdef ZIP_OPSYS_DEFAULT */ +/* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setcompname, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, compflags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ziparchive_setcompindex, 0, 0, 2) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, compflags) +ZEND_END_ARG_INFO() + +/* {{{ ze_zip_object_class_functions */ +static const zend_function_entry zip_class_functions[] = { + ZIPARCHIVE_ME(open, arginfo_ziparchive_open, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setPassword, arginfo_ziparchive_setpassword, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(close, arginfo_ziparchive__void, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getStatusString, arginfo_ziparchive__void, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(addEmptyDir, arginfo_ziparchive_addemptydir, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(addFromString, arginfo_ziparchive_addfromstring, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(addFile, arginfo_ziparchive_addfile, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(addGlob, arginfo_ziparchive_addglob, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(addPattern, arginfo_ziparchive_addpattern, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(renameIndex, arginfo_ziparchive_renameindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(renameName, arginfo_ziparchive_renamename, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setArchiveComment, arginfo_ziparchive_setarchivecomment, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getArchiveComment, arginfo_ziparchive_getarchivecomment, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setCommentIndex, arginfo_ziparchive_setcommentindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setCommentName, arginfo_ziparchive_setcommentname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getCommentIndex, arginfo_ziparchive_getcommentindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getCommentName, arginfo_ziparchive_getcommentname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(deleteIndex, arginfo_ziparchive_unchangeindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(deleteName, arginfo_ziparchive_unchangename, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(statName, arginfo_ziparchive_statname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(statIndex, arginfo_ziparchive_statindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(locateName, arginfo_ziparchive_statname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getNameIndex, arginfo_ziparchive_statindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(unchangeArchive, arginfo_ziparchive__void, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(unchangeAll, arginfo_ziparchive__void, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(unchangeIndex, arginfo_ziparchive_unchangeindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(unchangeName, arginfo_ziparchive_unchangename, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(extractTo, arginfo_ziparchive_extractto, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getFromName, arginfo_ziparchive_getfromname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getFromIndex, arginfo_ziparchive_getfromindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getStream, arginfo_ziparchive_getstream, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setExternalAttributesName, arginfo_ziparchive_setextattrname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setExternalAttributesIndex, arginfo_ziparchive_setextattrindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getExternalAttributesName, arginfo_ziparchive_getextattrname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(getExternalAttributesIndex, arginfo_ziparchive_getextattrindex, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setCompressionName, arginfo_ziparchive_setcompname, ZEND_ACC_PUBLIC) + ZIPARCHIVE_ME(setCompressionIndex, arginfo_ziparchive_setcompindex, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; +/* }}} */ + +static void php_zip_free_prop_handler(zval *el) /* {{{ */ { + pefree(Z_PTR_P(el), 1); +} /* }}} */ + +/* {{{ PHP_MINIT_FUNCTION */ +static PHP_MINIT_FUNCTION(zip) +{ + zend_class_entry ce; + + memcpy(&zip_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + zip_object_handlers.offset = XtOffsetOf(ze_zip_object, zo); + zip_object_handlers.free_obj = php_zip_object_free_storage; + zip_object_handlers.clone_obj = NULL; + zip_object_handlers.get_property_ptr_ptr = php_zip_get_property_ptr_ptr; + + zip_object_handlers.get_properties = php_zip_get_properties; + zip_object_handlers.read_property = php_zip_read_property; + zip_object_handlers.has_property = php_zip_has_property; + + INIT_CLASS_ENTRY(ce, "ZipArchive", zip_class_functions); + ce.create_object = php_zip_object_new; + zip_class_entry = zend_register_internal_class(&ce); + + zend_hash_init(&zip_prop_handlers, 0, NULL, php_zip_free_prop_handler, 1); + php_zip_register_prop_handler(&zip_prop_handlers, "status", php_zip_status, NULL, NULL, IS_LONG); + php_zip_register_prop_handler(&zip_prop_handlers, "statusSys", php_zip_status_sys, NULL, NULL, IS_LONG); + php_zip_register_prop_handler(&zip_prop_handlers, "numFiles", php_zip_get_num_files, NULL, NULL, IS_LONG); + php_zip_register_prop_handler(&zip_prop_handlers, "filename", NULL, NULL, php_zipobj_get_filename, IS_STRING); + php_zip_register_prop_handler(&zip_prop_handlers, "comment", NULL, php_zipobj_get_zip_comment, NULL, IS_STRING); + + REGISTER_ZIP_CLASS_CONST_LONG("CREATE", ZIP_CREATE); + REGISTER_ZIP_CLASS_CONST_LONG("EXCL", ZIP_EXCL); + REGISTER_ZIP_CLASS_CONST_LONG("CHECKCONS", ZIP_CHECKCONS); + REGISTER_ZIP_CLASS_CONST_LONG("OVERWRITE", ZIP_OVERWRITE); + + REGISTER_ZIP_CLASS_CONST_LONG("FL_NOCASE", ZIP_FL_NOCASE); + REGISTER_ZIP_CLASS_CONST_LONG("FL_NODIR", ZIP_FL_NODIR); + REGISTER_ZIP_CLASS_CONST_LONG("FL_COMPRESSED", ZIP_FL_COMPRESSED); + REGISTER_ZIP_CLASS_CONST_LONG("FL_UNCHANGED", ZIP_FL_UNCHANGED); + REGISTER_ZIP_CLASS_CONST_LONG("CM_DEFAULT", ZIP_CM_DEFAULT); + REGISTER_ZIP_CLASS_CONST_LONG("CM_STORE", ZIP_CM_STORE); + REGISTER_ZIP_CLASS_CONST_LONG("CM_SHRINK", ZIP_CM_SHRINK); + REGISTER_ZIP_CLASS_CONST_LONG("CM_REDUCE_1", ZIP_CM_REDUCE_1); + REGISTER_ZIP_CLASS_CONST_LONG("CM_REDUCE_2", ZIP_CM_REDUCE_2); + REGISTER_ZIP_CLASS_CONST_LONG("CM_REDUCE_3", ZIP_CM_REDUCE_3); + REGISTER_ZIP_CLASS_CONST_LONG("CM_REDUCE_4", ZIP_CM_REDUCE_4); + REGISTER_ZIP_CLASS_CONST_LONG("CM_IMPLODE", ZIP_CM_IMPLODE); + REGISTER_ZIP_CLASS_CONST_LONG("CM_DEFLATE", ZIP_CM_DEFLATE); + REGISTER_ZIP_CLASS_CONST_LONG("CM_DEFLATE64", ZIP_CM_DEFLATE64); + REGISTER_ZIP_CLASS_CONST_LONG("CM_PKWARE_IMPLODE", ZIP_CM_PKWARE_IMPLODE); + REGISTER_ZIP_CLASS_CONST_LONG("CM_BZIP2", ZIP_CM_BZIP2); + REGISTER_ZIP_CLASS_CONST_LONG("CM_LZMA", ZIP_CM_LZMA); + REGISTER_ZIP_CLASS_CONST_LONG("CM_TERSE", ZIP_CM_TERSE); + REGISTER_ZIP_CLASS_CONST_LONG("CM_LZ77", ZIP_CM_LZ77); + REGISTER_ZIP_CLASS_CONST_LONG("CM_WAVPACK", ZIP_CM_WAVPACK); + REGISTER_ZIP_CLASS_CONST_LONG("CM_PPMD", ZIP_CM_PPMD); + + /* Error code */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_OK", ZIP_ER_OK); /* N No error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_MULTIDISK", ZIP_ER_MULTIDISK); /* N Multi-disk zip archives not supported */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_RENAME", ZIP_ER_RENAME); /* S Renaming temporary file failed */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_CLOSE", ZIP_ER_CLOSE); /* S Closing zip archive failed */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_SEEK", ZIP_ER_SEEK); /* S Seek error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_READ", ZIP_ER_READ); /* S Read error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_WRITE", ZIP_ER_WRITE); /* S Write error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_CRC", ZIP_ER_CRC); /* N CRC error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_ZIPCLOSED", ZIP_ER_ZIPCLOSED); /* N Containing zip archive was closed */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_NOENT", ZIP_ER_NOENT); /* N No such file */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_EXISTS", ZIP_ER_EXISTS); /* N File already exists */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_OPEN", ZIP_ER_OPEN); /* S Can't open file */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_TMPOPEN", ZIP_ER_TMPOPEN); /* S Failure to create temporary file */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_ZLIB", ZIP_ER_ZLIB); /* Z Zlib error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_MEMORY", ZIP_ER_MEMORY); /* N Malloc failure */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_CHANGED", ZIP_ER_CHANGED); /* N Entry has been changed */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_COMPNOTSUPP", ZIP_ER_COMPNOTSUPP);/* N Compression method not supported */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_EOF", ZIP_ER_EOF); /* N Premature EOF */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_INVAL", ZIP_ER_INVAL); /* N Invalid argument */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_NOZIP", ZIP_ER_NOZIP); /* N Not a zip archive */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_INTERNAL", ZIP_ER_INTERNAL); /* N Internal error */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_INCONS", ZIP_ER_INCONS); /* N Zip archive inconsistent */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_REMOVE", ZIP_ER_REMOVE); /* S Can't remove file */ + REGISTER_ZIP_CLASS_CONST_LONG("ER_DELETED", ZIP_ER_DELETED); /* N Entry has been deleted */ + +#ifdef ZIP_OPSYS_DEFAULT + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_DOS", ZIP_OPSYS_DOS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_AMIGA", ZIP_OPSYS_AMIGA); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_OPENVMS", ZIP_OPSYS_OPENVMS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_UNIX", ZIP_OPSYS_UNIX); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_VM_CMS", ZIP_OPSYS_VM_CMS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_ATARI_ST", ZIP_OPSYS_ATARI_ST); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_OS_2", ZIP_OPSYS_OS_2); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_MACINTOSH", ZIP_OPSYS_MACINTOSH); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_Z_SYSTEM", ZIP_OPSYS_Z_SYSTEM); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_Z_CPM", ZIP_OPSYS_CPM); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_WINDOWS_NTFS", ZIP_OPSYS_WINDOWS_NTFS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_MVS", ZIP_OPSYS_MVS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_VSE", ZIP_OPSYS_VSE); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_ACORN_RISC", ZIP_OPSYS_ACORN_RISC); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_VFAT", ZIP_OPSYS_VFAT); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_ALTERNATE_MVS", ZIP_OPSYS_ALTERNATE_MVS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_BEOS", ZIP_OPSYS_BEOS); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_TANDEM", ZIP_OPSYS_TANDEM); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_OS_400", ZIP_OPSYS_OS_400); + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_OS_X", ZIP_OPSYS_OS_X); + + REGISTER_ZIP_CLASS_CONST_LONG("OPSYS_DEFAULT", ZIP_OPSYS_DEFAULT); +#endif /* ifdef ZIP_OPSYS_DEFAULT */ + + php_register_url_stream_wrapper("zip", &php_stream_zip_wrapper); + + le_zip_dir = zend_register_list_destructors_ex(php_zip_free_dir, NULL, le_zip_dir_name, module_number); + le_zip_entry = zend_register_list_destructors_ex(php_zip_free_entry, NULL, le_zip_entry_name, module_number); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +static PHP_MSHUTDOWN_FUNCTION(zip) +{ + zend_hash_destroy(&zip_prop_handlers); + php_unregister_url_stream_wrapper("zip"); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +static PHP_MINFO_FUNCTION(zip) +{ + php_info_print_table_start(); + + php_info_print_table_row(2, "Zip", "enabled"); + php_info_print_table_row(2, "Extension Version","$Id$"); + php_info_print_table_row(2, "Zip version", PHP_ZIP_VERSION); + php_info_print_table_row(2, "Libzip version", LIBZIP_VERSION); + + php_info_print_table_end(); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/c4ee76_2a1218_ext#ftp#ftp.c b/src/main/resource/testFiles/php-src/revFiles/c4ee76_2a1218_ext#ftp#ftp.c new file mode 100644 index 0000000..563570c --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/c4ee76_2a1218_ext#ftp#ftp.c @@ -0,0 +1,1923 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2005 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andrew Skalski | + | Stefan Esser (resume functions) | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" + +#if HAVE_FTP + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#ifdef PHP_WIN32 +#include +#elif defined(NETWARE) +#ifdef USE_WINSOCK /* Modified to use Winsock (NOVSOCK2.H), atleast for now */ +#include +#else +#include +#include +#include +#endif +#else +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#include +#include +#include +#endif +#include + +#if HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#if HAVE_OPENSSL_EXT +#include +#endif + +#include "ftp.h" +#include "ext/standard/fsock.h" + +/* Additional headers for NetWare */ +#if defined(NETWARE) && !defined(USE_WINSOCK) +#include +#endif + +/* sends an ftp command, returns true on success, false on error. + * it sends the string "cmd args\r\n" if args is non-null, or + * "cmd\r\n" if args is null + */ +static int ftp_putcmd( ftpbuf_t *ftp, + const char *cmd, + const char *args); + +/* wrapper around send/recv to handle timeouts */ +static int my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len); +static int my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len); +static int my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen); + +/* reads a line the socket , returns true on success, false on error */ +static int ftp_readline(ftpbuf_t *ftp); + +/* reads an ftp response, returns true on success, false on error */ +static int ftp_getresp(ftpbuf_t *ftp); + +/* sets the ftp transfer type */ +static int ftp_type(ftpbuf_t *ftp, ftptype_t type); + +/* opens up a data stream */ +static databuf_t* ftp_getdata(ftpbuf_t *ftp TSRMLS_DC); + +/* accepts the data connection, returns updated data buffer */ +static databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC); + +/* closes the data connection, returns NULL */ +static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data); + +/* generic file lister */ +static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC); + +/* IP and port conversion box */ +union ipbox { + struct in_addr ia[2]; + unsigned short s[4]; + unsigned char c[8]; +}; + +/* {{{ ftp_open + */ +ftpbuf_t* +ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC) +{ + ftpbuf_t *ftp; + socklen_t size; + struct timeval tv; + + + /* alloc the ftp structure */ + ftp = ecalloc(1, sizeof(*ftp)); + + tv.tv_sec = timeout_sec; + tv.tv_usec = 0; + + ftp->fd = php_network_connect_socket_to_host(host, + (unsigned short) (port ? port : 21), SOCK_STREAM, + 0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC); + if (ftp->fd == -1) { + goto bail; + } + + /* Default Settings */ + ftp->timeout_sec = timeout_sec; + ftp->nb = 0; + + size = sizeof(ftp->localaddr); + memset(&ftp->localaddr, 0, size); + if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + if (!ftp_getresp(ftp) || ftp->resp != 220) { + goto bail; + } + + return ftp; + +bail: + if (ftp->fd != -1) { + closesocket(ftp->fd); + } + efree(ftp); + return NULL; +} +/* }}} */ + +/* {{{ ftp_close + */ +ftpbuf_t* +ftp_close(ftpbuf_t *ftp) +{ + if (ftp == NULL) { + return NULL; + } + if (ftp->data) { + data_close(ftp, ftp->data); + } + if (ftp->fd != -1) { +#if HAVE_OPENSSL_EXT + if (ftp->ssl_active) { + SSL_shutdown(ftp->ssl_handle); + } +#endif + closesocket(ftp->fd); + } + ftp_gc(ftp); + efree(ftp); + return NULL; +} +/* }}} */ + +/* {{{ ftp_gc + */ +void +ftp_gc(ftpbuf_t *ftp) +{ + if (ftp == NULL) { + return; + } + if (ftp->pwd) { + efree(ftp->pwd); + ftp->pwd = NULL; + } + if (ftp->syst) { + efree(ftp->syst); + ftp->syst = NULL; + } +} +/* }}} */ + +/* {{{ ftp_quit + */ +int +ftp_quit(ftpbuf_t *ftp) +{ + if (ftp == NULL) { + return 0; + } + + if (!ftp_putcmd(ftp, "QUIT", NULL)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 221) { + return 0; + } + + if (ftp->pwd) { + efree(ftp->pwd); + ftp->pwd = NULL; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_login + */ +int +ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC) +{ +#if HAVE_OPENSSL_EXT + SSL_CTX *ctx = NULL; +#endif + if (ftp == NULL) { + return 0; + } + +#if HAVE_OPENSSL_EXT + if (ftp->use_ssl && !ftp->ssl_active) { + if (!ftp_putcmd(ftp, "AUTH", "TLS")) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + + if (ftp->resp != 234) { + if (!ftp_putcmd(ftp, "AUTH", "SSL")) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + + if (ftp->resp != 334) { + ftp->use_ssl = 0; + } else { + ftp->old_ssl = 1; + ftp->use_ssl_for_data = 1; + } + } + + /* now enable ssl if we still need to */ + if (ftp->use_ssl) { + ctx = SSL_CTX_new(SSLv23_client_method()); + if (ctx == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL context"); + return 0; + } + + SSL_CTX_set_options(ctx, SSL_OP_ALL); + + ftp->ssl_handle = SSL_new(ctx); + if (ftp->ssl_handle == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL handle"); + SSL_CTX_free(ctx); + return 0; + } + + SSL_set_fd(ftp->ssl_handle, ftp->fd); + + if (SSL_connect(ftp->ssl_handle) <= 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed"); + SSL_shutdown(ftp->ssl_handle); + return 0; + } + + ftp->ssl_active = 1; + + if (!ftp->old_ssl) { + + /* set protection buffersize to zero */ + if (!ftp_putcmd(ftp, "PBSZ", "0")) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + + /* enable data conn encryption */ + if (!ftp_putcmd(ftp, "PROT", "P")) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + + ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299); + } + } + } +#endif + + if (!ftp_putcmd(ftp, "USER", user)) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + if (ftp->resp == 230) { + return 1; + } + if (ftp->resp != 331) { + return 0; + } + if (!ftp_putcmd(ftp, "PASS", pass)) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + return (ftp->resp == 230); +} +/* }}} */ + +/* {{{ ftp_reinit + */ +int +ftp_reinit(ftpbuf_t *ftp) +{ + if (ftp == NULL) { + return 0; + } + + ftp_gc(ftp); + + ftp->nb = 0; + + if (!ftp_putcmd(ftp, "REIN", NULL)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 220) { + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_syst + */ +const char* +ftp_syst(ftpbuf_t *ftp) +{ + char *syst, *end; + + if (ftp == NULL) { + return NULL; + } + + /* default to cached value */ + if (ftp->syst) { + return ftp->syst; + } + if (!ftp_putcmd(ftp, "SYST", NULL)) { + return NULL; + } + if (!ftp_getresp(ftp) || ftp->resp != 215) { + return NULL; + } + syst = ftp->inbuf; + while (*syst == ' ') { + syst++; + } + if ((end = strchr(syst, ' '))) { + *end = 0; + } + ftp->syst = estrdup(syst); + if (end) { + *end = ' '; + } + return ftp->syst; +} +/* }}} */ + +/* {{{ ftp_pwd + */ +const char* +ftp_pwd(ftpbuf_t *ftp) +{ + char *pwd, *end; + + if (ftp == NULL) { + return NULL; + } + + /* default to cached value */ + if (ftp->pwd) { + return ftp->pwd; + } + if (!ftp_putcmd(ftp, "PWD", NULL)) { + return NULL; + } + if (!ftp_getresp(ftp) || ftp->resp != 257) { + return NULL; + } + /* copy out the pwd from response */ + if ((pwd = strchr(ftp->inbuf, '"')) == NULL) { + return NULL; + } + if ((end = strrchr(++pwd, '"')) == NULL) { + return NULL; + } + ftp->pwd = estrndup(pwd, end - pwd); + + return ftp->pwd; +} +/* }}} */ + +/* {{{ ftp_exec + */ +int +ftp_exec(ftpbuf_t *ftp, const char *cmd) +{ + if (ftp == NULL) { + return 0; + } + if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 200) { + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_raw + */ +void +ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value) +{ + if (ftp == NULL || cmd == NULL) { + RETURN_NULL(); + } + if (!ftp_putcmd(ftp, cmd, NULL)) { + RETURN_NULL(); + } + array_init(return_value); + while (ftp_readline(ftp)) { + add_next_index_string(return_value, ftp->inbuf, 1); + if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') { + return; + } + } +} +/* }}} */ + +/* {{{ ftp_chdir + */ +int +ftp_chdir(ftpbuf_t *ftp, const char *dir) +{ + if (ftp == NULL) { + return 0; + } + + if (ftp->pwd) { + efree(ftp->pwd); + ftp->pwd = NULL; + } + + if (!ftp_putcmd(ftp, "CWD", dir)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 250) { + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ ftp_cdup + */ +int +ftp_cdup(ftpbuf_t *ftp) +{ + if (ftp == NULL) { + return 0; + } + + if (ftp->pwd) { + efree(ftp->pwd); + ftp->pwd = NULL; + } + + if (!ftp_putcmd(ftp, "CDUP", NULL)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 250) { + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ ftp_mkdir + */ +char* +ftp_mkdir(ftpbuf_t *ftp, const char *dir) +{ + char *mkd, *end; + + if (ftp == NULL) { + return NULL; + } + if (!ftp_putcmd(ftp, "MKD", dir)) { + return NULL; + } + if (!ftp_getresp(ftp) || ftp->resp != 257) { + return NULL; + } + /* copy out the dir from response */ + if ((mkd = strchr(ftp->inbuf, '"')) == NULL) { + mkd = estrdup(dir); + return mkd; + } + if ((end = strrchr(++mkd, '"')) == NULL) { + return NULL; + } + *end = 0; + mkd = estrdup(mkd); + *end = '"'; + + return mkd; +} +/* }}} */ + +/* {{{ ftp_rmdir + */ +int +ftp_rmdir(ftpbuf_t *ftp, const char *dir) +{ + if (ftp == NULL) { + return 0; + } + if (!ftp_putcmd(ftp, "RMD", dir)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 250) { + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ ftp_chmod + */ +int +ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len) +{ + char *buffer; + + if (ftp == NULL || filename_len <= 0) { + return 0; + } + + if (!(buffer = emalloc(32 + filename_len + 1))) { + return 0; + } + + sprintf(buffer, "CHMOD %o %s", mode, filename); + + if (!ftp_putcmd(ftp, "SITE", buffer)) { + efree(buffer); + return 0; + } + + efree(buffer); + + if (!ftp_getresp(ftp) || ftp->resp != 200) { + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_alloc + */ +int +ftp_alloc(ftpbuf_t *ftp, const int size, char **response) +{ + char buffer[64]; + + if (ftp == NULL || size <= 0) { + return 0; + } + + snprintf(buffer, sizeof(buffer) - 1, "%d", size); + + if (!ftp_putcmd(ftp, "ALLO", buffer)) { + return 0; + } + + if (!ftp_getresp(ftp)) { + return 0; + } + + if (response && ftp->inbuf) { + *response = estrdup(ftp->inbuf); + } + + if (ftp->resp < 200 || ftp->resp >= 300) { + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_nlist + */ +char** +ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC) +{ + return ftp_genlist(ftp, "NLST", path TSRMLS_CC); +} +/* }}} */ + +/* {{{ ftp_list + */ +char** +ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC) +{ + return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC); +} +/* }}} */ + +/* {{{ ftp_type + */ +int +ftp_type(ftpbuf_t *ftp, ftptype_t type) +{ + char typechar[2] = "?"; + + if (ftp == NULL) { + return 0; + } + if (type == ftp->type) { + return 1; + } + if (type == FTPTYPE_ASCII) { + typechar[0] = 'A'; + } else if (type == FTPTYPE_IMAGE) { + typechar[0] = 'I'; + } else { + return 0; + } + if (!ftp_putcmd(ftp, "TYPE", typechar)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 200) { + return 0; + } + ftp->type = type; + + return 1; +} +/* }}} */ + +/* {{{ ftp_pasv + */ +int +ftp_pasv(ftpbuf_t *ftp, int pasv) +{ + char *ptr; + union ipbox ipbox; + unsigned long b[6]; + socklen_t n; + struct sockaddr *sa; + struct sockaddr_in *sin; + + if (ftp == NULL) { + return 0; + } + if (pasv && ftp->pasv == 2) { + return 1; + } + ftp->pasv = 0; + if (!pasv) { + return 1; + } + n = sizeof(ftp->pasvaddr); + memset(&ftp->pasvaddr, 0, n); + sa = (struct sockaddr *) &ftp->pasvaddr; + +#if HAVE_IPV6 + if (getpeername(ftp->fd, sa, &n) < 0) { + return 0; + } + if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; + char *endptr, delimiter; + + /* try EPSV first */ + if (!ftp_putcmd(ftp, "EPSV", NULL)) { + return 0; + } + if (!ftp_getresp(ftp)) { + return 0; + } + if (ftp->resp == 229) { + /* parse out the port */ + for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++); + if (!*ptr) { + return 0; + } + delimiter = *++ptr; + for (n = 0; *ptr && n < 3; ptr++) { + if (*ptr == delimiter) { + n++; + } + } + + sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10)); + if (ptr == endptr || *endptr != delimiter) { + return 0; + } + ftp->pasv = 2; + return 1; + } + } + + /* fall back to PASV */ +#endif + + if (!ftp_putcmd(ftp, "PASV", NULL)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 227) { + return 0; + } + /* parse out the IP and port */ + for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++); + n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]); + if (n != 6) { + return 0; + } + for (n = 0; n < 6; n++) { + ipbox.c[n] = (unsigned char) b[n]; + } + sin = (struct sockaddr_in *) sa; + sin->sin_family = AF_INET; + sin->sin_addr = ipbox.ia[0]; + sin->sin_port = ipbox.s[2]; + + ftp->pasv = 2; + + return 1; +} +/* }}} */ + +/* {{{ ftp_get + */ +int +ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC) +{ + databuf_t *data = NULL; + int lastch; + size_t rcvd; + char arg[11]; + + if (ftp == NULL) { + return 0; + } + if (!ftp_type(ftp, type)) { + goto bail; + } + + if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) { + goto bail; + } + + ftp->data = data; + + if (resumepos > 0) { + if (resumepos > 2147483647) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater then 2147483647 bytes."); + goto bail; + } + sprintf(arg, "%u", resumepos); + if (!ftp_putcmd(ftp, "REST", arg)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 350)) { + goto bail; + } + } + + if (!ftp_putcmd(ftp, "RETR", path)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) { + goto bail; + } + + if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { + goto bail; + } + + lastch = 0; + while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) { + if (rcvd == -1) { + goto bail; + } + + if (type == FTPTYPE_ASCII) { +#ifndef PHP_WIN32 + char *s; +#endif + char *ptr = data->buf; + char *e = ptr + rcvd; + /* logic depends on the OS EOL + * Win32 -> \r\n + * Everything Else \n + */ +#ifdef PHP_WIN32 + php_stream_write(outstream, ptr, (e - ptr)); + ptr = e; +#else + while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) { + php_stream_write(outstream, ptr, (s - ptr)); + if (*(s + 1) == '\n') { + s++; + php_stream_putc(outstream, '\n'); + } + ptr = s + 1; + } +#endif + if (ptr < e) { + php_stream_write(outstream, ptr, (e - ptr)); + } + } else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) { + goto bail; + } + } + + ftp->data = data = data_close(ftp, data); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + goto bail; + } + + return 1; +bail: + ftp->data = data_close(ftp, data); + return 0; +} +/* }}} */ + +/* {{{ ftp_put + */ +int +ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC) +{ + databuf_t *data = NULL; + int size; + char *ptr; + int ch; + char arg[11]; + + if (ftp == NULL) { + return 0; + } + if (!ftp_type(ftp, type)) { + goto bail; + } + if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) { + goto bail; + } + ftp->data = data; + + if (startpos > 0) { + if (startpos > 2147483647) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater then 2147483647 bytes."); + goto bail; + } + sprintf(arg, "%u", startpos); + if (!ftp_putcmd(ftp, "REST", arg)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 350)) { + goto bail; + } + } + + if (!ftp_putcmd(ftp, "STOR", path)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) { + goto bail; + } + if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { + goto bail; + } + + size = 0; + ptr = data->buf; + while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) { + /* flush if necessary */ + if (FTP_BUFSIZE - size < 2) { + if (my_send(ftp, data->fd, data->buf, size) != size) { + goto bail; + } + ptr = data->buf; + size = 0; + } + + if (ch == '\n' && type == FTPTYPE_ASCII) { + *ptr++ = '\r'; + size++; + } + + *ptr++ = ch; + size++; + } + + if (size && my_send(ftp, data->fd, data->buf, size) != size) { + goto bail; + } + ftp->data = data = data_close(ftp, data); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + goto bail; + } + return 1; +bail: + ftp->data = data_close(ftp, data); + return 0; +} +/* }}} */ + +/* {{{ ftp_size + */ +int +ftp_size(ftpbuf_t *ftp, const char *path) +{ + if (ftp == NULL) { + return -1; + } + if (!ftp_type(ftp, FTPTYPE_IMAGE)) { + return -1; + } + if (!ftp_putcmd(ftp, "SIZE", path)) { + return -1; + } + if (!ftp_getresp(ftp) || ftp->resp != 213) { + return -1; + } + return atoi(ftp->inbuf); +} +/* }}} */ + +/* {{{ ftp_mdtm + */ +time_t +ftp_mdtm(ftpbuf_t *ftp, const char *path) +{ + time_t stamp; + struct tm *gmt, tmbuf; + struct tm tm; + char *ptr; + int n; + + if (ftp == NULL) { + return -1; + } + if (!ftp_putcmd(ftp, "MDTM", path)) { + return -1; + } + if (!ftp_getresp(ftp) || ftp->resp != 213) { + return -1; + } + /* parse out the timestamp */ + for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++); + n = sscanf(ptr, "%4u%2u%2u%2u%2u%2u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); + if (n != 6) { + return -1; + } + tm.tm_year -= 1900; + tm.tm_mon--; + tm.tm_isdst = -1; + + /* figure out the GMT offset */ + stamp = time(NULL); + gmt = php_gmtime_r(&stamp, &tmbuf); + gmt->tm_isdst = -1; + + /* apply the GMT offset */ + tm.tm_sec += stamp - mktime(gmt); + tm.tm_isdst = gmt->tm_isdst; + + stamp = mktime(&tm); + + return stamp; +} +/* }}} */ + +/* {{{ ftp_delete + */ +int +ftp_delete(ftpbuf_t *ftp, const char *path) +{ + if (ftp == NULL) { + return 0; + } + if (!ftp_putcmd(ftp, "DELE", path)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 250) { + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ ftp_rename + */ +int +ftp_rename(ftpbuf_t *ftp, const char *src, const char *dest) +{ + if (ftp == NULL) { + return 0; + } + if (!ftp_putcmd(ftp, "RNFR", src)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 350) { + return 0; + } + if (!ftp_putcmd(ftp, "RNTO", dest)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp != 250) { + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ ftp_site + */ +int +ftp_site(ftpbuf_t *ftp, const char *cmd) +{ + if (ftp == NULL) { + return 0; + } + if (!ftp_putcmd(ftp, "SITE", cmd)) { + return 0; + } + if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) { + return 0; + } + + return 1; +} +/* }}} */ + +/* static functions */ + +/* {{{ ftp_putcmd + */ +int +ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const char *args) +{ + int size; + char *data; + + /* build the output buffer */ + if (args && args[0]) { + /* "cmd args\r\n\0" */ + if (strlen(cmd) + strlen(args) + 4 > FTP_BUFSIZE) { + return 0; + } + size = sprintf(ftp->outbuf, "%s %s\r\n", cmd, args); + } else { + /* "cmd\r\n\0" */ + if (strlen(cmd) + 3 > FTP_BUFSIZE) { + return 0; + } + size = sprintf(ftp->outbuf, "%s\r\n", cmd); + } + + data = ftp->outbuf; + + if (my_send(ftp, ftp->fd, data, size) != size) { + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ ftp_readline + */ +int +ftp_readline(ftpbuf_t *ftp) +{ + int size, rcvd; + char *data, *eol; + + /* shift the extra to the front */ + size = FTP_BUFSIZE; + rcvd = 0; + if (ftp->extra) { + memmove(ftp->inbuf, ftp->extra, ftp->extralen); + rcvd = ftp->extralen; + } + + data = ftp->inbuf; + + do { + size -= rcvd; + for (eol = data; rcvd; rcvd--, eol++) { + if (*eol == '\r') { + *eol = 0; + ftp->extra = eol + 1; + if (rcvd > 1 && *(eol + 1) == '\n') { + ftp->extra++; + rcvd--; + } + if ((ftp->extralen = --rcvd) == 0) { + ftp->extra = NULL; + } + return 1; + } else if (*eol == '\n') { + *eol = 0; + ftp->extra = eol + 1; + if ((ftp->extralen = --rcvd) == 0) { + ftp->extra = NULL; + } + return 1; + } + } + + data = eol; + if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) { + return 0; + } + } while (size); + + return 0; +} +/* }}} */ + +/* {{{ ftp_getresp + */ +int +ftp_getresp(ftpbuf_t *ftp) +{ + char *buf; + + if (ftp == NULL) { + return 0; + } + buf = ftp->inbuf; + ftp->resp = 0; + + while (1) { + + if (!ftp_readline(ftp)) { + return 0; + } + + /* Break out when the end-tag is found */ + if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') { + break; + } + } + + /* translate the tag */ + if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) { + return 0; + } + + ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0'); + + memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4); + + if (ftp->extra) { + ftp->extra -= 4; + } + return 1; +} +/* }}} */ + +/* {{{ my_send + */ +int +my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len) +{ + int n, size, sent; + + size = len; + while (size) { + n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000); + + if (n < 1) { + +#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK)) + if (n == 0) { + errno = ETIMEDOUT; + } +#endif + return -1; + } + +#if HAVE_OPENSSL_EXT + if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) { + sent = SSL_write(ftp->ssl_handle, buf, size); + } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) { + sent = SSL_write(ftp->data->ssl_handle, buf, size); + } else { +#endif + sent = send(s, buf, size, 0); +#if HAVE_OPENSSL_EXT + } +#endif + if (sent == -1) { + return -1; + } + + buf = (char*) buf + sent; + size -= sent; + } + + return len; +} +/* }}} */ + +/* {{{ my_recv + */ +int +my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len) +{ + int n, nr_bytes; + + n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000); + if (n < 1) { +#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK)) + if (n == 0) { + errno = ETIMEDOUT; + } +#endif + return -1; + } + +#if HAVE_OPENSSL_EXT + if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) { + nr_bytes = SSL_read(ftp->ssl_handle, buf, len); + } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) { + nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len); + } else { +#endif + nr_bytes = recv(s, buf, len, 0); +#if HAVE_OPENSSL_EXT + } +#endif + return (nr_bytes); +} +/* }}} */ + +/* {{{ data_available + */ +int +data_available(ftpbuf_t *ftp, php_socket_t s) +{ + int n; + + n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000); + if (n < 1) { +#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK)) + if (n == 0) { + errno = ETIMEDOUT; + } +#endif + return 0; + } + + return 1; +} +/* }}} */ +/* {{{ data_writeable + */ +int +data_writeable(ftpbuf_t *ftp, php_socket_t s) +{ + int n; + + n = php_pollfd_for_ms(s, POLLOUT, 1000); + if (n < 1) { +#ifndef PHP_WIN32 + if (n == 0) { + errno = ETIMEDOUT; + } +#endif + return 0; + } + + return 1; +} +/* }}} */ + +/* {{{ my_accept + */ +int +my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen) +{ + int n; + + n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000); + if (n < 1) { +#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK)) + if (n == 0) { + errno = ETIMEDOUT; + } +#endif + return -1; + } + + return accept(s, addr, addrlen); +} +/* }}} */ + +/* {{{ ftp_getdata + */ +databuf_t* +ftp_getdata(ftpbuf_t *ftp TSRMLS_DC) +{ + int fd = -1; + databuf_t *data; + php_sockaddr_storage addr; + struct sockaddr *sa; + socklen_t size; + union ipbox ipbox; + char arg[sizeof("255, 255, 255, 255, 255, 255")]; + struct timeval tv; + + + /* ask for a passive connection if we need one */ + if (ftp->pasv && !ftp_pasv(ftp, 1)) { + return NULL; + } + /* alloc the data structure */ + data = ecalloc(1, sizeof(*data)); + data->listener = -1; + data->fd = -1; + data->type = ftp->type; + + sa = (struct sockaddr *) &ftp->localaddr; + /* bind/listen */ + if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + /* passive connection handler */ + if (ftp->pasv) { + /* clear the ready status */ + ftp->pasv = 1; + + /* connect */ + /* Win 95/98 seems not to like size > sizeof(sockaddr_in) */ + size = php_sockaddr_size(&ftp->pasvaddr); + tv.tv_sec = ftp->timeout_sec; + tv.tv_usec = 0; + if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + data->fd = fd; + + ftp->data = data; + return data; + } + + + /* active (normal) connection */ + + /* bind to a local address */ + php_any_addr(sa->sa_family, &addr, 0); + size = php_sockaddr_size(&addr); + + if (bind(fd, (struct sockaddr*) &addr, size) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + if (getsockname(fd, (struct sockaddr*) &addr, &size) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + if (listen(fd, 5) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno); + goto bail; + } + + data->listener = fd; + +#if HAVE_IPV6 + if (sa->sa_family == AF_INET6) { + /* need to use EPRT */ + char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")]; + char out[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out)); + sprintf(eprtarg, "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port)); + + if (!ftp_putcmd(ftp, "EPRT", eprtarg)) { + goto bail; + } + + if (!ftp_getresp(ftp) || ftp->resp != 200) { + goto bail; + } + + ftp->data = data; + return data; + } +#endif + + /* send the PORT */ + ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr; + ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port; + sprintf(arg, "%u,%u,%u,%u,%u,%u", ipbox.c[0], ipbox.c[1], ipbox.c[2], ipbox.c[3], ipbox.c[4], ipbox.c[5]); + + if (!ftp_putcmd(ftp, "PORT", arg)) { + goto bail; + } + if (!ftp_getresp(ftp) || ftp->resp != 200) { + goto bail; + } + + ftp->data = data; + return data; + +bail: + if (fd != -1) { + closesocket(fd); + } + efree(data); + return NULL; +} +/* }}} */ + +/* {{{ data_accept + */ +databuf_t* +data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC) +{ + php_sockaddr_storage addr; + socklen_t size; + +#if HAVE_OPENSSL_EXT + SSL_CTX *ctx; +#endif + + if (data->fd != -1) { + goto data_accepted; + } + size = sizeof(addr); + data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size); + closesocket(data->listener); + data->listener = -1; + + if (data->fd == -1) { + efree(data); + return NULL; + } + +data_accepted: +#if HAVE_OPENSSL_EXT + + /* now enable ssl if we need to */ + if (ftp->use_ssl && ftp->use_ssl_for_data) { + ctx = SSL_CTX_new(SSLv23_client_method()); + if (ctx == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL context"); + return 0; + } + + SSL_CTX_set_options(ctx, SSL_OP_ALL); + + data->ssl_handle = SSL_new(ctx); + if (data->ssl_handle == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL handle"); + SSL_CTX_free(ctx); + return 0; + } + + + SSL_set_fd(data->ssl_handle, data->fd); + + if (ftp->old_ssl) { + SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle); + } + + if (SSL_connect(data->ssl_handle) <= 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed"); + SSL_shutdown(data->ssl_handle); + return 0; + } + + data->ssl_active = 1; + } + +#endif + + return data; +} +/* }}} */ + +/* {{{ data_close + */ +databuf_t* +data_close(ftpbuf_t *ftp, databuf_t *data) +{ + if (data == NULL) { + return NULL; + } + if (data->listener != -1) { +#if HAVE_OPENSSL_EXT + if (data->ssl_active) { + SSL_shutdown(data->ssl_handle); + data->ssl_active = 0; + } +#endif + closesocket(data->listener); + } + if (data->fd != -1) { +#if HAVE_OPENSSL_EXT + if (data->ssl_active) { + SSL_shutdown(data->ssl_handle); + data->ssl_active = 0; + } +#endif + closesocket(data->fd); + } + if (ftp) { + ftp->data = NULL; + } + efree(data); + return NULL; +} +/* }}} */ + +/* {{{ ftp_genlist + */ +char** +ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC) +{ + php_stream *tmpstream = NULL; + databuf_t *data = NULL; + char *ptr; + int ch, lastch; + int size, rcvd; + int lines; + char **ret = NULL; + char **entry; + char *text; + + + if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create temporary file. Check permissions in temporary files directory."); + return NULL; + } + + if (!ftp_type(ftp, FTPTYPE_ASCII)) { + goto bail; + } + + if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) { + goto bail; + } + ftp->data = data; + + if (!ftp_putcmd(ftp, cmd, path)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) { + goto bail; + } + + /* pull data buffer into tmpfile */ + if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { + goto bail; + } + size = 0; + lines = 0; + lastch = 0; + while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) { + if (rcvd == -1) { + goto bail; + } + + php_stream_write(tmpstream, data->buf, rcvd); + + size += rcvd; + for (ptr = data->buf; rcvd; rcvd--, ptr++) { + if (*ptr == '\n' && lastch == '\r') { + lines++; + } else { + size++; + } + lastch = *ptr; + } + } + + ftp->data = data = data_close(ftp, data); + + php_stream_rewind(tmpstream); + + ret = safe_emalloc((lines + 1), sizeof(char**), size * sizeof(char*)); + + entry = ret; + text = (char*) (ret + lines + 1); + *entry = text; + lastch = 0; + while ((ch = php_stream_getc(tmpstream)) != EOF) { + if (ch == '\n' && lastch == '\r') { + *(text - 1) = 0; + *++entry = text; + } else { + *text++ = ch; + } + lastch = ch; + } + *entry = NULL; + + php_stream_close(tmpstream); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + efree(ret); + return NULL; + } + + return ret; +bail: + ftp->data = data_close(ftp, data); + php_stream_close(tmpstream); + if (ret) + efree(ret); + return NULL; +} +/* }}} */ + +/* {{{ ftp_nb_get + */ +int +ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC) +{ + databuf_t *data = NULL; + char arg[11]; + + if (ftp == NULL) { + goto bail; + } + + if (!ftp_type(ftp, type)) { + goto bail; + } + + if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) { + goto bail; + } + + if (resumepos>0) { + /* We are working on an architecture that supports 64-bit integers + * since php is 32 bit by design, we bail out with warning + */ + if (resumepos > 2147483647) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater then 2147483648 bytes."); + goto bail; + } + sprintf(arg, "%u", resumepos); + if (!ftp_putcmd(ftp, "REST", arg)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 350)) { + goto bail; + } + } + + if (!ftp_putcmd(ftp, "RETR", path)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) { + goto bail; + } + + if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { + goto bail; + } + + ftp->data = data; + ftp->stream = outstream; + ftp->lastch = 0; + ftp->nb = 1; + + return (ftp_nb_continue_read(ftp TSRMLS_CC)); + +bail: + ftp->data = data_close(ftp, data); + return PHP_FTP_FAILED; +} +/* }}} */ + +/* {{{ ftp_nb_continue_read + */ +int +ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC) +{ + databuf_t *data = NULL; + char *ptr; + int lastch; + size_t rcvd; + ftptype_t type; + + data = ftp->data; + + /* check if there is already more data */ + if (!data_available(ftp, data->fd)) { + return PHP_FTP_MOREDATA; + } + + type = ftp->type; + + lastch = ftp->lastch; + if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) { + if (rcvd == -1) { + goto bail; + } + + if (type == FTPTYPE_ASCII) { + for (ptr = data->buf; rcvd; rcvd--, ptr++) { + if (lastch == '\r' && *ptr != '\n') { + php_stream_putc(ftp->stream, '\r'); + } + if (*ptr != '\r') { + php_stream_putc(ftp->stream, *ptr); + } + lastch = *ptr; + } + } else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) { + goto bail; + } + + ftp->lastch = lastch; + return PHP_FTP_MOREDATA; + } + + if (type == FTPTYPE_ASCII && lastch == '\r') { + php_stream_putc(ftp->stream, '\r'); + } + + ftp->data = data = data_close(ftp, data); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + goto bail; + } + + ftp->nb = 0; + return PHP_FTP_FINISHED; +bail: + ftp->nb = 0; + ftp->data = data_close(ftp, data); + return PHP_FTP_FAILED; +} +/* }}} */ + +/* {{{ ftp_nb_put + */ +int +ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC) +{ + databuf_t *data = NULL; + char arg[11]; + + if (ftp == NULL) { + return 0; + } + if (!ftp_type(ftp, type)) { + goto bail; + } + if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) { + goto bail; + } + if (startpos > 0) { + if (startpos > 2147483647) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater then 2147483647 bytes."); + goto bail; + } + sprintf(arg, "%u", startpos); + if (!ftp_putcmd(ftp, "REST", arg)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 350)) { + goto bail; + } + } + + if (!ftp_putcmd(ftp, "STOR", path)) { + goto bail; + } + if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) { + goto bail; + } + if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { + goto bail; + } + ftp->data = data; + ftp->stream = instream; + ftp->lastch = 0; + ftp->nb = 1; + + return (ftp_nb_continue_write(ftp TSRMLS_CC)); + +bail: + ftp->data = data_close(ftp, data); + return PHP_FTP_FAILED; +} +/* }}} */ + + +/* {{{ ftp_nb_continue_write + */ +int +ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC) +{ + int size; + char *ptr; + int ch; + + /* check if we can write more data */ + if (!data_writeable(ftp, ftp->data->fd)) { + return PHP_FTP_MOREDATA; + } + + size = 0; + ptr = ftp->data->buf; + while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) { + + if (ch == '\n' && ftp->type == FTPTYPE_ASCII) { + *ptr++ = '\r'; + size++; + } + + *ptr++ = ch; + size++; + + /* flush if necessary */ + if (FTP_BUFSIZE - size < 2) { + if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) { + goto bail; + } + return PHP_FTP_MOREDATA; + } + } + + if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) { + goto bail; + } + ftp->data = data_close(ftp, ftp->data); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + goto bail; + } + ftp->nb = 0; + return PHP_FTP_FINISHED; +bail: + ftp->data = data_close(ftp, ftp->data); + ftp->nb = 0; + return PHP_FTP_FAILED; +} +/* }}} */ + +#endif /* HAVE_FTP */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/c71358_283565_main#main.c b/src/main/resource/testFiles/php-src/revFiles/c71358_283565_main#main.c new file mode 100644 index 0000000..6017b72 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/c71358_283565_main#main.c @@ -0,0 +1,2362 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2009 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Rasmus Lerdorf | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* {{{ includes + */ + +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS + +#include "php.h" +#include +#include +#ifdef PHP_WIN32 +#include "win32/time.h" +#include "win32/signal.h" +#include "win32/php_win32_globals.h" +#include +#elif defined(NETWARE) +#include +#ifdef USE_WINSOCK +#include +#endif +#endif +#if HAVE_SYS_TIME_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SIGNAL_H +#include +#endif +#if HAVE_SETLOCALE +#include +#endif +#include "zend.h" +#include "zend_extensions.h" +#include "php_ini.h" +#include "php_globals.h" +#include "php_main.h" +#include "fopen_wrappers.h" +#include "ext/standard/php_standard.h" +#include "ext/standard/php_string.h" +#include "ext/date/php_date.h" +#include "php_variables.h" +#include "ext/standard/credits.h" +#ifdef PHP_WIN32 +#include +#include "win32/php_registry.h" +#include "ext/standard/flock_compat.h" +#endif +#include "php_syslog.h" +#include "Zend/zend_exceptions.h" + +#if PHP_SIGCHILD +#include +#include +#endif + +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_highlight.h" +#include "zend_indent.h" +#include "zend_extensions.h" +#include "zend_ini.h" + +#include "php_content_types.h" +#include "php_ticks.h" +#include "php_logos.h" +#include "php_streams.h" +#include "php_open_temporary_file.h" + +#include "SAPI.h" +#include "rfc1867.h" + +#if HAVE_SYS_MMAN_H +# include +# ifndef PAGE_SIZE +# define PAGE_SIZE 4096 +# endif +#endif +#ifdef PHP_WIN32 +# define PAGE_SIZE 4096 +#endif +/* }}} */ + +PHPAPI int (*php_register_internal_extensions_func)(TSRMLS_D) = php_register_internal_extensions; + +#ifndef ZTS +php_core_globals core_globals; +#else +PHPAPI int core_globals_id; +#endif + +#ifdef PHP_WIN32 +#include "win32_internal_function_disabled.h" + +static php_win32_disable_functions() { + int i; + TSRMLS_FETCH(); + + if (EG(windows_version_info).dwMajorVersion < 5) { + for (i = 0; i < function_name_cnt_5; i++) { + if (zend_hash_del(CG(function_table), function_name_5[i], strlen(function_name_5[i]) + 1)==FAILURE) { + php_printf("Unable to disable function '%s'\n", function_name_5[i]); + return FAILURE; + } + } + } + + if (EG(windows_version_info).dwMajorVersion < 6) { + for (i = 0; i < function_name_cnt_6; i++) { + if (zend_hash_del(CG(function_table), function_name_6[i], strlen(function_name_6[i]) + 1)==FAILURE) { + php_printf("Unable to disable function '%s'\n", function_name_6[i]); + return FAILURE; + } + } + } + return SUCCESS; +} +#endif + +#define SAFE_FILENAME(f) ((f)?(f):"-") + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnSetPrecision) +{ + int i = atoi(new_value); + if (i >= 0) { + EG(precision) = i; + return SUCCESS; + } else { + return FAILURE; + } +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnChangeMemoryLimit) +{ + if (new_value) { + PG(memory_limit) = zend_atol(new_value, new_value_length); + } else { + PG(memory_limit) = 1<<30; /* effectively, no limit */ + } + return zend_set_memory_limit(PG(memory_limit)); +} +/* }}} */ + + +/* {{{ php_disable_functions + */ +static void php_disable_functions(TSRMLS_D) +{ + char *s = NULL, *e; + + if (!*(INI_STR("disable_functions"))) { + return; + } + + e = PG(disable_functions) = strdup(INI_STR("disable_functions")); + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_disable_function(s, e-s TSRMLS_CC); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_disable_function(s, e-s TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ php_disable_classes + */ +static void php_disable_classes(TSRMLS_D) +{ + char *s = NULL, *e; + + if (!*(INI_STR("disable_classes"))) { + return; + } + + e = PG(disable_classes) = strdup(INI_STR("disable_classes")); + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_disable_class(s, e-s TSRMLS_CC); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_disable_class(s, e-s TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnUpdateTimeout) +{ + EG(timeout_seconds) = atoi(new_value); + if (stage==PHP_INI_STAGE_STARTUP) { + /* Don't set a timeout on startup, only per-request */ + return SUCCESS; + } + zend_unset_timeout(TSRMLS_C); + zend_set_timeout(EG(timeout_seconds), 0); + return SUCCESS; +} +/* }}} */ + +/* {{{ php_get_display_errors_mode() helper function + */ +static int php_get_display_errors_mode(char *value, int value_length) +{ + int mode; + + if (!value) { + return PHP_DISPLAY_ERRORS_STDOUT; + } + + if (value_length == 2 && !strcasecmp("on", value)) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else if (value_length == 3 && !strcasecmp("yes", value)) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else if (value_length == 4 && !strcasecmp("true", value)) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else if (value_length == 6 && !strcasecmp(value, "stderr")) { + mode = PHP_DISPLAY_ERRORS_STDERR; + } else if (value_length == 6 && !strcasecmp(value, "stdout")) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else { + mode = atoi(value); + if (mode && mode != PHP_DISPLAY_ERRORS_STDOUT && mode != PHP_DISPLAY_ERRORS_STDERR) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } + } + + return mode; +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnUpdateDisplayErrors) +{ + PG(display_errors) = (zend_bool) php_get_display_errors_mode(new_value, new_value_length); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_INI_DISP + */ +static PHP_INI_DISP(display_errors_mode) +{ + int mode, tmp_value_length, cgi_or_cli; + char *tmp_value; + TSRMLS_FETCH(); + + if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) { + tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL ); + tmp_value_length = ini_entry->orig_value_length; + } else if (ini_entry->value) { + tmp_value = ini_entry->value; + tmp_value_length = ini_entry->value_length; + } else { + tmp_value = NULL; + tmp_value_length = 0; + } + + mode = php_get_display_errors_mode(tmp_value, tmp_value_length); + + /* Display 'On' for other SAPIs instead of STDOUT or STDERR */ + cgi_or_cli = (!strcmp(sapi_module.name, "cli") || !strcmp(sapi_module.name, "cgi")); + + switch (mode) { + case PHP_DISPLAY_ERRORS_STDERR: + if (cgi_or_cli ) { + PUTS("STDERR"); + } else { + PUTS("On"); + } + break; + + case PHP_DISPLAY_ERRORS_STDOUT: + if (cgi_or_cli ) { + PUTS("STDOUT"); + } else { + PUTS("On"); + } + break; + + default: + PUTS("Off"); + break; + } +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnUpdateErrorLog) +{ + /* Only do the safemode/open_basedir check at runtime */ + if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && new_value && strcmp(new_value, "syslog")) { + if (PG(safe_mode) && (!php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { + return FAILURE; + } + + if (PG(open_basedir) && php_check_open_basedir(new_value TSRMLS_CC)) { + return FAILURE; + } + + } + OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnUpdateMailLog) +{ + /* Only do the safemode/open_basedir check at runtime */ + if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && new_value) { + if (PG(safe_mode) && (!php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { + return FAILURE; + } + + if (PG(open_basedir) && php_check_open_basedir(new_value TSRMLS_CC)) { + return FAILURE; + } + + } + OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnChangeMailForceExtra) +{ + /* Don't allow changing it in htaccess */ + if (stage == PHP_INI_STAGE_HTACCESS) { + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + + +/* Need to convert to strings and make use of: + * PHP_SAFE_MODE + * + * Need to be read from the environment (?): + * PHP_AUTO_PREPEND_FILE + * PHP_AUTO_APPEND_FILE + * PHP_DOCUMENT_ROOT + * PHP_USER_DIR + * PHP_INCLUDE_PATH + */ + +#ifndef PHP_SAFE_MODE_EXEC_DIR +# define PHP_SAFE_MODE_EXEC_DIR "" +#endif + + /* Windows and Netware use the internal mail */ +#if defined(PHP_WIN32) || defined(NETWARE) +# define DEFAULT_SENDMAIL_PATH NULL +#elif defined(PHP_PROG_SENDMAIL) +# define DEFAULT_SENDMAIL_PATH PHP_PROG_SENDMAIL " -t -i " +#else +# define DEFAULT_SENDMAIL_PATH "/usr/sbin/sendmail -t -i" +#endif + +/* {{{ PHP_INI + */ +PHP_INI_BEGIN() + PHP_INI_ENTRY_EX("define_syslog_variables", "0", PHP_INI_ALL, NULL, php_ini_boolean_displayer_cb) + PHP_INI_ENTRY_EX("highlight.bg", HL_BG_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + PHP_INI_ENTRY_EX("highlight.comment", HL_COMMENT_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + PHP_INI_ENTRY_EX("highlight.default", HL_DEFAULT_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + PHP_INI_ENTRY_EX("highlight.html", HL_HTML_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + PHP_INI_ENTRY_EX("highlight.keyword", HL_KEYWORD_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + PHP_INI_ENTRY_EX("highlight.string", HL_STRING_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) + + STD_PHP_INI_BOOLEAN("allow_call_time_pass_reference", "1", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, allow_call_time_pass_reference, zend_compiler_globals, compiler_globals) + STD_PHP_INI_BOOLEAN("asp_tags", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, asp_tags, zend_compiler_globals, compiler_globals) + STD_PHP_INI_ENTRY_EX("display_errors", "1", PHP_INI_ALL, OnUpdateDisplayErrors, display_errors, php_core_globals, core_globals, display_errors_mode) + STD_PHP_INI_BOOLEAN("display_startup_errors", "0", PHP_INI_ALL, OnUpdateBool, display_startup_errors, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("enable_dl", "1", PHP_INI_SYSTEM, OnUpdateBool, enable_dl, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("expose_php", "1", PHP_INI_SYSTEM, OnUpdateBool, expose_php, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("docref_root", "", PHP_INI_ALL, OnUpdateString, docref_root, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("docref_ext", "", PHP_INI_ALL, OnUpdateString, docref_ext, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("html_errors", "1", PHP_INI_ALL, OnUpdateBool, html_errors, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("xmlrpc_errors", "0", PHP_INI_SYSTEM, OnUpdateBool, xmlrpc_errors, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("xmlrpc_error_number", "0", PHP_INI_ALL, OnUpdateLong, xmlrpc_error_number, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("max_input_time", "-1", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, max_input_time, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("ignore_user_abort", "0", PHP_INI_ALL, OnUpdateBool, ignore_user_abort, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("implicit_flush", "0", PHP_INI_ALL, OnUpdateBool, implicit_flush, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("log_errors", "0", PHP_INI_ALL, OnUpdateBool, log_errors, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("log_errors_max_len", "1024", PHP_INI_ALL, OnUpdateLong, log_errors_max_len, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("ignore_repeated_errors", "0", PHP_INI_ALL, OnUpdateBool, ignore_repeated_errors, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("ignore_repeated_source", "0", PHP_INI_ALL, OnUpdateBool, ignore_repeated_source, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("report_memleaks", "1", PHP_INI_ALL, OnUpdateBool, report_memleaks, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("report_zend_debug", "1", PHP_INI_ALL, OnUpdateBool, report_zend_debug, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("magic_quotes_gpc", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, magic_quotes_gpc, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("magic_quotes_runtime", "0", PHP_INI_ALL, OnUpdateBool, magic_quotes_runtime, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("magic_quotes_sybase", "0", PHP_INI_ALL, OnUpdateBool, magic_quotes_sybase, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("output_buffering", "0", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateLong, output_buffering, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("output_handler", NULL, PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateString, output_handler, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("register_argc_argv", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, register_argc_argv, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("register_globals", "0", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, register_globals, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("register_long_arrays", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, register_long_arrays, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("auto_globals_jit", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, auto_globals_jit, php_core_globals, core_globals) +#if PHP_SAFE_MODE + STD_PHP_INI_BOOLEAN("safe_mode", "1", PHP_INI_SYSTEM, OnUpdateBool, safe_mode, php_core_globals, core_globals) +#else + STD_PHP_INI_BOOLEAN("safe_mode", "0", PHP_INI_SYSTEM, OnUpdateBool, safe_mode, php_core_globals, core_globals) +#endif + STD_PHP_INI_ENTRY("safe_mode_include_dir", NULL, PHP_INI_SYSTEM, OnUpdateString, safe_mode_include_dir, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("safe_mode_gid", "0", PHP_INI_SYSTEM, OnUpdateBool, safe_mode_gid, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("short_open_tag", DEFAULT_SHORT_OPEN_TAG, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, short_tags, zend_compiler_globals, compiler_globals) + STD_PHP_INI_BOOLEAN("sql.safe_mode", "0", PHP_INI_SYSTEM, OnUpdateBool, sql_safe_mode, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("track_errors", "0", PHP_INI_ALL, OnUpdateBool, track_errors, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("y2k_compliance", "1", PHP_INI_ALL, OnUpdateBool, y2k_compliance, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("unserialize_callback_func", NULL, PHP_INI_ALL, OnUpdateString, unserialize_callback_func, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("serialize_precision", "100", PHP_INI_ALL, OnUpdateLongGEZero, serialize_precision, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("arg_separator.output", "&", PHP_INI_ALL, OnUpdateStringUnempty, arg_separator.output, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("arg_separator.input", "&", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateStringUnempty, arg_separator.input, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("auto_append_file", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, auto_append_file, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("auto_prepend_file", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, auto_prepend_file, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("doc_root", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, doc_root, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("default_charset", SAPI_DEFAULT_CHARSET, PHP_INI_ALL, OnUpdateString, default_charset, sapi_globals_struct,sapi_globals) + STD_PHP_INI_ENTRY("default_mimetype", SAPI_DEFAULT_MIMETYPE, PHP_INI_ALL, OnUpdateString, default_mimetype, sapi_globals_struct,sapi_globals) + STD_PHP_INI_ENTRY("error_log", NULL, PHP_INI_ALL, OnUpdateErrorLog, error_log, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("extension_dir", PHP_EXTENSION_DIR, PHP_INI_SYSTEM, OnUpdateStringUnempty, extension_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("include_path", PHP_INCLUDE_PATH, PHP_INI_ALL, OnUpdateStringUnempty, include_path, php_core_globals, core_globals) + PHP_INI_ENTRY("max_execution_time", "30", PHP_INI_ALL, OnUpdateTimeout) + STD_PHP_INI_ENTRY("open_basedir", NULL, PHP_INI_ALL, OnUpdateBaseDir, open_basedir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("safe_mode_exec_dir", PHP_SAFE_MODE_EXEC_DIR, PHP_INI_SYSTEM, OnUpdateString, safe_mode_exec_dir, php_core_globals, core_globals) + + STD_PHP_INI_BOOLEAN("file_uploads", "1", PHP_INI_SYSTEM, OnUpdateBool, file_uploads, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("upload_max_filesize", "2M", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, upload_max_filesize, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("post_max_size", "8M", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, post_max_size, sapi_globals_struct,sapi_globals) + STD_PHP_INI_ENTRY("upload_tmp_dir", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, upload_tmp_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("max_input_nesting_level", "64", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLongGEZero, max_input_nesting_level, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("user_dir", NULL, PHP_INI_SYSTEM, OnUpdateString, user_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("variables_order", "EGPCS", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateStringUnempty, variables_order, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("request_order", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, request_order, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("error_append_string", NULL, PHP_INI_ALL, OnUpdateString, error_append_string, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("error_prepend_string", NULL, PHP_INI_ALL, OnUpdateString, error_prepend_string, php_core_globals, core_globals) + + PHP_INI_ENTRY("SMTP", "localhost",PHP_INI_ALL, NULL) + PHP_INI_ENTRY("smtp_port", "25", PHP_INI_ALL, NULL) + STD_PHP_INI_BOOLEAN("mail.add_x_header", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, mail_x_header, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("mail.log", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateMailLog, mail_log, php_core_globals, core_globals) + PHP_INI_ENTRY("browscap", NULL, PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("memory_limit", "128M", PHP_INI_ALL, OnChangeMemoryLimit) + PHP_INI_ENTRY("precision", "14", PHP_INI_ALL, OnSetPrecision) + PHP_INI_ENTRY("sendmail_from", NULL, PHP_INI_ALL, NULL) + PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("mail.force_extra_parameters",NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnChangeMailForceExtra) + PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL) + + STD_PHP_INI_BOOLEAN("allow_url_fopen", "1", PHP_INI_SYSTEM, OnUpdateBool, allow_url_fopen, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("allow_url_include", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_url_include, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("always_populate_raw_post_data", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, always_populate_raw_post_data, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("realpath_cache_size", "16K", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_size_limit, virtual_cwd_globals, cwd_globals) + STD_PHP_INI_ENTRY("realpath_cache_ttl", "120", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_ttl, virtual_cwd_globals, cwd_globals) + + STD_PHP_INI_ENTRY("user_ini.filename", ".user.ini", PHP_INI_SYSTEM, OnUpdateString, user_ini_filename, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("user_ini.cache_ttl", "300", PHP_INI_SYSTEM, OnUpdateLong, user_ini_cache_ttl, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("exit_on_timeout", "0", PHP_INI_ALL, OnUpdateBool, exit_on_timeout, php_core_globals, core_globals) +PHP_INI_END() +/* }}} */ + +/* True globals (no need for thread safety */ +/* But don't make them a single int bitfield */ +static int module_initialized = 0; +static int module_startup = 1; +static int module_shutdown = 0; + +/* {{{ php_during_module_startup */ +static int php_during_module_startup(void) +{ + return module_startup; +} +/* }}} */ + +/* {{{ php_during_module_shutdown */ +static int php_during_module_shutdown(void) +{ + return module_shutdown; +} +/* }}} */ + +/* {{{ php_log_err + */ +PHPAPI void php_log_err(char *log_message TSRMLS_DC) +{ + int fd = -1; + time_t error_time; + + /* Try to use the specified logging location. */ + if (PG(error_log) != NULL) { +#ifdef HAVE_SYSLOG_H + if (!strcmp(PG(error_log), "syslog")) { + php_syslog(LOG_NOTICE, "%.500s", log_message); + return; + } +#endif + fd = VCWD_OPEN_MODE(PG(error_log), O_CREAT | O_APPEND | O_WRONLY, 0644); + if (fd != -1) { + char *tmp; + int len; + char *error_time_str; + + time(&error_time); + error_time_str = php_format_date("d-M-Y H:i:s", 11, error_time, php_during_module_startup() TSRMLS_CC); + len = spprintf(&tmp, 0, "[%s] %s%s", error_time_str, log_message, PHP_EOL); +#ifdef PHP_WIN32 + php_flock(fd, 2); +#endif + write(fd, tmp, len); + efree(tmp); + efree(error_time_str); + close(fd); + return; + } + } + + /* Otherwise fall back to the default logging location, if we have one */ + + if (sapi_module.log_message) { + sapi_module.log_message(log_message); + } +} +/* }}} */ + +/* {{{ php_write + wrapper for modules to use PHPWRITE */ +PHPAPI int php_write(void *buf, uint size TSRMLS_DC) +{ + return PHPWRITE(buf, size); +} +/* }}} */ + +/* {{{ php_printf + */ +PHPAPI int php_printf(const char *format, ...) +{ + va_list args; + int ret; + char *buffer; + int size; + TSRMLS_FETCH(); + + va_start(args, format); + size = vspprintf(&buffer, 0, format, args); + ret = PHPWRITE(buffer, size); + efree(buffer); + va_end(args); + + return ret; +} +/* }}} */ + +/* {{{ php_verror */ +/* php_verror is called from php_error_docref functions. + * Its purpose is to unify error messages and automatically generate clickable + * html error messages if correcponding ini setting (html_errors) is activated. + * See: CODING_STANDARDS for details. + */ +PHPAPI void php_verror(const char *docref, const char *params, int type, const char *format, va_list args TSRMLS_DC) +{ + char *buffer = NULL, *docref_buf = NULL, *target = NULL; + char *docref_target = "", *docref_root = ""; + char *p; + int buffer_len = 0; + char *space = ""; + char *class_name = ""; + char *function; + int origin_len; + char *origin; + char *message; + int is_function = 0; + + /* get error text into buffer and escape for html if necessary */ + buffer_len = vspprintf(&buffer, 0, format, args); + if (PG(html_errors)) { + int len; + char *replace = php_escape_html_entities(buffer, buffer_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); + efree(buffer); + buffer = replace; + buffer_len = len; + } + + /* which function caused the problem if any at all */ + if (php_during_module_startup()) { + function = "PHP Startup"; + } else if (php_during_module_shutdown()) { + function = "PHP Shutdown"; + } else if (EG(current_execute_data) && + EG(current_execute_data)->opline && + EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL + ) { + switch (EG(current_execute_data)->opline->op2.u.constant.value.lval) { + case ZEND_EVAL: + function = "eval"; + is_function = 1; + break; + case ZEND_INCLUDE: + function = "include"; + is_function = 1; + break; + case ZEND_INCLUDE_ONCE: + function = "include_once"; + is_function = 1; + break; + case ZEND_REQUIRE: + function = "require"; + is_function = 1; + break; + case ZEND_REQUIRE_ONCE: + function = "require_once"; + is_function = 1; + break; + default: + function = "Unknown"; + } + } else { + function = get_active_function_name(TSRMLS_C); + if (!function || !strlen(function)) { + function = "Unknown"; + } else { + is_function = 1; + class_name = get_active_class_name(&space TSRMLS_CC); + } + } + + /* if we still have memory then format the origin */ + if (is_function) { + origin_len = spprintf(&origin, 0, "%s%s%s(%s)", class_name, space, function, params); + } else { + origin_len = spprintf(&origin, 0, "%s", function); + } + + if (PG(html_errors)) { + int len; + char *replace = php_escape_html_entities(origin, origin_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); + efree(origin); + origin = replace; + } + + /* origin and buffer available, so lets come up with the error message */ + if (docref && docref[0] == '#') { + docref_target = strchr(docref, '#'); + docref = NULL; + } + + /* no docref given but function is known (the default) */ + if (!docref && is_function) { + int doclen; + if (space[0] == '\0') { + doclen = spprintf(&docref_buf, 0, "function.%s", function); + } else { + doclen = spprintf(&docref_buf, 0, "%s.%s", class_name, function); + } + while((p = strchr(docref_buf, '_')) != NULL) { + *p = '-'; + } + docref = php_strtolower(docref_buf, doclen); + } + + /* we have a docref for a function AND + * - we show erroes in html mode OR + * - the user wants to see the links anyway + */ + if (docref && is_function && (PG(html_errors) || strlen(PG(docref_root)))) { + if (strncmp(docref, "http://", 7)) { + /* We don't have 'http://' so we use docref_root */ + + char *ref; /* temp copy for duplicated docref */ + + docref_root = PG(docref_root); + + ref = estrdup(docref); + if (docref_buf) { + efree(docref_buf); + } + docref_buf = ref; + /* strip of the target if any */ + p = strrchr(ref, '#'); + if (p) { + target = estrdup(p); + if (target) { + docref_target = target; + *p = '\0'; + } + } + /* add the extension if it is set in ini */ + if (PG(docref_ext) && strlen(PG(docref_ext))) { + spprintf(&docref_buf, 0, "%s%s", ref, PG(docref_ext)); + efree(ref); + } + docref = docref_buf; + } + /* display html formatted or only show the additional links */ + if (PG(html_errors)) { + spprintf(&message, 0, "%s [%s]: %s", origin, docref_root, docref, docref_target, docref, buffer); + } else { + spprintf(&message, 0, "%s [%s%s%s]: %s", origin, docref_root, docref, docref_target, buffer); + } + if (target) { + efree(target); + } + } else { + spprintf(&message, 0, "%s: %s", origin, buffer); + } + efree(origin); + if (docref_buf) { + efree(docref_buf); + } + + if (PG(track_errors) && module_initialized && + (!EG(user_error_handler) || !(EG(user_error_handler_error_reporting) & type))) { + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + if (EG(active_symbol_table)) { + zval *tmp; + ALLOC_INIT_ZVAL(tmp); + ZVAL_STRINGL(tmp, buffer, buffer_len, 1); + zend_hash_update(EG(active_symbol_table), "php_errormsg", sizeof("php_errormsg"), (void **) &tmp, sizeof(zval *), NULL); + } + } + efree(buffer); + + php_error(type, "%s", message); + efree(message); +} +/* }}} */ + +/* {{{ php_error_docref0 */ +/* See: CODING_STANDARDS for details. */ +PHPAPI void php_error_docref0(const char *docref TSRMLS_DC, int type, const char *format, ...) +{ + va_list args; + + va_start(args, format); + php_verror(docref, "", type, format, args TSRMLS_CC); + va_end(args); +} +/* }}} */ + +/* {{{ php_error_docref1 */ +/* See: CODING_STANDARDS for details. */ +PHPAPI void php_error_docref1(const char *docref TSRMLS_DC, const char *param1, int type, const char *format, ...) +{ + va_list args; + + va_start(args, format); + php_verror(docref, param1, type, format, args TSRMLS_CC); + va_end(args); +} +/* }}} */ + +/* {{{ php_error_docref2 */ +/* See: CODING_STANDARDS for details. */ +PHPAPI void php_error_docref2(const char *docref TSRMLS_DC, const char *param1, const char *param2, int type, const char *format, ...) +{ + char *params; + va_list args; + + spprintf(¶ms, 0, "%s,%s", param1, param2); + va_start(args, format); + php_verror(docref, params ? params : "...", type, format, args TSRMLS_CC); + va_end(args); + if (params) { + efree(params); + } +} +/* }}} */ + +/* {{{ php_html_puts */ +PHPAPI void php_html_puts(const char *str, uint size TSRMLS_DC) +{ + zend_html_puts(str, size TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_error_cb + extended error handling function */ +static void php_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) +{ + char *buffer; + int buffer_len, display; + TSRMLS_FETCH(); + + buffer_len = vspprintf(&buffer, PG(log_errors_max_len), format, args); + + /* check for repeated errors to be ignored */ + if (PG(ignore_repeated_errors) && PG(last_error_message)) { + /* no check for PG(last_error_file) is needed since it cannot + * be NULL if PG(last_error_message) is not NULL */ + if (strcmp(PG(last_error_message), buffer) + || (!PG(ignore_repeated_source) + && ((PG(last_error_lineno) != (int)error_lineno) + || strcmp(PG(last_error_file), error_filename)))) { + display = 1; + } else { + display = 0; + } + } else { + display = 1; + } + + /* store the error if it has changed */ + if (display) { + if (PG(last_error_message)) { + free(PG(last_error_message)); + } + if (PG(last_error_file)) { + free(PG(last_error_file)); + } + PG(last_error_type) = type; + PG(last_error_message) = strdup(buffer); + PG(last_error_file) = strdup(error_filename); + PG(last_error_lineno) = error_lineno; + } + + /* according to error handling mode, suppress error, throw exception or show it */ + if (EG(error_handling) != EH_NORMAL) { + switch (type) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + case E_PARSE: + /* fatal errors are real errors and cannot be made exceptions */ + break; + case E_STRICT: + case E_DEPRECATED: + case E_USER_DEPRECATED: + /* for the sake of BC to old damaged code */ + break; + case E_NOTICE: + case E_USER_NOTICE: + /* notices are no errors and are not treated as such like E_WARNINGS */ + break; + default: + /* throw an exception if we are in EH_THROW mode + * but DO NOT overwrite a pending exception + */ + if (EG(error_handling) == EH_THROW && !EG(exception)) { + zend_throw_error_exception(EG(exception_class), buffer, 0, type TSRMLS_CC); + } + efree(buffer); + return; + } + } + + /* display/log the error if necessary */ + if (display && (EG(error_reporting) & type || (type & E_CORE)) + && (PG(log_errors) || PG(display_errors) || (!module_initialized))) { + char *error_type_str; + + switch (type) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + error_type_str = "Fatal error"; + break; + case E_RECOVERABLE_ERROR: + error_type_str = "Catchable fatal error"; + break; + case E_WARNING: + case E_CORE_WARNING: + case E_COMPILE_WARNING: + case E_USER_WARNING: + error_type_str = "Warning"; + break; + case E_PARSE: + error_type_str = "Parse error"; + break; + case E_NOTICE: + case E_USER_NOTICE: + error_type_str = "Notice"; + break; + case E_STRICT: + error_type_str = "Strict Standards"; + break; + case E_DEPRECATED: + case E_USER_DEPRECATED: + error_type_str = "Deprecated"; + break; + default: + error_type_str = "Unknown error"; + break; + } + + if (!module_initialized || PG(log_errors)) { + char *log_buffer; +#ifdef PHP_WIN32 + if ((type == E_CORE_ERROR || type == E_CORE_WARNING) && PG(display_startup_errors)) { + MessageBox(NULL, buffer, error_type_str, MB_OK|ZEND_SERVICE_MB_STYLE); + } +#endif + spprintf(&log_buffer, 0, "PHP %s: %s in %s on line %d", error_type_str, buffer, error_filename, error_lineno); + php_log_err(log_buffer TSRMLS_CC); + efree(log_buffer); + } + if (PG(display_errors) + && ((module_initialized && !PG(during_request_startup)) + || (PG(display_startup_errors) + && (OG(php_body_write)==php_default_output_func || OG(php_body_write)==php_ub_body_write_no_header || OG(php_body_write)==php_ub_body_write) + ) + ) + ) { + + if (PG(xmlrpc_errors)) { + php_printf("faultCode%ldfaultString%s:%s in %s on line %d", PG(xmlrpc_error_number), error_type_str, buffer, error_filename, error_lineno); + } else { + char *prepend_string = INI_STR("error_prepend_string"); + char *append_string = INI_STR("error_append_string"); + + if (PG(html_errors)) { + if (type == E_ERROR) { + int len; + char *buf = php_escape_html_entities(buffer, buffer_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); + php_printf("%s
\n%s: %s in %s on line %d
\n%s", STR_PRINT(prepend_string), error_type_str, buf, error_filename, error_lineno, STR_PRINT(append_string)); + efree(buf); + } else { + php_printf("%s
\n%s: %s in %s on line %d
\n%s", STR_PRINT(prepend_string), error_type_str, buffer, error_filename, error_lineno, STR_PRINT(append_string)); + } + } else { + /* Write CLI/CGI errors to stderr if display_errors = "stderr" */ + if ((!strcmp(sapi_module.name, "cli") || !strcmp(sapi_module.name, "cgi")) && + PG(display_errors) == PHP_DISPLAY_ERRORS_STDERR + ) { + fprintf(stderr, "%s: %s in %s on line %d\n", error_type_str, buffer, error_filename, error_lineno); + } else { + php_printf("%s\n%s: %s in %s on line %d\n%s", STR_PRINT(prepend_string), error_type_str, buffer, error_filename, error_lineno, STR_PRINT(append_string)); + } + } + } + } +#if ZEND_DEBUG + if (PG(report_zend_debug)) { + zend_bool trigger_break; + + switch (type) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + trigger_break=1; + break; + default: + trigger_break=0; + break; + } + zend_output_debug_string(trigger_break, "%s(%d) : %s - %s", error_filename, error_lineno, error_type_str, buffer); + } +#endif + } + + /* Bail out if we can't recover */ + switch (type) { + case E_CORE_ERROR: + if(!module_initialized) { + /* bad error in module startup - no way we can live with this */ + exit(-2); + } + /* no break - intentionally */ + case E_ERROR: + case E_RECOVERABLE_ERROR: + case E_PARSE: + case E_COMPILE_ERROR: + case E_USER_ERROR: + EG(exit_status) = 255; + if (module_initialized) { + if (!PG(display_errors) && + !SG(headers_sent) && + SG(sapi_headers).http_response_code == 200 + ) { + sapi_header_line ctr = {0}; + + ctr.line = "HTTP/1.0 500 Internal Server Error"; + ctr.line_len = strlen(ctr.line); + sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); + } + /* the parser would return 1 (failure), we can bail out nicely */ + if (type != E_PARSE) { + /* restore memory limit */ + zend_set_memory_limit(PG(memory_limit)); + efree(buffer); + zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC); + zend_bailout(); + return; + } + } + break; + } + + /* Log if necessary */ + if (!display) { + efree(buffer); + return; + } + + if (PG(track_errors) && module_initialized) { + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + if (EG(active_symbol_table)) { + zval *tmp; + ALLOC_INIT_ZVAL(tmp); + ZVAL_STRINGL(tmp, buffer, buffer_len, 1); + zend_hash_update(EG(active_symbol_table), "php_errormsg", sizeof("php_errormsg"), (void **) & tmp, sizeof(zval *), NULL); + } + } + + efree(buffer); +} +/* }}} */ + +/* {{{ proto bool set_time_limit(int seconds) + Sets the maximum time a script can run */ +PHP_FUNCTION(set_time_limit) +{ + long new_timeout; + char *new_timeout_str; + int new_timeout_strlen; + + if (PG(safe_mode)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot set time limit in safe mode"); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &new_timeout) == FAILURE) { + return; + } + + new_timeout_strlen = zend_spprintf(&new_timeout_str, 0, "%ld", new_timeout); + + if (zend_alter_ini_entry_ex("max_execution_time", sizeof("max_execution_time"), new_timeout_str, new_timeout_strlen, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == SUCCESS) { + RETVAL_TRUE; + } else { + RETVAL_FALSE; + } + efree(new_timeout_str); +} +/* }}} */ + +/* {{{ php_fopen_wrapper_for_zend + */ +static FILE *php_fopen_wrapper_for_zend(const char *filename, char **opened_path TSRMLS_DC) +{ + return php_stream_open_wrapper_as_file((char *)filename, "rb", ENFORCE_SAFE_MODE|USE_PATH|IGNORE_URL_WIN|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE, opened_path); +} +/* }}} */ + +static void php_zend_stream_closer(void *handle TSRMLS_DC) /* {{{ */ +{ + php_stream_close((php_stream*)handle); +} +/* }}} */ + +static void php_zend_stream_mmap_closer(void *handle TSRMLS_DC) /* {{{ */ +{ + php_stream_mmap_unmap((php_stream*)handle); + php_zend_stream_closer(handle TSRMLS_CC); +} +/* }}} */ + +static size_t php_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */ +{ + php_stream_statbuf ssb; + if (php_stream_stat((php_stream*)handle, &ssb) == 0) { + return ssb.sb.st_size; + } + return 0; +} +/* }}} */ + +static int php_stream_open_for_zend(const char *filename, zend_file_handle *handle TSRMLS_DC) /* {{{ */ +{ + return php_stream_open_for_zend_ex(filename, handle, ENFORCE_SAFE_MODE|USE_PATH|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); +} +/* }}} */ + +PHPAPI int php_stream_open_for_zend_ex(const char *filename, zend_file_handle *handle, int mode TSRMLS_DC) /* {{{ */ +{ + char *p; + size_t len, mapped_len; + php_stream *stream = php_stream_open_wrapper((char *)filename, "rb", mode, &handle->opened_path); + + if (stream) { + handle->filename = (char*)filename; + handle->free_filename = 0; + handle->handle.stream.handle = stream; + handle->handle.stream.reader = (zend_stream_reader_t)_php_stream_read; + handle->handle.stream.fsizer = php_zend_stream_fsizer; + handle->handle.stream.isatty = 0; + /* can we mmap immeadiately? */ + memset(&handle->handle.stream.mmap, 0, sizeof(handle->handle.stream.mmap)); + len = php_zend_stream_fsizer(stream TSRMLS_CC); + if (len != 0 + && ((len - 1) % PAGE_SIZE) <= PAGE_SIZE - ZEND_MMAP_AHEAD + && php_stream_mmap_possible(stream) + && (p = php_stream_mmap_range(stream, 0, len, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped_len)) != NULL) { + handle->handle.stream.closer = php_zend_stream_mmap_closer; + handle->handle.stream.mmap.buf = p; + handle->handle.stream.mmap.len = mapped_len; + handle->type = ZEND_HANDLE_MAPPED; + } else { + handle->handle.stream.closer = php_zend_stream_closer; + handle->type = ZEND_HANDLE_STREAM; + } + /* suppress warning if this stream is not explicitly closed */ + php_stream_auto_cleanup(stream); + + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +static char *php_resolve_path_for_zend(const char *filename, int filename_len TSRMLS_DC) /* {{{ */ +{ + return php_resolve_path(filename, filename_len, PG(include_path) TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_get_configuration_directive_for_zend + */ +static int php_get_configuration_directive_for_zend(const char *name, uint name_length, zval *contents) +{ + zval *retval = cfg_get_entry(name, name_length); + + if (retval) { + *contents = *retval; + return SUCCESS; + } else { + return FAILURE; + } +} +/* }}} */ + +/* {{{ php_message_handler_for_zend + */ +static void php_message_handler_for_zend(long message, void *data TSRMLS_DC) +{ + switch (message) { + case ZMSG_FAILED_INCLUDE_FOPEN: + php_error_docref("function.include" TSRMLS_CC, E_WARNING, "Failed opening '%s' for inclusion (include_path='%s')", php_strip_url_passwd((char *) data), STR_PRINT(PG(include_path))); + break; + case ZMSG_FAILED_REQUIRE_FOPEN: + php_error_docref("function.require" TSRMLS_CC, E_COMPILE_ERROR, "Failed opening required '%s' (include_path='%s')", php_strip_url_passwd((char *) data), STR_PRINT(PG(include_path))); + break; + case ZMSG_FAILED_HIGHLIGHT_FOPEN: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed opening '%s' for highlighting", php_strip_url_passwd((char *) data)); + break; + case ZMSG_MEMORY_LEAK_DETECTED: + case ZMSG_MEMORY_LEAK_REPEATED: +#if ZEND_DEBUG + if (EG(error_reporting) & E_WARNING) { + char memory_leak_buf[1024]; + + if (message==ZMSG_MEMORY_LEAK_DETECTED) { + zend_leak_info *t = (zend_leak_info *) data; + + snprintf(memory_leak_buf, 512, "%s(%d) : Freeing 0x%.8lX (%zu bytes), script=%s\n", t->filename, t->lineno, (zend_uintptr_t)t->addr, t->size, SAFE_FILENAME(SG(request_info).path_translated)); + if (t->orig_filename) { + char relay_buf[512]; + + snprintf(relay_buf, 512, "%s(%d) : Actual location (location was relayed)\n", t->orig_filename, t->orig_lineno); + strlcat(memory_leak_buf, relay_buf, sizeof(memory_leak_buf)); + } + } else { + unsigned long leak_count = (zend_uintptr_t) data; + + snprintf(memory_leak_buf, 512, "Last leak repeated %ld time%s\n", leak_count, (leak_count>1?"s":"")); + } +# if defined(PHP_WIN32) + OutputDebugString(memory_leak_buf); +# else + fprintf(stderr, "%s", memory_leak_buf); +# endif + } +#endif + break; + case ZMSG_MEMORY_LEAKS_GRAND_TOTAL: +#if ZEND_DEBUG + if (EG(error_reporting) & E_WARNING) { + char memory_leak_buf[512]; + + snprintf(memory_leak_buf, 512, "=== Total %d memory leaks detected ===\n", *((zend_uint *) data)); +# if defined(PHP_WIN32) + OutputDebugString(memory_leak_buf); +# else + fprintf(stderr, "%s", memory_leak_buf); +# endif + } +#endif + break; + case ZMSG_LOG_SCRIPT_NAME: { + struct tm *ta, tmbuf; + time_t curtime; + char *datetime_str, asctimebuf[52]; + char memory_leak_buf[4096]; + + time(&curtime); + ta = php_localtime_r(&curtime, &tmbuf); + datetime_str = php_asctime_r(ta, asctimebuf); + if (datetime_str) { + datetime_str[strlen(datetime_str)-1]=0; /* get rid of the trailing newline */ + snprintf(memory_leak_buf, sizeof(memory_leak_buf), "[%s] Script: '%s'\n", datetime_str, SAFE_FILENAME(SG(request_info).path_translated)); + } else { + snprintf(memory_leak_buf, sizeof(memory_leak_buf), "[null] Script: '%s'\n", SAFE_FILENAME(SG(request_info).path_translated)); + } +# if defined(PHP_WIN32) + OutputDebugString(memory_leak_buf); +# else + fprintf(stderr, "%s", memory_leak_buf); +# endif + } + break; + } +} +/* }}} */ + + +void php_on_timeout(int seconds TSRMLS_DC) +{ + PG(connection_status) |= PHP_CONNECTION_TIMEOUT; + zend_set_timeout(EG(timeout_seconds), 1); + if(PG(exit_on_timeout)) sapi_terminate_process(TSRMLS_C); +} + +#if PHP_SIGCHILD +/* {{{ sigchld_handler + */ +static void sigchld_handler(int apar) +{ + while (waitpid(-1, NULL, WNOHANG) > 0); + signal(SIGCHLD, sigchld_handler); +} +/* }}} */ +#endif + +/* {{{ php_start_sapi() + */ +static int php_start_sapi(TSRMLS_D) +{ + int retval = SUCCESS; + + if(!SG(sapi_started)) { + zend_try { + PG(during_request_startup) = 1; + + /* initialize global variables */ + PG(modules_activated) = 0; + PG(header_is_being_sent) = 0; + PG(connection_status) = PHP_CONNECTION_NORMAL; + + zend_activate(TSRMLS_C); + zend_set_timeout(EG(timeout_seconds), 1); + zend_activate_modules(TSRMLS_C); + PG(modules_activated)=1; + } zend_catch { + retval = FAILURE; + } zend_end_try(); + + SG(sapi_started) = 1; + } + return retval; +} + +/* }}} */ + +/* {{{ php_request_startup + */ +#ifndef APACHE_HOOKS +int php_request_startup(TSRMLS_D) +{ + int retval = SUCCESS; + +#ifdef PHP_WIN32 + PG(com_initialized) = 0; +#endif + +#if PHP_SIGCHILD + signal(SIGCHLD, sigchld_handler); +#endif + + zend_try { + PG(during_request_startup) = 1; + + php_output_activate(TSRMLS_C); + + /* initialize global variables */ + PG(modules_activated) = 0; + PG(header_is_being_sent) = 0; + PG(connection_status) = PHP_CONNECTION_NORMAL; + PG(in_user_include) = 0; + + zend_activate(TSRMLS_C); + sapi_activate(TSRMLS_C); + + if (PG(max_input_time) == -1) { + zend_set_timeout(EG(timeout_seconds), 1); + } else { + zend_set_timeout(PG(max_input_time), 1); + } + + /* Disable realpath cache if safe_mode or open_basedir are set */ + if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) { + CWDG(realpath_cache_size_limit) = 0; + } + + if (PG(expose_php)) { + sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); + } + + if (PG(output_handler) && PG(output_handler)[0]) { + php_start_ob_buffer_named(PG(output_handler), 0, 1 TSRMLS_CC); + } else if (PG(output_buffering)) { + if (PG(output_buffering)>1) { + php_start_ob_buffer(NULL, PG(output_buffering), 1 TSRMLS_CC); + } else { + php_start_ob_buffer(NULL, 0, 1 TSRMLS_CC); + } + } else if (PG(implicit_flush)) { + php_start_implicit_flush(TSRMLS_C); + } + + /* We turn this off in php_execute_script() */ + /* PG(during_request_startup) = 0; */ + + php_hash_environment(TSRMLS_C); + zend_activate_modules(TSRMLS_C); + PG(modules_activated)=1; + } zend_catch { + retval = FAILURE; + } zend_end_try(); + + SG(sapi_started) = 1; + + return retval; +} +# else +int php_request_startup(TSRMLS_D) +{ + int retval = SUCCESS; + +#if PHP_SIGCHILD + signal(SIGCHLD, sigchld_handler); +#endif + + if (php_start_sapi() == FAILURE) { + return FAILURE; + } + + php_output_activate(TSRMLS_C); + sapi_activate(TSRMLS_C); + php_hash_environment(TSRMLS_C); + + zend_try { + PG(during_request_startup) = 1; + php_output_activate(TSRMLS_C); + if (PG(expose_php)) { + sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); + } + } zend_catch { + retval = FAILURE; + } zend_end_try(); + + return retval; +} +# endif +/* }}} */ + +/* {{{ php_request_startup_for_hook + */ +int php_request_startup_for_hook(TSRMLS_D) +{ + int retval = SUCCESS; + +#if PHP_SIGCHLD + signal(SIGCHLD, sigchld_handler); +#endif + + if (php_start_sapi(TSRMLS_C) == FAILURE) { + return FAILURE; + } + + php_output_activate(TSRMLS_C); + sapi_activate_headers_only(TSRMLS_C); + php_hash_environment(TSRMLS_C); + + return retval; +} +/* }}} */ + +/* {{{ php_request_shutdown_for_exec + */ +void php_request_shutdown_for_exec(void *dummy) +{ + TSRMLS_FETCH(); + + /* used to close fd's in the 3..255 range here, but it's problematic + */ + shutdown_memory_manager(1, 1 TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_request_shutdown_for_hook + */ +void php_request_shutdown_for_hook(void *dummy) +{ + TSRMLS_FETCH(); + + if (PG(modules_activated)) zend_try { + php_call_shutdown_functions(TSRMLS_C); + } zend_end_try(); + + if (PG(modules_activated)) { + zend_deactivate_modules(TSRMLS_C); + php_free_shutdown_functions(TSRMLS_C); + } + + zend_try { + int i; + + for (i = 0; i < NUM_TRACK_VARS; i++) { + if (PG(http_globals)[i]) { + zval_ptr_dtor(&PG(http_globals)[i]); + } + } + } zend_end_try(); + + zend_deactivate(TSRMLS_C); + + zend_try { + sapi_deactivate(TSRMLS_C); + } zend_end_try(); + + zend_try { + php_shutdown_stream_hashes(TSRMLS_C); + } zend_end_try(); + + zend_try { + shutdown_memory_manager(CG(unclean_shutdown), 0 TSRMLS_CC); + } zend_end_try(); + + zend_try { + zend_unset_timeout(TSRMLS_C); + } zend_end_try(); +} + +/* }}} */ + +/* {{{ php_request_shutdown + */ +void php_request_shutdown(void *dummy) +{ + zend_bool report_memleaks; + TSRMLS_FETCH(); + + report_memleaks = PG(report_memleaks); + + /* EG(opline_ptr) points into nirvana and therefore cannot be safely accessed + * inside zend_executor callback functions. + */ + EG(opline_ptr) = NULL; + EG(active_op_array) = NULL; + + php_deactivate_ticks(TSRMLS_C); + + /* 1. Call all possible shutdown functions registered with register_shutdown_function() */ + if (PG(modules_activated)) zend_try { + php_call_shutdown_functions(TSRMLS_C); + } zend_end_try(); + + /* 2. Call all possible __destruct() functions */ + zend_try { + zend_call_destructors(TSRMLS_C); + } zend_end_try(); + + /* 3. Flush all output buffers */ + zend_try { + zend_bool send_buffer = SG(request_info).headers_only ? 0 : 1; + if (CG(unclean_shutdown) && PG(last_error_type) == E_ERROR && + OG(ob_nesting_level) && !OG(active_ob_buffer).chunk_size && PG(memory_limit) < zend_memory_usage(1 TSRMLS_CC)) { + send_buffer = 0; + } + php_end_ob_buffers(send_buffer TSRMLS_CC); + } zend_end_try(); + + /* 4. Send the set HTTP headers (note: This must be done AFTER php_end_ob_buffers() !!) */ + zend_try { + sapi_send_headers(TSRMLS_C); + } zend_end_try(); + + /* 5. Call all extensions RSHUTDOWN functions */ + if (PG(modules_activated)) { + zend_deactivate_modules(TSRMLS_C); + php_free_shutdown_functions(TSRMLS_C); + } + + /* 6. Destroy super-globals */ + zend_try { + int i; + + for (i=0; ilast_error_message) { + free(core_globals->last_error_message); + } + if (core_globals->last_error_file) { + free(core_globals->last_error_file); + } + if (core_globals->disable_functions) { + free(core_globals->disable_functions); + } + if (core_globals->disable_classes) { + free(core_globals->disable_classes); + } + + php_shutdown_ticks(TSRMLS_C); +} +/* }}} */ + +PHP_MINFO_FUNCTION(php_core) { /* {{{ */ + php_info_print_table_start(); + php_info_print_table_row(2, "PHP Version", PHP_VERSION); + php_info_print_table_end(); + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +/* {{{ php_register_extensions + */ +int php_register_extensions(zend_module_entry **ptr, int count TSRMLS_DC) +{ + zend_module_entry **end = ptr + count; + + while (ptr < end) { + if (*ptr) { + if (zend_register_internal_module(*ptr TSRMLS_CC)==NULL) { + return FAILURE; + } + } + ptr++; + } + return SUCCESS; +} +/* }}} */ + +#if defined(PHP_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1400) +static _invalid_parameter_handler old_invalid_parameter_handler; + +void dummy_invalid_parameter_handler( + const wchar_t *expression, + const wchar_t *function, + const wchar_t *file, + unsigned int line, + uintptr_t pEwserved) +{ + static int called = 0; + char buf[1024]; + int len; + + if (!called) { + called = 1; + if (function) { + if (file) { + len = _snprintf(buf, sizeof(buf)-1, "Invalid parameter detected in CRT function '%ws' (%ws:%d)", function, file, line); + } else { + len = _snprintf(buf, sizeof(buf)-1, "Invalid parameter detected in CRT function '%ws'", function); + } + } else { + len = _snprintf(buf, sizeof(buf)-1, "Invalid CRT parameters detected"); + } + zend_error(E_WARNING, "%s", buf); + called = 0; + } +} +#endif + +/* {{{ php_module_startup + */ +int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additional_modules) +{ + zend_utility_functions zuf; + zend_utility_values zuv; + int module_number=0; /* for REGISTER_INI_ENTRIES() */ + char *php_os; + zend_module_entry *module; +#ifdef ZTS + zend_executor_globals *executor_globals; + void ***tsrm_ls; + php_core_globals *core_globals; +#endif +#if defined(PHP_WIN32) || (defined(NETWARE) && defined(USE_WINSOCK)) + WORD wVersionRequested = MAKEWORD(2, 0); + WSADATA wsaData; +#endif +#ifdef PHP_WIN32 + DWORD dwVersion = GetVersion(); + /* Get build numbers for Windows NT or Win95 */ + if (dwVersion < 0x80000000){ + php_os="WINNT"; + } else { + php_os="WIN32"; + } +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + old_invalid_parameter_handler = + _set_invalid_parameter_handler(dummy_invalid_parameter_handler); + if (old_invalid_parameter_handler != NULL) { + _set_invalid_parameter_handler(old_invalid_parameter_handler); + } + + /* Disable the message box for assertions.*/ + _CrtSetReportMode(_CRT_ASSERT, 0); +#endif +#else + php_os=PHP_OS; +#endif + +#ifdef ZTS + tsrm_ls = ts_resource(0); +#endif + + module_shutdown = 0; + module_startup = 1; + sapi_initialize_empty_request(TSRMLS_C); + sapi_activate(TSRMLS_C); + + if (module_initialized) { + return SUCCESS; + } + + sapi_module = *sf; + + php_output_startup(); + + zuf.error_function = php_error_cb; + zuf.printf_function = php_printf; + zuf.write_function = php_body_write_wrapper; + zuf.fopen_function = php_fopen_wrapper_for_zend; + zuf.message_handler = php_message_handler_for_zend; + zuf.block_interruptions = sapi_module.block_interruptions; + zuf.unblock_interruptions = sapi_module.unblock_interruptions; + zuf.get_configuration_directive = php_get_configuration_directive_for_zend; + zuf.ticks_function = php_run_ticks; + zuf.on_timeout = php_on_timeout; + zuf.stream_open_function = php_stream_open_for_zend; + zuf.vspprintf_function = vspprintf; + zuf.getenv_function = sapi_getenv; + zuf.resolve_path_function = php_resolve_path_for_zend; + zend_startup(&zuf, NULL TSRMLS_CC); + +#ifdef ZTS + executor_globals = ts_resource(executor_globals_id); + ts_allocate_id(&core_globals_id, sizeof(php_core_globals), (ts_allocate_ctor) core_globals_ctor, (ts_allocate_dtor) core_globals_dtor); + core_globals = ts_resource(core_globals_id); +#ifdef PHP_WIN32 + ts_allocate_id(&php_win32_core_globals_id, sizeof(php_win32_core_globals), (ts_allocate_ctor) php_win32_core_globals_ctor, (ts_allocate_dtor) php_win32_core_globals_dtor); +#endif +#else + php_startup_ticks(TSRMLS_C); +#endif + gc_globals_ctor(TSRMLS_C); + +#ifdef PHP_WIN32 + { + OSVERSIONINFOEX *osvi = &EG(windows_version_info); + + ZeroMemory(osvi, sizeof(OSVERSIONINFOEX)); + osvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if( !GetVersionEx((OSVERSIONINFO *) osvi)) { + php_printf("\nGetVersionEx unusable. %d\n", GetLastError()); + return FAILURE; + } + } +#endif + EG(bailout) = NULL; + EG(error_reporting) = E_ALL & ~E_NOTICE; + + PG(header_is_being_sent) = 0; + SG(request_info).headers_only = 0; + SG(request_info).argv0 = NULL; + SG(request_info).argc=0; + SG(request_info).argv=(char **)NULL; + PG(connection_status) = PHP_CONNECTION_NORMAL; + PG(during_request_startup) = 0; + PG(last_error_message) = NULL; + PG(last_error_file) = NULL; + PG(last_error_lineno) = 0; + EG(error_handling) = EH_NORMAL; + EG(exception_class) = NULL; + PG(disable_functions) = NULL; + PG(disable_classes) = NULL; + +#if HAVE_SETLOCALE + setlocale(LC_CTYPE, ""); + zend_update_current_locale(); +#endif + +#if HAVE_TZSET + tzset(); +#endif + +#if defined(PHP_WIN32) || (defined(NETWARE) && defined(USE_WINSOCK)) + /* start up winsock services */ + if (WSAStartup(wVersionRequested, &wsaData) != 0) { + php_printf("\nwinsock.dll unusable. %d\n", WSAGetLastError()); + return FAILURE; + } +#endif + + le_index_ptr = zend_register_list_destructors_ex(NULL, NULL, "index pointer", 0); + + /* Register constants */ + REGISTER_MAIN_STRINGL_CONSTANT("PHP_VERSION", PHP_VERSION, sizeof(PHP_VERSION)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_MAJOR_VERSION", PHP_MAJOR_VERSION, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_MINOR_VERSION", PHP_MINOR_VERSION, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_RELEASE_VERSION", PHP_RELEASE_VERSION, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_EXTRA_VERSION", PHP_EXTRA_VERSION, sizeof(PHP_EXTRA_VERSION) - 1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_VERSION_ID", PHP_VERSION_ID, CONST_PERSISTENT | CONST_CS); +#ifdef ZTS + REGISTER_MAIN_LONG_CONSTANT("PHP_ZTS", 1, CONST_PERSISTENT | CONST_CS); +#else + REGISTER_MAIN_LONG_CONSTANT("PHP_ZTS", 0, CONST_PERSISTENT | CONST_CS); +#endif + REGISTER_MAIN_LONG_CONSTANT("PHP_DEBUG", PHP_DEBUG, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_OS", php_os, strlen(php_os), CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_SAPI", sapi_module.name, strlen(sapi_module.name), CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("DEFAULT_INCLUDE_PATH", PHP_INCLUDE_PATH, sizeof(PHP_INCLUDE_PATH)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PEAR_INSTALL_DIR", PEAR_INSTALLDIR, sizeof(PEAR_INSTALLDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PEAR_EXTENSION_DIR", PHP_EXTENSION_DIR, sizeof(PHP_EXTENSION_DIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_EXTENSION_DIR", PHP_EXTENSION_DIR, sizeof(PHP_EXTENSION_DIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_PREFIX", PHP_PREFIX, sizeof(PHP_PREFIX)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_BINDIR", PHP_BINDIR, sizeof(PHP_BINDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_LIBDIR", PHP_LIBDIR, sizeof(PHP_LIBDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_DATADIR", PHP_DATADIR, sizeof(PHP_DATADIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_SYSCONFDIR", PHP_SYSCONFDIR, sizeof(PHP_SYSCONFDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_LOCALSTATEDIR", PHP_LOCALSTATEDIR, sizeof(PHP_LOCALSTATEDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_PATH", PHP_CONFIG_FILE_PATH, strlen(PHP_CONFIG_FILE_PATH), CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_SCAN_DIR", PHP_CONFIG_FILE_SCAN_DIR, sizeof(PHP_CONFIG_FILE_SCAN_DIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_SHLIB_SUFFIX", PHP_SHLIB_SUFFIX, sizeof(PHP_SHLIB_SUFFIX)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_EOL", PHP_EOL, sizeof(PHP_EOL)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_MAXPATHLEN", MAXPATHLEN, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_INT_MAX", LONG_MAX, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_INT_SIZE", sizeof(long), CONST_PERSISTENT | CONST_CS); + +#ifdef PHP_WIN32 + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_MAJOR", EG(windows_version_info).dwMajorVersion, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_MINOR", EG(windows_version_info).dwMinorVersion, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_BUILD", EG(windows_version_info).dwBuildNumber, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_PLATFORM", EG(windows_version_info).dwPlatformId, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SP_MAJOR", EG(windows_version_info).wServicePackMajor, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SP_MINOR", EG(windows_version_info).wServicePackMinor, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SUITEMASK", EG(windows_version_info).wSuiteMask, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_PRODUCTTYPE", EG(windows_version_info).wProductType, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_DOMAIN_CONTROLLER", VER_NT_DOMAIN_CONTROLLER, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_SERVER", VER_NT_SERVER, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_WORKSTATION", VER_NT_WORKSTATION, CONST_PERSISTENT | CONST_CS); +#endif + + php_output_register_constants(TSRMLS_C); + php_rfc1867_register_constants(TSRMLS_C); + + /* this will read in php.ini, set up the configuration parameters, + load zend extensions and register php function extensions + to be loaded later */ + if (php_init_config(TSRMLS_C) == FAILURE) { + return FAILURE; + } + + /* Check for deprecated directives */ + { + static const char *directives[] = { + "define_syslog_variables", + "register_globals", + "register_long_arrays", + "safe_mode", + "magic_quotes_gpc", + "magic_quotes_runtime", + "magic_quotes_sybase", + NULL}; + const char **p = directives; + long val; + + while (*p) { + if (cfg_get_long((char*)*p, &val) == SUCCESS && val) { + zend_error(E_WARNING, "Directive '%s' is deprecated in PHP 5.3 and greater", *p); + } + ++p; + } + + /* This is not too nice, but since its the only one theres no need for extra stuff here */ + if (cfg_get_long("zend.ze1_compatibility_mode", &val) == SUCCESS && val) { + zend_error(E_ERROR, "zend.ze1_compatibility_mode is no longer supported in PHP 5.3 and greater"); + } + } + + /* Register PHP core ini entries */ + REGISTER_INI_ENTRIES(); + + /* Register Zend ini entries */ + zend_register_standard_ini_entries(TSRMLS_C); + + /* Disable realpath cache if safe_mode or open_basedir are set */ + if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) { + CWDG(realpath_cache_size_limit) = 0; + } + + /* initialize stream wrappers registry + * (this uses configuration parameters from php.ini) + */ + if (php_init_stream_wrappers(module_number TSRMLS_CC) == FAILURE) { + php_printf("PHP: Unable to initialize stream url wrappers.\n"); + return FAILURE; + } + + /* initialize registry for images to be used in phpinfo() + (this uses configuration parameters from php.ini) + */ + if (php_init_info_logos() == FAILURE) { + php_printf("PHP: Unable to initialize info phpinfo logos.\n"); + return FAILURE; + } + + zuv.html_errors = 1; + zuv.import_use_extension = ".php"; + php_startup_auto_globals(TSRMLS_C); + zend_set_utility_values(&zuv); + php_startup_sapi_content_types(TSRMLS_C); + + /* startup extensions staticly compiled in */ + if (php_register_internal_extensions_func(TSRMLS_C) == FAILURE) { + php_printf("Unable to start builtin modules\n"); + return FAILURE; + } + + /* start additional PHP extensions */ + php_register_extensions(&additional_modules, num_additional_modules TSRMLS_CC); + + /* load and startup extensions compiled as shared objects (aka DLLs) + as requested by php.ini entries + theese are loaded after initialization of internal extensions + as extensions *might* rely on things from ext/standard + which is always an internal extension and to be initialized + ahead of all other internals + */ + php_ini_register_extensions(TSRMLS_C); + zend_startup_modules(TSRMLS_C); + + /* disable certain classes and functions as requested by php.ini */ + php_disable_functions(TSRMLS_C); + php_disable_classes(TSRMLS_C); + + /* start Zend extensions */ + zend_startup_extensions(); + + /* register additional functions */ + if (sapi_module.additional_functions) { + if (zend_hash_find(&module_registry, "standard", sizeof("standard"), (void**)&module)==SUCCESS) { + EG(current_module) = module; + zend_register_functions(NULL, sapi_module.additional_functions, NULL, MODULE_PERSISTENT TSRMLS_CC); + EG(current_module) = NULL; + } + } + + /* make core report what it should */ + if (zend_hash_find(&module_registry, "core", sizeof("core"), (void**)&module)==SUCCESS) { + module->version = PHP_VERSION; + module->info_func = PHP_MINFO(php_core); + } + +#ifdef PHP_WIN32 + /* Disable incompatible functions for the running platform */ + if (php_win32_disable_functions() == FAILURE) { + php_printf("Unable to disable unsupported functions\n"); + return FAILURE; + } +#endif + +#ifdef ZTS + zend_post_startup(TSRMLS_C); +#endif + + module_initialized = 1; + sapi_deactivate(TSRMLS_C); + module_startup = 0; + + shutdown_memory_manager(1, 0 TSRMLS_CC); + + /* we're done */ + return SUCCESS; +} +/* }}} */ + +void php_module_shutdown_for_exec(void) +{ + /* used to close fd's in the range 3.255 here, but it's problematic */ +} + +/* {{{ php_module_shutdown_wrapper + */ +int php_module_shutdown_wrapper(sapi_module_struct *sapi_globals) +{ + TSRMLS_FETCH(); + php_module_shutdown(TSRMLS_C); + return SUCCESS; +} +/* }}} */ + +/* {{{ php_module_shutdown + */ +void php_module_shutdown(TSRMLS_D) +{ + int module_number=0; /* for UNREGISTER_INI_ENTRIES() */ + + module_shutdown = 1; + + if (!module_initialized) { + return; + } + +#ifdef ZTS + ts_free_worker_threads(); +#endif + +#if defined(PHP_WIN32) || (defined(NETWARE) && defined(USE_WINSOCK)) + /*close winsock */ + WSACleanup(); +#endif + + sapi_flush(TSRMLS_C); + + zend_shutdown(TSRMLS_C); + + /* Destroys filter & transport registries too */ + php_shutdown_stream_wrappers(module_number TSRMLS_CC); + + php_shutdown_info_logos(); + UNREGISTER_INI_ENTRIES(); + + /* close down the ini config */ + php_shutdown_config(); + +#ifndef ZTS + zend_ini_shutdown(TSRMLS_C); + shutdown_memory_manager(CG(unclean_shutdown), 1 TSRMLS_CC); + core_globals_dtor(&core_globals TSRMLS_CC); + gc_globals_dtor(TSRMLS_C); +#else + zend_ini_global_shutdown(TSRMLS_C); + ts_free_id(core_globals_id); +#endif + + php_shutdown_temporary_directory(); + + module_initialized = 0; + +#if defined(PHP_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1400) + if (old_invalid_parameter_handler == NULL) { + _set_invalid_parameter_handler(old_invalid_parameter_handler); + } +#endif +} +/* }}} */ + +/* {{{ php_execute_script + */ +PHPAPI int php_execute_script(zend_file_handle *primary_file TSRMLS_DC) +{ + zend_file_handle *prepend_file_p, *append_file_p; + zend_file_handle prepend_file = {0}, append_file = {0}; +#if HAVE_BROKEN_GETCWD + int old_cwd_fd = -1; +#else + char *old_cwd; + ALLOCA_FLAG(use_heap) +#endif + int retval = 0; + + EG(exit_status) = 0; + if (php_handle_special_queries(TSRMLS_C)) { + zend_file_handle_dtor(primary_file TSRMLS_CC); + return 0; + } +#ifndef HAVE_BROKEN_GETCWD +# define OLD_CWD_SIZE 4096 + old_cwd = do_alloca(OLD_CWD_SIZE, use_heap); + old_cwd[0] = '\0'; +#endif + + zend_try { + char realfile[MAXPATHLEN]; + +#ifdef PHP_WIN32 + UpdateIniFromRegistry(primary_file->filename TSRMLS_CC); +#endif + + PG(during_request_startup) = 0; + + if (primary_file->filename && !(SG(options) & SAPI_OPTION_NO_CHDIR)) { +#if HAVE_BROKEN_GETCWD + /* this looks nasty to me */ + old_cwd_fd = open(".", 0); +#else + VCWD_GETCWD(old_cwd, OLD_CWD_SIZE-1); +#endif + VCWD_CHDIR_FILE(primary_file->filename); + } + + /* Only lookup the real file path and add it to the included_files list if already opened + * otherwise it will get opened and added to the included_files list in zend_execute_scripts + */ + if (primary_file->filename && + primary_file->opened_path == NULL && + primary_file->type != ZEND_HANDLE_FILENAME + ) { + int realfile_len; + int dummy = 1; + + if (expand_filepath(primary_file->filename, realfile TSRMLS_CC)) { + realfile_len = strlen(realfile); + zend_hash_add(&EG(included_files), realfile, realfile_len+1, (void *)&dummy, sizeof(int), NULL); + primary_file->opened_path = estrndup(realfile, realfile_len); + } + } + + if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) { + prepend_file.filename = PG(auto_prepend_file); + prepend_file.opened_path = NULL; + prepend_file.free_filename = 0; + prepend_file.type = ZEND_HANDLE_FILENAME; + prepend_file_p = &prepend_file; + } else { + prepend_file_p = NULL; + } + + if (PG(auto_append_file) && PG(auto_append_file)[0]) { + append_file.filename = PG(auto_append_file); + append_file.opened_path = NULL; + append_file.free_filename = 0; + append_file.type = ZEND_HANDLE_FILENAME; + append_file_p = &append_file; + } else { + append_file_p = NULL; + } + if (PG(max_input_time) != -1) { +#ifdef PHP_WIN32 + zend_unset_timeout(TSRMLS_C); +#endif + zend_set_timeout(EG(timeout_seconds), 0); + } + retval = (zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, NULL, 3, prepend_file_p, primary_file, append_file_p) == SUCCESS); + + } zend_end_try(); + +#if HAVE_BROKEN_GETCWD + if (old_cwd_fd != -1) { + fchdir(old_cwd_fd); + close(old_cwd_fd); + } +#else + if (old_cwd[0] != '\0') { + VCWD_CHDIR(old_cwd); + } + free_alloca(old_cwd, use_heap); +#endif + return retval; +} +/* }}} */ + +/* {{{ php_execute_simple_script + */ +PHPAPI int php_execute_simple_script(zend_file_handle *primary_file, zval **ret TSRMLS_DC) +{ + char *old_cwd; + ALLOCA_FLAG(use_heap) + + EG(exit_status) = 0; +#define OLD_CWD_SIZE 4096 + old_cwd = do_alloca(OLD_CWD_SIZE, use_heap); + old_cwd[0] = '\0'; + + zend_try { +#ifdef PHP_WIN32 + UpdateIniFromRegistry(primary_file->filename TSRMLS_CC); +#endif + + PG(during_request_startup) = 0; + + if (primary_file->filename && !(SG(options) & SAPI_OPTION_NO_CHDIR)) { + VCWD_GETCWD(old_cwd, OLD_CWD_SIZE-1); + VCWD_CHDIR_FILE(primary_file->filename); + } + zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, ret, 1, primary_file); + } zend_end_try(); + + if (old_cwd[0] != '\0') { + VCWD_CHDIR(old_cwd); + } + + free_alloca(old_cwd, use_heap); + return EG(exit_status); +} +/* }}} */ + +/* {{{ php_handle_aborted_connection + */ +PHPAPI void php_handle_aborted_connection(void) +{ + TSRMLS_FETCH(); + + PG(connection_status) = PHP_CONNECTION_ABORTED; + php_output_set_status(0 TSRMLS_CC); + + if (!PG(ignore_user_abort)) { + zend_bailout(); + } +} +/* }}} */ + +/* {{{ php_handle_auth_data + */ +PHPAPI int php_handle_auth_data(const char *auth TSRMLS_DC) +{ + int ret = -1; + + if (auth && auth[0] != '\0' && strncmp(auth, "Basic ", 6) == 0) { + char *pass; + char *user; + + user = php_base64_decode(auth + 6, strlen(auth) - 6, NULL); + if (user) { + pass = strchr(user, ':'); + if (pass) { + *pass++ = '\0'; + SG(request_info).auth_user = user; + SG(request_info).auth_password = estrdup(pass); + ret = 0; + } else { + efree(user); + } + } + } + + if (ret == -1) { + SG(request_info).auth_user = SG(request_info).auth_password = NULL; + } else { + SG(request_info).auth_digest = NULL; + } + + if (ret == -1 && auth && auth[0] != '\0' && strncmp(auth, "Digest ", 7) == 0) { + SG(request_info).auth_digest = estrdup(auth + 7); + ret = 0; + } + + if (ret == -1) { + SG(request_info).auth_digest = NULL; + } + + return ret; +} +/* }}} */ + +/* {{{ php_lint_script + */ +PHPAPI int php_lint_script(zend_file_handle *file TSRMLS_DC) +{ + zend_op_array *op_array; + int retval = FAILURE; + + zend_try { + op_array = zend_compile_file(file, ZEND_INCLUDE TSRMLS_CC); + zend_destroy_file_handle(file TSRMLS_CC); + + if (op_array) { + destroy_op_array(op_array TSRMLS_CC); + efree(op_array); + retval = SUCCESS; + } + } zend_end_try(); + + return retval; +} +/* }}} */ + +#ifdef PHP_WIN32 +/* {{{ dummy_indent + just so that this symbol gets exported... */ +PHPAPI void dummy_indent(void) +{ + zend_indent(); +} +/* }}} */ +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/ea27fd_5645de_ext#fileinfo#libmagic#cdf.c b/src/main/resource/testFiles/php-src/revFiles/ea27fd_5645de_ext#fileinfo#libmagic#cdf.c new file mode 100644 index 0000000..152e6a5 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/ea27fd_5645de_ext#fileinfo#libmagic#cdf.c @@ -0,0 +1,1111 @@ +/*- + * Copyright (c) 2008 Christos Zoulas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Parse composite document files, the format used in Microsoft Office + * document files before they switched to zipped xml. + * Info from: http://sc.openoffice.org/compdocfileformat.pdf + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: cdf.c,v 1.17 2009/02/03 20:27:51 christos Exp $") +#endif + +#include +#ifdef CDF_DEBUG +#include +#endif +#include + +#ifdef PHP_WIN32 +#include "win32/unistd.h" +#else +#include +#endif + +#include +#include +#include + +#ifndef EFTYPE +#define EFTYPE EINVAL +#endif + +#include "cdf.h" + +#ifndef __arraycount +#define __arraycount(a) (sizeof(a) / sizeof(a[0])) +#endif + +#ifdef CDF_DEBUG +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif + +static union { + char s[4]; + uint32_t u; +} cdf_bo; + +#define NEED_SWAP (cdf_bo.u == (uint32_t)0x01020304) + +#define CDF_TOLE8(x) (NEED_SWAP ? cdf_tole8(x) : (uint64_t)(x)) +#define CDF_TOLE4(x) (NEED_SWAP ? cdf_tole4(x) : (uint32_t)(x)) +#define CDF_TOLE2(x) (NEED_SWAP ? cdf_tole2(x) : (uint16_t)(x)) + +/* + * swap a short + */ +uint16_t +cdf_tole2(uint16_t sv) +{ + uint16_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[1]; + d[1] = s[0]; + return rv; +} + +/* + * swap an int + */ +uint32_t +cdf_tole4(uint32_t sv) +{ + uint32_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + return rv; +} + +/* + * swap a quad + */ +uint64_t +cdf_tole8(uint64_t sv) +{ + uint64_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[7]; + d[1] = s[6]; + d[2] = s[5]; + d[3] = s[4]; + d[4] = s[3]; + d[5] = s[2]; + d[6] = s[1]; + d[7] = s[0]; + return rv; +} + +#define CDF_UNPACK(a) \ + (void)memcpy(&(a), &buf[len], sizeof(a)), len += sizeof(a) +#define CDF_UNPACKA(a) \ + (void)memcpy((a), &buf[len], sizeof(a)), len += sizeof(a) + +void +cdf_swap_header(cdf_header_t *h) +{ + size_t i; + + h->h_magic = CDF_TOLE8(h->h_magic); + h->h_uuid[0] = CDF_TOLE8(h->h_uuid[0]); + h->h_uuid[1] = CDF_TOLE8(h->h_uuid[1]); + h->h_revision = CDF_TOLE2(h->h_revision); + h->h_version = CDF_TOLE2(h->h_version); + h->h_byte_order = CDF_TOLE2(h->h_byte_order); + h->h_sec_size_p2 = CDF_TOLE2(h->h_sec_size_p2); + h->h_short_sec_size_p2 = CDF_TOLE2(h->h_short_sec_size_p2); + h->h_num_sectors_in_sat = CDF_TOLE4(h->h_num_sectors_in_sat); + h->h_secid_first_directory = CDF_TOLE4(h->h_secid_first_directory); + h->h_min_size_standard_stream = + CDF_TOLE4(h->h_min_size_standard_stream); + h->h_secid_first_sector_in_short_sat = + CDF_TOLE4(h->h_secid_first_sector_in_short_sat); + h->h_num_sectors_in_short_sat = + CDF_TOLE4(h->h_num_sectors_in_short_sat); + h->h_secid_first_sector_in_master_sat = + CDF_TOLE4(h->h_secid_first_sector_in_master_sat); + h->h_num_sectors_in_master_sat = + CDF_TOLE4(h->h_num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) + h->h_master_sat[i] = CDF_TOLE4(h->h_master_sat[i]); +} + +void +cdf_unpack_header(cdf_header_t *h, char *buf) +{ + size_t i; + size_t len = 0; + + CDF_UNPACK(h->h_magic); + CDF_UNPACKA(h->h_uuid); + CDF_UNPACK(h->h_revision); + CDF_UNPACK(h->h_version); + CDF_UNPACK(h->h_byte_order); + CDF_UNPACK(h->h_sec_size_p2); + CDF_UNPACK(h->h_short_sec_size_p2); + CDF_UNPACKA(h->h_unused0); + CDF_UNPACK(h->h_num_sectors_in_sat); + CDF_UNPACK(h->h_secid_first_directory); + CDF_UNPACKA(h->h_unused1); + CDF_UNPACK(h->h_min_size_standard_stream); + CDF_UNPACK(h->h_secid_first_sector_in_short_sat); + CDF_UNPACK(h->h_num_sectors_in_short_sat); + CDF_UNPACK(h->h_secid_first_sector_in_master_sat); + CDF_UNPACK(h->h_num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) + CDF_UNPACK(h->h_master_sat[i]); +} + +void +cdf_swap_dir(cdf_directory_t *d) +{ + d->d_namelen = CDF_TOLE2(d->d_namelen); + d->d_left_child = CDF_TOLE4(d->d_left_child); + d->d_right_child = CDF_TOLE4(d->d_right_child); + d->d_storage = CDF_TOLE4(d->d_storage); + d->d_storage_uuid[0] = CDF_TOLE8(d->d_storage_uuid[0]); + d->d_storage_uuid[1] = CDF_TOLE8(d->d_storage_uuid[1]); + d->d_flags = CDF_TOLE4(d->d_flags); + d->d_created = CDF_TOLE8(d->d_created); + d->d_modified = CDF_TOLE8(d->d_modified); + d->d_stream_first_sector = CDF_TOLE4(d->d_stream_first_sector); + d->d_size = CDF_TOLE4(d->d_size); +} + +void +cdf_swap_class(cdf_classid_t *d) +{ + d->cl_dword = CDF_TOLE4(d->cl_dword); + d->cl_word[0] = CDF_TOLE2(d->cl_word[0]); + d->cl_word[1] = CDF_TOLE2(d->cl_word[1]); +} + +void +cdf_unpack_dir(cdf_directory_t *d, char *buf) +{ + size_t len = 0; + + CDF_UNPACKA(d->d_name); + CDF_UNPACK(d->d_namelen); + CDF_UNPACK(d->d_type); + CDF_UNPACK(d->d_color); + CDF_UNPACK(d->d_left_child); + CDF_UNPACK(d->d_right_child); + CDF_UNPACK(d->d_storage); + CDF_UNPACKA(d->d_storage_uuid); + CDF_UNPACK(d->d_flags); + CDF_UNPACK(d->d_created); + CDF_UNPACK(d->d_modified); + CDF_UNPACK(d->d_stream_first_sector); + CDF_UNPACK(d->d_size); + CDF_UNPACK(d->d_unused0); +} + +int +cdf_read_header(int fd, cdf_header_t *h) +{ + char buf[512]; + (void)memcpy(cdf_bo.s, "\01\02\03\04", 4); + if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) + return -1; + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) + return -1; + cdf_unpack_header(h, buf); + cdf_swap_header(h); + if (h->h_magic != CDF_MAGIC) { + DPRINTF(("Bad magic 0x%x != 0x$x\n", h->h_magic, CDF_MAGIC)); + errno = EFTYPE; + return -1; + } + return 0; +} + + +ssize_t +cdf_read_sector(int fd, void *buf, size_t offs, size_t len, + const cdf_header_t *h, cdf_secid_t id) +{ + assert((size_t)CDF_SEC_SIZE(h) == len); + if (lseek(fd, (off_t)CDF_SEC_POS(h, id), SEEK_SET) == (off_t)-1) + return -1; + return read(fd, ((char *)buf) + offs, len); +} + +ssize_t +cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs, + size_t len, const cdf_header_t *h, cdf_secid_t id) +{ + assert((size_t)CDF_SHORT_SEC_SIZE(h) == len); + (void)memcpy(((char *)buf) + offs, + ((const char *)sst->sst_tab) + CDF_SHORT_SEC_POS(h, id), len); + return len; +} + +/* + * Read the sector allocation table. + */ +int +cdf_read_sat(int fd, cdf_header_t *h, cdf_sat_t *sat) +{ + size_t i, j, k; + size_t ss = CDF_SEC_SIZE(h); + cdf_secid_t *msa, mid; + + for (i = 0; i < __arraycount(h->h_master_sat); i++) + if (h->h_master_sat[i] == CDF_SECID_FREE) + break; + + sat->sat_len = (h->h_num_sectors_in_master_sat + i); + if ((sat->sat_tab = calloc(sat->sat_len, ss)) == NULL) + return -1; + + for (i = 0; i < __arraycount(h->h_master_sat); i++) { + if (h->h_master_sat[i] < 0) + break; + if (cdf_read_sector(fd, sat->sat_tab, ss * i, ss, h, + h->h_master_sat[i]) != (ssize_t)ss) { + DPRINTF(("Reading sector %d", h->h_master_sat[i])); + goto out1; + } + } + + if ((msa = calloc(1, ss)) == NULL) + goto out1; + + mid = h->h_secid_first_sector_in_master_sat; + for (j = 0; j < h->h_num_sectors_in_master_sat; j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Reading master sector loop limit")); + errno = EFTYPE; + goto out2; + } + if (cdf_read_sector(fd, msa, 0, ss, h, mid) != (ssize_t)ss) { + DPRINTF(("Reading master sector %d", mid)); + goto out2; + } + for (k = 0; k < (ss / sizeof(mid)) - 1; k++, i++) + if (cdf_read_sector(fd, sat->sat_tab, ss * i, ss, h, + CDF_TOLE4(msa[k])) != (ssize_t)ss) { + DPRINTF(("Reading sector %d", + CDF_TOLE4(msa[k]))); + goto out2; + } + mid = CDF_TOLE4(msa[(ss / sizeof(mid)) - 1]); + } + free(msa); + return 0; +out2: + free(msa); +out1: + free(sat->sat_tab); + return -1; +} + +size_t +cdf_count_chain(const cdf_header_t *h, const cdf_sat_t *sat, + cdf_secid_t sid) +{ + size_t i, j, s = CDF_SEC_SIZE(h) / sizeof(cdf_secid_t); + cdf_secid_t maxsector = (cdf_secid_t)(sat->sat_len * s); + + DPRINTF(("Chain:")); + for (j = i = 0; sid >= 0; i++, j++) { + DPRINTF((" %d", sid)); + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Counting chain loop limit")); + errno = EFTYPE; + return (size_t)-1; + } + if (sid > maxsector) { + DPRINTF(("Sector %d > %d\n", sid, maxsector)); + errno = EFTYPE; + return (size_t)-1; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + DPRINTF(("\n")); + return i; +} + +int +cdf_read_long_sector_chain(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + size_t ss = CDF_SEC_SIZE(h), i, j; + ssize_t nr; + scn->sst_len = cdf_count_chain(h, sat, sid); + scn->sst_dirlen = len; + + if (scn->sst_len == (size_t)-1) + return -1; + + scn->sst_tab = calloc(scn->sst_len, ss); + if (scn->sst_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if ((nr = cdf_read_sector(fd, scn->sst_tab, i * ss, ss, h, + sid)) != (ssize_t)ss) { + if (i == scn->sst_len - 1 && nr > 0) { + /* Last sector might be truncated */ + return 0; + } + DPRINTF(("Reading long sector chain %d", sid)); + goto out; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read long sector chain loop limit")); + errno = EFTYPE; + goto out; + } + } + return 0; +out: + free(scn->sst_tab); + return -1; +} + +int +cdf_read_short_sector_chain(const cdf_header_t *h, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + size_t ss = CDF_SHORT_SEC_SIZE(h), i, j; + scn->sst_len = cdf_count_chain(h, ssat, sid); + scn->sst_dirlen = len; + + if (scn->sst_len == (size_t)-1) + return -1; + + scn->sst_tab = calloc(scn->sst_len, ss); + if (scn->sst_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read short sector chain loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h, + sid) != (ssize_t)ss) { + DPRINTF(("Reading short sector chain %d", sid)); + goto out; + } + sid = CDF_TOLE4(ssat->sat_tab[sid]); + } + return 0; +out: + free(scn->sst_tab); + return -1; +} + +int +cdf_read_sector_chain(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + + if (len < h->h_min_size_standard_stream) + return cdf_read_short_sector_chain(h, ssat, sst, sid, len, + scn); + else + return cdf_read_long_sector_chain(fd, h, sat, sid, len, scn); +} + +int +cdf_read_dir(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_dir_t *dir) +{ + size_t i, j; + size_t ss = CDF_SEC_SIZE(h), ns, nd; + char *buf; + cdf_secid_t sid = h->h_secid_first_directory; + + ns = cdf_count_chain(h, sat, sid); + if (ns == (size_t)-1) + return -1; + + nd = ss / CDF_DIRECTORY_SIZE; + + dir->dir_len = ns * nd; + dir->dir_tab = calloc(dir->dir_len, sizeof(dir->dir_tab[0])); + if (dir->dir_tab == NULL) + return -1; + + if ((buf = malloc(ss)) == NULL) { + free(dir->dir_tab); + return -1; + } + + for (j = i = 0; i < ns; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read dir loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_sector(fd, buf, 0, ss, h, sid) != (ssize_t)ss) { + DPRINTF(("Reading directory sector %d", sid)); + goto out; + } + for (j = 0; j < nd; j++) { + cdf_unpack_dir(&dir->dir_tab[i * nd + j], + &buf[j * CDF_DIRECTORY_SIZE]); + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + if (NEED_SWAP) + for (i = 0; i < dir->dir_len; i++) + cdf_swap_dir(&dir->dir_tab[i]); + free(buf); + return 0; +out: + free(dir->dir_tab); + free(buf); + return -1; +} + + +int +cdf_read_ssat(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + cdf_sat_t *ssat) +{ + size_t i, j; + size_t ss = CDF_SEC_SIZE(h); + cdf_secid_t sid = h->h_secid_first_sector_in_short_sat; + + ssat->sat_len = cdf_count_chain(h, sat, sid); + if (ssat->sat_len == (size_t)-1) + return -1; + + ssat->sat_tab = calloc(ssat->sat_len, ss); + if (ssat->sat_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read short sat sector loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_sector(fd, ssat->sat_tab, i * ss, ss, h, sid) != + (ssize_t)ss) { + DPRINTF(("Reading short sat sector %d", sid)); + goto out; + } + sid = CDF_TOLE4(sat->sat_tab[sid]); + } + return 0; +out: + free(ssat->sat_tab); + return -1; +} + +int +cdf_read_short_stream(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_dir_t *dir, cdf_stream_t *scn) +{ + size_t i; + const cdf_directory_t *d; + + for (i = 0; i < dir->dir_len; i++) + if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE) + break; + + if (i == dir->dir_len) { + DPRINTF(("Cannot find root storage node\n")); + errno = EFTYPE; + return -1; + } + d = &dir->dir_tab[i]; + + /* If the it is not there, just fake it; some docs don't have it */ + if (d->d_stream_first_sector < 0) { + scn->sst_tab = NULL; + scn->sst_len = 0; + return 0; + } + + return cdf_read_long_sector_chain(fd, h, sat, + d->d_stream_first_sector, d->d_size, scn); +} + +static int +cdf_namecmp(const char *d, const uint16_t *s, size_t l) +{ + for (; l--; d++, s++) + if (*d != CDF_TOLE2(*s)) + return (unsigned char)*d - CDF_TOLE2(*s); + return 0; +} + +int +cdf_read_summary_info(int fd, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir, cdf_stream_t *scn) +{ + size_t i; + const cdf_directory_t *d; + static const char name[] = "\05SummaryInformation"; + + for (i = 0; i < dir->dir_len; i++) + if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_USER_STREAM && + cdf_namecmp(name, dir->dir_tab[i].d_name, sizeof(name)) + == 0) + break; + + if (i == dir->dir_len) { + DPRINTF(("Cannot find summary information section\n")); + errno = EFTYPE; + return -1; + } + d = &dir->dir_tab[i]; + return cdf_read_sector_chain(fd, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, scn); +} + +int +cdf_read_property_info(const cdf_stream_t *sst, uint32_t offs, + cdf_property_info_t **info, size_t *count, size_t *maxcount) +{ + const cdf_section_header_t *shp; + cdf_section_header_t sh; + const uint32_t *p, *q, *e; + int16_t s16; + int32_t s32; + uint32_t u32; + int64_t s64; + uint64_t u64; + cdf_timestamp_t tp; + size_t i, o, nelements, j; + cdf_property_info_t *inp; + + shp = (const void *)((const char *)sst->sst_tab + offs); + sh.sh_len = CDF_TOLE4(shp->sh_len); + sh.sh_properties = CDF_TOLE4(shp->sh_properties); + DPRINTF(("section len: %d properties %d\n", sh.sh_len, + sh.sh_properties)); + if (*maxcount) { + *maxcount += sh.sh_properties; + inp = realloc(*info, *maxcount * sizeof(*inp)); + } else { + *maxcount = sh.sh_properties; + inp = malloc(*maxcount * sizeof(*inp)); + } + if (inp == NULL) + goto out; + *info = inp; + inp += *count; + *count += sh.sh_properties; + p = (const void *)((const char *)sst->sst_tab + offs + sizeof(sh)); + e = (const void *)(((const char *)shp) + sh.sh_len); + for (i = 0; i < sh.sh_properties; i++) { + q = (const uint32_t *)((const char *)p + + CDF_TOLE4(p[(i << 1) + 1])) - 2; + if (q > e) { + DPRINTF(("Ran of the end %p > %p\n", q, e)); + goto out; + } + inp[i].pi_id = CDF_TOLE4(p[i << 1]); + inp[i].pi_type = CDF_TOLE4(q[0]); + DPRINTF(("%d) id=%x type=%x offs=%x\n", i, inp[i].pi_id, + inp[i].pi_type, (const char *)q - (const char *)p)); + if (inp[i].pi_type & CDF_VECTOR) { + nelements = CDF_TOLE4(q[1]); + o = 2; + } else { + nelements = 1; + o = 1; + } + if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED)) + goto unknown; + switch (inp[i].pi_type & CDF_TYPEMASK) { + case CDF_EMPTY: + break; + case CDF_SIGNED16: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s16, &q[o], sizeof(s16)); + inp[i].pi_s16 = CDF_TOLE2(s16); + break; + case CDF_SIGNED32: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s32, &q[o], sizeof(s32)); + inp[i].pi_s32 = CDF_TOLE4(s32); + break; + case CDF_BOOL: + case CDF_UNSIGNED32: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u32, &q[o], sizeof(u32)); + inp[i].pi_u32 = CDF_TOLE4(u32); + break; + case CDF_SIGNED64: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s64, &q[o], sizeof(s64)); + inp[i].pi_s64 = CDF_TOLE4(s64); + break; + case CDF_UNSIGNED64: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u64, &q[o], sizeof(u64)); + inp[i].pi_u64 = CDF_TOLE4(u64); + break; + case CDF_LENGTH32_STRING: + if (nelements > 1) { + size_t nelem = inp - *info; + *maxcount += nelements; + inp = realloc(*info, *maxcount * sizeof(*inp)); + if (inp == NULL) + goto out; + *info = inp; + inp = *info + nelem; + } + DPRINTF(("nelements = %d\n", nelements)); + for (j = 0; j < nelements; j++, i++) { + uint32_t l = CDF_TOLE4(q[o]); + inp[i].pi_str.s_len = l; + inp[i].pi_str.s_buf = (const char *)(&q[o+1]); + DPRINTF(("l = %d, r = %d, s = %s\n", l, + CDF_ROUND(l, sizeof(l)), + inp[i].pi_str.s_buf)); + l = 4 + CDF_ROUND(l, sizeof(l)); + o += l >> 2; + } + i--; + break; + case CDF_FILETIME: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&tp, &q[o], sizeof(tp)); + inp[i].pi_tp = CDF_TOLE8(tp); + break; + case CDF_CLIPBOARD: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + break; + default: + unknown: + DPRINTF(("Don't know how to deal with %x\n", + inp[i].pi_type)); + goto out; + } + } + return 0; +out: + free(*info); + return -1; +} + +int +cdf_unpack_summary_info(const cdf_stream_t *sst, cdf_summary_info_header_t *ssi, + cdf_property_info_t **info, size_t *count) +{ + size_t i, maxcount; + const cdf_summary_info_header_t *si = sst->sst_tab; + const cdf_section_declaration_t *sd = (const void *) + ((const char *)sst->sst_tab + CDF_SECTION_DECLARATION_OFFSET); + + ssi->si_byte_order = CDF_TOLE2(si->si_byte_order); + ssi->si_os_version = CDF_TOLE2(si->si_os_version); + ssi->si_os = CDF_TOLE2(si->si_os); + ssi->si_class = si->si_class; + cdf_swap_class(&ssi->si_class); + ssi->si_count = CDF_TOLE2(si->si_count); + *count = 0; + maxcount = 0; + *info = NULL; + for (i = 0; i < CDF_TOLE4(si->si_count); i++) { + if (i >= CDF_LOOP_LIMIT) { + DPRINTF(("Unpack summary info loop limit")); + errno = EFTYPE; + return -1; + } + if (cdf_read_property_info(sst, CDF_TOLE4(sd->sd_offset), + info, count, &maxcount) == -1) + return -1; + } + return 0; +} + + + +int +cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id) +{ + return snprintf(buf, buflen, "%.8x-%.4x-%.4x-%.2x%.2x-" + "%.2x%.2x%.2x%.2x%.2x%.2x", id->cl_dword, id->cl_word[0], + id->cl_word[1], id->cl_two[0], id->cl_two[1], id->cl_six[0], + id->cl_six[1], id->cl_six[2], id->cl_six[3], id->cl_six[4], + id->cl_six[5]); +} + +static const struct { + uint32_t v; + const char *n; +} vn[] = { + { CDF_PROPERTY_CODE_PAGE, "Code page" }, + { CDF_PROPERTY_TITLE, "Title" }, + { CDF_PROPERTY_SUBJECT, "Subject" }, + { CDF_PROPERTY_AUTHOR, "Author" }, + { CDF_PROPERTY_KEYWORDS, "Keywords" }, + { CDF_PROPERTY_COMMENTS, "Comments" }, + { CDF_PROPERTY_TEMPLATE, "Template" }, + { CDF_PROPERTY_LAST_SAVED_BY, "Last Saved By" }, + { CDF_PROPERTY_REVISION_NUMBER, "Revision Number" }, + { CDF_PROPERTY_TOTAL_EDITING_TIME, "Total Editing Time" }, + { CDF_PROPERTY_LAST_PRINTED, "Last Printed" }, + { CDF_PROPERTY_CREATE_TIME, "Create Time/Date" }, + { CDF_PROPERTY_LAST_SAVED_TIME, "Last Saved Time/Date" }, + { CDF_PROPERTY_NUMBER_OF_PAGES, "Number of Pages" }, + { CDF_PROPERTY_NUMBER_OF_WORDS, "Number of Words" }, + { CDF_PROPERTY_NUMBER_OF_CHARACTERS, "Number of Characters" }, + { CDF_PROPERTY_THUMBNAIL, "Thumbnail" }, + { CDF_PROPERTY_NAME_OF_APPLICATION, "Name of Creating Application" }, + { CDF_PROPERTY_SECURITY, "Security" }, + { CDF_PROPERTY_LOCALE_ID, "Locale ID" }, +}; + +int +cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p) +{ + size_t i; + + for (i = 0; i < __arraycount(vn); i++) + if (vn[i].v == p) + return snprintf(buf, bufsiz, "%s", vn[i].n); + return snprintf(buf, bufsiz, "0x%x", p); +} + +int +cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts) +{ + size_t len = 0; + int days, hours, mins, secs; + + ts /= CDF_TIME_PREC; + secs = ts % 60; + ts /= 60; + mins = ts % 60; + ts /= 60; + hours = ts % 24; + ts /= 24; + days = ts; + + if (days) { + len += snprintf(buf + len, bufsiz - len, "%dd+", days); + if (len >= bufsiz) + return len; + } + + if (days || hours) { + len += snprintf(buf + len, bufsiz - len, "%.2d:", hours); + if (len >= bufsiz) + return len; + } + + len += snprintf(buf + len, bufsiz - len, "%.2d:", mins); + if (len >= bufsiz) + return len; + + len += snprintf(buf + len, bufsiz - len, "%.2d", secs); + return len; +} + + +#ifdef CDF_DEBUG +void +cdf_dump_header(const cdf_header_t *h) +{ + size_t i; + +#define DUMP(a, b) printf("%40.40s = " a "\n", # b, h->h_ ## b) + DUMP("%d", revision); + DUMP("%d", version); + DUMP("0x%x", byte_order); + DUMP("%d", sec_size_p2); + DUMP("%d", short_sec_size_p2); + DUMP("%d", num_sectors_in_sat); + DUMP("%d", secid_first_directory); + DUMP("%d", min_size_standard_stream); + DUMP("%d", secid_first_sector_in_short_sat); + DUMP("%d", num_sectors_in_short_sat); + DUMP("%d", secid_first_sector_in_master_sat); + DUMP("%d", num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) { + if (h->h_master_sat[i] == CDF_SECID_FREE) + break; + printf("%35.35s[%.3zu] = %d\n", + "master_sat", i, h->h_master_sat[i]); + } +} + +void +cdf_dump_sat(const char *prefix, const cdf_header_t *h, const cdf_sat_t *sat) +{ + size_t i, j, s = CDF_SEC_SIZE(h) / sizeof(cdf_secid_t); + + for (i = 0; i < sat->sat_len; i++) { + printf("%s[%zu]:\n", prefix, i); + for (j = 0; j < s; j++) { + printf("%5d, ", CDF_TOLE4(sat->sat_tab[s * i + j])); + if ((j + 1) % 10 == 0) + printf("\n"); + } + printf("\n"); + } +} + +void +cdf_dump(void *v, size_t len) +{ + size_t i, j; + unsigned char *p = v; + char abuf[16]; + printf("%.4x: ", 0); + for (i = 0, j = 0; i < len; i++, p++) { + printf("%.2x ", *p); + abuf[j++] = isprint(*p) ? *p : '.'; + if (j == 16) { + j = 0; + abuf[15] = '\0'; + printf("%s\n%.4x: ", abuf, i + 1); + } + } + printf("\n"); +} + +void +cdf_dump_stream(const cdf_header_t *h, const cdf_stream_t *sst) +{ + size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? + CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); + cdf_dump(sst->sst_tab, ss * sst->sst_len); +} + +void +cdf_dump_dir(int fd, const cdf_header_t *h, const cdf_sat_t *sat, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir) +{ + size_t i, j; + cdf_directory_t *d; + char name[__arraycount(d->d_name)]; + cdf_stream_t scn; + struct timeval ts; + + static const char *types[] = { "empty", "user storage", + "user stream", "lockbytes", "property", "root storage" }; + + for (i = 0; i < dir->dir_len; i++) { + d = &dir->dir_tab[i]; + for (j = 0; j < sizeof(name); j++) + name[j] = (char)CDF_TOLE2(d->d_name[j]); + printf("Directory %zu: %s\n", i, name); + if (d->d_type < __arraycount(types)) + printf("Type: %s\n", types[d->d_type]); + else + printf("Type: %d\n", d->d_type); + printf("Color: %s\n", d->d_color ? "black" : "red"); + printf("Left child: %d\n", d->d_left_child); + printf("Right child: %d\n", d->d_right_child); + printf("Flags: 0x%x\n", d->d_flags); + cdf_timestamp_to_timespec(&ts, d->d_created); + printf("Created %s", ctime(&ts.tv_sec)); + cdf_timestamp_to_timespec(&ts, d->d_modified); + printf("Modified %s", ctime(&ts.tv_sec)); + printf("Stream %d\n", d->d_stream_first_sector); + printf("Size %d\n", d->d_size); + switch (d->d_type) { + case CDF_DIR_TYPE_USER_STORAGE: + printf("Storage: %d\n", d->d_storage); + break; + case CDF_DIR_TYPE_USER_STREAM: + if (sst == NULL) + break; + if (cdf_read_sector_chain(fd, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, &scn) == -1) { + warn("Can't read stream for %s at %d len %d", + name, d->d_stream_first_sector, d->d_size); + break; + } + cdf_dump_stream(h, &scn); + free(scn.sst_tab); + break; + default: + break; + } + + } +} + +void +cdf_dump_property_info(const cdf_property_info_t *info, size_t count) +{ + cdf_timestamp_t tp; + struct timeval ts; + char buf[64]; + size_t i; + + for (i = 0; i < count; i++) { + cdf_print_property_name(buf, sizeof(buf), info[i].pi_id); + printf("%zu) %s: ", i, buf); + switch (info[i].pi_type) { + case CDF_SIGNED16: + printf("signed 16 [%hd]\n", info[i].pi_s16); + break; + case CDF_SIGNED32: + printf("signed 32 [%d]\n", info[i].pi_s32); + break; + case CDF_UNSIGNED32: + printf("unsigned 32 [%u]\n", info[i].pi_u32); + break; + case CDF_LENGTH32_STRING: + printf("string %u [%.*s]\n", info[i].pi_str.s_len, + info[i].pi_str.s_len, info[i].pi_str.s_buf); + break; + case CDF_FILETIME: + tp = info[i].pi_tp; + if (tp < 1000000000000000LL) { + cdf_print_elapsed_time(buf, sizeof(buf), tp); + printf("timestamp %s\n", buf); + } else { + cdf_timestamp_to_timespec(&ts, tp); + printf("timestamp %s", ctime(&ts.tv_sec)); + } + break; + case CDF_CLIPBOARD: + printf("CLIPBOARD %u\n", info[i].pi_u32); + break; + default: + DPRINTF(("Don't know how to deal with %x\n", + info[i].pi_type)); + break; + } + } +} + + +void +cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst) +{ + char buf[128]; + cdf_summary_info_header_t ssi; + cdf_property_info_t *info; + size_t count; + + (void)&h; + if (cdf_unpack_summary_info(sst, &ssi, &info, &count) == -1) + return; + printf("Endian: %x\n", ssi.si_byte_order); + printf("Os Version %d.%d\n", ssi.si_os_version & 0xff, + ssi.si_os_version >> 8); + printf("Os %d\n", ssi.si_os); + cdf_print_classid(buf, sizeof(buf), &ssi.si_class); + printf("Class %s\n", buf); + printf("Count %d\n", ssi.si_count); + cdf_dump_property_info(info, count); + free(info); +} + +#endif + +#ifdef TEST +int +main(int argc, char *argv[]) +{ + int fd, i; + cdf_header_t h; + cdf_sat_t sat, ssat; + cdf_stream_t sst, scn; + cdf_dir_t dir; + + if (argc < 2) { + (void)fprintf(stderr, "Usage: %s \n", getprogname()); + return -1; + } + + for (i = 1; i < argc; i++) { + if ((fd = open(argv[1], O_RDONLY)) == -1) + err(1, "Cannot open `%s'", argv[1]); + + if (cdf_read_header(fd, &h) == -1) + err(1, "Cannot read header"); +#ifdef CDF_DEBUG + cdf_dump_header(&h); +#endif + + if (cdf_read_sat(fd, &h, &sat) == -1) + err(1, "Cannot read sat"); +#ifdef CDF_DEBUG + cdf_dump_sat("SAT", &h, &sat); +#endif + + if (cdf_read_ssat(fd, &h, &sat, &ssat) == -1) + err(1, "Cannot read ssat"); +#ifdef CDF_DEBUG + cdf_dump_sat("SSAT", &h, &ssat); +#endif + + if (cdf_read_dir(fd, &h, &sat, &dir) == -1) + err(1, "Cannot read dir"); + + if (cdf_read_short_stream(fd, &h, &sat, &dir, &sst) == -1) + err(1, "Cannot read short stream"); +#ifdef CDF_DEBUG + cdf_dump_stream(&h, &sst); +#endif + +#ifdef CDF_DEBUG + cdf_dump_dir(fd, &h, &sat, &ssat, &sst, &dir); +#endif + + + if (cdf_read_summary_info(fd, &h, &sat, &ssat, &sst, &dir, + &scn) == -1) + err(1, "Cannot read summary info"); +#ifdef CDF_DEBUG + cdf_dump_summary_info(&h, &scn); +#endif + + (void)close(fd); + } + + return 0; +} +#endif diff --git a/src/main/resource/testFiles/php-src/revFiles/f91b3d_5f1bff_ext#ereg#ereg.c b/src/main/resource/testFiles/php-src/revFiles/f91b3d_5f1bff_ext#ereg#ereg.c new file mode 100644 index 0000000..6d67f17 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/f91b3d_5f1bff_ext#ereg#ereg.c @@ -0,0 +1,655 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2002 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jim Winstead | + | Jaakko Hyvätti | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include +#include +#include "php.h" +#include "php_string.h" +#include "reg.h" +#include "ext/standard/info.h" + +#ifdef ZTS +int reg_globals_id; +#else +static php_reg_globals reg_globals; +#endif + +typedef struct { + regex_t preg; + int cflags; +} reg_cache; + +/* {{{ _php_regcomp + */ +static int _php_regcomp(regex_t *preg, const char *pattern, int cflags) +{ + int r = 0; + int patlen = strlen(pattern); + reg_cache *rc = NULL; + TSRMLS_FETCH(); + + if(zend_hash_find(®(ht_rc), (char *) pattern, patlen+1, (void **) &rc) == FAILURE || + rc->cflags != cflags) { + r = regcomp(preg, pattern, cflags); + if(!r) { + reg_cache rcp; + + rcp.cflags = cflags; + memcpy(&rcp.preg, preg, sizeof(*preg)); + zend_hash_update(®(ht_rc), (char *) pattern, patlen+1, + (void *) &rcp, sizeof(rcp), NULL); + } + } else { + memcpy(preg, &rc->preg, sizeof(*preg)); + } + + return r; +} +/* }}} */ + +static void _free_reg_cache(reg_cache *rc) +{ + regfree(&rc->preg); +} + +#undef regfree +#define regfree(a); +#undef regcomp +#define regcomp(a, b, c) _php_regcomp(a, b, c) + +static void php_reg_init_globals(php_reg_globals *reg_globals TSRMLS_DC) +{ + zend_hash_init(®_globals->ht_rc, 0, NULL, (void (*)(void *)) _free_reg_cache, 1); +} + +PHP_MINIT_FUNCTION(regex) +{ +#ifdef ZTS + ts_allocate_id(®_globals_id, sizeof(php_reg_globals), (ts_allocate_ctor) php_reg_init_globals, NULL); +#else + php_reg_init_globals(®_globals TSRMLS_CC); +#endif + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(regex) +{ + zend_hash_destroy(®(ht_rc)); + return SUCCESS; +} + +PHP_MINFO_FUNCTION(regex) +{ +#if HSREGEX + php_info_print_table_row(2, "Regex Library", "Bundled library enabled"); +#else + php_info_print_table_row(2, "Regex Library", "System library enabled"); +#endif +} + + +/* {{{ php_reg_eprint + * php_reg_eprint - convert error number to name + */ +static void php_reg_eprint(int err, regex_t *re) { + char *buf = NULL, *message = NULL; + size_t len; + size_t buf_len; + +#ifdef REG_ITOA + /* get the length of the message */ + buf_len = regerror(REG_ITOA | err, re, NULL, 0); + if (buf_len) { + buf = (char *)emalloc(buf_len * sizeof(char)); + if (!buf) return; /* fail silently */ + /* finally, get the error message */ + regerror(REG_ITOA | err, re, buf, buf_len); + } +#else + buf_len = 0; +#endif + len = regerror(err, re, NULL, 0); + if (len) { + message = (char *)emalloc((buf_len + len + 2) * sizeof(char)); + if (!message) { + return; /* fail silently */ + } + if (buf_len) { + snprintf(message, buf_len, "%s: ", buf); + buf_len += 1; /* so pointer math below works */ + } + /* drop the message into place */ + regerror(err, re, message + buf_len, len); + + php_error(E_WARNING, "%s", message); + } + + STR_FREE(buf); + STR_FREE(message); +} +/* }}} */ + +/* {{{ php_ereg + */ +static void php_ereg(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval **regex, /* Regular expression */ + **findin, /* String to apply expression to */ + **array = NULL; /* Optional register array */ + regex_t re; + regmatch_t *subs; + int err, match_len, string_len; + uint i; + int copts = 0; + off_t start, end; + char *buf = NULL; + char *string = NULL; + int argc = ZEND_NUM_ARGS(); + + if (argc < 2 || argc > 3 || + zend_get_parameters_ex(argc, ®ex, &findin, &array) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (icase) + copts |= REG_ICASE; + + if (argc == 2) + copts |= REG_NOSUB; + + /* compile the regular expression from the supplied regex */ + if (Z_TYPE_PP(regex) == IS_STRING) { + err = regcomp(&re, Z_STRVAL_PP(regex), REG_EXTENDED | copts); + } else { + /* we convert numbers to integers and treat them as a string */ + if (Z_TYPE_PP(regex) == IS_DOUBLE) + convert_to_long_ex(regex); /* get rid of decimal places */ + convert_to_string_ex(regex); + /* don't bother doing an extended regex with just a number */ + err = regcomp(&re, Z_STRVAL_PP(regex), copts); + } + + if (err) { + php_reg_eprint(err, &re); + RETURN_FALSE; + } + + /* make a copy of the string we're looking in */ + convert_to_string_ex(findin); + string = estrndup(Z_STRVAL_PP(findin), Z_STRLEN_PP(findin)); + + /* allocate storage for (sub-)expression-matches */ + subs = (regmatch_t *)ecalloc(sizeof(regmatch_t),re.re_nsub+1); + if (!subs) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg"); + RETURN_FALSE; + } + + /* actually execute the regular expression */ + err = regexec(&re, string, re.re_nsub+1, subs, 0); + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + regfree(&re); + efree(subs); + RETURN_FALSE; + } + match_len = 1; + + if (array && err != REG_NOMATCH) { + match_len = (int) (subs[0].rm_eo - subs[0].rm_so); + string_len = Z_STRLEN_PP(findin) + 1; + + buf = emalloc(string_len); + if (!buf) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg"); + regfree(&re); + efree(subs); + RETURN_FALSE; + } + + zval_dtor(*array); /* start with clean array */ + array_init(*array); + + for (i = 0; i <= re.re_nsub; i++) { + start = subs[i].rm_so; + end = subs[i].rm_eo; + if (start != -1 && end > 0 && start < string_len && end < string_len && start < end) { + add_index_stringl(*array, i, string+start, end-start, 1); + } else { + add_index_bool(*array, i, 0); + } + } + efree(buf); + } + + efree(subs); + efree(string); + if (err == REG_NOMATCH) { + RETVAL_FALSE; + } else { + if (match_len == 0) + match_len = 1; + RETVAL_LONG(match_len); + } + regfree(&re); +} +/* }}} */ + +/* {{{ proto int ereg(string pattern, string string [, array registers]) + Regular expression match */ +PHP_FUNCTION(ereg) +{ + php_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto int eregi(string pattern, string string [, array registers]) + Case-insensitive regular expression match */ +PHP_FUNCTION(eregi) +{ + php_ereg(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ php_reg_replace + * this is the meat and potatoes of regex replacement! */ +PHPAPI char *php_reg_replace(const char *pattern, const char *replace, const char *string, int icase, int extended) +{ + regex_t re; + regmatch_t *subs; + + char *buf, /* buf is where we build the replaced string */ + *nbuf, /* nbuf is used when we grow the buffer */ + *walkbuf; /* used to walk buf when replacing backrefs */ + const char *walk; /* used to walk replacement string for backrefs */ + int buf_len; + int pos, tmp, string_len, new_l; + int err, copts = 0; + + string_len = strlen(string); + + if (icase) + copts = REG_ICASE; + if (extended) + copts |= REG_EXTENDED; + + err = regcomp(&re, pattern, copts); + if (err) { + php_reg_eprint(err, &re); + return ((char *) -1); + } + + + /* allocate storage for (sub-)expression-matches */ + subs = (regmatch_t *)ecalloc(sizeof(regmatch_t),re.re_nsub+1); + if (!subs) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg_replace"); + return ((char *) -1); + } + + /* start with a buffer that is twice the size of the stringo + we're doing replacements in */ + buf_len = 2 * string_len + 1; + buf = emalloc(buf_len * sizeof(char)); + if (!buf) { + php_error(E_WARNING, "Unable to allocate memory in php_ereg_replace"); + efree(subs); + regfree(&re); + return ((char *) -1); + } + + err = pos = 0; + buf[0] = '\0'; + while (!err) { + err = regexec(&re, &string[pos], re.re_nsub+1, subs, (pos ? REG_NOTBOL : 0)); + + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + efree(subs); + efree(buf); + regfree(&re); + return ((char *) -1); + } + + if (!err) { + /* backref replacement is done in two passes: + 1) find out how long the string will be, and allocate buf + 2) copy the part before match, replacement and backrefs to buf + + Jaakko Hyvätti + */ + + new_l = strlen(buf) + subs[0].rm_so; /* part before the match */ + walk = replace; + while (*walk) + if ('\\' == *walk && isdigit(walk[1]) && walk[1] - '0' <= ((char) re.re_nsub)) { + if (subs[walk[1] - '0'].rm_so > -1 && subs[walk[1] - '0'].rm_eo > -1) { + new_l += subs[walk[1] - '0'].rm_eo - subs[walk[1] - '0'].rm_so; + } + walk += 2; + } else { + new_l++; + walk++; + } + + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + tmp = strlen(buf); + /* copy the part of the string before the match */ + strncat(buf, &string[pos], subs[0].rm_so); + + /* copy replacement and backrefs */ + walkbuf = &buf[tmp + subs[0].rm_so]; + walk = replace; + while (*walk) + if ('\\' == *walk && isdigit(walk[1]) && walk[1] - '0' <= re.re_nsub) { + if (subs[walk[1] - '0'].rm_so > -1 && subs[walk[1] - '0'].rm_eo > -1 + /* this next case shouldn't happen. it does. */ + && subs[walk[1] - '0'].rm_so <= subs[walk[1] - '0'].rm_eo) { + + tmp = subs[walk[1] - '0'].rm_eo - subs[walk[1] - '0'].rm_so; + memcpy (walkbuf, &string[pos + subs[walk[1] - '0'].rm_so], tmp); + walkbuf += tmp; + } + walk += 2; + } else { + *walkbuf++ = *walk++; + } + *walkbuf = '\0'; + + /* and get ready to keep looking for replacements */ + if (subs[0].rm_so == subs[0].rm_eo) { + if (subs[0].rm_so + pos >= string_len) + break; + new_l = strlen (buf) + 1; + if (new_l + 1 > buf_len) { + buf_len = 1 + buf_len + 2 * new_l; + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + pos += subs[0].rm_eo + 1; + buf [new_l-1] = string [pos-1]; + buf [new_l] = '\0'; + } else { + pos += subs[0].rm_eo; + } + } else { /* REG_NOMATCH */ + new_l = strlen(buf) + strlen(&string[pos]); + if (new_l + 1 > buf_len) { + buf_len = new_l + 1; /* now we know exactly how long it is */ + nbuf = emalloc(buf_len * sizeof(char)); + strcpy(nbuf, buf); + efree(buf); + buf = nbuf; + } + /* stick that last bit of string on our output */ + strcat(buf, &string[pos]); + } + } + + /* don't want to leak memory .. */ + efree(subs); + regfree(&re); + + /* whew. */ + return (buf); +} +/* }}} */ + +/* {{{ php_ereg_replace + */ +static void php_ereg_replace(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + pval **arg_pattern, + **arg_replace, + **arg_string; + char *pattern; + char *string; + char *replace; + char *ret; + + if (ZEND_NUM_ARGS() != 3 || + zend_get_parameters_ex(3, &arg_pattern, &arg_replace, &arg_string) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (Z_TYPE_PP(arg_pattern) == IS_STRING) { + if (Z_STRVAL_PP(arg_pattern) && Z_STRLEN_PP(arg_pattern)) + pattern = estrndup(Z_STRVAL_PP(arg_pattern), Z_STRLEN_PP(arg_pattern)); + else + pattern = empty_string; + } else { + convert_to_long_ex(arg_pattern); + pattern = emalloc(2); + pattern[0] = (char) Z_LVAL_PP(arg_pattern); + pattern[1] = '\0'; + } + + if (Z_TYPE_PP(arg_replace) == IS_STRING) { + if (Z_STRVAL_PP(arg_replace) && Z_STRLEN_PP(arg_replace)) + replace = estrndup(Z_STRVAL_PP(arg_replace), Z_STRLEN_PP(arg_replace)); + else + replace = empty_string; + } else { + convert_to_long_ex(arg_replace); + replace = emalloc(2); + replace[0] = (char) Z_LVAL_PP(arg_replace); + replace[1] = '\0'; + } + + convert_to_string_ex(arg_string); + if (Z_STRVAL_PP(arg_string) && Z_STRLEN_PP(arg_string)) + string = estrndup(Z_STRVAL_PP(arg_string), Z_STRLEN_PP(arg_string)); + else + string = empty_string; + + /* do the actual work */ + ret = php_reg_replace(pattern, replace, string, icase, 1); + if (ret == (char *) -1) { + RETVAL_FALSE; + } else { + RETVAL_STRING(ret, 1); + STR_FREE(ret); + } + + STR_FREE(string); + STR_FREE(replace); + STR_FREE(pattern); +} +/* }}} */ + +/* {{{ proto string ereg_replace(string pattern, string replacement, string string) + Replace regular expression */ +PHP_FUNCTION(ereg_replace) +{ + php_ereg_replace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto string eregi_replace(string pattern, string replacement, string string) + Case insensitive replace regular expression */ +PHP_FUNCTION(eregi_replace) +{ + php_ereg_replace(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ php_split + */ +static void php_split(INTERNAL_FUNCTION_PARAMETERS, int icase) +{ + zval **spliton, **str, **arg_count = NULL; + regex_t re; + regmatch_t subs[1]; + char *strp, *endp; + int err, size, count = -1, copts = 0; + int argc = ZEND_NUM_ARGS(); + + if (argc < 2 || argc > 3 || + zend_get_parameters_ex(argc, &spliton, &str, &arg_count) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (argc > 2) { + convert_to_long_ex(arg_count); + count = Z_LVAL_PP(arg_count); + } + + if (icase) + copts = REG_ICASE; + + convert_to_string_ex(spliton); + convert_to_string_ex(str); + + strp = Z_STRVAL_PP(str); + endp = strp + Z_STRLEN_PP(str); + + err = regcomp(&re, Z_STRVAL_PP(spliton), REG_EXTENDED | copts); + if (err) { + php_reg_eprint(err, &re); + RETURN_FALSE; + } + + if (array_init(return_value) == FAILURE) { + regfree(&re); + RETURN_FALSE; + } + + /* churn through str, generating array entries as we go */ + while ((count == -1 || count > 1) && !(err = regexec(&re, strp, 1, subs, 0))) { + if (subs[0].rm_so == 0 && subs[0].rm_eo) { + /* match is at start of string, return empty string */ + add_next_index_stringl(return_value, empty_string, 0, 1); + /* skip ahead the length of the regex match */ + strp += subs[0].rm_eo; + } else if (subs[0].rm_so == 0 && subs[0].rm_eo == 0) { + /* No more matches */ + regfree(&re); + php_error(E_WARNING, "Invalid Regular Expression to split()"); + zend_hash_destroy(Z_ARRVAL_P(return_value)); + efree(Z_ARRVAL_P(return_value)); + RETURN_FALSE; + } else { + /* On a real match */ + + /* make a copy of the substring */ + size = subs[0].rm_so; + + /* add it to the array */ + add_next_index_stringl(return_value, strp, size, 1); + + /* point at our new starting point */ + strp = strp + subs[0].rm_eo; + } + + /* if we're only looking for a certain number of points, + stop looking once we hit it */ + if (count != -1) { + count--; + } + } + + /* see if we encountered an error */ + if (err && err != REG_NOMATCH) { + php_reg_eprint(err, &re); + regfree(&re); + zend_hash_destroy(Z_ARRVAL_P(return_value)); + efree(Z_ARRVAL_P(return_value)); + RETURN_FALSE; + } + + /* otherwise we just have one last element to add to the array */ + size = endp - strp; + + add_next_index_stringl(return_value, strp, size, 1); + + regfree(&re); +} +/* }}} */ + +/* {{{ proto array split(string pattern, string string [, int limit]) + Split string into array by regular expression */ +PHP_FUNCTION(split) +{ + php_split(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto array spliti(string pattern, string string [, int limit]) + Split string into array by regular expression case-insensitive */ + +PHP_FUNCTION(spliti) +{ + php_split(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} + +/* }}} */ + +/* {{{ proto string sql_regcase(string string) + Make regular expression for case insensitive match */ +PHPAPI PHP_FUNCTION(sql_regcase) +{ + zval **string; + char *tmp; + unsigned char c; + register int i, j; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &string)==FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_string_ex(string); + + tmp = emalloc((Z_STRLEN_PP(string) * 4) + 1); + + for (i = j = 0; i < Z_STRLEN_PP(string); i++) { + c = (unsigned char) Z_STRVAL_PP(string)[i]; + if(isalpha(c)) { + tmp[j++] = '['; + tmp[j++] = toupper(c); + tmp[j++] = tolower(c); + tmp[j++] = ']'; + } else { + tmp[j++] = c; + } + } + tmp[j] = 0; + + RETVAL_STRINGL(tmp, j, 1); + efree(tmp); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/wireshark/prevFiles/prev_02dff2_2fba8c_epan#dissectors#packet-usb.c b/src/main/resource/testFiles/wireshark/prevFiles/prev_02dff2_2fba8c_epan#dissectors#packet-usb.c new file mode 100644 index 0000000..4588e5f --- /dev/null +++ b/src/main/resource/testFiles/wireshark/prevFiles/prev_02dff2_2fba8c_epan#dissectors#packet-usb.c @@ -0,0 +1,3495 @@ +/* packet-usb.c + * + * $Id$ + * + * USB basic dissector + * By Paolo Abeni + * Ronnie Sahlberg 2006 + * + * http://www.usb.org/developers/docs/usb_20_122909-2.zip + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include "config.h" + +#include +#include "isprint.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "packet-usb.h" +#include "packet-usb-hid.h" + +/* protocols and header fields */ +static int proto_usb = -1; + +/* Linux USB pseudoheader fields */ +static int hf_usb_urb_id = -1; +static int hf_usb_urb_type = -1; +static int hf_usb_transfer_type = -1; +static int hf_usb_endpoint_number = -1; +static int hf_usb_endpoint_direction = -1; +static int hf_usb_endpoint_number_value = -1; +static int hf_usb_device_address = -1; +static int hf_usb_bus_id = -1; +static int hf_usb_setup_flag = -1; +static int hf_usb_data_flag = -1; +static int hf_usb_urb_ts_sec = -1; +static int hf_usb_urb_ts_usec = -1; +static int hf_usb_urb_status = -1; +static int hf_usb_urb_len = -1; +static int hf_usb_urb_data_len = -1; + +static int hf_usb_request = -1; +static int hf_usb_request_unknown_class = -1; +static int hf_usb_value = -1; +static int hf_usb_index = -1; +static int hf_usb_length = -1; +/* static int hf_usb_data_len = -1; */ +static int hf_usb_capdata = -1; +static int hf_usb_wFeatureSelector = -1; +static int hf_usb_wInterface = -1; +static int hf_usb_wStatus = -1; +static int hf_usb_wFrameNumber = -1; + +static int hf_usb_iso_error_count = -1; +static int hf_usb_iso_numdesc = -1; +static int hf_usb_iso_status = -1; +static int hf_usb_iso_off = -1; +static int hf_usb_iso_len = -1; +static int hf_usb_iso_pad = -1; +static int hf_usb_iso_data = -1; + +static int hf_usb_bmRequestType = -1; +static int hf_usb_bmRequestType_direction = -1; +static int hf_usb_bmRequestType_type = -1; +static int hf_usb_bmRequestType_recipient = -1; +static int hf_usb_bDescriptorType = -1; +static int hf_usb_descriptor_index = -1; +static int hf_usb_language_id = -1; +static int hf_usb_bLength = -1; +static int hf_usb_bcdUSB = -1; +static int hf_usb_bDeviceClass = -1; +static int hf_usb_bDeviceSubClass = -1; +static int hf_usb_bDeviceProtocol = -1; +static int hf_usb_bMaxPacketSize0 = -1; +static int hf_usb_idVendor = -1; +static int hf_usb_idProduct = -1; +static int hf_usb_bcdDevice = -1; +static int hf_usb_iManufacturer = -1; +static int hf_usb_iProduct = -1; +static int hf_usb_iSerialNumber = -1; +static int hf_usb_bNumConfigurations = -1; +static int hf_usb_wLANGID = -1; +static int hf_usb_bString = -1; +static int hf_usb_bInterfaceNumber = -1; +static int hf_usb_bAlternateSetting = -1; +static int hf_usb_bNumEndpoints = -1; +static int hf_usb_bInterfaceClass = -1; +static int hf_usb_bInterfaceSubClass = -1; +static int hf_usb_bInterfaceProtocol = -1; +static int hf_usb_iInterface = -1; +static int hf_usb_bEndpointAddress = -1; +static int hf_usb_bmAttributes = -1; +static int hf_usb_bEndpointAttributeTransfer = -1; +static int hf_usb_bEndpointAttributeSynchonisation = -1; +static int hf_usb_bEndpointAttributeBehaviour = -1; +static int hf_usb_wMaxPacketSize = -1; +static int hf_usb_wMaxPacketSize_size = -1; +static int hf_usb_wMaxPacketSize_slots = -1; +static int hf_usb_bInterval = -1; +static int hf_usb_wTotalLength = -1; +static int hf_usb_bNumInterfaces = -1; +static int hf_usb_bConfigurationValue = -1; +static int hf_usb_iConfiguration = -1; +static int hf_usb_bMaxPower = -1; +static int hf_usb_configuration_bmAttributes = -1; +static int hf_usb_configuration_legacy10buspowered = -1; +static int hf_usb_configuration_selfpowered = -1; +static int hf_usb_configuration_remotewakeup = -1; +static int hf_usb_bEndpointAddress_direction = -1; +static int hf_usb_bEndpointAddress_number = -1; +static int hf_usb_response_in = -1; +static int hf_usb_time = -1; +static int hf_usb_request_in = -1; +static int hf_usb_bFirstInterface = -1; +static int hf_usb_bInterfaceCount = -1; +static int hf_usb_bFunctionClass = -1; +static int hf_usb_bFunctionSubClass = -1; +static int hf_usb_bFunctionProtocol = -1; +static int hf_usb_iFunction = -1; + +static gint usb_hdr = -1; +static gint usb_setup_hdr = -1; +static gint usb_isodesc = -1; +static gint ett_usb_endpoint = -1; +static gint ett_usb_setup_bmrequesttype = -1; +static gint ett_descriptor_device = -1; +static gint ett_configuration_bmAttributes = -1; +static gint ett_configuration_bEndpointAddress = -1; +static gint ett_endpoint_bmAttributes = -1; +static gint ett_endpoint_wMaxPacketSize = -1; + +static const int *usb_endpoint_fields[] = { + &hf_usb_endpoint_direction, + &hf_usb_endpoint_number_value, + NULL +}; + +static int usb_tap = -1; +static gboolean try_heuristics = TRUE; + +static dissector_table_t usb_bulk_dissector_table; +static dissector_table_t usb_control_dissector_table; +static dissector_table_t usb_interrupt_dissector_table; +static dissector_table_t usb_descriptor_dissector_table; + +static heur_dissector_list_t heur_bulk_subdissector_list; +static heur_dissector_list_t heur_control_subdissector_list; +static heur_dissector_list_t heur_interrupt_subdissector_list; + +static emem_tree_t *device_to_protocol_table = NULL; +static emem_tree_t *device_to_product_table = NULL; + +static dissector_table_t device_to_dissector; +static dissector_table_t protocol_to_dissector; +static dissector_table_t product_to_dissector; + +typedef struct _device_product_data_t { + guint16 vendor; + guint16 product; + guint bus_id; + guint device_address; +} device_product_data_t; + +typedef struct _device_protocol_data_t { + guint32 protocol; + guint bus_id; + guint device_address; +} device_protocol_data_t; + + +/* http://www.usb.org/developers/docs/USB_LANGIDs.pdf */ +static const value_string usb_langid_vals[] = { + {0x0000, "no language specified"}, + {0x0401, "Arabic (Saudi Arabia)"}, + {0x0402, "Bulgarian"}, + {0x0403, "Catalan"}, + {0x0404, "Chinese (Taiwan)"}, + {0x0405, "Czech"}, + {0x0406, "Danish"}, + {0x0407, "German (Standard)"}, + {0x0408, "Greek"}, + {0x0409, "English (United States)"}, + {0x040a, "Spanish (Traditional Sort)"}, + {0x040b, "Finnish"}, + {0x040c, "French (Standard)"}, + {0x040d, "Hebrew"}, + {0x040e, "Hungarian"}, + {0x040f, "Icelandic"}, + {0x0410, "Italian (Standard)"}, + {0x0411, "Japanese"}, + {0x0412, "Korean"}, + {0x0413, "Dutch (Netherlands)"}, + {0x0414, "Norwegian (Bokmal)"}, + {0x0415, "Polish"}, + {0x0416, "Portuguese (Brazil)"}, + {0x0418, "Romanian"}, + {0x0419, "Russian"}, + {0x041a, "Croatian"}, + {0x041b, "Slovak"}, + {0x041c, "Albanian"}, + {0x041d, "Swedish"}, + {0x041e, "Thai"}, + {0x041f, "Turkish"}, + {0x0420, "Urdu (Pakistan)"}, + {0x0421, "Indonesian"}, + {0x0422, "Ukrainian"}, + {0x0423, "Belarussian"}, + {0x0424, "Slovenian"}, + {0x0425, "Estonian"}, + {0x0426, "Latvian"}, + {0x0427, "Lithuanian"}, + {0x0429, "Farsi"}, + {0x042a, "Vietnamese"}, + {0x042b, "Armenian"}, + {0x042c, "Azeri (Latin)"}, + {0x042d, "Basque"}, + {0x042f, "Macedonian"}, + {0x0430, "Sutu"}, + {0x0436, "Afrikaans"}, + {0x0437, "Georgian"}, + {0x0438, "Faeroese"}, + {0x0439, "Hindi"}, + {0x043e, "Malay (Malaysian)"}, + {0x043f, "Kazakh"}, + {0x0441, "Swahili (Kenya)"}, + {0x0443, "Uzbek (Latin)"}, + {0x0444, "Tatar (Tatarstan)"}, + {0x0445, "Bengali"}, + {0x0446, "Punjabi"}, + {0x0447, "Gujarati"}, + {0x0448, "Oriya"}, + {0x0449, "Tamil"}, + {0x044a, "Telugu"}, + {0x044b, "Kannada"}, + {0x044c, "Malayalam"}, + {0x044d, "Assamese"}, + {0x044e, "Marathi"}, + {0x044f, "Sanskrit"}, + {0x0455, "Burmese"}, + {0x0457, "Konkani"}, + {0x0458, "Manipuri"}, + {0x0459, "Sindhi"}, + {0x04ff, "HID (Usage Data Descriptor)"}, + {0x0801, "Arabic (Iraq)"}, + {0x0804, "Chinese (PRC)"}, + {0x0807, "German (Switzerland)"}, + {0x0809, "English (United Kingdom)"}, + {0x080a, "Spanish (Mexican)"}, + {0x080c, "French (Belgian)"}, + {0x0810, "Italian (Switzerland)"}, + {0x0812, "Korean (Johab)"}, + {0x0813, "Dutch (Belgium)"}, + {0x0814, "Norwegian (Nynorsk)"}, + {0x0816, "Portuguese (Standard)"}, + {0x081a, "Serbian (Latin)"}, + {0x081d, "Swedish (Finland)"}, + {0x0820, "Urdu (India)"}, + {0x0827, "Lithuanian (Classic)"}, + {0x082c, "Azeri (Cyrillic)"}, + {0x083e, "Malay (Brunei Darussalam)"}, + {0x0843, "Uzbek (Cyrillic)"}, + {0x0860, "Kashmiri (India)"}, + {0x0861, "Nepali (India)"}, + {0x0c01, "Arabic (Egypt)"}, + {0x0c04, "Chinese (Hong Kong SAR, PRC)"}, + {0x0c07, "German (Austria)"}, + {0x0c09, "English (Australian)"}, + {0x0c0a, "Spanish (Modern Sort)"}, + {0x0c0c, "French (Canadian)"}, + {0x0c1a, "Serbian (Cyrillic)"}, + {0x1001, "Arabic (Libya)"}, + {0x1004, "Chinese (Singapore)"}, + {0x1007, "German (Luxembourg)"}, + {0x1009, "English (Canadian)"}, + {0x100a, "Spanish (Guatemala)"}, + {0x100c, "French (Switzerland)"}, + {0x1401, "Arabic (Algeria)"}, + {0x1404, "Chinese (Macau SAR)"}, + {0x1407, "German (Liechtenstein)"}, + {0x1409, "English (New Zealand)"}, + {0x140a, "Spanish (Costa Rica)"}, + {0x140c, "French (Luxembourg)"}, + {0x1801, "Arabic (Morocco)"}, + {0x1809, "English (Ireland)"}, + {0x180a, "Spanish (Panama)"}, + {0x180c, "French (Monaco)"}, + {0x1c01, "Arabic (Tunisia)"}, + {0x1c09, "English (South Africa)"}, + {0x1c0a, "Spanish (Dominican Republic)"}, + {0x2001, "Arabic (Oman)"}, + {0x2009, "English (Jamaica)"}, + {0x200a, "Spanish (Venezuela)"}, + {0x2401, "Arabic (Yemen)"}, + {0x2409, "English (Caribbean)"}, + {0x240a, "Spanish (Colombia)"}, + {0x2801, "Arabic (Syria)"}, + {0x2809, "English (Belize)"}, + {0x280a, "Spanish (Peru)"}, + {0x2c01, "Arabic (Jordan)"}, + {0x2c09, "English (Trinidad)"}, + {0x2c0a, "Spanish (Argentina)"}, + {0x3001, "Arabic (Lebanon)"}, + {0x3009, "English (Zimbabwe)"}, + {0x300a, "Spanish (Ecuador)"}, + {0x3401, "Arabic (Kuwait)"}, + {0x3409, "English (Philippines)"}, + {0x340a, "Spanish (Chile)"}, + {0x3801, "Arabic (U.A.E.)"}, + {0x380a, "Spanish (Uruguay)"}, + {0x3c01, "Arabic (Bahrain)"}, + {0x3c0a, "Spanish (Paraguay)"}, + {0x4001, "Arabic (Qatar)"}, + {0x400a, "Spanish (Bolivia)"}, + {0x440a, "Spanish (El Salvador)"}, + {0x480a, "Spanish (Honduras)"}, + {0x4c0a, "Spanish (Nicaragua)"}, + {0x500a, "Spanish (Puerto Rico)"}, + {0xf0ff, "HID (Vendor Defined 1)"}, + {0xf4ff, "HID (Vendor Defined 2)"}, + {0xf8ff, "HID (Vendor Defined 3)"}, + {0xfcff, "HID (Vendor Defined 4)"}, + {0, NULL} +}; +static value_string_ext usb_langid_vals_ext = VALUE_STRING_EXT_INIT(usb_langid_vals); + +static const value_string usb_class_vals[] = { + {IF_CLASS_DEVICE, "Device"}, + {IF_CLASS_AUDIO, "Audio"}, + {IF_CLASS_COMMUNICATIONS, "Communications and CDC Control"}, + {IF_CLASS_HID, "HID"}, + {IF_CLASS_PHYSICAL, "Physical"}, + {IF_CLASS_IMAGE, "Imaging"}, + {IF_CLASS_PRINTER, "Printer"}, + {IF_CLASS_MASS_STORAGE, "Mass Storage"}, + {IF_CLASS_HUB, "Hub"}, + {IF_CLASS_CDC_DATA, "CDC-Data"}, + {IF_CLASS_SMART_CARD, "Smart Card"}, + {IF_CLASS_CONTENT_SECURITY, "Content Security"}, + {IF_CLASS_VIDEO, "Video"}, + {IF_CLASS_PERSONAL_HEALTHCARE, "Personal Healthcare"}, + {IF_CLASS_AUDIO_VIDEO, "Audio/Video Devices"}, + {IF_CLASS_DIAGNOSTIC_DEVICE, "Diagnostic Device"}, + {IF_CLASS_WIRELESS_CONTROLLER, "Wireless Controller"}, + {IF_CLASS_MISCELLANEOUS, "Miscellaneous"}, + {IF_CLASS_APPLICATION_SPECIFIC, "Application Specific"}, + {IF_CLASS_VENDOR_SPECIFIC, "Vendor Specific"}, + {0, NULL} +}; +static value_string_ext usb_class_vals_ext = VALUE_STRING_EXT_INIT(usb_class_vals); + +/* use usb class, subclass and protocol id together + http://www.usb.org/developers/defined_class*/ +static const value_string usb_protocols[] = { + {0x000000, "Use class code info from Interface Descriptors"}, + {0x060101, "Still Imaging"}, + {0x090000, "Full speed Hub"}, + {0x090001, "Hi-speed hub with single TT"}, + {0x090002, "Hi-speed hub with multiple TTs"}, + {0x0D0000, "Content Security"}, + {0x100100, "AVControl Interface"}, + {0x100200, "AVData Video Streaming Interface"}, + {0x100300, "AVData Audio Streaming Interface"}, + {0xDC0101, "USB2 Compliance Device"}, + {0xE00101, "Bluetooth Programming Interface"}, + {0xE00102, "UWB Radio Control Interface"}, + {0xE00103, "Remote NDIS"}, + {0xE00104, "Bluetooth AMP Controller"}, + {0xE00201, "Host Wire Adapter Control/Data interface"}, + {0xE00202, "Device Wire Adapter Control/Data interface"}, + {0xE00203, "Device Wire Adapter Isochronous interface"}, + {0xEF0101, "Active Sync device"}, + {0xEF0102, "Palm Sync"}, + {0xEF0201, "Interface Association Descriptor"}, + {0xEF0202, "Wire Adapter Multifunction Peripheral programming interface"}, + {0xEF0301, "Cable Based Association Framework"}, + {0xFE0101, "Device Firmware Upgrade"}, + {0xFE0200, "IRDA Bridge device"}, + {0xFE0300, "USB Test and Measurement Device"}, + {0xFE0301, "USB Test and Measurement Device conforming to the USBTMC USB488"}, + {0, NULL} +}; + +static const value_string usb_transfer_type_vals[] = { + {URB_CONTROL, "URB_CONTROL"}, + {URB_ISOCHRONOUS, "URB_ISOCHRONOUS"}, + {URB_INTERRUPT, "URB_INTERRUPT"}, + {URB_BULK, "URB_BULK"}, + {0, NULL} +}; + +static const value_string usb_transfer_type_and_direction_vals[] = { + {URB_CONTROL, "URB_CONTROL out"}, + {URB_ISOCHRONOUS, "URB_ISOCHRONOUS out"}, + {URB_INTERRUPT, "URB_INTERRUPT out"}, + {URB_BULK, "URB_BULK out"}, + {URB_CONTROL | URB_TRANSFER_IN, "URB_CONTROL in"}, + {URB_ISOCHRONOUS | URB_TRANSFER_IN, "URB_ISOCHRONOUS in"}, + {URB_INTERRUPT | URB_TRANSFER_IN, "URB_INTERRUPT in"}, + {URB_BULK | URB_TRANSFER_IN, "URB_BULK in"}, + {0, NULL} +}; + +static const value_string usb_endpoint_direction_vals[] = { + {0, "OUT"}, + {1, "IN"}, + {0, NULL} +}; + +static const value_string usb_urb_type_vals[] = { + {URB_SUBMIT, "URB_SUBMIT"}, + {URB_COMPLETE, "URB_COMPLETE"}, + {URB_ERROR, "URB_ERROR"}, + {0, NULL} +}; + +extern value_string_ext ext_usb_vendors_vals; +extern value_string_ext ext_usb_products_vals; + +/* + * Descriptor types. + */ +#define USB_DT_DEVICE 1 +#define USB_DT_CONFIG 2 +#define USB_DT_STRING 3 +#define USB_DT_INTERFACE 4 +#define USB_DT_ENDPOINT 5 +#define USB_DT_DEVICE_QUALIFIER 6 +#define USB_DT_OTHER_SPEED_CONFIG 7 +#define USB_DT_INTERFACE_POWER 8 +/* these are from a minor usb 2.0 revision (ECN) */ +#define USB_DT_OTG 9 +#define USB_DT_DEBUG 10 +#define USB_DT_INTERFACE_ASSOCIATION 11 +/* these are from the Wireless USB spec */ +#define USB_DT_SECURITY 12 +#define USB_DT_KEY 13 +#define USB_DT_ENCRYPTION_TYPE 14 +#define USB_DT_BOS 15 +#define USB_DT_DEVICE_CAPABILITY 16 +#define USB_DT_WIRELESS_ENDPOINT_COMP 17 +#define USB_DT_HID 33 +#define USB_DT_RPIPE 34 +#define USB_DT_SUPERSPEED_ENDPOINT_COMPANION 48 + +static const value_string descriptor_type_vals[] = { + {USB_DT_DEVICE, "DEVICE"}, + {USB_DT_CONFIG, "CONFIGURATION"}, + {USB_DT_STRING, "STRING"}, + {USB_DT_INTERFACE, "INTERFACE"}, + {USB_DT_ENDPOINT, "ENDPOINT"}, + {USB_DT_DEVICE_QUALIFIER, "DEVICE QUALIFIER"}, + {USB_DT_OTHER_SPEED_CONFIG, "OTHER_SPEED CONFIG"}, + {USB_DT_INTERFACE_POWER, "INTERFACE POWER"}, + {USB_DT_OTG, "OTG"}, + {USB_DT_DEBUG, "DEBUG"}, + {USB_DT_INTERFACE_ASSOCIATION, "INTERFACE ASSOCIATION"}, + {USB_DT_SECURITY, "SECURITY"}, + {USB_DT_KEY, "KEY"}, + {USB_DT_ENCRYPTION_TYPE, "ENCRYPTION TYPE"}, + {USB_DT_BOS, "BOS"}, + {USB_DT_DEVICE_CAPABILITY, "DEVICE CAPABILITY"}, + {USB_DT_WIRELESS_ENDPOINT_COMP, "WIRELESS ENDPOINT COMP"}, + {USB_DT_HID, "HID"}, + {USB_DT_RPIPE, "RPIPE"}, + {USB_DT_SUPERSPEED_ENDPOINT_COMPANION, "SUPERSPEED USB ENDPOINT COMPANION"}, + {0,NULL} +}; +static value_string_ext descriptor_type_vals_ext = VALUE_STRING_EXT_INIT(descriptor_type_vals); + +/* + * Feature selectors. + */ +#define USB_FS_ENDPOINT_HALT 0 +#define USB_FS_DEVICE_REMOTE_WAKEUP 1 +#define USB_FS_TEST_MODE 2 + +static const value_string usb_feature_selector_vals[] = { + {USB_FS_ENDPOINT_HALT, "ENDPOINT HALT"}, + {USB_FS_DEVICE_REMOTE_WAKEUP, "DEVICE REMOTE WAKEUP"}, + {USB_FS_TEST_MODE, "TEST MODE"}, + {0, NULL} +}; + +static const value_string usb_bmAttributes_transfer_vals[] = { + {0x00, "Control-Transfer"}, + {0x01, "Isochronous-Transfer"}, + {0x02, "Bulk-Transfer"}, + {0x03, "Interrupt-Transfer"}, + {0, NULL} +}; + +static const value_string usb_bmAttributes_sync_vals[] = { + {0x00, "No Sync"}, + {0x01, "Asynchronous"}, + {0x02, "Adaptive"}, + {0x03, "Synchronous"}, + {0, NULL} +}; + +static const value_string usb_bmAttributes_behaviour_vals[] = { + {0x00, "Data-Endpoint"}, + {0x01, "Explicit Feedback-Endpoint"}, + {0x02, "Implicit Feedback-Data-Endpoint"}, + {0x03, "Reserved"}, + {0, NULL} +}; + +static const value_string usb_wMaxPacketSize_slots_vals[] = { + {0x00, "1"}, + {0x01, "2"}, + {0x02, "3"}, + {0x03, "Reserved"}, + {0, NULL} +}; + +/* from linux/include/asm-generic/errno.h */ +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ + + +/* from linux/include/asm-generic/errno.h*/ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ +#define ECANCELED 125 /* Operation Canceled */ +#define ENOKEY 126 /* Required key not available */ +#define EKEYEXPIRED 127 /* Key has expired */ +#define EKEYREVOKED 128 /* Key has been revoked */ +#define EKEYREJECTED 129 /* Key was rejected by service */ + +/* for robust mutexes */ +#define EOWNERDEAD 130 /* Owner died */ +#define ENOTRECOVERABLE 131 /* State not recoverable */ + + +/* Note: sorted in (unsigned) ascending order */ +static const value_string usb_urb_status_vals[] = { + { -ENOTRECOVERABLE, "State not recoverable (-ENOTRECOVERABLE)" }, + { -EOWNERDEAD, "Owner died (-EOWNERDEAD)" }, + { -EKEYREJECTED, "Key was rejected by service (-EKEYREJECTED)" }, + { -EKEYREVOKED, "Key has been revoked (-EKEYREVOKED)" }, + { -EKEYEXPIRED, "Key has expired (-EKEYEXPIRED)" }, + { -ENOKEY, "Required key not available (-ENOKEY)" }, + { -ECANCELED, "Operation Canceled (-ECANCELED)" }, + { -EMEDIUMTYPE, "Wrong medium type (-EMEDIUMTYPE)" }, + { -ENOMEDIUM, "No medium found (-ENOMEDIUM)" }, + { -EDQUOT, "Quota exceeded (-EDQUOT)" }, + { -EREMOTEIO, "Remote I/O error (-EREMOTEIO)" }, + { -EISNAM, "Is a named type file (-EISNAM)" }, + { -ENAVAIL, "No XENIX semaphores available (-ENAVAIL)" }, + { -ENOTNAM, "Not a XENIX named type file (-ENOTNAM)" }, + { -EUCLEAN, "Structure needs cleaning (-EUCLEAN)" }, + { -ESTALE, "Stale NFS file handle (-ESTALE)" }, + { -EINPROGRESS, "Operation now in progress (-EINPROGRESS)" }, + { -EALREADY, "Operation already in progress (-EALREADY)" }, + { -EHOSTUNREACH, "No route to host (-EHOSTUNREACH)" }, + { -EHOSTDOWN, "Host is down (-EHOSTDOWN)" }, + { -ECONNREFUSED, "Connection refused (-ECONNREFUSED)" }, + { -ETIMEDOUT, "Connection timed out (-ETIMEDOUT)" }, + { -ETOOMANYREFS, "Too many references: cannot splice (-ETOOMANYREFS)" }, + { -ESHUTDOWN, "Cannot send after transport endpoint shutdown (-ESHUTDOWN)" }, + { -ENOTCONN, "Transport endpoint is not connected (-ENOTCONN)" }, + { -EISCONN, "Transport endpoint is already connected (-EISCONN)" }, + { -ENOBUFS, "No buffer space available (-ENOBUFS)" }, + { -ECONNRESET, "Connection reset by peer (-ECONNRESET)" }, + { -ECONNABORTED, "Software caused connection abort (-ECONNABORTED)" }, + { -ENETRESET, "Network dropped connection because of reset (-ENETRESET)" }, + { -ENETUNREACH, "Network is unreachable (-ENETUNREACH)" }, + { -ENETDOWN, "Network is down (-ENETDOWN)" }, + { -EADDRNOTAVAIL, "Cannot assign requested address (-EADDRNOTAVAIL)" }, + { -EADDRINUSE, "Address already in use (-EADDRINUSE)" }, + { -EAFNOSUPPORT, "Address family not supported by protocol (-EAFNOSUPPORT)" }, + { -EPFNOSUPPORT, "Protocol family not supported (-EPFNOSUPPORT)" }, + { -EOPNOTSUPP, "Operation not supported on transport endpoint (-EOPNOTSUPP)" }, + { -ESOCKTNOSUPPORT, "Socket type not supported (-ESOCKTNOSUPPORT)" }, + { -EPROTONOSUPPORT, "Protocol not supported (-EPROTONOSUPPORT)" }, + { -ENOPROTOOPT, "Protocol not available (-ENOPROTOOPT)" }, + { -EPROTOTYPE, "Protocol wrong type for socket (-EPROTOTYPE)" }, + { -EMSGSIZE, "Message too long (-EMSGSIZE)" }, + { -EDESTADDRREQ, "Destination address required (-EDESTADDRREQ)" }, + { -ENOTSOCK, "Socket operation on non-socket (-ENOTSOCK)" }, + { -EUSERS, "Too many users (-EUSERS)" }, + { -ESTRPIPE, "Streams pipe error (-ESTRPIPE)" }, + { -ERESTART, "Interrupted system call should be restarted (-ERESTART)" }, + { -EILSEQ, "Illegal byte sequence (-EILSEQ)" }, + { -ELIBEXEC, "Cannot exec a shared library directly (-ELIBEXEC)" }, + { -ELIBMAX, "Attempting to link in too many shared libraries (-ELIBMAX)" }, + { -ELIBSCN, ".lib section in a.out corrupted (-ELIBSCN)" }, + { -ELIBBAD, "Accessing a corrupted shared library (-ELIBBAD)" }, + { -ELIBACC, "Can not access a needed shared library (-ELIBACC)" }, + { -EREMCHG, "Remote address changed (-EREMCHG)" }, + { -EBADFD, "File descriptor in bad state (-EBADFD)" }, + { -ENOTUNIQ, "Name not unique on network (-ENOTUNIQ)" }, + { -EOVERFLOW, "Value too large for defined data type (-EOVERFLOW)" }, + { -EBADMSG, "Not a data message (-EBADMSG)" }, + { -EDOTDOT, "RFS specific error (-EDOTDOT)" }, + { -EMULTIHOP, "Multihop attempted (-EMULTIHOP)" }, + { -EPROTO, "Protocol error (-EPROTO)" }, + { -ECOMM, "Communication error on send (-ECOMM)" }, + { -ESRMNT, "Srmount error (-ESRMNT)" }, + { -EADV, "Advertise error (-EADV)" }, + { -ENOLINK, "Link has been severed (-ENOLINK)" }, + { -EREMOTE, "Object is remote (-EREMOTE)" }, + { -ENOPKG, "Package not installed (-ENOPKG)" }, + { -ENONET, "Machine is not on the network (-ENONET)" }, + { -ENOSR, "Out of streams resources (-ENOSR)" }, + { -ETIME, "Timer expired (-ETIME)" }, + { -ENODATA, "No data available (-ENODATA)" }, + { -ENOSTR, "Device not a stream (-ENOSTR)" }, + { -EBFONT, "Bad font file format (-EBFONT)" }, + { -58, "(-58 \?\?\?)" }, /* dummy so that there are no "gaps" */ + { -EBADSLT, "Invalid slot (-EBADSLT)" }, + { -EBADRQC, "Invalid request code (-EBADRQC)" }, + { -ENOANO, "No anode (-ENOANO)" }, + { -EXFULL, "Exchange full (-EXFULL)" }, + { -EBADR, "Invalid request descriptor (-EBADR)" }, + { -EBADE, "Invalid exchange (-EBADE)" }, + { -EL2HLT, "Level 2 halted (-EL2HLT)" }, + { -ENOCSI, "No CSI structure available (-ENOCSI)" }, + { -EUNATCH, "Protocol driver not attached (-EUNATCH)" }, + { -ELNRNG, "Link number out of range (-ELNRNG)" }, + { -EL3RST, "Level 3 reset (-EL3RST)" }, + { -EL3HLT, "Level 3 halted (-EL3HLT)" }, + { -EL2NSYNC, "Level 2 not synchronized (-EL2NSYNC)" }, + { -ECHRNG, "Channel number out of range (-ECHRNG)" }, + { -EIDRM, "Identifier removed (-EIDRM)" }, + { -ENOMSG, "No message of desired type (-ENOMSG)" }, + { -41, "(-41 \?\?\?)" }, /* dummy so that there are no "gaps" */ + { -ELOOP, "Too many symbolic links encountered (-ELOOP)" }, + { -ENOTEMPTY, "Directory not empty (-ENOTEMPTY)" }, + { -ENOSYS, "Function not implemented (-ENOSYS)" }, + { -ENOLCK, "No record locks available (-ENOLCK)" }, + { -ENAMETOOLONG, "File name too long (-ENAMETOOLONG)" }, + { -EDEADLK, "Resource deadlock would occur (-EDEADLK)" }, + { -ERANGE, "Math result not representable (-ERANGE)" }, + { -EDOM, "Math argument out of domain of func (-EDOM)" }, + { -EPIPE, "Broken pipe (-EPIPE)" }, + { -EMLINK, "Too many links (-EMLINK)" }, + { -EROFS, "Read-only file system (-EROFS)" }, + { -ESPIPE, "Illegal seek (-ESPIPE)" }, + { -ENOSPC, "No space left on device (-ENOSPC)" }, + { -EFBIG, "File too large (-EFBIG)" }, + { -ETXTBSY, "Text file busy (-ETXTBSY)" }, + { -ENOTTY, "Not a typewriter (-ENOTTY)" }, + { -EMFILE, "Too many open files (-EMFILE)" }, + { -ENFILE, "File table overflow (-ENFILE)" }, + { -EINVAL, "Invalid argument (-EINVAL)" }, + { -EISDIR, "Is a directory (-EISDIR)" }, + { -ENOTDIR, "Not a directory (-ENOTDIR)" }, + { -ENODEV, "No such device (-ENODEV)" }, + { -EXDEV, "Cross-device link (-EXDEV)" }, + { -EEXIST, "File exists (-EEXIST)" }, + { -EBUSY, "Device or resource busy (-EBUSY)" }, + { -ENOTBLK, "Block device required (-ENOTBLK)" }, + { -EFAULT, "Bad address (-EFAULT)" }, + { -EACCES, "Permission denied (-EACCES)" }, + { -ENOMEM, "Out of memory (-ENOMEM)" }, + { -EAGAIN, "Try again (-EAGAIN)" }, + { -ECHILD, "No child processes (-ECHILD)" }, + { -EBADF, "Bad file number (-EBADF)" }, + { -ENOEXEC, "Exec format error (-ENOEXEC)" }, + { -E2BIG, "Argument list too long (-E2BIG)" }, + { -ENXIO, "No such device or address (-ENXIO)" }, + { -EIO, "I/O error (-EIO)" }, + { -EINTR, "Interrupted system call (-EINTR)" }, + { -ESRCH, "No such process (-ESRCH)" }, + { -ENOENT, "No such file or directory (-ENOENT)" }, + { -EPERM, "Operation not permitted (-EPERM)" }, + { 0, "Success"}, + { 0, NULL } +}; +static value_string_ext usb_urb_status_vals_ext = VALUE_STRING_EXT_INIT(usb_urb_status_vals); + + +static usb_conv_info_t * +get_usb_conv_info(conversation_t *conversation) +{ + usb_conv_info_t *usb_conv_info; + + /* do we have conversation specific data ? */ + usb_conv_info = (usb_conv_info_t *)conversation_get_proto_data(conversation, proto_usb); + if (!usb_conv_info) { + /* no not yet so create some */ + usb_conv_info = se_new0(usb_conv_info_t); + usb_conv_info->interfaceClass = IF_CLASS_UNKNOWN; + usb_conv_info->interfaceSubclass = IF_SUBCLASS_UNKNOWN; + usb_conv_info->interfaceProtocol = IF_PROTOCOL_UNKNOWN; + usb_conv_info->deviceVendor = DEV_VENDOR_UNKNOWN; + usb_conv_info->deviceProduct = DEV_PRODUCT_UNKNOWN; + usb_conv_info->transactions = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "usb transactions"); + + conversation_add_proto_data(conversation, proto_usb, usb_conv_info); + } + + return usb_conv_info; +} + +static conversation_t * +get_usb_conversation(packet_info *pinfo, + address *src_addr, address *dst_addr, + guint32 src_endpoint, guint32 dst_endpoint) +{ + conversation_t *conversation; + + /* + * Do we have a conversation for this connection? + */ + conversation = find_conversation(pinfo->fd->num, + src_addr, dst_addr, + pinfo->ptype, + src_endpoint, dst_endpoint, 0); + if (conversation) { + return conversation; + } + + /* We don't yet have a conversation, so create one. */ + conversation = conversation_new(pinfo->fd->num, + src_addr, dst_addr, + pinfo->ptype, + src_endpoint, dst_endpoint, 0); + return conversation; +} + +/* Fetch or create usb_conv_info for a specified interface. */ +usb_conv_info_t * +get_usb_iface_conv_info(packet_info *pinfo, guint8 interface_num) +{ + conversation_t *conversation; + guint32 if_port; + + if_port = htolel(INTERFACE_PORT | interface_num); + + if (pinfo->srcport == NO_ENDPOINT) { + conversation = get_usb_conversation(pinfo, &pinfo->src, &pinfo->dst, pinfo->srcport, if_port); + } else { + conversation = get_usb_conversation(pinfo, &pinfo->src, &pinfo->dst, if_port, pinfo->destport); + } + + return get_usb_conv_info(conversation); +} + + +/* SETUP dissectors */ + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / CLEAR FEATURE + */ + + +/* 9.4.1 */ +static int +dissect_usb_setup_clear_feature_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* feature selector */ + proto_tree_add_item(tree, hf_usb_wFeatureSelector, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero/interface/endpoint */ + /* XXX - check based on request type */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* length */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_clear_feature_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / GET CONFIGURATION + */ + + +/* 9.4.2 */ +static int +dissect_usb_setup_get_configuration_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + proto_tree_add_item(tree, hf_usb_bConfigurationValue, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / GET DESCRIPTOR + */ + +void dissect_usb_descriptor_header(proto_tree *tree, + tvbuff_t *tvb, int offset) +{ + /* bLength */ + proto_tree_add_item(tree, + hf_usb_bLength, + tvb, offset, 1, ENC_LITTLE_ENDIAN); + + /* bDescriptorType */ + proto_tree_add_item(tree, hf_usb_bDescriptorType, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); +} + +/* 9.6.2 */ +static int +dissect_usb_device_qualifier_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id, guint device_address) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + proto_item *nitem = NULL; + int old_offset = offset; + guint32 protocol; + const gchar *description; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "DEVICE QUALIFIER DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + /* bcdUSB */ + proto_tree_add_item(tree, hf_usb_bcdUSB, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + protocol = tvb_get_ntoh24(tvb, offset); + description = val_to_str_const(protocol, usb_protocols, ""); + + /* bDeviceClass */ + proto_tree_add_item(tree, hf_usb_bDeviceClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bDeviceSubClass */ + proto_tree_add_item(tree, hf_usb_bDeviceSubClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bDeviceProtocol */ + nitem = proto_tree_add_item(tree, hf_usb_bDeviceProtocol, tvb, offset, 1, ENC_LITTLE_ENDIAN); + if (*description) + proto_item_append_text(nitem, " (%s)", description); + offset += 1; + + if (!pinfo->fd->flags.visited) { + guint k_bus_id; + guint k_device_address; + guint k_frame_number; + emem_tree_key_t key[4]; + device_protocol_data_t *device_protocol_data; + + k_frame_number = pinfo->fd->num; + k_device_address = device_address; + k_bus_id = bus_id; + + key[0].length = 1; + key[0].key = &k_device_address; + key[1].length = 1; + key[1].key = &k_bus_id; + key[2].length = 1; + key[2].key = &k_frame_number; + key[3].length = 0; + key[3].key = NULL; + + device_protocol_data = se_new(device_protocol_data_t); + device_protocol_data->protocol = protocol; + device_protocol_data->bus_id = bus_id; + device_protocol_data->device_address = device_address; + se_tree_insert32_array(device_to_protocol_table, key, device_protocol_data); + } + + /* bMaxPacketSize0 */ + proto_tree_add_item(tree, hf_usb_bMaxPacketSize0, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bNumConfigurations */ + proto_tree_add_item(tree, hf_usb_bNumConfigurations, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* one reserved byte */ + offset += 1; + + if (item) { + proto_item_set_len(item, offset-old_offset); + } + + return offset; +} + +/* 9.6.1 */ +static int +dissect_usb_device_descriptor(packet_info *pinfo, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + guint bus_id, guint device_address) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + proto_item *nitem = NULL; + int old_offset = offset; + guint32 protocol; + const gchar *description; + guint16 vendor_id; + guint32 product; + guint16 product_id; + usb_conv_info_t *usb_conv_info; + guint8 *field_description; + gint field_description_length; + header_field_info *hfi; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "DEVICE DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + usb_conv_info=(usb_conv_info_t *)pinfo->usb_conv_info; + + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + /* bcdUSB */ + proto_tree_add_item(tree, hf_usb_bcdUSB, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + protocol = tvb_get_ntoh24(tvb, offset); + description = val_to_str_const(protocol, usb_protocols, ""); + + /* bDeviceClass */ + proto_tree_add_item(tree, hf_usb_bDeviceClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bDeviceSubClass */ + proto_tree_add_item(tree, hf_usb_bDeviceSubClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bDeviceProtocol */ + nitem = proto_tree_add_item(tree, hf_usb_bDeviceProtocol, tvb, offset, 1, ENC_LITTLE_ENDIAN); + if (*description) + proto_item_append_text(nitem, " (%s)", description); + offset += 1; + + /* bMaxPacketSize0 */ + proto_tree_add_item(tree, hf_usb_bMaxPacketSize0, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* idVendor */ + proto_tree_add_item(tree, hf_usb_idVendor, tvb, offset, 2, ENC_LITTLE_ENDIAN); + vendor_id = tvb_get_letohs(tvb, offset); + usb_conv_info->deviceVendor = vendor_id; + offset += 2; + + /* idProduct */ + nitem = proto_tree_add_item(tree, hf_usb_idProduct, tvb, offset, 2, ENC_LITTLE_ENDIAN); + product_id = tvb_get_letohs(tvb, offset); + usb_conv_info->deviceProduct = product_id; + product = vendor_id << 16 | product_id; + + hfi = proto_registrar_get_nth(hf_usb_idProduct); + field_description_length = (gint)strlen(hfi->name) + 14; + field_description = (guint8 *)ep_alloc(field_description_length); + g_strlcpy(field_description, hfi->name, field_description_length); + g_strlcat(field_description, ": %s (0x%04x)", field_description_length); + + proto_item_set_text(nitem, field_description, + val_to_str_ext_const(product, &ext_usb_products_vals, "Unknown"), + product_id); + offset += 2; + + if (!pinfo->fd->flags.visited) { + guint k_bus_id; + guint k_device_address; + guint k_frame_number; + emem_tree_key_t key[4]; + device_product_data_t *device_product_data; + device_protocol_data_t *device_protocol_data; + + k_frame_number = pinfo->fd->num; + k_device_address = device_address; + k_bus_id = bus_id; + + key[0].length = 1; + key[0].key = &k_device_address; + key[1].length = 1; + key[1].key = &k_bus_id; + key[2].length = 1; + key[2].key = &k_frame_number; + key[3].length = 0; + key[3].key = NULL; + + device_product_data = se_new(device_product_data_t); + device_product_data->vendor = vendor_id; + device_product_data->product = product_id; + device_product_data->bus_id = bus_id; + device_product_data->device_address = device_address; + se_tree_insert32_array(device_to_product_table, key, device_product_data); + + device_protocol_data = se_new(device_protocol_data_t); + device_protocol_data->protocol = protocol; + device_protocol_data->bus_id = bus_id; + device_protocol_data->device_address = device_address; + + se_tree_insert32_array(device_to_protocol_table, key, device_protocol_data); + } + + /* bcdDevice */ + proto_tree_add_item(tree, hf_usb_bcdDevice, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* iManufacturer */ + proto_tree_add_item(tree, hf_usb_iManufacturer, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* iProduct */ + proto_tree_add_item(tree, hf_usb_iProduct, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* iSerialNumber */ + proto_tree_add_item(tree, hf_usb_iSerialNumber, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bNumConfigurations */ + proto_tree_add_item(tree, hf_usb_bNumConfigurations, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + if (item) { + proto_item_set_len(item, offset-old_offset); + } + + return offset; +} + +/* 9.6.7 */ +static int +dissect_usb_string_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info _U_) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + int old_offset = offset; + guint8 len; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "STRING DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + len = tvb_get_guint8(tvb, offset); + /* The USB spec says that the languages / the string are UTF16 and not + 0-terminated, i.e. the length field must contain an even number */ + if (len & 0x1) { + proto_item *len_item; + + /* bLength */ + len_item = proto_tree_add_item(tree, hf_usb_bLength, tvb, offset, 1, ENC_LITTLE_ENDIAN); + expert_add_info_format(pinfo, len_item, PI_PROTOCOL, PI_WARN, + "Invalid STRING DESCRIPTOR Length (must be even)"); + + /* bDescriptorType */ + proto_tree_add_item(tree, hf_usb_bDescriptorType, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); + } + else + dissect_usb_descriptor_header(tree, tvb, offset); + + offset += 2; + + if (!usb_trans_info->u.get_descriptor.index) { + /* list of languanges */ + while(len>(offset-old_offset)) { + /* wLANGID */ + proto_tree_add_item(tree, hf_usb_wLANGID, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset+=2; + } + } else { + char *str; + guint8 str_len; + + /* Make sure that tvb_get_ephemeral_unicode_string() gets an even + string length even if the length field contains an (invalid) + odd number. + */ + str_len = (len-2) & ~0x1; + + /* unicode string */ + str = tvb_get_ephemeral_unicode_string(tvb, offset, str_len, ENC_LITTLE_ENDIAN); + proto_tree_add_string(tree, hf_usb_bString, tvb, offset, len-2, str); + offset += len-2; + } + + if (item) { + proto_item_set_len(item, offset-old_offset); + } + + return offset; +} + + + +/* 9.6.5 */ +static int +dissect_usb_interface_descriptor(packet_info *pinfo, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + const char *class_str = NULL; + int old_offset = offset; + guint8 len; + guint8 interface_num; + guint8 alt_setting; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "INTERFACE DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + len = tvb_get_guint8(tvb, offset); + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + /* bInterfaceNumber */ + interface_num = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_usb_bInterfaceNumber, tvb, offset, 1, ENC_LITTLE_ENDIAN); + usb_conv_info->interfaceNum = interface_num; + offset += 1; + + /* bAlternateSetting */ + alt_setting = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_usb_bAlternateSetting, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bNumEndpoints */ + proto_tree_add_item(tree, hf_usb_bNumEndpoints, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bInterfaceClass */ + proto_tree_add_item(tree, hf_usb_bInterfaceClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + /* save the class so we can access it later in the endpoint descriptor */ + usb_conv_info->interfaceClass = tvb_get_guint8(tvb, offset); + + class_str = val_to_str_ext(usb_conv_info->interfaceClass, &usb_class_vals_ext, "unknown (0x%X)"); + proto_item_append_text(item, " (%u.%u): class %s", interface_num, alt_setting, class_str); + + if (!pinfo->fd->flags.visited && (alt_setting == 0)) { + /* Register conversation for this interface in case CONTROL messages are sent to it */ + usb_trans_info->interface_info = get_usb_iface_conv_info(pinfo, interface_num); + usb_trans_info->interface_info->interfaceClass = tvb_get_guint8(tvb, offset); + /* save information useful to class-specific dissectors */ + usb_trans_info->interface_info->interfaceSubclass = tvb_get_guint8(tvb, offset+1); + usb_trans_info->interface_info->interfaceProtocol = tvb_get_guint8(tvb, offset+2); + usb_trans_info->interface_info->deviceVendor = usb_conv_info->deviceVendor; + usb_trans_info->interface_info->deviceProduct = usb_conv_info->deviceProduct; + } + offset += 1; + + /* bInterfaceSubClass */ + proto_tree_add_item(tree, hf_usb_bInterfaceSubClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + /* save the subclass so we can access it later in class-specific descriptors */ + usb_conv_info->interfaceSubclass = tvb_get_guint8(tvb, offset); + offset += 1; + + /* bInterfaceProtocol */ + proto_tree_add_item(tree, hf_usb_bInterfaceProtocol, tvb, offset, 1, ENC_LITTLE_ENDIAN); + usb_conv_info->interfaceProtocol = tvb_get_guint8(tvb, offset); + offset += 1; + + /* iInterface */ + proto_tree_add_item(tree, hf_usb_iInterface, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + if (item) { + proto_item_set_len(item, len); + } + if (offset < old_offset+len) { + /* skip unknown records */ + offset = old_offset + len; + } + + return offset; +} + +/* 9.6.6 */ +static const true_false_string tfs_endpoint_direction = { + "IN Endpoint", + "OUT Endpoint" +}; + +void dissect_usb_endpoint_address(proto_tree *tree, tvbuff_t *tvb, int offset) +{ + proto_item *endpoint_item = NULL; + proto_tree *endpoint_tree = NULL; + guint8 endpoint; + + if (tree) { + endpoint_item = proto_tree_add_item(tree, hf_usb_bEndpointAddress, tvb, offset, 1, ENC_LITTLE_ENDIAN); + endpoint_tree = proto_item_add_subtree(endpoint_item, ett_configuration_bEndpointAddress); + } + endpoint = tvb_get_guint8(tvb, offset)&0x0f; + proto_tree_add_item(endpoint_tree, hf_usb_bEndpointAddress_direction, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_item_append_text(endpoint_item, " %s", (tvb_get_guint8(tvb, offset)&0x80)?"IN":"OUT"); + proto_tree_add_item(endpoint_tree, hf_usb_bEndpointAddress_number, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_item_append_text(endpoint_item, " Endpoint:%d", endpoint); +} + +static int +dissect_usb_endpoint_descriptor(packet_info *pinfo, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + proto_item *ep_attrib_item = NULL; + proto_tree *ep_attrib_tree = NULL; + proto_item *ep_pktsize_item; + proto_tree *ep_pktsize_tree; + int old_offset = offset; + guint8 endpoint; + guint8 ep_type; + guint8 len; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "ENDPOINT DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + len = tvb_get_guint8(tvb, offset); + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + endpoint = tvb_get_guint8(tvb, offset)&0x0f; + dissect_usb_endpoint_address(tree, tvb, offset); + offset += 1; + + /* Together with class from the interface descriptor we know what kind + * of class the device at endpoint is. + * Make sure a conversation exists for this endpoint and attach a + * usb_conv_into_t structure to it. + * + * All endpoints for the same interface descriptor share the same + * usb_conv_info structure. + */ + if ((!pinfo->fd->flags.visited)&&usb_trans_info->interface_info) { + conversation_t *conversation; + + if (pinfo->destport == NO_ENDPOINT) { + static address tmp_addr; + static usb_address_t usb_addr; + + /* Create a new address structure that points to the same device + * but the new endpoint. + */ + usb_addr.device = ((usb_address_t *)(pinfo->src.data))->device; + usb_addr.endpoint = htolel(endpoint); + SET_ADDRESS(&tmp_addr, AT_USB, USB_ADDR_LEN, (char *)&usb_addr); + conversation = get_usb_conversation(pinfo, &tmp_addr, &pinfo->dst, usb_addr.endpoint, pinfo->destport); + } else { + static address tmp_addr; + static usb_address_t usb_addr; + + /* Create a new address structure that points to the same device + * but the new endpoint. + */ + usb_addr.device = ((usb_address_t *)(pinfo->dst.data))->device; + usb_addr.endpoint = htolel(endpoint); + SET_ADDRESS(&tmp_addr, AT_USB, USB_ADDR_LEN, (char *)&usb_addr); + conversation = get_usb_conversation(pinfo, &pinfo->src, &tmp_addr, pinfo->srcport, usb_addr.endpoint); + } + + conversation_add_proto_data(conversation, proto_usb, usb_trans_info->interface_info); + } + + /* bmAttributes */ + ep_type = ENDPOINT_TYPE(tvb_get_guint8(tvb, offset)); + if (tree) { + ep_attrib_item = proto_tree_add_item(tree, hf_usb_bmAttributes, tvb, offset, 1, ENC_LITTLE_ENDIAN); + ep_attrib_tree = proto_item_add_subtree(ep_attrib_item, ett_endpoint_bmAttributes); + } + proto_tree_add_item(ep_attrib_tree, hf_usb_bEndpointAttributeTransfer, tvb, offset, 1, ENC_LITTLE_ENDIAN); + /* isochronous only */ + proto_tree_add_item(ep_attrib_tree, hf_usb_bEndpointAttributeSynchonisation, tvb, offset, 1, ENC_LITTLE_ENDIAN); + /* isochronous only */ + proto_tree_add_item(ep_attrib_tree, hf_usb_bEndpointAttributeBehaviour, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* wMaxPacketSize */ + ep_pktsize_item = proto_tree_add_item(tree, hf_usb_wMaxPacketSize, tvb, offset, 2, ENC_LITTLE_ENDIAN); + ep_pktsize_tree = proto_item_add_subtree(ep_pktsize_item, ett_endpoint_wMaxPacketSize); + if ((ep_type == ENDPOINT_TYPE_INTERRUPT) || (ep_type == ENDPOINT_TYPE_ISOCHRONOUS)) { + proto_tree_add_item(ep_pktsize_tree, hf_usb_wMaxPacketSize_slots, tvb, offset, 2, ENC_LITTLE_ENDIAN); + } + proto_tree_add_item(ep_pktsize_tree, hf_usb_wMaxPacketSize_size, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset+=2; + + /* bInterval */ + proto_tree_add_item(tree, hf_usb_bInterval, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + if (item) { + proto_item_set_len(item, len); + } + if (offset < old_offset+len) { + /* skip unknown records */ + offset = old_offset + len; + } + + return offset; +} + +/* ECN */ +static int +dissect_usb_interface_assn_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + int old_offset = offset; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "INTERFACE ASSOCIATION DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + /* bFirstInterface */ + proto_tree_add_item(tree, hf_usb_bFirstInterface, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bInterfaceCount */ + proto_tree_add_item(tree, hf_usb_bInterfaceCount, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bFunctionClass */ + proto_tree_add_item(tree, hf_usb_bFunctionClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bFunctionSubclass */ + proto_tree_add_item(tree, hf_usb_bFunctionSubClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bFunctionProtocol */ + proto_tree_add_item(tree, hf_usb_bFunctionProtocol, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* iFunction */ + proto_tree_add_item(tree, hf_usb_iFunction, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + if (item) { + proto_item_set_len(item, offset-old_offset); + } + + return offset; +} + +static int +dissect_usb_unknown_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + guint8 bLength; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "UNKNOWN DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + bLength = tvb_get_guint8(tvb, offset); + dissect_usb_descriptor_header(tree, tvb, offset); + offset += bLength; + + if (item) { + proto_item_set_len(item, bLength); + } + + return offset; +} + +/* 9.6.3 */ +static const true_false_string tfs_mustbeone = { + "Must be 1 for USB 1.1 and higher", + "FIXME: Is this a USB 1.0 device" +}; +static const true_false_string tfs_selfpowered = { + "This device is SELF-POWERED", + "This device is powered from the USB bus" +}; +static const true_false_string tfs_remotewakeup = { + "This device supports REMOTE WAKEUP", + "This device does NOT support remote wakeup" +}; +static int +dissect_usb_configuration_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + int old_offset = offset; + guint16 len; + proto_item *flags_item = NULL; + proto_tree *flags_tree = NULL; + guint8 flags; + proto_item *power_item; + guint8 power; + gboolean truncation_expected; + + usb_conv_info->interfaceClass = IF_CLASS_UNKNOWN; + usb_conv_info->interfaceSubclass = IF_SUBCLASS_UNKNOWN; + usb_conv_info->interfaceProtocol = IF_PROTOCOL_UNKNOWN; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "CONFIGURATION DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + /* wTotalLength */ + proto_tree_add_item(tree, hf_usb_wTotalLength, tvb, offset, 2, ENC_LITTLE_ENDIAN); + len = tvb_get_letohs(tvb, offset); + offset+=2; + + /* bNumInterfaces */ + proto_tree_add_item(tree, hf_usb_bNumInterfaces, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bConfigurationValue */ + proto_tree_add_item(tree, hf_usb_bConfigurationValue, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* iConfiguration */ + proto_tree_add_item(tree, hf_usb_iConfiguration, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bmAttributes */ + if (tree) { + flags_item = proto_tree_add_item(tree, hf_usb_configuration_bmAttributes, tvb, offset, 1, ENC_LITTLE_ENDIAN); + flags_tree = proto_item_add_subtree(flags_item, ett_configuration_bmAttributes); + } + flags = tvb_get_guint8(tvb, offset); + proto_tree_add_item(flags_tree, hf_usb_configuration_legacy10buspowered, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(flags_tree, hf_usb_configuration_selfpowered, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_item_append_text(flags_item, " %sSELF-POWERED", (flags&0x40)?"":"NOT "); + proto_tree_add_item(flags_tree, hf_usb_configuration_remotewakeup, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_item_append_text(flags_item, " %sREMOTE-WAKEUP", (flags&0x20)?"":"NO "); + offset += 1; + + /* bMaxPower */ + power_item = proto_tree_add_item(tree, hf_usb_bMaxPower, tvb, offset, 1, ENC_LITTLE_ENDIAN); + power = tvb_get_guint8(tvb, offset); + proto_item_append_text(power_item, " (%dmA)", power*2); + offset += 1; + + /* initialize interface_info to NULL */ + usb_trans_info->interface_info = NULL; + + truncation_expected = (usb_trans_info->setup.wLength < len); + + /* decode any additional interface and endpoint descriptors */ + while(len>(offset-old_offset)) { + guint8 next_type; + guint8 next_len = 0; + gint remaining_tvb, remaining_len; + tvbuff_t *next_tvb = NULL; + + /* Handle truncated descriptors appropriately */ + remaining_tvb = tvb_length_remaining(tvb, offset); + if (remaining_tvb > 0) { + next_len = tvb_get_guint8(tvb, offset); + remaining_len = len - (offset - old_offset); + if ((next_len < 3) || (next_len > remaining_len)) { + item = proto_tree_add_text(parent_tree, tvb, offset, 1, + "Invalid descriptor length: %u", next_len); + proto_item_set_len(item, 1); + expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR, + "Invalid descriptor length: %u", next_len); + item = NULL; + break; + } + } + + if ((remaining_tvb == 0) || (next_len > remaining_tvb)) { + if (!truncation_expected) { + THROW(ReportedBoundsError); + } + break; + } + + next_type = tvb_get_guint8(tvb, offset+1); + switch(next_type) { + case USB_DT_INTERFACE: + offset = dissect_usb_interface_descriptor(pinfo, parent_tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_ENDPOINT: + offset = dissect_usb_endpoint_descriptor(pinfo, parent_tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_INTERFACE_ASSOCIATION: + offset = dissect_usb_interface_assn_descriptor(pinfo, parent_tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + default: + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (dissector_try_uint(usb_descriptor_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, parent_tree)) { + offset += next_len; + } else { + offset = dissect_usb_unknown_descriptor(pinfo, parent_tree, tvb, offset, usb_trans_info, usb_conv_info); + } + break; + /* was: return offset; */ + } + } + + if (item) { + proto_item_set_len(item, offset-old_offset); + } + + /* Clear any class association from the Control endpoint. + * We need the association temporarily, to establish + * context for class-specific descriptor dissectors, + * but the association must not persist beyond this function. + * If it did, all traffic on the Control endpoint would be labeled + * as belonging to the class of the last INTERFACE descriptor, + * which would be especially inappropriate for composite devices. + */ + usb_conv_info->interfaceClass = IF_CLASS_UNKNOWN; + usb_conv_info->interfaceSubclass = IF_SUBCLASS_UNKNOWN; + usb_conv_info->interfaceProtocol = IF_PROTOCOL_UNKNOWN; + + return offset; +} + +/* 9.4.3 */ +static int +dissect_usb_setup_get_descriptor_request(packet_info *pinfo, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* descriptor index */ + proto_tree_add_item(tree, hf_usb_descriptor_index, tvb, offset, 1, ENC_LITTLE_ENDIAN); + usb_trans_info->u.get_descriptor.index = tvb_get_guint8(tvb, offset); + offset += 1; + + /* descriptor type */ + proto_tree_add_item(tree, hf_usb_bDescriptorType, tvb, offset, 1, ENC_LITTLE_ENDIAN); + usb_trans_info->u.get_descriptor.type = tvb_get_guint8(tvb, offset); + offset += 1; + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, " %s", + val_to_str_ext(usb_trans_info->u.get_descriptor.type, &descriptor_type_vals_ext, "Unknown type %u")); + } + + /* language id */ + proto_tree_add_item(tree, hf_usb_language_id, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset+=2; + + /* length */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_get_descriptor_response(packet_info *pinfo, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info, + guint bus_id, guint device_address) +{ + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, " %s", + val_to_str_ext(usb_trans_info->u.get_descriptor.type, &descriptor_type_vals_ext, "Unknown type %u")); + } + switch(usb_trans_info->u.get_descriptor.type) { + case USB_DT_DEVICE: + offset = dissect_usb_device_descriptor(pinfo, tree, tvb, offset, usb_trans_info, bus_id, device_address); + break; + case USB_DT_CONFIG: + offset = dissect_usb_configuration_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_STRING: + offset = dissect_usb_string_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_INTERFACE: + offset = dissect_usb_interface_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_ENDPOINT: + offset = dissect_usb_endpoint_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_DEVICE_QUALIFIER: + offset = dissect_usb_device_qualifier_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info, bus_id, device_address); + break; + case USB_DT_RPIPE: + if (usb_conv_info->interfaceClass == IF_CLASS_HID) { + offset = dissect_usb_hid_get_report_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + } + /* else fall through as default/unknown */ + default: + /* XXX dissect the descriptor coming back from the device */ + proto_tree_add_text(tree, tvb, offset, -1, "GET DESCRIPTOR data (unknown descriptor type %u)", usb_trans_info->u.get_descriptor.type); + offset = tvb_length(tvb); + break; + } + + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / GET INTERFACE + */ + + +/* 9.4.4 */ +static int +dissect_usb_setup_get_interface_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* zero */ + proto_tree_add_item(tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* interface */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* length */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_get_interface_response(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* alternate setting */ + proto_tree_add_item(tree, hf_usb_bAlternateSetting, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / GET STATUS + */ + + +/* 9.4.5 */ +static int +dissect_usb_setup_get_status_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* zero */ + proto_tree_add_item(tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero/interface/endpoint */ + /* XXX - check based on request type */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* length */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_get_status_response(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* status */ + /* XXX - show bits */ + proto_tree_add_item(tree, hf_usb_wStatus, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / SET ADDRESS + */ + + +/* 9.4.6 */ +static int +dissect_usb_setup_set_address_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* device address */ + proto_tree_add_item(tree, hf_usb_device_address, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_set_address_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / SET CONFIGURATION + */ + + +/* 9.4.7 */ +static int +dissect_usb_setup_set_configuration_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* configuration value */ + proto_tree_add_item(tree, hf_usb_bConfigurationValue, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_set_configuration_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / SET FEATURE + */ + + +/* 9.4.9 */ +static int +dissect_usb_setup_set_feature_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* feature selector */ + proto_tree_add_item(tree, hf_usb_wFeatureSelector, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero/interface/endpoint or test selector */ + /* XXX - check based on request type */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_set_feature_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / SET INTERFACE + */ + + +/* 9.4.10 */ +static int +dissect_usb_setup_set_interface_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* alternate setting */ + proto_tree_add_item(tree, hf_usb_bAlternateSetting, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 2; + + /* interface */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_set_interface_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / SYNCH FRAME + */ + + +/* 9.4.11 */ +static int +dissect_usb_setup_synch_frame_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* zero */ + proto_tree_add_item(tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* endpoint */ + /* XXX */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* two */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_synch_frame_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* frame number */ + proto_tree_add_item(tree, hf_usb_wFrameNumber, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + + +typedef int (*usb_setup_dissector)(packet_info *pinfo, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info, + guint bus_id, guint device_address); + +typedef struct _usb_setup_dissector_table_t { + guint8 request; + usb_setup_dissector dissector; + +} usb_setup_dissector_table_t; +#define USB_SETUP_GET_STATUS 0 +#define USB_SETUP_CLEAR_FEATURE 1 +#define USB_SETUP_SET_FEATURE 3 +#define USB_SETUP_SET_ADDRESS 5 +#define USB_SETUP_GET_DESCRIPTOR 6 +#define USB_SETUP_SET_DESCRIPTOR 7 +#define USB_SETUP_GET_CONFIGURATION 8 +#define USB_SETUP_SET_CONFIGURATION 9 +#define USB_SETUP_GET_INTERFACE 10 +#define USB_SETUP_SET_INTERFACE 11 +#define USB_SETUP_SYNCH_FRAME 12 +#define USB_SETUP_SET_SEL 48 +#define USB_SETUP_SET_ISOCH_DELAY 49 + +static const usb_setup_dissector_table_t setup_request_dissectors[] = { + {USB_SETUP_GET_STATUS, dissect_usb_setup_get_status_request}, + {USB_SETUP_CLEAR_FEATURE, dissect_usb_setup_clear_feature_request}, + {USB_SETUP_SET_FEATURE, dissect_usb_setup_set_feature_request}, + {USB_SETUP_SET_ADDRESS, dissect_usb_setup_set_address_request}, + {USB_SETUP_GET_DESCRIPTOR, dissect_usb_setup_get_descriptor_request}, + {USB_SETUP_SET_CONFIGURATION, dissect_usb_setup_set_configuration_request}, + {USB_SETUP_GET_INTERFACE, dissect_usb_setup_get_interface_request}, + {USB_SETUP_SET_INTERFACE, dissect_usb_setup_set_interface_request}, + {USB_SETUP_SYNCH_FRAME, dissect_usb_setup_synch_frame_request}, + {0, NULL} +}; + +static const usb_setup_dissector_table_t setup_response_dissectors[] = { + {USB_SETUP_GET_STATUS, dissect_usb_setup_get_status_response}, + {USB_SETUP_CLEAR_FEATURE, dissect_usb_setup_clear_feature_response}, + {USB_SETUP_SET_FEATURE, dissect_usb_setup_set_feature_response}, + {USB_SETUP_SET_ADDRESS, dissect_usb_setup_set_address_response}, + {USB_SETUP_GET_DESCRIPTOR, dissect_usb_setup_get_descriptor_response}, + {USB_SETUP_GET_CONFIGURATION, dissect_usb_setup_get_configuration_response}, + {USB_SETUP_SET_CONFIGURATION, dissect_usb_setup_set_configuration_response}, + {USB_SETUP_GET_INTERFACE, dissect_usb_setup_get_interface_response}, + {USB_SETUP_SET_INTERFACE, dissect_usb_setup_set_interface_response}, + {USB_SETUP_SYNCH_FRAME, dissect_usb_setup_synch_frame_response}, + {0, NULL} +}; + +static const value_string setup_request_names_vals[] = { + {USB_SETUP_GET_STATUS, "GET STATUS"}, + {USB_SETUP_CLEAR_FEATURE, "CLEAR FEATURE"}, + {USB_SETUP_SET_FEATURE, "SET FEATURE"}, + {USB_SETUP_SET_ADDRESS, "SET ADDRESS"}, + {USB_SETUP_GET_DESCRIPTOR, "GET DESCRIPTOR"}, + {USB_SETUP_SET_DESCRIPTOR, "SET DESCRIPTOR"}, + {USB_SETUP_GET_CONFIGURATION, "GET CONFIGURATION"}, + {USB_SETUP_SET_CONFIGURATION, "SET CONFIGURATION"}, + {USB_SETUP_GET_INTERFACE, "GET INTERFACE"}, + {USB_SETUP_SET_INTERFACE, "SET INTERFACE"}, + {USB_SETUP_SYNCH_FRAME, "SYNCH FRAME"}, + {USB_SETUP_SET_SEL, "SET SEL"}, + {USB_SETUP_SET_ISOCH_DELAY, "SET ISOCH DELAY"}, + {0, NULL} +}; + + +static const true_false_string tfs_bmrequesttype_direction = { + "Device-to-host", + "Host-to-device" +}; + +static const value_string bmrequesttype_type_vals[] = { + {RQT_SETUP_TYPE_STANDARD, "Standard"}, + {RQT_SETUP_TYPE_CLASS, "Class"}, + {RQT_SETUP_TYPE_VENDOR, "Vendor"}, + {0, NULL} +}; + +static const value_string bmrequesttype_recipient_vals[] = { + {RQT_SETUP_RECIPIENT_DEVICE, "Device" }, + {RQT_SETUP_RECIPIENT_INTERFACE, "Interface" }, + {RQT_SETUP_RECIPIENT_ENDPOINT, "Endpoint" }, + {RQT_SETUP_RECIPIENT_OTHER, "Other" }, + {0, NULL } +}; + +static int +dissect_usb_bmrequesttype(proto_tree *parent_tree, tvbuff_t *tvb, int offset, int *type) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + + if (parent_tree) { + item = proto_tree_add_item(parent_tree, hf_usb_bmRequestType, tvb, offset, 1, ENC_LITTLE_ENDIAN); + tree = proto_item_add_subtree(item, ett_usb_setup_bmrequesttype); + } + + *type = USB_TYPE(tvb_get_guint8(tvb, offset)); + proto_tree_add_item(tree, hf_usb_bmRequestType_direction, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(tree, hf_usb_bmRequestType_type, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(tree, hf_usb_bmRequestType_recipient, tvb, offset, 1, ENC_LITTLE_ENDIAN); + + return ++offset; +} + +/* Adds the Linux USB pseudo header fields to the tree. + * NOTE: The multi-byte fields in this header, and the pseudo-header + * extension, are in host-endian format so we can't + * use proto_tree_add_item() nor the tvb_get_xyz() routines and is + * the reason for the tvb_memcpy() and proto_tree_add_uint[64]() + * pairs below. */ +static void +dissect_linux_usb_pseudo_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint *bus_id, guint *device_address) +{ + guint8 transfer_type; + guint8 endpoint_number; + guint8 transfer_type_and_direction; + guint8 type; + guint8 flag[2]; + guint16 val16; + guint32 val32; + guint64 val64; + + tvb_memcpy(tvb, (guint8 *)&val64, 0, 8); + proto_tree_add_uint64(tree, hf_usb_urb_id, tvb, 0, 8, val64); + + /* show the event type of this URB as string and as a character */ + type = tvb_get_guint8(tvb, 8); + proto_tree_add_uint_format_value(tree, hf_usb_urb_type, tvb, 8, 1, + type, "%s ('%c')", val_to_str(type, usb_urb_type_vals, "Unknown %d"), + isprint(type) ? type : '.'); + proto_tree_add_item(tree, hf_usb_transfer_type, tvb, 9, 1, ENC_BIG_ENDIAN); + + if (check_col(pinfo->cinfo, COL_INFO)) { + transfer_type = tvb_get_guint8(tvb, 9); + endpoint_number = tvb_get_guint8(tvb, 10); + transfer_type_and_direction = (transfer_type & 0x7F) | (endpoint_number & 0x80); + col_append_str(pinfo->cinfo, COL_INFO, + val_to_str(transfer_type_and_direction, usb_transfer_type_and_direction_vals, "Unknown type %x")); + } + + proto_tree_add_bitmask(tree, tvb, 10, hf_usb_endpoint_number, ett_usb_endpoint, usb_endpoint_fields, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_usb_device_address, tvb, 11, 1, ENC_BIG_ENDIAN); + *device_address = tvb_get_guint8(tvb, 11); + + tvb_memcpy(tvb, (guint8 *)&val16, 12, 2); + proto_tree_add_uint(tree, hf_usb_bus_id, tvb, 12, 2, val16); + *bus_id = tvb_get_letohs(tvb, 12); + + /* Right after the pseudo header we always have + * sizeof(struct usb_device_setup_hdr) bytes. The content of these + * bytes only have meaning in case setup_flag == 0. + */ + flag[0] = tvb_get_guint8(tvb, 14); + flag[1] = '\0'; + if (flag[0] == 0) { + proto_tree_add_string(tree, hf_usb_setup_flag, tvb, 14, 1, "relevant (0)"); + } else { + proto_tree_add_string_format_value(tree, hf_usb_setup_flag, tvb, + 14, 1, flag, "not relevant ('%c')", isprint(flag[0]) ? flag[0]: '.'); + } + + flag[0] = tvb_get_guint8(tvb, 15); + flag[1] = '\0'; + if (flag[0] == 0) { + proto_tree_add_string(tree, hf_usb_data_flag, tvb, 15, 1, "present (0)"); + } else { + proto_tree_add_string_format_value(tree, hf_usb_data_flag, tvb, + 15, 1, flag, "not present ('%c')", isprint(flag[0]) ? flag[0] : '.'); + } + + tvb_memcpy(tvb, (guint8 *)&val64, 16, 8); + proto_tree_add_uint64(tree, hf_usb_urb_ts_sec, tvb, 16, 8, val64); + + tvb_memcpy(tvb, (guint8 *)&val32, 24, 4); + proto_tree_add_uint(tree, hf_usb_urb_ts_usec, tvb, 24, 4, val32); + + tvb_memcpy(tvb, (guint8 *)&val32, 28, 4); + proto_tree_add_int(tree, hf_usb_urb_status, tvb, 28, 4, val32); + + tvb_memcpy(tvb, (guint8 *)&val32, 32, 4); + proto_tree_add_uint(tree, hf_usb_urb_len, tvb, 32, 4, val32); + + tvb_memcpy(tvb, (guint8 *)&val32, 36, 4); + proto_tree_add_uint(tree, hf_usb_urb_data_len, tvb, 36, 4, val32); +} + +/* + * XXX - put these into the protocol tree as appropriate. + */ +static int +dissect_linux_usb_pseudo_header_ext(tvbuff_t *tvb, int offset, + packet_info *pinfo _U_, + proto_tree *tree _U_) +{ + guint32 ndesc; + + offset += 4; /* interval */ + offset += 4; /* start_frame */ + offset += 4; /* copy of URB's transfer flags */ + + tvb_memcpy(tvb, (guint8 *)&ndesc, offset, 4); + offset += 4; + + /* + * Isochronous descriptors. Each one is 16 bytes long. + */ + offset += ndesc*16; + + return offset; +} + +static void +dissect_linux_usb_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent, + gboolean header_len_64_bytes) +{ + unsigned int offset = 0; + int type, endpoint; + guint8 setup_flag; + proto_tree *tree = NULL; + guint32 tmp_addr; + static usb_address_t src_addr, dst_addr; /* has to be static due to SET_ADDRESS */ + guint32 src_endpoint, dst_endpoint; + gboolean is_request; + usb_conv_info_t *usb_conv_info; + usb_trans_info_t *usb_trans_info = NULL; + conversation_t *conversation; + usb_tap_data_t *tap_data; + guint bus_id = 0; + guint device_address = 0; + usb_data_t *usb_data; + void *pd_save; + tvbuff_t *next_tvb = NULL; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "USB"); + + /* add usb hdr*/ + if (parent) { + proto_item *ti; + ti = proto_tree_add_protocol_format(parent, proto_usb, tvb, 0, + header_len_64_bytes ? 64 : 48, "USB URB"); + tree = proto_item_add_subtree(ti, usb_hdr); + } + + dissect_linux_usb_pseudo_header(tvb, pinfo, tree, &bus_id, &device_address); + is_request = (tvb_get_guint8(tvb, 8) == URB_SUBMIT) ? TRUE : FALSE; + type = tvb_get_guint8(tvb, 9); + endpoint = tvb_get_guint8(tvb, 10) & (~URB_TRANSFER_IN); + tmp_addr = tvb_get_guint8(tvb, 11); + setup_flag = tvb_get_guint8(tvb, 14); + offset += 40; /* skip first part of the pseudo-header */ + + usb_data = se_new(usb_data_t); + usb_data->bus_id = bus_id; + usb_data->device_address = device_address; + usb_data->endpoint = endpoint; + + if (tvb_get_guint8(tvb, 10) & URB_TRANSFER_IN) { + usb_data->direction = P2P_DIR_RECV; + } else { + usb_data->direction = P2P_DIR_SENT; + } + + pd_save = pinfo->private_data; + pinfo->private_data = usb_data; + + /* Set up addresses and ports. */ + if (is_request) { + src_addr.device = 0xffffffff; + src_addr.endpoint = src_endpoint = NO_ENDPOINT; + dst_addr.device = htolel(tmp_addr); + dst_addr.endpoint = dst_endpoint = htolel(endpoint); + } else { + src_addr.device = htolel(tmp_addr); + src_addr.endpoint = src_endpoint = htolel(endpoint); + dst_addr.device = 0xffffffff; + dst_addr.endpoint = dst_endpoint = NO_ENDPOINT; + } + + SET_ADDRESS(&pinfo->net_src, AT_USB, USB_ADDR_LEN, (char *)&src_addr); + SET_ADDRESS(&pinfo->src, AT_USB, USB_ADDR_LEN, (char *)&src_addr); + SET_ADDRESS(&pinfo->net_dst, AT_USB, USB_ADDR_LEN, (char *)&dst_addr); + SET_ADDRESS(&pinfo->dst, AT_USB, USB_ADDR_LEN, (char *)&dst_addr); + pinfo->ptype = PT_USB; + pinfo->srcport = src_endpoint; + pinfo->destport = dst_endpoint; + + conversation = get_usb_conversation(pinfo, &pinfo->src, &pinfo->dst, pinfo->srcport, pinfo->destport); + + usb_conv_info = get_usb_conv_info(conversation); + pinfo->usb_conv_info = usb_conv_info; + + + /* request/response matching so we can keep track of transaction specific + * data. + */ + if (is_request) { + /* this is a request */ + usb_trans_info = (usb_trans_info_t *)se_tree_lookup32(usb_conv_info->transactions, pinfo->fd->num); + if (!usb_trans_info) { + usb_trans_info = se_new0(usb_trans_info_t); + usb_trans_info->request_in = pinfo->fd->num; + usb_trans_info->req_time = pinfo->fd->abs_ts; + usb_trans_info->header_len_64 = header_len_64_bytes; + + se_tree_insert32(usb_conv_info->transactions, pinfo->fd->num, usb_trans_info); + } + usb_conv_info->usb_trans_info = usb_trans_info; + + if (usb_trans_info->response_in) { + proto_item *ti; + + ti = proto_tree_add_uint(tree, hf_usb_response_in, tvb, 0, 0, usb_trans_info->response_in); + PROTO_ITEM_SET_GENERATED(ti); + } + } else { + /* this is a response */ + if (pinfo->fd->flags.visited) { + usb_trans_info = (usb_trans_info_t *)se_tree_lookup32(usb_conv_info->transactions, pinfo->fd->num); + } else { + usb_trans_info = (usb_trans_info_t *)se_tree_lookup32_le(usb_conv_info->transactions, pinfo->fd->num); + if (usb_trans_info) { + usb_trans_info->response_in = pinfo->fd->num; + se_tree_insert32(usb_conv_info->transactions, pinfo->fd->num, usb_trans_info); + } + } + usb_conv_info->usb_trans_info = usb_trans_info; + + if (usb_trans_info && usb_trans_info->request_in) { + proto_item *ti; + nstime_t t, deltat; + + ti = proto_tree_add_uint(tree, hf_usb_request_in, tvb, 0, 0, usb_trans_info->request_in); + PROTO_ITEM_SET_GENERATED(ti); + + t = pinfo->fd->abs_ts; + nstime_delta(&deltat, &t, &usb_trans_info->req_time); + ti = proto_tree_add_time(tree, hf_usb_time, tvb, 0, 0, &deltat); + PROTO_ITEM_SET_GENERATED(ti); + } + } + + tap_data = ep_new(usb_tap_data_t); + tap_data->urb_type = tvb_get_guint8(tvb, 8); + tap_data->transfer_type = (guint8)type; + tap_data->conv_info = usb_conv_info; + tap_data->trans_info = usb_trans_info; + + if (type != URB_CONTROL) { + tap_queue_packet(usb_tap, pinfo, tap_data); + } + + switch(type) { + case URB_BULK: + { + proto_item *item; + + item = proto_tree_add_uint(tree, hf_usb_bInterfaceClass, tvb, 0, 0, usb_conv_info->interfaceClass); + PROTO_ITEM_SET_GENERATED(item); + + /* Skip setup/isochronous header - it's not applicable */ + offset += 8; + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) + offset = dissect_linux_usb_pseudo_header_ext(tvb, offset, pinfo, tree); + + if (tvb_reported_length_remaining(tvb, offset)) { + pinfo->usb_conv_info = usb_conv_info; + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (try_heuristics && dissector_try_heuristic(heur_bulk_subdissector_list, next_tvb, pinfo, parent, NULL)) { + return; + } + else if (dissector_try_uint(usb_bulk_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, parent)) { + return; + } + } + } + break; + case URB_INTERRUPT: + { + proto_item *item; + + item = proto_tree_add_uint(tree, hf_usb_bInterfaceClass, tvb, 0, 0, usb_conv_info->interfaceClass); + PROTO_ITEM_SET_GENERATED(item); + + /* Skip setup/isochronous header - it's not applicable */ + offset += 8; + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) + offset = dissect_linux_usb_pseudo_header_ext(tvb, offset, pinfo, tree); + + if (tvb_reported_length_remaining(tvb, offset)) { + pinfo->usb_conv_info = usb_conv_info; + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (try_heuristics && dissector_try_heuristic(heur_interrupt_subdissector_list, next_tvb, pinfo, parent, NULL)) { + return; + } + else if (dissector_try_uint(usb_interrupt_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, parent)) { + return; + } + } + } + break; + case URB_CONTROL: + { + const usb_setup_dissector_table_t *tmp; + usb_setup_dissector dissector; + proto_item *ti = NULL; + proto_tree *setup_tree = NULL; + int type_2; + + if (is_request) { + if (setup_flag == 0) { + /* this is a request */ + + /* Dissect the setup header - it's applicable */ + + ti = proto_tree_add_protocol_format(parent, proto_usb, tvb, offset, 8, "URB setup"); + setup_tree = proto_item_add_subtree(ti, usb_setup_hdr); + usb_trans_info->setup.requesttype = tvb_get_guint8(tvb, offset); + offset = dissect_usb_bmrequesttype(setup_tree, tvb, offset, &type_2); + + + /* read the request code and spawn off to a class specific + * dissector if found + */ + usb_trans_info->setup.request = tvb_get_guint8(tvb, offset); + usb_trans_info->setup.wValue = tvb_get_letohs(tvb, offset+1); + usb_trans_info->setup.wIndex = tvb_get_letohs(tvb, offset+3); + usb_trans_info->setup.wLength = tvb_get_letohs(tvb, offset+5); + + if (type_2 != RQT_SETUP_TYPE_CLASS) { + tap_queue_packet(usb_tap, pinfo, tap_data); + } + + switch (type_2) { + + case RQT_SETUP_TYPE_STANDARD: + /* + * This is a standard request which is managed by this + * dissector + */ + proto_tree_add_item(setup_tree, hf_usb_request, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s Request", + val_to_str(usb_trans_info->setup.request, setup_request_names_vals, "Unknown type %x")); + } + + dissector = NULL; + for(tmp = setup_request_dissectors;tmp->dissector;tmp++) { + if (tmp->request == usb_trans_info->setup.request) { + dissector = tmp->dissector; + break; + } + } + + if (dissector) { + offset = dissector(pinfo, setup_tree, tvb, offset, usb_trans_info, + usb_conv_info, bus_id, device_address); + } else { + proto_tree_add_item(setup_tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + } + break; + + case RQT_SETUP_TYPE_CLASS: + /* Make sure we have the proper conversation */ + if (USB_RECIPIENT(usb_trans_info->setup.requesttype) == RQT_SETUP_RECIPIENT_INTERFACE) { + guint8 interface_num = usb_trans_info->setup.wIndex & 0xff; + usb_conv_info = get_usb_iface_conv_info(pinfo, interface_num); + usb_conv_info->usb_trans_info = usb_trans_info; + pinfo->usb_conv_info = usb_conv_info; + } else if (USB_RECIPIENT(usb_trans_info->setup.requesttype) == RQT_SETUP_RECIPIENT_ENDPOINT) { + static address endpoint_addr; + endpoint = usb_trans_info->setup.wIndex & 0x0f; + + dst_addr.endpoint = dst_endpoint = htolel(endpoint); + SET_ADDRESS(&endpoint_addr, AT_USB, USB_ADDR_LEN, (char *)&dst_addr); + + conversation = get_usb_conversation(pinfo, &pinfo->src, &endpoint_addr, pinfo->srcport, dst_endpoint); + usb_conv_info = get_usb_conv_info(conversation); + usb_conv_info->usb_trans_info = usb_trans_info; + pinfo->usb_conv_info = usb_conv_info; + } + + tap_data->conv_info = usb_conv_info; + tap_data->trans_info = usb_trans_info; + tap_queue_packet(usb_tap, pinfo, tap_data); + + ti = proto_tree_add_uint(tree, hf_usb_bInterfaceClass, tvb, 0, 0, usb_conv_info->interfaceClass); + PROTO_ITEM_SET_GENERATED(ti); + + /* Try to find a class specific dissector */ + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (try_heuristics && dissector_try_heuristic(heur_control_subdissector_list, next_tvb, pinfo, setup_tree, NULL)) { + return; + } + if (dissector_try_uint(usb_control_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, setup_tree)) { + return; + } + /* Else no class dissector, just display generic fields */ + proto_tree_add_item(setup_tree, hf_usb_request_unknown_class, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(setup_tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + break; + + default: + proto_tree_add_item(setup_tree, hf_usb_request_unknown_class, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(setup_tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + } + } else { + /* Skip setup/isochronous header - it's not applicable */ + offset += 8; + } + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) + offset = dissect_linux_usb_pseudo_header_ext(tvb, offset, pinfo, tree); + + if (tvb_reported_length_remaining(tvb, offset) != 0) { + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (try_heuristics && dissector_try_heuristic(heur_control_subdissector_list, next_tvb, pinfo, parent, NULL)) { + return; + } + if (dissector_try_uint(usb_control_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, parent)) { + return; + } + } + } else { + /* this is a response */ + + /* Skip setup header - it's never applicable for responses */ + offset += 8; + + /* Make sure we have the proper conversation */ + if (usb_trans_info) { + if (USB_TYPE(usb_trans_info->setup.requesttype) == RQT_SETUP_TYPE_CLASS) { + if (USB_RECIPIENT(usb_trans_info->setup.requesttype) == RQT_SETUP_RECIPIENT_INTERFACE) { + guint8 interface_num = usb_trans_info->setup.wIndex & 0xff; + usb_conv_info = get_usb_iface_conv_info(pinfo, interface_num); + usb_conv_info->usb_trans_info = usb_trans_info; + pinfo->usb_conv_info = usb_conv_info; + } else if (USB_RECIPIENT(usb_trans_info->setup.requesttype) == RQT_SETUP_RECIPIENT_ENDPOINT) { + static address endpoint_addr; + endpoint = usb_trans_info->setup.wIndex & 0x0f; + + src_addr.endpoint = src_endpoint = htolel(endpoint); + SET_ADDRESS(&endpoint_addr, AT_USB, USB_ADDR_LEN, (char *)&src_addr); + + conversation = get_usb_conversation(pinfo, &endpoint_addr, &pinfo->dst, src_endpoint, pinfo->destport); + usb_conv_info = get_usb_conv_info(conversation); + usb_conv_info->usb_trans_info = usb_trans_info; + pinfo->usb_conv_info = usb_conv_info; + } + } + } + + tap_data->conv_info = usb_conv_info; + tap_data->trans_info = usb_trans_info; + tap_queue_packet(usb_tap, pinfo, tap_data); + ti = proto_tree_add_uint(tree, hf_usb_bInterfaceClass, tvb, 0, 0, usb_conv_info->interfaceClass); + PROTO_ITEM_SET_GENERATED(ti); + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) + offset = dissect_linux_usb_pseudo_header_ext(tvb, offset, pinfo, tree); + + if (usb_trans_info) { + /* Try to find a class specific dissector */ + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (try_heuristics && dissector_try_heuristic(heur_control_subdissector_list, next_tvb, pinfo, parent, NULL)) { + return; + } + if (dissector_try_uint(usb_control_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, parent)) { + return; + } + + type_2 = USB_TYPE(usb_trans_info->setup.requesttype); + switch (type_2) { + + case RQT_SETUP_TYPE_STANDARD: + /* + * This is a standard response which is managed by this + * dissector + */ + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s Response", + val_to_str(usb_conv_info->usb_trans_info->setup.request, + setup_request_names_vals, "Unknown type %x")); + } + + dissector = NULL; + for(tmp = setup_response_dissectors;tmp->dissector;tmp++) { + if (tmp->request == usb_conv_info->usb_trans_info->setup.request) { + dissector = tmp->dissector; + break; + } + } + + if (dissector) { + offset = dissector(pinfo, parent, tvb, offset, usb_conv_info->usb_trans_info, + usb_conv_info, bus_id, device_address); + } else { + if (tvb_reported_length_remaining(tvb, offset) != 0) { + proto_tree_add_text(parent, tvb, offset, -1, "CONTROL response data"); + offset += tvb_length_remaining(tvb, offset); + } + } + break; + default: + if (tvb_reported_length_remaining(tvb, offset) != 0) { + proto_tree_add_text(parent, tvb, offset, -1, "CONTROL response data"); + offset += tvb_length_remaining(tvb, offset); + } + break; + } + } else { + /* no matching request available */ + if (tvb_reported_length_remaining(tvb, offset) != 0) { + proto_tree_add_text(parent, tvb, offset, -1, "CONTROL response data"); + offset += tvb_length_remaining(tvb, offset); + } + } + } + } + break; + case URB_ISOCHRONOUS: + { + guint32 iso_numdesc = 0; + proto_item *tii; + tii = proto_tree_add_uint(tree, hf_usb_bInterfaceClass, tvb, offset, 0, usb_conv_info->interfaceClass); + PROTO_ITEM_SET_GENERATED(tii); + /* All fields which belong to Linux usbmon headers are in host-endian + * byte order. The fields coming from the USB communication are in little + * endian format (see usb_20.pdf, chapter 8.1 Byte/Bit ordering). + * + * When a capture file is transfered to a host with different endianness + * than packet was captured then the necessary swapping happens in + * wiretap/pcap-common.c, pcap_process_linux_usb_pseudoheader(). + */ + + if (setup_flag == 0) { + proto_item *ti; + proto_tree *setup_tree; + int type_2; + + /* Dissect the setup header - it's applicable */ + + ti = proto_tree_add_protocol_format(parent, proto_usb, tvb, offset, 8, "URB setup"); + setup_tree = proto_item_add_subtree(ti, usb_setup_hdr); + + offset = dissect_usb_bmrequesttype(setup_tree, tvb, offset, &type_2); + proto_tree_add_item(setup_tree, hf_usb_request, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(setup_tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + } else { + + /* Process ISO related fields (usbmon_packet.iso). The fields are + * in host endian byte order so use tvb_memcopy() and + * proto_tree_add_uint() pair. + */ + guint32 val32; + + tvb_memcpy(tvb, (guint8 *)&val32, offset, 4); + proto_tree_add_uint(tree, hf_usb_iso_error_count, tvb, offset, 4, val32); + offset += 4; + + tvb_memcpy(tvb, (guint8 *)&iso_numdesc, offset, 4); + proto_tree_add_uint(tree, hf_usb_iso_numdesc, tvb, offset, 4, iso_numdesc); + offset += 4; + } + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) { + guint32 ndesc; + + offset += 4; /* interval */ + offset += 4; /* start_frame */ + offset += 4; /* copy of URB's transfer flags */ + + tvb_memcpy(tvb, (guint8 *)&ndesc, offset, 4); + offset += 4; + + } + + if (setup_flag != 0) { + proto_tree *urb_tree; + guint32 i; + unsigned int data_base; + guint32 iso_status; + guint32 iso_off; + guint32 iso_len; + guint32 iso_pad; + + data_base = offset + iso_numdesc * 16; + urb_tree = tree; + for (i = 0; i != iso_numdesc; i++) { + /* Fetch ISO descriptor fields stored in host + * endian byte order. + */ + tvb_memcpy(tvb, (guint8 *)&iso_status, offset, 4); + tvb_memcpy(tvb, (guint8 *)&iso_off, offset+4, 4); + tvb_memcpy(tvb, (guint8 *)&iso_len, offset+8, 4); + + if (parent) { + proto_item *ti; + if (iso_len > 0) { + ti = proto_tree_add_protocol_format(urb_tree, proto_usb, tvb, offset, + 16, "USB isodesc %u [%s] (%u bytes)", i, + val_to_str_ext(iso_status, &usb_urb_status_vals_ext, "Error %d"), iso_len); + } else { + ti = proto_tree_add_protocol_format(urb_tree, proto_usb, tvb, offset, + 16, "USB isodesc %u [%s]", i, val_to_str_ext(iso_status, &usb_urb_status_vals_ext, "Error %d")); + } + tree = proto_item_add_subtree(ti, usb_isodesc); + } + + proto_tree_add_int(tree, hf_usb_iso_status, tvb, offset, 4, iso_status); + offset += 4; + + proto_tree_add_uint(tree, hf_usb_iso_off, tvb, offset, 4, iso_off); + offset += 4; + + proto_tree_add_uint(tree, hf_usb_iso_len, tvb, offset, 4, iso_len); + offset += 4; + + /* When the ISO status is OK and there is ISO data and this ISO data is + * fully captured then show this data. + */ + if (!iso_status && iso_len && data_base + iso_off + iso_len <= tvb_length(tvb)) + proto_tree_add_item(tree, hf_usb_iso_data, tvb, data_base + iso_off, iso_len, ENC_NA); + + tvb_memcpy(tvb, (guint8 *)&iso_pad, offset, 4); + proto_tree_add_uint(tree, hf_usb_iso_pad, tvb, offset, 4, iso_pad); + offset += 4; + } + } + + } + break; + + default: + /* dont know */ + if (setup_flag == 0) { + proto_item *ti; + proto_tree *setup_tree; + int type_2; + + /* Dissect the setup header - it's applicable */ + + ti = proto_tree_add_protocol_format(parent, proto_usb, tvb, offset, 8, "URB setup"); + setup_tree = proto_item_add_subtree(ti, usb_setup_hdr); + + offset = dissect_usb_bmrequesttype(setup_tree, tvb, offset, &type_2); + proto_tree_add_item(setup_tree, hf_usb_request, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(setup_tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + } else { + /* Skip setup/isochronous header - it's not applicable */ + offset += 8; + } + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) + offset = dissect_linux_usb_pseudo_header_ext(tvb, offset, pinfo, tree); + + break; + } + + next_tvb = tvb_new_subset_remaining(tvb, offset); + + if (!dissector_try_uint(device_to_dissector, (guint32) (bus_id << 8 | device_address), next_tvb, pinfo, parent)) { + emem_tree_key_t key[4]; + guint32 k_frame_number; + guint32 k_device_address; + guint32 k_bus_id; + device_protocol_data_t *device_protocol_data; + + k_frame_number = pinfo->fd->num; + k_device_address = device_address; + k_bus_id = bus_id; + + key[0].length = 1; + key[0].key = &k_device_address; + key[1].length = 1; + key[1].key = &k_bus_id; + key[2].length = 1; + key[2].key = &k_frame_number; + key[3].length = 0; + key[3].key = NULL; + + device_protocol_data = (device_protocol_data_t *)se_tree_lookup32_array_le(device_to_protocol_table, key); + if (device_protocol_data && device_protocol_data->bus_id == bus_id && + device_protocol_data->device_address == device_address && + dissector_try_uint(protocol_to_dissector, (guint32) device_protocol_data->protocol, next_tvb, pinfo, parent)) { + offset += tvb_length_remaining(tvb, offset); + } else { + device_product_data_t *device_product_data; + + device_product_data = (device_product_data_t *)se_tree_lookup32_array_le(device_to_product_table, key); + if (device_product_data && device_product_data->bus_id == bus_id && + device_product_data->device_address == device_address && + dissector_try_uint(product_to_dissector, (guint32) (device_product_data->vendor << 16 | device_product_data->product), next_tvb, pinfo, parent)) { + offset += tvb_length_remaining(tvb, offset); + } + } + } else { + offset += tvb_length_remaining(tvb, offset); + } + + if (tvb_reported_length_remaining(tvb, offset) != 0) { + /* There is leftover capture data to add (padding?) */ + proto_tree_add_item(parent, hf_usb_capdata, tvb, offset, -1, ENC_NA); + } + + pinfo->private_data = pd_save; +} + +static void +dissect_linux_usb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent) +{ + dissect_linux_usb_common(tvb, pinfo, parent, FALSE); +} + +static void +dissect_linux_usb_mmapped(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent) +{ + dissect_linux_usb_common(tvb, pinfo, parent, TRUE); +} + +void +proto_register_usb(void) +{ + module_t *usb_module; + static hf_register_info hf[] = { + + /* USB packet pseudoheader members */ + { &hf_usb_urb_id, + { "URB id", "usb.urb_id", + FT_UINT64, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_urb_type, + { "URB type", "usb.urb_type", + FT_UINT8, BASE_DEC, VALS(usb_urb_type_vals), 0x0, + NULL, HFILL }}, + + { &hf_usb_transfer_type, + { "URB transfer type", "usb.transfer_type", + FT_UINT8, BASE_HEX, VALS(usb_transfer_type_vals), 0x0, + NULL, HFILL }}, + + { &hf_usb_endpoint_number, + { "Endpoint", "usb.endpoint_number", + FT_UINT8, BASE_HEX, NULL, 0x0, + "USB endpoint number", HFILL }}, + + { &hf_usb_endpoint_direction, + { "Direction", "usb.endpoint_number.direction", + FT_UINT8, BASE_DEC, VALS(usb_endpoint_direction_vals), 0x80, + "USB endpoint direction", HFILL }}, + + { &hf_usb_endpoint_number_value, + { "Endpoint value", "usb.endpoint_number.endpoint", + FT_UINT8, BASE_DEC, NULL, 0x7F, + "USB endpoint value", HFILL }}, + + { &hf_usb_device_address, + { "Device", "usb.device_address", + FT_UINT8, BASE_DEC, NULL, 0x0, + "USB device address", HFILL }}, + + { &hf_usb_bus_id, + { "URB bus id", "usb.bus_id", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_setup_flag, + { "Device setup request", "usb.setup_flag", + FT_STRING, BASE_NONE, NULL, 0x0, + "USB device setup request is relevant (0) or not", HFILL }}, + + { &hf_usb_data_flag, + { "Data", "usb.data_flag", + FT_STRING, BASE_NONE, NULL, 0x0, + "USB data is present (0) or not", HFILL }}, + + { &hf_usb_urb_ts_sec, + { "URB sec", "usb.urb_ts_sec", + FT_UINT64, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_urb_ts_usec, + { "URB usec", "usb.urb_ts_usec", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_urb_status, + { "URB status", "usb.urb_status", + FT_INT32, BASE_DEC|BASE_EXT_STRING, &usb_urb_status_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_urb_len, + { "URB length [bytes]", "usb.urb_len", + FT_UINT32, BASE_DEC, NULL, 0x0, + "URB length in bytes", HFILL }}, + + { &hf_usb_urb_data_len, + { "Data length [bytes]", "usb.data_len", + FT_UINT32, BASE_DEC, NULL, 0x0, + "URB data length in bytes", HFILL }}, + + { &hf_usb_bmRequestType, + { "bmRequestType", "usb.bmRequestType", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_request, + { "bRequest", "usb.setup.bRequest", + FT_UINT8, BASE_DEC, VALS(setup_request_names_vals), 0x0, + NULL, HFILL }}, + + /* Same as hf_usb_request but no descriptive text */ + { &hf_usb_request_unknown_class, + { "bRequest", "usb.setup.bRequest", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_value, + { "wValue", "usb.setup.wValue", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_index, + { "wIndex", "usb.setup.wIndex", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_length, + { "wLength", "usb.setup.wLength", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wFeatureSelector, + { "wFeatureSelector", "usb.setup.wFeatureSelector", + FT_UINT16, BASE_DEC, VALS(usb_feature_selector_vals), 0x0, + NULL, HFILL }}, + + { &hf_usb_wInterface, + { "wInterface", "usb.setup.wInterface", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wStatus, + { "wStatus", "usb.setup.wStatus", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wFrameNumber, + { "wFrameNumber", "usb.setup.wFrameNumber", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + /* --------------------------------- */ + { &hf_usb_iso_error_count, /* host endian byte order */ + { "ISO error count", "usb.iso.error_count", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iso_numdesc, /* host endian byte order */ + { "Number of ISO descriptors", "usb.iso.numdesc", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + /* fields of struct mon_bin_isodesc from linux/drivers/usb/mon/mon_bin.c */ + { &hf_usb_iso_status, /* host endian byte order */ + { "Status", "usb.iso.iso_status", + FT_INT32, BASE_DEC|BASE_EXT_STRING, &usb_urb_status_vals_ext, 0x0, + "ISO descriptor status", HFILL }}, + + { &hf_usb_iso_off, /* host endian byte order */ + { "Offset [bytes]", "usb.iso.iso_off", + FT_UINT32, BASE_DEC, NULL, 0x0, + "ISO data offset in bytes starting from the end of the last ISO descriptor", HFILL }}, + + { &hf_usb_iso_len, /* host endian byte order */ + { "Length [bytes]", "usb.iso.iso_len", + FT_UINT32, BASE_DEC, NULL, 0x0, + "ISO data length in bytes", HFILL }}, + + { &hf_usb_iso_pad, /* host endian byte order */ + { "Padding", "usb.iso.pad", + FT_UINT32, BASE_HEX, NULL, 0x0, + "Padding field of ISO descriptor structure", HFILL }}, + + { &hf_usb_iso_data, + {"ISO Data", "usb.iso.data", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + /* --------------------------------- */ +#if 0 + { &hf_usb_data_len, + {"Application Data Length", "usb.data.length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, +#endif + + { &hf_usb_capdata, + {"Leftover Capture Data", "usb.capdata", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Padding added by the USB capture system", HFILL }}, + + { &hf_usb_bmRequestType_direction, + { "Direction", "usb.bmRequestType.direction", + FT_BOOLEAN, 8, TFS(&tfs_bmrequesttype_direction), USB_DIR_IN, + NULL, HFILL }}, + + { &hf_usb_bmRequestType_type, + { "Type", "usb.bmRequestType.type", + FT_UINT8, BASE_HEX, VALS(bmrequesttype_type_vals), USB_TYPE_MASK, + NULL, HFILL }}, + + { &hf_usb_bmRequestType_recipient, + { "Recipient", "usb.bmRequestType.recipient", + FT_UINT8, BASE_HEX, VALS(bmrequesttype_recipient_vals), 0x1f, + NULL, HFILL }}, + + { &hf_usb_bDescriptorType, + { "bDescriptorType", "usb.bDescriptorType", + FT_UINT8, BASE_DEC|BASE_EXT_STRING, &descriptor_type_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_descriptor_index, + { "Descriptor Index", "usb.DescriptorIndex", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_language_id, + { "Language Id", "usb.LanguageId", + FT_UINT16, BASE_HEX|BASE_EXT_STRING,&usb_langid_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_bLength, + { "bLength", "usb.bLength", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bcdUSB, + { "bcdUSB", "usb.bcdUSB", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bDeviceClass, + { "bDeviceClass", "usb.bDeviceClass", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, &usb_class_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_bDeviceSubClass, + { "bDeviceSubClass", "usb.bDeviceSubClass", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bDeviceProtocol, + { "bDeviceProtocol", "usb.bDeviceProtocol", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bMaxPacketSize0, + { "bMaxPacketSize0", "usb.bMaxPacketSize0", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_idVendor, + { "idVendor", "usb.idVendor", + FT_UINT16, BASE_HEX | BASE_EXT_STRING, &ext_usb_vendors_vals, 0x0, + NULL, HFILL }}, + + { &hf_usb_idProduct, + { "idProduct", "usb.idProduct", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bcdDevice, + { "bcdDevice", "usb.bcdDevice", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iManufacturer, + { "iManufacturer", "usb.iManufacturer", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iProduct, + { "iProduct", "usb.iProduct", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iSerialNumber, + { "iSerialNumber", "usb.iSerialNumber", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bNumConfigurations, + { "bNumConfigurations", "usb.bNumConfigurations", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wLANGID, + { "wLANGID", "usb.wLANGID", + FT_UINT16, BASE_HEX|BASE_EXT_STRING,&usb_langid_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_bString, + { "bString", "usb.bString", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bInterfaceNumber, + { "bInterfaceNumber", "usb.bInterfaceNumber", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bAlternateSetting, + { "bAlternateSetting", "usb.bAlternateSetting", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bNumEndpoints, + { "bNumEndpoints", "usb.bNumEndpoints", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bInterfaceClass, + { "bInterfaceClass", "usb.bInterfaceClass", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, &usb_class_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_bInterfaceSubClass, + { "bInterfaceSubClass", "usb.bInterfaceSubClass", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bInterfaceProtocol, + { "bInterfaceProtocol", "usb.bInterfaceProtocol", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iInterface, + { "iInterface", "usb.iInterface", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bEndpointAddress, + { "bEndpointAddress", "usb.bEndpointAddress", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_configuration_bmAttributes, + { "Configuration bmAttributes", "usb.configuration.bmAttributes", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bmAttributes, + { "bmAttributes", "usb.bmAttributes", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bEndpointAttributeTransfer, + { "Transfertype", "usb.bmAttributes.transfer", + FT_UINT8, BASE_HEX, VALS(usb_bmAttributes_transfer_vals), 0x03, + NULL, HFILL }}, + + { &hf_usb_bEndpointAttributeSynchonisation, + { "Synchronisationtype", "usb.bmAttributes.sync", + FT_UINT8, BASE_HEX, VALS(usb_bmAttributes_sync_vals), 0x0c, + NULL, HFILL }}, + + { &hf_usb_bEndpointAttributeBehaviour, + { "Behaviourtype", "usb.bmAttributes.behaviour", + FT_UINT8, BASE_HEX, VALS(usb_bmAttributes_behaviour_vals), 0x30, + NULL, HFILL }}, + + { &hf_usb_wMaxPacketSize, + { "wMaxPacketSize", "usb.wMaxPacketSize", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wMaxPacketSize_size, + { "Maximum Packet Size", "usb.wMaxPacketSize.size", + FT_UINT16, BASE_DEC, NULL, 0x3FF, + NULL, HFILL }}, + + { &hf_usb_wMaxPacketSize_slots, + { "Transactions per microframe", "usb.wMaxPacketSize.slots", + FT_UINT16, BASE_DEC, VALS(usb_wMaxPacketSize_slots_vals), (3<<11), + NULL, HFILL }}, + + { &hf_usb_bInterval, + { "bInterval", "usb.bInterval", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wTotalLength, + { "wTotalLength", "usb.wTotalLength", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bNumInterfaces, + { "bNumInterfaces", "usb.bNumInterfaces", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bConfigurationValue, + { "bConfigurationValue", "usb.bConfigurationValue", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iConfiguration, + { "iConfiguration", "usb.iConfiguration", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bMaxPower, + { "bMaxPower", "usb.bMaxPower", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_configuration_legacy10buspowered, + { "Must be 1", "usb.configuration.legacy10buspowered", + FT_BOOLEAN, 8, TFS(&tfs_mustbeone), 0x80, + "Legacy USB 1.0 bus powered", HFILL }}, + + { &hf_usb_configuration_selfpowered, + { "Self-Powered", "usb.configuration.selfpowered", + FT_BOOLEAN, 8, TFS(&tfs_selfpowered), 0x40, + NULL, HFILL }}, + + { &hf_usb_configuration_remotewakeup, + { "Remote Wakeup", "usb.configuration.remotewakeup", + FT_BOOLEAN, 8, TFS(&tfs_remotewakeup), 0x20, + NULL, HFILL }}, + + { &hf_usb_bEndpointAddress_number, + { "Endpoint Number", "usb.bEndpointAddress.number", + FT_UINT8, BASE_HEX, NULL, 0x0f, + NULL, HFILL }}, + + { &hf_usb_bEndpointAddress_direction, + { "Direction", "usb.bEndpointAddress.direction", + FT_BOOLEAN, 8, TFS(&tfs_endpoint_direction), 0x80, + NULL, HFILL }}, + + { &hf_usb_request_in, + { "Request in", "usb.request_in", + FT_FRAMENUM, BASE_NONE, NULL, 0, + "The request to this packet is in this packet", HFILL }}, + + { &hf_usb_time, + { "Time from request", "usb.time", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0, + "Time between Request and Response for USB cmds", HFILL }}, + + { &hf_usb_response_in, + { "Response in", "usb.response_in", + FT_FRAMENUM, BASE_NONE, NULL, 0, + "The response to this packet is in this packet", HFILL }}, + + { &hf_usb_bFirstInterface, + { "bFirstInterface", "usb.bFirstInterface", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_usb_bInterfaceCount, + { "bInterfaceCount", + "usb.bInterfaceCount", FT_UINT8, BASE_DEC, + NULL, 0x0, NULL, HFILL }}, + + { &hf_usb_bFunctionClass, + { "bFunctionClass", "usb.bFunctionClass", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, &usb_class_vals_ext, 0x0, NULL, HFILL }}, + + { &hf_usb_bFunctionSubClass, + { "bFunctionSubClass", + "usb.bFunctionSubClass", FT_UINT8, BASE_HEX, + NULL, 0x0, NULL, HFILL }}, + + { &hf_usb_bFunctionProtocol, + { "bFunctionProtocol", "usb.bFunctionProtocol", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + + { &hf_usb_iFunction, + { "iFunction", + "usb.iFunction", FT_UINT8, BASE_DEC, + NULL, 0x0, NULL, HFILL }}, + }; + + static gint *usb_subtrees[] = { + &usb_hdr, + &usb_setup_hdr, + &usb_isodesc, + &ett_usb_endpoint, + &ett_usb_setup_bmrequesttype, + &ett_descriptor_device, + &ett_configuration_bmAttributes, + &ett_configuration_bEndpointAddress, + &ett_endpoint_bmAttributes, + &ett_endpoint_wMaxPacketSize + }; + + device_to_product_table = se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "usb device_address, bus_id and frame number to vendor_product"); + device_to_protocol_table = se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "usb device_address, bus_id and frame number to class_subclass_protocol"); + device_to_dissector = register_dissector_table("usb.device", "USB device", FT_UINT32, BASE_HEX); + protocol_to_dissector = register_dissector_table("usb.protocol", "USB protocol", FT_UINT32, BASE_HEX); + product_to_dissector = register_dissector_table("usb.product", "USB product", FT_UINT32, BASE_HEX); + + proto_usb = proto_register_protocol("USB", "USB", "usb"); + proto_register_field_array(proto_usb, hf, array_length(hf)); + proto_register_subtree_array(usb_subtrees, array_length(usb_subtrees)); + register_dissector("usb", dissect_linux_usb, proto_usb); + + usb_bulk_dissector_table = register_dissector_table("usb.bulk", + "USB bulk endpoint", FT_UINT8, BASE_DEC); + register_heur_dissector_list("usb.bulk", &heur_bulk_subdissector_list); + usb_control_dissector_table = register_dissector_table("usb.control", + "USB control endpoint", FT_UINT8, BASE_DEC); + register_heur_dissector_list("usb.control", &heur_control_subdissector_list); + usb_interrupt_dissector_table = register_dissector_table("usb.interrupt", + "USB interrupt endpoint", FT_UINT8, BASE_DEC); + register_heur_dissector_list("usb.interrupt", &heur_interrupt_subdissector_list); + usb_descriptor_dissector_table = register_dissector_table("usb.descriptor", + "USB descriptor", FT_UINT8, BASE_DEC); + + usb_module = prefs_register_protocol(proto_usb, NULL); + prefs_register_bool_preference(usb_module, "try_heuristics", + "Try heuristic sub-dissectors", + "Try to decode a packet using a heuristic sub-dissector before " + "attempting to dissect the packet using the \"usb.bulk\", \"usb.interrupt\" or " + "\"usb.control\" dissector tables.", &try_heuristics); + + usb_tap = register_tap("usb"); +} + +void +proto_reg_handoff_usb(void) +{ + dissector_handle_t linux_usb_handle; + dissector_handle_t linux_usb_mmapped_handle; + + linux_usb_handle = create_dissector_handle(dissect_linux_usb, proto_usb); + linux_usb_mmapped_handle = create_dissector_handle(dissect_linux_usb_mmapped, + proto_usb); + + dissector_add_uint("wtap_encap", WTAP_ENCAP_USB_LINUX, linux_usb_handle); + dissector_add_uint("wtap_encap", WTAP_ENCAP_USB_LINUX_MMAPPED, linux_usb_mmapped_handle); +} diff --git a/src/main/resource/testFiles/wireshark/prevFiles/prev_1a76dd_1d1a48_epan#dissectors#packet-ua3g.c b/src/main/resource/testFiles/wireshark/prevFiles/prev_1a76dd_1d1a48_epan#dissectors#packet-ua3g.c new file mode 100644 index 0000000..d000d1a --- /dev/null +++ b/src/main/resource/testFiles/wireshark/prevFiles/prev_1a76dd_1d1a48_epan#dissectors#packet-ua3g.c @@ -0,0 +1,4547 @@ +/* packet-ua3g.c + * Routines for UA/UDP (Universal Alcatel over UDP) packet dissection. + * Copyright 2012, Alcatel-Lucent Enterprise + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include + +#include + +#include "epan/packet.h" +#include "epan/emem.h" +#include "packet-uaudp.h" + + +/*----------------------------------------------------------------------------- + Globals + ---------------------------------------------------------------------------*/ + +#if 0 +static dissector_table_t ua3g_opcode_dissector_table; +#endif + + +static int proto_ua3g = -1; +static gint ett_ua3g = -1; +static gint ett_ua3g_body = -1; +static gint ett_ua3g_param = -1; +static gint ett_ua3g_param_sub = -1; +static gint ett_ua3g_option = -1; + +static int hf_ua3g_length = -1; +static int hf_ua3g_opcode_sys = -1; +static int hf_ua3g_opcode_term = -1; +static int hf_ua3g_opcode_production_test = -1; +static int hf_ua3g_opcode_subservice_reset = -1; +static int hf_ua3g_opcode_are_you_there = -1; +static int hf_ua3g_opcode_set_speaker_vol = -1; +static int hf_ua3g_opcode_trace_on = -1; +static int hf_ua3g_ip = -1; +static int hf_ua3g_ip_cs = -1; +static int hf_ua3g_command_led = -1; +static int hf_ua3g_command_lcd_line = -1; +static int hf_ua3g_main_voice_mode = -1; +static int hf_ua3g_command_set_clck = -1; +static int hf_ua3g_external_ringing_command= -1; +static int hf_ua3g_lcd_cursor = -1; +static int hf_ua3g_command_beep = -1; +static int hf_ua3g_command_sidetone = -1; +static int hf_ua3g_command_mute = -1; +static int hf_ua3g_command_feedback = -1; +static int hf_ua3g_command_audio_config = -1; +static int hf_ua3g_command_key_release = -1; +static int hf_ua3g_command_amplified_handset = -1; +static int hf_ua3g_command_loudspeaker = -1; +static int hf_ua3g_command_announce = -1; +static int hf_ua3g_command_ring = -1; +static int hf_ua3g_command_ua_dwl_protocol = -1; +static int hf_ua3g_command_unsolicited_msg = -1; +static int hf_ua3g_ip_device_routing_stop_rtp_parameter = -1; +static int hf_ua3g_ip_device_routing_stop_rtp_parameter_length = -1; +static int hf_ua3g_ip_device_routing_stop_rtp_parameter_value_num = -1; +static int hf_ua3g_ip_device_routing_stop_rtp_parameter_value_bytes = -1; +/* Generated from convert_proto_tree_add_text.pl */ +static int hf_ua3g_ip_device_routing_start_tone_direction = -1; +static int hf_ua3g_ip_device_routing_start_tone_num_entries = -1; +static int hf_ua3g_ip_device_routing_def_tones_num_entries = -1; +static int hf_ua3g_cs_ip_device_routing_cmd00_characteristic_number = -1; +static int hf_ua3g_subdevice_msg_subdev_type = -1; +static int hf_ua3g_unsolicited_msg_next_byte_of_bad_segment = -1; +static int hf_ua3g_ip_device_routing_start_tone_identification = -1; +static int hf_ua3g_ip_device_routing_def_tones_level_2 = -1; +static int hf_ua3g_r_w_peripheral_content = -1; +static int hf_ua3g_subdevice_metastate_subchannel_address = -1; +static int hf_ua3g_subdevice_parameter_bytes = -1; +static int hf_ua3g_subdevice_msg_parameter_bytes = -1; +static int hf_ua3g_set_clck_timer_pos_call_timer_column_number = -1; +static int hf_ua3g_unsolicited_msg_segment_failure_s = -1; +static int hf_ua3g_ip_device_routing_reset_parameter = -1; +static int hf_ua3g_ip_device_routing_get_param_req_parameter = -1; +static int hf_ua3g_set_lcd_contrast_driver_number = -1; +static int hf_ua3g_dwl_special_char_character_number = -1; +static int hf_ua3g_cs_ip_device_routing_cmd00_vta_type = -1; +static int hf_ua3g_ua_dwl_protocol_cause = -1; +static int hf_ua3g_audio_padded_path_emission_padded_level = -1; +static int hf_ua3g_set_clck_timer_pos_clock_column_number = -1; +static int hf_ua3g_segment_msg_num_remaining = -1; +static int hf_ua3g_ip_device_routing_digit_value = -1; +static int hf_ua3g_super_msg_data = -1; +static int hf_ua3g_unsolicited_msg_hardware_version = -1; +static int hf_ua3g_voice_channel_announce = -1; +static int hf_ua3g_ring_silent = -1; +static int hf_ua3g_audio_config_handsfree_return = -1; +static int hf_ua3g_dwl_dtmf_clck_format_inter_digit_pause_time = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_length = -1; +static int hf_ua3g_unsolicited_msg_opcode_bad_segment = -1; +static int hf_ua3g_unsolicited_msg_firmware_version_loader = -1; +static int hf_ua3g_debug_in_line = -1; +static int hf_ua3g_voice_channel_b_microphones = -1; +static int hf_ua3g_beep_beep_number = -1; +static int hf_ua3g_main_voice_mode_tune = -1; +static int hf_ua3g_super_msg_length = -1; +static int hf_ua3g_ip_device_routing_redirect_parameter = -1; +static int hf_ua3g_unsolicited_msg_next_byte_of_bad_command = -1; +static int hf_ua3g_unsolicited_msg_self_test_result = -1; +static int hf_ua3g_beep_on_off = -1; +static int hf_ua3g_ua_dwl_protocol_binary_length = -1; +static int hf_ua3g_ring_speaker_level = -1; +static int hf_ua3g_voice_channel_channel_mode = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_length = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter = -1; +static int hf_ua3g_subdevice_metastate_new_metastate = -1; +static int hf_ua3g_unsolicited_msg_other_information_2 = -1; +static int hf_ua3g_set_lcd_contrast_contrast_value = -1; +static int hf_ua3g_unsolicited_msg_vta_type = -1; +static int hf_ua3g_ua_dwl_protocol_packet_number = -1; +static int hf_ua3g_unsolicited_msg_segment_failure_l = -1; +static int hf_ua3g_voice_channel_b_ear_piece = -1; +static int hf_ua3g_subdevice_msg_subdev_address = -1; +static int hf_ua3g_ring_progressive = -1; +static int hf_ua3g_ua_dwl_protocol_item_version = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_length = -1; +static int hf_ua3g_dwl_dtmf_clck_format_minimum_on_time = -1; +static int hf_ua3g_ring_melody = -1; +static int hf_ua3g_ua_dwl_protocol_item_identifier = -1; +static int hf_ua3g_main_voice_mode_speaker_volume = -1; +static int hf_ua3g_ip_device_routing_listen_rtp_parameter_length = -1; +static int hf_ua3g_ringing_cadence_length = -1; +static int hf_ua3g_software_reset = -1; +static int hf_ua3g_feedback_level = -1; +static int hf_ua3g_ip_phone_warmstart = -1; +static int hf_ua3g_subdevice_opcode = -1; +static int hf_ua3g_unsolicited_msg_device_event = -1; +static int hf_ua3g_segment_message_data = -1; +static int hf_ua3g_main_voice_mode_sending_level = -1; +static int hf_ua3g_subdevice_msg_subdevice_opcode = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter = -1; +static int hf_ua3g_audio_padded_path_reception_padded_level = -1; +static int hf_ua3g_ua_dwl_protocol_force_mode = -1; +static int hf_ua3g_lcd_line_cmd_starting_column = -1; +static int hf_ua3g_subdevice_address = -1; +static int hf_ua3g_ip_device_routing_pause_restart_rtp_parameter = -1; +static int hf_ua3g_audio_config_ignored = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options = -1; +static int hf_ua3g_main_voice_mode_cadence = -1; +static int hf_ua3g_segment_msg_length = -1; +static int hf_ua3g_ua_dwl_protocol_acknowledge = -1; +static int hf_ua3g_command_led_number = -1; +static int hf_ua3g_set_clck_timer_pos_call_timer_line_number = -1; +static int hf_ua3g_unsolicited_msg_segment_failure_t = -1; +static int hf_ua3g_ip_device_routing_start_tone_duration = -1; +static int hf_ua3g_unsolicited_msg_other_information_1 = -1; +static int hf_ua3g_unsolicited_msg_firmware_datas_patch_version = -1; +static int hf_ua3g_ring_beep_number = -1; +static int hf_ua3g_feedback_duration = -1; +static int hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_length = -1; +static int hf_ua3g_audio_config_law = -1; +static int hf_ua3g_ua_dwl_protocol_checksum = -1; +static int hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_value = -1; +static int hf_ua3g_audio_config_handsfree_handsfree = -1; +static int hf_ua3g_ringing_cadence_cadence = -1; +static int hf_ua3g_lcd_cursor_line_number = -1; +static int hf_ua3g_ip_device_routing_def_tones_level_1 = -1; +static int hf_ua3g_unsolicited_msg_opcode_of_bad_command = -1; +static int hf_ua3g_ua_dwl_protocol_download_ack_status = -1; +static int hf_ua3g_voice_channel_main_voice = -1; +static int hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_length = -1; +static int hf_ua3g_icon_cmd_segment = -1; +static int hf_ua3g_cs_ip_device_routing_cmd01_incident_0 = -1; +static int hf_ua3g_beep_destination = -1; +static int hf_ua3g_ip_device_routing_def_tones_frequency_1 = -1; +static int hf_ua3g_unsolicited_msg_datas_version = -1; +static int hf_ua3g_dwl_dtmf_clck_format_dtmf_country_adaptation = -1; +static int hf_ua3g_ringing_cadence_on_off = -1; +static int hf_ua3g_audio_config_volume_level = -1; +static int hf_ua3g_voice_channel_b_general = -1; +static int hf_ua3g_beep_terminator = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter = -1; +static int hf_ua3g_unsolicited_msg_firmware_version_bootloader = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter = -1; +static int hf_ua3g_ip_device_routing_start_rtp_direction = -1; +static int hf_ua3g_set_clck_timer_pos_clock_line_number = -1; +static int hf_ua3g_voice_channel_b_loud_speaker = -1; +static int hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter = -1; +static int hf_ua3g_on_off_level_level_on_loudspeaker = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_length = -1; +static int hf_ua3g_main_voice_mode_microphone_volume = -1; +static int hf_ua3g_sidetone_level = -1; +static int hf_ua3g_beep_number_of_notes = -1; +static int hf_ua3g_unsolicited_msg_segment_failure_num = -1; +static int hf_ua3g_dwl_special_char_byte = -1; +static int hf_ua3g_ring_cadence = -1; +static int hf_ua3g_unsolicited_msg_device_type = -1; +static int hf_ua3g_voice_channel_codec = -1; +static int hf_ua3g_ip_device_routing_redirect_parameter_length = -1; +static int hf_ua3g_ip_device_routing_listen_rtp_parameter = -1; +static int hf_ua3g_beep_cadence = -1; +static int hf_ua3g_voice_channel_voice_channel = -1; +static int hf_ua3g_unsolicited_msg_other_information = -1; +static int hf_ua3g_ip_device_routing_def_tones_frequency_2 = -1; +static int hf_ua3g_digit_dialed_digit_value = -1; +static int hf_ua3g_unsolicited_msg_subdevice_address = -1; +static int hf_ua3g_ua_dwl_protocol_packet_download_end_ack_ok_status = -1; +static int hf_ua3g_r_w_peripheral_address = -1; +static int hf_ua3g_icon_cmd_icon_number = -1; +static int hf_ua3g_dwl_dtmf_clck_format_clock_time_format = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_length = -1; +static int hf_ua3g_i_m_here_id_code = -1; +static int hf_ua3g_ua_dwl_protocol_item_version_nc = -1; +static int hf_ua3g_unsolicited_msg_firmware_version = -1; +static int hf_ua3g_segment_msg_segment = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_bootloader = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_data = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_customization = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_localization = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_code = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_sip = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_value = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_bad_sec_mode = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_cust_name = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_l10n_name = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_ip = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_compressor = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_value = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_enabler = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_send_qos = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_dtmf_sending = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_rfc2198 = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_srtp_encryption = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_uint = -1; +static int hf_ua3g_ip_device_routing_redirect_parameter_ip = -1; +static int hf_ua3g_ip_device_routing_redirect_parameter_uint = -1; +static int hf_ua3g_ip_device_routing_redirect_parameter_value = -1; +static int hf_ua3g_ip_device_routing_listen_rtp_parameter_ip = -1; +static int hf_ua3g_ip_device_routing_listen_rtp_parameter_port = -1; +static int hf_ua3g_ip_device_routing_listen_rtp_parameter_value = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_compressor = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_err_string = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_tftp_backup_ip = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_set_pc_port_status = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_record_rtp_auth = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_security_flag_filter = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_uint = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_value = -1; +static int hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_uint = -1; +static int hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_remote_ip = -1; +static int hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_uint = -1; +static int hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_value = -1; +static int hf_ua3g_main_voice_mode_handset_level = -1; +static int hf_ua3g_main_voice_mode_headset_level = -1; +static int hf_ua3g_main_voice_mode_handsfree_level = -1; +static int hf_ua3g_audio_config_dpi_chan_ua_tx1 = -1; +static int hf_ua3g_audio_config_dpi_chan_ua_tx2 = -1; +static int hf_ua3g_audio_config_dpi_chan_gci_tx1 = -1; +static int hf_ua3g_audio_config_dpi_chan_gci_tx2 = -1; +static int hf_ua3g_audio_config_dpi_chan_cod_tx = -1; +static int hf_ua3g_audio_config_audio_circuit_dth = -1; +static int hf_ua3g_audio_config_audio_circuit_dtr = -1; +static int hf_ua3g_audio_config_audio_circuit_dtf = -1; +static int hf_ua3g_audio_config_audio_circuit_str = -1; +static int hf_ua3g_audio_config_audio_circuit_ahp1 = -1; +static int hf_ua3g_audio_config_audio_circuit_ahp2 = -1; +static int hf_ua3g_audio_config_audio_circuit_ath = -1; +static int hf_ua3g_audio_config_audio_circuit_atr = -1; +static int hf_ua3g_audio_config_audio_circuit_atf = -1; +static int hf_ua3g_audio_config_audio_circuit_alm = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_group_listen = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_attenuation = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_stay_in_send = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mtx = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mrc = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_trans_threshold = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_trans_threshold = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_recv_threshold = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_recv_threshold = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_med_recv_threshold = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_high_recv_threshold = -1; +static int hf_ua3g_ua_dwl_protocol_files_inc_boot_binary = -1; +static int hf_ua3g_ua_dwl_protocol_files_inc_loader_binary = -1; +static int hf_ua3g_ua_dwl_protocol_files_inc_appli_binary = -1; +static int hf_ua3g_ua_dwl_protocol_files_inc_data_binary = -1; +static int hf_ua3g_ua_dwl_protocol_model_selection_a = -1; +static int hf_ua3g_ua_dwl_protocol_model_selection_b = -1; +static int hf_ua3g_ua_dwl_protocol_model_selection_c = -1; +static int hf_ua3g_ua_dwl_protocol_model_selection_country_ver = -1; +static int hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe1 = -1; +static int hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe2 = -1; +static int hf_ua3g_ua_dwl_protocol_memory_sizes_flash = -1; +static int hf_ua3g_ua_dwl_protocol_memory_sizes_ext_ram = -1; +static int hf_ua3g_unsolicited_msg_char_num_vta_subtype = -1; +static int hf_ua3g_unsolicited_msg_char_num_generation = -1; +static int hf_ua3g_unsolicited_msg_char_num_design = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_vta_type = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_design = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_subtype = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_hard_config_chip = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_hard_config_flash = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_config_ram = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_hard_config_ip = -1; +static int hf_ua3g_unsolicited_msg_hook_status = -1; +static int hf_ua3g_special_key_shift = -1; +static int hf_ua3g_special_key_ctrl = -1; +static int hf_ua3g_special_key_alt = -1; +static int hf_ua3g_special_key_cmd = -1; +static int hf_ua3g_special_key_shift_prime = -1; +static int hf_ua3g_special_key_ctrl_prime = -1; +static int hf_ua3g_special_key_alt_prime = -1; +static int hf_ua3g_special_key_cmd_prime = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_call_timer = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_blink = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_call_timer_control = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_call_timer_display = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_time_of_day_display = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_suspend_display_refresh = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_firmware_version = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_ip = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_uint = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_bytes = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_mac_address = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_uint = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_value = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_speed = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_duplex = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_speed = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_duplex = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip1 = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip2 = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_ip = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_string = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_default_codec = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_vad = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_ece = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_voice_mode = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_delay_distribution = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_bfi = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021Q_used = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021P_priority = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_vlan_id = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_diffserv = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution_200ms = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_rtp_lost = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_uint = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_jitter_depth_distribution = -1; + +/* Definition of opcodes */ +/* System To Terminal */ +#define SC_NOP 0x00 +#define SC_PRODUCTION_TEST 0x01 /* IP Phone */ +#define SC_SUBDEVICE_ESCAPE 0x02 /* IP Phone */ +#define SC_SOFT_RESET 0x03 +#define SC_IP_PHONE_WARMSTART 0x04 /* IP Phone */ +#define SC_HE_ROUTING 0x05 /* IP Phone - NOT EXPECTED */ +#define SC_SUBDEVICE_RESET 0x06 +#define SC_LOOPBACK_ON 0x07 /* IP Phone & UA NOE */ +#define SC_LOOPBACK_OFF 0x08 /* IP Phone & UA NOE */ +#define SC_VIDEO_ROUTING 0x09 /* IP Phone - NOT EXPECTED */ +#define SC_SUPER_MSG 0x0B +#define SC_SEGMENT_MSG 0x0C +#define SC_REMOTE_UA_ROUTING 0x0D /* IP Phone - NOT EXPECTED */ +#define SC_VERY_REMOTE_UA_ROUTING 0x0E /* IP Phone - NOT EXPECTED */ +#define SC_OSI_ROUTING 0x0F /* IP Phone - NOT EXPECTED */ +#define SC_ABC_A_ROUTING 0x11 /* IP Phone - NOT EXPECTED */ +#define SC_IBS_ROUTING 0x12 /* IP Phone - NOT EXPECTED */ +#define SC_IP_DEVICE_ROUTING 0x13 +#define SC_M_REFLEX_HUB_ROUTING 0x14 /* IP Phone - NOT EXPECTED */ +#if 0 +#define SC_NOE_CS_ROUTING 0x15 /* Decoded by packet-noe.c */ +#define SC_NOE_PS_ROUTING 0x16 /* Decoded by packet-noe.c */ +#endif +#define SC_SUPER_MSG_2 0x17 +#define SC_DEBUG_IN_LINE 0x18 +#define SC_LED_COMMAND 0x21 /* IP Phone */ +#define SC_START_BUZZER 0x22 /* VTA */ +#define SC_STOP_BUZZER 0x23 /* VTA */ +#define SC_ENABLE_DTMF 0x24 /* Only IP NOE */ +#define SC_DISABLE_DTMF 0x25 /* Only IP NOE */ +#define SC_CLEAR_LCD_DISP 0x26 /* IP Phone */ +#define SC_LCD_LINE_1_CMD 0x27 /* IP Phone */ +#define SC_LCD_LINE_2_CMD 0x28 /* IP Phone */ +#define SC_MAIN_VOICE_MODE 0x29 +#define SC_VERSION_INQUIRY 0x2A +#define SC_ARE_YOU_THERE 0x2B /* IP Phone & UA NOE */ +#define SC_SUBDEVICE_METASTATE 0x2C +#define SC_VTA_STATUS_INQUIRY 0x2D /* IP Phone */ +#define SC_SUBDEVICE_STATE 0x2E +#define SC_DWL_DTMF_CLCK_FORMAT 0x30 /* IP Phone */ +#define SC_SET_CLCK 0x31 /* IP Phone */ +#define SC_VOICE_CHANNEL 0x32 /* IP Phone & UA NOE */ +#define SC_EXTERNAL_RINGING 0x33 +#define SC_LCD_CURSOR 0x35 /* IP Phone */ +#define SC_DWL_SPECIAL_CHAR 0x36 /* IP Phone */ +#define SC_SET_CLCK_TIMER_POS 0x38 /* IP Phone */ +#define SC_SET_LCD_CONTRAST 0x39 /* IP Phone */ +#define SC_AUDIO_IDLE 0x3A +#define SC_SET_SPEAKER_VOL 0x3B /* IP Phone */ +#define SC_BEEP 0x3C +#define SC_SIDETONE 0x3D +#define SC_RINGING_CADENCE 0x3E +#define SC_MUTE 0x3F +#define SC_FEEDBACK 0x40 +#define SC_KEY_RELEASE 0x41 /* IP Phone */ +#define SC_TRACE_ON 0x42 /* IP Phone - NOT EXPECTED */ +#define SC_TRACE_OFF 0x43 /* IP Phone - NOT EXPECTED */ +#define SC_READ_PERIPHERAL 0x44 /* IP Phone - NOT EXPECTED */ +#define SC_WRITE_PERIPHERAL 0x45 /* IP Phone - NOT EXPECTED */ +#define SC_ALL_ICONS_OFF 0x46 /* IP Phone */ +#define SC_ICON_CMD 0x47 /* IP Phone */ +#define SC_AMPLIFIED_HANDSET 0x48 /* IP Phone */ +#define SC_AUDIO_CONFIG 0x49 +#define SC_AUDIO_PADDED_PATH 0x4A /* IP Phone */ +#define SC_RELEASE_RADIO_LINK 0x4B /* IP Phone - NOT EXPECTED */ +#define SC_DECT_HANDOVER 0x4C /* IP Phone - NOT EXPECTED */ +#define SC_LOUDSPEAKER 0x4D +#define SC_ANNOUNCE 0x4E +#define SC_RING 0x4F +#define SC_UA_DWL_PROTOCOL 0x50 /* Only UA NOE */ + +/* Terminal To System */ +#define CS_NOP_ACK 0x00 +#define CS_HANDSET_OFFHOOK 0x01 /* IP Phone */ +#define CS_HANDSET_ONHOOK 0x02 /* IP Phone */ +#define CS_DIGIT_DIALED 0x03 /* IP Phone */ +#define CS_SUBDEVICE_MSG 0x04 +#define CS_HE_ROUTING 0x05 /* IP Phone - NOT EXPECTED */ +#define CS_LOOPBACK_ON 0x06 /* IP Phone & UA NOE */ +#define CS_LOOPBACK_OFF 0x07 /* IP Phone & UA NOE */ +#define CS_VIDEO_ROUTING 0x09 /* IP Phone - NOT EXPECTED */ +#define CS_WARMSTART_ACK 0x0A /* IP Phone */ +#define CS_SUPER_MSG 0x0B /* IP Phone - NOT EXPECTED */ +#define CS_SEGMENT_MSG 0x0C +#define CS_REMOTE_UA_ROUTING 0x0D /* IP Phone - NOT EXPECTED */ +#define CS_VERY_REMOTE_UA_R 0x0E /* IP Phone - NOT EXPECTED */ +#define CS_OSI_ROUTING 0x0F /* IP Phone - NOT EXPECTED */ +#define CS_ABC_A_ROUTING 0x11 /* IP Phone - NOT EXPECTED */ +#define CS_IBS_ROUTING 0x12 /* IP Phone - NOT EXPECTED */ +#define CS_IP_DEVICE_ROUTING 0x13 +#if 0 +#define CS_NOE_CS_ROUTING 0x15 /* Decoded by packet-noe.c */ +#define CS_NOE_PS_ROUTING 0x16 /* Decoded by packet-noe.c */ +#endif +#define CS_SUPER_MSG_2 0x17 +#define CS_DEBUG_IN_LINE 0x18 +#define CS_NON_DIGIT_KEY_PUSHED 0x20 /* IP Phone */ +#define CS_VERSION_RESPONSE 0x21 +#define CS_I_M_HERE 0x22 +#define CS_RSP_STATUS_INQUIRY 0x23 /* IP Phone */ +#define CS_SUBDEVICE_STATE 0x24 +#define CS_DIGIT_KEY_RELEASED 0x26 /* IP Phone */ +#define CS_TRACE_ON_ACK 0x27 /* IP Phone */ +#define CS_TRACE_OFF_ACK 0x28 /* IP Phone */ +#define CS_SPECIAL_KEY_STATUS 0x29 /* IP Phone */ +#define CS_KEY_RELEASED 0x2A /* IP Phone */ +#define CS_PERIPHERAL_CONTENT 0x2B /* IP Phone */ +#define CS_TM_KEY_PUSHED 0x2D /* IP Phone */ +#define CS_UA_DWL_PROTOCOL 0x50 /* Only UA NOE */ +#define CS_UNSOLICITED_MSG 0x9F + +/* System To Terminal Opcodes */ +static const value_string opcodes_vals_sys[] = +{ + {SC_NOP , "NOP"}, + {SC_PRODUCTION_TEST , "Production Test"}, /* IP Phone */ + {SC_SUBDEVICE_ESCAPE , "Subdevice Escape To Subdevice"}, /* IP Phone */ + {SC_SOFT_RESET , "Software Reset"}, + {SC_IP_PHONE_WARMSTART , "IP-Phone Warmstart"}, /* IP Phone */ + {SC_HE_ROUTING , "HE Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_SUBDEVICE_RESET , "Subdevice Reset"}, + {SC_LOOPBACK_ON , "Loopback On"}, + {SC_LOOPBACK_OFF , "Loopback Off"}, + {SC_VIDEO_ROUTING , "Video Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_SUPER_MSG , "Super Message"}, + {SC_SEGMENT_MSG , "Segment Message"}, + {SC_REMOTE_UA_ROUTING , "Remote UA Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_VERY_REMOTE_UA_ROUTING , "Very Remote UA Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_OSI_ROUTING , "OSI Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_ABC_A_ROUTING , "ABC-A Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_IBS_ROUTING , "IBS Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_IP_DEVICE_ROUTING , "IP Device Routing"}, + {SC_M_REFLEX_HUB_ROUTING , "Mutli-Reflex Hub Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_SUPER_MSG_2 , "Super Message 2"}, + {SC_DEBUG_IN_LINE , "Debug In Line"}, + {SC_LED_COMMAND , "Led Command"}, /* IP Phone */ + {SC_START_BUZZER , "Start Buzzer"}, /* VTA */ + {SC_STOP_BUZZER , "Stop Buzzer"}, /* VTA */ + {SC_ENABLE_DTMF , "Enable DTMF"}, + {SC_DISABLE_DTMF , "Disable DTMF"}, + {SC_CLEAR_LCD_DISP , "Clear LCD Display"}, /* IP Phone */ + {SC_LCD_LINE_1_CMD , "LCD Line 1 Commands"}, /* IP Phone */ + {SC_LCD_LINE_2_CMD , "LCD Line 2 Commands"}, /* IP Phone */ + {SC_MAIN_VOICE_MODE , "Main Voice Mode"}, + {SC_VERSION_INQUIRY , "Version Inquiry"}, + {SC_ARE_YOU_THERE , "Are You There?"}, + {SC_SUBDEVICE_METASTATE , "Subdevice Metastate"}, + {SC_VTA_STATUS_INQUIRY , "VTA Status Inquiry"}, /* IP Phone */ + {SC_SUBDEVICE_STATE , "Subdevice State?"}, + {SC_DWL_DTMF_CLCK_FORMAT , "Download DTMF & Clock Format"}, /* IP Phone */ + {SC_SET_CLCK , "Set Clock"}, /* IP Phone */ + {SC_VOICE_CHANNEL , "Voice Channel"}, /* IP Phone & UA NOE */ + {SC_EXTERNAL_RINGING , "External Ringing"}, + {SC_LCD_CURSOR , "LCD Cursor"}, /* IP Phone */ + {SC_DWL_SPECIAL_CHAR , "Download Special Character"}, /* IP Phone */ + {SC_SET_CLCK_TIMER_POS , "Set Clock/Timer Position"}, /* IP Phone */ + {SC_SET_LCD_CONTRAST , "Set LCD Contrast"}, /* IP Phone */ + {SC_AUDIO_IDLE , "Audio Idle"}, + {SC_SET_SPEAKER_VOL , "Set Speaker Volume"}, /* IP Phone */ + {SC_BEEP , "Beep"}, + {SC_SIDETONE , "Sidetone"}, + {SC_RINGING_CADENCE , "Set Programmable Ringing Cadence"}, + {SC_MUTE , "Mute"}, + {SC_FEEDBACK , "Feedback"}, + {SC_KEY_RELEASE , "Key Release"}, /* IP Phone */ + {SC_TRACE_ON , "Trace On"}, /* IP Phone - NOT EXPECTED */ + {SC_TRACE_OFF , "Trace Off"}, /* IP Phone - NOT EXPECTED */ + {SC_READ_PERIPHERAL , "Read Peripheral"}, /* IP Phone - NOT EXPECTED */ + {SC_WRITE_PERIPHERAL , "Write Peripheral"}, /* IP Phone - NOT EXPECTED */ + {SC_ALL_ICONS_OFF , "All Icons Off"}, /* IP Phone */ + {SC_ICON_CMD , "Icon Command"}, /* IP Phone */ + {SC_AMPLIFIED_HANDSET , "Amplified Handset (Boost)"}, /* IP Phone */ + {SC_AUDIO_CONFIG , "Audio Config"}, + {SC_AUDIO_PADDED_PATH , "Audio Padded Path"}, /* IP Phone */ + {SC_RELEASE_RADIO_LINK , "Release Radio Link"}, /* IP Phone - NOT EXPECTED */ + {SC_DECT_HANDOVER , "DECT External Handover Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_LOUDSPEAKER , "Loudspeaker"}, + {SC_ANNOUNCE , "Announce"}, + {SC_RING , "Ring"}, + {SC_UA_DWL_PROTOCOL , "UA Download Protocol"}, + {0, NULL} +}; +static value_string_ext opcodes_vals_sys_ext = VALUE_STRING_EXT_INIT(opcodes_vals_sys); + +/* Terminal To System Opcodes */ +static const value_string opcodes_vals_term[] = +{ + {CS_NOP_ACK , "NOP Acknowledge"}, + {CS_HANDSET_OFFHOOK , "Handset Offhook"}, /* IP Phone */ + {CS_HANDSET_ONHOOK , "Hansdet Onhook"}, /* IP Phone */ + {CS_DIGIT_DIALED , "Digital Dialed"}, /* IP Phone */ + {CS_SUBDEVICE_MSG , "Subdevice Message"}, + {CS_HE_ROUTING , "HE Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_LOOPBACK_ON , "Loopback On Acknowledge"}, /* Same as CS To Terminal */ + {CS_LOOPBACK_OFF , "Loopback Off Acknowledge"}, /* Same as CS To Terminal */ + {CS_VIDEO_ROUTING , "Video Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_WARMSTART_ACK , "Warmstart Acknowledge"}, /* IP Phone */ + {CS_SUPER_MSG , "Super Message"}, /* IP Phone - NOT EXPECTED */ + {CS_SEGMENT_MSG , "Segment Message"}, /* Same as CS To Terminal */ + {CS_REMOTE_UA_ROUTING , "Remote UA Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_VERY_REMOTE_UA_R , "Very Remote UA Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_OSI_ROUTING , "OSI Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_ABC_A_ROUTING , "ABC-A Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_IBS_ROUTING , "IBS Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_IP_DEVICE_ROUTING , "IP Device Routing"}, + {CS_SUPER_MSG_2 , "Super Message 2"}, /* Same as CS To Terminal */ + {CS_DEBUG_IN_LINE , "Debug Message"}, + {CS_NON_DIGIT_KEY_PUSHED , "Non-Digit Key Pushed"}, /* IP Phone */ + {CS_VERSION_RESPONSE , "Version Information"}, + {CS_I_M_HERE , "I'm Here Response"}, + {CS_RSP_STATUS_INQUIRY , "Response To Status Inquiry"}, /* IP Phone */ + {CS_SUBDEVICE_STATE , "Subdevice State Response"}, + {CS_DIGIT_KEY_RELEASED , "Digit Key Released"}, /* IP Phone */ + {CS_TRACE_ON_ACK , "Trace On Acknowledge"}, /* IP Phone - NOT EXPECTED */ + {CS_TRACE_OFF_ACK , "Trace Off Acknowledge"}, /* IP Phone - NOT EXPECTED */ + {CS_SPECIAL_KEY_STATUS , "Special Key Status"}, /* IP Phone */ + {CS_KEY_RELEASED , "Key Released"}, /* IP Phone */ + {CS_PERIPHERAL_CONTENT , "Peripheral Content"}, /* IP Phone - NOT EXPECTED */ + {CS_TM_KEY_PUSHED , "TM Key Pushed"}, /* IP Phone - NOT EXPECTED */ + {CS_UA_DWL_PROTOCOL , "Download Protocol"}, + {CS_UNSOLICITED_MSG , "Unsolicited Message"}, + {0, NULL} +}; +static value_string_ext opcodes_vals_term_ext = VALUE_STRING_EXT_INIT(opcodes_vals_term); + +static const value_string str_digit[] = { + { 0, "0"}, + { 1, "1"}, + { 2, "2"}, + { 3, "3"}, + { 4, "4"}, + { 5, "5"}, + { 6, "6"}, + { 7, "7"}, + { 8, "8"}, + { 9, "9"}, + {10, "*"}, + {11, "#"}, + {12, "A"}, + {13, "B"}, + {14, "C"}, + {15, "D"}, + {16, "Flash"}, + {0, NULL} +}; +static value_string_ext str_digit_ext = VALUE_STRING_EXT_INIT(str_digit); + +#define STR_ON_OFF(arg) ((arg) ? "On" : "Off") +#define STR_YES_NO(arg) ((arg) ? "Yes" : "No") + + +static const value_string str_device_type[] = { + {0x00, "Voice Terminal Adaptor"}, + {0, NULL} +}; + + +/*----------------------------------------------------------------------------- + VERSION NUMBER COMPUTER - This function computes a version number (S.SZ.AB) from a 16 bits number + ---------------------------------------------------------------------------*/ +static void +version_number_computer( gchar *result, guint32 hexa_version ) +{ + int release, vers, fix; + + release = (int)(hexa_version / 10000); + vers = (int)((hexa_version % 10000) / 100); + fix = (hexa_version % 10000) % 100; + g_snprintf( result, ITEM_LABEL_LENGTH, "%d.%02d.%02d", release, vers, fix); +} + + +/*----------------------------------------------------------------------------- + Function for UA3G message with opcode and one parameter + + PRODUCTION TEST - 01h (MESSAGE FROM THE SYSTEM) + SUBDEVICE RESET - 06h (MESSAGE FROM THE SYSTEM) + ARE YOU THERE - 2Bh - IPhone & UA NOE (MESSAGE FROM THE SYSTEM) + SET SPEAKER VOLUME - 3Bh (MESSAGE FROM THE SYSTEM) + TRACE ON - 42h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_with_one_parameter(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset, guint length, + int hf_opcode) +{ + if (length == 0) + return; + + proto_tree_add_item(tree, hf_opcode, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SUBDEVICE ESCAPE TO SUBDEVICE - 02h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_subdevice_escape(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + proto_tree_add_item(tree, hf_ua3g_subdevice_address, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_subdevice_opcode, tvb, offset+1, 1, ENC_NA); + if (length > 2) { + proto_tree_add_item(tree, hf_ua3g_subdevice_parameter_bytes, tvb, offset+2, length-2, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + SOFTWARE RESET - 03h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string software_reset_verswitch_vals[] = { + {0x00, "Reset Without Version Switch"}, + {0x01, "Reset With Version Switch"}, + {0, NULL} +}; + +static void +decode_software_reset(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + if (length == 0) + return; + + proto_tree_add_item(tree, hf_ua3g_software_reset, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + IP-PHONE WARMSTART - 04h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_ip_phone_warmstart[] = { + {0x00, "Run In UA2G Emulation Mode"}, + {0x01, "Run In Full UA3G Mode"}, + {0, NULL} +}; + +static void +decode_ip_phone_warmstart(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + if (length == 0) + return; + + proto_tree_add_item(tree, hf_ua3g_ip_phone_warmstart, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SUPER MESSAGE - 0Bh (MESSAGE FROM THE SYSTEM) + SUPER MESSAGE 2 - 17h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_super_msg(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length, guint8 opcode) +{ + proto_tree *ua3g_body_tree = tree; + int j = 0, parameter_length; + + if (!ua3g_body_tree) + return; + + while (length > 0) { + if (opcode == 0x17) { + parameter_length = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_super_msg_length, tvb, offset, 2, + parameter_length, "Length %d: %d", j++, parameter_length); + offset += 2; + length -= 2; + } else { + parameter_length = tvb_get_guint8(tvb, offset); + proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_super_msg_length, tvb, offset, 1, + parameter_length, "Length %d: %d", j++, parameter_length); + offset++; + length--; + } + + if (parameter_length > 0) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_super_msg_data, tvb, offset, parameter_length, ENC_NA); + offset += parameter_length; + length -= parameter_length; + } + } +} + + +/*----------------------------------------------------------------------------- + SEGMENT MESSAGE - 0Ch (MESSAGE FROM THE TERMINAL AND FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +const true_false_string tfs_segment_msg_segment = { "First Segment", "Subsequent Segment" }; + +static void +decode_segment_msg(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + guint8 val; + + if (!tree) + return; + + val = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_ua3g_segment_msg_segment, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_segment_msg_num_remaining, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (val & 0x80) { + proto_tree_add_item(tree, hf_ua3g_segment_msg_length, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + } + + if (length > 0) { + proto_tree_add_item(tree, hf_ua3g_segment_message_data, tvb, offset, length, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + IP DEVICE ROUTING - 13h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_ip_device_routing[] = { + {0x00, "Reset"}, + {0x01, "Start RTP"}, + {0x02, "Stop RTP"}, + {0x03, "Redirect"}, + {0x04, "Tone Definition"}, + {0x05, "Start Tone"}, + {0x06, "Stop Tone"}, + {0x07, "Start Listen RTP"}, + {0x08, "Stop Listen RTP"}, + {0x09, "Get Parameters Value"}, + {0x0A, "Set Parameters Value"}, + {0x0B, "Send Digit"}, + {0x0C, "Pause RTP"}, + {0x0D, "Restart RTP"}, + {0x0E, "Start Record RTP"}, + {0x0F, "Stop Record RTP"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_reset_vals[] = { + {0x00, "Update Mode"}, + {0x01, "Bad Sec Mode"}, + {0x02, "Customization Name"}, + {0x03, "Localization Name"}, + {0, NULL} +}; + +static const value_string reset_param_bad_sec_mode[] = { + {0x01, "Binary is full, CS is secured, but terminal running in clear mode"}, + {0, NULL} +}; + +static const value_string start_rtp_str_direction[] = { + {0x00, "Terminal Input"}, + {0x01, "Terminal Output"}, + {0x02, "Terminal Input/Output (Both Directions)"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_start_rtp_vals[] = { + {0x00, "Local UDP Port"}, + {0x01, "Remote IP Address"}, + {0x02, "Remote UDP Port"}, + {0x03, "Type Of Service"}, + {0x04, "Compressor"}, + {0x05, "Payload Concatenation (ms)"}, + {0x06, "Echo Cancellation Enabler"}, + {0x07, "Silence Suppression Enabler"}, + {0x08, "802.1 Q User Priority"}, + {0x09, "Reserved"}, + {0x0a, "Post Filtering Enabler"}, + {0x0b, "High Pass Filtering Enabler"}, + {0x0c, "Remote SSRC"}, + {0x0d, "Must Send QOS Tickets"}, + {0x0e, "Local Identifier"}, + {0x0f, "Distant Identifier"}, + {0x10, "Destination For RTCP Sender Reports - Port Number"}, + {0x11, "Destination For RTCP Sender Reports - IP Address"}, + {0x12, "Destination For RTCP Receiver Reports - Port Number"}, + {0x13, "Destination For RTCP Receiver Reports - IP Address"}, + {0x14, "Channel Number"}, + {0x15, "DTMF Sending"}, + {0x16, "Payload Type Of Redundancy"}, + {0x17, "Payload Type Of DTMF Events"}, + {0x18, "Enable / Disable RFC 2198"}, + {0x31, "SRTP Encryption Enable For This Communication"}, + {0x32, "Master Key For SRTP Session"}, + {0x33, "Master Salt Key For SRTP Session"}, + {0x34, "Master key for output stream of SRTP session"}, + {0x35, "Master salt key for output stream of SRTP session"}, + {0x36, "Integrity checking enabled for this communication"}, + {0x37, "MKI value for SRTP packets in input stream"}, + {0x38, "MKI value for SRTP packets in output stream"}, + {0x50, "MD5 Authentication"}, + {0, NULL} +}; +static value_string_ext ip_device_routing_cmd_start_rtp_vals_ext = VALUE_STRING_EXT_INIT(ip_device_routing_cmd_start_rtp_vals); + +static const val64_string str_start_rtp_compressor[] = { + {0x00, "G.711 A-law"}, + {0x01, "G.711 mu-law"}, + {0x0F, "G.723.1 5.3kbps"}, + {0x10, "G.723.1 6.3kbps"}, + {0x11, "G.729A 8 kbps"}, + {0, NULL} +}; + +static const value_string str_set_param_req_compressor[] = { + {0x00, "G.711 A-law"}, + {0x01, "G.711 mu-law"}, + {0x0F, "G.723.1 5.3kbps"}, + {0x10, "G.723.1 6.3kbps"}, + {0x11, "G.729A 8 kbps"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_stop_rtp_vals[] = { + {0x0E, "Local Identifier"}, + {0x0F, "Distant Identifier"}, + {0x14, "Canal Identifier"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_redirect_vals[] = { + {0x00, "Remote MainCPU Server IP Address"}, + {0x01, "Remote MainCPU Server Port"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_listen_rtp_vals[] = { + {0x00, "Remote IP Address "}, + {0x01, "Remote UDP Port In "}, + {0x02, "Remote UDP Port Out "}, + {0x03, "Remote IP Address Out"}, + {0x04, "Canal Number"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_set_param_req_vals[] = { + {0x00 , "QOS IP TOS"}, + {0x01 , "QOS 8021 VLID"}, + {0x02 , "QOS 8021 PRI"}, + {0x03 , "SNMP MIB2 SysContact"}, + {0x04 , "SNMP MIB2 SysName"}, + {0x05 , "SNMP MIB2 SysLocation"}, + {0x06 , "Default Compressor"}, + {0x07 , "Error String Net Down"}, + {0x08 , "Error String Cable PB"}, + {0x09 , "Error String Try Connect"}, + {0x0A , "Error String Connected"}, + {0x0B , "Error String Reset"}, + {0x0C , "Error String Duplicate IP Address"}, + {0x0D , "SNMP MIB Community"}, + {0x0E , "TFTP Backup Sec Mode"}, + {0x0F , "TFTP Backup IP Address"}, + {0x10 , "Set MMI Password"}, + {0x11 , "Set PC Port Status"}, + {0x12 , "Record RTP Authorization"}, + {0x13 , "Security Flags"}, + {0x14 , "ARP Spoofing"}, + {0x15 , "Session Param"}, + {0x30 , "MD5 Authentication"}, + {0, NULL} +}; +static value_string_ext ip_device_routing_cmd_set_param_req_vals_ext = VALUE_STRING_EXT_INIT(ip_device_routing_cmd_set_param_req_vals); + +static const value_string ip_device_routing_cmd_pause_restart_vals[] = { + {0x14, "Canal Identifier"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_record_rtp_vals[] = { + {0x00 , "Recorder Index"}, + {0x01 , "Remote IP Address"}, + {0x02 , "Remote UDP Port In"}, + {0x03 , "Remote UDP Port Out"}, + {0x04 , "Remote IP Address Out"}, + {0x05 , "Local UDP Port In"}, + {0x06 , "Local UDP Port Out"}, + {0x07 , "Type Of Service"}, + {0x08 , "Master Key For SRTP Session"}, + {0x09 , "Master Salt Key For SRTP Session"}, + {0x30 , "MD5 Authentication"}, + {0, NULL} +}; + +static const value_string ip_device_routing_tone_direction_vals[] = { + {0x00, "On The Phone"}, + {0x40, "To The Network"}, + {0x80, "On The Phone and To The Network"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_get_param_req_vals[] = { + {0x00 , "Firmware Version"}, + {0x01 , "Firmware Version"}, + {0x02 , "DHCP IP Address"}, + {0x03 , "Local IP Address"}, + {0x04 , "Subnetwork Mask"}, + {0x05 , "Router IP Address"}, + {0x06 , "TFTP IP Address"}, + {0x07 , "MainCPU IP Address"}, + {0x08 , "Default Codec"}, + {0x09 , "Ethernet Drivers Config"}, + {0x0A , "MAC Address"}, + {0, NULL} +}; + +static const value_string str_set_pc_port_status[] = { + {0x00, "No PC Port Security"}, + {0x01, "Block PC Port"}, + {0x02, "Filter VLAN"}, + {0, NULL} +}; + +static const value_string str_enable_feature[] = { + {0x00, "Disable Feature"}, + {0x01, "Enable Feature"}, + {0, NULL} +}; + +static void +decode_ip_device_routing(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command; + proto_tree *ua3g_body_tree = tree, *ua3g_param_tree, *ua3g_param_subtree; + proto_item *ua3g_param_item; + int parameter_length, parameter_id; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_ip_device_routing, "Unknown")); + + if (!ua3g_body_tree) + return; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (command) { + case 0x00: /* RESET */ + { + if (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_const(parameter_id, ip_device_routing_cmd_reset_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + guint8 param; + switch (parameter_id) { + case 0x00: /* Update Mode */ + + param = tvb_get_guint8(tvb, offset); + if ((param & 0x80) == 0x00) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_noe_update, tvb, offset, 1, ENC_NA); + ua3g_param_subtree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param_sub); + + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_bootloader, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_data, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_customization, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_localization, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_code, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_sip, tvb, offset, 1, ENC_NA); + + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + + break; + case 0x01: /* Bad_Sec_Mode */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_bad_sec_mode, tvb, offset, 1, ENC_NA); + break; + case 0x02: /* Cust_Name */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_cust_name, tvb, offset, parameter_length, ENC_NA|ENC_ASCII); + break; + case 0x03: /* L10N_Name */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_l10n_name, tvb, offset, parameter_length, ENC_NA|ENC_ASCII); + break; + default: + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_value, tvb, offset, parameter_length, ENC_NA); + break; + } + + /*offset += parameter_length; + length -= parameter_length;*/ + } + } + break; + } + case 0x01: /* START RTP */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_start_rtp_direction, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_start_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_ext_const(parameter_id, &ip_device_routing_cmd_start_rtp_vals_ext, "Unknown")); + + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x01: /* Remote IP Address */ + case 0x11: /* Destination For RTCP Sender Reports - IP Address */ + case 0x13: /* Destination For RTCP Receiver Reports - IP Address */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x04: /* Compressor */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_compressor, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x06: /* Echo Cancelation Enabler */ + case 0x07: /* Silence Suppression Enabler */ + case 0x0A: /* Post Filtering Enabler */ + case 0x0B: /* High Pass Filtering Enabler */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_enabler, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x0D: /* Must Send QOS Tickets */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_send_qos, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x0E: /* Local Identifier */ + case 0x0F: /* Distant Identifier */ + break; + case 0x15: /* DTMF Sending */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_dtmf_sending, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x18: /* Enable / Disable RFC 2198 */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_rfc2198, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x31: /* SRTP Encryption Enable For This Communication */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_srtp_encryption, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x00: /* Local UDP Port */ + case 0x02: /* Remote UDP Port */ + case 0x03: /* Type Of Service */ + case 0x05: /* Payload Concatenation */ + case 0x08: /* 802.1 Q User Priority */ + case 0x09: /* Reserved */ + case 0x0C: /* Remote SSRC */ + case 0x10: /* Destination For RTCP Sender Reports - Port Number */ + case 0x12: /* Destination For RTCP Receiver Reports - Port Number */ + case 0x14: /* Channel Number */ + case 0x16: /* Payload Type For Redundancy */ + case 0x17: /* Payload Type For DTMF Events */ + case 0x32: /* Master Key For SRTP Session */ + case 0x33: /* Master Salt Key For SRTP Session */ + case 0x34: /* Master key for output stream of SRTP session */ + case 0x35: /* Master salt key for output stream of SRTP session */ + case 0x36: /* Integrity checking enabled for this communication */ + case 0x37: /* MKI value for SRTP packets in input stream */ + case 0x38: /* MKI value for SRTP packets in output stream */ + case 0x50: /* MD5 Authentication */ + default: + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + } + case 0x02: /* STOP_RTP */ + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_const(parameter_id, ip_device_routing_cmd_stop_rtp_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter_value_num, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter_value_bytes, tvb, offset, parameter_length, ENC_NA); + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x03: /* REDIRECT */ + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_redirect_parameter, + tvb, offset, parameter_length + 2, parameter_id, + "%s", val_to_str_const(parameter_id, ip_device_routing_cmd_redirect_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_redirect_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_redirect_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x00: /* Remote MainCPU Server IP Address */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_redirect_parameter_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x01: /* Remote MainCPU Server Port */ + default: + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_redirect_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_redirect_parameter_value, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x04: /* DEF_TONES */ + { + int i, tone_nb_entries; + guint16 frequency_1, frequency_2; + signed char level_1, level_2; + + tone_nb_entries = tvb_get_guint8(tvb, offset); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_def_tones_num_entries, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0 && tone_nb_entries) { + for (i = 1; i <= tone_nb_entries; i++) { + frequency_1 = tvb_get_ntohs(tvb, offset); + level_1 = (signed char)(tvb_get_guint8(tvb, offset + 2)) / 2; + frequency_2 = tvb_get_ntohs(tvb, offset + 3); + level_2 = (signed char)(tvb_get_guint8(tvb, offset + 5)) / 2; + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 6, + "Tone Pair %d: %d Hz at %d dB / %d Hz at %d dB", + i, frequency_1, level_1, frequency_2, level_2); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_def_tones_frequency_1, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + proto_tree_add_int(ua3g_param_tree, hf_ua3g_ip_device_routing_def_tones_level_1, tvb, offset, 1, level_1); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_def_tones_frequency_2, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + proto_tree_add_int(ua3g_param_tree, hf_ua3g_ip_device_routing_def_tones_level_2, tvb, offset, 1, level_2); + offset++; + length--; + } + } + break; + } + case 0x05: /* START TONE */ + { + guint8 ii, tone_nb_entries, tone_id; +#if 0 + guint8 tone_direction, tone_id, tone_duration tone_silence; +#endif + int tone_duration; + + tone_nb_entries = tvb_get_guint8(tvb, offset); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_start_tone_direction, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_start_tone_num_entries, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0 && tone_nb_entries) { + for (ii = 0; ii < tone_nb_entries; ii++) { + tone_id = tvb_get_guint8(tvb, offset); + tone_duration = tvb_get_ntohs(tvb, offset + 1); +#if 0 + tone_duration = tvb_get_guint8(tvb, offset + 1); + tone_silence = tvb_get_guint8(tvb, offset + 2); +#endif + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 6, +#if 0 + "Tone Pair %d: Id: %d, Duration: %d ms, Silence: %d ms", + ii+1, tone_id, tone_duration, tone_silence); +#endif + "Tone Pair %d: Id: %d, Duration: %d ms", + ii+1, tone_id, tone_duration); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_tone_identification, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_tone_duration, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + +#if 0 + proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, + "Duration: %d ms", tone_duration); + offset++; + length--; + + proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, + "Silence: %d ms", tone_silence); + offset++; + length--; +#endif + } + } + break; + } + case 0x07: /* START LISTEN RTP */ + case 0x08: /* STOP LISTEN RTP */ + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_const(parameter_id, ip_device_routing_cmd_listen_rtp_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x00: /* Remote IP Address - Not for start listening rtp */ + case 0x03: /* Remote IP Address Out - Not for start listening rtp */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter_ip, tvb, offset, 1, ENC_NA); + break; + case 0x01: /* Remote UDP Port In - Not for start listening rtp */ + case 0x02: /* Remote UDP Port Out - Not for start listening rtp */ + case 0x04: /* Canal Number */ + default: + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter_port, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x09: /* GET_PARAM_REQ */ + while (length > 0) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_get_param_req_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + break; + + case 0x0A: /* SET_PARAM_REQ */ + { + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_set_param_req_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_ext_const(parameter_id, &ip_device_routing_cmd_set_param_req_vals_ext, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x06: /* Compressor */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_compressor, tvb, offset, 1, ENC_NA); + break; + case 0x07: /* ERR STRING NET DOWN */ + case 0x08: /* ERR STRING CABLE PB */ + case 0x09: /* ERR STRING TRY CONNECT */ + case 0x0A: /* ERR STRING CONNECTED */ + case 0x0B: /* ERR STRING RESET */ + case 0x0C: /* ERR STRING DUPLICATE IP ADDRESS */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_err_string, tvb, offset, parameter_length, ENC_NA|ENC_ASCII); + break; + case 0x0F: /* TFTP BACKUP IP ADDR */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_tftp_backup_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x11: /* Set PC Port status */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_set_pc_port_status, tvb, offset, 1, ENC_NA); + break; + case 0x12: /* Record RTP Authorization */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_record_rtp_auth, tvb, offset, 1, ENC_NA); + break; + case 0x13: /* Security Flags */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_security_flag_filter, tvb, offset, 1, ENC_NA); + break; + case 0x00: /* QOS IP TOS */ + case 0x01: /* QOS 8021 VLID */ + case 0x02: /* QOS 8021 PRI */ + case 0x03: /* SNMP MIB2 SYSCONTACT */ + case 0x04: /* SNMP MIB2 SYSNAME */ + case 0x05: /* SNMP MIB2 SYSLOCATION */ + case 0x0D: /* SNMP MIB COMMUNITY */ + case 0x0E: /* TFTP BACKUP SEC MODE */ + case 0x10: /* SET MMI PASSWORD */ + case 0x14: /* ARP Spoofing */ + case 0x15: /* Session Param */ + case 0x30: /* MD5 Authentication */ + default: + if ((parameter_length > 0) && (parameter_length <= 8)) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else if (parameter_length > 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + } + case 0x0B: /* SEND_DIGIT */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_digit_value, tvb, offset, 1, ENC_NA); + break; + + case 0x0C: /* PAUSE_RTP */ + case 0x0D: /* RESTART_RTP */ + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_pause_restart_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_const(parameter_id, ip_device_routing_cmd_pause_restart_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_pause_restart_rtp_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x0E: /* START_RECORD_RTP */ + case 0x0F: /* STOP RECORD RTP */ + while (length > 0) { + + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_const(parameter_id, ip_device_routing_cmd_record_rtp_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x01: /* Remote IP Address */ + case 0x04: /* Remote IP Address Out */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_remote_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x00: /* Recorder Index */ + case 0x02: /* Remote UDP Port In */ + case 0x03: /* Remote UDP Port Out */ + case 0x05: /* Local UDP Port In */ + case 0x06: /* Local UDP Port Out */ + case 0x07: /* Type Of Service */ + case 0x08: /* Master Key For SRTP Session */ + case 0x09: /* Master Salt Key For SRTP Session */ + case 0x30: /* MD5 Authentication */ + default: + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_value, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x06: /* STOP TONE */ + default: + { + break; + } + } +} + + +/*----------------------------------------------------------------------------- + DEBUG IN LINE - 18h (MESSAGE FROM THE TERMINAL AND FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_debug_in_line(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + proto_tree_add_item(tree, hf_ua3g_debug_in_line, tvb, offset, length, ENC_NA|ENC_ASCII); +} + + +/*----------------------------------------------------------------------------- + LED COMMAND - 21h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_led[] = { + {0x00, "Led Off"}, + {0x01, "Led On"}, + {0x02, "Red Led Fast Flash"}, + {0x03, "Red Led Slow Flash"}, + {0x04, "Green Led On"}, + {0x05, "Green Led Fast Flash"}, + {0x06, "Green Led Slow Flash"}, + {0x07, "All Led Off"}, + {0, NULL} +}; + +static void +decode_led_command(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset) +{ + int command; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_led, "Unknown")); + + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_command_led, tvb, offset, 1, ENC_NA); + + if ((command >= 0) && (command < 7)) { + proto_tree_add_item(tree, hf_ua3g_command_led_number, tvb, offset+1, 1, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + LCD LINE 1 COMMANDS - 27h (MESSAGE FROM THE SYSTEM) + LCD LINE 2 COMMANDS - 28h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_lcd_line[] = { + {0, "Clear Line & Write From Column"}, + {1, "Write From Column"}, + {2, "Append To Current Line"}, + {0, NULL} +}; + +static const value_string str_call_timer_ctrl[] = { + {0x00, "Call Timer Status Not Changed"}, + {0x01, "Stop Call Timer"}, + {0x02, "Start Call Timer From Current Value"}, + {0x03, "Initialize And Call Timer"}, + {0, NULL} +}; + +static void +decode_lcd_line_cmd(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command, column_n; + const gchar* command_str; + proto_tree *ua3g_body_tree = tree, *ua3g_param_tree, *ua3g_option_tree; + proto_item *ua3g_param_item, *ua3g_option_item; + emem_strbuf_t *strbuf; + + command = tvb_get_guint8(tvb, offset) & 0x03; + column_n = tvb_get_guint8(tvb, offset + 1); + command_str = val_to_str_const(command, str_command_lcd_line, "Unknown"); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s %d", command_str, column_n); + + if (!ua3g_body_tree) + return; + + strbuf = ep_strbuf_new_label("\""); + + ep_strbuf_append_printf(strbuf, "%s\"", tvb_format_text(tvb, offset + 2, length - 2)); + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, + length, "%s %d: %s", + command_str, column_n, strbuf->str); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_command_lcd_line, tvb, offset, 1, ENC_NA); + ua3g_option_item = proto_tree_add_item(ua3g_param_tree, hf_ua3g_lcd_line_cmd_lcd_options, tvb, offset, 1, ENC_NA); + ua3g_option_tree = proto_item_add_subtree(ua3g_option_item, ett_ua3g_option); + + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_call_timer, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_blink, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_call_timer_control, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_call_timer_display, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_time_of_day_display, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_suspend_display_refresh, tvb, offset, 1, ENC_NA); + + offset++; + length--; + + if (command != 3) + proto_tree_add_item(ua3g_param_tree, hf_ua3g_lcd_line_cmd_starting_column, tvb, offset, 1, ENC_NA); + else + proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Unused"); + + offset++; + length--; + proto_tree_add_text(ua3g_param_tree, tvb, offset, length, "ASCII Char: %s", strbuf->str); +} + + +/*----------------------------------------------------------------------------- + MAIN VOICE MODE - 29h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_main_voice_mode[] = { + {0x00, "Idle"}, + {0x01, "Handset"}, + {0x02, "Group Listening"}, + {0x03, "On Hook Dial"}, + {0x04, "Handsfree"}, + {0x05, "Announce Loudspeaker"}, + {0x06, "Ringing"}, + {0x10, "Idle"}, + {0x11, "Handset"}, + {0x12, "Headset"}, + {0x13, "Handsfree"}, + {0, NULL} +}; + +static const value_string str_cadence[] = { + {0x00, "Standard Ringing"}, + {0x01, "Double Burst"}, + {0x02, "Triple Burst"}, + {0x03, "Continuous Ringing"}, + {0x04, "Priority Attendant Ringing"}, + {0x05, "Regular Attendant Ringing"}, + {0x06, "Programmable Cadence"}, + {0x07, "Programmable Cadence"}, + {0, NULL} +}; + +static void +decode_main_voice_mode(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 mode; + proto_tree *ua3g_body_tree = tree; + + mode = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(mode, str_main_voice_mode, "Unknown")); + + if (!ua3g_body_tree) + return; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_main_voice_mode, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (mode) { + case 0x06: /* Ringing */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_main_voice_mode_tune, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_main_voice_mode_cadence, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + /* FALLTHROUGH */ + case 0x02: /* Group Listening */ + case 0x03: /* On Hook Dial */ + case 0x04: /* Handsfree */ + case 0x05: /* Announce Loudspeaker */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_main_voice_mode_speaker_volume, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (length > 0) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_main_voice_mode_microphone_volume, tvb, offset, 1, ENC_NA); + } + break; + } + case 0x11: /* Handset */ + { + signed char level; + + level = (signed char)(tvb_get_guint8(tvb, offset)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_handset_level, tvb, offset, 1, level); + + level = (signed char)(tvb_get_guint8(tvb, offset+1)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_sending_level, tvb, offset+1, 1, level); + break; + } + case 0x12: /* Headset */ + { + signed char level; + + level = (signed char)(tvb_get_guint8(tvb, offset)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_headset_level, tvb, offset, 1, level); + + level = (signed char)(tvb_get_guint8(tvb, offset+1)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_sending_level, tvb, offset+1, 1, level); + break; + } + case 0x13: /* Handsfree */ + { + signed char level; + + level = (signed char)(tvb_get_guint8(tvb, offset)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_handsfree_level, tvb, offset, 1, level); + + level = (signed char)(tvb_get_guint8(tvb, offset+1)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_sending_level, tvb, offset+1, 1, level); + break; + } + case 0x00: /* Idle */ + case 0x01: /* Handset */ + case 0x10: /* Idle */ + default: + { + break; + } + } +} + + +/*----------------------------------------------------------------------------- + SUBDEVICE METASTATE - 2Ch (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_new_metastate[] = { + {0x00, "Disable"}, + {0x01, "Active"}, + {0x02, "Wake Up"}, + {0, NULL} +}; + +static void +decode_subdevice_metastate(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset) +{ + proto_tree_add_item(tree, hf_ua3g_subdevice_metastate_subchannel_address, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_subdevice_metastate_new_metastate, tvb, offset+1, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + Download DTMF & CLOCK FORMAT - 30h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_clock_format[] = { + {0, "Europe"}, + {1, "US"}, + {0, NULL} +}; + +static void +decode_dwl_dtmf_clck_format(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset, guint length) +{ + proto_tree_add_item(tree, hf_ua3g_dwl_dtmf_clck_format_minimum_on_time, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_dwl_dtmf_clck_format_inter_digit_pause_time, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_dwl_dtmf_clck_format_clock_time_format, tvb, offset+2, 1, ENC_NA); + + if (length > 2) + proto_tree_add_item(tree, hf_ua3g_dwl_dtmf_clck_format_dtmf_country_adaptation, tvb, offset+3, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SET CLOCK - 31h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_set_clck[] = { + {0x00, "Set Current Time/Call Timer"}, + {0x01, "Set Current Time"}, + {0x02, "Set Call Timer"}, + {0, NULL} +}; + +static const value_string str_call_timer[] = { + {1, "Call Timer "}, + {0, NULL} +}; + +static void +decode_set_clck(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command; + int hour, minute, second, call_timer; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_set_clck, "Unknown")); + + proto_tree_add_item(tree, hf_ua3g_command_set_clck, tvb, offset, 1, ENC_NA); + offset++; + length--; + call_timer = 0; + + switch (command) { + case 0x02: /* Timer Form */ + { + call_timer = 1; + } + /* FALLTHROUGH */ + case 0x00: /* Set Current Time/Call Timer */ + case 0x01: /* Set Current Time */ + { + while (length > 0) { + hour = tvb_get_guint8(tvb, offset); + minute = tvb_get_guint8(tvb, offset + 1); + second = tvb_get_guint8(tvb, offset + 2); + + proto_tree_add_text(tree, tvb, offset, 3, + "%s: %d:%d:%d", + val_to_str_const(call_timer, str_call_timer, "Current Time"), hour, minute, second); + offset += 3; + length -= 3; + + call_timer = 1; + } + } + default: + { + break; + } + } + +} + + +/*----------------------------------------------------------------------------- + VOICE CHANNEL - 32h - (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_voice_channel[] = { + {0x00, "No"}, + {0x01, "B1"}, + {0x02, "B2"}, + {0x03, "B3"}, + {0, NULL} +}; + +static const true_false_string tfs_voice_channel_channel_mode = { "Write 00 to Voice Channel", "Normal Voice Channel Mode" }; +static const true_false_string tfs_voice_channel_codec = { "Write Quiet To Codec", "Normal Codec Operation" }; +static const true_false_string tfs_voice_channel_voice_channel = { "Use B3 As Voice Channel", "Use B1 As Voice Channel" }; + +static void +decode_voice_channel(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + if (length == 1) { + proto_tree_add_item(tree, hf_ua3g_voice_channel_channel_mode, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_codec, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_voice_channel, tvb, offset, 1, ENC_NA); + } else if (length == 2) { + proto_tree_add_item(tree, hf_ua3g_voice_channel_main_voice, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_announce, tvb, offset+1, 1, ENC_NA); + } else if (length == 4) { + proto_tree_add_item(tree, hf_ua3g_voice_channel_b_general, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_b_loud_speaker, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_b_ear_piece, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_b_microphones, tvb, offset+3, 1, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + EXTERNAL RINGING - 33h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_ext_ring_cmd[] = { + {0x00, "Turn Off"}, + {0x01, "Turn On"}, + {0x02, "Follow The Normal Ringing"}, + {0, NULL} +}; + +static void +decode_external_ringing(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset) +{ + guint8 command; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_ext_ring_cmd, "Unknown")); + + proto_tree_add_item(tree, hf_ua3g_external_ringing_command, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + LCD CURSOR - 35h - (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_lcd_cursor(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo, guint offset) +{ + const gchar* str_on_off_val = STR_ON_OFF(tvb_get_guint8(tvb, offset + 1) & 0x02); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", str_on_off_val); + + proto_tree_add_item(tree, hf_ua3g_lcd_cursor_line_number, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_lcd_cursor, tvb, offset+1, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + DOWNLOAD SPECIAL CHARACTER - 36h - (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_dwl_special_char(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset, guint length) +{ + int i; + + while (length > 0) { + proto_tree_add_item(tree, hf_ua3g_dwl_special_char_character_number, tvb, offset, 1, ENC_NA); + offset++; + length--; + for (i = 1; i <= 8; i++) { + proto_tree_add_item(tree, hf_ua3g_dwl_special_char_byte, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + } +} + + +/*----------------------------------------------------------------------------- + SET CLOCK/TIMER POSITION - 38h - (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_set_clck_timer_pos(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset) +{ + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_set_clck_timer_pos_clock_line_number, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_set_clck_timer_pos_clock_column_number, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_set_clck_timer_pos_call_timer_line_number, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_set_clck_timer_pos_call_timer_column_number, tvb, offset+3, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SET LCD CONTRAST - 39h - (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_driver_number[] = { + {0x00, "Display"}, + {0x01, "Icon"}, + {0, NULL} +}; + +static void +decode_set_lcd_contrast(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset) +{ + proto_tree_add_item(tree, hf_ua3g_set_lcd_contrast_driver_number, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_set_lcd_contrast_contrast_value, tvb, offset+1, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + BEEP - 3Ch (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_beep[] = { + {0x01, "Beep Once"}, + {0x02, "Beep Start"}, + {0x03, "Stop Beep"}, + {0x04, "Start Beep"}, + {0x05, "Define Beep"}, + {0, NULL} +}; + +static const value_string str_beep_start_destination[] = { + {0x01, "Ear-Piece"}, + {0x02, "Loudspeaker"}, + {0x03, "Ear-Piece and Loudspeaker"}, + {0, NULL} +}; + +static const value_string str_start_beep_destination[] = { + {0x01, "Handset"}, + {0x02, "Headset"}, + {0x04, "Loudspeaker"}, + {0x08, "Announce Loudspeaker"}, + {0x10, "Handsfree"}, + {0, NULL} +}; + +static const value_string str_beep_freq_sample_nb[] = { + {0x00, "Frequency"}, + {0xFF, "Audio Sample Number"}, + {0, NULL} +}; +static const value_string str_beep_duration[] = { + {0x00, "Duration "}, + {0xFF, "Duration (Ignored)"}, + {0, NULL} +}; +static const value_string str_beep_terminator[] = { + {0xFD, "Stop"}, + {0xFE, "Loop"}, + {0xFF, "Infinite"}, + {0, NULL} +}; + +static void +decode_beep(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + if (length > 0) { /* All cases except classical beep */ + guint8 command; + proto_tree *ua3g_body_tree = tree; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_beep, "Unknown")); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_command_beep, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (command) { + case 0x01: /* Beep Once */ + case 0x02: /* Beep Start */ + { + int i = 0; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_destination, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0) { + guint8 val; + + i++; + val = (tvb_get_guint8(tvb, offset) & 0x7F) * 10; + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_on_off, tvb, offset, 1, ENC_NA); + proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_beep_cadence, tvb, offset, 1, val, + "Cadence T%d: %d ms", i, val); + offset++; + length--; + } + break; + } + case 0x04: /* Start Beep */ + { + guint8 beep_dest; + emem_strbuf_t *strbuf; + int i; + + beep_dest = tvb_get_guint8(tvb, offset); + + strbuf = ep_strbuf_new_label(NULL); + + for (i = 0; i < 5; i++) { + ep_strbuf_append(strbuf, + val_to_str_const(beep_dest & (0x01 << i), str_start_beep_destination, "")); + } + + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "Destination: %s", strbuf->str); + offset++; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_beep_number, tvb, offset, 1, ENC_NA); + break; + } + case 0x05: + { + int i, nb_of_notes, beep_number; + + beep_number = tvb_get_guint8(tvb, offset); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_beep_number, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (beep_number <= 0x44) + beep_number = 0x00; + else + beep_number = 0xFF; + + nb_of_notes = tvb_get_guint8(tvb, offset); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_number_of_notes, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0) { + for (i = 1; i <= nb_of_notes; i++) { + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s %d: %d", + val_to_str_const(beep_number, str_beep_freq_sample_nb, "Unknown"), + i, tvb_get_guint8(tvb, offset)); + offset++; + length--; + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Level %d: %d", + i, tvb_get_guint8(tvb, offset)); + offset++; + length--; + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s %d: %x", + val_to_str_const(beep_number, str_beep_duration, "Unknown"), + i, tvb_get_guint8(tvb, offset)); + offset++; + length--; + } + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_terminator, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + break; + } + case 0x03: /* Stop Beep */ + default: + { + break; + } + } + } else { /* Classical Beep */ + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": Classical Beep"); + } +} + + +/*----------------------------------------------------------------------------- + SIDETONE ON / OFF - 3Dh (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_sidetone(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, guint offset) +{ + guint8 command; + const gchar* command_str; + + command = tvb_get_guint8(tvb, offset); + command_str = STR_ON_OFF(command); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", command_str); + + proto_tree_add_item(tree, hf_ua3g_command_sidetone, tvb, offset, 1, ENC_NA); + + if (command == 0x01) { + proto_tree_add_int(tree, hf_ua3g_sidetone_level, tvb, offset+1, 1, + (signed char)(tvb_get_guint8(tvb, offset+1) / 2)); + } +} + + +/*----------------------------------------------------------------------------- + SET PROGRAMMABLE RINGING CADENCE - 3Eh (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_ringing_cadence(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset, guint length) +{ + int i = 0; + guint16 cadence_length; + + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_ringing_cadence_cadence, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0) { + i++; + proto_tree_add_item(tree, hf_ua3g_ringing_cadence_on_off, tvb, offset, 1, ENC_NA); + cadence_length = ((tvb_get_guint8(tvb, offset) & 0x7F) * 10); + proto_tree_add_uint_format(tree, hf_ua3g_ringing_cadence_length, tvb, offset, 1, cadence_length, + "Length %d : %d ms", i, cadence_length); + offset++; + length--; + } +} + + +/*----------------------------------------------------------------------------- + MUTE ON / OFF - 3Fh (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_mute[] = { + {0x00, "Microphone Disable"}, + {0x01, "Microphone Enable"}, + {0, NULL} +}; + +static void +decode_mute(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, guint offset) +{ + guint8 command; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_mute, "Unknown")); + + proto_tree_add_item(tree, hf_ua3g_command_mute, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + FEEDBACK ON / OFF - 40h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_feedback(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command; + const gchar* command_str; + + command = tvb_get_guint8(tvb, offset); + command_str = STR_ON_OFF(command); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", command_str); + + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_command_feedback, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (command == 0x01) { + proto_tree_add_int(tree, hf_ua3g_feedback_level, tvb, offset, 1, + (signed char)(tvb_get_guint8(tvb, offset) / 2)); + offset++; + length--; + + if (length > 0) { + proto_tree_add_uint_format_value(tree, hf_ua3g_feedback_duration, tvb, offset, 1, + tvb_get_guint8(tvb, offset) * 10, "%d ms", tvb_get_guint8(tvb, offset) * 10); + } + } +} + + +/*----------------------------------------------------------------------------- + READ PERIPHERAL - 44h (MESSAGE FROM THE SYSTEM) + WRITE PERIPHERAL - 45h (MESSAGE FROM THE SYSTEM) + PERIPHERAL CONTENT - 2Bh (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static void +decode_r_w_peripheral(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset, guint length) +{ + proto_tree_add_item(tree, hf_ua3g_r_w_peripheral_address, tvb, offset, 2, ENC_BIG_ENDIAN); + + if (length > 2) { + proto_tree_add_item(tree, hf_ua3g_r_w_peripheral_content, tvb, offset+2, 1, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + ICON COMMAND - 47h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_icon_cmd_state[] = { + {0x00, "Off"}, + {0x01, "Slow Flash"}, + {0x02, "Not Used"}, + {0x03, "Steady On"}, + {0, NULL} +}; + +static void +decode_icon_cmd(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, guint offset) +{ + guint8 byte0, byte1, bytex; + int i; + + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_icon_cmd_icon_number, tvb, offset, 1, ENC_NA); + + byte0 = tvb_get_guint8(tvb, offset+1); + byte1 = tvb_get_guint8(tvb, offset+2); + + for (i = 0; i < 8; i++) { + bytex = + ((byte0 >> i) & 0x01) * 2 + + ((byte1 >> i) & 0x01); + proto_tree_add_uint_format(tree, hf_ua3g_icon_cmd_segment, tvb, offset+1, 2, bytex, + "Segment %d: %s (%d)", i, val_to_str_const(bytex, str_icon_cmd_state, "Unknown"), bytex); + } +} + + +/*----------------------------------------------------------------------------- + AUDIO CONFIGURATION - 49h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_audio_config[] = { + {0x00, "Audio Coding"}, + {0x01, "DPI Channel Allocations"}, + {0x02, "Loudspeaker Volume Adjust"}, + {0x03, "Audio Circuit Configuration"}, + {0x04, "Handsfree Parameters"}, + {0x05, "Loudspeaker Acoustic Parameters"}, + {0x06, "Device Configuration"}, + {0, NULL} +}; + +static const value_string str_audio_coding_law[] = { + {0x00, "A Law"}, + {0x01, "m Law"}, + {0, NULL} +}; + +static const value_string str_device_configuration[] = { + { 0, "Handset Device "}, + { 1, "Headset Device "}, + { 2, "Loudspeaker Device "}, + { 3, "Announce Loudspeaker Device"}, + { 4, "Handsfree Device "}, + { 0, NULL } +}; + +const true_false_string tfs_audio_config_handsfree_return = { "Return Loss Active", "Return Loss Normal" }; +const true_false_string tfs_audio_config_handsfree_handsfree = { "More Full Duplex", "Handsfree Normal" }; + +static void +decode_audio_config(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command; + proto_tree *ua3g_body_tree = tree; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_audio_config, "Unknown")); + + if (!ua3g_body_tree) + return; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_command_audio_config, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (command) { + case 0x00: /* Audio Coding */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_ignored, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_law, tvb, offset+1, 1, ENC_NA); + break; + case 0x01: /* DPI Channel Allocations */ + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_dpi_chan_ua_tx1, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_dpi_chan_ua_tx2, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_dpi_chan_gci_tx1, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_dpi_chan_gci_tx2, tvb, offset+3, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_dpi_chan_cod_tx, tvb, offset+4, 1, ENC_NA); + break; + case 0x02: /* Loudspeaker Volume Adjust */ + { + int i; + for (i = 1; i < 8; i++) { + proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_audio_config_volume_level, tvb, offset, + 1, tvb_get_guint8(tvb, offset), "Volume Level %d: %d", + i, tvb_get_guint8(tvb, offset)); + offset++; + length--; + } + break; + } + case 0x03: /* Audio Circuit Configuration */ + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_dth, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_dtr, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_dtf, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_str, tvb, offset+3, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_ahp1, tvb, offset+4, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_ahp2, tvb, offset+5, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_ath, tvb, offset+6, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_atr, tvb, offset+7, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_atf, tvb, offset+8, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_alm, tvb, offset+9, 1, ENC_NA); + break; + case 0x04: /* Handsfree Parameters */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_handsfree_return, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_handsfree_handsfree, tvb, offset, 1, ENC_NA); + break; + case 0x05: /* Loudspeaker Acoustic Parameters */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_group_listen, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_attenuation, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_stay_in_send, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mtx, tvb, offset+3, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mrc, tvb, offset+4, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_trans_threshold, tvb, offset+5, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_trans_threshold, tvb, offset+6, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_recv_threshold, tvb, offset+7, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_recv_threshold, tvb, offset+8, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_med_recv_threshold, tvb, offset+9, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_high_recv_threshold, tvb, offset+10, 1, ENC_NA); + break; + case 0x06: /* Device Configuration */ + { + static const gchar *str_device_values[] = { + " Internal", + " Rj9 Plug", + " Jack Plug", + " Bluetooth Link", + " USB Link" + }; + emem_strbuf_t *strbuf; + guint8 device_values; + int j; + int device_index = 0; + + strbuf = ep_strbuf_new_label(NULL); + + while (length > 0) { + + device_values = tvb_get_guint8(tvb, offset); + + ep_strbuf_truncate(strbuf, 0); + + if (device_values != 0) { + for (j = 0; j < 5; j++) { + if (device_values & (0x01 << j)) { + ep_strbuf_append(strbuf, str_device_values[j]); + } + } + } else { + ep_strbuf_append(strbuf, " None"); + } + + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "%s:%s", + val_to_str_const(device_index, str_device_configuration, "Unknown"), + strbuf->str); + offset++; + length--; + device_index++; + } + break; + } + default: + { + break; + } + } +} + + +/*----------------------------------------------------------------------------- + AUDIO PADDED PATH - 4Ah (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_audio_padded_path(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset) +{ + proto_tree_add_item(tree, hf_ua3g_audio_padded_path_emission_padded_level, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_audio_padded_path_reception_padded_level, tvb, offset+1, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + KEY RELEASE ON / OFF - 41h (MESSAGE FROM THE SYSTEM) + AMPLIFIED HANDSET (BOOST) - 48h (MESSAGE FROM THE SYSTEM) + LOUDSPEAKER ON / OFF - 4Dh (MESSAGE FROM THE SYSTEM) + ANNOUNCE ON / OFF - 4Eh (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_on_off_level(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length, int hf_opcode) +{ + guint8 command; + const gchar* command_str; + + command = tvb_get_guint8(tvb, offset); + command_str = STR_ON_OFF(command); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", command_str); + + proto_tree_add_item(tree, hf_opcode, tvb, offset, 1, ENC_NA); + + if (length > 1) { + if (command == 0x01) { + proto_tree_add_item(tree, hf_ua3g_on_off_level_level_on_loudspeaker, tvb, offset+1, 1, ENC_NA); + } + } +} + + +/*----------------------------------------------------------------------------- + RING ON / OFF - 4Fh (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_ring(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, guint offset) +{ + guint8 command; + const gchar* command_str; + + command = tvb_get_guint8(tvb, offset); + command_str = STR_ON_OFF(command); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", command_str); + + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_command_ring, tvb, offset, 1, ENC_NA); + + if (command == 0x01) { + proto_tree_add_item(tree, hf_ua3g_ring_melody, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_ring_cadence, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_ring_speaker_level, tvb, offset+3, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_ring_beep_number, tvb, offset+4, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_ring_silent, tvb, offset+5, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_ring_progressive, tvb, offset+5, 1, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + UA DOWNLOAD PROTOCOL - 50h - Only for UA NOE (MESSAGE FROM THE TERMINAL AND FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_ua_dwl_protocol[] = { + {0x00, "Downloading Suggest"}, + {0x01, "Downloading Request"}, + {0x02, "Downloading Acknowledge"}, + {0x03, "Downloading Data"}, + {0x04, "Downloading End"}, + {0x05, "Downloading End Acknowledge"}, + {0x06, "Downloading ISO Checksum"}, + {0x07, "Downloading ISO Checksum Acknowledge"}, + {0, NULL} +}; + +static const value_string str_download_req_force_mode[] = { + {0x00, "System Accept All Refusals"}, + {0x01, "Force Software Lock"}, + {0, NULL} +}; +static const value_string str_download_req_item_id[] = { + {0x00, "Patches File"}, + {0x01, "Application Binary"}, + {0x02, "Datas Binary"}, + {0, NULL} +}; +static const value_string str_download_req_mode_selection_country[] = { + {0x00, "No Check"}, + {0x01, "For All Countries Except Chinese"}, + {0x02, "For Chinese"}, + {0, NULL} +}; + +static const value_string str_download_ack_status[] = { + {0x00, "Ok (Binary Item Downloading In \"Normal\" Progress)"}, + {0x01, "Hardware Failure: Flash Failure"}, + {0x02, "Not Enough Place To Store The Downloaded Binary"}, + {0x03, "Wrong Seq Number On Latest Received Download_Data Message"}, + {0x04, "Wrong Packet Number On Latest Received Download_Data Message"}, + {0x05, "Download Refusal Terminal (Validation Purpose)"}, + {0x06, "Download Refusal Terminal (Development Purpose)"}, + {0x10, "Download Refusal: Hardware Cause (Unknown Flash Device, Incompatible Hardware)"}, + {0x11, "Download Refusal: No Loader Available Into The Terminal"}, + {0x12, "Download Refusal: Software Lock"}, + {0x13, "Download Refusal: Wrong Parameter Into Download Request"}, + {0x20, "Wrong Packet Number On Latest Received Downloading_Data Message"}, + {0x21, "Compress Header Invalid"}, + {0x22, "Decompress Error"}, + {0x23, "Binary Header Invalid"}, + {0x24, "Binary Check Error: Flash Write Error Or Binary Is Invalid"}, + {0x25, "Error Already Signaled - No More Data Accepted"}, + {0x26, "No Downloading In Progress"}, + {0x27, "Too Many Bytes Received (More Than Size Given Into The Download_Req Message)"}, + {0xFF, "Undefined Error"}, + {0, NULL} +}; +static value_string_ext str_download_ack_status_ext = VALUE_STRING_EXT_INIT(str_download_ack_status); + +static const value_string str_download_end_ack_ok[] = { + {0x00, "Ok"}, + {0x01, "Hardware Failure: Flash Problems"}, + {0x02, "Not Enough Place To Store The Downloaded Binary"}, + {0, NULL} +}; + +static const value_string str_iso_checksum_ack_status[] = { + {0x00, "The Checksum Matches"}, + {0x25, "Error Detected And Already Signaled"}, + {0x30, "Checksum Error (All Bytes Received)"}, + {0x31, "Checksum Error (Bytes Missing)"}, + {0, NULL} +}; + +static const value_string str_mem_size[] = { + {0x00, "No Check"}, + {0x01, "128 Kbytes"}, + {0x02, "256 Kbytes"}, + {0x03, "512 Kbytes"}, + {0x04, "1 Mbytes"}, + {0x05, "2 Mbytes"}, + {0x06, "4 Mbytes"}, + {0x07, "8 Mbytes"}, + {0, NULL} +}; + + +static void +decode_ua_dwl_protocol(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command; + proto_tree *ua3g_body_tree = tree, *ua3g_param_tree; + proto_item *ua3g_param_item; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_ua_dwl_protocol, "Unknown")); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_command_ua_dwl_protocol, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (command) { + case 0x00: /* Downloading Suggest (MESSAGE FROM THE TERMINAL) */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_item_identifier, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_item_version_nc, tvb, offset+1, 2, ENC_BIG_ENDIAN); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_cause, tvb, offset+3, 1, ENC_NA); + break; + case 0x01: /* Downloading Request (MESSAGE FROM THE SYSTEM) */ + { + static const gchar *str_bin_info[] = { + "Uncompressed Binary", + "LZO Compressed Binary" + }; + + if (length > 7) { /* Not R1 */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_force_mode, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_item_identifier, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_item_version, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + if (length > 2) { /* Not R1 */ + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Files Included"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_files_inc_boot_binary, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_files_inc_loader_binary, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_files_inc_appli_binary, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_files_inc_data_binary, + tvb, offset, 1, ENC_NA); + offset++; + length--; + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Model Selection"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_model_selection_a, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_model_selection_b, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_model_selection_c, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_model_selection_country_ver, + tvb, offset, 1, ENC_NA); + offset++; + length--; + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Hardware Selection"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe1, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe2, + tvb, offset, 1, ENC_NA); + offset++; + length--; + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Memory Sizes Required"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_memory_sizes_flash, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_memory_sizes_ext_ram, + tvb, offset, 1, ENC_NA); + offset++; + length--; + } else { /* R1 */ + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "Binary Information: %s, Country/Operator/CLient Identifier ?", + str_bin_info[tvb_get_guint8(tvb, offset) & 0x01]); + offset++; + length--; + } + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_binary_length, tvb, offset, 3, ENC_BIG_ENDIAN); + break; + } + case 0x02: /* Downloading Acknowledge (MESSAGE FROM THE TERMINAL) */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_packet_number, tvb, offset, 2, ENC_BIG_ENDIAN); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_download_ack_status, tvb, offset+2, 1, ENC_NA); + break; + case 0x03: /* Downloading Data (MESSAGE FROM THE SYSTEM) */ + { + int i = 1; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_packet_number, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + while (length > 0) { + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "Packet Number %3d: %d", i, tvb_get_guint8(tvb, offset)); + offset++; + length--; + i++; + } + break; + } + case 0x05: /* Downloading End Acknowledge (MESSAGE FROM THE TERMINAL) */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_packet_download_end_ack_ok_status, tvb, offset, 1, ENC_NA); + break; + case 0x06: /* Downloading Iso Checksum (MESSAGE FROM THE SYSTEM) */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_checksum, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x07: /* Downloading ISO Checksum Acknowledge (MESSAGE FROM THE TERMINAL) */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_acknowledge, tvb, offset, 1, ENC_NA); + break; + case 0x04: /* Downloading End (MESSAGE FROM THE SYSTEM) */ + default: + break; + } +} + + +/*----------------------------------------------------------------------------- + DIGIT DIALED - 03h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_digit_dialed(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, guint offset) +{ + proto_tree_add_item(tree, hf_ua3g_digit_dialed_digit_value, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SUBDEVICE_MSG - 04h (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static void +decode_subdevice_msg(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_subdevice_msg_subdev_type, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_subdevice_msg_subdev_address, tvb, offset, 1, ENC_NA); + + proto_tree_add_item(tree, hf_ua3g_subdevice_msg_subdevice_opcode, tvb, offset+1, 1, ENC_NA); + + if (length > 2) { + proto_tree_add_item(tree, hf_ua3g_subdevice_msg_parameter_bytes, tvb, offset+2, length-2, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + IP DEVICE ROUTING - 13h (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static const value_string str_command_cs_ip_device_routing[] = { + {0x00, "Init"}, + {0x01, "Incident"}, + {0x02, "Get Parameters Value Response"}, + {0x03, "QOS Ticket RSP"}, + {0, NULL} +}; + +static const value_string str_cs_ip_device_routing_vta_type[] = { + {0x20, "NOE A"}, + {0x21, "NOE B"}, + {0x22, "NOE C"}, + {0x23, "NOE D"}, + {0, NULL} +}; + +static const value_string str_cs_ip_device_routing_08_compressor[] = { + {0x00, "G.711 A-law"}, + {0x01, "G.711 mu-law"}, + {0x0F, "G.723.1 5.3kbps"}, + {0x10, "G.723.1 6.3kbps"}, + {0x11, "G.729A 8 kbps"}, + {0, NULL} +}; + +static const val64_string str_cs_ip_device_routing_0F_compressor[] = { + {0x00, "G.711 A-law"}, + {0x01, "G.711 mu-law"}, + {0x02, "G.723.1 6.3kbps"}, + {0x03, "G.729"}, + {0x04, "G.723.1 5.3kbps"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_03_parameter_id_vals[] = { + {0x01, "Date Of End Of Communication"}, + {0x02, "Node Number"}, + {0x03, "Ticket Protocol Version"}, + {0x06, "Equipment Type"}, + {0x08, "Local IP Address"}, + {0x09, "Distant IP Address"}, + {0x0A, "Local ID"}, + {0x0B, "Distant ID"}, + {0x0C, "Call Duration (second)"}, + {0x0D, "Local SSRC"}, + {0x0E, "Distant SSRC"}, + {0x0F, "Codec"}, + {0x10, "VAD"}, + {0x11, "ECE"}, + {0x12, "Voice Mode"}, + {0x13, "Transmitted Framing (ms)"}, + {0x14, "Received Framing (ms)"}, + {0x15, "Framing Changes"}, + {0x16, "Number Of RTP Packets Received"}, + {0x17, "Number Of RTP Packets Sent"}, + {0x18, "Number Of RTP Packets Lost"}, + {0x19, "Total Silence Detected (second)"}, + {0x1A, "Number Of SID Received"}, + {0x1B, "Delay Distribution"}, + {0x1C, "Maximum Delay (ms)"}, + {0x1D, "Number Of DTMF Received"}, + {0x1E, "Consecutive BFI"}, + {0x1F, "BFI Distribution"}, + {0x20, "Jitter Depth Distribution"}, + {0x21, "Number Of ICMP Host Unreachable"}, + {0x26, "Firmware Version"}, + {0x29, "DSP Framing (ms)"}, + {0x2A, "Transmitter SID"}, + {0x2D, "Minimum Delay (ms)"}, + {0x2E, "802.1 Q Used"}, + {0x2F, "802.1p Priority"}, + {0x30, "VLAN Id"}, + {0x31, "DiffServ"}, + {0x3D, "200 ms BFI Distribution"}, + {0x3E, "Consecutive RTP Lost"}, + {0, NULL} +}; +static value_string_ext cs_ip_device_routing_03_parameter_id_vals_ext = VALUE_STRING_EXT_INIT(cs_ip_device_routing_03_parameter_id_vals); + +static const value_string cs_ip_device_routing_03_parameter_id_tab_vals[] = { + {0x1B, "Range: Value"}, + {0x1F, "Range: Value"}, + {0x20, "Jitter: Value"}, + {0x3D, "Contents: Value"}, + {0x3E, "Contents: Value"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_cmd03_first_byte_vals[] = { + {0x01, "IP-Phone"}, + {0x02, "Appli-PC"}, + {0x03, "Coupler OmniPCX Enterprise"}, + {0x04, "Coupler OmniPCX Office"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_cmd03_second_byte_vals[] = { + {0x0101, "IP-Phone V2"}, + {0x0102, "NOE-IP"}, + {0x0200, "4980 Softphone (PCMM2)"}, + {0x0201, "WebSoftphoneIP"}, + {0x0300, "INTIP"}, + {0x0301, "GD"}, + {0x0302, "eVA"}, + {0, NULL} +}; + +static const val64_string cs_ip_device_routing_cmd03_voice_mode_vals[] = { + {0x50, "Idle"}, + {0x51, "Handset"}, + {0x52, "Group Listening"}, + {0x53, "On Hook Dial"}, + {0x54, "Handsfree"}, + {0x55, "Headset"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_delay_distribution_range_vals[] = { + {0, "0-40 "}, + {1, "40-80 "}, + {2, "80-150 "}, + {3, "150-250 "}, + {4, "250 and +"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_consecutive_bfi_range_vals[] = { + {0, "0"}, + {1, "1"}, + {2, "2"}, + {3, "3"}, + {4, "4"}, + {5, "5"}, + {6, "5"}, + {7, "7"}, + {8, "8"}, + {9, "9"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_bfi_distribution_range_vals[] = { + {0, "0 "}, + {1, "0-1 "}, + {2, "1-2 "}, + {3, "2-3 "}, + {4, "3 and +"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_200ms_bfi_distribution_range_vals[] = { + {0, "< 10 % "}, + {1, "< 20 % "}, + {2, "< 40 % "}, + {3, "< 60 % "}, + {4, ">= 60 % "}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_consecutive_rtp_lost_range_vals[] = { + {0, "1 "}, + {1, "2 "}, + {2, "3 "}, + {3, "4 "}, + {4, "5 and more"}, + {0, NULL} +}; + +static void +decode_cs_ip_device_routing(proto_tree *tree _U_, tvbuff_t *tvb, + packet_info *pinfo, guint offset, guint length) +{ + guint8 command; + proto_tree *ua3g_body_tree = tree, *ua3g_param_tree; + proto_item *ua3g_param_item; + int i, parameter_id, parameter_length; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_cs_ip_device_routing, "Unknown")); + + if (!ua3g_body_tree) + return; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_cs, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (command) { + case 0x00: + proto_tree_add_item(ua3g_body_tree, hf_ua3g_cs_ip_device_routing_cmd00_vta_type, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_cs_ip_device_routing_cmd00_characteristic_number, tvb, offset+1, 1, ENC_NA); + break; + case 0x01: + { + int j = 0; + if (length == 1) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_cs_ip_device_routing_cmd01_incident_0, tvb, offset, 1, ENC_NA); + } else { + while (length >0) { + j++; + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "Parameter %d Identifier: %d", + j, tvb_get_guint8(tvb, offset)); + offset++; + length--; + } + } + break; + } + case 0x02: + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter, tvb, offset, + parameter_id, parameter_length + 2, + "%s", val_to_str_const(parameter_id, ip_device_routing_cmd_get_param_req_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x00: /* Firmware Version */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_firmware_version, tvb, offset, 2, ENC_BIG_ENDIAN); + break; + case 0x01: /* Firmware Version */ + case 0x02: /* DHCP IP Address */ + case 0x03: /* Local IP Address */ + case 0x04: /* Subnetwork Mask */ + case 0x05: /* Router IP Address */ + case 0x06: /* TFTP IP Address */ + case 0x07: /* Main CPU Address */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x08: /* Default Codec */ + { + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_bytes, tvb, offset, parameter_length, ENC_NA); + } + break; + } + case 0x09: /* Ethernet Drivers Config */ + { + if (parameter_length == 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_speed, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_duplex, tvb, offset+1, 1, ENC_NA); + } else if (parameter_length == 4) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_speed, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_duplex, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_speed, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_duplex, tvb, offset+3, 1, ENC_NA); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + case 0x0A: /* MAC Address */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_mac_address, tvb, offset, 6, ENC_NA); + break; + default: + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x03: + { + int framing_rtp = 0; + + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_ntohs(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter, tvb, offset, + parameter_length+3, parameter_id, "%s", + val_to_str_const(parameter_id, cs_ip_device_routing_03_parameter_id_tab_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_length, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x06: /* Type Of Equipment */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip1, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip2, tvb, offset, 2, ENC_BIG_ENDIAN); + break; + case 0x08: /* Local IP Address */ + case 0x09: /* Distant IP Address */ + case 0x26: /* Firmware Version */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x0A: + case 0x0B: + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_string, tvb, offset, parameter_length, ENC_NA|ENC_ASCII); + break; + case 0x0F: /* Default Codec */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_default_codec, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_default_codec, tvb, offset, 8, ENC_BIG_ENDIAN); + /* XXX - add as expert info ep_strbuf_append(strbuf, "Parameter Value Too Long (more than 64 bits)"); */ + } + break; + case 0x10: /* VAD */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_vad, tvb, offset, 1, ENC_NA); + break; + case 0x11: /* ECE */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_ece, tvb, offset, 1, ENC_NA); + break; + case 0x12: /* Voice Mode */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_voice_mode, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_voice_mode, tvb, offset, 8, ENC_BIG_ENDIAN); + /* XXX - add as expert info ep_strbuf_append(strbuf, "Parameter Value Too Long (more than 64 bits)"); */ + } + break; + case 0x1B: /* Delay Distribution */ + for (i = 0; i < parameter_length; i += 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_delay_distribution, tvb, offset+i, 2, ENC_BIG_ENDIAN); + } + break; + case 0x1E: /* Consecutive BFI */ + for (i = 0; i < parameter_length; i += 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_bfi, tvb, offset+i, 2, ENC_BIG_ENDIAN); + } + break; + case 0x1F: /* BFI Distribution */ + for (i = 0; i < parameter_length; i += 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution, tvb, offset+i, 2, ENC_BIG_ENDIAN); + } + break; + case 0x20: /* Jitter Depth Distribution */ + for (i = 0; i < parameter_length / 4; i+=4) { + proto_tree_add_uint_format(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_jitter_depth_distribution, + tvb, offset+i, 4, tvb_get_ntohl(tvb, offset+i), "Jitter Depth Distribution: +/- %3d ms: %d", + ((2 * i/4) + 1) * framing_rtp / 2, tvb_get_ntohl(tvb, offset+i)); + } + break; + case 0x2E: /* 802.1 Q Used */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021Q_used, tvb, offset, 1, ENC_NA); + break; + case 0x2F: /* 802.1p Priority */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021P_priority, tvb, offset, 1, ENC_NA); + break; + case 0x30: /* VLAN Id */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_vlan_id, tvb, offset, 2, ENC_BIG_ENDIAN); + break; + case 0x31: /* DiffServ */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_diffserv, tvb, offset, 1, ENC_NA); + break; + case 0x3D: /* 200 ms BFI Distribution */ + for (i = 0; i < parameter_length; i += 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution_200ms, tvb, offset+i, 2, ENC_BIG_ENDIAN); + } + break; + case 0x3E: /* Consecutive RTP Lost */ + for (i = 0; i < parameter_length; i += 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_rtp_lost, tvb, offset+i, 2, ENC_BIG_ENDIAN); + } + break; + case 0x14: /* Received Framing (ms) */ + { + /* XXX: What is the point of this assignment? */ + framing_rtp = tvb_get_guint8(tvb, offset + 3); + /* FALLTHROUGH */ + } + case 0x01: /* Date Of End Of Communication */ + case 0x02: /* Node Number */ + case 0x03: /* Ticket Protocol Version */ + case 0x0C: /* Call Duration (second) */ + case 0x0D: /* Local SSRC */ + case 0x0E: /* Distant SSRC */ + case 0x13: /* Transmitted Framing (ms) */ + case 0x15: /* Framing Changes */ + case 0x16: /* Number Of RTP Packets Received */ + case 0x17: /* Number Of RTP Packets Sent */ + case 0x18: /* Number Of RTP Packets Lost */ + case 0x19: /* Total Silence Detected (second) */ + case 0x1A: /* Number Of SID Received */ + case 0x1C: /* Maximum Delay (ms) */ + case 0x1D: /* Number Of DTMF Received */ + case 0x21: /* Number Of ICMP Host Unreachable */ + case 0x29: /* DSP Framing (ms) */ + case 0x2A: /* Transmitter SID */ + case 0x2D: /* Minimum Delay (ms) */ + default: + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + } + default: + break; + } +} + + +/*----------------------------------------------------------------------------- + UNSOLICITED MESSAGE - 9Fh/1Fh (MESSAGE FROM THE TERMINAL) + VERSION RESPONSE - 21h (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static const value_string str_command_unsolicited_msg[] = { + {0x00, "Hardware Reset Acknowledge"}, + {0x01, "Software Reset Acknowledge"}, + {0x02, "Illegal Command Received"}, + {0x05, "Subdevice Down"}, + {0x06, "Segment Failure"}, + {0x07, "UA Device Event"}, + {0, NULL} +}; + +static const value_string str_unsolicited_msg_vta_type[] = { + {0x03, "4035"}, + {0x04, "4020"}, + {0x05, "4010"}, + {0x20, "NOE A"}, + {0x21, "NOE B"}, + {0x22, "NOE C"}, + {0x23, "NOE D"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_other_info_1[] = { + {0x00, "Link Is TDM"}, + {0x01, "Link Is IP"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_other_info_2[] = { + {0x00, "Download Allowed"}, + {0x01, "Download Refused"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_config_ip[] = { + {0x00, "Export Binary (No Thales)"}, + {0x01, "Full Binary (Thales)"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_config_chip[] = { + {0x01, "Ivanoe 1"}, + {0x02, "Ivanoe 2"}, + {0x03, "Reserved"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_config_flash[] = { + {0x00, "No Flash"}, + {0x01, "128 Kbytes"}, + {0x02, "256 Kbytes"}, + {0x03, "512 Kbytes"}, + {0x04, "1 Mbytes"}, + {0x05, "2 Mbytes"}, + {0x06, "4 Mbytes"}, + {0x07, "8 Mbytes"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_config_ram[] = { + {0x00, "No External RAM"}, + {0x01, "128 Kbytes"}, + {0x02, "256 Kbytes"}, + {0x03, "512 Kbytes"}, + {0x04, "1 Mbytes"}, + {0x05, "2 Mbytes"}, + {0x06, "4 Mbytes"}, + {0x07, "8 Mbytes"}, + {0, NULL} +}; + +static const value_string str_unsolicited_msg_subtype[] = { + {0x03, "2x40"}, + {0x04, "1x20"}, + {0x05, "1x20"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_generation[] = { + {0x02, "3"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_design[] = { + {0x00, "Alpha"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_vta_type[] = { + {0x03, "MR2 (4035)"}, + {0x05, "VLE (4010)"}, + {0x07, "LE (4020)"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_design[] = { + {0x06, "Alpha"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_subtype[] = { + {0x06, "2x40"}, + {0x07, "1x20"}, + {0x08, "1x20"}, + {0, NULL} +}; + +static void +decode_unsolicited_msg(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length, guint8 opcode) +{ + guint8 command; + proto_tree *ua3g_body_tree = tree, *ua3g_param_tree; + proto_item *ua3g_param_item; + + command = tvb_get_guint8(tvb, offset); + + if (opcode != 0x21) { + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_unsolicited_msg, "Unknown")); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_command_unsolicited_msg, tvb, offset, 1, ENC_NA); + offset++; + length--; + } else { + command = 0xFF; /* Opcode = 0x21 */ + } + + switch (command) + { + case 0x00: /* Hardware Reset Acknowledge */ + case 0x01: /* Software Reset Acknowledge */ + case 0xFF: /* Opcode = 0x21 : Version Response */ + { + int link, vta_type; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_device_type, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_firmware_version, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + if (opcode != 0x21) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_self_test_result, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + + vta_type = tvb_get_guint8(tvb, offset); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_vta_type, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (vta_type) + { + case 0x03: + case 0x04: + case 0x05: + { + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Characteristic Number"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_char_num_vta_subtype, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_char_num_generation, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_char_num_design, + tvb, offset, 1, ENC_NA); + offset++; + length--; + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_other_information, tvb, offset, 1, ENC_NA); + offset++; + length--; + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Hardware Configuration"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_vta_type, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_design, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_subtype, + tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (opcode != 0x21) { + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "Hook Status/BCM Version: %s Hook", + STR_ON_OFF(tvb_get_guint8(tvb, offset))); + offset++; + length--; + + } + break; + } + case 0x20: + case 0x21: + case 0x22: + case 0x23: + default: + { + link = tvb_get_guint8(tvb, offset); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_other_information_1, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (link == 0x00) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_hardware_version, tvb, offset, 1, ENC_NA); + offset++; + length--; + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Hardware Configuration"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_hard_config_chip, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_hard_config_flash, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_config_ram, + tvb, offset, 1, ENC_NA); + offset++; + length--; + } else { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_other_information_2, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_hardware_config_hard_config_ip, + tvb, offset, 1, ENC_NA); + offset++; + length--; + } + + if (opcode != 0x21) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_hook_status, + tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (length > 0) { + if (link == 0x00) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_firmware_datas_patch_version, + tvb, offset, 2, ENC_BIG_ENDIAN); + if (length > 2) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_firmware_version_loader, tvb, + offset+2, 2, ENC_BIG_ENDIAN); + } + } else { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_datas_version, tvb, offset, 2, ENC_BIG_ENDIAN); + + if (length > 2) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_firmware_version_bootloader, + tvb, offset+2, 2, ENC_BIG_ENDIAN); + } + } + } + } + break; + } + } + break; + } + case 0x02: /* Illegal Command Received */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_opcode_of_bad_command, tvb, offset, 1, ENC_NA); + + if (length > 1) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_next_byte_of_bad_command, tvb, offset+1, length-1, ENC_NA); + } + break; + } + case 0x05: /* Subdevice Down */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_subdevice_address, tvb, offset, 1, ENC_NA); + break; + } + case 0x06: /* Segment Failure */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_segment_failure_t, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_segment_failure_num, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_segment_failure_s, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_segment_failure_l, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_opcode_bad_segment, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_next_byte_of_bad_segment, tvb, offset+2, 1, ENC_NA); + break; + } + case 0x07: /* UA Device Event */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_device_event, tvb, offset, 1, ENC_NA); + break; + } + default: + { + break; + } + } +} + + +/*----------------------------------------------------------------------------- + NON-DIGIT KEY PUSHED - 20h (MESSAGE FROM THE TERMINAL) + DIGIT KEY RELEASED - 26h (MESSAGE FROM THE TERMINAL) + KEY RELEASED - 2Ah (MESSAGE FROM THE TERMINAL) + TM KEY PUSHED - 2Dh (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static void +decode_key_number(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ +#if 0 + proto_tree *ua3g_body_tree; + static const value_string str_first_parameter[] = { + {0x01, "Production Test Command"}, + {0x06, "Reserved For Compatibility"}, + {0x3B, "Volume"}, + {0x42, "Subdevice Address"}, + {0, NULL} + }; +#endif + + if (!tree) + return; + + if (length > 0) { + proto_tree_add_text(tree, tvb, offset, length, + "Key Number: Row %d, Column %d", + (tvb_get_guint8(tvb, offset) & 0xF0), (tvb_get_guint8(tvb, offset) & 0x0F)); + } +} + + +/*----------------------------------------------------------------------------- + I'M HERE - 22h - Only for UA NOE (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static void +decode_i_m_here(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, guint offset) +{ + proto_tree_add_item(tree, hf_ua3g_i_m_here_id_code, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + RESPONSE STATUS INQUIRY - 23h (MESSAGE FROM THE TERMINAL) + SPECIAL KEY STATUS - 29h (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static const value_string str_special_key_parameters[] = { + {0x00, "Not Received Default In Effect"}, + {0x02, "Downloaded Values In Effect"}, + {0, NULL} +}; + +const true_false_string tfs_released_pressed = { "Released", "Pressed" }; + +static void +decode_special_key(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint8 opcode) +{ + if (!tree) + return; + + if (opcode == 0x23) { + proto_tree_add_text(tree, tvb, offset, 1, + "Parameters Received for DTMF: %s", + val_to_str_const((tvb_get_guint8(tvb, offset) & 0x02), str_special_key_parameters, "Unknown")); + proto_tree_add_text(tree, tvb, offset, 1, + "Hookswitch Status: %shook", + STR_ON_OFF(tvb_get_guint8(tvb, offset) & 0x01)); + offset++; + } + + proto_tree_add_item(tree, hf_ua3g_special_key_shift, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_ctrl, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_alt, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_cmd, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_shift_prime, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_ctrl_prime, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_alt_prime, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_cmd_prime, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SUBDEVICE STATE ENQUIRY - 24h (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static void +decode_subdevice_state(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset) +{ + guint8 info; + int i; + + for (i = 0; i <= 7; i++) { + info = tvb_get_guint8(tvb, offset); + proto_tree_add_text(tree, tvb, offset, 1, + "Subdevice %d State: %d", + i, info & 0x0F); + i++; + proto_tree_add_text(tree, tvb, offset, 1, + "Subdevice %d State: %d", + i, (info & 0xF0) >> 4); + offset++; + } +} + + +/*----------------------------------------------------------------------------- + UA3G DISSECTOR + ---------------------------------------------------------------------------*/ +static int +dissect_ua3g(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + gint offset = 0; + proto_item *ua3g_item, *ua3g_body_item; + proto_tree *ua3g_tree, *ua3g_body_tree; + gint length; + guint8 opcode; + const gchar* opcode_str; + e_ua_direction message_direction = (e_ua_direction)data; + + ua3g_item = proto_tree_add_item(tree, proto_ua3g, tvb, 0, -1, ENC_NA); + ua3g_tree = proto_item_add_subtree(ua3g_item, ett_ua3g); + + /* Length of the UA Message */ + length = tvb_get_letohs(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " - UA3G Message:"); + + proto_tree_add_uint(ua3g_tree, hf_ua3g_length, tvb, offset, 2, length); + offset += 2; + + /* Opcode of the UA Message */ + opcode = tvb_get_guint8(tvb, offset); + if (opcode != 0x9f) + opcode = (opcode & 0x7f); + + /* Useful for a research in wireshark */ + if (message_direction == SYS_TO_TERM) { + proto_tree_add_uint(ua3g_tree, hf_ua3g_opcode_sys, tvb, offset, 1, opcode); + opcode_str = val_to_str_ext_const(opcode, &opcodes_vals_sys_ext, "Unknown"); + } else { + proto_tree_add_uint(ua3g_tree, hf_ua3g_opcode_term, tvb, offset, 1, opcode); + opcode_str = val_to_str_ext_const(opcode, &opcodes_vals_term_ext, "Unknown"); + } + + offset++; + length--; + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " %s", opcode_str); + + proto_item_append_text(ua3g_item, ", %s", opcode_str); + + ua3g_body_item = proto_tree_add_text(ua3g_tree, tvb, offset, length, "UA3G Body"); + ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body); + + if (message_direction == SYS_TO_TERM) { + switch (opcode) { + case SC_PRODUCTION_TEST: /* 0x01 */ + { + decode_with_one_parameter(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_opcode_production_test); + break; + } + case SC_SUBDEVICE_RESET: /* 0x06 */ + { + decode_with_one_parameter(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_opcode_subservice_reset); + break; + } + case SC_ARE_YOU_THERE: /* 0x2B */ + { + decode_with_one_parameter(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_opcode_are_you_there); + break; + } + case SC_SET_SPEAKER_VOL: /* 0x3B */ + { + decode_with_one_parameter(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_opcode_set_speaker_vol); + break; + } + case SC_TRACE_ON: /* 0x42 */ + { + decode_with_one_parameter(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_opcode_trace_on); + break; + } + case SC_SUBDEVICE_ESCAPE: /* 0x02 */ + { + decode_subdevice_escape(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SOFT_RESET: /* 0x03 */ + { + decode_software_reset(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_IP_PHONE_WARMSTART: /* 0x04 */ + { + decode_ip_phone_warmstart(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SUPER_MSG: /* 0x0B */ + case SC_SUPER_MSG_2: /* 0x17 */ + { + decode_super_msg(ua3g_body_tree, tvb, pinfo, offset, length, opcode); + break; + } + case SC_SEGMENT_MSG: /* 0x0C */ + { + decode_segment_msg(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_IP_DEVICE_ROUTING: /* 0x13 */ + { + decode_ip_device_routing(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_DEBUG_IN_LINE: /* 0x18 */ + { + decode_debug_in_line(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_LED_COMMAND: /* 0x21 */ + { + decode_led_command(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_LCD_LINE_1_CMD: /* 0x27 */ + case SC_LCD_LINE_2_CMD: /* 0x28 */ + { + decode_lcd_line_cmd(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_MAIN_VOICE_MODE: /* 0x29 */ + { + decode_main_voice_mode(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SUBDEVICE_METASTATE: /* 0x2C */ + { + decode_subdevice_metastate(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_DWL_DTMF_CLCK_FORMAT: /* 0x30 */ + { + decode_dwl_dtmf_clck_format(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SET_CLCK: /* 0x31 */ + { + decode_set_clck(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_VOICE_CHANNEL: /* 0x32 */ + { + decode_voice_channel(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_EXTERNAL_RINGING: /* 0x33 */ + { + decode_external_ringing(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_LCD_CURSOR: /* 0x35 */ + { + decode_lcd_cursor(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_DWL_SPECIAL_CHAR: /* 0x36 */ + { + decode_dwl_special_char(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SET_CLCK_TIMER_POS: /* 0x38 */ + { + decode_set_clck_timer_pos(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_SET_LCD_CONTRAST: /* 0x39 */ + { + decode_set_lcd_contrast(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_BEEP: /* 0x3C */ + { + decode_beep(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SIDETONE: /* 0x3D */ + { + decode_sidetone(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_RINGING_CADENCE: /* 0x3E */ + { + decode_ringing_cadence(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_MUTE: /* 0x3F */ + { + decode_mute(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_FEEDBACK: /* 0x40 */ + { + decode_feedback(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_READ_PERIPHERAL: /* 0x44 */ + case SC_WRITE_PERIPHERAL: /* 0x45 */ + { + decode_r_w_peripheral(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_ICON_CMD: /* 0x47 */ + { + decode_icon_cmd(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_AUDIO_CONFIG: /* 0x49 */ + { + decode_audio_config(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_AUDIO_PADDED_PATH: /* 0x4A */ + { + decode_audio_padded_path(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_KEY_RELEASE: /* 0x41 */ + { + decode_on_off_level(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_command_key_release); + break; + } + case SC_AMPLIFIED_HANDSET: /* 0x48 */ + { + decode_on_off_level(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_command_amplified_handset); + break; + } + case SC_LOUDSPEAKER: /* 0x4D */ + { + decode_on_off_level(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_command_loudspeaker); + break; + } + case SC_ANNOUNCE: /* 0x4E */ + { + decode_on_off_level(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_command_announce); + break; + } + case SC_RING: /* 0x4F */ + { + decode_ring(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_UA_DWL_PROTOCOL: /* 0x50 */ + { + decode_ua_dwl_protocol(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + /* Case for UA3G message with only opcode (No body) */ + case SC_NOP: /* 0x00 */ + case SC_HE_ROUTING: /* 0x05 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_LOOPBACK_ON: /* 0x07 */ + case SC_LOOPBACK_OFF: /* 0x08 */ + case SC_VIDEO_ROUTING: /* 0x09 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_REMOTE_UA_ROUTING: /* 0x0D NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_VERY_REMOTE_UA_ROUTING: /* 0x0E NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_OSI_ROUTING: /* 0x0F NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_ABC_A_ROUTING: /* 0x11 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_IBS_ROUTING: /* 0x12 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_M_REFLEX_HUB_ROUTING: /* 0x14 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_START_BUZZER: /* 0x22 */ + case SC_STOP_BUZZER: /* 0x23 */ + case SC_ENABLE_DTMF: /* 0x24 */ + case SC_DISABLE_DTMF: /* 0x25 */ + case SC_CLEAR_LCD_DISP: /* 0x26 */ + case SC_VERSION_INQUIRY: /* 0x2A */ + case SC_VTA_STATUS_INQUIRY: /* 0x2D */ + case SC_SUBDEVICE_STATE: /* 0x2E */ + case SC_AUDIO_IDLE: /* 0x3A */ + case SC_TRACE_OFF: /* 0x43 */ + case SC_ALL_ICONS_OFF: /* 0x46 */ + case SC_RELEASE_RADIO_LINK: /* 0x4B */ + case SC_DECT_HANDOVER: /* 0x4C NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + default: + { + break; + } + } + } + if (message_direction == TERM_TO_SYS) { + switch (opcode) { + case CS_DIGIT_DIALED: /* 0x03 */ + { + decode_digit_dialed(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case CS_SUBDEVICE_MSG: /* 0x04 */ + { + decode_subdevice_msg(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_SUPER_MSG: /* 0x0B */ + case CS_SUPER_MSG_2: /* 0x17 */ + { + decode_super_msg(ua3g_body_tree, tvb, pinfo, offset, length, opcode); + break; + } + case CS_SEGMENT_MSG: /* 0x0C */ + { + decode_segment_msg(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_IP_DEVICE_ROUTING: /* 0x13 */ + { + decode_cs_ip_device_routing(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_DEBUG_IN_LINE: /* 0x18 */ + { + decode_debug_in_line(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_NON_DIGIT_KEY_PUSHED: /* 0x20 Key translation not sure */ + case CS_DIGIT_KEY_RELEASED: /* 0x26 Key translation not sure */ + case CS_KEY_RELEASED: /* 0x2A */ + case CS_TM_KEY_PUSHED: /* 0x2D Key translation not sure */ + { + decode_key_number(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_UNSOLICITED_MSG: /* 0x9F (0x1F) */ + case CS_VERSION_RESPONSE: /* 0x21 */ + { + decode_unsolicited_msg(ua3g_body_tree, tvb, pinfo, offset, length, opcode); + break; + } + case CS_I_M_HERE: /* 0x22 */ + { + decode_i_m_here(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case CS_RSP_STATUS_INQUIRY: /* 0x23 */ + case CS_SPECIAL_KEY_STATUS: /* 0x29 */ + { + decode_special_key(ua3g_body_tree, tvb, pinfo, offset, opcode); + break; + } + case CS_SUBDEVICE_STATE: /* 0x24 */ + { + decode_subdevice_state(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case CS_PERIPHERAL_CONTENT: /* 0x2B */ + { + decode_r_w_peripheral(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_UA_DWL_PROTOCOL: /* 0x50 */ + { + decode_ua_dwl_protocol(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + /* Case for UA3G message with only opcode (No body) */ + case CS_NOP_ACK: /* 0x00 */ + case CS_HANDSET_OFFHOOK: /* 0x01 */ + case CS_HANDSET_ONHOOK: /* 0x02 */ + case CS_HE_ROUTING: /* 0x05 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_LOOPBACK_ON: /* 0x06 */ + case CS_LOOPBACK_OFF: /* 0x07 */ + case CS_VIDEO_ROUTING: /* 0x09 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_WARMSTART_ACK: /* 0x0A */ + case CS_REMOTE_UA_ROUTING: /* 0x0D NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_VERY_REMOTE_UA_R: /* 0x0E NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_OSI_ROUTING: /* 0x0F NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_ABC_A_ROUTING: /* 0x11 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_IBS_ROUTING: /* 0x12 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_TRACE_ON_ACK: /* 0x27 */ + case CS_TRACE_OFF_ACK: /* 0x28 */ + default: + { + break; + } + } + } + + return tvb_length(tvb); +} + + +/*----------------------------------------------------------------------------- + DISSECTORS REGISTRATION FUNCTIONS + ---------------------------------------------------------------------------*/ +void +proto_register_ua3g(void) +{ + static hf_register_info hf[] = + { + { &hf_ua3g_length, + { "Length", "ua3g.length", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_sys, + { "Opcode", "ua3g.opcode", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, &opcodes_vals_sys_ext, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_term, + { "Opcode", "ua3g.opcode", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, &opcodes_vals_term_ext, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_production_test, + { "Production Test Command", "ua3g.production_test", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_subservice_reset, + { "Reserved For Compatibility", "ua3g.subservice_reset", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_are_you_there, + { "Temporization", "ua3g.are_you_there", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_set_speaker_vol, + { "Volume", "ua3g.set_speaker_vol", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_trace_on, + { "Subdevice Address", "ua3g.trace_on", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip, + { "IP Device Routing", "ua3g.ip", + FT_UINT8, BASE_HEX, VALS(str_command_ip_device_routing), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip_cs, + { "IP Device Routing", "ua3g.ip.cs", + FT_UINT8, BASE_HEX, VALS(str_command_cs_ip_device_routing), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_led, + { "Led Command", "ua3g.command.led", + FT_UINT8, BASE_HEX, VALS(str_command_led), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_lcd_line, + { "LCD Line Command", "ua3g.command.lcd_line", + FT_UINT8, BASE_HEX, VALS(str_command_lcd_line), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_main_voice_mode, + { "Voice Mode", "ua3g.command.main_voice_mode", + FT_UINT8, BASE_HEX, VALS(str_main_voice_mode), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_set_clck, + { "Set Clock", "ua3g.command.set_clck", + FT_UINT8, BASE_HEX, VALS(str_command_set_clck), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_external_ringing_command, + { "External Ringing Command", "ua3g.command.external_ringing", + FT_UINT8, BASE_HEX, VALS(str_ext_ring_cmd), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_lcd_cursor, + { "Cursor", "ua3g.lcd_cursor", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x02, + NULL, HFILL } + }, + { &hf_ua3g_command_beep, + { "Beep", "ua3g.command.beep", + FT_UINT8, BASE_HEX, VALS(str_command_beep), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_sidetone, + { "Sidetone", "ua3g.command.sidetone", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_mute, + { "Microphone", "ua3g.command.mute", + FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x01, + NULL, HFILL } + }, + { &hf_ua3g_command_feedback, + { "Feedback", "ua3g.command.feedback", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_audio_config, + { "Audio Config", "ua3g.command.audio_config", + FT_UINT8, BASE_HEX, VALS(str_command_audio_config), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_key_release, + { "Key Release", "ua3g.command.key_release", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_amplified_handset, + { "Amplified Handset (Boost)", "ua3g.command.amplified_handset", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_loudspeaker, + { "Loudspeaker", "ua3g.command.loudspeaker", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_announce, + { "Announce", "ua3g.command.announce", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_ring, + { "Ring", "ua3g.command.ring", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_ua_dwl_protocol, + { "UA Download Protocol", "ua3g.command.ua_dwl_protocol", + FT_UINT8, BASE_HEX, VALS(str_command_ua_dwl_protocol), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_unsolicited_msg, + { "Unsolicited Message", "ua3g.command.unsolicited_msg", + FT_UINT8, BASE_HEX, VALS(str_command_unsolicited_msg), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip_device_routing_stop_rtp_parameter, + { "Parameter", "ua3g.ip.stop_rtp.parameter", + FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_stop_rtp_vals), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip_device_routing_stop_rtp_parameter_length, + { "Length", "ua3g.ip.stop_rtp.parameter.length", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip_device_routing_stop_rtp_parameter_value_num, + { "Value", "ua3g.ip.stop_rtp.parameter.value.num", + FT_UINT64, BASE_HEX, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip_device_routing_stop_rtp_parameter_value_bytes, + { "Value", "ua3g.ip.stop_rtp.parameter.value.bytes", + FT_BYTES, BASE_NONE, NULL, 0x00, + NULL, HFILL } + }, + /* Generated from convert_proto_tree_add_text.pl */ + { &hf_ua3g_subdevice_address, { "Subdevice Address", "ua3g.subdevice.address", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL }}, + { &hf_ua3g_subdevice_opcode, { "Subdevice Opcode", "ua3g.subdevice.opcode", FT_UINT8, BASE_HEX, NULL, 0x7F, NULL, HFILL }}, + { &hf_ua3g_subdevice_parameter_bytes, { "Parameter Bytes", "ua3g.subdevice.parameter_bytes", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_software_reset, { "Software Reset", "ua3g.software_reset", FT_UINT8, BASE_DEC, VALS(software_reset_verswitch_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_phone_warmstart, { "IP Phone Warmstart", "ua3g.ip_phone_warmstart", FT_UINT8, BASE_DEC, VALS(str_command_ip_phone_warmstart), 0x0, NULL, HFILL }}, + { &hf_ua3g_super_msg_length, { "Length", "ua3g.super_msg.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_super_msg_data, { "Data", "ua3g.super_msg.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_segment_msg_num_remaining, { "Number Of Remaining Segments", "ua3g.segment_msg.num_remaining", FT_UINT8, BASE_DEC, NULL, 0x7F, NULL, HFILL }}, + { &hf_ua3g_segment_msg_length, { "Length", "ua3g.segment_msg.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_segment_message_data, { "Segment Message Data", "ua3g.segment_message.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter, { "Parameter", "ua3g.ip.reset.parameter", FT_UINT8, BASE_DEC, VALS(ip_device_routing_cmd_reset_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_length, { "Length", "ua3g.ip.reset.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_direction, { "Direction", "ua3g.ip.start_rtp.direction", FT_UINT8, BASE_DEC, VALS(start_rtp_str_direction), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter, { "Parameter", "ua3g.ip.start_rtp.parameter", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &ip_device_routing_cmd_start_rtp_vals_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_length, { "Length", "ua3g.ip.start_rtp.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_redirect_parameter, { "Parameter", "ua3g.ip.redirect.parameter", FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_redirect_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_redirect_parameter_length, { "Length", "ua3g.ip.redirect.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_def_tones_num_entries, { "Number Of Entries", "ua3g.ip.def_tones.num_entries", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_def_tones_frequency_1, { "Frequency 1 (Hz)", "ua3g.ip.def_tones.frequency_1", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_def_tones_level_1, { "Level 1 (dB)", "ua3g.ip.def_tones.level_1", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_def_tones_frequency_2, { "Frequency 2 (Hz)", "ua3g.ip.def_tones.frequency_2", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_def_tones_level_2, { "Level 2 (dB)", "ua3g.ip.def_tones.level_2", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_tone_direction, { "Direction", "ua3g.ip.start_tone.direction", FT_UINT8, BASE_DEC, VALS(ip_device_routing_tone_direction_vals), 0xC0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_tone_num_entries, { "Number of entries", "ua3g.ip.start_tone.num_entries", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_tone_identification, { "Identification", "ua3g.ip.start_tone.identification", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_tone_duration, { "Duration (ms)", "ua3g.ip.start_tone.duration", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_listen_rtp_parameter, { "Parameter", "ua3g.ip.listen_rtp.parameter", FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_listen_rtp_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_listen_rtp_parameter_length, { "Length", "ua3g.ip.listen_rtp.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_get_param_req_parameter, { "Parameter", "ua3g.ip.get_param_req.parameter", FT_UINT8, BASE_DEC, VALS(ip_device_routing_cmd_get_param_req_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter, { "Parameter", "ua3g.ip.set_param_req.parameter", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &ip_device_routing_cmd_set_param_req_vals_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_length, { "Length", "ua3g.ip.set_param_req.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_digit_value, { "Digit Value", "ua3g.ip.digit_value", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &str_digit_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_pause_restart_rtp_parameter, { "Parameter", "ua3g.ip.pause_restart_rtp.parameter", FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_pause_restart_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_length, { "Length", "ua3g.ip.pause_restart_rtp.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_value, { "Value", "ua3g.ip.pause_restart_rtp.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter, { "Parameter", "ua3g.ip.start_stop_record_rtp.parameter", FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_record_rtp_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_length, { "Length", "ua3g.ip.start_stop_record_rtp.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_debug_in_line, { "Text String With Debug", "ua3g.debug_in_line", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_command_led_number, { "Led Number", "ua3g.command.led.number", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options, { "LCD Options", "ua3g.command.lcd_line.lcd_options", FT_UINT8, BASE_HEX, NULL, 0xFC, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_starting_column, { "Starting Column", "ua3g.command.lcd_line.starting_column", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_tune, { "Tune", "ua3g.main_voice_mode.tune", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_cadence, { "Cadence", "ua3g.main_voice_mode.cadence", FT_UINT8, BASE_DEC, VALS(str_cadence), 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_speaker_volume, { "Speaker Volume", "ua3g.main_voice_mode.speaker_volume", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_microphone_volume, { "Microphone Volume", "ua3g.main_voice_mode.microphone_volume", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_sending_level, { "Sending Level (dB)", "ua3g.main_voice_mode.sending_level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_handset_level, { "Receiving Level (dB)", "ua3g.main_voice_mode.handset_level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_headset_level, { "Receiving Level (dB)", "ua3g.main_voice_mode.headset_level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_handsfree_level, { "Sending Level (dB)", "ua3g.main_voice_mode.handsfree_level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_metastate_subchannel_address, { "Subchannel Address", "ua3g.subdevice_metastate.subchannel_address", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_metastate_new_metastate, { "New Metastate", "ua3g.subdevice_metastate.new_metastate", FT_UINT8, BASE_DEC, VALS(str_new_metastate), 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_dtmf_clck_format_minimum_on_time, { "Minimum 'ON' Time (ms)", "ua3g.dwl_dtmf_clck_format.minimum_on_time", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_dtmf_clck_format_inter_digit_pause_time, { "Inter-Digit Pause Time (ms)", "ua3g.dwl_dtmf_clck_format.inter_digit_pause_time", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_dtmf_clck_format_clock_time_format, { "Clock Time Format", "ua3g.dwl_dtmf_clck_format.clock_time_format", FT_UINT8, BASE_DEC, VALS(str_clock_format), 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_dtmf_clck_format_dtmf_country_adaptation, { "DTMF Country Adaptation", "ua3g.dwl_dtmf_clck_format.dtmf_country_adaptation", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_channel_mode, { "Channel Mode", "ua3g.voice_channel.channel_mode", FT_BOOLEAN, 8, TFS(&tfs_voice_channel_channel_mode), 0x01, NULL, HFILL }}, + { &hf_ua3g_voice_channel_codec, { "Codec", "ua3g.voice_channel.codec", FT_BOOLEAN, 8, TFS(&tfs_voice_channel_codec), 0x02, NULL, HFILL }}, + { &hf_ua3g_voice_channel_voice_channel, { "Voice Channel", "ua3g.voice_channel.voice_channel", FT_BOOLEAN, 8, TFS(&tfs_voice_channel_voice_channel), 0x04, NULL, HFILL }}, + { &hf_ua3g_voice_channel_main_voice, { "Main Voice", "ua3g.voice_channel.main_voice", FT_UINT8, BASE_DEC, VALS(str_voice_channel), 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_announce, { "Announce", "ua3g.voice_channel.announce", FT_UINT8, BASE_DEC, VALS(str_voice_channel), 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_b_general, { "B General", "ua3g.voice_channel.b_general", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_b_loud_speaker, { "B Loud Speaker", "ua3g.voice_channel.b_loud_speaker", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_b_ear_piece, { "B Ear Piece", "ua3g.voice_channel.b_ear_piece", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_b_microphones, { "B Microphones", "ua3g.voice_channel.b_microphones", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_lcd_cursor_line_number, { "Line Number", "ua3g.lcd_cursor.line_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_special_char_character_number, { "Character Number", "ua3g.dwl_special_char.character_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_special_char_byte, { "Byte", "ua3g.dwl_special_char.byte", FT_UINT8, BASE_DEC, NULL, 0xFF, NULL, HFILL }}, + { &hf_ua3g_set_clck_timer_pos_clock_line_number, { "Clock Line Number", "ua3g.set_clck_timer_pos.clock_line_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_set_clck_timer_pos_clock_column_number, { "Clock Column Number", "ua3g.set_clck_timer_pos.clock_column_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_set_clck_timer_pos_call_timer_line_number, { "Call Timer Line Number", "ua3g.set_clck_timer_pos.call_timer_line_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_set_clck_timer_pos_call_timer_column_number, { "Call Timer Column Number", "ua3g.set_clck_timer_pos.call_timer_column_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_set_lcd_contrast_driver_number, { "Driver Number", "ua3g.set_lcd_contrast.driver_number", FT_UINT8, BASE_DEC, VALS(str_driver_number), 0x0, NULL, HFILL }}, + { &hf_ua3g_set_lcd_contrast_contrast_value, { "Contrast Value", "ua3g.set_lcd_contrast.contrast_value", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_beep_destination, { "Destination", "ua3g.command.beep.destination", FT_UINT8, BASE_DEC, VALS(str_beep_start_destination), 0x0, NULL, HFILL }}, + { &hf_ua3g_beep_on_off, { "On / Off", "ua3g.command.beep.on_off", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x80, NULL, HFILL }}, + { &hf_ua3g_beep_cadence, { "Cadence", "ua3g.command.beep.cadence", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_beep_beep_number, { "Beep Number", "ua3g.command.beep.beep_number", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_beep_number_of_notes, { "Number Of Notes", "ua3g.command.beep.number_of_notes", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_beep_terminator, { "Terminator", "ua3g.command.beep.terminator", FT_UINT8, BASE_DEC, VALS(str_beep_terminator), 0x0, NULL, HFILL }}, + { &hf_ua3g_sidetone_level, { "Level", "ua3g.command.sidetone.level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ringing_cadence_cadence, { "Cadence", "ua3g.ringing_cadence.cadence", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ringing_cadence_on_off, { "On / Off", "ua3g.ringing_cadence.on_off", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x80, NULL, HFILL }}, + { &hf_ua3g_ringing_cadence_length, { "Length (ms)", "ua3g.ringing_cadence.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_feedback_level, { "Level (dB)", "ua3g.command.feedback.level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_feedback_duration, { "Duration (ms)", "ua3g.command.feedback.duration", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_r_w_peripheral_address, { "Address", "ua3g.r_w_peripheral.address", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_r_w_peripheral_content, { "Content", "ua3g.r_w_peripheral.content", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_icon_cmd_icon_number, { "Icon Number", "ua3g.icon_cmd.icon_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_icon_cmd_segment, { "Segment", "ua3g.icon_cmd.segment", FT_UINT16, BASE_DEC, VALS(str_icon_cmd_state), 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_ignored, { "Ignored", "ua3g.command.audio_config.ignored", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_law, { "Law", "ua3g.command.audio_config.law", FT_UINT8, BASE_DEC, VALS(str_audio_coding_law), 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_volume_level, { "Volume Level", "ua3g.command.audio_config.volume_level", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_handsfree_return, { "Return", "ua3g.command.audio_config.handsfree_return", FT_BOOLEAN, 8, TFS(&tfs_audio_config_handsfree_return), 0x01, NULL, HFILL }}, + { &hf_ua3g_audio_config_handsfree_handsfree, { "Handsfree", "ua3g.command.audio_config.handsfree", FT_BOOLEAN, 8, TFS(&tfs_audio_config_handsfree_handsfree), 0x02, NULL, HFILL }}, + { &hf_ua3g_audio_padded_path_emission_padded_level, { "Emission Padded Level", "ua3g.audio_padded_path.emission_padded_level", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_padded_path_reception_padded_level, { "Reception Padded Level", "ua3g.audio_padded_path.reception_padded_level", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_on_off_level_level_on_loudspeaker, { "Level on Loudspeaker (dB)", "ua3g.on_off_level.level_on_loudspeaker", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ring_melody, { "Melody", "ua3g.command.ring.melody", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ring_cadence, { "Cadence", "ua3g.command.ring.cadence", FT_UINT8, BASE_DEC, VALS(str_cadence), 0x0, NULL, HFILL }}, + { &hf_ua3g_ring_speaker_level, { "Speaker level (dB)", "ua3g.command.ring.speaker_level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ring_beep_number, { "Beep number", "ua3g.command.ring.beep_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ring_silent, { "Silent", "ua3g.command.ring.silent", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x80, NULL, HFILL }}, + { &hf_ua3g_ring_progressive, { "Progressive", "ua3g.command.ring.progressive", FT_UINT8, BASE_DEC, NULL, 0x03, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_item_identifier, { "Item Identifier", "ua3g.ua_dwl_protocol.item_identifier", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_cause, { "Cause", "ua3g.ua_dwl_protocol.cause", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_force_mode, { "Force Mode", "ua3g.ua_dwl_protocol.force_mode", FT_UINT8, BASE_DEC, VALS(str_download_req_force_mode), 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_item_version, { "Item Version", "ua3g.ua_dwl_protocol.item_version", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_item_version_nc, { "Item Version", "ua3g.ua_dwl_protocol.item_version", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_binary_length, { "Binary Length", "ua3g.ua_dwl_protocol.binary_length", FT_UINT24, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_packet_number, { "Packet Number", "ua3g.ua_dwl_protocol.packet_number", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_download_ack_status, { "Status", "ua3g.ua_dwl_protocol.download_ack_status", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &str_download_ack_status_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_packet_download_end_ack_ok_status, { "Status", "ua3g.ua_dwl_protocol_packet.download_end_ack_ok_status", FT_UINT8, BASE_DEC, VALS(str_download_end_ack_ok), 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_checksum, { "Checksum", "ua3g.ua_dwl_protocol.checksum", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_acknowledge, { "Acknowledge", "ua3g.ua_dwl_protocol.acknowledge", FT_UINT8, BASE_DEC, VALS(str_iso_checksum_ack_status), 0x0, NULL, HFILL }}, + { &hf_ua3g_digit_dialed_digit_value, { "Digit Value", "ua3g.digit_dialed.digit_value", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &str_digit_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_msg_subdev_type, { "Subdev Type", "ua3g.subdevice_msg.subdev_type", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_msg_subdev_address, { "Subdev Address", "ua3g.subdevice_msg.subdev_address", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_msg_subdevice_opcode, { "Subdevice Opcode", "ua3g.subdevice_msg.subdevice_opcode", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_msg_parameter_bytes, { "Parameter Bytes", "ua3g.subdevice_msg.parameter_bytes", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd00_vta_type, { "VTA Type", "ua3g.ip.cs.cmd00.vta_type", FT_UINT8, BASE_DEC, VALS(str_cs_ip_device_routing_vta_type), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd00_characteristic_number, { "Characteristic Number", "ua3g.ip.cs.cmd00.characteristic_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd01_incident_0, { "Incident 0", "ua3g.ip.cs.cmd01.incident_0", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter, { "Parameter", "ua3g.ip.cs.cmd02.parameter", FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_get_param_req_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_length, { "Length", "ua3g.ip.cs.cmd02.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter, { "Parameter", "ua3g.ip.cs.cmd03.parameter", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cs_ip_device_routing_03_parameter_id_vals_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_length, { "Length", "ua3g.ip.cs.cmd03.parameter.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_device_type, { "Device Type", "ua3g.unsolicited_msg.device_type", FT_UINT8, BASE_DEC, VALS(str_device_type), 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_self_test_result, { "Self-Test Result", "ua3g.unsolicited_msg.self_test_result", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_vta_type, { "VTA Type", "ua3g.unsolicited_msg.vta_type", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_vta_type), 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_other_information, { "Other Information", "ua3g.unsolicited_msg.other_information", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_other_info_2), 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_other_information_1, { "Other Information 1", "ua3g.unsolicited_msg.other_information_1", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_other_info_1), 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_version, { "Hardware Version", "ua3g.unsolicited_msg.hardware_version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_other_information_2, { "Other Information 2", "ua3g.unsolicited_msg.other_information_2", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_other_info_2), 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_firmware_datas_patch_version, { "Firmware Datas Patch Version", "ua3g.unsolicited_msg.firmware_datas_patch_version", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_firmware_version_loader, { "Firmware Version (Loader)", "ua3g.unsolicited_msg.firmware_version_loader", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_firmware_version, { "Firmware Version", "ua3g.unsolicited_msg.firmware_version", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_datas_version, { "Datas Version", "ua3g.unsolicited_msg.datas_version", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_firmware_version_bootloader, { "Firmware Version (Bootloader)", "ua3g.unsolicited_msg.firmware_version_bootloader", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_opcode_of_bad_command, { "Opcode Of Bad Command", "ua3g.unsolicited_msg.opcode_of_bad_command", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_next_byte_of_bad_command, { "Next Byte Of Bad Command", "ua3g.unsolicited_msg.next_byte_of_bad_command", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_subdevice_address, { "Subdevice Address", "ua3g.unsolicited_msg.subdevice_address", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_segment_failure_t, { "T", "ua3g.unsolicited_msg.segment_failure.t", FT_BOOLEAN, 8, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_segment_failure_num, { "Num", "ua3g.unsolicited_msg.segment_failurenum", FT_BOOLEAN, 8, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_segment_failure_s, { "/S", "ua3g.unsolicited_msg.segment_failure.s", FT_BOOLEAN, 8, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_segment_failure_l, { "L", "ua3g.unsolicited_msg.segment_failure.l", FT_BOOLEAN, 8, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_opcode_bad_segment, { "Opcode Bad Segment", "ua3g.unsolicited_msg.opcode_bad_segment", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_next_byte_of_bad_segment, { "Next Byte Of Bad Segment", "ua3g.unsolicited_msg.next_byte_of_bad_segment", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_device_event, { "Device Event", "ua3g.unsolicited_msg.device_event", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_i_m_here_id_code, { "Id Code", "ua3g.i_m_here.id_code", FT_UINT8, BASE_DEC, VALS(str_device_type), 0x0, NULL, HFILL }}, + { &hf_ua3g_segment_msg_segment, { "F/S", "ua3g.segment_msg.segment", FT_BOOLEAN, 8, TFS(&tfs_segment_msg_segment), 0x80, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update, { "NOE Update Mode", "ua3g.ip.reset.parameter.noe_update", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_bootloader, { "Bootloader", "ua3g.ip.reset.parameter.noe_update.bootloader", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x01, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_data, { "Data", "ua3g.ip.reset.parameter.noe_update.data", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x02, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_customization, { "Customization", "ua3g.ip.reset.parameter.noe_update.customization", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x04, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_localization, { "Localization", "ua3g.ip.reset.parameter.noe_update.localization", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x08, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_code, { "Code", "ua3g.ip.reset.parameter.noe_update.code", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x10, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_sip, { "SIP", "ua3g.ip.reset.parameter.noe_update.sip", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x20, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_bad_sec_mode, { "Bad Sec Mode", "ua3g.ip.reset.parameter.bad_sec_mode", FT_UINT8, BASE_DEC, VALS(reset_param_bad_sec_mode), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_value, { "Value", "ua3g.ip.reset.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_cust_name, { "Cust_Name", "ua3g.ip.reset.parameter.cust_name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_l10n_name, { "L10N_Name", "ua3g.ip.reset.parameter.l10n_name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_value, { "Value", "ua3g.ip.start_rtp.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_ip, { "IP", "ua3g.ip.start_rtp.parameter.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_compressor, { "Compressor", "ua3g.ip.start_rtp.parameter.compressor", FT_UINT64, BASE_DEC|BASE_VAL64_STRING, VALS(str_start_rtp_compressor), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_enabler, { "Enabler", "ua3g.ip.start_rtp.parameter.enabler", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_send_qos, { "Must Send QOS Tickets", "ua3g.ip.start_rtp.parameter.enabler", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_uint, { "Value", "ua3g.ip.start_rtp.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_dtmf_sending, { "Send DTMF", "ua3g.ip.start_rtp.parameter.dtmf_sending", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_rfc2198, { "Enable RFC 2198", "ua3g.ip.start_rtp.parameter.rfc2198", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_srtp_encryption, { "Enable SRTP Encryption", "ua3g.ip.start_rtp.parameter.srtp_encryption", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_redirect_parameter_value, { "Value", "ua3g.ip.redirect.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_redirect_parameter_ip, { "IP", "ua3g.ip.redirect.parameter.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_redirect_parameter_uint, { "Value", "ua3g.ip.redirect.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_listen_rtp_parameter_value, { "Value", "ua3g.ip.listen_rtp.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_listen_rtp_parameter_ip, { "IP", "ua3g.ip.listen_rtp.parameter.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_listen_rtp_parameter_port, { "Port", "ua3g.ip.listen_rtp.parameter.port", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_value, { "Value", "ua3g.ip.set_param_req.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_tftp_backup_ip, { "TFTP Backup IP", "ua3g.ip.set_param_req.parameter.tftp_backup_ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_uint, { "Value", "ua3g.ip.set_param_req.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_err_string, { "Value", "ua3g.ip.set_param_req.parameter.err_string", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_compressor, { "Compressor", "ua3g.ip.set_param_req.parameter.compressor", FT_UINT8, BASE_DEC, VALS(str_set_param_req_compressor), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_set_pc_port_status, { "Set PC Port status", "ua3g.ip.set_param_req.parameter.set_pc_port_status", FT_UINT8, BASE_DEC, VALS(str_set_pc_port_status), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_record_rtp_auth, { "Record RTP Authorization", "ua3g.ip.set_param_req.parameter.record_rtp_auth", FT_UINT8, BASE_DEC, VALS(str_enable_feature), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_security_flag_filter, { "Filtering", "ua3g.ip.set_param_req.parameter.security_flag.filter", FT_BOOLEAN, 8, TFS(&tfs_active_inactive), 0x01, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_uint, { "Value", "ua3g.ip.pause_restart_rtp.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_value, { "Value", "ua3g.ip.start_stop_record_rtp.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_remote_ip, { "Remote IP", "ua3g.ip.start_stop_record_rtp.parameter.remote_ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_uint, { "Value", "ua3g.ip.start_stop_record_rtp.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_dpi_chan_ua_tx1, { "UA Channel UA-TX1", "ua3g.command.audio_config.dpi_chan.ua_tx1", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_dpi_chan_ua_tx2, { "UA Channel UA-TX2", "ua3g.command.audio_config.dpi_chan.ua_tx2", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_dpi_chan_gci_tx1, { "GCI Channel GCI-TX1", "ua3g.command.audio_config.dpi_chan.gci_tx1", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_dpi_chan_gci_tx2, { "GCI Channel GCI-TX2", "ua3g.command.audio_config.dpi_chan.gci_tx2", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_dpi_chan_cod_tx, { "Codec Channel COD-TX", "ua3g.command.audio_config.dpi_chan.cod_tx", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_dth, { "Anti-Distortion Coeff 1(DTH)", "ua3g.command.audio_config.audio_circuit.dth", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_dtr, { "Anti-Distortion Coeff 2(DTR)", "ua3g.command.audio_config.audio_circuit.dtr", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_dtf, { "Anti-Distortion Coeff 3(DTF)", "ua3g.command.audio_config.audio_circuit.dtf", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_str, { "Sidetone Attenuation (STR)", "ua3g.command.audio_config.audio_circuit.str", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_ahp1, { "Anti-Larsen Coeff 1 (AHP1)", "ua3g.command.audio_config.audio_circuit.ahp1", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_ahp2, { "Anti-Larsen Coeff 2 (AHP2)", "ua3g.command.audio_config.audio_circuit.ahp2", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_ath, { "Anti-Larsen Coeff 3 (ATH)", "ua3g.command.audio_config.audio_circuit.ath", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_atr, { "Anti-Larsen Coeff 4 (ATR)", "ua3g.command.audio_config.audio_circuit.atr", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_atf, { "Anti-Larsen Coeff 5 (ATF)", "ua3g.command.audio_config.audio_circuit.atf", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_alm, { "Anti-Larsen Coeff 6 (ALM)", "ua3g.command.audio_config.audio_circuit.alm", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_group_listen, { "Group Listening Attenuation Constant", "ua3g.command.audio_config.loudspeaker_aco_param.group_listen", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_attenuation, { "Handsfree Attenuation Constant", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_attenuation", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_stay_in_send, { "Handsfree Number Of ms To Stay In Send State Before Going To Another State", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_stay_in_send", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mtx, { "Handsfree Number Of Positions To Shift Right MTx", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_shift_right_mtx", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mrc, { "Handsfree Number Of Positions To Shift Right MRc", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_shift_right_mrc", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_trans_threshold, { "Handsfree Idle Transmission Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_idle_trans_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_trans_threshold, { "Handsfree Low Transmission Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_low_trans_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_recv_threshold, { "Handsfree Idle Reception Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_idle_recv_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_recv_threshold, { "Handsfree Low Reception Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_low_recv_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_med_recv_threshold, { "Handsfree Medium Reception Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_med_recv_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_high_recv_threshold, { "Handsfree High Reception Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_high_recv_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_files_inc_boot_binary, { "Boot Binary Included", "ua3g.ua_dwl_protocol.files_inc.boot_binary", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x01, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_files_inc_loader_binary, { "Loader Binary Included", "ua3g.ua_dwl_protocol.files_inc.loader_binary", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x02, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_files_inc_appli_binary, { "Appli Binary Included", "ua3g.ua_dwl_protocol.files_inc.appli_binary", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x04, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_files_inc_data_binary, { "Datas Binary Included", "ua3g.ua_dwl_protocol.files_inc.data_binary", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x08, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_model_selection_a, { "For A Model", "ua3g.ua_dwl_protocol.model_selection.a", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x01, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_model_selection_b, { "For B Model", "ua3g.ua_dwl_protocol.model_selection.b", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x02, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_model_selection_c, { "For C Model", "ua3g.ua_dwl_protocol.model_selection.c", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x04, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_model_selection_country_ver, { "Country Version", "ua3g.ua_dwl_protocol.model_selection.country_ver", FT_UINT8, BASE_DEC, VALS(str_download_req_mode_selection_country), 0xE0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe1, { "For Ivanoe 1", "ua3g.ua_dwl_protocol.hardware_selection.ivanoe1", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x01, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe2, { "For Ivanoe 2", "ua3g.ua_dwl_protocol.hardware_selection.ivanoe2", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x02, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_memory_sizes_flash, { "Flash Min Size", "ua3g.ua_dwl_protocol.memory_sizes.flash", FT_UINT8, BASE_DEC, VALS(str_mem_size), 0x07, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_memory_sizes_ext_ram, { "External Ram Min Size", "ua3g.ua_dwl_protocol.memory_sizes.ext_ram", FT_UINT8, BASE_DEC, VALS(str_mem_size), 0x38, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_char_num_vta_subtype, { "VTA SubType", "ua3g.unsolicited_msg.char_num.vta_subtype", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_subtype), 0xC0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_char_num_generation, { "Generation", "ua3g.unsolicited_msg.char_num.generation", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_generation), 0x38, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_char_num_design, { "Design", "ua3g.unsolicited_msg.char_num.design", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_design), 0x07, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_vta_type, { "VTA Type", "ua3g.unsolicited_msg.hardware_config.vta_type", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_vta_type), 0xE0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_design, { "Design", "ua3g.unsolicited_msg.hardware_config.design", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_design), 0x1C, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_subtype, { "VTA SubType", "ua3g.unsolicited_msg.hardware_config.subtype", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_subtype), 0x03, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_hard_config_chip, { "Chip Id", "ua3g.unsolicited_msg.hardware_config.hard_config_chip", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_config_chip), 0x03, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_hard_config_flash, { "Flash Size", "ua3g.unsolicited_msg.hardware_config.hard_config_flash", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_config_flash), 0x1C, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_config_ram, { "External RAM Size", "ua3g.unsolicited_msg.hardware_config.config_ram", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_config_ram), 0xE0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_hard_config_ip, { "Hardware Configuration", "ua3g.unsolicited_msg.hardware_config.ip", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_config_ip), 0x01, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hook_status, { "Hook Status", "ua3g.unsolicited_msg.hook_status", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, NULL, HFILL }}, + { &hf_ua3g_special_key_shift, { "Shift", "ua3g.special_key.shift", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x01, NULL, HFILL }}, + { &hf_ua3g_special_key_ctrl, { "Ctrl", "ua3g.special_key.ctrl", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x02, NULL, HFILL }}, + { &hf_ua3g_special_key_alt, { "Alt", "ua3g.special_key.alt", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x04, NULL, HFILL }}, + { &hf_ua3g_special_key_cmd, { "Cmd", "ua3g.special_key.cmd", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x08, NULL, HFILL }}, + { &hf_ua3g_special_key_shift_prime, { "Shift'", "ua3g.special_key.shift_prime", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x10, NULL, HFILL }}, + { &hf_ua3g_special_key_ctrl_prime, { "Ctrl'", "ua3g.special_key.ctrl_prime", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x20, NULL, HFILL }}, + { &hf_ua3g_special_key_alt_prime, { "Alt'", "ua3g.special_key.alt_prime", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x40, NULL, HFILL }}, + { &hf_ua3g_special_key_cmd_prime, { "Cmd'", "ua3g.special_key.cmd_prime", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x80, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_call_timer, { "Call Timer", "ua3g.lcd_line_cmd.lcd_options.call_timer", FT_UINT8, BASE_DEC, VALS(str_call_timer_ctrl), 0x03, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_blink, { "Blink", "ua3g.lcd_line_cmd.lcd_options.blink", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x04, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_call_timer_control, { "Call Timer Control", "ua3g.lcd_line_cmd.lcd_options.call_timer_control", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x10, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_call_timer_display, { "Call Timer Display", "ua3g.lcd_line_cmd.lcd_options.call_timer_display", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x20, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_time_of_day_display, { "Time Of Day Display", "ua3g.lcd_line_cmd.lcd_options.time_of_day_display", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x40, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_suspend_display_refresh, { "Suspend Display Refresh", "ua3g.lcd_line_cmd.lcd_options.suspend_display_refresh", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x80, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_value, { "Value", "ua3g.ip.cs.cmd02.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_ip, { "IP", "ua3g.ip.cs.cmd02.parameter.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_mac_address, { "MAC Address", "ua3g.ip.cs.cmd02.parameter.mac_address", FT_ETHER, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_uint, { "Value", "ua3g.ip.cs.cmd02.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_bytes, { "Default Codec", "ua3g.ip.cs.cmd02.parameter.default_codec.bytes", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_uint, { "Default Codec", "ua3g.ip.cs.cmd02.parameter.default_codec.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_firmware_version, { "Firmware Version", "ua3g.ip.cs.cmd02.parameter.firmware_version", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_speed, { "Port Lan Speed", "ua3g.ip.cs.cmd02.parameter.eth_driver_config.port_lan_speed", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_duplex, { "Port Lan Duplex", "ua3g.ip.cs.cmd02.parameter.eth_driver_config.port_lan_duplex", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_speed, { "Port PC Speed", "ua3g.ip.cs.cmd02.parameter.eth_driver_config.port_pc_speed", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_duplex, { "Port PC Duplex", "ua3g.ip.cs.cmd02.parameter.eth_driver_config.port_pc_duplex", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_ip, { "IP", "ua3g.ip.cs.cmd03.parameter.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_string, { "IP", "ua3g.ip.cs.cmd03.parameter.string", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip1, { "Type Of Equipment (first byte)", "ua3g.ip.cs.cmd03.parameter.type_of_equip1", FT_UINT8, BASE_DEC, VALS(cs_ip_device_routing_cmd03_first_byte_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip2, { "Type Of Equipment (second byte)", "ua3g.ip.cs.cmd03.parameter.type_of_equip2", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_cmd03_second_byte_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_default_codec, { "Default Codec", "ua3g.ip.cs.cmd03.parameter.default_codec", FT_UINT64, BASE_DEC|BASE_VAL64_STRING, VALS(str_cs_ip_device_routing_0F_compressor), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_vad, { "VAD", "ua3g.ip.cs.cmd03.parameter.vad", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_ece, { "ECE", "ua3g.ip.cs.cmd03.parameter.ece", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_voice_mode, { "Voice Mode", "ua3g.ip.cs.cmd03.parameter.voice_mode", FT_UINT64, BASE_DEC|BASE_VAL64_STRING, VALS(cs_ip_device_routing_cmd03_voice_mode_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_delay_distribution, { "Delay Distribution", "ua3g.ip.cs.cmd03.parameter.delay_distribution", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_delay_distribution_range_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_bfi, { "Consecutive BFI", "ua3g.ip.cs.cmd03.parameter.consecutive_bfi", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_consecutive_bfi_range_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution, { "BFI Distribution", "ua3g.ip.cs.cmd03.parameter.bfi_distribution", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_bfi_distribution_range_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021Q_used, { "802.1 Q Used", "ua3g.ip.cs.cmd03.parameter.8021Q_used", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021P_priority, { "802.1p Priority", "ua3g.ip.cs.cmd03.parameter.8021P_priority", FT_UINT8, BASE_DEC, NULL, 0x07, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_vlan_id, { "VLAN Id", "ua3g.ip.cs.cmd03.parameter.vlan_id", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_diffserv, { "DiffServ", "ua3g.ip.cs.cmd03.parameter.diffserv", FT_UINT8, BASE_DEC, NULL, 0xFC, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution_200ms, { "200 ms BFI Distribution", "ua3g.ip.cs.cmd03.parameter.bfi_distribution_200ms", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_200ms_bfi_distribution_range_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_uint, { "Value", "ua3g.ip.cs.cmd03.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_rtp_lost, { "Consecutive RTP Lost", "ua3g.ip.cs.cmd03.parameter.consecutive_rtp_lost", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_consecutive_rtp_lost_range_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_jitter_depth_distribution, { "Jitter Depth Distribution", "ua3g.ip.cs.cmd03.parameter.jitter_depth_distribution", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + }; + + static gint *ett[] = + { + &ett_ua3g, + &ett_ua3g_body, + &ett_ua3g_param, + &ett_ua3g_param_sub, + &ett_ua3g_option, + }; + + /* UA3G dissector registration */ + proto_ua3g = proto_register_protocol("UA3G Message", "UA3G", "ua3g"); + + proto_register_field_array(proto_ua3g, hf, array_length(hf)); + + new_register_dissector("ua3g", dissect_ua3g, proto_ua3g); + + /* Common subtree array registration */ + proto_register_subtree_array(ett, array_length(ett)); +} + +void proto_reg_handoff_ua3g(void) +{ +#if 0 /* Future */ + dissector_handle_t handle_ua3g = find_dissector("ua3g"); + + /* hooking of UA3G on UA */ + + dissector_add_uint("ua.opcode", 0x15, handle_ua3g); +#endif +} diff --git a/src/main/resource/testFiles/wireshark/prevFiles/prev_2cc860_12fddb_packet-rtps.c b/src/main/resource/testFiles/wireshark/prevFiles/prev_2cc860_12fddb_packet-rtps.c new file mode 100644 index 0000000..a43a196 --- /dev/null +++ b/src/main/resource/testFiles/wireshark/prevFiles/prev_2cc860_12fddb_packet-rtps.c @@ -0,0 +1,1989 @@ +/* packet-rtps.c + * Routines for Real-Time Publish-Subscribe Protocol (RTPS) dissection + * + * Copyright 2003, LUKAS POKORNY + * PETR SMOLIK + * ZDENEK SEBEK + * + * Czech Technical University in Prague + * Faculty of Electrical Engineering + * Department of Control Engineering + * + * version: 2004/04/15 9:40:45 + * dedication to Kj :] + * + * $Id: packet-rtps.c,v 1.9 2004/04/19 22:41:19 guy Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Copied from packet-udp.c, packet-tftp.c, packet-x25.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * *********************************************************************** */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + + +/* *********************************************************************** * + RTPS protocol was developed by Real Time Innovation, Inc. + + Protocol specifikation and documenation you can find on these addresses: + + http://www.rti.com/ + + http://www.rti.com/products/ndds/literature.html + + http://www.schneider-electric.com.au/Products/Automation/TF_RealTime/ + /technical%20library/specifications/WireProtocolExternal.pdf + + + * *********************************************************************** */ + + + +/* redefine types because of definitions in 'packet-rtps.h' */ +/* +#define u_int8_t guint8 +#define int8_t gint8 + +#define u_int16_t guint16 +#define int16_t gint16 + +#define u_int32_t guint32 +#define int32_t gint32 +*/ + +#include "packet-rtps.h" + +/* number of APP_KIND byte in packet header */ +#define APP_KIND_BYTE 15 + + +/* definitions of flags */ +#define FLAG_E 0x01 +#define FLAG_F 0x02 +#define FLAG_I 0x02 +#define FLAG_M 0x02 +#define FLAG_P 0x02 +#define FLAG_A 0x04 +#define FLAG_H 0x08 + + +/* submessageId's ranges */ +#define SUBMSG_ID_MIN PAD +#define SUBMSG_ID_MAX INFO_DST + +/* Vendor specific submessageId's ranges */ +#define VENDOR_SUBMSG_ID_MIN 0x80 +#define VENDOR_SUBMSG_ID_MAX 0xff + +/* *********************************************************************** */ + + +/* initialize the protocol and registered fields */ +static int proto_rtps = -1; +static int hf_rtps_submessage_id = -1; +static int hf_rtps_submessage_flags = -1; +static int hf_rtps_octets_to_next_header = -1; +static int hf_rtps_parameter_id = -1; +static int hf_rtps_parameter_length = -1; +static int hf_rtps_issue_data = -1; + +/* Initialize the subtree pointers */ +static gint ett_rtps = -1; +static gint ett_rtps_submessage = -1; +static gint ett_rtps_bitmap = -1; +static gint ett_rtps_parameter_sequence = -1; +static gint ett_rtps_parameter = -1; + +/* Functions declarations */ +static void dissect_PAD(tvbuff_t *tvb,gint offset,guint8 flags, + int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_VAR(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_ISSUE(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_ACK(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_HEARTBEAT(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_GAP(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_INFO_TS(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_INFO_SRC(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_INFO_REPLY(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_INFO_DST(tvbuff_t *tvb,gint offset,guint8 flags, + int next_submsg_offset, + proto_tree *rtps_submessage_tree); + +static guint16 get_guint16(tvbuff_t *tvb, gint offset, gboolean little_endian); +static guint32 get_guint32(tvbuff_t *tvb, gint offset, gboolean little_endian); + +static char *protocol_version_to_string(gint offset,tvbuff_t *tvb,char *buff); +static char *vendor_id_to_string(gint offset, tvbuff_t *tvb, char *buff); + +static char *host_id_to_string(gint offset,tvbuff_t *tvb, char buff[]); +static char *app_id_to_string(gint offset,tvbuff_t *tvb,char buff[]); +static char *object_id_to_string(gint offset, tvbuff_t *tvb, char buff[]); + +static char *IP_to_string(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]); +static char *port_to_string(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]); +static char *get_NtpTime(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]); + +static void get_bitmap(tvbuff_t *tvb, gint *p_offset, gboolean little_endian, + gint next_submsg, proto_tree *tree); + +static void get_parameter_sequence(tvbuff_t *tvb, gint *p_offset, + gboolean little_endian, + gint next_submsg_offset, proto_tree *tree); + +static gint seq_nr_to_string( gint offset, gboolean little_endian, tvbuff_t *tvb, + SequenceNumber *p_seqNumber); + +static const value_string submessage_id_vals[] = { + { PAD, "PAD" }, + { VAR, "VAR" }, + { ISSUE, "ISSUE" }, + { ACK, "ACK" }, + { HEARTBEAT, "HEARTBEAT" }, + { GAP, "GAP" }, + { INFO_TS, "INFO_TS" }, + { INFO_SRC, "INFO_SRC" }, + { INFO_REPLY, "INFO_REPLY" }, + { INFO_DST, "INFO_DST" }, + { APP_QUIT, "APP_QUIT" }, + { 0, NULL } +}; + +static const value_string parameter_id_vals[] = { + { PID_PAD, "PID_PAD" }, + { PID_SENTINEL, "PID_SENTINEL" }, + { PID_EXPIRATION_TIME, "PID_EXPIRATION_TIME" }, + { PID_PERSISTENCE, "PID_PERSISTENCE" }, + { PID_MINIMUM_SEPARATION, "PID_MINIMUM_SEPARATION" }, + { PID_TOPIC, "PID_TOPIC" }, + { PID_STRENGTH, "PID_STRENGTH" }, + { PID_TYPE_NAME, "PID_TYPE_NAME" }, + { PID_TYPE_CHECKSUM, "PID_TYPE_CHECKSUM" }, + { RTPS_PID_TYPE2_NAME, "RTPS_PID_TYPE2_NAME" }, + { RTPS_PID_TYPE2_CHECKSUM, "RTPS_PID_TYPE2_CHECKSUM" }, + { PID_METATRAFFIC_MULTICAST_IPADDRESS, "PID_METATRAFFIC_MULTICAST_IPADDRESS" }, + { PID_APP_IPADDRESS, "PID_APP_IPADDRESS" }, + { PID_METATRAFFIC_UNICAST_PORT, "PID_METATRAFFIC_UNICAST_PORT" }, + { PID_USERDATA_UNICAST_PORT, "PID_USERDATA_UNICAST_PORT" }, + { PID_IS_RELIABLE, "PID_IS_RELIABLE" }, + { PID_EXPECTS_ACK, "PID_EXPECTS_ACK" }, + { PID_USERDATA_MULTICAST_IPADDRESS, "PID_USERDATA_MULTICAST_IPADDRESS" }, + { PID_MANAGER_KEY, "PID_MANAGER_KEY" }, + { PID_SEND_QUEUE_SIZE, "PID_SEND_QUEUE_SIZE" }, + { PID_RELIABILITY_ENABLED, "PID_RELIABILITY_ENABLED" }, + { PID_PROTOCOL_VERSION, "PID_PROTOCOL_VERSION" }, + { PID_VENDOR_ID, "PID_VENDOR_ID" }, + { PID_VARGAPPS_SEQUENCE_NUMBER_LAST, "PID_VARGAPPS_SEQUENCE_NUMBER_LAST" }, + { PID_RECV_QUEUE_SIZE, "PID_RECV_QUEUE_SIZE" }, + { PID_RELIABILITY_OFFERED, "PID_RELIABILITY_OFFERED" }, + { PID_RELIABILITY_REQUESTED, "PID_RELIABILITY_REQUESTED" }, + { 0, NULL } +}; + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * Code to actually dissect the packets * + * * + * *********************************************************************** */ + +static gboolean +dissect_rtps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_item *ti; + proto_tree *rtps_tree=NULL; + gint offset = 0; + gint appKind; + proto_tree *rtps_submessage_tree; + guint8 submessageId; + guint8 flags; + gboolean little_endian; + int next_submsg; + int count_msg_type[11]; + char buff[200], buff_tmp[30];/* buffers */ + + /* offset is the byte offset of 'tvb' at which the new tvbuff + should start. The first byte is the 0th byte. */ + + /* --- making disition if protocol is RTPS protocol --- */ + if (!tvb_bytes_exist(tvb, offset, 4)) return FALSE; + if (tvb_get_guint8(tvb,offset++) != 'R') return FALSE; + if (tvb_get_guint8(tvb,offset++) != 'T') return FALSE; + if (tvb_get_guint8(tvb,offset++) != 'P') return FALSE; + if (tvb_get_guint8(tvb,offset++) != 'S') return FALSE; + + /* --- Make entries in Protocol column ---*/ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTPS"); + + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + memset(count_msg_type, 0, sizeof(count_msg_type)); + + if (tree) { + + /* create display subtree for the protocol */ + ti = proto_tree_add_item(tree, proto_rtps, tvb, 0, -1, FALSE); + rtps_tree = proto_item_add_subtree(ti, ett_rtps); + + /* Protocol Version */ + proto_tree_add_text(rtps_tree, tvb, offset, 2, + "Protocol RTPS, version %s", + protocol_version_to_string(offset, tvb, buff)); + offset +=2; + + /* Vendor Id */ + proto_tree_add_text(rtps_tree, tvb, offset, 2, + "VendorId: %s", + vendor_id_to_string(offset, tvb, buff)); + offset +=2; + + /* Host Id */ + proto_tree_add_text(rtps_tree, tvb, offset, 4, + "HostId: %s", + host_id_to_string(offset,tvb,buff)); + offset +=4; + + /* App Id */ + proto_tree_add_text(rtps_tree, tvb, offset, 4, + "App ID: %s", + app_id_to_string(offset, tvb, buff)); + + } + + /* offset behind RTPS's Header */ + offset=16; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + submessageId = tvb_get_guint8(tvb, offset); + if (submessageId & 0x80) { + ti = proto_tree_add_text(tree, tvb, offset, -1, "Submessage: %s", + val_to_str(submessageId, submessage_id_vals, + "Vendor-specific (0x%02X)")); + } else { + ti = proto_tree_add_text(tree, tvb, offset, -1, "Submessage: %s", + val_to_str(submessageId, submessage_id_vals, + "Unknown (0x%02X)")); + } + rtps_submessage_tree = proto_item_add_subtree(ti, ett_rtps_submessage); + if (submessageId & 0x80) { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_submessage_id, + tvb, offset, 1, submessageId, + "Submessage Id: Vendor-specific (0x%02x)", + submessageId); + } else { + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_id, + tvb, offset, 1, submessageId); + } + + flags = tvb_get_guint8(tvb, offset + 1); + /* E flag |XXXX|HAPE| => masks with 000000001b = 1 */ + if ((flags & FLAG_E) != 0) little_endian = TRUE; + else little_endian = FALSE; + + next_submsg = get_guint16(tvb, offset + 2, little_endian); + proto_item_set_len(ti, next_submsg); + + switch (submessageId) + { + case PAD: + if (tree) + dissect_PAD(tvb, offset + 1, flags, next_submsg, + rtps_submessage_tree); + count_msg_type[0]++; + break; + case VAR: + if (tree) + dissect_VAR(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[1]++; + break; + case ISSUE: + if (tree) + dissect_ISSUE(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[2]++; + break; + case ACK: + if (tree) + dissect_ACK(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[3]++; + break; + case HEARTBEAT: + if (tree) + dissect_HEARTBEAT(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[4]++; + break; + case GAP: + if (tree) + dissect_GAP(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[5]++; + break; + case INFO_TS: + if (tree) + dissect_INFO_TS(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[6]++; + break; + case INFO_SRC: + if (tree) + dissect_INFO_SRC(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[7]++; + break; + case INFO_REPLY: + if (tree) + dissect_INFO_REPLY(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[8]++; + break; + case INFO_DST: + if (tree) + dissect_INFO_DST(tvb, offset + 1, flags, next_submsg, + rtps_submessage_tree); + count_msg_type[9]++; + break; + default: + if (tree) { + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset + 1, 1, flags); + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset + 2, 2, next_submsg); + } + break; + } + + /* next submessage's offset */ + offset += next_submsg+4; + + } + + /* --- and Info column on summary display ---*/ + + if (check_col(pinfo->cinfo, COL_INFO)) + { + appKind = tvb_get_guint8(tvb, APP_KIND_BYTE); + + if (appKind == MANAGEDAPPLICATION ) {sprintf(buff,"App: ");} + if (appKind == MANAGER) {sprintf(buff,"Man: ");} + if (appKind == AID_UNKNOWN) {sprintf(buff,"Unknown:");} + + if (appKind != MANAGEDAPPLICATION && appKind != MANAGER && + appKind != AID_UNKNOWN) {sprintf(buff,"ERROR in APP type");} + + /* -- counts of submessages - for Information Frame */ + if (count_msg_type[0]>0) { + sprintf(buff_tmp,"PAD(%d) ",count_msg_type[0]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[1]>0) { + sprintf(buff_tmp,"VAR(%d) ",count_msg_type[1]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[2]>0) { + sprintf(buff_tmp,"ISSUE(%d) ",count_msg_type[2]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[3]>0) { + sprintf(buff_tmp,"ACK(%d) ",count_msg_type[3]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[4]>0) { + sprintf(buff_tmp,"HEARTBEAT(%d) ",count_msg_type[4]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[5]>0) { + sprintf(buff_tmp,"GAP(%d) ",count_msg_type[5]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[6]>0) { + sprintf(buff_tmp,"INFO_TS(%d) ",count_msg_type[6]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[7]>0) { + sprintf(buff_tmp, "INFO_SRC(%d) ",count_msg_type[7]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[8]>0) { + sprintf(buff_tmp,"INFO_REPLY(%d) ",count_msg_type[8]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[9]>0) { + sprintf(buff_tmp,"INFO_DST(%d) ",count_msg_type[9]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[10]>0) { + sprintf(buff_tmp,"vendor specific(%d) ",count_msg_type[10]); + strcat(buff,buff_tmp); + } + + col_add_fstr(pinfo->cinfo, COL_INFO, buff); + + } + + + return TRUE; + +} /* end dissect_rtps(...) */ + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get 16 bit from the stream * + * * + * *********************************************************************** */ + +static guint16 get_guint16(tvbuff_t *tvb, gint offset, gboolean little_endian) +{ + guint16 value; + + if (little_endian) + value = tvb_get_letohs(tvb, offset); + else + value = tvb_get_ntohs(tvb, offset); + + return(value); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get 32 bit from the stream * + * * + * *********************************************************************** */ + +static guint32 get_guint32(tvbuff_t *tvb, gint offset, gboolean little_endian) +{ + guint32 value; + + if (little_endian) + value = tvb_get_letohl(tvb, offset); + else + value = tvb_get_ntohl(tvb, offset); + + return(value); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Protocol version * + * * + * *********************************************************************** */ + +static char * +protocol_version_to_string(gint offset,tvbuff_t *tvb,char *buff) +{ + guint8 major, minor; + + /* protocol verzion = major.minor */ + major = tvb_get_guint8(tvb, offset); + minor = tvb_get_guint8(tvb, (offset+1)); + + sprintf(buff,"%d.%d", major, minor); + return(buff); + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Vendor Id * + * * + * *********************************************************************** */ + +static char * +vendor_id_to_string(gint offset, tvbuff_t *tvb, char *buff) +{ + guint8 major, minor; + VendorId vendorId_rti; + + VENDOR_ID_RTI(vendorId_rti); + + major = tvb_get_guint8(tvb, offset); + minor = tvb_get_guint8(tvb, (offset+1)); + + if (major == vendorId_rti.major && + minor == vendorId_rti.minor) + { sprintf(buff,"Real-Time Innovations,Inc.,CA,USA"); + return(buff); } + + sprintf(buff,"Vendor unknown"); + return(buff); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get IP Address * + * * + * *********************************************************************** */ + +static char * +IP_to_string(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]) +{ + IPAddress ip; + guint8 a = 0, b = 0, c = 0, d = 0; /* IP Adresss = a.b.c.d */ + + ip = get_guint32(tvb, offset, little_endian); + /* get_guint32() - reads + endian conversion */ + a = (ip >> 24); + b = (ip >> 16) & 0xff; + c = (ip >> 8) & 0xff; + d = ip & 0xff; + + sprintf(buff,"%d.%d.%d.%d", a, b, c, d); + return(buff); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Port * + * * + * *********************************************************************** */ + +static char * +port_to_string(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]) +{ + Port port = get_guint32(tvb, offset, little_endian); + /* get_guint32() - reads + endian conversion */ + + if (port == PORT_INVALID) + sprintf(buff,"PORT_INVALID"); + else + sprintf(buff,"0x%X",port); + + return(buff); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get NTP Time * + * * + * *********************************************************************** */ + +static char * +get_NtpTime(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]) +{ + NtpTime ntpTime; + float time; + + /* get_guint32() - reads + endian conversion */ + ntpTime.seconds = get_guint32(tvb, offset, little_endian); + ntpTime.fraction = get_guint32(tvb, (offset + 4), little_endian); + time = (float) ntpTime.seconds + (ntpTime.fraction / 2^(32)); + + sprintf(buff,"%f", time); + return(buff); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Host Id * + * * + * *********************************************************************** */ + +static char * +host_id_to_string(gint offset,tvbuff_t *tvb, char buff[]) +{ + guint32 hostId = tvb_get_ntohl(tvb, offset); + /* get_ntohl() automaticaly convert data to BIG ENDIAN */ + + sprintf(buff,"0x%X", hostId); + return(buff); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get AppID * + * * + * *********************************************************************** */ + +static char * +app_id_to_string(gint offset,tvbuff_t *tvb,char buff[]) +{ + guint32 appId = tvb_get_ntohl(tvb, offset); + /* get_ntohl() automaticaly convert data to BIG ENDIAN */ + + /* Instance Id */ + guint32 instanceId = (appId >> 8); + /* applicatin Kind */ + guint8 appKind = (appId & 0xff); + + if (appKind == MANAGEDAPPLICATION) + { + sprintf(buff,"Managed App, InstanceId: 0x%X",instanceId); + return(buff); + } + + if (appKind == MANAGER) + { + sprintf(buff,"Manager, InstanceId: 0x%X",instanceId); + return(buff); + } + + sprintf(buff,"Unknown"); + return(buff); + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Object_Id (32 bit) * + * * + * *********************************************************************** */ + +static char * +object_id_to_string(gint offset, tvbuff_t *tvb, char buff[]) +{ + guint32 objectId = tvb_get_ntohl(tvb, offset); + /* get_ntohl() automaticaly convert data to BIG ENDIAN */ + + if (objectId == OID_UNKNOWN) { sprintf(buff,"Unknown ObjectId"); + return(buff);} + if (objectId == OID_APP) { sprintf(buff,"applicationSelf"); + return(buff);} + if (objectId == OID_WRITE_APPSELF){ sprintf(buff,"writerApplicationSelf"); + return(buff);} + if (objectId == OID_WRITE_APP) { sprintf(buff,"writerApplications"); + return(buff);} + if (objectId == OID_READ_APP) { sprintf(buff,"readerApplications"); + return(buff);} + if (objectId == OID_WRITE_MGR) { sprintf(buff,"writerManagers"); + return(buff);} + if (objectId == OID_READ_MGR) { sprintf(buff,"readerManagers "); + return(buff);} + if (objectId == OID_WRITE_PUBL) { sprintf(buff,"writerPublications"); + return(buff);} + if (objectId == OID_READ_PUBL) { sprintf(buff,"readerPublications"); + return(buff);} + if (objectId == OID_WRITE_SUBS) { sprintf(buff,"writerSubscriptions"); + return(buff);} + if (objectId == OID_READ_SUBS) { sprintf(buff,"readerSubscriptions"); + return(buff);} + + /* nothing from the possibilites above */ + sprintf(buff,"instanceId: 0x%X, objKind: 0x%X", + (objectId >> 8),(objectId & 0xff)); + return(buff); + +/* for the future +//Kind +#define OID_APPLICATION 0x01 +#define OID_CSTWRITER 0x02 +#define OID_PUBLICATION 0x03 +#define OID_SUBSCRIPTION 0x04 +#define OID_CSTREADER 0x07 +// +#define OID_USEROBJ 0x00 +#define OID_RESUSEROBJ 0x40 +#define OID_METAOBJ 0x80 +#define OID_RESMETAOBJ 0xC0 +*/ +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Sequence Number (64 bit) * + * * + * *********************************************************************** */ + +static gint +seq_nr_to_string(gint offset, gboolean little_endian, tvbuff_t *tvb, + SequenceNumber *p_seqNumber) +{ + p_seqNumber->high = get_guint32(tvb, offset, little_endian); + p_seqNumber->low = get_guint32(tvb, offset + 4, little_endian); + + return(1); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get_Bitmap * + * * + * *********************************************************************** */ + +static void +get_bitmap(tvbuff_t *tvb, gint *p_offset, gboolean little_endian, + gint next_submsg, proto_tree *tree) +{ + proto_item *ti; + proto_tree *rtps_bitmap_tree; + gint i = 0; + gint offset = *p_offset; + SequenceNumber sequenceNumber; + guint32 num_bits; + guint num_longs; + + /* making subtree for the bitmap */ + ti = proto_tree_add_text(tree,tvb,offset,(next_submsg-offset),"Bitmap"); + rtps_bitmap_tree = proto_item_add_subtree(ti, ett_rtps_bitmap); + + /* SekvenceNumber bitmapBase */ + seq_nr_to_string(offset, little_endian, tvb, &sequenceNumber); + proto_tree_add_text(rtps_bitmap_tree, tvb, offset, 8, + "bitmapBase: 0x%X%X", + sequenceNumber.high, sequenceNumber.low); + offset +=8; + + num_bits = get_guint32(tvb, offset, little_endian); + proto_tree_add_text(rtps_bitmap_tree, tvb, offset, 4, + "numBits: %u", + num_bits); + offset += 4; + + if (num_bits+31 < num_bits) + num_longs = UINT_MAX; /* overflow */ + else + num_longs = (num_bits+31)/32; + while (num_longs != 0) + { + if (next_submsg-offset < 4) + { + proto_tree_add_text(rtps_bitmap_tree, tvb, offset, next_submsg-offset, + "bitmap[%d]: < 4 bytes remain in message", i); + offset = next_submsg; + break; + } + proto_tree_add_text(rtps_bitmap_tree, tvb, offset, 4, + "bitmap[%d]: 0x%08X", + i, get_guint32(tvb, offset, little_endian)); + offset +=4; + ++i; + --num_longs; + } /* end while */ + + *p_offset = offset; +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * dissect submessage: PAD * + * * + * (this submessage has no meaning and it is always valid) * + * *********************************************************************** */ + +static void +dissect_PAD(tvbuff_t *tvb, gint offset, guint8 flags, + int next_submsg_offset, proto_tree *rtps_submessage_tree) +{ + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset += 1; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * dissect submessage: VAR * + * * + * *********************************************************************** */ + +static void +dissect_VAR(tvbuff_t *tvb, gint offset, guint8 flags, gboolean little_endian, + int next_submsg_offset, proto_tree *rtps_submessage_tree) +{ + int min_len; + char buff[200]; + SequenceNumber writerSeqNumber; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + min_len = 20; + if ((flags & FLAG_H) != 0) + min_len += 8; + if ((flags & FLAG_P) != 0) + min_len += 4; + if (next_submsg_offset < min_len) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= %u)", + next_submsg_offset, min_len); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* readerObjectId*/ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Reader Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* writerObjectId*/ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Writer Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset+=4; + + /* H flag |XXXX|HAPE| => masks with 00001000b = 8 */ + if ((flags & FLAG_H) != 0) + { + /* HostId */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Host ID: %s", + host_id_to_string(offset,tvb,buff)); + offset+=4; + + /* App Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "App ID: %s", + app_id_to_string(offset, tvb, buff)); + offset +=4; + } + + /* Object Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* WriterSequence Number */ + seq_nr_to_string(offset, little_endian, tvb, &writerSeqNumber); + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "WriterSeqNumber: 0x%X%X", + writerSeqNumber.high, writerSeqNumber.low); + offset +=8; + + /* P flag |XXXX|HAPE| => masks with 00000010b = 2 */ + if ((flags & FLAG_P) != 0) + { + get_parameter_sequence(tvb, &offset, little_endian, next_submsg_offset, + rtps_submessage_tree); + } +} + +/* *********************************************************************** */ + +/* *********************************************************************** * + * * + * get_ParameterSequence * + * * + * *********************************************************************** */ + + +static void +get_parameter_sequence(tvbuff_t *tvb, gint *p_offset, gboolean little_endian, + gint next_submsg_offset, proto_tree *tree) +{ + proto_item *ti; + proto_tree *rtps_parameter_sequence_tree; + proto_tree *rtps_parameter_tree; + gint offset = *p_offset; + guint16 parameter, param_length; + gint str_length; + SequenceNumber seqNumber; + char buff_tmp[MAX_PATHNAME]; + int i; + char sep; + + ti = proto_tree_add_text(tree, tvb, offset, (next_submsg_offset - offset), + "Parameters:"); + rtps_parameter_sequence_tree = proto_item_add_subtree(ti, + ett_rtps_parameter_sequence); + for (;;) + { + if (next_submsg_offset-offset < 2) + { + proto_tree_add_text(rtps_parameter_sequence_tree, tvb, offset, + next_submsg_offset-offset, + "Parameter: < 2 bytes remain in message"); + offset = next_submsg_offset; + break; + } + parameter = get_guint16(tvb, offset, little_endian); + ti = proto_tree_add_text(rtps_parameter_sequence_tree, tvb, offset, 2, + "%s", + val_to_str(parameter, parameter_id_vals, + "Unknown parameter (0x%04X)")); + rtps_parameter_tree = proto_item_add_subtree(ti, ett_rtps_parameter); + proto_tree_add_uint(rtps_parameter_tree, hf_rtps_parameter_id, + tvb, offset, 2, parameter); + offset +=2; + if (next_submsg_offset-offset < 2) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, + next_submsg_offset-offset, + "Parameter length: < 2 bytes remain in message"); + offset = next_submsg_offset; + proto_item_set_end(ti, tvb, offset); + break; + } + param_length = get_guint16(tvb, offset, little_endian); + proto_tree_add_uint(rtps_parameter_tree, hf_rtps_parameter_length, + tvb, offset, 2, param_length); + offset +=2; + + if (parameter == PID_SENTINEL) { + proto_item_set_end(ti, tvb, offset); + break; + } + + if (next_submsg_offset-offset < param_length) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, + next_submsg_offset-offset, + "Parameter value: < %u bytes remain in message", + param_length); + offset = next_submsg_offset; + proto_item_set_end(ti, tvb, offset); + break; + } + proto_item_set_end(ti, tvb, offset + param_length); + + switch (parameter) + { + case PID_PAD: + proto_item_append_text(ti, ": -"); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Padding"); + break; + + case PID_EXPIRATION_TIME: + if (param_length < 8) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 8"); + } + else + { + char *ntp_time_str; + + ntp_time_str = get_NtpTime(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, ": %s", ntp_time_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Expiration time: %s", ntp_time_str); + } + break; + + case PID_PERSISTENCE: + if (param_length < 8) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 8"); + } + else + { + char *ntp_time_str; + + ntp_time_str = get_NtpTime(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, ": %s", ntp_time_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Persistence: %s", ntp_time_str); + } + break; + + case PID_MINIMUM_SEPARATION: + if (param_length < 8) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 8"); + } + else + { + char *ntp_time_str; + + ntp_time_str = get_NtpTime(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, ": %s", ntp_time_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Minimum separation: %s", ntp_time_str); + } + break; + + case PID_TOPIC: /* --- ?? funguje spravne ?? */ + str_length = tvb_strnlen(tvb, offset, param_length); + if (str_length == -1) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: Terminating zero missing"); + } + else + { + char *str; + + str = tvb_format_text(tvb, offset, str_length); + proto_item_append_text(ti, ": %s", str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Topic: %s", str); + } + break; + + case PID_STRENGTH: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 strength; + + strength = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": 0x%X", strength); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Strength: 0x%X", strength); + } + break; + + case PID_TYPE_NAME: /* --- ?? funguje spravne ?? */ + str_length = tvb_strnlen(tvb, offset, param_length); + if (str_length == -1) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: Terminating zero missing"); + } + else + { + char *str; + + str = tvb_format_text(tvb, offset, str_length); + proto_item_append_text(ti, ": %s", str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Type name: %s", str); + } + break; + + case PID_TYPE_CHECKSUM: + /* nacitam jako UNSIGNED - nemuze to byt i zaporne cislo?? */ + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 checksum; + + checksum = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": 0x%X", checksum); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Checksum: 0x%X", checksum); + } + break; + + case RTPS_PID_TYPE2_NAME: + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Parameter data"); + break; + + case RTPS_PID_TYPE2_CHECKSUM: + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Parameter data"); + break; + + case PID_METATRAFFIC_MULTICAST_IPADDRESS: + i = 0; + sep = ':'; + while (param_length >= 4) + { + char *ip_string; + + ip_string = IP_to_string(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, "%c %s", sep, ip_string); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Address[%d]: %s", i, ip_string); + ++i; + offset +=4; + sep = ','; + } + offset += param_length; + break; + + case PID_APP_IPADDRESS: + i = 0; + sep = ':'; + while (param_length >= 4) + { + char *ip_string; + + ip_string = IP_to_string(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, "%c %s", sep, ip_string); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Address[%d]: %s", i, ip_string); + ++i; + offset +=4; + sep = ','; + } + offset += param_length; + break; + + case PID_METATRAFFIC_UNICAST_PORT: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + char *port_str; + + port_str = port_to_string(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, ": %s", port_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Port: %s", port_str); + } + break; + + case PID_USERDATA_UNICAST_PORT: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + char *port_str; + + port_str = port_to_string(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, ": %s", port_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Port: %s", port_str); + } + break; + + case PID_EXPECTS_ACK: + if (param_length < 1) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 1"); + } + else + { + if (tvb_get_guint8(tvb, offset) == 0) + { + proto_item_append_text(ti, ": No"); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "ACK expected: No"); + } + else + { + proto_item_append_text(ti, ": Yes"); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "ACK expected: Yes"); + } + } + break; + + case PID_USERDATA_MULTICAST_IPADDRESS: + i = 0; + sep = ':'; + while (param_length >= 4) + { + char *ip_string; + + ip_string = IP_to_string(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, "%c %s", sep, ip_string); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Address[%d]: %s", i, ip_string); + ++i; + offset +=4; + } + offset += param_length; + break; + + case PID_MANAGER_KEY: + i = 0; + sep = ':'; + while (param_length >= 4) + { + guint32 manager_key; + + manager_key = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, "%c 0x%X", sep, manager_key); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Key[%d]: 0x%X", i, manager_key); + ++i; + offset +=4; + sep = ','; + } + offset += param_length; + break; + + case PID_SEND_QUEUE_SIZE: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 send_queue_size; + + send_queue_size = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": %u", send_queue_size); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Send queue size: %u", send_queue_size); + } + break; + + case PID_PROTOCOL_VERSION: + if (param_length < 2) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 2"); + } + else + { + char *protocol_version_str; + + protocol_version_str = protocol_version_to_string(offset, tvb, buff_tmp); + proto_item_append_text(ti, ": %s", protocol_version_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Protocol version: %s", protocol_version_str); + } + break; + + case PID_VENDOR_ID: + if (param_length < 2) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 2"); + } + else + { + char *vendor_id_str; + + vendor_id_str = vendor_id_to_string(offset, tvb, buff_tmp); + proto_item_append_text(ti, ": %s", vendor_id_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Vendor ID: %s", vendor_id_str); + } + break; + + case PID_VARGAPPS_SEQUENCE_NUMBER_LAST: + if (param_length < 8) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 8"); + } + else + { + seq_nr_to_string(offset, little_endian, tvb, &seqNumber); + proto_item_append_text(ti, ": 0x%X%X", + seqNumber.high, seqNumber.low); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Sequence number: 0x%X%X", + seqNumber.high, seqNumber.low); + } + break; + + case PID_RECV_QUEUE_SIZE: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 recv_queue_size; + + recv_queue_size = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": %u", recv_queue_size); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Receive queue size: %u", recv_queue_size); + } + break; + + case PID_RELIABILITY_OFFERED: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 reliability_offered; + + reliability_offered = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": 0x%X", reliability_offered); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Reliability offered: 0x%X", reliability_offered); + } + break; + + case PID_RELIABILITY_REQUESTED: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 reliability_requested; + + reliability_requested = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": 0x%X", reliability_requested); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Reliability requested: 0x%X", reliability_requested); + } + break; + + default: + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Unknown parameter value"); + break; + } /* end switch */ + + offset += param_length; + } + + *p_offset = offset; +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: ISSUE * + * * + * *********************************************************************** */ + /* hotovo 12.01.04 - JEN OTESTOVAT :] */ +static void +dissect_ISSUE(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + int min_len; + char buff[40]; + SequenceNumber sequenceNumber; /* type struct */ + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + min_len = 16; + if ((flags & FLAG_P) != 0) + min_len += 4; + if (next_submsg_offset < min_len) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= %u)", + next_submsg_offset, min_len); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Reader Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Reader Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Writer Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Writer Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Sequence Number */ + seq_nr_to_string(offset, little_endian, tvb, &sequenceNumber); + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "firstSeqNumber: 0x%X%X", + sequenceNumber.high, sequenceNumber.low); + offset += 8; + + /* Parameters */ +/* *********************************************************************** * + * - for future extension of the protocol - in * + * implementation of RTPS 1.0 can ignore the content * + * *********************************************************************** */ + + /* -- P flag |XXXX|HAPE| => masks with 00000010b = 2 */ + if ((flags & FLAG_P) != 0) + { + get_parameter_sequence(tvb, &offset, little_endian, next_submsg_offset, + rtps_submessage_tree); + } + + /* Issue Data */ + proto_tree_add_item(rtps_submessage_tree, hf_rtps_issue_data, tvb, + offset, (next_submsg_offset - offset), FALSE); + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: ACK * + * * + * *********************************************************************** */ + /* hotovo 12.01.04 - JEN OTESTOVAT :] */ +static void +dissect_ACK(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[40]; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + if (next_submsg_offset < 20) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 20)", + next_submsg_offset); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Reader Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Reader Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Writer Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Writer Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + get_bitmap(tvb,&offset,little_endian,next_submsg_offset,rtps_submessage_tree); + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: HEARTBEAT * + * * + * *********************************************************************** */ + /* hotovo 12.01.04 - JEN OTESTOVAT :] */ +static void +dissect_HEARTBEAT(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[40]; + SequenceNumber sequenceNumber; /* type struct */ + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + if (next_submsg_offset < 24) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 24)", + next_submsg_offset); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Reader Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Reader Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Writer Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Writer Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* firstSeqNumber */ + seq_nr_to_string(offset, little_endian, tvb, &sequenceNumber); + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "firstSeqNumber: 0x%X%X", + sequenceNumber.high, sequenceNumber.low); + offset +=8; + + /* lastSeqNumber */ + seq_nr_to_string(offset, little_endian, tvb, &sequenceNumber); + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "lastSeqNumber: 0x%X%X", + sequenceNumber.high, sequenceNumber.low); + offset +=8; + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: GAP * + * * + * *********************************************************************** */ + /* hotovo 12.01.04 - JEN OTESTOVAT :] */ +static void +dissect_GAP(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[40]; + SequenceNumber sequenceNumber; /* type struct */ + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + if (next_submsg_offset < 28) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 28)", + next_submsg_offset); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Reader Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Reader Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Writer Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Writer Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Sequence Number */ + seq_nr_to_string(offset, little_endian, tvb, &sequenceNumber); + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "firstSeqNumber: 0x%X%X", + sequenceNumber.high, sequenceNumber.low); + offset +=8; + + get_bitmap(tvb,&offset,little_endian,next_submsg_offset,rtps_submessage_tree); + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: INFO_TS * + * * + * *********************************************************************** */ + /* hotovo 12.01.04 - JEN OTESTOVAT :] */ + +static void +dissect_INFO_TS(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[10]; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + /* npTimestamp - valid if flag I = 1 * + * |XXXX|XXIE| => masks with 00000010b = 2 */ + if ((flags & FLAG_I) != 0) + { + if (next_submsg_offset < 8) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 8)", + next_submsg_offset); + return; + } + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* npTimestamp - valid if flag I = 1 * + * |XXXX|XXIE| => masks with 00000010b = 2 */ + if ((flags & FLAG_I) != 0) + { + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "ntpTimestamp: %s (sec)", + get_NtpTime(offset, tvb, little_endian,buff)); + offset +=8; + } + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: INFO_SRC * + * * + * *********************************************************************** */ +/* hotovo 12.01.04 JEN OTESTOVAT :] */ +static void +dissect_INFO_SRC(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[200]; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + if (next_submsg_offset < 16) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 16)", + next_submsg_offset); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* IPAddress */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "appIP address: %s", + IP_to_string(offset, tvb, little_endian,buff)); + offset +=4; + + /* Protocol Version */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 2, + "Protocol RTPS version %s -new", + protocol_version_to_string(offset, tvb, buff)); + offset +=2; + + /* Vendor Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 2, + "VendorId: %s -new", + vendor_id_to_string(offset, tvb, buff)); + offset +=2; + + /* Host Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Host ID: %s", + host_id_to_string(offset,tvb,buff)); + offset+=4; + + /* App Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "App ID: %s-new", + app_id_to_string(offset, tvb, buff)); + offset +=4; + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: INFO_REPLY * + * * + * *********************************************************************** */ + /* hotovo 11.01.04 :] */ +static void +dissect_INFO_REPLY(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + int min_len; + char buff_ip[10], buff_port[10]; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + /* 'multicastReplyAdress' and 'multicastReplyPort' are * + * parts of submessage INFO REPLY which are available * + * only when FLAG M=1 flags: XXXX XXME */ + + if ((flags & FLAG_M) != 0) + min_len = 16; + else + min_len = 8; + if (next_submsg_offset < min_len) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= %u)", + next_submsg_offset, min_len); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Unicat Reply IPAddress */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Unicast Reply IP Adress: %s", + IP_to_string(offset, tvb, little_endian,buff_ip)); + offset +=4; + + + /* Unicast Reply Port */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Unicast Reply IP Port: %s", + port_to_string(offset, tvb, little_endian,buff_port)); + offset +=4; + + + /* 'multicastReplyAdress' and 'multicastReplyPort' are * + * parts of submessage INFO REPLY which are available * + * only when FLAG M=1 flags: XXXX XXME */ + + if ((flags & FLAG_M) != 0) + { + /* Multicast Reply IPAddress */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Multicast Reply IP Adress: %s", + IP_to_string(offset, tvb, little_endian,buff_ip)); + offset +=4; + + /* Multicast Reply Port */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Multicast Reply IP Port: %s", + port_to_string(offset, tvb, little_endian,buff_port)); + offset +=4; + + } +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: INFO_DST * + * * + * *********************************************************************** */ + /* HOTOVO 12.01.04 - JEN OTESOVAT :]*/ +static void +dissect_INFO_DST(tvbuff_t *tvb, gint offset, guint8 flags, + int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[200]; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + if (next_submsg_offset < 8) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 8)", + next_submsg_offset); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Host Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Host ID: %s", + host_id_to_string(offset,tvb,buff)); + offset+=4; + + /* App Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "App ID: %s-new", + app_id_to_string(offset, tvb, buff)); + offset +=4; + +} + +/* *********************************************************************** * + * * + * Register the protocol with Ethereal * + * * + * *********************************************************************** */ + +void proto_register_rtps(void) +{ + static hf_register_info hf[] = { + + { &hf_rtps_submessage_id, + { "Submessage Id", "rtps.submessage_id", + FT_UINT8, BASE_HEX, VALS(submessage_id_vals), 0x0, + "Submessage flags", HFILL }}, + + { &hf_rtps_submessage_flags, + { "Submessage flags", "rtps.submessage_flags", + FT_UINT8, BASE_HEX, NULL, 0x0, + "Submessage flags", HFILL }}, + + { &hf_rtps_octets_to_next_header, + { "Octets to next header", "rtps.octets_to_next_header", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Octets to next header", HFILL }}, + + { &hf_rtps_parameter_id, + { "Parameter Id", "rtps.parameter_id", + FT_UINT16, BASE_HEX, VALS(parameter_id_vals), 0x0, + "Parameter Id", HFILL }}, + + { &hf_rtps_parameter_length, + { "Parameter Length", "rtps.parameter_length", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Parameter Length", HFILL }}, + + { &hf_rtps_issue_data, + { "User Data", "rtps.issue_data", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Issue Data", HFILL }}, + }; + + static gint *ett[] = { + &ett_rtps, + &ett_rtps_submessage, + &ett_rtps_bitmap, + &ett_rtps_parameter_sequence, + &ett_rtps_parameter, + }; + + proto_rtps = proto_register_protocol("Real-Time Publish-Subscribe Wire Protocol", + "RTPS", "rtps"); + proto_register_field_array(proto_rtps, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + +} + + +void +proto_reg_handoff_rtps(void) +{ + heur_dissector_add("udp", dissect_rtps, proto_rtps); +} + diff --git a/src/main/resource/testFiles/wireshark/prevFiles/prev_3f2283_9e184d_epan#dissectors#packet-bacapp.c b/src/main/resource/testFiles/wireshark/prevFiles/prev_3f2283_9e184d_epan#dissectors#packet-bacapp.c new file mode 100644 index 0000000..b9adf3c --- /dev/null +++ b/src/main/resource/testFiles/wireshark/prevFiles/prev_3f2283_9e184d_epan#dissectors#packet-bacapp.c @@ -0,0 +1,5743 @@ +/* packet-bacapp.c + * Routines for BACnet (APDU) dissection + * Copyright 2001, Hartmut Mueller , FH Dortmund + * Enhanced by Steve Karg, 2005, , Atlanta + * Enhanced by Herbert Lischka, 2005, , Berlin + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Copied from README.developer,v 1.23 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "packet-bacapp.h" + +/* some necessary forward function prototypes */ +static guint +fApplicationTypesEnumerated (tvbuff_t *tvb, proto_tree *tree, guint offset, + const gchar *label, const value_string *vs); + +static const char *bacapp_unknown_service_str = "unknown service"; +static const char *ASHRAE_Reserved_Fmt = "(%d) Reserved for Use by ASHRAE"; +static const char *Vendor_Proprietary_Fmt = "(%d) Vendor Proprietary Value"; + +static const value_string +BACnetTypeName[] = { + {0, "Confirmed-Request "}, + {1, "Unconfirmed-Request "}, + {2, "SimpleACK "}, + {3, "ComplexACK "}, + {4, "SegmentACK "}, + {5, "Error "}, + {6, "Reject "}, + {7, "Abort "}, + {0, NULL } +}; + +static const true_false_string segments_follow = { + "Segmented Request", + "Unsegmented Request" +}; + +static const true_false_string more_follow = { + "More Segments Follow", + "No More Segments Follow" +}; + +static const true_false_string segmented_accept = { + "Segmented Response accepted", + "Segmented Response not accepted" +}; + +static const true_false_string +BACnetTagClass = { + "Context Specific Tag", + "Application Tag" +}; + +static const value_string +BACnetMaxSegmentsAccepted [] = { + {0,"Unspecified"}, + {1,"2 segments"}, + {2,"4 segments"}, + {3,"8 segments"}, + {4,"16 segments"}, + {5,"32 segments"}, + {6,"64 segments"}, + {7,"Greater than 64 segments"}, + {0,NULL } +}; + +static const value_string +BACnetMaxAPDULengthAccepted [] = { + {0,"Up to MinimumMessageSize (50 octets)"}, + {1,"Up to 128 octets"}, + {2,"Up to 206 octets (fits in a LonTalk frame)"}, + {3,"Up to 480 octets (fits in an ARCNET frame)"}, + {4,"Up to 1024 octets"}, + {5,"Up to 1476 octets (fits in an ISO 8802-3 frame)"}, + {6,"reserved by ASHRAE"}, + {7,"reserved by ASHRAE"}, + {8,"reserved by ASHRAE"}, + {9,"reserved by ASHRAE"}, + {10,"reserved by ASHRAE"}, + {11,"reserved by ASHRAE"}, + {12,"reserved by ASHRAE"}, + {13,"reserved by ASHRAE"}, + {14,"reserved by ASHRAE"}, + {15,"reserved by ASHRAE"}, + {0,NULL} +}; + +static const value_string +BACnetRejectReason [] = { + {0,"other"}, + {1,"buffer-overflow"}, + {2,"inconsistent-parameters"}, + {3,"invalid-parameter-data-type"}, + {4,"invalid-tag"}, + {5,"missing-required-parameter"}, + {6,"parameter-out-of-range"}, + {7,"too-many-arguments"}, + {8,"undefined-enumeration"}, + {9,"unrecognized-service"}, + {0,NULL} +}; + +static const value_string +BACnetApplicationTagNumber [] = { + {0,"Null"}, + {1,"Boolean"}, + {2,"Unsigned Integer"}, + {3,"Signed Integer (2's complement notation)"}, + {4,"Real (ANSI/IEE-754 floating point)"}, + {5,"Double (ANSI/IEE-754 double precision floating point)"}, + {6,"Octet String"}, + {7,"Character String"}, + {8,"Bit String"}, + {9,"Enumerated"}, + {10,"Date"}, + {11,"Time"}, + {12,"BACnetObjectIdentifier"}, + {13,"reserved by ASHRAE"}, + {14,"reserved by ASHRAE"}, + {15,"reserved by ASHRAE"}, + {0,NULL} +}; + +static const value_string +BACnetAction [] = { + {0,"direct"}, + {1,"reverse"}, + {0,NULL} +}; + +static const value_string +BACnetFileAccessMethod [] = { + {0,"record-access"}, + {1,"stream-access"}, + {0,NULL} +}; + +/* For some reason, BACnet defines the choice parameter + in the file read and write services backwards from the + BACnetFileAccessMethod enumeration. +*/ +static const value_string +BACnetFileAccessOption [] = { + {0,"stream access"}, + {1,"record access"}, + {0,NULL} +}; + +static const value_string +BACnetFileStartOption [] = { + {0, "File Start Position: "}, + {1, "File Start Record: "}, + {0, NULL} +}; + +static const value_string +BACnetFileRequestCount [] = { + {0, "Requested Octet Count: "}, + {1, "Requested Record Count: "}, + {0, NULL} +}; + +static const value_string +BACnetFileWriteInfo [] = { + {0, "File Data: "}, + {1, "Record Count: "}, + {0, NULL} +}; + +static const value_string +BACnetAbortReason [] = { + {0,"other"}, + {1,"buffer-overflow"}, + {2,"invalid-apdu-in-this-state"}, + {3,"preempted-by-higher-priority-task"}, + {4,"segmentation-not-supported"}, + {0,NULL} +}; + +static const value_string +BACnetLifeSafetyMode [] = { + {0,"off"}, + {1,"on"}, + {2,"test"}, + {3,"manned"}, + {4,"unmanned"}, + {5,"armed"}, + {6,"disarmed"}, + {7,"prearmed"}, + {8,"slow"}, + {9,"fast"}, + {10,"disconnected"}, + {11,"enabled"}, + {12,"disabled"}, + {13,"atomic-release-disabled"}, + {14,"default"}, + {0,NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetLifeSafetyOperation [] = { + {0,"none"}, + {1,"silence"}, + {2,"silence-audible"}, + {3,"silence-visual"}, + {4,"reset"}, + {5,"reset-alarm"}, + {6,"reset-fault"}, + {7,"unsilence"}, + {8,"unsilence-audible"}, + {9,"unsilence-visual"}, + {0,NULL} +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetLimitEnable [] = { + {0,"lowLimitEnable"}, + {1,"highLimitEnable"}, + {0,NULL} +}; + +static const value_string +BACnetLifeSafetyState [] = { + {0,"quiet"}, + {1,"pre-alarm"}, + {2,"alarm"}, + {3,"fault"}, + {4,"fault-pre-alarm"}, + {5,"fault-alarm"}, + {6,"not-ready"}, + {7,"active"}, + {8,"tamper"}, + {9,"test-alarm"}, + {10,"test-active"}, + {11,"test-fault"}, + {12,"test-fault-alarm"}, + {13,"holdup"}, + {14,"duress"}, + {15,"tamper-alarm"}, + {16,"abnormal"}, + {17,"emergency-power"}, + {18,"delayed"}, + {19,"blocked"}, + {20,"local-alarm"}, + {21,"general-alarm"}, + {22,"supervisory"}, + {23,"test-supervisory"}, + {0,NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetConfirmedServiceChoice [] = { + {0,"acknowledgeAlarm"}, + {1,"confirmedCOVNotification"}, + {2,"confirmedEventNotification"}, + {3,"getAlarmSummary"}, + {4,"getEnrollmentSummary"}, + {5,"subscribeCOV"}, + {6,"atomicReadFile"}, + {7,"atomicWriteFile"}, + {8,"addListElement"}, + {9,"removeListElement"}, + {10,"createObject"}, + {11,"deleteObject"}, + {12,"readProperty"}, + {13,"readPropertyConditional"}, + {14,"readPropertyMultiple"}, + {15,"writeProperty"}, + {16,"writePropertyMultiple"}, + {17,"deviceCommunicationControl"}, + {18,"confirmedPrivateTransfer"}, + {19,"confirmedTextMessage"}, + {20,"reinitializeDevice"}, + {21,"vtOpen"}, + {22,"vtClose"}, + {23,"vtData"}, + {24,"authenticate"}, + {25,"requestKey"}, + {26,"readRange"}, + {27,"lifeSafetyOperation"}, + {28,"subscribeCOVProperty"}, + {29,"getEventInformation"}, + {30,"reserved by ASHRAE"}, + {0, NULL} +}; + +static const value_string +BACnetReliability [] = { + {0,"no-fault-detected"}, + {1,"no-sensor"}, + {2,"over-range"}, + {3,"under-range"}, + {4,"open-loop"}, + {5,"shorted-loop"}, + {6,"no-output"}, + {7,"unreliable-other"}, + {8,"process-error"}, + {9,"multi-state-fault"}, + {0,NULL} +}; + +static const value_string +BACnetUnconfirmedServiceChoice [] = { + {0,"i-Am"}, + {1,"i-Have"}, + {2,"unconfirmedCOVNotification"}, + {3,"unconfirmedEventNotification"}, + {4,"unconfirmedPrivateTransfer"}, + {5,"unconfirmedTextMessage"}, + {6,"timeSynchronization"}, + {7,"who-Has"}, + {8,"who-Is"}, + {9,"utcTimeSynchonization"}, + {0,NULL} +}; + +static const value_string +BACnetUnconfirmedServiceRequest [] = { + {0,"i-Am-Request"}, + {1,"i-Have-Request"}, + {2,"unconfirmedCOVNotification-Request"}, + {3,"unconfirmedEventNotification-Request"}, + {4,"unconfirmedPrivateTransfer-Request"}, + {5,"unconfirmedTextMessage-Request"}, + {6,"timeSynchronization-Request"}, + {7,"who-Has-Request"}, + {8,"who-Is-Request"}, + {9,"utcTimeSynchonization-Request"}, + {0,NULL} +}; + +static const value_string +BACnetObjectType [] = { + {0,"analog-input object"}, + {1,"analog-output object"}, + {2,"analog-value object"}, + {3,"binary-input object"}, + {4,"binary-output object"}, + {5,"binary-value object"}, + {6,"calendar object"}, + {7,"command object"}, + {8,"device object"}, + {9,"event-enrollment object"}, + {10,"file object"}, + {11,"group object"}, + {12,"loop object"}, + {13,"multi-state-input object"}, + {14,"multi-state-output object"}, + {15,"notification-class object"}, + {16,"program object"}, + {17,"schedule object"}, + {18,"averaging object"}, + {19,"multi-state-value object"}, + {20,"trend-log object"}, + {21,"life-safety-point object"}, + {22,"life-safety-zone object"}, + {23,"accumulator object"}, + {24,"pulse-converter object"}, + {0, NULL} +/* Enumerated values 0-127 are reserved for definition by ASHRAE. + Enumerated values 128-1023 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetEngineeringUnits [] = { + {0,"Sq Meters"}, + {1,"Sq Feet"}, + {2,"Milliamperes"}, + {3,"Amperes"}, + {4,"Ohms"}, + {5,"Volts"}, + {6,"Kilovolts"}, + {7,"Megavolts"}, + {8,"Volt Amperes"}, + {9,"Kilovolt Amperes"}, + {10,"Megavolt Amperes"}, + {11,"Volt Amperes Reactive"}, + {12,"Kilovolt Amperes Reactive"}, + {13,"Megavolt Amperes Ractive"}, + {14,"Degrees Phase"}, + {15,"Power Factor"}, + {16,"Joules"}, + {17,"Kilojoules"}, + {18,"Watt Hours"}, + {19,"Kilowatt Hours"}, + {20,"BTUs"}, + {21,"Therms"}, + {22,"Ton Hours"}, + {23,"Joules Per Kg Dry Air"}, + {24,"BTUs Per Pound Dry Air"}, + {25,"Cycles Per Hour"}, + {26,"Cycles Per Minute"}, + {27,"Hertz"}, + {28,"Gramms Of Water Per Kilogram Dry Air"}, + {29,"Relative Humidity"}, + {30,"Millimeters"}, + {31,"Meters"}, + {32,"Inches"}, + {33,"Feed"}, + {34,"Watts Per Sq Foot"}, + {35,"Watts Per Sq meter"}, + {36,"Lumens"}, + {37,"Lux"}, + {38,"Foot Candels"}, + {39,"Kilograms"}, + {40,"Pounds Mass"}, + {41,"Tons"}, + {42,"Kgs per Second"}, + {43,"Kgs Per Minute"}, + {44,"Kgs Per Hour"}, + {45,"Pounds Mass Per Minute"}, + {46,"Pounds Mass Per Hour"}, + {47,"Watt"}, + {48,"Kilowatts"}, + {49,"Megawatts"}, + {50,"BTUs Per Hour"}, + {51,"Horsepower"}, + {52,"Tons Refrigeration"}, + {53,"Pascals"}, + {54,"Kilopascals"}, + {55,"Bars"}, + {56,"Pounds Force Per Square Inch"}, + {57,"Centimeters Of Water"}, + {58,"Inches Of Water"}, + {59,"Millimeters Of Mercury"}, + {60,"Centimeters Of Mercury"}, + {61,"Inches Of Mercury"}, + {62,"Degrees Celsius"}, + {63,"Degress Kelvin"}, + {64,"Degrees Fahrenheit"}, + {65,"Degree Days Celsius"}, + {66,"Degree Days Fahrenheit"}, + {67,"Years"}, + {68,"Months"}, + {69,"Weeks"}, + {70,"Days"}, + {71,"Hours"}, + {72,"Minutes"}, + {73,"Seconds"}, + {74,"Meters Per Second"}, + {75,"Kilometers Per Hour"}, + {76,"Feed Per Second"}, + {77,"Feet Per Minute"}, + {78,"Miles Per Hour"}, + {79,"Cubic Feet"}, + {80,"Cubic Meters"}, + {81,"Imperial Gallons"}, + {82,"Liters"}, + {83,"US Gallons"}, + {84,"Cubic Feet Per Minute"}, + {85,"Cubic Meters Per Second"}, + {86,"Imperial Gallons Per Minute"}, + {87,"Liters Per Second"}, + {88,"Liters Per Minute"}, + {89,"US Gallons Per Minute"}, + {90,"Degrees Angular"}, + {91,"Degrees Celsius Per Hour"}, + {92,"Degrees Celsius Per Minute"}, + {93,"Degrees Fahrenheit Per Hour"}, + {94,"Degrees Fahrenheit Per Minute"}, + {95,"No Units"}, + {96,"Parts Per Million"}, + {97,"Parts Per Billion"}, + {98,"Percent"}, + {99,"Pecent Per Second"}, + {100,"Per Minute"}, + {101,"Per Second"}, + {102,"Psi Per Degree Fahrenheit"}, + {103,"Radians"}, + {104,"Revolutions Per Min"}, + {105,"Currency1"}, + {106,"Currency2"}, + {107,"Currency3"}, + {108,"Currency4"}, + {109,"Currency5"}, + {110,"Currency6"}, + {111,"Currency7"}, + {112,"Currency8"}, + {113,"Currency9"}, + {114,"Currency10"}, + {115,"Sq Inches"}, + {116,"Sq Centimeters"}, + {117,"BTUs Per Pound"}, + {118,"Centimeters"}, + {119,"Pounds Mass Per Second"}, + {120,"Delta Degrees Fahrenheit"}, + {121,"Delta Degrees Kelvin"}, + {122,"Kilohms"}, + {123,"Megohms"}, + {124,"Millivolts"}, + {125,"Kilojoules Per Kg"}, + {126,"Megajoules"}, + {127,"Joules Per Degree Kelvin"}, + {128,"Joules Per Kg Degree Kelvin"}, + {129,"Kilohertz"}, + {130,"Megahertz"}, + {131,"Per Hour"}, + {132,"Milliwatts"}, + {133,"Hectopascals"}, + {134,"Millibars"}, + {135,"Cubic Meters Per Hour"}, + {136,"Liters Per Hour"}, + {137,"KWatt Hours Per Square Meter"}, + {138,"KWatt Hours Per Square Foot"}, + {139,"Megajoules Per Square Meter"}, + {140,"Megajoules Per Square Foot"}, + {141,"Watts Per Sq Meter Degree Kelvin"}, + {142,"Cubic Feet Per Second"}, + {143,"Percent Obstruction Per Foot"}, + {144,"Percent Obstruction Per Meter"}, + {145,"milliohms"}, + {146,"megawatt-hours"}, + {147,"kilo-btus"}, + {148,"mega-btus"}, + {149,"kilojoules-per-kilogram-dry-air"}, + {150,"megajoules-per-kilogram-dry-air"}, + {151,"kilojoules-per-degree-Kelvin"}, + {152,"megajoules-per-degree-Kelvin"}, + {153,"newton"}, + {154,"grams-per-second"}, + {155,"grams-per-minute"}, + {156,"tons-per-hour"}, + {157,"kilo-btus-per-hour"}, + {158,"hundredths-seconds"}, + {159,"milliseconds"}, + {160,"newton-meters"}, + {161,"millimeters-per-second"}, + {162,"millimeters-per-minute"}, + {163,"meters-per-minute"}, + {164,"meters-per-hour"}, + {165,"cubic-meters-per-minute"}, + {166,"meters-per-second-per-second"}, + {167,"amperes-per-meter"}, + {168,"amperes-per-square-meter"}, + {169,"ampere-square-meters"}, + {170,"farads"}, + {171,"henrys"}, + {172,"ohm-meters"}, + {173,"siemens"}, + {174,"siemens-per-meter"}, + {175,"teslas"}, + {176,"volts-per-degree-Kelvin"}, + {177,"volts-per-meter"}, + {178,"webers"}, + {179,"candelas"}, + {180,"candelas-per-square-meter"}, + {181,"degrees-Kelvin-per-hour"}, + {182,"degrees-Kelvin-per-minute"}, + {183,"joule-seconds"}, + {184,"radians-per-second"}, + {185,"square-meters-per-Newton"}, + {186,"kilograms-per-cubic-meter"}, + {187,"newton-seconds"}, + {188,"newtons-per-meter"}, + {189,"watts-per-meter-per-degree-Kelvin"}, + {0,NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetErrorCode [] = { + {0,"other"}, + {1,"authentication-failed"}, + {2,"character-set-not-supported"}, + {3,"configuration-in-progress"}, + {4,"device-busy"}, + {5,"file-access-denied"}, + {6,"incompatible-security-levels"}, + {7,"inconsistent-parameters"}, + {8,"inconsistent-selection-criterion"}, + {9,"invalid-data-type"}, + {10,"invalid-file-access-method"}, + {11,"invalid-file-start-position"}, + {12,"invalid-operator-name"}, + {13,"invalid-parameter-data-type"}, + {14,"invalid-time-stamp"}, + {15,"key-generation-error"}, + {16,"missing-required-parameter"}, + {17,"no-objects-of-specified-type"}, + {18,"no-space-for-object"}, + {19,"no-space-to-add-list-element"}, + {20,"no-space-to-write-property"}, + {21,"no-vt-sessions-available"}, + {22,"property-is-not-a-list"}, + {23,"object-deletion-not-permitted"}, + {24,"object-identifier-already-exists"}, + {25,"operational-problem"}, + {26,"password-failure"}, + {27,"read-access-denied"}, + {28,"security-not-supported"}, + {29,"service-request-denied"}, + {30,"timeout"}, + {31,"unknown-object"}, + {32,"unknown-property"}, + {33,"removed enumeration"}, + {34,"unknown-vt-class"}, + {35,"unknown-vt-session"}, + {36,"unsupported-object-type"}, + {37,"value-out-of-range"}, + {38,"vt-session-already-closed"}, + {39,"vt-session-termination-failure"}, + {40,"write-access-denied"}, + {41,"character-set-not-supported"}, + {42,"invalid-array-index"}, + {43,"cov-subscription-failed"}, + {44,"not-cov-property"}, + {45,"optional-functionaltity-not-supported"}, + {46,"invalid-configuration-data"}, + {47,"datatype-not-supported"}, + {48,"duplicate-name"}, + {49,"duplicate-object-id"}, + {50,"property-is-not-an-array"}, + {0, NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to the + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetPropertyIdentifier [] = { + {0,"acked-transition"}, + {1,"ack-required"}, + {2,"action"}, + {3,"action-text"}, + {4,"active-text"}, + {5,"active-vt-session"}, + {6,"alarm-value"}, + {7,"alarm-values"}, + {8,"all"}, + {9,"all-write-successful"}, + {10,"apdu-segment-timeout"}, + {11,"apdu-timeout"}, + {12,"application-software-version"}, + {13,"archive"}, + {14,"bias"}, + {15,"change-of-state-count"}, + {16,"change-of-state-time"}, + {17,"notification-class"}, + {18,"the property in this place was deleted"}, + {19,"controlled-variable-reference"}, + {20,"controlled-variable-units"}, + {21,"controlled-variable-value"}, + {22,"cov-increment"}, + {23,"datelist"}, + {24,"daylights-savings-status"}, + {25,"deadband"}, + {26,"derivative-constant"}, + {27,"derivative-constant-units"}, + {28,"description"}, + {29,"description-of-halt"}, + {30,"device-address-binding"}, + {31,"device-type"}, + {32,"effective-period"}, + {33,"elapsed-active-time"}, + {34,"error-limit"}, + {35,"event-enable"}, + {36,"event-state"}, + {37,"event-type"}, + {38,"exception-schedule"}, + {39,"fault-values"}, + {40,"feedback-value"}, + {41,"file-access-method"}, + {42,"file-size"}, + {43,"file-type"}, + {44,"firmware-revision"}, + {45,"high-limit"}, + {46,"inactive-text"}, + {47,"in-progress"}, + {48,"instance-of"}, + {49,"integral-constant"}, + {50,"integral-constant-units"}, + {51,"issue-confirmed-notifications"}, + {52,"limit-enable"}, + {53,"list-of-group-members"}, + {54,"list-of-object-property-references"}, + {55,"list-of-session-keys"}, + {56,"local-date"}, + {57,"local-time"}, + {58,"location"}, + {59,"low-limit"}, + {60,"manipulated-variable-reference"}, + {61,"maximum-output"}, + {62,"max-apdu-length-accepted"}, + {63,"max-info-frames"}, + {64,"max-master"}, + {65,"max-pres-value"}, + {66,"minimum-off-time"}, + {67,"minimum-on-time"}, + {68,"minimum-output"}, + {69,"min-pres-value"}, + {70,"model-name"}, + {71,"modification-date"}, + {72,"notify-type"}, + {73,"number-of-APDU-retries"}, + {74,"number-of-states"}, + {75,"object-identifier"}, + {76,"object-list"}, + {77,"object-name"}, + {78,"object-property-reference"}, + {79,"object-type"}, + {80,"optional"}, + {81,"out-of-service"}, + {82,"output-units"}, + {83,"event-parameters"}, + {84,"polarity"}, + {85,"present-value"}, + {86,"priority"}, + {87,"priority-array"}, + {88,"priority-for-writing"}, + {89,"process-identifier"}, + {90,"program-change"}, + {91,"program-location"}, + {92,"program-state"}, + {93,"proportional-constant"}, + {94,"proportional-constant-units"}, + {95,"protocol-conformance-class"}, + {96,"protocol-object-types-supported"}, + {97,"protocol-services-supported"}, + {98,"protocol-version"}, + {99,"read-only"}, + {100,"reason-for-halt"}, + {101,"recipient"}, + {102,"recipient-list"}, + {103,"reliability"}, + {104,"relinquish-default"}, + {105,"required"}, + {106,"resolution"}, + {107,"segmentation-supported"}, + {108,"setpoint"}, + {109,"setpoint-reference"}, + {110,"state-text"}, + {111,"status-flags"}, + {112,"system-status"}, + {113,"time-delay"}, + {114,"time-of-active-time-reset"}, + {115,"time-of-state-count-reset"}, + {116,"time-synchronization-recipients"}, + {117,"units"}, + {118,"update-interval"}, + {119,"utc-offset"}, + {120,"vendor-identifier"}, + {121,"vendor-name"}, + {122,"vt-class-supported"}, + {123,"weekly-schedule"}, + {124,"attempted-samples"}, + {125,"average-value"}, + {126,"buffer-size"}, + {127,"client-cov-increment"}, + {128,"cov-resubscription-interval"}, + {129,"current-notify-time"}, + {130,"event-time-stamp"}, + {131,"log-buffer"}, + {132,"log-device-object-property"}, + {133,"log-enable"}, + {134,"log-interval"}, + {135,"maximum-value"}, + {136,"minimum-value"}, + {137,"notification-threshold"}, + {138,"previous-notify-time"}, + {139,"protocol-revision"}, + {140,"records-since-notification"}, + {141,"record-count"}, + {142,"start-time"}, + {143,"stop-time"}, + {144,"stop-when-full"}, + {145,"total-record-count"}, + {146,"valid-samples"}, + {147,"window-interval"}, + {148,"window-samples"}, + {149,"maximum-value-time-stamp"}, + {150,"minimum-value-time-stamp"}, + {151,"variance-value"}, + {152,"active-cov-subscriptions"}, + {153,"backup-failure-timeout"}, + {154,"configuration-files"}, + {155,"database-revision"}, + {156,"direct-reading"}, + {157,"last-restore-time"}, + {158,"maintenance-required"}, + {159,"member-of"}, + {160,"mode"}, + {161,"operation-expected"}, + {162,"setting"}, + {163,"silenced"}, + {164,"tracking-value"}, + {165,"zone-members"}, + {166,"life-safety-alarm-values"}, + {167,"max-segments-accepted"}, + {168,"profile-name"}, + {169,"auto-slave-discovery"}, + {170,"manual-slave-address-binding"}, + {171,"slave-address-binding"}, + {172,"slave-proxy-enable"}, + {173,"last-notify-time"}, + {174,"schedule-default"}, + {175,"accepted-modes"}, + {176,"adjust-value"}, + {177,"count"}, + {178,"count-before-change"}, + {179,"count-change-time"}, + {180,"cov-period"}, + {181,"input-reference"}, + {182,"limit-monitoring-interval"}, + {183,"logging-device"}, + {184,"logging-record"}, + {185,"prescale"}, + {186,"pulse-rate"}, + {187,"scale"}, + {188,"scale-factor"}, + {189,"update-time"}, + {190,"value-before-change"}, + {191,"value-set"}, + {192,"value-change-time"}, + {0, NULL} +/* Enumerated values 0-511 are reserved for definition by ASHRAE. + Enumerated values 512-4194303 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetBinaryPV [] = { + {0,"inactive"}, + {1,"active"}, + {0,NULL} +}; + + +static const value_string +BACnetCharacterSet [] = { + {0,"ANSI X3.4"}, + {1,"IBM/Microsoft DBCS"}, + {2,"JIS C 6226"}, + {3,"ISO 10646(UCS-4)"}, + {4,"ISO 10646(UCS-2)"}, + {5,"ISO 18859-1"}, + {0,NULL} +}; + +static const value_string +BACnetStatusFlags [] = { + {0,"in-alarm"}, + {1,"fault"}, + {2,"overridden"}, + {3,"out-of-service"}, + {0,NULL} +}; + +static const value_string +BACnetMessagePriority [] = { + {0,"normal"}, + {1,"urgent"}, + {0,NULL} +}; + +static const value_string +BACnetAcknowledgementFilter [] = { + {0,"all"}, + {1,"acked"}, + {2,"not-acked"}, + {0,NULL} +}; + +static const value_string +BACnetResultFlags [] = { + {0,"firstitem"}, + {1,"lastitem"}, + {2,"moreitems"}, + {0,NULL} +}; + +static const value_string +BACnetRelationSpecifier [] = { + {0,"equal"}, + {1,"not-equal"}, + {2,"less-than"}, + {3,"greater-than"}, + {4,"less-than-or-equal"}, + {5,"greater-than-or-equal"}, + {0,NULL} +}; + +static const value_string +BACnetSelectionLogic [] = { + {0,"and"}, + {1,"or"}, + {2,"all"}, + {0,NULL} +}; + +static const value_string +BACnetEventStateFilter [] = { + {0,"offnormal"}, + {1,"fault"}, + {2,"normal"}, + {3,"all"}, + {4,"active"}, + {0,NULL} +}; + +static const value_string +BACnetEventTransitionBits [] = { + {0,"to-offnormal"}, + {1,"to-fault"}, + {2,"to-normal"}, + {0,NULL} +}; + +static const value_string +BACnetSegmentation [] = { + {0,"segmented-both"}, + {1,"segmented-transmit"}, + {2,"segmented-receive"}, + {3,"no-segmentation"}, + {0,NULL} +}; + +static const value_string +BACnetSilencedState [] = { + {0,"unsilenced"}, + {1,"audible-silenced"}, + {2,"visible-silenced"}, + {3,"all-silenced"}, + {0,NULL} +}; + +static const value_string +BACnetDeviceStatus [] = { + {0,"operational"}, + {1,"operational-read-only"}, + {2,"download-required"}, + {3,"download-in-progress"}, + {4,"non-operational"}, + {5,"backup-in-progress"}, + {0,NULL} +}; + +static const value_string +BACnetEnableDisable [] = { + {0,"enable"}, + {1,"disable"}, + {2,"disable-initiation"}, + {0,NULL} +}; + +static const value_string +months [] = { + {1,"January" }, + {2,"February" }, + {3,"March" }, + {4,"April" }, + {5,"May" }, + {6,"June" }, + {7,"July" }, + {8,"August" }, + {9,"September" }, + {10,"October" }, + {11,"November" }, + {12,"December" }, + {255,"any month" }, + {0,NULL } +}; + +static const value_string +weekofmonth [] = { + {1,"days numbered 1-7" }, + {2,"days numbered 8-14" }, + {3,"days numbered 15-21" }, + {4,"days numbered 22-28" }, + {5,"days numbered 29-31" }, + {6,"last 7 days of this month" }, + {255,"any week of this month" }, + {0,NULL } +}; + +static const value_string +days [] = { + {1,"Monday" }, + {2,"Tuesday" }, + {3,"Wednesday" }, + {4,"Thursday" }, + {5,"Friday" }, + {6,"Saturday" }, + {7,"Sunday" }, + {255,"any day of week" }, + {0,NULL }, +}; + +static const value_string +BACnetErrorClass [] = { + {0,"device" }, + {1,"object" }, + {2,"property" }, + {3,"resources" }, + {4,"security" }, + {5,"services" }, + {6,"vt" }, + {0,NULL }, +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetVTClass [] = { + {0,"default-terminal" }, + {1,"ansi-x3-64" }, + {2,"dec-vt52" }, + {3,"dec-vt100" }, + {4,"dec-vt200" }, + {5,"hp-700-94" }, + {6,"ibm-3130" }, + {0,NULL }, +}; + +static const value_string +BACnetEventType [] = { + {0,"change-of-bitstring" }, + {1,"change-of-state" }, + {2,"change-of-value" }, + {3,"command-failure" }, + {4,"floating-limit" }, + {5,"out-of-range" }, + {6,"complex-event-type" }, + {7,"buffer-ready" }, + {8,"change-of-life-safety" }, + {9,"extended" }, + {10,"buffer-ready" }, + {11,"unsigned-range" }, + {0,NULL }, +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. + It is expected that these enumerated values will correspond + to the use of the complex-event-type CHOICE [6] of the + BACnetNotificationParameters production. */ +}; + +static const value_string +BACnetEventState [] = { + {0,"normal" }, + {1,"fault" }, + {2,"offnormal" }, + {3,"high-limit" }, + {4,"low-limit" }, + {5,"life-safety-alarm" }, + {0,NULL }, +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetLogStatus [] = { + {0,"log-disabled" }, + {1,"buffer-purged" }, + {0,NULL }, +}; + +static const value_string +BACnetMaintenance [] = { + {0,"none" }, + {1,"periodic-test" }, + {2,"need-service-operational" }, + {3,"need-service-inoperative" }, + {0,NULL }, +}; + +static const value_string +BACnetNotifyType [] = { + {0,"alarm" }, + {1,"event" }, + {2,"ack-notification" }, + {0,NULL }, +}; + +static const value_string +BACnetServicesSupported [] = { + {0,"acknowledgeAlarm"}, + {1,"confirmedCOVNotification"}, + {2,"confirmedEventNotification"}, + {3,"getAlarmSummary"}, + {4,"getEnrollmentSummary"}, + {5,"subscribeCOV"}, + {6,"atomicReadFile"}, + {7,"atomicWriteFile"}, + {8,"addListElement"}, + {9,"removeListElement"}, + {10,"createObject"}, + {11,"deleteObject"}, + {12,"readProperty"}, + {13,"readPropertyConditional"}, + {14,"readPropertyMultiple"}, + {15,"writeProperty"}, + {16,"writePropertyMultiple"}, + {17,"deviceCommunicationControl"}, + {18,"confirmedPrivateTransfer"}, + {19,"confirmedTextMessage"}, + {20,"reinitializeDevice"}, + {21,"vtOpen"}, + {22,"vtClose"}, + {23,"vtData"}, + {24,"authenticate"}, + {25,"requestKey"}, + {26,"i-Am"}, + {27,"i-Have"}, + {28,"unconfirmedCOVNotification"}, + {29,"unconfirmedEventNotification"}, + {30,"unconfirmedPrivateTransfer"}, + {31,"unconfirmedTextMessage"}, + {32,"timeSynchronization"}, + {33,"who-Has"}, + {34,"who-Is"}, + {35,"readRange"}, + {36,"utcTimeSynchronization"}, + {37,"lifeSafetyOperation"}, + {38,"subscribeCOVProperty"}, + {39,"getEventInformation"}, + {0, NULL} +}; + +static const value_string +BACnetPropertyStates [] = { + {0,"boolean-value"}, + {1,"binary-value"}, + {2,"event-type"}, + {3,"polarity"}, + {4,"program-change"}, + {5,"program-state"}, + {6,"reason-for-halt"}, + {7,"reliability"}, + {8,"state"}, + {9,"system-status"}, + {10,"units"}, + {11,"unsigned-value"}, + {12,"life-safety-mode"}, + {13,"life-safety-state"}, + {0,NULL} +/* Tag values 0-63 are reserved for definition by ASHRAE. + Tag values of 64-254 may be used by others to accommodate + vendor specific properties that have discrete or enumerated values, + subject to the constraints described in Clause 23. */ +}; + +static const value_string +BACnetProgramError [] = { + {0,"normal"}, + {1,"load-failed"}, + {2,"internal"}, + {3,"program"}, + {4,"other"}, + {0,NULL} +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetProgramRequest [] = { + {0,"ready"}, + {1,"load"}, + {2,"run"}, + {3,"halt"}, + {4,"restart"}, + {4,"unload"}, + {0,NULL} +}; + +static const value_string +BACnetProgramState [] = { + {0,"idle"}, + {1,"loading"}, + {2,"running"}, + {3,"waiting"}, + {4,"halted"}, + {4,"unloading"}, + {0,NULL} +}; + +static const value_string +BACnetReinitializedStateOfDevice [] = { + {0,"coldstart"}, + {1,"warmstart"}, + {2,"startbackup"}, + {3,"endbackup"}, + {4,"startrestore"}, + {5,"endrestore"}, + {6,"abortrestore"}, + {0,NULL} +}; + +static const value_string +BACnetPolarity [] = { + {0,"normal"}, + {1,"reverse"}, + {0,NULL} +}; + +static const value_string +BACnetTagNames[] = { + { 5, "Extended Value" }, + { 6, "Opening Tag" }, + { 7, "Closing Tag" }, + { 0, NULL } +}; + +static const value_string +BACnetReadRangeOptions[] = { + { 3, "range byPosition" }, + { 4, "range byTime" }, + { 5, "range timeRange" }, + { 6, "range bySequenceNumber" }, + { 7, "range byTime" }, + { 0, NULL } +}; + +static int proto_bacapp = -1; +static int hf_bacapp_type = -1; +static int hf_bacapp_pduflags = -1; +static int hf_bacapp_SEG = -1; +static int hf_bacapp_MOR = -1; +static int hf_bacapp_SA = -1; +static int hf_bacapp_response_segments = -1; +static int hf_bacapp_max_adpu_size = -1; +static int hf_bacapp_invoke_id = -1; +static int hf_bacapp_objectType = -1; +static int hf_bacapp_instanceNumber = -1; +static int hf_bacapp_sequence_number = -1; +static int hf_bacapp_window_size = -1; +static int hf_bacapp_service = -1; +static int hf_bacapp_NAK = -1; +static int hf_bacapp_SRV = -1; +static int hf_BACnetRejectReason = -1; +static int hf_BACnetAbortReason = -1; +static int hf_BACnetApplicationTagNumber = -1; +static int hf_BACnetContextTagNumber = -1; +static int hf_BACnetExtendedTagNumber = -1; +static int hf_BACnetNamedTag = -1; +static int hf_BACnetTagClass = -1; +static int hf_BACnetCharacterSet = -1; +static int hf_bacapp_tag = -1; +static int hf_bacapp_tag_lvt = -1; +static int hf_bacapp_tag_value8 = -1; +static int hf_bacapp_tag_value16 = -1; +static int hf_bacapp_tag_value32 = -1; +static int hf_bacapp_tag_ProcessId = -1; +static int hf_bacapp_tag_initiatingObjectType = -1; +static int hf_bacapp_vpart = -1; + +static int hf_bacapp_uservice = -1; + + +static gint ett_bacapp = -1; +static gint ett_bacapp_control = -1; +static gint ett_bacapp_tag = -1; +static gint ett_bacapp_list = -1; +static gint ett_bacapp_value = -1; + +static dissector_handle_t data_handle; +static gint32 propertyIdentifier = -1; +static guint32 object_type = 4096; + +static guint8 bacapp_flags = 0; +static guint8 bacapp_seq = 0; + +/* Used when there are ranges of reserved and proprietary enumerations */ +static const char* +val_to_split_str(guint32 val, guint32 split_val, const value_string *vs, + const char *fmt, const char *split_fmt) +{ + if (val < split_val) + return val_to_str(val, vs, fmt); + else + return val_to_str(val, vs, split_fmt); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns true if the extended value is used */ +static gboolean tag_is_extended_value(guint8 tag) +{ + return (tag & 0x07) == 5; +} + +static gboolean tag_is_opening(guint8 tag) +{ + return (tag & 0x07) == 6; +} + +static gboolean tag_is_closing(guint8 tag) +{ + return (tag & 0x07) == 7; +} + +/* from clause 20.2.1.1 Class + class bit shall be one for context specific tags */ +/* returns true if the tag is context specific */ +static gboolean tag_is_context_specific(guint8 tag) +{ + return (tag & 0x08) != 0; +} + +static gboolean tag_is_extended_tag_number(guint8 tag) +{ + return ((tag & 0xF0) == 0xF0); +} + +static guint32 object_id_type(guint32 object_identifier) +{ + return ((object_identifier >> 22) & 0x3FF); +} + +static guint32 object_id_instance(guint32 object_identifier) +{ + return (object_identifier & 0x3FFFFF); +} + +static guint +fTagNo (tvbuff_t *tvb, guint offset) +{ + return (guint)(tvb_get_guint8(tvb, offset) >> 4); +} + +static gboolean +fUnsigned32 (tvbuff_t *tvb, guint offset, guint32 lvt, guint32 *val) +{ + gboolean valid = TRUE; + + switch (lvt) { + case 1: + *val = tvb_get_guint8(tvb, offset); + break; + case 2: + *val = tvb_get_ntohs(tvb, offset); + break; + case 3: + *val = tvb_get_ntoh24(tvb, offset); + break; + case 4: + *val = tvb_get_ntohl(tvb, offset); + break; + default: + valid = FALSE; + break; + } + + return valid; +} + +static gboolean +fUnsigned64 (tvbuff_t *tvb, guint offset, guint32 lvt, guint64 *val) +{ + gboolean valid = FALSE; + gint64 value = 0; + guint8 data, i; + + if (lvt && (lvt <= 8)) { + valid = TRUE; + data = tvb_get_guint8(tvb, offset); + for (i = 0; i < lvt; i++) { + data = tvb_get_guint8(tvb, offset+i); + value = (value << 8) + data; + } + *val = value; + } + + return valid; +} + +/* BACnet Signed Value uses 2's compliment notation, but with a twist: + All signed integers shall be encoded in the smallest number of octets + possible. That is, the first octet of any multi-octet encoded value + shall not be X'00' if the most significant bit (bit 7) of the second + octet is 0, and the first octet shall not be X'FF' if the most + significant bit of the second octet is 1. ASHRAE-135-2004-20.2.5 */ +static gboolean +fSigned64 (tvbuff_t *tvb, guint offset, guint32 lvt, gint64 *val) +{ + gboolean valid = FALSE; + gint64 value = 0; + guint8 data, i; + + /* we can only handle 7 bytes for a 64-bit value due to signed-ness */ + if (lvt && (lvt <= 7)) { + valid = TRUE; + data = tvb_get_guint8(tvb, offset); + if ((data & 0x80) != 0) + value = (-1 << 8) | data; + else + value = data; + for (i = 1; i < lvt; i++) { + data = tvb_get_guint8(tvb, offset+i); + value = (value << 8) + data; + } + *val = value; + } + + return valid; +} + +static guint +fTagHeaderTree (tvbuff_t *tvb, proto_tree *tree, guint offset, + guint8 *tag_no, guint8* tag_info, guint32 *lvt) +{ + guint8 tag; + guint8 value; + guint tag_len = 1; + guint lvt_len = 1; /* used for tree display of lvt */ + guint lvt_offset; /* used for tree display of lvt */ + proto_item *ti; + proto_tree *subtree; + + lvt_offset = offset; + tag = tvb_get_guint8(tvb, offset); + *tag_info = 0; + *lvt = tag & 0x07; + /* To solve the problem of lvt values of 6/7 being indeterminate - it */ + /* can mean open/close tag or length of 6/7 after the length is */ + /* computed below - store whole tag info, not just context bit. */ + if (tag_is_context_specific(tag)) *tag_info = tag & 0x0F; + *tag_no = tag >> 4; + if (tag_is_extended_tag_number(tag)) { + *tag_no = tvb_get_guint8(tvb, offset + tag_len++); + } + if (tag_is_extended_value(tag)) { /* length is more than 4 Bytes */ + lvt_offset += tag_len; + value = tvb_get_guint8(tvb, lvt_offset); + tag_len++; + if (value == 254) { /* length is encoded with 16 Bits */ + *lvt = tvb_get_ntohs(tvb, lvt_offset+1); + tag_len += 2; + lvt_len += 2; + } else if (value == 255) { /* length is encoded with 32 Bits */ + *lvt = tvb_get_ntohl(tvb, lvt_offset+1); + tag_len += 4; + lvt_len += 4; + } else + *lvt = value; + } + if (tree) + { + if (tag_is_closing(tag) || tag_is_opening(tag)) + ti = proto_tree_add_text(tree, tvb, offset, tag_len, + "%s: %u", match_strval( + tag & 0x07, BACnetTagNames), + *tag_no); + else if (tag_is_context_specific(tag)) { + ti = proto_tree_add_text(tree, tvb, offset, tag_len, + "Context Tag: %u, Length/Value/Type: %u", + *tag_no, *lvt); + } else + ti = proto_tree_add_text(tree, tvb, offset, tag_len, + "Application Tag: %s, Length/Value/Type: %u", + val_to_str(*tag_no, + BACnetApplicationTagNumber, + ASHRAE_Reserved_Fmt), + *lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + /* details if needed */ + proto_tree_add_item(subtree, hf_BACnetTagClass, tvb, offset, 1, FALSE); + if (tag_is_extended_tag_number(tag)) { + proto_tree_add_uint_format(subtree, + hf_BACnetContextTagNumber, + tvb, offset, 1, tag, + "Extended Tag Number"); + proto_tree_add_item(subtree, + hf_BACnetExtendedTagNumber, + tvb, offset + 1, 1, FALSE); + } else { + if (tag_is_context_specific(tag)) + proto_tree_add_item(subtree, + hf_BACnetContextTagNumber, + tvb, offset, 1, FALSE); + else + proto_tree_add_item(subtree, + hf_BACnetApplicationTagNumber, + tvb, offset, 1, FALSE); + } + if (tag_is_closing(tag) || tag_is_opening(tag)) + proto_tree_add_item(subtree, + hf_BACnetNamedTag, + tvb, offset, 1, FALSE); + else if (tag_is_extended_value(tag)) { + proto_tree_add_item(subtree, + hf_BACnetNamedTag, + tvb, offset, 1, FALSE); + proto_tree_add_uint(subtree, hf_bacapp_tag_lvt, + tvb, lvt_offset, lvt_len, *lvt); + } else + proto_tree_add_uint(subtree, hf_bacapp_tag_lvt, + tvb, lvt_offset, lvt_len, *lvt); + } + + return tag_len; +} + +static guint +fTagHeader (tvbuff_t *tvb, guint offset, guint8 *tag_no, guint8* tag_info, + guint32 *lvt) +{ + return fTagHeaderTree (tvb, NULL, offset, tag_no, tag_info, lvt); +} + +static guint +fNullTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_item *ti; + proto_tree *subtree; + + ti = proto_tree_add_text(tree, tvb, offset, 1, "%sNULL", label); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset + 1; +} + +static guint +fBooleanTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt = 0; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + guint bool_len = 1; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_info && lvt == 1) + { + lvt = tvb_get_guint8(tvb, offset+1); + ++bool_len; + } + + ti = proto_tree_add_text(tree, tvb, offset, bool_len, + "%s%s", label, lvt == 0 ? "FALSE" : "TRUE"); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset + bool_len; +} + +static guint +fUnsignedTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint64 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + /* only support up to an 8 byte (64-bit) integer */ + if (fUnsigned64 (tvb, offset + tag_len, lvt, &val)) + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s(Unsigned) %" PRIu64, label, val); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s - %u octets (Unsigned)", label, lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +/* set split_val to zero when not needed */ +static guint +fEnumeratedTagSplit (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label, + const value_string *vs, guint32 split_val) +{ + guint32 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + /* only support up to a 4 byte (32-bit) enumeration */ + if (fUnsigned32 (tvb, offset+tag_len, lvt, &val)) { + if (vs) + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s %s", label, val_to_split_str(val, split_val, vs, + ASHRAE_Reserved_Fmt,Vendor_Proprietary_Fmt)); + else + ti =proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s %u", label, val); + } else { + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s - %u octets (enumeration)", label, lvt); + } + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fEnumeratedTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label, + const value_string *vs) +{ + return fEnumeratedTagSplit (tvb, tree, offset, label, vs, 0); +} + +static guint +fSignedTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + gint64 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (fSigned64 (tvb, offset + tag_len, lvt, &val)) + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s(Signed) %" PRId64, label, val); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s - %u octets (Signed)", label, lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fRealTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + gfloat f_val = 0.0; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, offset, &tag_no, &tag_info, &lvt); + f_val = tvb_get_ntohieee_float(tvb, offset+tag_len); + ti = proto_tree_add_text(tree, tvb, offset, 4+tag_len, + "%s%f (Real)", label, f_val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+4; +} + +static guint +fDoubleTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + gdouble d_val = 0.0; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, offset, &tag_no, &tag_info, &lvt); + d_val = tvb_get_ntohieee_double(tvb, offset+tag_len); + ti = proto_tree_add_text(tree, tvb, offset, 8+tag_len, + "%s%lf (Double)", label, d_val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+8; +} + +static guint +fProcessId (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32 (tvb, offset+tag_len, lvt, &val)) + ti = proto_tree_add_uint(tree, hf_bacapp_tag_ProcessId, + tvb, offset, lvt+tag_len, val); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "Process Identifier - %u octets (Signed)", lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + offset += tag_len + lvt; + + return offset; +} + +static guint +fTimeSpan (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32 (tvb, offset+tag_len, lvt, &val)) + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s (hh.mm.ss): %d.%02d.%02d%s", + label, + (val / 3600), ((val % 3600) / 60), (val % 60), + val == 0 ? " (indefinite)" : ""); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s - %u octets (Signed)", label, lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fWeekNDay (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint32 month, weekOfMonth, dayOfWeek; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + month = tvb_get_guint8(tvb, offset+tag_len); + weekOfMonth = tvb_get_guint8(tvb, offset+tag_len+1); + dayOfWeek = tvb_get_guint8(tvb, offset+tag_len+2); + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, "%s %s, %s", + val_to_str(month, months, "month (%d) not found"), + val_to_str(weekOfMonth, weekofmonth, "week of month (%d) not found"), + val_to_str(dayOfWeek, days, "day of week (%d) not found")); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fDate (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint32 year, month, day, weekday; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + year = tvb_get_guint8(tvb, offset+tag_len); + month = tvb_get_guint8(tvb, offset+tag_len+1); + day = tvb_get_guint8(tvb, offset+tag_len+2); + weekday = tvb_get_guint8(tvb, offset+tag_len+3); + if ((year == 255) && (day == 255) && (month == 255) && (weekday == 255)) + { + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%sany", label); + } + else if (year != 255) + { + year += 1900; + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s%s %d, %d, (Day of Week = %s)", + label, val_to_str(month, + months, + "month (%d) not found"), + day, year, val_to_str(weekday, + days, + "(%d) not found")); + } + else + { + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s%s %d, any year, (Day of Week = %s)", + label, val_to_str(month, months, "month (%d) not found"), + day, val_to_str(weekday, days, "(%d) not found")); + } + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fTime (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint32 hour, minute, second, msec, lvt; + guint8 tag_no, tag_info; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + hour = tvb_get_guint8(tvb, offset+tag_len); + minute = tvb_get_guint8(tvb, offset+tag_len+1); + second = tvb_get_guint8(tvb, offset+tag_len+2); + msec = tvb_get_guint8(tvb, offset+tag_len+3); + if ((hour == 255) && (minute == 255) && (second == 255) && (msec == 255)) + ti = proto_tree_add_text(tree, tvb, offset, + lvt+tag_len, "%sany", label); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s%d:%02d:%02d.%d %s = %02d:%02d:%02d.%d", + label, + hour > 12 ? hour - 12 : hour, + minute, second, msec, + hour > 12 ? "P.M." : "A.M.", + hour, minute, second, msec); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fDateTime (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + proto_tree *subtree = tree; + proto_item *tt; + + if (label != NULL) { + tt = proto_tree_add_text (subtree, tvb, offset, 1, "%s", label); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + } + offset = fDate (tvb,subtree,offset,"Date: "); + return fTime (tvb,subtree,offset,"Time: "); +} + +static guint +fTimeValue (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* closing Tag, but not for me */ + return offset; + } + offset = fTime (tvb,tree,offset,"Time: "); + offset = fApplicationTypes(tvb, tree, offset, "Value: "); + } + return offset; +} + +static guint +fCalendaryEntry (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + + switch (fTagNo(tvb, offset)) { + case 0: /* Date */ + offset = fDate (tvb, tree, offset, "Date: "); + break; + case 1: /* dateRange */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateRange (tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* BACnetWeekNDay */ + offset = fWeekNDay (tvb, tree, offset); + break; + default: + return offset; + break; + } + + return offset; +} + +static guint +fTimeStamp (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + if (tvb_length_remaining(tvb, offset) > 0) { /* don't loop, it's a CHOICE */ + switch (fTagNo(tvb, offset)) { + case 0: /* time */ + offset = fTime (tvb, tree, offset, "timestamp: "); + break; + case 1: /* sequenceNumber */ + offset = fUnsignedTag (tvb, tree, offset, "sequence Number: "); + break; + case 2: /* dateTime */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime (tvb, tree, offset, "timestamp: "); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + break; + } + } + return offset; +} + +#if 0 +static guint +fSetpointReference (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* setpointReference */ + offset = fBACnetObjectPropertyReference (tvb,tree,offset); + break; + default: + return offset; + break; + } + } + return offset; +} +#endif + +#if 0 +static guint +fClientCOV (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) > 0) { + offset = fApplicationTypes(tvb,tree,offset, "increment: "); + } + return offset; +} + +static guint +fDestination (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) > 0) { + offset = fApplicationTypesEnumerated(tvb,tree,offset, + "valid Days: ", days); + offset = fTime (tvb,tree,offset,"from time: "); + offset = fTime (tvb,tree,offset,"to time: "); + offset = fRecipient (tvb,tree,offset); + offset = fProcessId (tvb,tree,offset); + offset = fApplicationTypes (tvb,tree,offset, + "issue confirmed notifications: "); + offset = fApplicationTypesEnumerated (tvb,tree,offset, + "transitions: ", BACnetEventTransitionBits); + } + return offset; +} + +#endif + +static guint +fOctetString (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label, guint32 lvt) +{ + gchar *tmp; + guint start = offset; + guint8 tag_no, tag_info; + proto_tree* subtree = tree; + proto_item* ti = 0; + + offset += fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + if (lvt > 0) + { + tmp = tvb_bytes_to_str(tvb, offset, lvt); + ti = proto_tree_add_text(tree, tvb, offset, lvt, "%s %s", label, tmp); + offset += lvt; + } + + if (ti) + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + + fTagHeaderTree(tvb, subtree, start, &tag_no, &tag_info, &lvt); + + return offset; +} + +static guint +fAddress (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint offs; + + offset = fUnsignedTag (tvb, tree, offset, "network-number"); + offs = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (lvt == 0) { + proto_tree_add_text(tree, tvb, offset, offs, "mac-address: broadcast"); + offset += offs; + } else + offset = fOctetString (tvb, tree, offset, "mac-address: ", lvt); + return offset; +} + +static guint +fSessionKey (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fOctetString (tvb,tree,offset,"session key: ", 8); + return fAddress (tvb,tree,offset); +} + +static guint +fObjectIdentifier (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_length; + proto_item *ti; + proto_tree *subtree; + guint32 object_id; + + tag_length = fTagHeader(tvb, offset, &tag_no, &tag_info, &lvt); + object_id = tvb_get_ntohl(tvb,offset+tag_length); + object_type = object_id_type(object_id); + ti = proto_tree_add_text(tree, tvb, offset, tag_length + 4, + "ObjectIdentifier: %s, %u", + val_to_split_str(object_type, + 128, + BACnetObjectType, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), + object_id_instance(object_id)); + /* here are the details of how we arrived at the above text */ + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset += tag_length; + proto_tree_add_item(subtree, hf_bacapp_objectType, tvb, offset, 4, FALSE); + proto_tree_add_item(subtree, hf_bacapp_instanceNumber, tvb, offset, 4, FALSE); + offset += 4; + + return offset; +} + +static guint +fRecipient (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + + fTagHeader(tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_no < 2) { + if (tag_no == 0) { /* device */ + offset = fObjectIdentifier (tvb, tree, offset); + } + else { /* address */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAddress (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + } + } + return offset; +} + +static guint +fRecipientProcess (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* recipient */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fRecipient (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* processId */ + offset = fProcessId (tvb, tree, offset); + lastoffset = offset; + break; + default: + break; + } + } + return offset; +} + +static guint +fAddressBinding (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fObjectIdentifier (tvb, tree, offset); + return fAddress (tvb, tree, offset); +} + +static guint +fActionCommand (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + continue; + } + switch (tag_no) { + + case 0: /* deviceIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 2: /* propertyIdentifier */ + offset = fPropertyIdentifier (tvb, subtree, offset); + break; + case 3: /* propertyArrayIndex */ + offset = fUnsignedTag (tvb,subtree,offset,"Property Array Index: "); + break; + case 4: /* propertyValue */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "propertyValue"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + case 5: /* priority */ + offset = fUnsignedTag (tvb,subtree,offset,"Priority: "); + break; + case 6: /* postDelay */ + offset = fUnsignedTag (tvb,subtree,offset,"Post Delay: "); + break; + case 7: /* quitOnFailure */ + offset = fBooleanTag(tvb, subtree, offset, + "Quit On Failure: "); + break; + case 8: /* writeSuccessful */ + offset = fBooleanTag(tvb, subtree, offset, + "Write Successful: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fActionList (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fActionCommand (tvb,tree,offset); +} + +static guint +fPropertyIdentifier (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + propertyIdentifier = 0; /* global Variable */ + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32 (tvb, offset+tag_len, lvt, (guint32 *)&propertyIdentifier)) + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "property Identifier: %s", + val_to_split_str(propertyIdentifier, 512, + BACnetPropertyIdentifier, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "Property Identifier - %u octets", lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fCharacterString (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info, character_set; + guint32 lvt, l; + size_t inbytesleft, outbytesleft = 512; + guint offs, extra = 1; + guint8 *str_val; + guint8 bf_arr[512], *out = &bf_arr[0]; + proto_item *ti; + proto_tree *subtree; + guint start = offset; + + if (tvb_length_remaining(tvb, offset) > 0) { + + offs = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + character_set = tvb_get_guint8(tvb, offset+offs); + /* Account for code page if DBCS */ + if (character_set == 1) + { + extra = 3; + } + offset += (offs+extra); + lvt -= (extra); + + do { + l = inbytesleft = min(lvt, 255); + /* + * XXX - are we guaranteed that these encoding + * names correspond, on *all* platforms with + * iconv(), to the encodings we want? + * If not (and perhaps even if so), we should + * perhaps have our own iconv() implementation, + * with a different name, so that we control the + * encodings it supports and the names of those + * encodings. + * + * We should also handle that in the general + * string handling code, rather than making it + * specific to the BACAPP dissector, as many + * other dissectors need to handle various + * character encodings. + */ + str_val = tvb_get_ephemeral_string(tvb, offset, l); + /** this decoding may be not correct for multi-byte characters, Lka */ + switch (character_set) { + case 0x00: /* ANSI_X3.4 */ + fConvertXXXtoUTF8(str_val, &inbytesleft, out, &outbytesleft, "ANSI_X3.4"); + break; + case 1: /* IBM/MICROSOFT DBCS */ + out = str_val; + break; + case 2: /* JIS C 6226 */ + out = str_val; + break; + case 3: /* UCS-4 */ + fConvertXXXtoUTF8(str_val, &inbytesleft, out, &outbytesleft, "UCS-4BE"); + break; + case 4: /* UCS-2 */ + fConvertXXXtoUTF8(str_val, &inbytesleft, out, &outbytesleft, "UCS-2BE"); + break; + case 5: /* ISO8859-1 */ + fConvertXXXtoUTF8(str_val, &inbytesleft, out, &outbytesleft, "ISO8859-1"); + break; + default: + out = str_val; + break; + } + ti = proto_tree_add_text(tree, tvb, offset, l, "%s'%s'", label, out); + lvt-=l; + offset+=l; + } while (lvt > 0); + + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + + fTagHeaderTree (tvb, subtree, start, &tag_no, &tag_info, &lvt); + proto_tree_add_item(subtree, hf_BACnetCharacterSet, tvb, start+offs, 1, FALSE); + if (character_set == 1) + { + proto_tree_add_text(subtree, tvb, start+offs+1, 2, "Code Page: %d", tvb_get_ntohs(tvb, start+offs+1)); + } + } + return offset; +} + +static guint +fBitStringTagVS (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label, + const value_string *src) +{ + guint8 tag_no, tag_info, tmp; + gint j, unused, skip; + guint offs; + guint32 lvt, i, numberOfBytes; + guint8 bf_arr[256]; + + offs = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + numberOfBytes = lvt-1; /* Ignore byte for unused bit count */ + offset+=offs; + unused = tvb_get_guint8(tvb, offset); /* get the unused Bits */ + skip = 0; + for (i = 0; i < numberOfBytes; i++) { + tmp = tvb_get_guint8(tvb, (offset)+i+1); + if (i == numberOfBytes-1) { skip = unused; } + for (j = 0; j < 8-skip; j++) { + if (src != NULL) { + if (tmp & (1 << (7 - j))) + proto_tree_add_text(tree, tvb, + offset+i+1, 1, + "%s%s = TRUE", + label, + val_to_str((guint) (i*8 +j), + src, + ASHRAE_Reserved_Fmt)); + else + proto_tree_add_text(tree, tvb, + offset+i+1, 1, + "%s%s = FALSE", + label, + val_to_str((guint) (i*8 +j), + src, + ASHRAE_Reserved_Fmt)); + + } else { + bf_arr[min(255,(i*8)+j)] = tmp & (1 << (7 - j)) ? '1' : '0'; + } + } + } + + if (src == NULL) + { + bf_arr[min(255,numberOfBytes*8-unused)] = 0; + proto_tree_add_text(tree, tvb, offset, lvt, "%sB'%s'", label, bf_arr); + } + + offset+=lvt; + + return offset; +} + +static guint +fBitStringTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + return fBitStringTagVS (tvb, tree, offset, label, NULL); +} + +/* handles generic application types, as well as enumerated and enumerations + with reserved and proprietarty ranges (split) */ +static guint +fApplicationTypesEnumeratedSplit (tvbuff_t *tvb, proto_tree *tree, guint offset, + const gchar *label, const value_string *src, guint32 split_val) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + + if (tvb_length_remaining(tvb, offset) > 0) { + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (!tag_is_context_specific(tag_info)) + { + switch (tag_no) { + case 0: /** NULL 20.2.2 */ + offset = fNullTag(tvb, tree, offset, label); + break; + case 1: /** BOOLEAN 20.2.3 */ + offset = fBooleanTag(tvb, tree, offset, label); + break; + case 2: /** Unsigned Integer 20.2.4 */ + offset = fUnsignedTag(tvb, tree, offset, label); + break; + case 3: /** Signed Integer 20.2.5 */ + offset = fSignedTag(tvb, tree, offset, label); + break; + case 4: /** Real 20.2.6 */ + offset = fRealTag(tvb, tree, offset, label); + break; + case 5: /** Double 20.2.7 */ + offset = fDoubleTag(tvb, tree, offset, label); + break; + case 6: /** Octet String 20.2.8 */ + offset = fOctetString (tvb, tree, offset, label, lvt); + break; + case 7: /** Character String 20.2.9 */ + offset = fCharacterString (tvb,tree,offset,label); + break; + case 8: /** Bit String 20.2.10 */ + offset = fBitStringTagVS (tvb, tree, offset, label, src); + break; + case 9: /** Enumerated 20.2.11 */ + offset = fEnumeratedTagSplit (tvb, tree, offset, label, src, split_val); + break; + case 10: /** Date 20.2.12 */ + offset = fDate (tvb, tree, offset, label); + break; + case 11: /** Time 20.2.13 */ + offset = fTime (tvb, tree, offset, label); + break; + case 12: /** BACnetObjectIdentifier 20.2.14 */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 13: /* reserved for ASHRAE */ + case 14: + case 15: + proto_tree_add_text(tree, tvb, offset, lvt+tag_len, "%s'reserved for ASHRAE'", label); + offset+=lvt+tag_len; + break; + default: + break; + } + + } + } + return offset; +} + +static guint +fApplicationTypesEnumerated (tvbuff_t *tvb, proto_tree *tree, guint offset, + const gchar *label, const value_string *vs) +{ + return fApplicationTypesEnumeratedSplit(tvb, tree, offset, label, vs, 0); +} + +static guint +fApplicationTypes (tvbuff_t *tvb, proto_tree *tree, guint offset, + const gchar *label) +{ + return fApplicationTypesEnumeratedSplit(tvb, tree, offset, label, NULL, 0); +} + +static guint +fContextTaggedValue(tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + gint tvb_len; + + (void)label; + tag_len = fTagHeader(tvb, offset, &tag_no, &tag_info, &lvt); + /* cap the the suggested length in case of bad data */ + tvb_len = tvb_length_remaining(tvb, offset+tag_len); + if ((tvb_len >= 0) && ((guint32)tvb_len < lvt)) + { + lvt = tvb_len; + } + ti = proto_tree_add_text(tree, tvb, offset+tag_len, lvt, + "Context Value (as %u DATA octets)", lvt); + + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset + tag_len + lvt; +} + +static guint +fAbstractSyntaxNType (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0, depth = 0; + char ar[256]; + + if (propertyIdentifier >= 0) + { + g_snprintf (ar, sizeof(ar), "%s: ", + val_to_split_str(propertyIdentifier, 512, + BACnetPropertyIdentifier, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + } + else + { + g_snprintf (ar, sizeof(ar), "Abstract Type: "); + } + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* closing tag, but not for me */ + if (depth <= 0) return offset; + } + + /* Application Tags */ + switch (propertyIdentifier) { + case 2: /* BACnetActionList */ + offset = fActionList (tvb,tree,offset); + break; + case 30: /* BACnetAddressBinding */ + offset = fAddressBinding (tvb,tree,offset); + break; + case 55: /* list-of-session-keys */ + fSessionKey (tvb, tree, offset); + break; + case 79: /* object-type */ + case 96: /* protocol-object-types-supported */ + offset = fApplicationTypesEnumeratedSplit (tvb, tree, offset, ar, + BACnetObjectType, 128); + break; + case 97: /* Protocol-Services-Supported */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, ar, + BACnetServicesSupported); + break; + case 107: /* segmentation-supported */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, ar, + BACnetSegmentation); + break; + case 111: /* Status-Flags */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, ar, + BACnetStatusFlags); + break; + case 112: /* System-Status */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, ar, + BACnetDeviceStatus); + break; + case 117: /* units */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, ar, + BACnetEngineeringUnits); + break; + case 87: /* priority-array */ + offset = fPriorityArray (tvb, tree, offset); + break; + case 38: /* exception-schedule */ + if (object_type < 128) + { + offset = fSpecialEvent (tvb,tree,offset); + } + break; + case 123: /* weekly-schedule */ + if (object_type < 128) + { + offset = fWeeklySchedule (tvb,tree,offset); + } + break; + case 159: /* member-of */ + case 165: /* zone-members */ + fDeviceObjectReference (tvb, tree, offset); + break; + default: + if (tag_info) + { + if (tag_is_opening(tag_info)) + { + ++depth; + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + } + else if (tag_is_closing(tag_info)) + { + --depth; + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + } + else + { + offset = fContextTaggedValue(tvb, tree, offset, ar); + } + } + else + { + offset = fApplicationTypes (tvb, tree, offset, ar); + } + break; + } + } + return offset; + +} + +static guint +fPropertyValue (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 tagoffset) +{ + guint lastoffset = offset; + proto_item *tt; + proto_tree *subtree; + guint8 tag_no, tag_info; + guint32 lvt; + + offset = fPropertyReference(tvb, tree, offset, tagoffset, 0); + if (offset > lastoffset) + { + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_no == tagoffset+2) { /* Value - might not be present in ReadAccessResult */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(tree, tvb, offset, 1, "propertyValue"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + } + } + return offset; +} + +static guint +fBACnetPropertyValue (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fPropertyValue(tvb, tree, offset, 0); + if (offset > lastoffset) + { + /* detect optional priority + by looking to see if the next tag is context tag number 3 */ + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_context_specific(tag_info) && (tag_no == 3)) + offset = fUnsignedTag (tvb,tree,offset,"Priority: "); + } + } + return offset; +} + +static guint +fSubscribeCOVPropertyRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* ProcessId */ + offset = fUnsignedTag (tvb, tree, offset, "subscriber Process Id: "); + break; + case 1: /* monitored ObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 2: /* issueConfirmedNotifications */ + offset = fBooleanTag (tvb, tree, offset, "issue Confirmed Notifications: "); + break; + case 3: /* life time */ + offset = fTimeSpan (tvb,tree,offset,"life time"); + break; + case 4: /* monitoredPropertyIdentifier */ + offset = fBACnetPropertyReference (tvb, tree, offset, 0); + break; + case 5: /* covIncrement */ + offset = fRealTag (tvb, tree, offset, "COV Increment: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fSubscribeCOVRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fSubscribeCOVPropertyRequest(tvb, tree, offset); +} + +static guint +fWhoHas (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* deviceInstanceLowLimit */ + offset = fUnsignedTag (tvb, tree, offset, "device Instance Low Limit: "); + break; + case 1: /* deviceInstanceHighLimit */ + offset = fUnsignedTag (tvb, tree, offset, "device Instance High Limit: "); + break; + case 2: /* BACnetObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 3: /* messageText */ + offset = fCharacterString (tvb,tree,offset, "Object Name: "); + break; + default: + return offset; + } + } + return offset; +} + + +static guint +fDailySchedule (tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_opening(tag_info) && tag_no == 0) + { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); /* opening context tag 0 */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + /* should be closing context tag 0 */ + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + return offset; + } + + offset = fTimeValue (tvb, subtree, offset); + } + } + else if (tag_no == 0 && lvt == 0) + { + /* not sure null (empty array element) is legal */ + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + return offset; +} + +static guint +fWeeklySchedule (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint i=1; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; /* outer encoding will print out closing tag */ + } + tt = proto_tree_add_text(tree, tvb, offset, 0, val_to_str(i++, days, "day of week (%d) not found")); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset = fDailySchedule (tvb,subtree,offset); + } + return offset; +} + + +static guint +fUTCTimeSynchronizationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) <= 0) + return offset; + + return fDateTime (tvb, tree, offset, "UTC-Time: "); +} + +static guint +fTimeSynchronizationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) <= 0) + return offset; + + return fDateTime (tvb, tree, offset, NULL); +} + +static guint +fDateRange (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) <= 0) + return offset; + offset = fDate (tvb,tree,offset,"Start Date: "); + return fDate (tvb, tree, offset, "End Date: "); +} + +static guint +fConfirmedTextMessageRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + + case 0: /* textMessageSourceDevice */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 1: /* messageClass */ + switch (fTagNo(tvb, offset)) { + case 0: /* numeric */ + offset = fUnsignedTag (tvb, tree, offset, "message Class: "); + break; + case 1: /* character */ + offset = fCharacterString (tvb, tree, offset, "message Class: "); + break; + } + break; + case 2: /* messagePriority */ + offset = fEnumeratedTag (tvb, tree, offset, "message Priority: ", + BACnetMessagePriority); + break; + case 3: /* message */ + offset = fCharacterString (tvb, tree, offset, "message: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fUnconfirmedTextMessageRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fConfirmedTextMessageRequest(tvb, tree, offset); +} + +static guint +fConfirmedPrivateTransferRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + /* exit loop if nothing happens inside */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + if (tag_no == 2) /* Make sure it's the expected tag */ + { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + continue; + } + else + { + break; /* End loop if incorrect closing tag */ + } + } + switch (tag_no) { + + case 0: /* vendorID */ + offset = fUnsignedTag (tvb, subtree, offset, "vendor ID: "); + break; + case 1: /* serviceNumber */ + offset = fUnsignedTag (tvb, subtree, offset, "service Number: "); + break; + case 2: /*serviceParameters */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "service Parameters"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + propertyIdentifier = -1; + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + return offset; + } + } + return offset; +} + +static guint +fUnconfirmedPrivateTransferRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fConfirmedPrivateTransferRequest(tvb, tree, offset); +} + +static guint +fConfirmedPrivateTransferAck(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fConfirmedPrivateTransferRequest(tvb, tree, offset); +} + +static guint +fLifeSafetyOperationRequest(tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + if (label != NULL) { + tt = proto_tree_add_text (subtree, tvb, offset, 1, "%s", label); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + } + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: /* subscriberProcessId */ + offset = fUnsignedTag (tvb, subtree, offset, "requesting Process Id: "); + break; + case 1: /* requestingSource */ + offset = fCharacterString (tvb, tree, offset, "requesting Source: "); + break; + case 2: /* request */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "request: ", BACnetLifeSafetyOperation, 64); + break; + case 3: /* objectId */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint fBACnetPropertyStates(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + switch (fTagNo(tvb, offset)) + { + case 0: + offset = fBooleanTag (tvb, tree, offset, "boolean-value: "); + break; + case 1: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "binary-value: ", BACnetBinaryPV, 2); + break; + case 2: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "event-type: ", BACnetEventType, 12); + break; + case 3: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "polarity: ", BACnetPolarity, 2); + break; + case 4: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "program-change: ", BACnetProgramRequest, 5); + break; + case 5: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "program-state: ", BACnetProgramState, 5); + break; + case 6: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "reason-for-halt: ", BACnetProgramError, 5); + break; + case 7: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "reliability: ", BACnetReliability, 10); + break; + case 8: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "state: ", BACnetEventState, 64); + break; + case 9: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "system-status: ", BACnetDeviceStatus, 64); + break; + case 10: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "units: ", BACnetEngineeringUnits, 2); + break; + case 11: + offset = fUnsignedTag(tvb, tree, offset, "unsigned-value: "); + break; + case 12: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "life-safety-mode: ", BACnetLifeSafetyMode, 64); + break; + case 13: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "life-safety-state: ", BACnetLifeSafetyState, 64); + break; + default: + break; + } + return offset; +} + +static guint +fNotificationParameters (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = offset; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + tt = proto_tree_add_text(subtree, tvb, offset, 0, "notification parameters"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + /* Opeing tag for parameter choice */ + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: /* change-of-bitstring */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fBitStringTag (tvb, subtree, offset, + "referenced-bitstring: "); + break; + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + default: + return offset; + break; + } + } + break; + case 1: /* change-of-state */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyStates(tvb, subtree, offset); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + lastoffset = offset; + break; + default: + break; + } + } + break; + case 2: /* change-of-value */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: + offset = fBitStringTag (tvb, subtree, offset, + "changed-bits: "); + break; + case 1: + offset = fRealTag (tvb, subtree, offset, + "changed-value: "); + break; + default: + break; + } + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + default: + break; + } + } + break; + case 3: /* command-failure */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* "command-value: " */ + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + case 2: /* "feedback-value: " */ + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + default: + break; + } + } + break; + case 4: /* floating-limit */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fRealTag (tvb, subtree, offset, "reference-value: "); + break; + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fRealTag (tvb, subtree, offset, "setpoint-value: "); + break; + case 3: + offset = fRealTag (tvb, subtree, offset, "error-limit: "); + default: + break; + } + } + break; + case 5: /* out-of-range */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fRealTag (tvb, subtree, offset, "exceeding-value: "); + break; + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fRealTag (tvb, subtree, offset, "deadband: "); + break; + case 3: + offset = fRealTag (tvb, subtree, offset, "exceeded-limit: "); + default: + break; + } + } + break; + case 6: + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset =fBACnetPropertyValue (tvb,subtree,offset); + } + break; + case 7: /* buffer-ready */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fObjectIdentifier (tvb, subtree, offset); /* buffer-device */ + break; + case 1: + offset = fObjectIdentifier (tvb, subtree, offset); /* buffer-object */ + break; + case 2: + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime (tvb, subtree, offset, "previous-notification: "); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 3: + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime (tvb, subtree, offset, "current-notification: "); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + default: + break; + } + } + break; + case 8: /* change-of-life-safety */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fEnumeratedTagSplit (tvb, subtree, offset, + "new-state: ", BACnetLifeSafetyState, 256); + break; + case 1: + offset = fEnumeratedTagSplit (tvb, subtree, offset, + "new-mode: ", BACnetLifeSafetyState, 256); + break; + case 2: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + case 3: + offset = fEnumeratedTagSplit (tvb, subtree, offset, + "operation-expected: ", BACnetLifeSafetyOperation, 64); + default: + return offset; + break; + } + } + break; + default: + break; + } + /* Closing tag for parameter choice */ + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset; +} + +#if 0 +static guint +fEventParameter (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* change-of-bitstring */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: + offset = fBitStringTag (tvb, tree, offset, "bitmask: "); + break; + case 2: /* SEQUENCE OF BIT STRING */ + offset = fBitStringTagVS (tvb, tree, offset, + "bitstring value: ", BACnetEventTransitionBits); + break; + default: + return offset; + } + } + break; + case 1: /* change-of-state */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: /* SEQUENCE OF BACnetPropertyStates */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "value: ", BACnetPropertyStates, 64); + break; + default: + return offset; + } + } + break; + case 2: /* change-of-value */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: /* don't loop it, it's a CHOICE */ + switch (fTagNo(tvb, offset)) { + case 0: + offset = fBitStringTag (tvb, tree, offset, "bitmask: "); + break; + case 1: + offset = fRealTag (tvb, tree, offset, + "referenced Property Increment: "); + break; + default: + return offset; + } + default: + return offset; + } + } + break; + case 3: /* command-failure */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: + offset = fDeviceObjectPropertyReference (tvb,tree,offset); + default: + return offset; + } + } + break; + case 4: /* floating-limit */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: + offset = fDeviceObjectPropertyReference (tvb,tree,offset); + break; + case 2: + offset = fRealTag (tvb, tree, offset, "low diff limit: "); + break; + case 3: + offset = fRealTag (tvb, tree, offset, "high diff limit: "); + break; + case 4: + offset = fRealTag (tvb, tree, offset, "deadband: "); + break; + default: + return offset; + } + } + break; + case 5: /* out-of-range */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: + offset = fRealTag (tvb, tree, offset, "low limit: "); + break; + case 2: + offset = fRealTag (tvb, tree, offset, "high limit: "); + break; + case 3: + offset = fRealTag (tvb, tree, offset, "deadband: "); + break; + default: + return offset; + } + } + break; + case 6: + offset = fBACnetPropertyValue (tvb,tree,offset); + break; + case 7: /* buffer-ready */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fUnsignedTag (tvb,tree,offset,"notification threshold"); + break; + case 1: + offset = fUnsignedTag (tvb,tree,offset, + "previous notification count: "); + break; + default: + return offset; + } + } + break; + case 8: /* change-of-life-safety */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "life safety alarm value: ", BACnetLifeSafetyState, 256); + break; + case 2: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "alarm value: ", BACnetLifeSafetyState, 256); + break; + case 3: + offset = fDeviceObjectPropertyReference (tvb, tree, offset); + break; + default: + return offset; + } + } + break; + default: + return offset; + } + } + return offset; +} + +static guint +fLogRecord (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* timestamp */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime (tvb,tree,offset,NULL); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* logDatum: don't loop, it's a CHOICE */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: /* logStatus */ + offset = fEnumeratedTag (tvb, tree, offset, + "log status: ", BACnetLogStatus); + break; + case 1: + offset = fBooleanTag (tvb, tree, offset, "boolean-value: "); + break; + case 2: + offset = fRealTag (tvb, tree, offset, "real value: "); + break; + case 3: + offset = fUnsignedTag (tvb, tree, offset, "enum value: "); + break; + case 4: + offset = fUnsignedTag (tvb, tree, offset, "unsigned value: "); + break; + case 5: + offset = fSignedTag (tvb, tree, offset, "signed value: "); + break; + case 6: + offset = fBitStringTag (tvb, tree, offset, "bitstring value: "); + break; + case 7: + offset = fNullTag(tvb, tree, offset, "null value: "); + break; + case 8: + offset = fError (tvb,tree,offset); + break; + case 9: + offset = fRealTag (tvb, tree, offset, "time change: "); + break; + case 10: /* any Value */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: + offset = fEnumeratedTag (tvb, tree, offset, + "status Flags: ", BACnetStatusFlags); + break; + default: + return offset; + } + } + return offset; +} +#endif + +static guint +fConfirmedEventNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* ProcessId */ + offset = fProcessId (tvb,tree,offset); + break; + case 1: /* initiating ObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 2: /* event ObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 3: /* time stamp */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* notificationClass */ + offset = fUnsignedTag (tvb, tree, offset, "Notification Class: "); + break; + case 5: /* Priority */ + offset = fUnsignedTag (tvb, tree, offset, "Priority: "); + break; + case 6: /* EventType */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "Event Type: ", BACnetEventType, 64); + break; + case 7: /* messageText */ + offset = fCharacterString (tvb, tree, offset, "message Text: "); + break; + case 8: /* NotifyType */ + offset = fEnumeratedTag (tvb, tree, offset, + "Notify Type: ", BACnetNotifyType); + break; + case 9: /* ackRequired */ + offset = fBooleanTag (tvb, tree, offset, "ack Required: "); + break; + case 10: /* fromState */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "from State: ", BACnetEventState, 64); + break; + case 11: /* toState */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "to State: ", BACnetEventState, 64); + break; + case 12: /* NotificationParameters */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fNotificationParameters (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + return offset; +} + +static guint +fUnconfirmedEventNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fConfirmedEventNotificationRequest (tvb, tree, offset); +} + +static guint +fConfirmedCOVNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + lastoffset = offset; + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* ProcessId */ + offset = fProcessId (tvb,tree,offset); + break; + case 1: /* initiating DeviceId */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 2: /* monitored ObjectId */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 3: /* time remaining */ + offset = fTimeSpan (tvb, tree, offset, "Time remaining"); + break; + case 4: /* List of Values */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "list of Values"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyValue (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fUnconfirmedCOVNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fConfirmedCOVNotificationRequest (tvb, tree, offset); +} + +static guint +fAcknowledgeAlarmRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* acknowledgingProcessId */ + offset = fUnsignedTag (tvb, tree, offset, "acknowledging Process Id: "); + break; + case 1: /* eventObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 2: /* eventStateAcknowledged */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "event State Acknowledged: ", BACnetEventState, 64); + break; + case 3: /* timeStamp */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp(tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* acknowledgementSource */ + offset = fCharacterString (tvb, tree, offset, "acknowledgement Source: "); + break; + case 5: /* timeOfAcknowledgement */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp(tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fGetAlarmSummaryAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes (tvb, tree, offset, "Object Identifier: "); + offset = fApplicationTypesEnumeratedSplit (tvb, tree, offset, + "alarm State: ", BACnetEventState, 64); + offset = fApplicationTypesEnumerated (tvb, tree, offset, + "acknowledged Transitions: ", BACnetEventTransitionBits); + } + return offset; +} + +static guint +fGetEnrollmentSummaryRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* acknowledgmentFilter */ + offset = fEnumeratedTag (tvb, tree, offset, + "acknowledgment Filter: ", BACnetAcknowledgementFilter); + break; + case 1: /* eventObjectId - OPTIONAL */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fRecipientProcess (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* eventStateFilter */ + offset = fEnumeratedTag (tvb, tree, offset, + "event State Filter: ", BACnetEventStateFilter); + break; + case 3: /* eventTypeFilter - OPTIONAL */ + offset = fEnumeratedTag (tvb, tree, offset, + "event Type Filter: ", BACnetEventType); + break; + case 4: /* priorityFilter */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fUnsignedTag (tvb, tree, offset, "min Priority: "); + offset = fUnsignedTag (tvb, tree, offset, "max Priority: "); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 5: /* notificationClassFilter - OPTIONAL */ + offset = fUnsignedTag (tvb, tree, offset, "notification Class Filter: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fGetEnrollmentSummaryAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes (tvb, tree, offset, "Object Identifier: "); + offset = fApplicationTypesEnumeratedSplit (tvb, tree, offset, + "event Type: ", BACnetEventType, 64); + offset = fApplicationTypesEnumerated (tvb, tree, offset, + "event State: ", BACnetEventState); + offset = fApplicationTypes (tvb, tree, offset, "Priority: "); + offset = fApplicationTypes (tvb, tree, offset, "Notification Class: "); + } + + return offset; +} + +static guint +fGetEventInformationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) > 0) { + if (fTagNo(tvb, offset) == 0) { + offset = fObjectIdentifier (tvb, tree, offset); + } + } + return offset; +} + +static guint +flistOfEventSummaries (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* ObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 1: /* eventState */ + offset = fEnumeratedTag (tvb, tree, offset, + "event State: ", BACnetEventStateFilter); + break; + case 2: /* acknowledgedTransitions */ + offset = fEnumeratedTag (tvb, tree, offset, + "acknowledged Transitions: ", BACnetEventTransitionBits); + break; + case 3: /* eventTimeStamps */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp (tvb, tree, offset); + offset = fTimeStamp (tvb, tree, offset); + offset = fTimeStamp (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* notifyType */ + offset = fEnumeratedTag (tvb, tree, offset, + "Notify Type: ", BACnetNotifyType); + break; + case 5: /* eventEnable */ + offset = fEnumeratedTag (tvb, tree, offset, + "event Enable: ", BACnetEventTransitionBits); + break; + case 6: /* eventPriorities */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fUnsignedTag (tvb, tree, offset, "event Priority: "); + offset = fUnsignedTag (tvb, tree, offset, "event Priority: "); + offset = fUnsignedTag (tvb, tree, offset, "event Priority: "); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + lastoffset = offset; + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fGetEventInformationACK (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* listOfEventSummaries */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = flistOfEventSummaries (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* moreEvents */ + offset = fBooleanTag (tvb, tree, offset, "more Events: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fAddListElementRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* ObjectId */ + offset = fBACnetObjectPropertyReference (tvb, subtree, offset); + break; + case 3: /* listOfElements */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "listOfElements"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fDeleteObjectRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fObjectIdentifier (tvb, tree, offset); +} + +static guint +fDeviceCommunicationControlRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* timeDuration */ + offset = fUnsignedTag (tvb,tree,offset,"time Duration: "); + break; + case 1: /* enable-disable */ + offset = fEnumeratedTag (tvb, tree, offset, "enable-disable: ", + BACnetEnableDisable); + break; + case 2: /* password - OPTIONAL */ + offset = fCharacterString (tvb, tree, offset, "Password: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fReinitializeDeviceRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* reinitializedStateOfDevice */ + offset = fEnumeratedTag (tvb, tree, offset, + "reinitialized State Of Device: ", + BACnetReinitializedStateOfDevice); + break; + case 1: /* password - OPTIONAL */ + offset = fCharacterString (tvb, tree, offset, "Password: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fVtOpenRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fApplicationTypesEnumerated (tvb, tree, offset, + "vtClass: ", BACnetVTClass); + return fApplicationTypes (tvb,tree,offset,"local VT Session ID: "); +} + +static guint +fVtOpenAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fApplicationTypes (tvb,tree,offset,"remote VT Session ID: "); +} + +static guint +fVtCloseRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset= fApplicationTypes (tvb,tree,offset,"remote VT Session ID: "); + } + return offset; +} + +static guint +fVtDataRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset= fApplicationTypes (tvb,tree,offset,"VT Session ID: "); + offset = fApplicationTypes (tvb, tree, offset, "VT New Data: "); + return fApplicationTypes (tvb,tree,offset,"VT Data Flag: ");; +} + +static guint +fVtDataAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* BOOLEAN */ + offset = fBooleanTag (tvb, tree, offset, "all New Data Accepted: "); + break; + case 1: /* Unsigned OPTIONAL */ + offset = fUnsignedTag (tvb, tree, offset, "accepted Octet Count: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fAuthenticateRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* Unsigned32 */ + offset = fUnsignedTag (tvb, tree, offset, "pseudo Random Number: "); + break; + case 1: /* expected Invoke ID Unsigned8 OPTIONAL */ + proto_tree_add_item(tree, hf_bacapp_invoke_id, tvb, offset++, 1, TRUE); + break; + case 2: /* Chararacter String OPTIONAL */ + offset = fCharacterString (tvb, tree, offset, "operator Name: "); + break; + case 3: /* Chararacter String OPTIONAL */ + offset = fCharacterString (tvb, tree, offset, "operator Password: "); + break; + case 4: /* Boolean OPTIONAL */ + offset = fBooleanTag (tvb, tree, offset, "start Encyphered Session: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fAuthenticateAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fApplicationTypes (tvb, tree, offset, "modified Random Number: "); +} + +static guint +fRequestKeyRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fObjectIdentifier (tvb, tree, offset); /* Requesting Device Identifier */ + offset = fAddress (tvb, tree, offset); + offset = fObjectIdentifier (tvb, tree, offset); /* Remote Device Identifier */ + return fAddress (tvb, tree, offset); +} + +static guint +fRemoveListElementRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + /* Same as AddListElement request after service choice */ + return fAddListElementRequest(tvb, tree, offset); +} + +static guint +fReadPropertyRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fBACnetObjectPropertyReference(tvb, tree, offset); +} + +static guint +fReadPropertyAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + continue; + } + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* propertyIdentifier */ + offset = fPropertyIdentifier (tvb, subtree, offset); + break; + case 2: /* propertyArrayIndex */ + offset = fSignedTag (tvb, subtree, offset, "property Array Index: "); + break; + case 3: /* propertyValue */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "propertyValue"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + break; + } + } + return offset; +} + +static guint +fWritePropertyRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* propertyIdentifier */ + offset = fPropertyIdentifier (tvb, subtree, offset); + break; + case 2: /* propertyArrayIndex */ + offset = fSignedTag (tvb, subtree, offset, "property Array Index: "); + break; + case 3: /* propertyValue */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "propertyValue"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + case 4: /* Priority (only used for write) */ + offset = fUnsignedTag (tvb, subtree, offset, "Priority: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fWriteAccessSpecification (tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + continue; + } + + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* listOfPropertyValues */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyValue (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + return offset; + } + } + return offset; +} + +static guint +fWritePropertyMultipleRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (offset >= tvb_reported_length(tvb)) + return offset; + + return fWriteAccessSpecification (tvb, tree, offset); +} + +static guint +fPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 tagoffset, guint8 list) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* closing Tag, but not for me */ + return offset; + } + switch (tag_no-tagoffset) { + case 0: /* PropertyIdentifier */ + offset = fPropertyIdentifier (tvb, tree, offset); + break; + case 1: /* propertyArrayIndex */ + offset = fUnsignedTag (tvb, tree, offset, "property Array Index: "); + if (list != 0) break; /* Continue decoding if this may be a list */ + default: + lastoffset = offset; /* Set loop end condition */ + break; + } + } + return offset; +} + +static guint +fBACnetPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 list) +{ + return fPropertyReference(tvb, tree, offset, 0, list); +} + +static guint +fBACnetObjectPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* ObjectIdentifier */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 1: /* PropertyIdentifier and propertyArrayIndex */ + offset = fPropertyReference (tvb, tree, offset, 1, 0); + default: + lastoffset = offset; /* Set loop end condition */ + break; + } + } + return offset; +} + +#if 0 +static guint +fObjectPropertyValue (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree* subtree = tree; + proto_item* tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + continue; + } + switch (tag_no) { + case 0: /* ObjectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* PropertyIdentifier */ + offset = fPropertyIdentifier (tvb, subtree, offset); + break; + case 2: /* propertyArrayIndex */ + offset = fUnsignedTag (tvb, subtree, offset, "property Array Index: "); + break; + case 3: /* Value */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "propertyValue"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + case 4: /* Priority */ + offset = fUnsignedTag (tvb, subtree, offset, "Priority: "); + break; + default: + break; + } + } + return offset; +} +#endif + +static guint +fDeviceObjectPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* ObjectIdentifier */ + offset = fBACnetObjectPropertyReference (tvb, tree, offset); + break; + case 3: /* deviceIdentifier - OPTIONAL */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fPriorityArray (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + char i, ar[256]; + + if (offset >= tvb_reported_length(tvb)) + return offset; + + for (i = 1; i <= 16; i++) { + g_snprintf (ar, sizeof(ar), "%s[%d]: ", + val_to_split_str(87 , 512, + BACnetPropertyIdentifier, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), + i); + /* DMR Should be fAbstractNSyntax, but that's where we came from! */ + offset = fApplicationTypes(tvb, tree, offset, ar); + } + return offset; +} + +static guint +fDeviceObjectReference (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* deviceIdentifier - OPTIONAL */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 1: /* ObjectIdentifier */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fSpecialEvent (tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + continue; + } + + switch (tag_no) { + case 0: /* calendaryEntry */ + if (tag_is_opening(tag_info)) + { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fCalendaryEntry (tvb, subtree, offset); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + break; + case 1: /* calendarReference */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 2: /* list of BACnetTimeValue */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeValue (tvb, subtree, offset); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + } + FAULT; + break; + case 3: /* eventPriority */ + offset = fUnsignedTag (tvb, subtree, offset, "event priority: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fSelectionCriteria (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* stop when we hit outer closing tag */ + continue; + } + + switch (fTagNo(tvb,offset)) { + case 0: /* propertyIdentifier */ + offset = fPropertyIdentifier (tvb, tree, offset); + break; + case 1: /* propertyArrayIndex */ + offset = fUnsignedTag (tvb, tree, offset, "property Array Index: "); + break; + case 2: /* relationSpecifier */ + offset = fEnumeratedTag (tvb, tree, offset, + "relation Specifier: ", BACnetRelationSpecifier); + break; + case 3: /* comparisonValue */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fObjectSelectionCriteria (tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* stop when we hit outer closing tag */ + continue; + } + + switch (tag_no) { + case 0: /* selectionLogic */ + offset = fEnumeratedTag (tvb, subtree, offset, + "selection Logic: ", BACnetSelectionLogic); + break; + case 1: /* listOfSelectionCriteria */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fSelectionCriteria (tvb, subtree, offset); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + } + FAULT; + break; + default: + return offset; + } + } + return offset; +} + + +static guint +fReadPropertyConditionalRequest(tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + if (tag_is_opening(tag_info) && tag_no < 2) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 0: /* objectSelectionCriteria */ + offset = fObjectSelectionCriteria (tvb, subtree, offset); + break; + case 1: /* listOfPropertyReferences */ + offset = fBACnetPropertyReference (tvb, subtree, offset, 1); + break; + default: + return offset; + } + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + } + return offset; +} + +static guint +fReadAccessSpecification (tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, + &tag_info, &lvt); + continue; + } + + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* listOfPropertyReferences */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyReference (tvb, subtree, offset, 1); + break; + } + FAULT; + break; + default: + return offset; + } + } + return offset; +} + +static guint +fReadAccessResult (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no; + guint8 tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + if (tag_no == 4 || tag_no == 5) subtree = tree; /* Value and error have extra subtree */ + continue; + } + + switch (tag_no) { + case 0: /* objectSpecifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* list of Results */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "listOfResults"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + } + FAULT; + break; + case 2: /* propertyIdentifier */ + offset = fPropertyValue(tvb, subtree, offset, 2); + break; + case 5: /* propertyAccessError */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "propertyAccessError"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + /* Error Code follows */ + offset = fError(tvb, subtree, offset); + break; + } + FAULT; + break; + default: + return offset; + } + } + return offset; +} + + +static guint +fReadPropertyConditionalAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + /* listOfReadAccessResults */ + return fReadAccessResult (tvb, tree, offset); +} + + +static guint +fCreateObjectRequest(tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0) && (offset > lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + if (tag_no < 2) + { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 0: /* objectSpecifier */ + switch (fTagNo(tvb, offset)) { /* choice of objectType or objectIdentifier */ + case 0: /* objectType */ + offset = fEnumeratedTagSplit (tvb, subtree, offset, "Object Type: ", BACnetObjectType, 128); + break; + case 1: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + default: + break; + } + break; + case 1: /* propertyValue */ + if (tag_is_opening(tag_info)) { + offset = fBACnetPropertyValue (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + break; + } + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + } + return offset; +} + +static guint +fCreateObjectAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fObjectIdentifier (tvb, tree, offset); +} + +static guint +fReadRangeRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + offset = fBACnetObjectPropertyReference(tvb, subtree, offset); + + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + /* optional range choice */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, val_to_str(tag_no, BACnetReadRangeOptions, "unknown range option")); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 3: /* range byPosition */ + case 6: /* range bySequenceNumber, 2004 spec */ + offset = fApplicationTypes (tvb, subtree, offset, "reference Index: "); + offset = fApplicationTypes (tvb, subtree, offset, "reference Count: "); + break; + case 4: /* range byTime - deprecated in 2004 */ + case 7: /* 2004 spec */ + offset = fDateTime(tvb, subtree, offset, "reference Date/Time: "); + offset = fApplicationTypes (tvb, subtree, offset, "reference Count: "); + break; + case 5: /* range timeRange - deprecated in 2004 */ + offset = fDateTime(tvb, subtree, offset, "beginning Time: "); + offset = fDateTime(tvb, subtree, offset, "ending Time: "); + break; + default: + break; + } + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + return offset; +} + +static guint +fReadRangeAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + offset = fBACnetObjectPropertyReference(tvb, subtree, offset); + + /* resultFlags */ + offset = fEnumeratedTag (tvb, tree, offset, "result Flags: ", BACnetResultFlags); + + /* itemCount */ + offset = fUnsignedTag (tvb, subtree, offset, "item Count: "); + + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + /* itemData */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "itemData"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + + if (fTagNo(tvb, offset) == 6) { /* firstSequenceNumber - OPTIONAL */ + offset = fUnsignedTag (tvb, subtree, offset, "first Sequence Number: "); + } + + return offset; +} + +static guint fAccessMethod(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint32 lvt; + guint8 tag_no, tag_info; + proto_item* tt; + proto_tree* subtree = NULL; + + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + if (tag_is_opening(tag_info)) + { + tt = proto_tree_add_text(tree, tvb, offset, 1, val_to_str(tag_no, BACnetFileAccessOption, "invalid access method")); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fApplicationTypes (tvb, subtree, offset, val_to_str(tag_no, BACnetFileStartOption, "invalid option")); + offset = fApplicationTypes (tvb, subtree, offset, val_to_str(tag_no, BACnetFileWriteInfo, "unknown option")); + + if (tag_no == 1) + { + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) + { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes (tvb, subtree, offset, "Record Data: "); + } + } + + if ((bacapp_flags & BACAPP_MORE_SEGMENTS) == 0) + { + /* More Flag is not set, so we can look for closing tag in this segment */ + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + } + } + return offset; +} + +static guint +fAtomicReadFileRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + offset = fObjectIdentifier (tvb, tree, offset); + + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + if (tag_is_opening(tag_info)) + { + tt = proto_tree_add_text(subtree, tvb, offset, 1, val_to_str(tag_no, BACnetFileAccessOption, "unknown access method")); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fSignedTag (tvb, subtree, offset, val_to_str(tag_no, BACnetFileStartOption, "unknown option")); + offset = fUnsignedTag (tvb, subtree, offset, val_to_str(tag_no, BACnetFileRequestCount, "unknown option")); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + return offset; +} + +static guint +fAtomicWriteFileRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + + offset = fObjectIdentifier (tvb, tree, offset); /* file Identifier */ + offset = fAccessMethod(tvb, tree, offset); + + return offset; +} + +static guint +fAtomicWriteFileAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint tag_no = fTagNo(tvb, offset); + return fSignedTag (tvb, tree, offset, val_to_str(tag_no, BACnetFileStartOption, "unknown option")); +} + +static guint +fAtomicReadFileAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fApplicationTypes (tvb, tree, offset, "End Of File: "); + offset = fAccessMethod(tvb, tree, offset); + + return offset; +} + +static guint +fReadPropertyMultipleRequest(tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + return fReadAccessSpecification (tvb,subtree,offset); +} + +static guint +fReadPropertyMultipleAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fReadAccessResult (tvb,tree,offset); +} + +static guint +fConfirmedServiceRequest (tvbuff_t *tvb, proto_tree *tree, guint offset, gint service_choice) +{ + if (tvb_length_remaining(tvb,offset) <= 0) + return offset; + + switch (service_choice) { + case 0: /* acknowledgeAlarm */ + offset = fAcknowledgeAlarmRequest (tvb, tree, offset); + break; + case 1: /* confirmedCOVNotification */ + offset = fConfirmedCOVNotificationRequest (tvb, tree, offset); + break; + case 2: /* confirmedEventNotification */ + offset = fConfirmedEventNotificationRequest (tvb, tree, offset); + break; + case 3: /* confirmedGetAlarmSummary conveys no parameters */ + break; + case 4: /* getEnrollmentSummaryRequest */ + offset = fGetEnrollmentSummaryRequest (tvb, tree, offset); + break; + case 5: /* subscribeCOVRequest */ + offset = fSubscribeCOVRequest(tvb, tree, offset); + break; + case 6: /* atomicReadFile-Request */ + offset = fAtomicReadFileRequest(tvb, tree, offset); + break; + case 7: /* atomicWriteFile-Request */ + offset = fAtomicWriteFileRequest(tvb, tree, offset); + break; + case 8: /* AddListElement-Request */ + offset = fAddListElementRequest(tvb, tree, offset); + break; + case 9: /* removeListElement-Request */ + offset = fRemoveListElementRequest(tvb, tree, offset); + break; + case 10: /* createObjectRequest */ + offset = fCreateObjectRequest(tvb, tree, offset); + break; + case 11: /* deleteObject */ + offset = fDeleteObjectRequest(tvb, tree, offset); + break; + case 12: + offset = fReadPropertyRequest(tvb, tree, offset); + break; + case 13: + offset = fReadPropertyConditionalRequest(tvb, tree, offset); + break; + case 14: + offset = fReadPropertyMultipleRequest(tvb, tree, offset); + break; + case 15: + offset = fWritePropertyRequest(tvb, tree, offset); + break; + case 16: + offset = fWritePropertyMultipleRequest(tvb, tree, offset); + break; + case 17: + offset = fDeviceCommunicationControlRequest(tvb, tree, offset); + break; + case 18: + offset = fConfirmedPrivateTransferRequest(tvb, tree, offset); + break; + case 19: + offset = fConfirmedTextMessageRequest(tvb, tree, offset); + break; + case 20: + offset = fReinitializeDeviceRequest(tvb, tree, offset); + break; + case 21: + offset = fVtOpenRequest(tvb, tree, offset); + break; + case 22: + offset = fVtCloseRequest (tvb, tree, offset); + break; + case 23: + offset = fVtDataRequest (tvb, tree, offset); + break; + case 24: + offset = fAuthenticateRequest (tvb, tree, offset); + break; + case 25: + offset = fRequestKeyRequest (tvb, tree, offset); + break; + case 26: + offset = fReadRangeRequest (tvb, tree, offset); + break; + case 27: + offset = fLifeSafetyOperationRequest(tvb, tree, offset, NULL); + break; + case 28: + offset = fSubscribeCOVPropertyRequest(tvb, tree, offset); + break; + case 29: + offset = fGetEventInformationRequest (tvb, tree, offset); + break; + default: + return offset; + break; + } + return offset; +} + +static guint +fConfirmedServiceAck (tvbuff_t *tvb, proto_tree *tree, guint offset, gint service_choice) +{ + if (tvb_length_remaining(tvb,offset) <= 0) + return offset; + + switch (service_choice) { + case 3: /* confirmedEventNotificationAck */ + offset = fGetAlarmSummaryAck (tvb, tree, offset); + break; + case 4: /* getEnrollmentSummaryAck */ + offset = fGetEnrollmentSummaryAck (tvb, tree, offset); + break; + case 6: /* atomicReadFile */ + offset = fAtomicReadFileAck (tvb, tree, offset); + break; + case 7: /* atomicReadFileAck */ + offset = fAtomicWriteFileAck (tvb, tree, offset); + break; + case 10: /* createObject */ + offset = fCreateObjectAck (tvb, tree, offset); + break; + case 12: + offset = fReadPropertyAck (tvb, tree, offset); + break; + case 13: + offset = fReadPropertyConditionalAck (tvb, tree, offset); + break; + case 14: + offset = fReadPropertyMultipleAck (tvb, tree, offset); + break; + case 18: + offset = fConfirmedPrivateTransferAck(tvb, tree, offset); + break; + case 21: + offset = fVtOpenAck (tvb, tree, offset); + break; + case 23: + offset = fVtDataAck (tvb, tree, offset); + break; + case 24: + offset = fAuthenticateAck (tvb, tree, offset); + break; + case 26: + offset = fReadRangeAck (tvb, tree, offset); + break; + case 29: + offset = fGetEventInformationACK (tvb, tree, offset); + break; + default: + return offset; + } + return offset; +} + +static guint +fIAmRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + /* BACnetObjectIdentifier */ + offset = fApplicationTypes (tvb, tree, offset, "BACnet Object Identifier: "); + + /* MaxAPDULengthAccepted */ + offset = fApplicationTypes (tvb, tree, offset, "Maximum ADPU Length Accepted: "); + + /* segmentationSupported */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, + "Segmentation Supported: ", BACnetSegmentation); + + /* vendor ID */ + return fUnsignedTag (tvb, tree, offset, "Vendor ID: "); +} + +static guint +fIHaveRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + /* BACnetDeviceIdentifier */ + offset = fApplicationTypes (tvb, tree, offset, "Device Identifier: "); + + /* BACnetObjectIdentifier */ + offset = fApplicationTypes (tvb, tree, offset, "Object Identifier: "); + + /* ObjectName */ + return fApplicationTypes (tvb, tree, offset, "Object Name: "); + +} + +static guint +fWhoIsRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* DeviceInstanceRangeLowLimit Optional */ + offset = fUnsignedTag (tvb, tree, offset, "Device Instance Range Low Limit: "); + break; + case 1: /* DeviceInstanceRangeHighLimit Optional but required if DeviceInstanceRangeLowLimit is there */ + offset = fUnsignedTag (tvb, tree, offset, "Device Instance Range High Limit: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fUnconfirmedServiceRequest (tvbuff_t *tvb, proto_tree *tree, guint offset, gint service_choice) +{ + if (tvb_length_remaining(tvb,offset) <= 0) + return offset; + + switch (service_choice) { + case 0: /* I-Am-Request */ + offset = fIAmRequest (tvb, tree, offset); + break; + case 1: /* i-Have Request */ + offset = fIHaveRequest (tvb, tree, offset); + break; + case 2: /* unconfirmedCOVNotification */ + offset = fUnconfirmedCOVNotificationRequest (tvb, tree, offset); + break; + case 3: /* unconfirmedEventNotification */ + offset = fUnconfirmedEventNotificationRequest (tvb, tree, offset); + break; + case 4: /* unconfirmedPrivateTransfer */ + offset = fUnconfirmedPrivateTransferRequest(tvb, tree, offset); + break; + case 5: /* unconfirmedTextMessage */ + offset = fUnconfirmedTextMessageRequest(tvb, tree, offset); + break; + case 6: /* timeSynchronization */ + offset = fTimeSynchronizationRequest (tvb, tree, offset); + break; + case 7: /* who-Has */ + offset = fWhoHas (tvb, tree, offset); + break; + case 8: /* who-Is */ + offset = fWhoIsRequest (tvb, tree, offset); + break; + case 9: /* utcTimeSynchronization */ + offset = fUTCTimeSynchronizationRequest (tvb, tree, offset); + break; + default: + break; + } + return offset; +} + +static guint +fStartConfirmed(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset, guint8 ack, + gint *svc, proto_item **tt) +{ + proto_item *tc; + proto_tree *bacapp_tree_control; + gint tmp, bacapp_type; + guint extra = 2; + + bacapp_seq = 0; + tmp = (gint) tvb_get_guint8(tvb, offset); + bacapp_type = (tmp >> 4) & 0x0f; + bacapp_flags = tmp & 0x0f; + + if (ack == 0) { + extra = 3; + } + *svc = (gint) tvb_get_guint8(tvb, offset+extra); + if (bacapp_flags & 0x08) + *svc = (gint) tvb_get_guint8(tvb, offset+extra+2); + + proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset, 1, TRUE); + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_pduflags, tvb, offset, 1, TRUE); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp_control); + + proto_tree_add_item(bacapp_tree_control, hf_bacapp_SEG, tvb, offset, 1, TRUE); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_MOR, tvb, offset, 1, TRUE); + if (ack == 0) /* The following are for ConfirmedRequest, not Complex ack */ + { + proto_tree_add_item(bacapp_tree_control, hf_bacapp_SA, tvb, offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_response_segments, tvb, + offset, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_max_adpu_size, tvb, + offset, 1, TRUE); + } + offset++; + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, offset++, 1, TRUE); + if (bacapp_flags & 0x08) { + bacapp_seq = tvb_get_guint8(tvb, offset); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_sequence_number, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_window_size, tvb, + offset++, 1, TRUE); + } + *tt = proto_tree_add_item(bacapp_tree, hf_bacapp_service, tvb, + offset++, 1, TRUE); + return offset; +} + +static guint +fConfirmedRequestPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Confirmed-Request */ + /* ASHRAE 135-2001 20.1.2 */ + gint svc; + proto_item *tt = 0; + + offset = fStartConfirmed(tvb, bacapp_tree, offset, 0, &svc, &tt); + if (bacapp_seq > 0) /* Can't handle continuation segments, so just treat as data */ + { + proto_tree_add_text(bacapp_tree, tvb, offset, 0, "(continuation)"); + return offset; + } + else + { + /* Service Request follows... Variable Encoding 20.2ff */ + return fConfirmedServiceRequest (tvb, bacapp_tree, offset, svc); + } +} + +static guint +fUnconfirmedRequestPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Unconfirmed-Request-PDU */ + /* ASHRAE 135-2001 20.1.3 */ + + gint tmp; + + proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, TRUE); + + tmp = tvb_get_guint8(tvb, offset); + proto_tree_add_item(bacapp_tree, hf_bacapp_uservice, tvb, + offset++, 1, TRUE); + /* Service Request follows... Variable Encoding 20.2ff */ + return fUnconfirmedServiceRequest (tvb, bacapp_tree, offset, tmp); +} + +static guint +fSimpleAckPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Simple-Ack-PDU */ + /* ASHRAE 135-2001 20.1.4 */ + + proto_item *tc; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, TRUE); + + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_service, tvb, + offset++, 1, TRUE); + return offset; +} + +static guint +fComplexAckPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Complex-Ack-PDU */ + /* ASHRAE 135-2001 20.1.5 */ + gint svc; + proto_item *tt = 0; + + offset = fStartConfirmed(tvb, bacapp_tree, offset, 1, &svc, &tt); + + if (bacapp_seq > 0) /* Can't handle continuation segments, so just treat as data */ + { + proto_tree_add_text(bacapp_tree, tvb, offset, 0, "(continuation)"); + return offset; + } + else + { + /* Service ACK follows... */ + return fConfirmedServiceAck (tvb, bacapp_tree, offset, svc); + } +} + + +static guint +fSegmentAckPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-SegmentAck-PDU */ + /* ASHRAE 135-2001 20.1.6 */ + + proto_item *tc; + proto_tree *bacapp_tree_control; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset, 1, TRUE); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree, hf_bacapp_NAK, tvb, offset, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_SRV, tvb, offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_sequence_number, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_window_size, tvb, + offset++, 1, TRUE); + return offset; +} + +static guint fContextTaggedError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_info = 0; + guint8 parsed_tag = 0; + guint32 lvt = 0; + offset += fTagHeaderTree(tvb, tree, offset, &parsed_tag, &tag_info, &lvt); + offset = fError(tvb, tree, offset); + return offset + fTagHeaderTree(tvb, tree, offset, &parsed_tag, &tag_info, &lvt); +} + +static guint +fConfirmedPrivateTransferError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no = 0, tag_info = 0; + guint lvt = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* errorType */ + offset = fContextTaggedError(tvb,tree,offset); + break; + case 1: /* vendorID */ + offset = fUnsignedTag (tvb,tree,offset,"vendor ID: "); + break; + case 2: /* serviceNumber */ + offset = fUnsignedTag (tvb,tree,offset,"service Number: "); + break; + case 3: /* errorParameters */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fCreateObjectError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* errorType */ + offset = fContextTaggedError(tvb,tree,offset); + break; + case 1: /* firstFailedElementNumber */ + offset = fUnsignedTag (tvb,tree,offset,"first failed element number: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fChangeListError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + /* Identical to CreateObjectError */ + return fCreateObjectError(tvb, tree, offset); +} + +static guint +fVTCloseError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + if (fTagNo(tvb, offset) == 0) + { + /* errorType */ + offset = fContextTaggedError(tvb,tree,offset); + if (fTagNo(tvb, offset) == 1) + { + /* listOfVTSessionIdentifiers [OPTIONAL] */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fVtCloseRequest (tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + } + } + /* should report bad packet if initial tag wasn't 0 */ + return offset; +} + +static guint +fWritePropertyMultipleError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* errorType */ + offset = fContextTaggedError(tvb,tree,offset); + break; + case 1: /* firstFailedWriteAttempt */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetObjectPropertyReference(tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fError (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fApplicationTypesEnumeratedSplit (tvb, tree, offset, + "error Class: ", BACnetErrorClass, 64); + return fApplicationTypesEnumeratedSplit (tvb, tree, offset, + "error Code: ", BACnetErrorCode, 256); +} + +static guint +fBACnetError (tvbuff_t *tvb, proto_tree *tree, guint offset, guint service) +{ + switch (service) { + case 8: /* no break here !!!! */ + case 9: + offset = fChangeListError (tvb, tree, offset); + break; + case 10: + offset = fCreateObjectError (tvb,tree,offset); + break; + case 16: + offset = fWritePropertyMultipleError (tvb,tree,offset); + break; + case 18: + offset = fConfirmedPrivateTransferError (tvb,tree,offset); + case 22: + offset = fVTCloseError (tvb,tree,offset); + default: + return fError (tvb, tree, offset); + break; + } + return offset; +} + +static guint +fErrorPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Error-PDU */ + /* ASHRAE 135-2001 20.1.7 */ + + proto_item *tc, *tt; + proto_tree *bacapp_tree_control; + guint8 tmp; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, TRUE); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, TRUE); + tmp = tvb_get_guint8(tvb, offset); + tt = proto_tree_add_item(bacapp_tree, hf_bacapp_service, tvb, + offset++, 1, TRUE); + /* Error Handling follows... */ + return fBACnetError (tvb, bacapp_tree, offset, tmp); +} + +static guint +fRejectPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Reject-PDU */ + /* ASHRAE 135-2001 20.1.8 */ + + proto_item *tc; + proto_tree *bacapp_tree_control; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, TRUE); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_BACnetRejectReason, tvb, + offset++, 1, TRUE); + return offset; +} + +static guint +fAbortPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Abort-PDU */ + /* ASHRAE 135-2001 20.1.9 */ + + proto_item *tc; + proto_tree *bacapp_tree_control; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset, 1, TRUE); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree, hf_bacapp_SRV, tvb, offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_BACnetAbortReason, tvb, + offset++, 1, TRUE); + return offset; +} + +void +dissect_bacapp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + gint8 tmp, bacapp_type; + tvbuff_t *next_tvb; + guint offset = 0; + guint8 bacapp_service, bacapp_reason; + guint8 bacapp_invoke_id; + proto_item *ti; + proto_tree *bacapp_tree; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet-APDU"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, "BACnet APDU "); + + tmp = (gint) tvb_get_guint8(tvb, 0); + bacapp_type = (tmp >> 4) & 0x0f; + + /* show some descriptive text in the INFO column */ + if (check_col(pinfo->cinfo, COL_INFO)) + { + col_clear(pinfo->cinfo, COL_INFO); + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(bacapp_type, BACnetTypeName, "#### unknown APDU ##### ")); + switch (bacapp_type) + { + case BACAPP_TYPE_CONFIRMED_SERVICE_REQUEST: + /* segmented messages have 2 additional bytes */ + if (tmp & BACAPP_SEGMENTED_REQUEST) + { + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 4); + bacapp_service = tvb_get_guint8(tvb, offset + 5); + } + else + { + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 2); + bacapp_service = tvb_get_guint8(tvb, offset + 3); + } + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_str(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str)); + break; + case BACAPP_TYPE_UNCONFIRMED_SERVICE_REQUEST: + bacapp_service = tvb_get_guint8(tvb, offset + 1); + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", + val_to_str(bacapp_service, + BACnetUnconfirmedServiceChoice, + bacapp_unknown_service_str)); + break; + case BACAPP_TYPE_SIMPLE_ACK: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_service = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_str(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str)); + break; + case BACAPP_TYPE_COMPLEX_ACK: + /* segmented messages have 2 additional bytes */ + if (tmp & BACAPP_SEGMENTED_REQUEST) + { + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 3); + bacapp_service = tvb_get_guint8(tvb, offset + 4); + } + else + { + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_service = tvb_get_guint8(tvb, offset + 2); + } + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_str(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str)); + break; + case BACAPP_TYPE_SEGMENT_ACK: + /* nothing more to add */ + break; + case BACAPP_TYPE_ERROR: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_service = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_str(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str)); + break; + case BACAPP_TYPE_REJECT: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_reason = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_split_str(bacapp_reason, + 64, + BACnetRejectReason, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + break; + case BACAPP_TYPE_ABORT: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_reason = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_split_str(bacapp_reason, + 64, + BACnetAbortReason, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + break; + /* UNKNOWN */ + default: + /* nothing more to add */ + break; + } + } + + if (tree) { + ti = proto_tree_add_item(tree, proto_bacapp, tvb, offset, -1, FALSE); + bacapp_tree = proto_item_add_subtree(ti, ett_bacapp); + + /* ASHRAE 135-2001 20.1.1 */ + switch (bacapp_type) { + case BACAPP_TYPE_CONFIRMED_SERVICE_REQUEST: /* BACnet-Confirmed-Service-Request */ + offset = fConfirmedRequestPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_UNCONFIRMED_SERVICE_REQUEST: /* BACnet-Unconfirmed-Request-PDU */ + offset = fUnconfirmedRequestPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_SIMPLE_ACK: /* BACnet-Simple-Ack-PDU */ + offset = fSimpleAckPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_COMPLEX_ACK: /* BACnet-Complex-Ack-PDU */ + offset = fComplexAckPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_SEGMENT_ACK: /* BACnet-SegmentAck-PDU */ + offset = fSegmentAckPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_ERROR: /* BACnet-Error-PDU */ + offset = fErrorPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_REJECT: /* BACnet-Reject-PDU */ + offset = fRejectPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_ABORT: /* BACnet-Abort-PDU */ + offset = fAbortPDU(tvb, bacapp_tree, offset); + break; + } + } + + next_tvb = tvb_new_subset(tvb,offset,-1,tvb_length_remaining(tvb,offset)); + call_dissector(data_handle,next_tvb, pinfo, tree); +} + +void +proto_register_bacapp(void) +{ + static hf_register_info hf[] = { + { &hf_bacapp_type, + { "APDU Type", "bacapp.type", + FT_UINT8, BASE_DEC, VALS(BACnetTypeName), 0xf0, "APDU Type", HFILL } + }, + { &hf_bacapp_pduflags, + { "PDU Flags", "bacapp.pduflags", + FT_UINT8, BASE_HEX, NULL, 0x0f, "PDU Flags", HFILL } + }, + { &hf_bacapp_SEG, + { "Segmented Request", "bacapp.segmented_request", + FT_BOOLEAN, 8, TFS(&segments_follow), 0x08, "Segmented Request", HFILL } + }, + { &hf_bacapp_MOR, + { "More Segments", "bacapp.more_segments", + FT_BOOLEAN, 8, TFS(&more_follow), 0x04, "More Segments Follow", HFILL } + }, + { &hf_bacapp_SA, + { "SA", "bacapp.SA", + FT_BOOLEAN, 8, TFS(&segmented_accept), 0x02, "Segmented Response accepted", HFILL } + }, + { &hf_bacapp_max_adpu_size, + { "Size of Maximum ADPU accepted", "bacapp.max_adpu_size", + FT_UINT8, BASE_DEC, VALS(BACnetMaxAPDULengthAccepted), 0x0f, "Size of Maximum ADPU accepted", HFILL } + }, + { &hf_bacapp_response_segments, + { "Max Response Segments accepted", "bacapp.response_segments", + FT_UINT8, BASE_DEC, VALS(BACnetMaxSegmentsAccepted), 0x70, "Max Response Segments accepted", HFILL } + }, + { &hf_bacapp_objectType, + { "Object Type", "bacapp.objectType", + FT_UINT32, BASE_DEC, VALS(BACnetObjectType), 0xffc00000, "Object Type", HFILL } + }, + { &hf_bacapp_instanceNumber, + { "Instance Number", "bacapp.instance_number", + FT_UINT32, BASE_DEC, NULL, 0x003fffff, "Instance Number", HFILL } + }, + { &hf_bacapp_invoke_id, + { "Invoke ID", "bacapp.invoke_id", + FT_UINT8, BASE_DEC, NULL, 0, "Invoke ID", HFILL } + }, + { &hf_bacapp_sequence_number, + { "Sequence Number", "bacapp.sequence_number", + FT_UINT8, BASE_DEC, NULL, 0, "Sequence Number", HFILL } + }, + { &hf_bacapp_window_size, + { "Proposed Window Size", "bacapp.window_size", + FT_UINT8, BASE_DEC, NULL, 0, "Proposed Window Size", HFILL } + }, + { &hf_bacapp_service, + { "Service Choice", "bacapp.confirmed_service", + FT_UINT8, BASE_DEC, VALS(BACnetConfirmedServiceChoice), 0x00, "Service Choice", HFILL } + }, + { &hf_bacapp_uservice, + { "Unconfirmed Service Choice", "bacapp.unconfirmed_service", + FT_UINT8, BASE_DEC, VALS(BACnetUnconfirmedServiceChoice), 0x00, "Unconfirmed Service Choice", HFILL } + }, + { &hf_bacapp_NAK, + { "NAK", "bacapp.NAK", + FT_BOOLEAN, 8, NULL, 0x02, "negativ ACK", HFILL } + }, + { &hf_bacapp_SRV, + { "SRV", "bacapp.SRV", + FT_BOOLEAN, 8, NULL, 0x01, "Server", HFILL } + }, + { &hf_BACnetRejectReason, + { "Reject Reason", "bacapp.reject_reason", + FT_UINT8, BASE_DEC, VALS(BACnetRejectReason), 0x00, "Reject Reason", HFILL } + }, + { &hf_BACnetAbortReason, + { "Abort Reason", "bacapp.abort_reason", + FT_UINT8, BASE_DEC, VALS(BACnetAbortReason), 0x00, "Abort Reason", HFILL } + }, + { &hf_bacapp_vpart, + { "BACnet APDU variable part:", "bacapp.variable_part", + FT_NONE, 0, NULL, 0, "BACnet APDU variable part", HFILL } + }, + { &hf_bacapp_tag, + { "BACnet Tag", + "bacapp.tag", + FT_BYTES, BASE_HEX, NULL, 0, + "BACnet Tag", HFILL } + }, + { &hf_BACnetApplicationTagNumber, + { "Application Tag Number", + "bacapp.application_tag_number", + FT_UINT8, BASE_DEC, VALS(&BACnetApplicationTagNumber), 0xF0, + "Application Tag Number", HFILL } + }, + { &hf_BACnetContextTagNumber, + { "Context Tag Number", + "bacapp.context_tag_number", + FT_UINT8, BASE_DEC, NULL, 0xF0, + "Context Tag Number", HFILL } + }, + { &hf_BACnetExtendedTagNumber, + { "Extended Tag Number", + "bacapp.extended_tag_number", + FT_UINT8, BASE_DEC, NULL, 0, + "Extended Tag Number", HFILL } + }, + { &hf_BACnetNamedTag, + { "Named Tag", + "bacapp.named_tag", + FT_UINT8, BASE_DEC, VALS(&BACnetTagNames), 0x07, + "Named Tag", HFILL } + }, + { &hf_BACnetCharacterSet, + { "String Character Set", + "bacapp.string_character_set", + FT_UINT8, BASE_DEC, VALS(&BACnetCharacterSet),0, + "String Character Set", HFILL } + }, + { &hf_BACnetTagClass, + { "Tag Class", "bacapp.tag_class", + FT_BOOLEAN, 8, TFS(&BACnetTagClass), 0x08, "Tag Class", HFILL } + }, + { &hf_bacapp_tag_lvt, + { "Length Value Type", + "bacapp.LVT", + FT_UINT8, BASE_DEC, NULL, 0, + "Length Value Type", HFILL } + }, + { &hf_bacapp_tag_value8, + { "Tag Value", + "bacapp.tag_value8", + FT_UINT8, BASE_DEC, NULL, 0, + "Tag Value", HFILL } + }, + { &hf_bacapp_tag_value16, + { "Tag Value 16-bit", + "bacapp.tag_value16", + FT_UINT16, BASE_DEC, NULL, 0, + "Tag Value 16-bit", HFILL } + }, + { &hf_bacapp_tag_value32, + { "Tag Value 32-bit", + "bacapp.tag_value32", + FT_UINT32, BASE_DEC, NULL, 0, + "Tag Value 32-bit", HFILL } + }, + { &hf_bacapp_tag_ProcessId, + { "ProcessIdentifier", "bacapp.processId", + FT_UINT32, BASE_DEC, NULL, 0, "Process Identifier", HFILL } + }, + { &hf_bacapp_tag_initiatingObjectType, + { "ObjectType", "bacapp.objectType", + FT_UINT16, BASE_DEC, VALS(BACnetObjectType), 0x00, "Object Type", HFILL } + }, + }; + static gint *ett[] = { + &ett_bacapp, + &ett_bacapp_control, + &ett_bacapp_tag, + &ett_bacapp_list, + &ett_bacapp_value, + }; + proto_bacapp = proto_register_protocol("Building Automation and Control Network APDU", + "BACapp", "bacapp"); + + proto_register_field_array(proto_bacapp, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + register_dissector("bacapp", dissect_bacapp, proto_bacapp); + +} + +void +proto_reg_handoff_bacapp(void) +{ + data_handle = find_dissector("data"); +} + +guint32 +fConvertXXXtoUTF8 (const guint8 *in, size_t *inbytesleft, guint8 *out, size_t *outbytesleft, const gchar *fromcoding) +{ /* I don't want to let in and out be modified */ +#ifdef HAVE_CONFIG_H +#if HAVE_ICONV + guint32 i; + iconv_t icd; + const guint8 *inp = in; + guint8 *outp = out; + const guint8 **inpp = &inp; + guint8 **outpp = &outp; + + if ((icd = iconv_open ("UTF-8", fromcoding)) != (iconv_t) -1) { + i = iconv (icd, (char**) inpp, inbytesleft, (char**) outpp, outbytesleft); + *outpp[0] = '\0'; + iconv_close (icd); + return i; + } + +#endif +#endif + + memcpy (out, in, *inbytesleft); + out[*inbytesleft] = '\0'; + *outbytesleft -= *inbytesleft; + *inbytesleft = 0; + + return 0; +} + diff --git a/src/main/resource/testFiles/wireshark/prevFiles/prev_606866_c79cf4_epan#dissectors#packet-atalk.c b/src/main/resource/testFiles/wireshark/prevFiles/prev_606866_c79cf4_epan#dissectors#packet-atalk.c new file mode 100644 index 0000000..40c3531 --- /dev/null +++ b/src/main/resource/testFiles/wireshark/prevFiles/prev_606866_c79cf4_epan#dissectors#packet-atalk.c @@ -0,0 +1,2494 @@ +/* packet-atalk.c + * Routines for AppleTalk packet disassembly: LLAP, DDP, NBP, ATP, ASP, + * RTMP, PAP. + * + * $Id$ + * + * Simon Wilkinson + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "packet-atalk.h" +#include "packet-afp.h" + +/* Tables for reassembly of fragments. */ +static GHashTable *atp_fragment_table = NULL; +static GHashTable *atp_reassembled_table = NULL; + +/* desegmentation of ATP */ +static gboolean atp_defragment = TRUE; + +static dissector_handle_t afp_handle; + +static int proto_llap = -1; +static int hf_llap_dst = -1; +static int hf_llap_src = -1; +static int hf_llap_type = -1; + +static int proto_ddp = -1; +static int hf_ddp_hopcount = -1; +static int hf_ddp_len = -1; +static int hf_ddp_checksum = -1; +static int hf_ddp_dst = -1; +static int hf_ddp_dst_net = -1; +static int hf_ddp_src = -1; +static int hf_ddp_src_net = -1; +static int hf_ddp_dst_node = -1; +static int hf_ddp_src_node = -1; +static int hf_ddp_dst_socket = -1; +static int hf_ddp_src_socket = -1; +static int hf_ddp_type = -1; + +static dissector_handle_t ddp_handle; + +/* -------------------------------------- + * ATP protocol parameters + * from netatalk/include/atalk/atp.h + */ +#define ATP_MAXDATA (578+4) /* maximum ATP data size */ +#define ATP_BUFSIZ 587 /* maximum packet size */ +#define ATP_HDRSIZE 5 /* includes DDP type field */ + +#define ATP_TRELMASK 0x07 /* mask all but TREL */ +#define ATP_RELTIME 30 /* base release timer (in secs) */ + +#define ATP_TREL30 0x0 /* release time codes */ +#define ATP_TREL1M 0x1 /* these are passed in flags of */ +#define ATP_TREL2M 0x2 /* atp_sreq call, and set in the */ +#define ATP_TREL4M 0x3 /* packet control info. */ +#define ATP_TREL8M 0x4 + +/* flags for ATP options (and control byte) +*/ +#define ATP_XO 0x20 /* (1<<5) eXactly Once mode */ +#define ATP_EOM 0x10 /* (1<<4) End Of Message */ +#define ATP_STS 0x08 /* (1<<3) Transaction Status */ + +/* function codes +*/ +#define ATP_FUNCMASK (3<<6) /* mask all but function */ + +#define ATP_TREQ 1 /* (1<<6) Trans. REQuest */ +#define ATP_TRESP 2 /* (2<<6) Trans. RESPonse */ +#define ATP_TREL 3 /* (3<<6) Trans. RELease */ + +/* ------------------------- */ +static dissector_handle_t asp_handle; +static dissector_handle_t pap_handle; + +static int proto_atp = -1; +static int hf_atp_ctrlinfo = -1; /* guint8_t control information */ +static int hf_atp_function = -1; /* bits 7,6 function */ +static int hf_atp_xo = -1; /* bit 5 exactly-once */ +static int hf_atp_eom = -1; /* bit 4 end-of-message */ +static int hf_atp_sts = -1; /* bit 3 send transaction status */ +static int hf_atp_treltimer = -1; /* bits 2,1,0 TRel timeout indicator */ + +static int hf_atp_bitmap = -1; /* guint8_t bitmap or sequence number */ +static int hf_atp_tid = -1; /* guint16_t transaction id. */ +static int hf_atp_user_bytes = -1; + +static int hf_atp_segments = -1; +static int hf_atp_segment = -1; +static int hf_atp_segment_overlap = -1; +static int hf_atp_segment_overlap_conflict = -1; +static int hf_atp_segment_multiple_tails = -1; +static int hf_atp_segment_too_long_segment = -1; +static int hf_atp_segment_error = -1; +static int hf_atp_reassembled_in = -1; + +/* ------------------------- */ +static int proto_zip = -1; +static dissector_handle_t zip_atp_handle; + +static int hf_zip_function = -1; +static int hf_zip_atp_function = -1; +static int hf_zip_start_index = -1; +static int hf_zip_count = -1; +static int hf_zip_zero_value = -1; + +static int hf_zip_network_count = -1; +static int hf_zip_network = -1; +static int hf_zip_network_start = -1; +static int hf_zip_network_end = -1; + +static int hf_zip_flags = -1; + +static int hf_zip_flags_zone_invalid = -1; +static int hf_zip_flags_use_broadcast = -1; +static int hf_zip_flags_only_one_zone = -1; + +static int hf_zip_last_flag = -1; + +static int hf_zip_zone_name = -1; +static int hf_zip_default_zone = -1; + +static int hf_zip_multicast_length = -1; +static int hf_zip_multicast_address = -1; + +static const value_string zip_function_vals[] = { + {1, "Query"}, + {2, "Reply"}, + {5, "GetNetInfo request"}, + {6, "GetNetInfo reply"}, + {7, "notify"}, + {8, "Extended reply"}, + {0, NULL} +}; + +static const value_string zip_atp_function_vals[] = { + {7, "GetMyZone"}, + {8, "GetZoneList"}, + {9, "GetLocalZones"}, + {0, NULL} +}; + +static gint ett_zip = -1; +static gint ett_zip_flags = -1; +static gint ett_zip_zones_list = -1; +static gint ett_zip_network_list = -1; + +/* -------------------------------- + * from netatalk/include/atalk/ats.h + */ + +#define ASPFUNC_CLOSE 1 +#define ASPFUNC_CMD 2 +#define ASPFUNC_STAT 3 +#define ASPFUNC_OPEN 4 +#define ASPFUNC_TICKLE 5 +#define ASPFUNC_WRITE 6 +#define ASPFUNC_WRTCONT 7 +#define ASPFUNC_ATTN 8 + +#define ASP_HDRSIZ 4 +#define ASPERR_OK 0x0000 +#define ASPERR_BADVERS 0xfbd6 +#define ASPERR_BUFSMALL 0xfbd5 +#define ASPERR_NOSESS 0xfbd4 +#define ASPERR_NOSERV 0xfbd3 +#define ASPERR_PARM 0xfbd2 +#define ASPERR_SERVBUSY 0xfbd1 +#define ASPERR_SESSCLOS 0xfbd0 +#define ASPERR_SIZERR 0xfbcf +#define ASPERR_TOOMANY 0xfbce +#define ASPERR_NOACK 0xfbcd + +static int proto_asp = -1; +static int hf_asp_func = -1; +static int hf_asp_error = -1; +static int hf_asp_socket = -1; +static int hf_asp_version = -1; +static int hf_asp_session_id = -1; +static int hf_asp_zero_value = -1; +static int hf_asp_init_error = -1; +static int hf_asp_attn_code = -1; +static int hf_asp_seq = -1; +static int hf_asp_size = -1; + +/* status stuff same for asp and afp */ +static int hf_asp_server_name = -1; +static int hf_asp_server_type = -1; +static int hf_asp_server_vers = -1; +static int hf_asp_server_uams = -1; +static int hf_asp_server_icon = -1; +static int hf_asp_server_directory = -1; + +static int hf_asp_server_flag = -1; +static int hf_asp_server_flag_copyfile = -1; +static int hf_asp_server_flag_passwd = -1; +static int hf_asp_server_flag_no_save_passwd = -1; +static int hf_asp_server_flag_srv_msg = -1; +static int hf_asp_server_flag_srv_sig = -1; +static int hf_asp_server_flag_tcpip = -1; +static int hf_asp_server_flag_notify = -1; +static int hf_asp_server_flag_reconnect = -1; +static int hf_asp_server_flag_directory = -1; +static int hf_asp_server_flag_utf8_name = -1; +static int hf_asp_server_flag_fast_copy = -1; +static int hf_asp_server_signature = -1; +static int hf_asp_server_utf8_name_len = -1; +static int hf_asp_server_utf8_name = -1; + +static int hf_asp_server_addr_len = -1; +static int hf_asp_server_addr_type = -1; +static int hf_asp_server_addr_value = -1; + +static gint ett_asp_status = -1; +static gint ett_asp_uams = -1; +static gint ett_asp_vers = -1; +static gint ett_asp_addr = -1; +static gint ett_asp_addr_line = -1; +static gint ett_asp_directory = -1; +static gint ett_asp_utf8_name = -1; +static gint ett_asp_status_server_flag = -1; + +typedef struct { + guint32 conversation; + guint8 src[4]; + guint16 seq; +} asp_request_key; + +typedef struct { + guint8 value; /* command for asp, bitmap for atp */ +} asp_request_val; + +static GHashTable *asp_request_hash = NULL; + +/* Hash Functions */ +static gint asp_equal (gconstpointer v, gconstpointer v2) +{ + const asp_request_key *val1 = (const asp_request_key*)v; + const asp_request_key *val2 = (const asp_request_key*)v2; + + if (val1->conversation == val2->conversation && + val1->seq == val2->seq && + !memcmp(val1->src, val2->src, 4)) { + return 1; + } + return 0; +} + +static guint asp_hash (gconstpointer v) +{ + const asp_request_key *asp_key = (const asp_request_key*)v; + return asp_key->seq; +} + +/* ------------------------------------ */ +static GHashTable *atp_request_hash = NULL; + + +/* ------------------------------------ */ +static int proto_nbp = -1; +static int hf_nbp_op = -1; +static int hf_nbp_info = -1; +static int hf_nbp_count = -1; +static int hf_nbp_tid = -1; + +static int hf_nbp_node_net = -1; +static int hf_nbp_node_port = -1; +static int hf_nbp_node_node = -1; +static int hf_nbp_node_enum = -1; +static int hf_nbp_node_object = -1; +static int hf_nbp_node_type = -1; +static int hf_nbp_node_zone = -1; + +static int proto_rtmp = -1; +static int hf_rtmp_net = -1; +static int hf_rtmp_node_len = -1; +static int hf_rtmp_node = -1; +static int hf_rtmp_tuple_net = -1; +static int hf_rtmp_tuple_range_start = -1; +static int hf_rtmp_tuple_range_end = -1; +static int hf_rtmp_tuple_dist = -1; +static int hf_rtmp_function = -1; + +static gint ett_atp = -1; + +static gint ett_atp_segments = -1; +static gint ett_atp_segment = -1; +static gint ett_atp_info = -1; +static gint ett_asp = -1; +static gint ett_pap = -1; + +static gint ett_nbp = -1; +static gint ett_nbp_info = -1; +static gint ett_nbp_node = -1; +static gint ett_rtmp = -1; +static gint ett_rtmp_tuple = -1; +static gint ett_ddp = -1; +static gint ett_llap = -1; +static gint ett_pstring = -1; + +static const fragment_items atp_frag_items = { + &ett_atp_segment, + &ett_atp_segments, + &hf_atp_segments, + &hf_atp_segment, + &hf_atp_segment_overlap, + &hf_atp_segment_overlap_conflict, + &hf_atp_segment_multiple_tails, + &hf_atp_segment_too_long_segment, + &hf_atp_segment_error, + &hf_atp_reassembled_in, + "segments" +}; + +/* -------------------------------- */ + +#define PAPOpenConn 1 +#define PAPOpenConnReply 2 +#define PAPSendData 3 +#define PAPData 4 +#define PAPTickle 5 +#define PAPCloseConn 6 +#define PAPCloseConnReply 7 +#define PAPSendStatus 8 +#define PAPStatus 9 + +static int proto_pap = -1; + +static int hf_pap_connid = -1; +static int hf_pap_function = -1; +static int hf_pap_socket = -1; +static int hf_pap_quantum = -1; +static int hf_pap_waittime = -1; +static int hf_pap_result = -1; +static int hf_pap_status = -1; +static int hf_pap_seq = -1; +static int hf_pap_eof = -1; + +static int hf_pap_pad = -1; + +static const value_string pap_function_vals[] = { + {PAPOpenConn , "Open Connection Query"}, + {PAPOpenConnReply , "Open Connection Reply"}, + {PAPSendData , "Send Data"}, + {PAPData , "Data"}, + {PAPTickle , "Tickle"}, + {PAPCloseConn , "Close Connection Query"}, + {PAPCloseConnReply , "Close Connection reply"}, + {PAPSendStatus , "Send Status"}, + {PAPStatus , "Status"}, + + {0, NULL} +}; + +/* -------------------------------- */ + +static dissector_table_t ddp_dissector_table; + +static dissector_handle_t data_handle; + +#define DDP_SHORT_HEADER_SIZE 5 + +/* + * P = Padding, H = Hops, L = Len + * + * PPHHHHLL LLLLLLLL + * + * Assumes the argument is in host byte order. + */ +#define ddp_hops(x) ( ( x >> 10) & 0x3C ) +#define ddp_len(x) ( x & 0x03ff ) +typedef struct _e_ddp { + guint16 hops_len; /* combines pad, hops, and len */ + guint16 sum,dnet,snet; + guint8 dnode,snode; + guint8 dport,sport; + guint8 type; +} e_ddp; + +#define DDP_HEADER_SIZE 13 + + +static const value_string op_vals[] = { + {DDP_RTMPDATA, "AppleTalk Routing Table response or data" }, + {DDP_NBP, "AppleTalk Name Binding Protocol packet"}, + {DDP_ATP, "AppleTalk Transaction Protocol packet"}, + {DDP_AEP, "AppleTalk Echo Protocol packet"}, + {DDP_RTMPREQ, "AppleTalk Routing Table request"}, + {DDP_ZIP, "AppleTalk Zone Information Protocol packet"}, + {DDP_ADSP, "AppleTalk Data Stream Protocol"}, + {DDP_EIGRP, "Cisco EIGRP for AppleTalk"}, + {0, NULL} +}; + +static const value_string rtmp_function_vals[] = { + {1, "Request"}, + {2, "Route Data Request (split horizon processed)"}, + {3, "Route Data Request (no split horizon processing)"}, + {0, NULL} +}; + +#define NBP_BROADCAST 1 +#define NBP_LOOKUP 2 +#define NBP_FORWARD 4 +#define NBP_REPLY 3 + +static const value_string nbp_op_vals[] = { + {NBP_BROADCAST, "broadcast request"}, + {NBP_LOOKUP, "lookup"}, + {NBP_FORWARD, "forward request"}, + {NBP_REPLY, "reply"}, + {0, NULL} +}; + +static const value_string atp_function_vals[] = { + {ATP_TREQ ,"REQuest"}, + {ATP_TRESP ,"RESPonse"}, + {ATP_TREL ,"RELease"}, + {0, NULL} +}; + +static const value_string atp_trel_timer_vals[] = { + {0, "30 seconds"}, + {1, "1 minute"}, + {2, "2 minutes"}, + {3, "4 minutes"}, + {4, "8 minutes"}, + {0, NULL} +}; + +/* +*/ +static const value_string asp_func_vals[] = { + {ASPFUNC_CLOSE, "CloseSession" }, + {ASPFUNC_CMD, "Command" }, + {ASPFUNC_STAT, "GetStatus" }, + {ASPFUNC_OPEN, "OpenSession" }, + {ASPFUNC_TICKLE, "Tickle" }, + {ASPFUNC_WRITE, "Write" }, + {ASPFUNC_WRTCONT, "Write Cont" }, + {ASPFUNC_ATTN, "Attention" }, + {0, NULL } }; + +const value_string asp_error_vals[] = { + {AFP_OK , "success"}, + {AFPERR_ACCESS , "permission denied" }, + {AFPERR_AUTHCONT , "logincont" }, + {AFPERR_BADUAM , "uam doesn't exist" }, + {AFPERR_BADVERS , "bad afp version number" }, + {AFPERR_BITMAP , "invalid bitmap" }, + {AFPERR_CANTMOVE , "can't move file" }, + {AFPERR_DENYCONF , "file synchronization locks conflict" }, + {AFPERR_DIRNEMPT , "directory not empty" }, + {AFPERR_DFULL , "disk full" }, + {AFPERR_EOF , "end of file" }, + {AFPERR_BUSY , "FileBusy" }, + {AFPERR_FLATVOL , "volume doesn't support directories" }, + {AFPERR_NOITEM , "ItemNotFound" }, + {AFPERR_LOCK , "LockErr" }, + {AFPERR_MISC , "misc. err" }, + {AFPERR_NLOCK , "no more locks" }, + {AFPERR_NOSRVR , "no response by server at that address" }, + {AFPERR_EXIST , "object already exists" }, + {AFPERR_NOOBJ , "object not found" }, + {AFPERR_PARAM , "parameter error" }, + {AFPERR_NORANGE , "no range lock" }, + {AFPERR_RANGEOVR , "range overlap" }, + {AFPERR_SESSCLOS , "session closed" }, + {AFPERR_NOTAUTH , "user not authenticated" }, + {AFPERR_NOOP , "command not supported" }, + {AFPERR_BADTYPE , "object is the wrong type" }, + {AFPERR_NFILE , "too many files open" }, + {AFPERR_SHUTDOWN , "server is going down" }, + {AFPERR_NORENAME , "can't rename" }, + {AFPERR_NODIR , "couldn't find directory" }, + {AFPERR_ITYPE , "wrong icon type" }, + {AFPERR_VLOCK , "volume locked" }, + {AFPERR_OLOCK , "object locked" }, + {AFPERR_CTNSHRD , "share point contains a share point" }, + {AFPERR_NOID , "file thread not found" }, + {AFPERR_EXISTID , "file already has an id" }, + {AFPERR_DIFFVOL , "different volume" }, + {AFPERR_CATCHNG , "catalog has changed" }, + {AFPERR_SAMEOBJ , "source file == destination file" }, + {AFPERR_BADID , "non-existent file id" }, + {AFPERR_PWDSAME , "same password/can't change password" }, + {AFPERR_PWDSHORT , "password too short" }, + {AFPERR_PWDEXPR , "password expired" }, + {AFPERR_INSHRD , "folder being shared is inside a shared folder." }, + {AFPERR_INTRASH , "shared folder in trash." }, + {AFPERR_PWDCHNG , "password needs to be changed" }, + {AFPERR_PWDPOLCY , "password fails policy check" }, + {AFPERR_USRLOGIN , "user already logged on" }, + {0, NULL } }; + +/* + * XXX - do this with an FT_UINT_STRING? + * Unfortunately, you can't extract from an FT_UINT_STRING the string, + * which we'd want to do in order to put it into the "Data:" portion. + * + * Are these always in the Mac extended character set? + */ +static int dissect_pascal_string(tvbuff_t *tvb, int offset, proto_tree *tree, + int hf_index) +{ + int len; + char *tmp; + + len = tvb_get_guint8(tvb, offset); + offset++; + + if ( tree ) + { + proto_tree *item; + proto_tree *subtree; + + /* + * XXX - if we could do this inside the protocol tree + * code, we could perhaps avoid allocating and freeing + * this string buffer. + */ + tmp = (gchar*)tvb_get_ephemeral_string(tvb, offset, len); + item = proto_tree_add_string(tree, hf_index, tvb, offset-1, len+1, tmp); + + subtree = proto_item_add_subtree(item, ett_pstring); + proto_tree_add_text(subtree, tvb, offset-1, 1, "Length: %d", len); + proto_tree_add_text(subtree, tvb, offset, len, "Data: %s", tmp); + + } + offset += len; + + return offset; +} + +static void +dissect_rtmp_request(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { + proto_tree *rtmp_tree; + proto_item *ti; + guint8 function; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + function = tvb_get_guint8(tvb, 0); + + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(function, rtmp_function_vals, "Unknown function (%02x)")); + + if (tree) { + ti = proto_tree_add_item(tree, proto_rtmp, tvb, 0, 1, FALSE); + rtmp_tree = proto_item_add_subtree(ti, ett_rtmp); + + proto_tree_add_uint(rtmp_tree, hf_rtmp_function, tvb, 0, 1, function); + } +} + +static void +dissect_rtmp_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { + proto_tree *rtmp_tree; + proto_item *ti; + int offset = 0; + guint16 net; + guint8 nodelen,nodelen_bits; + guint16 node; /* might be more than 8 bits */ + int i; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + net = tvb_get_ntohs(tvb, offset); + nodelen_bits = tvb_get_guint8(tvb, offset+2); + if ( nodelen_bits <= 8 ) { + node = tvb_get_guint8(tvb, offset)+1; + nodelen = 1; + } else { + node = tvb_get_ntohs(tvb, offset); + nodelen = 2; + } + + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_fstr(pinfo->cinfo, COL_INFO, "Net: %u Node Len: %u Node: %u", + net, nodelen_bits, node); + + if (tree) { + ti = proto_tree_add_item(tree, proto_rtmp, tvb, offset, -1, FALSE); + rtmp_tree = proto_item_add_subtree(ti, ett_rtmp); + + proto_tree_add_uint(rtmp_tree, hf_rtmp_net, tvb, offset, 2, net); + proto_tree_add_uint(rtmp_tree, hf_rtmp_node_len, tvb, offset+2, 1, + nodelen_bits); + proto_tree_add_uint(rtmp_tree, hf_rtmp_node, tvb, offset+3, nodelen, + node); + offset += 3 + nodelen; + + i = 1; + while (tvb_offset_exists(tvb, offset)) { + proto_tree *tuple_item, *tuple_tree; + guint16 tuple_net; + guint8 tuple_dist; + guint16 tuple_range_end; + + tuple_net = tvb_get_ntohs(tvb, offset); + tuple_dist = tvb_get_guint8(tvb, offset+2); + + if (tuple_dist & 0x80) { + tuple_range_end = tvb_get_ntohs(tvb, offset+3); + tuple_item = proto_tree_add_text(rtmp_tree, tvb, offset, 6, + "Tuple %d: Range Start: %u Dist: %u Range End: %u", + i, tuple_net, tuple_dist&0x7F, tuple_range_end); + } else { + tuple_item = proto_tree_add_text(rtmp_tree, tvb, offset, 3, + "Tuple %d: Net: %u Dist: %u", + i, tuple_net, tuple_dist); + } + tuple_tree = proto_item_add_subtree(tuple_item, ett_rtmp_tuple); + + if (tuple_dist & 0x80) { + proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_range_start, tvb, offset, 2, + tuple_net); + } else { + proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_net, tvb, offset, 2, + tuple_net); + } + proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_dist, tvb, offset+2, 1, + tuple_dist & 0x7F); + + if (tuple_dist & 0x80) { + /* + * Extended network tuple. + */ + proto_tree_add_item(tuple_tree, hf_rtmp_tuple_range_end, tvb, offset+3, 2, + FALSE); + offset += 6; + } else + offset += 3; + + i++; + } + } +} + +static void +dissect_nbp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { + proto_tree *nbp_tree; + proto_tree *nbp_info_tree; + proto_item *ti, *info_item; + int offset = 0; + guint8 info; + guint op, count; + unsigned int i; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + info = tvb_get_guint8(tvb, offset); + op = info >> 4; + count = info & 0x0F; + + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_fstr(pinfo->cinfo, COL_INFO, "Op: %s Count: %u", + val_to_str(op, nbp_op_vals, "Unknown (0x%01x)"), count); + + if (tree) { + ti = proto_tree_add_item(tree, proto_nbp, tvb, offset, -1, FALSE); + nbp_tree = proto_item_add_subtree(ti, ett_nbp); + + info_item = proto_tree_add_uint_format(nbp_tree, hf_nbp_info, tvb, offset, 1, + info, + "Info: 0x%01X Operation: %s Count: %u", info, + val_to_str(op, nbp_op_vals, "Unknown (0x%01X)"), + count); + nbp_info_tree = proto_item_add_subtree(info_item, ett_nbp_info); + proto_tree_add_uint(nbp_info_tree, hf_nbp_op, tvb, offset, 1, info); + proto_tree_add_uint(nbp_info_tree, hf_nbp_count, tvb, offset, 1, info); + proto_tree_add_item(nbp_tree, hf_nbp_tid, tvb, offset+1, 1, FALSE); + offset += 2; + + for (i=0; icinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ATP"); + + ctrlinfo = tvb_get_guint8(tvb, offset); + bitmap = tvb_get_guint8(tvb, offset +1); + tid = tvb_get_ntohs(tvb, offset +2); + + t = bitmap; + while(t) { + nbe++; + t >>= 1; + } + + op = ctrlinfo >> 6; + + aspinfo.reply = (0x80 == (ctrlinfo & ATP_FUNCMASK))?1:0; + aspinfo.release = (0xC0 == (ctrlinfo & ATP_FUNCMASK))?1:0; + aspinfo.seq = tid; + aspinfo.code = 0; + query = (!aspinfo.reply && !aspinfo.release); + + conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + + if (conversation == NULL) + { + conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, pinfo->destport, 0); + } + + if (atp_defragment) { + asp_request_key request_key; + + request_key.conversation = conversation->index; + memcpy(request_key.src, (!aspinfo.reply)?pinfo->src.data:pinfo->dst.data, 4); + request_key.seq = aspinfo.seq; + + request_val = (asp_request_val *) g_hash_table_lookup(atp_request_hash, &request_key); + + if (!request_val && query && nbe > 1) { + asp_request_key *new_request_key; + + /* only save nbe packets if more than 1 requested + save some memory and help the defragmentation if tid wraparound, ie + we have both a request for 1 packet and a request for n packets, + hopefully most of the time ATP_EOM will be set in the last packet. + */ + new_request_key = se_alloc(sizeof(asp_request_key)); + *new_request_key = request_key; + + request_val = se_alloc(sizeof(asp_request_val)); + request_val->value = nbe; + + g_hash_table_insert(atp_request_hash, new_request_key,request_val); + } + } + + /* + ATP_EOM is not mandatory. Some implementations don't always set it + if the query is only one packet. + + So it needs to keep the number of packets asked in request. + */ + + if (aspinfo.reply) { + more_fragment = !(ATP_EOM & ctrlinfo) && request_val; + frag_number = bitmap; + } + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_clear(pinfo->cinfo, COL_INFO); + col_add_fstr(pinfo->cinfo, COL_INFO, "%s transaction %u", + val_to_str(op, atp_function_vals, "Unknown (0x%01x)"),tid); + if (more_fragment) + col_append_str(pinfo->cinfo, COL_INFO, " [fragment]"); + } + + if (tree) { + ti = proto_tree_add_item(tree, proto_atp, tvb, offset, -1, FALSE); + atp_tree = proto_item_add_subtree(ti, ett_atp); + proto_item_set_len(atp_tree, aspinfo.release?8:ATP_HDRSIZE -1); + + info_item = proto_tree_add_item(atp_tree, hf_atp_ctrlinfo, tvb, offset, 1, FALSE); + atp_info_tree = proto_item_add_subtree(info_item, ett_atp_info); + + proto_tree_add_item(atp_info_tree, hf_atp_function, tvb, offset, 1, FALSE); + proto_tree_add_item(atp_info_tree, hf_atp_xo, tvb, offset, 1, FALSE); + proto_tree_add_item(atp_info_tree, hf_atp_eom, tvb, offset, 1, FALSE); + proto_tree_add_item(atp_info_tree, hf_atp_sts, tvb, offset, 1, FALSE); + if ((ctrlinfo & (ATP_FUNCMASK|ATP_XO)) == (0x40|ATP_XO)) { + /* TReq with XO set */ + proto_tree_add_item(atp_info_tree, hf_atp_treltimer, tvb, offset, 1, FALSE); + } + if (query) { + proto_tree_add_text(atp_tree, tvb, offset +1, 1, + "Bitmap: 0x%02x %u packet(s) max", bitmap, nbe); + } + else { + proto_tree_add_item(atp_tree, hf_atp_bitmap, tvb, offset +1, 1, FALSE); + } + proto_tree_add_item(atp_tree, hf_atp_tid, tvb, offset +2, 2, FALSE); + + if (aspinfo.release) + proto_tree_add_item(atp_tree, hf_atp_user_bytes, tvb, offset +4, 4, FALSE); + + } + + if (aspinfo.release) + return; + + save_fragmented = pinfo->fragmented; + + /* FIXME + asp doesn't fit very well here + move asp back in atp? + */ + if (atp_defragment && aspinfo.reply && (more_fragment || frag_number != 0)) { + fragment_data *fd_head; + int hdr; + + hdr = ATP_HDRSIZE -1; + if (frag_number != 0) + hdr += 4; /* asp header */ + len = tvb_reported_length_remaining(tvb, hdr); + fd_head = fragment_add_seq_check(tvb, hdr, pinfo, tid, + atp_fragment_table, + atp_reassembled_table, + frag_number, + len, + more_fragment); + new_tvb = process_reassembled_data(tvb, ATP_HDRSIZE -1, pinfo, + "Reassembled ATP", fd_head, &atp_frag_items, + NULL, atp_tree); + } + else { + /* full packet */ + new_tvb = tvb_new_subset(tvb, ATP_HDRSIZE -1, -1,- 1); + } + + if (new_tvb) { + pinfo->private_data = &aspinfo; + /* if port == 6 it's not an ASP packet but a ZIP packet */ + if (pinfo->srcport == 6 || pinfo->destport == 6 ) + call_dissector(zip_atp_handle, new_tvb, pinfo, tree); + else { + /* XXX need a conversation_get_dissector function ? */ + if (!aspinfo.reply && !conversation->dissector_handle) { + dissector_handle_t sub; + + /* if it's a known ASP function call ASP dissector + else assume it's a PAP connection ID. + the test is wrong because PAP conn IDs overlapped with ASP fn + but I don't want to keep track of NBP msgs and open connection + port allocation. + */ + guint8 fn = tvb_get_guint8(new_tvb, 0); + + if (!fn || fn > ASPFUNC_ATTN) { + sub = pap_handle; + } + else { + sub = asp_handle; + } + call_dissector(sub, new_tvb, pinfo, tree); + conversation_set_dissector(conversation, sub); + } + else if (!try_conversation_dissector(&pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, new_tvb,pinfo, tree)) { + call_dissector(data_handle, new_tvb, pinfo, tree); + + } + } + } + else { + /* Just show this as a fragment. */ + new_tvb = tvb_new_subset (tvb, ATP_HDRSIZE -1, -1, -1); + call_dissector(data_handle, new_tvb, pinfo, tree); + } + pinfo->fragmented = save_fragmented; + return; +} + +/* + copy and paste from dsi + XXX - is the format of this reply dependent on the type of server, + with this format being the format for AFP servers? +*/ +static gint +dissect_asp_reply_get_status(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, gint offset) +{ + proto_tree *sub_tree; + proto_item *ti; + + guint16 ofs; + guint16 flag; + guint16 machine_ofs; + guint16 sign_ofs = 0; + guint16 adr_ofs = 0; + guint16 dir_ofs = 0; + guint16 utf_ofs = 0; + guint8 nbe; + guint8 len; + guint8 i; + + proto_tree *adr_tree; + char *tmp; + const guint8 *ip; + guint16 net; + guint8 node; + guint16 port; + + guint16 ulen; + + if (!tree) + return offset; + + ti = proto_tree_add_text(tree, tvb, offset, -1, "Get Status"); + tree = proto_item_add_subtree(ti, ett_asp_status); + + machine_ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_MACHOFF); + proto_tree_add_text(tree, tvb, offset +AFPSTATUS_MACHOFF, 2, "Machine offset: %u", machine_ofs); + if (machine_ofs) + machine_ofs += offset; + + ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_VERSOFF); + proto_tree_add_text(tree, tvb, offset +AFPSTATUS_VERSOFF, 2, "Version offset: %u", ofs); + + ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_UAMSOFF); + proto_tree_add_text(tree, tvb, offset +AFPSTATUS_UAMSOFF, 2, "UAMS offset: %u", ofs); + + ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_ICONOFF); + proto_tree_add_text(tree, tvb, offset +AFPSTATUS_ICONOFF, 2, "Icon offset: %u", ofs); + + ofs = offset +AFPSTATUS_FLAGOFF; + ti = proto_tree_add_item(tree, hf_asp_server_flag, tvb, ofs, 2, FALSE); + sub_tree = proto_item_add_subtree(ti, ett_asp_status_server_flag); + proto_tree_add_item(sub_tree, hf_asp_server_flag_copyfile , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_passwd , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_no_save_passwd, tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_srv_msg , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_srv_sig , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_tcpip , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_notify , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_reconnect , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_directory , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_utf8_name , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_fast_copy , tvb, ofs, 2, FALSE); + + proto_tree_add_item(tree, hf_asp_server_name, tvb, offset +AFPSTATUS_PRELEN, 1, FALSE); + + flag = tvb_get_ntohs(tvb, ofs); + if ((flag & AFPSRVRINFO_SRVSIGNATURE)) { + ofs = offset +AFPSTATUS_PRELEN +tvb_get_guint8(tvb, offset +AFPSTATUS_PRELEN) +1; + if ((ofs & 1)) + ofs++; + + sign_ofs = tvb_get_ntohs(tvb, ofs); + proto_tree_add_text(tree, tvb, ofs, 2, "Signature offset: %u", sign_ofs); + sign_ofs += offset; + ofs += 2; + + if ((flag & AFPSRVRINFO_TCPIP) && ofs < machine_ofs ) { + adr_ofs = tvb_get_ntohs(tvb, ofs); + proto_tree_add_text(tree, tvb, ofs, 2, "Network address offset: %u", adr_ofs); + adr_ofs += offset; + ofs += 2; + } + + if ((flag & AFPSRVRINFO_SRVDIRECTORY) && ofs < machine_ofs) { + dir_ofs = tvb_get_ntohs(tvb, ofs); + proto_tree_add_text(tree, tvb, ofs, 2, "Directory services offset: %u", dir_ofs); + dir_ofs += offset; + ofs += 2; + } + + if ((flag & AFPSRVRINFO_SRVUTF8) && ofs < machine_ofs) { + utf_ofs = tvb_get_ntohs(tvb, ofs); + proto_tree_add_text(tree, tvb, ofs, 2, "UTF-8 Server name offset: %u", utf_ofs); + utf_ofs += offset; + } + } + + if (machine_ofs) + proto_tree_add_item(tree, hf_asp_server_type, tvb, machine_ofs, 1, FALSE); + + ofs = offset +tvb_get_ntohs(tvb, offset +AFPSTATUS_VERSOFF); + if (ofs) { + nbe = tvb_get_guint8(tvb, ofs); + ti = proto_tree_add_text(tree, tvb, ofs, 1, "Version list: %u", nbe); + ofs++; + sub_tree = proto_item_add_subtree(ti, ett_asp_vers); + for (i = 0; i < nbe; i++) { + len = tvb_get_guint8(tvb, ofs); + proto_tree_add_item(sub_tree, hf_asp_server_vers, tvb, ofs, 1, FALSE); + ofs += len + 1; + } + } + + ofs = offset +tvb_get_ntohs(tvb, offset +AFPSTATUS_UAMSOFF); + if (ofs) { + nbe = tvb_get_guint8(tvb, ofs); + ti = proto_tree_add_text(tree, tvb, ofs, 1, "UAMS list: %u", nbe); + ofs++; + sub_tree = proto_item_add_subtree(ti, ett_asp_uams); + for (i = 0; i < nbe; i++) { + len = tvb_get_guint8(tvb, ofs); + proto_tree_add_item(sub_tree, hf_asp_server_uams, tvb, ofs, 1, FALSE); + ofs += len + 1; + } + } + + ofs = offset +tvb_get_ntohs(tvb, offset +AFPSTATUS_ICONOFF); + if (ofs) + proto_tree_add_item(tree, hf_asp_server_icon, tvb, ofs, 256, FALSE); + + if (sign_ofs) { + proto_tree_add_item(tree, hf_asp_server_signature, tvb, sign_ofs, 16, FALSE); + } + + if (adr_ofs) { + + ofs = adr_ofs; + nbe = tvb_get_guint8(tvb, ofs); + ti = proto_tree_add_text(tree, tvb, ofs, 1, "Address list: %u", nbe); + ofs++; + adr_tree = proto_item_add_subtree(ti, ett_asp_addr); + for (i = 0; i < nbe; i++) { + guint8 type; + + len = tvb_get_guint8(tvb, ofs); + type = tvb_get_guint8(tvb, ofs +1); + switch (type) { + case 1: /* IP */ + ip = tvb_get_ptr(tvb, ofs+2, 4); + ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ip %s", ip_to_str(ip)); + break; + case 2: /* IP + port */ + ip = tvb_get_ptr(tvb, ofs+2, 4); + port = tvb_get_ntohs(tvb, ofs+6); + ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ip %s:%u",ip_to_str(ip),port); + break; + case 3: /* DDP, atalk_addr_to_str want host order not network */ + net = tvb_get_ntohs(tvb, ofs+2); + node = tvb_get_guint8(tvb, ofs +4); + port = tvb_get_guint8(tvb, ofs +5); + ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ddp %u.%u:%u", + net, node, port); + break; + case 5: /* IP + port ssh tunnel */ + ip = tvb_get_ptr(tvb, ofs+2, 4); + port = tvb_get_ntohs(tvb, ofs+6); + ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ip (ssh tunnel) %s:%u",ip_to_str(ip),port); + break; + case 4: /* DNS */ + if (len > 2) { + tmp = (gchar*)tvb_get_ephemeral_string(tvb, ofs +2, len -2); + ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "dns %s", tmp); + break; + } + /* else fall to default malformed record */ + default: + ti = proto_tree_add_text(adr_tree, tvb, ofs, len,"Unknow type : %u", type); + break; + } + len -= 2; + sub_tree = proto_item_add_subtree(ti,ett_asp_addr_line); + proto_tree_add_item(sub_tree, hf_asp_server_addr_len, tvb, ofs, 1, FALSE); + ofs++; + proto_tree_add_item(sub_tree, hf_asp_server_addr_type, tvb, ofs, 1, FALSE); + ofs++; + proto_tree_add_item(sub_tree, hf_asp_server_addr_value,tvb, ofs, len, FALSE); + ofs += len; + } + } + + if (dir_ofs) { + ofs = dir_ofs; + nbe = tvb_get_guint8(tvb, ofs); + ti = proto_tree_add_text(tree, tvb, ofs, 1, "Directory services list: %u", nbe); + ofs++; + sub_tree = proto_item_add_subtree(ti, ett_asp_directory); + for (i = 0; i < nbe; i++) { + len = tvb_get_guint8(tvb, ofs); + proto_tree_add_item(sub_tree, hf_asp_server_directory, tvb, ofs, 1, FALSE); + ofs += len + 1; + } + } + if (utf_ofs) { + + ofs = utf_ofs; + ulen = tvb_get_ntohs(tvb, ofs); + tmp = (gchar*)tvb_get_ephemeral_string(tvb, ofs + 2, ulen); + ti = proto_tree_add_text(tree, tvb, ofs, ulen +2, "UTF8 server name: %s", tmp); + sub_tree = proto_item_add_subtree(ti, ett_asp_utf8_name); + proto_tree_add_uint(sub_tree, hf_asp_server_utf8_name_len, tvb, ofs, 2, ulen); + ofs += 2; + proto_tree_add_string(sub_tree, hf_asp_server_utf8_name, tvb, ofs, ulen, tmp); + ofs += ulen; + } + /* FIXME: offset is not updated */ + return offset; +} + +/* ----------------------------- + PAP protocol cf. inside appletalk chap. 10 +*/ +#define PAD(x) { proto_tree_add_item(pap_tree, hf_pap_pad, tvb, offset, x, FALSE); offset += x; } + +static void +dissect_pap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + int offset = 0; + guint8 fn; + guint8 connID; + proto_tree *pap_tree = NULL; + proto_item *ti; + int len; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "PAP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + if (tree) { + ti = proto_tree_add_item(tree, proto_pap, tvb, offset, -1, FALSE); + pap_tree = proto_item_add_subtree(ti, ett_pap); + } + + connID = tvb_get_guint8(tvb, offset); + proto_tree_add_item(pap_tree, hf_pap_connid, tvb, offset, 1, FALSE); + offset++; + + fn = tvb_get_guint8(tvb, offset); + proto_tree_add_item(pap_tree, hf_pap_function, tvb, offset, 1, FALSE); + offset++; + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s ID: %d", + val_to_str(fn, pap_function_vals, "Unknown (0x%01x)"), connID); + } + switch(fn) { + case PAPOpenConn: + PAD(2); + proto_tree_add_item(pap_tree, hf_pap_socket, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(pap_tree, hf_pap_quantum, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(pap_tree, hf_pap_waittime, tvb, offset, 2, FALSE); + offset += 2; + break; + + case PAPOpenConnReply: + PAD(2); + proto_tree_add_item(pap_tree, hf_pap_socket, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(pap_tree, hf_pap_quantum, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(pap_tree, hf_pap_result, tvb, offset, 2, FALSE); + offset += 2; + offset = dissect_pascal_string(tvb, offset, pap_tree, hf_pap_status); + break; + + case PAPSendData: + proto_tree_add_item(pap_tree, hf_pap_seq, tvb, offset, 2, FALSE); + offset += 2; + break; + + case PAPData: + proto_tree_add_item(pap_tree, hf_pap_eof, tvb, offset, 1, FALSE); + offset++; + PAD(1); + /* follow by data */ + len = tvb_reported_length_remaining(tvb,offset); + call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,len), pinfo, tree); + break; + + case PAPTickle: + case PAPCloseConn: + case PAPCloseConnReply: + PAD(2); + break; + + case PAPSendStatus: + PAD(2); + break; + + case PAPStatus: + PAD(2); + PAD(4); + offset = dissect_pascal_string(tvb, offset, pap_tree, hf_pap_status); + break; + + default: /* unknown */ + break; + } +} + +/* ----------------------------- + ASP protocol cf. inside appletalk chap. 11 +*/ +static struct aspinfo * +get_transaction(tvbuff_t *tvb, packet_info *pinfo) +{ + struct aspinfo *aspinfo = pinfo->private_data; + conversation_t *conversation; + asp_request_key request_key, *new_request_key; + asp_request_val *request_val; + guint8 fn; + + conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + + if (conversation == NULL) + { + conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, pinfo->destport, 0); + } + + request_key.conversation = conversation->index; + memcpy(request_key.src, (!aspinfo->reply)?pinfo->src.data:pinfo->dst.data, 4); + request_key.seq = aspinfo->seq; + + request_val = (asp_request_val *) g_hash_table_lookup( + asp_request_hash, &request_key); + if (!request_val && !aspinfo->reply ) { + fn = tvb_get_guint8(tvb, 0); + new_request_key = se_alloc(sizeof(asp_request_key)); + *new_request_key = request_key; + + request_val = se_alloc(sizeof(asp_request_val)); + request_val->value = fn; + + g_hash_table_insert(asp_request_hash, new_request_key, + request_val); + } + + if (!request_val) + return NULL; + + aspinfo->command = request_val->value; + return aspinfo; +} + + +static void +dissect_asp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + struct aspinfo *aspinfo; + int offset = 0; + proto_tree *asp_tree = NULL; + proto_item *ti; + guint8 fn; + int len; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ASP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + aspinfo = get_transaction(tvb, pinfo); + if (!aspinfo) + return; + + fn = (guint8) aspinfo->command; + + if (check_col(pinfo->cinfo, COL_INFO)) { + if (aspinfo->reply) + col_add_fstr(pinfo->cinfo, COL_INFO, "Reply tid %u",aspinfo->seq); + else + col_add_fstr(pinfo->cinfo, COL_INFO, "Function: %s tid %u", + val_to_str(fn, asp_func_vals, "Unknown (0x%01x)"), aspinfo->seq); + } + + if (tree) { + ti = proto_tree_add_item(tree, proto_asp, tvb, offset, -1, FALSE); + asp_tree = proto_item_add_subtree(ti, ett_asp); + } + if (!aspinfo->reply) { + tvbuff_t *new_tvb; + /* let the called deal with asp_tree == NULL */ + + proto_tree_add_item(asp_tree, hf_asp_func, tvb, offset, 1, FALSE); + offset++; + switch(fn) { + case ASPFUNC_OPEN: + proto_tree_add_item(asp_tree, hf_asp_socket, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_version, tvb, offset, 2, FALSE); + offset += 2; + break; + case ASPFUNC_TICKLE: + case ASPFUNC_CLOSE: + proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 2, FALSE); + offset +=2; + break; + case ASPFUNC_STAT: + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 2, FALSE); + offset += 2; + break; + case ASPFUNC_ATTN: + proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_attn_code, tvb, offset, 2, FALSE); + offset +=2; + break; + case ASPFUNC_CMD: + case ASPFUNC_WRITE: + proto_item_set_len(asp_tree, 4); + proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_seq, tvb, offset, 2, FALSE); + offset += 2; + len = tvb_reported_length_remaining(tvb,offset); + new_tvb = tvb_new_subset(tvb, offset,-1,len); + call_dissector(afp_handle, new_tvb, pinfo, tree); + break; + case ASPFUNC_WRTCONT: + proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_seq, tvb, offset, 2, FALSE); + offset += 2; + proto_tree_add_item(asp_tree, hf_asp_size, tvb, offset, 2, FALSE); + offset += 2; + break; + default: + proto_item_set_len(asp_tree, 4); + offset += 3; + len = tvb_reported_length_remaining(tvb,offset); + call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,len), pinfo, tree); + break; + } + } + else { + tvbuff_t *new_tvb; + + proto_tree_add_uint(asp_tree, hf_asp_func, tvb, 0, 0, fn); + switch(fn) { + case ASPFUNC_OPEN: + proto_tree_add_item(asp_tree, hf_asp_socket, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_init_error, tvb, offset, 2, FALSE); + offset += 2; + break; + case ASPFUNC_CLOSE: + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 2, FALSE); + offset += 2; + break; + case ASPFUNC_STAT: + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 4, FALSE); + offset += 4; + dissect_asp_reply_get_status(tvb, pinfo, asp_tree, offset); + break; + case ASPFUNC_CMD: + case ASPFUNC_WRITE: + proto_item_set_len(asp_tree, 4); + aspinfo->code = tvb_get_ntohl(tvb, offset); + proto_tree_add_item(asp_tree, hf_asp_error, tvb, offset, 4, FALSE); + offset += 4; + len = tvb_reported_length_remaining(tvb,offset); + new_tvb = tvb_new_subset(tvb, offset,-1,len); + call_dissector(afp_handle, new_tvb, pinfo, tree); + break; + case ASPFUNC_TICKLE: + case ASPFUNC_WRTCONT: + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 4, FALSE); + /* fall */ + case ASPFUNC_ATTN: /* FIXME capture and spec disagree */ + default: + proto_item_set_len(asp_tree, 4); + offset += 4; + len = tvb_reported_length_remaining(tvb,offset); + call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,len), pinfo, tree); + break; + } + } +} + +/* ----------------------------- + ZIP protocol cf. inside appletalk chap. 8 +*/ +static void +dissect_atp_zip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + struct aspinfo *aspinfo; + int offset = 0; + proto_tree *zip_tree; + proto_tree *sub_tree; + proto_item *ti; + guint8 fn; + guint16 count; + guint8 len; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ZIP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + aspinfo = get_transaction(tvb, pinfo); + if (!aspinfo) + return; + + fn = (guint8) aspinfo->command; + + if (check_col(pinfo->cinfo, COL_INFO)) { + if (aspinfo->reply) + col_add_fstr(pinfo->cinfo, COL_INFO, "Reply tid %u",aspinfo->seq); + else + col_add_fstr(pinfo->cinfo, COL_INFO, "Function: %s tid %u", + val_to_str(fn, zip_atp_function_vals, "Unknown (0x%01x)"), aspinfo->seq); + } + + if (!tree) + return; + + ti = proto_tree_add_item(tree, proto_zip, tvb, offset, -1, FALSE); + zip_tree = proto_item_add_subtree(ti, ett_zip); + + if (!aspinfo->reply) { + proto_tree_add_item(zip_tree, hf_zip_atp_function, tvb, offset, 1, FALSE); + offset++; + switch(fn) { + case 7: /* start_index = 0 */ + case 8: + case 9: + proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(zip_tree, hf_zip_start_index, tvb, offset, 2, FALSE); + break; + } + } + else { + guint8 i; + + proto_tree_add_uint(zip_tree, hf_zip_atp_function, tvb, 0, 0, fn); + switch(fn) { + case 7: + case 8: + case 9: + proto_tree_add_item(zip_tree, hf_zip_last_flag, tvb, offset, 1, FALSE); + offset++; + + proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 1, FALSE); + offset++; + count = tvb_get_ntohs(tvb, offset); + ti = proto_tree_add_item(zip_tree, hf_zip_count, tvb, offset, 2, FALSE); + offset += 2; + sub_tree = proto_item_add_subtree(ti, ett_zip_zones_list); + for (i= 1; i <= count; i++) { + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(sub_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + offset += len +1; + } + break; + } + } +} + +static void +dissect_ddp_zip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_tree *zip_tree = NULL; + proto_item *ti; + guint8 fn; + guint8 len; + gint offset = 0; + proto_tree *flag_tree; + proto_tree *sub_tree; + proto_tree *net_tree; + guint8 flag; + guint16 net; + guint8 i; + guint8 count; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ZIP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + fn = tvb_get_guint8(tvb, 0); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(fn, zip_function_vals, "Unknown ZIP function (%02x)")); + } + + if (!tree) + return; + + ti = proto_tree_add_item(tree, proto_zip, tvb, 0, -1, FALSE); + zip_tree = proto_item_add_subtree(ti, ett_zip); + + proto_tree_add_item(zip_tree, hf_zip_function, tvb, offset, 1,FALSE); + offset++; + /* fn 1,7,2,8 are not tested */ + switch (fn) { + case 1: /* Query */ + count = tvb_get_guint8(tvb, offset); + ti = proto_tree_add_item(zip_tree, hf_zip_network_count, tvb, offset, 1, FALSE); + offset++; + sub_tree = proto_item_add_subtree(ti, ett_zip_network_list); + for (i= 1; i <= count; i++) { + proto_tree_add_item(sub_tree, hf_zip_network, tvb, offset, 2, FALSE); + offset += 2; + } + break; + case 7: /* Notify */ + flag = tvb_get_guint8(tvb, offset); + ti = proto_tree_add_text(zip_tree, tvb, offset , 1,"Flags : 0x%02x", flag); + flag_tree = proto_item_add_subtree(ti, ett_zip_flags); + proto_tree_add_item(flag_tree, hf_zip_flags_zone_invalid, tvb, offset, 1,FALSE); + proto_tree_add_item(flag_tree, hf_zip_flags_use_broadcast,tvb, offset, 1,FALSE); + proto_tree_add_item(flag_tree, hf_zip_flags_only_one_zone,tvb, offset, 1,FALSE); + offset++; + + proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 4, FALSE); + offset += 4; + + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + offset += len +1; + + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(zip_tree, hf_zip_multicast_length,tvb, offset, 1,FALSE); + offset++; + proto_tree_add_item(zip_tree, hf_zip_multicast_address,tvb, offset, len,FALSE); + offset += len; + + proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + break; + + case 2: /* Reply */ + case 8: /* Extended Reply */ + count = tvb_get_guint8(tvb, offset); + ti = proto_tree_add_item(zip_tree, hf_zip_network_count, tvb, offset, 1, FALSE); + offset++; + sub_tree = proto_item_add_subtree(ti, ett_zip_network_list); + for (i= 1; i <= count; i++) { + net = tvb_get_ntohs(tvb, offset); + ti = proto_tree_add_text(zip_tree, tvb, offset , 2, "Zone for network : %u", net); + net_tree = proto_item_add_subtree(ti, ett_zip_network_list); + proto_tree_add_item(net_tree, hf_zip_network, tvb, offset, 2, FALSE); + offset += 2; + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(net_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + offset += len +1; + } + break; + + case 5 : /* GetNetInfo request */ + proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 4, FALSE); + offset += 4; + proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + break; + + case 6 : /* GetNetInfo reply */ + flag = tvb_get_guint8(tvb, offset); + ti = proto_tree_add_text(zip_tree, tvb, offset , 1,"Flags : 0x%02x", flag); + flag_tree = proto_item_add_subtree(ti, ett_zip_flags); + proto_tree_add_item(flag_tree, hf_zip_flags_zone_invalid, tvb, offset, 1,FALSE); + proto_tree_add_item(flag_tree, hf_zip_flags_use_broadcast,tvb, offset, 1,FALSE); + proto_tree_add_item(flag_tree, hf_zip_flags_only_one_zone,tvb, offset, 1,FALSE); + offset++; + + proto_tree_add_item(zip_tree, hf_zip_network_start, tvb, offset, 2, FALSE); + offset += 2; + + proto_tree_add_item(zip_tree, hf_zip_network_end, tvb, offset, 2, FALSE); + offset += 2; + + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + offset += len +1; + + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(zip_tree, hf_zip_multicast_length,tvb, offset, 1,FALSE); + offset++; + proto_tree_add_item(zip_tree, hf_zip_multicast_address,tvb, offset, len,FALSE); + offset += len; + if ((flag & 0x80) != 0) + proto_tree_add_item(zip_tree, hf_zip_default_zone, tvb, offset, 1,FALSE); + break; + + default: + break; + } +} + +static void +dissect_ddp_short(tvbuff_t *tvb, packet_info *pinfo, guint8 dnode, + guint8 snode, proto_tree *tree) +{ + guint16 len; + guint8 dport; + guint8 sport; + guint8 type; + proto_tree *ddp_tree = NULL; + proto_item *ti; + static struct atalk_ddp_addr src, dst; + tvbuff_t *new_tvb; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "DDP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + if (tree) { + ti = proto_tree_add_item(tree, proto_ddp, tvb, 0, DDP_SHORT_HEADER_SIZE, + FALSE); + ddp_tree = proto_item_add_subtree(ti, ett_ddp); + } + len = tvb_get_ntohs(tvb, 0); + if (tree) + proto_tree_add_uint(ddp_tree, hf_ddp_len, tvb, 0, 2, len); + dport = tvb_get_guint8(tvb, 2); + if (tree) + proto_tree_add_uint(ddp_tree, hf_ddp_dst_socket, tvb, 2, 1, dport); + sport = tvb_get_guint8(tvb, 3); + if (tree) + proto_tree_add_uint(ddp_tree, hf_ddp_src_socket, tvb, 3, 1, sport); + type = tvb_get_guint8(tvb, 4); + + src.net = 0; + src.node = snode; + dst.net = 0; + dst.node = dnode; + SET_ADDRESS(&pinfo->net_src, AT_ATALK, sizeof src, (guint8 *)&src); + SET_ADDRESS(&pinfo->src, AT_ATALK, sizeof src, (guint8 *)&src); + SET_ADDRESS(&pinfo->net_dst, AT_ATALK, sizeof dst, (guint8 *)&dst); + SET_ADDRESS(&pinfo->dst, AT_ATALK, sizeof dst, (guint8 *)&dst); + + pinfo->ptype = PT_DDP; + pinfo->destport = dport; + pinfo->srcport = sport; + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(type, op_vals, "Unknown DDP protocol (%02x)")); + } + if (tree) { + proto_tree_add_string_hidden(ddp_tree, hf_ddp_src, tvb, + 4, 3, atalk_addr_to_str(&src)); + proto_tree_add_string_hidden(ddp_tree, hf_ddp_dst, tvb, + 6, 3, atalk_addr_to_str(&dst)); + + proto_tree_add_uint(ddp_tree, hf_ddp_type, tvb, 4, 1, type); + } + new_tvb = tvb_new_subset(tvb, DDP_SHORT_HEADER_SIZE, -1, -1); + + if (!dissector_try_port(ddp_dissector_table, type, new_tvb, pinfo, tree)) + call_dissector(data_handle,new_tvb, pinfo, tree); +} + +static void +dissect_ddp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + e_ddp ddp; + proto_tree *ddp_tree; + proto_item *ti; + static struct atalk_ddp_addr src, dst; + tvbuff_t *new_tvb; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "DDP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + tvb_memcpy(tvb, (guint8 *)&ddp, 0, sizeof(e_ddp)); + ddp.dnet=g_ntohs(ddp.dnet); + ddp.snet=g_ntohs(ddp.snet); + ddp.sum=g_ntohs(ddp.sum); + ddp.hops_len=g_ntohs(ddp.hops_len); + + src.net = ddp.snet; + src.node = ddp.snode; + dst.net = ddp.dnet; + dst.node = ddp.dnode; + SET_ADDRESS(&pinfo->net_src, AT_ATALK, sizeof src, (guint8 *)&src); + SET_ADDRESS(&pinfo->src, AT_ATALK, sizeof src, (guint8 *)&src); + SET_ADDRESS(&pinfo->net_dst, AT_ATALK, sizeof dst, (guint8 *)&dst); + SET_ADDRESS(&pinfo->dst, AT_ATALK, sizeof dst, (guint8 *)&dst); + + pinfo->ptype = PT_DDP; + pinfo->destport = ddp.dport; + pinfo->srcport = ddp.sport; + + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(ddp.type, op_vals, "Unknown DDP protocol (%02x)")); + + if (tree) { + ti = proto_tree_add_item(tree, proto_ddp, tvb, 0, DDP_HEADER_SIZE, + FALSE); + ddp_tree = proto_item_add_subtree(ti, ett_ddp); + + proto_tree_add_string_hidden(ddp_tree, hf_ddp_src, tvb, + 4, 3, atalk_addr_to_str(&src)); + proto_tree_add_string_hidden(ddp_tree, hf_ddp_dst, tvb, + 6, 3, atalk_addr_to_str(&dst)); + + proto_tree_add_uint(ddp_tree, hf_ddp_hopcount, tvb, 0, 1, + ddp_hops(ddp.hops_len)); + proto_tree_add_uint(ddp_tree, hf_ddp_len, tvb, 0, 2, + ddp_len(ddp.hops_len)); + proto_tree_add_uint(ddp_tree, hf_ddp_checksum, tvb, 2, 2, + ddp.sum); + proto_tree_add_uint(ddp_tree, hf_ddp_dst_net, tvb, 4, 2, + ddp.dnet); + proto_tree_add_uint(ddp_tree, hf_ddp_src_net, tvb, 6, 2, + ddp.snet); + proto_tree_add_uint(ddp_tree, hf_ddp_dst_node, tvb, 8, 1, + ddp.dnode); + proto_tree_add_uint(ddp_tree, hf_ddp_src_node, tvb, 9, 1, + ddp.snode); + proto_tree_add_uint(ddp_tree, hf_ddp_dst_socket, tvb, 10, 1, + ddp.dport); + proto_tree_add_uint(ddp_tree, hf_ddp_src_socket, tvb, 11, 1, + ddp.sport); + proto_tree_add_uint(ddp_tree, hf_ddp_type, tvb, 12, 1, + ddp.type); + } + + new_tvb = tvb_new_subset(tvb, DDP_HEADER_SIZE, -1, -1); + + if (!dissector_try_port(ddp_dissector_table, ddp.type, new_tvb, pinfo, tree)) + call_dissector(data_handle,new_tvb, pinfo, tree); +} + +static const value_string llap_type_vals[] = { + {0x01, "Short DDP"}, + {0x02, "DDP" }, + {0x81, "Enquiry"}, + {0x82, "Acknowledgement"}, + {0x84, "RTS"}, + {0x85, "CTS"}, + {0, NULL} +}; + +void +capture_llap(packet_counts *ld) +{ + ld->other++; +} + +static void +dissect_llap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + guint8 dnode; + guint8 snode; + guint8 type; + proto_tree *llap_tree = NULL; + proto_item *ti; + tvbuff_t *new_tvb; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "LLAP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + if (tree) { + ti = proto_tree_add_item(tree, proto_llap, tvb, 0, 3, FALSE); + llap_tree = proto_item_add_subtree(ti, ett_llap); + } + + dnode = tvb_get_guint8(tvb, 0); + if (tree) + proto_tree_add_uint(llap_tree, hf_llap_dst, tvb, 0, 1, dnode); + snode = tvb_get_guint8(tvb, 1); + if (tree) + proto_tree_add_uint(llap_tree, hf_llap_src, tvb, 1, 1, snode); + type = tvb_get_guint8(tvb, 2); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(type, llap_type_vals, "Unknown LLAP type (%02x)")); + } + if (tree) + proto_tree_add_uint(llap_tree, hf_llap_type, tvb, 2, 1, type); + + new_tvb = tvb_new_subset(tvb, 3, -1, -1); + + switch (type) { + + case 0x01: + if (proto_is_protocol_enabled(find_protocol_by_id(proto_ddp))) { + pinfo->current_proto = "DDP"; + dissect_ddp_short(new_tvb, pinfo, dnode, snode, tree); + return; + } + + case 0x02: + if (call_dissector(ddp_handle, new_tvb, pinfo, tree)) + return; + } + call_dissector(data_handle,new_tvb, pinfo, tree); +} + +static void +atp_init(void) +{ + /* fragment */ + fragment_table_init(&atp_fragment_table); + reassembled_table_init(&atp_reassembled_table); + /* bitmap */ + if (atp_request_hash) + g_hash_table_destroy(atp_request_hash); + + atp_request_hash = g_hash_table_new(asp_hash, asp_equal); + +} + +static void +asp_reinit( void) +{ + + if (asp_request_hash) + g_hash_table_destroy(asp_request_hash); + + asp_request_hash = g_hash_table_new(asp_hash, asp_equal); + +} + +void +proto_register_atalk(void) +{ + static hf_register_info hf_llap[] = { + { &hf_llap_dst, + { "Destination Node", "llap.dst", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_llap_src, + { "Source Node", "llap.src", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_llap_type, + { "Type", "llap.type", FT_UINT8, BASE_HEX, VALS(llap_type_vals), 0x0, + "", HFILL }}, + }; + + static hf_register_info hf_ddp[] = { + { &hf_ddp_hopcount, + { "Hop count", "ddp.hopcount", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_len, + { "Datagram length", "ddp.len", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_checksum, + { "Checksum", "ddp.checksum", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_dst, + { "Destination address", "ddp.dst", FT_STRING, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_dst_net, + { "Destination Net", "ddp.dst.net", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_src, + { "Source address", "ddp.src", FT_STRING, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_src_net, + { "Source Net", "ddp.src.net", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_dst_node, + { "Destination Node", "ddp.dst.node", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_src_node, + { "Source Node", "ddp.src.node", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_dst_socket, + { "Destination Socket", "ddp.dst_socket", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_src_socket, + { "Source Socket", "ddp.src_socket", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_type, + { "Protocol type", "ddp.type", FT_UINT8, BASE_DEC, VALS(op_vals), 0x0, + "", HFILL }}, + }; + + static hf_register_info hf_nbp[] = { + { &hf_nbp_op, + { "Operation", "nbp.op", FT_UINT8, BASE_DEC, + VALS(nbp_op_vals), 0xF0, "Operation", HFILL }}, + { &hf_nbp_info, + { "Info", "nbp.info", FT_UINT8, BASE_HEX, + NULL, 0x0, "Info", HFILL }}, + { &hf_nbp_count, + { "Count", "nbp.count", FT_UINT8, BASE_DEC, + NULL, 0x0F, "Count", HFILL }}, + { &hf_nbp_node_net, + { "Network", "nbp.net", FT_UINT16, BASE_DEC, + NULL, 0x0, "Network", HFILL }}, + { &hf_nbp_node_node, + { "Node", "nbp.node", FT_UINT8, BASE_DEC, + NULL, 0x0, "Node", HFILL }}, + { &hf_nbp_node_port, + { "Port", "nbp.port", FT_UINT8, BASE_DEC, + NULL, 0x0, "Port", HFILL }}, + { &hf_nbp_node_enum, + { "Enumerator", "nbp.enum", FT_UINT8, BASE_DEC, + NULL, 0x0, "Enumerator", HFILL }}, + { &hf_nbp_node_object, + { "Object", "nbp.object", FT_STRING, BASE_DEC, + NULL, 0x0, "Object", HFILL }}, + { &hf_nbp_node_type, + { "Type", "nbp.type", FT_STRING, BASE_DEC, + NULL, 0x0, "Type", HFILL }}, + { &hf_nbp_node_zone, + { "Zone", "nbp.zone", FT_STRING, BASE_DEC, + NULL, 0x0, "Zone", HFILL }}, + { &hf_nbp_tid, + { "Transaction ID", "nbp.tid", FT_UINT8, BASE_DEC, + NULL, 0x0, "Transaction ID", HFILL }} + }; + + static hf_register_info hf_rtmp[] = { + { &hf_rtmp_net, + { "Net", "rtmp.net", FT_UINT16, BASE_DEC, + NULL, 0x0, "Net", HFILL }}, + { &hf_rtmp_node, + { "Node", "nbp.nodeid", FT_UINT8, BASE_DEC, + NULL, 0x0, "Node", HFILL }}, + { &hf_rtmp_node_len, + { "Node Length", "nbp.nodeid.length", FT_UINT8, BASE_DEC, + NULL, 0x0, "Node Length", HFILL }}, + { &hf_rtmp_tuple_net, + { "Net", "rtmp.tuple.net", FT_UINT16, BASE_DEC, + NULL, 0x0, "Net", HFILL }}, + { &hf_rtmp_tuple_range_start, + { "Range Start", "rtmp.tuple.range_start", FT_UINT16, BASE_DEC, + NULL, 0x0, "Range Start", HFILL }}, + { &hf_rtmp_tuple_range_end, + { "Range End", "rtmp.tuple.range_end", FT_UINT16, BASE_DEC, + NULL, 0x0, "Range End", HFILL }}, + { &hf_rtmp_tuple_dist, + { "Distance", "rtmp.tuple.dist", FT_UINT16, BASE_DEC, + NULL, 0x0, "Distance", HFILL }}, + { &hf_rtmp_function, + { "Function", "rtmp.function", FT_UINT8, BASE_DEC, + VALS(rtmp_function_vals), 0x0, "Request Function", HFILL }} + }; + + static hf_register_info hf_atp[] = { + { &hf_atp_ctrlinfo, + { "Control info", "atp.ctrlinfo", FT_UINT8, BASE_HEX, + NULL, 0, "control info", HFILL }}, + + { &hf_atp_function, + { "Function", "atp.function", FT_UINT8, BASE_DEC, + VALS(atp_function_vals), ATP_FUNCMASK, "function code", HFILL }}, + + + { &hf_atp_xo, + { "XO", "atp.xo", FT_BOOLEAN, 8, + NULL, ATP_XO, "Exactly-once flag", HFILL }}, + + { &hf_atp_eom, + { "EOM", "atp.eom", FT_BOOLEAN, 8, + NULL, ATP_EOM, "End-of-message", HFILL }}, + + { &hf_atp_sts, + { "STS", "atp.sts", FT_BOOLEAN, 8, + NULL, ATP_STS, "Send transaction status", HFILL }}, + + { &hf_atp_treltimer, + { "TRel timer", "atp.treltimer", FT_UINT8, BASE_DEC, + VALS(atp_trel_timer_vals), 0x07, "TRel timer", HFILL }}, + + { &hf_atp_bitmap, + { "Bitmap", "atp.bitmap", FT_UINT8, BASE_HEX, + NULL, 0x0, "Bitmap or sequence number", HFILL }}, + + { &hf_atp_tid, + { "TID", "atp.tid", FT_UINT16, BASE_DEC, + NULL, 0x0, "Transaction id", HFILL }}, + { &hf_atp_user_bytes, + { "User bytes", "atp.user_bytes", FT_UINT32, BASE_HEX, + NULL, 0x0, "User bytes", HFILL }}, + + { &hf_atp_segment_overlap, + { "Segment overlap", "atp.segment.overlap", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Segment overlaps with other segments", HFILL }}, + + { &hf_atp_segment_overlap_conflict, + { "Conflicting data in segment overlap", "atp.segment.overlap.conflict", + FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Overlapping segments contained conflicting data", HFILL }}, + + { &hf_atp_segment_multiple_tails, + { "Multiple tail segments found", "atp.segment.multipletails", + FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Several tails were found when desegmenting the packet", HFILL }}, + + { &hf_atp_segment_too_long_segment, + { "Segment too long", "atp.segment.toolongsegment", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Segment contained data past end of packet", HFILL }}, + + { &hf_atp_segment_error, + {"Desegmentation error", "atp.segment.error", FT_FRAMENUM, BASE_NONE, + NULL, 0x0, "Desegmentation error due to illegal segments", HFILL }}, + + { &hf_atp_segment, + { "ATP Fragment", "atp.fragment", FT_FRAMENUM, BASE_NONE, + NULL, 0x0, "ATP Fragment", HFILL }}, + + { &hf_atp_segments, + { "ATP Fragments", "atp.fragments", FT_NONE, BASE_NONE, + NULL, 0x0, "ATP Fragments", HFILL }}, + + { &hf_atp_reassembled_in, + { "Reassembled ATP in frame", "atp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, + "This ATP packet is reassembled in this frame", HFILL }} + }; + + static hf_register_info hf_asp[] = { + { &hf_asp_func, + { "asp function", "asp.function", FT_UINT8, BASE_DEC, + VALS(asp_func_vals), 0, "asp function", HFILL }}, + + { &hf_asp_error, + { "asp error", "asp.error", FT_INT32, BASE_DEC, + VALS(asp_error_vals), 0, "return error code", HFILL }}, + + { &hf_asp_version, + { "Version", "asp.version", FT_UINT16, BASE_HEX, + NULL, 0, "asp version", HFILL }}, + + { &hf_asp_attn_code, + { "Attn code", "asp.attn_code", FT_UINT16, BASE_HEX, + NULL, 0, "asp attention code", HFILL }}, + + { &hf_asp_init_error, + { "Error", "asp.init_error", FT_UINT16, BASE_DEC, + NULL, 0, "asp init error", HFILL }}, + + { &hf_asp_session_id, + { "Session ID", "asp.session_id", FT_UINT8, BASE_DEC, + NULL, 0, "asp session id", HFILL }}, + + { &hf_asp_socket, + { "Socket", "asp.socket", FT_UINT8, BASE_DEC, + NULL, 0, "asp socket", HFILL }}, + + { &hf_asp_seq, + { "Sequence", "asp.seq", FT_UINT16, BASE_DEC, + NULL, 0, "asp sequence number", HFILL }}, + + { &hf_asp_size, + { "size", "asp.size", FT_UINT16, BASE_DEC, + NULL, 0, "asp available size for reply", HFILL }}, + + { &hf_asp_zero_value, + { "Pad (0)", "asp.zero_value", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Pad", HFILL }}, + + /* asp ,dsi, afp */ + { &hf_asp_server_name, + { "Server name", "asp.server_name", + FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "Server name", HFILL }}, + + { &hf_asp_server_type, + { "Server type", "asp.server_type", + FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "Server type", HFILL }}, + + { &hf_asp_server_vers, + { "AFP version", "asp.server_vers", + FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "AFP version", HFILL }}, + + { &hf_asp_server_uams, + { "UAM", "asp.server_uams", + FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "UAM", HFILL }}, + + { &hf_asp_server_icon, + { "Icon bitmap", "asp.server_icon", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Server icon bitmap", HFILL }}, + + { &hf_asp_server_directory, + { "Directory service", "asp.server_directory", + FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "Server directory service", HFILL }}, + + { &hf_asp_server_signature, + { "Server signature", "asp.server_signature", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Server signature", HFILL }}, + + { &hf_asp_server_flag, + { "Flag", "asp.server_flag", + FT_UINT16, BASE_HEX, NULL, 0x0, + "Server capabilities flag", HFILL }}, + { &hf_asp_server_flag_copyfile, + { "Support copyfile", "asp.server_flag.copyfile", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_COPY, + "Server support copyfile", HFILL }}, + { &hf_asp_server_flag_passwd, + { "Support change password", "asp.server_flag.passwd", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_PASSWD, + "Server support change password", HFILL }}, + { &hf_asp_server_flag_no_save_passwd, + { "Don't allow save password", "asp.server_flag.no_save_passwd", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_NOSAVEPASSWD, + "Don't allow save password", HFILL }}, + { &hf_asp_server_flag_srv_msg, + { "Support server message", "asp.server_flag.srv_msg", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVMSGS, + "Support server message", HFILL }}, + { &hf_asp_server_flag_srv_sig, + { "Support server signature", "asp.server_flag.srv_sig", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVSIGNATURE, + "Support server signature", HFILL }}, + { &hf_asp_server_flag_tcpip, + { "Support TCP/IP", "asp.server_flag.tcpip", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_TCPIP, + "Server support TCP/IP", HFILL }}, + { &hf_asp_server_flag_notify, + { "Support server notifications", "asp.server_flag.notify", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVNOTIFY, + "Server support notifications", HFILL }}, + { &hf_asp_server_flag_reconnect, + { "Support server reconnect", "asp.server_flag.reconnect", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVRECONNECT, + "Server support reconnect", HFILL }}, + { &hf_asp_server_flag_directory, + { "Support directory services", "asp.server_flag.directory", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVDIRECTORY, + "Server support directory services", HFILL }}, + { &hf_asp_server_flag_utf8_name, + { "Support UTF8 server name", "asp.server_flag.utf8_name", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVUTF8, + "Server support UTF8 server name", HFILL }}, + { &hf_asp_server_flag_fast_copy, + { "Support fast copy", "asp.server_flag.fast_copy", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_FASTBOZO, + "Server support fast copy", HFILL }}, + + { &hf_asp_server_addr_len, + { "Length", "asp.server_addr.len", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Address length.", HFILL }}, + + { &hf_asp_server_addr_type, + { "Type", "asp.server_addr.type", + FT_UINT8, BASE_DEC, VALS(afp_server_addr_type_vals), 0x0, + "Address type.", HFILL }}, + + { &hf_asp_server_addr_value, + { "Value", "asp.server_addr.value", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Address value", HFILL }}, + + { &hf_asp_server_utf8_name_len, + { "Server name length", "asp.server_utf8_name_len", + FT_UINT16, BASE_DEC, NULL, 0x0, + "UTF8 server name length", HFILL }}, + + { &hf_asp_server_utf8_name, + { "Server name (UTF8)", "asp.server_utf8_name", + FT_STRING, BASE_NONE, NULL, 0x0, + "Server name (UTF8)", HFILL }}, + }; + + static hf_register_info hf_zip[] = { + { &hf_zip_function, + { "Function", "zip.function", FT_UINT8, BASE_DEC, VALS(zip_function_vals), 0x0, + "ZIP function", HFILL }}, + + { &hf_zip_zero_value, + { "Pad (0)", "zip.zero_value",FT_BYTES, BASE_HEX, NULL, 0x0, + "Pad", HFILL }}, + + { &hf_zip_atp_function, + { "Function", "zip.atp_function", FT_UINT8, BASE_DEC, VALS(zip_atp_function_vals), 0x0, + "", HFILL }}, + + { &hf_zip_start_index, + { "Start index", "zip.start_index", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_zip_count, + { "Count", "zip.count", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_zip_network_count, + { "Count", "zip.network_count", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + { &hf_zip_network, + { "Network","zip.network", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + { &hf_zip_network_start, + { "Network start","zip.network_start", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + { &hf_zip_network_end, + { "Network end", "zip.network_end", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_zip_flags, + { "Flags", "zip.flags", FT_BOOLEAN, 8, NULL, 0xC0, + "", HFILL }}, + + { &hf_zip_last_flag, + { "Last Flag", "zip.last_flag", FT_BOOLEAN, 8, NULL, 0, + "Non zero if contains last zone name in the zone list", HFILL }}, + + { &hf_zip_flags_zone_invalid, + { "Zone invalid", "zip.flags.zone_invalid", FT_BOOLEAN, 8, NULL, 0x80, + "", HFILL }}, + + { &hf_zip_flags_use_broadcast, + { "Use broadcast","zip.flags.use_broadcast", FT_BOOLEAN, 8, NULL, 0x40, + "", HFILL }}, + + { &hf_zip_flags_only_one_zone, + { "Only one zone","zip.flags.only_one_zone", FT_BOOLEAN, 8, NULL, 0x20, + "", HFILL }}, + + { &hf_zip_zone_name, + { "Zone", "zip.zone_name", FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_zip_default_zone, + { "Default zone", "zip.default_zone",FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_zip_multicast_length, + { "Multicast length", "zip.multicast_length", FT_UINT8, BASE_DEC, NULL, 0x0, + "Multicast address length", HFILL }}, + + { &hf_zip_multicast_address, + { "Multicast address", "zip.multicast_address",FT_BYTES, BASE_HEX, NULL, 0x0, + "Multicast address", HFILL }}, + + }; + + static hf_register_info hf_pap[] = { + { &hf_pap_connid, + { "ConnID", "pap.connid", FT_UINT8, BASE_DEC, NULL, 0x0, + "PAP connection ID", HFILL }}, + + { &hf_pap_function, + { "Function", "pap.function", FT_UINT8, BASE_DEC, VALS(pap_function_vals), 0x0, + "PAP function", HFILL }}, + + { &hf_pap_socket, + { "Socket", "pap.socket", FT_UINT8, BASE_DEC, NULL, 0x0, + "ATP responding socket number", HFILL }}, + + { &hf_pap_quantum, + { "Quantum", "pap.quantum", FT_UINT8, BASE_DEC, NULL, 0x0, + "Flow quantum", HFILL }}, + + { &hf_pap_waittime, + { "Wait time", "pap.quantum", FT_UINT16, BASE_DEC, NULL, 0x0, + "Wait time", HFILL }}, + + { &hf_pap_result, + { "Result", "pap.quantum", FT_UINT16, BASE_DEC, NULL, 0x0, + "Result", HFILL }}, + + { &hf_pap_seq, + { "Sequence", "pap.seq", FT_UINT16, BASE_DEC, NULL, 0x0, + "Sequence number", HFILL }}, + + { &hf_pap_status, + { "Status", "pap.status", FT_STRING, BASE_DEC, NULL, 0x0, + "Printer status", HFILL }}, + + { &hf_pap_eof, + { "EOF", "pap.eof", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "EOF", HFILL }}, + + { &hf_pap_pad, + { "Pad", "pad.pad", FT_NONE, BASE_NONE, NULL, 0, + "Pad Byte", HFILL }}, + + }; + + static gint *ett[] = { + &ett_llap, + &ett_ddp, + &ett_atp, + &ett_atp_info, + &ett_atp_segments, + &ett_atp_segment, + &ett_asp, + &ett_pap, + + /* asp dsi afp */ + &ett_asp_status, + &ett_asp_status_server_flag, + &ett_asp_vers, + &ett_asp_uams, + &ett_asp_addr, + &ett_asp_addr_line, + &ett_asp_directory, + &ett_asp_utf8_name, + + &ett_nbp, + &ett_nbp_info, + &ett_nbp_node, + &ett_pstring, + &ett_rtmp, + &ett_rtmp_tuple, + + &ett_zip, + &ett_zip_flags, + &ett_zip_zones_list, + &ett_zip_network_list, + }; + module_t *atp_module; + + proto_llap = proto_register_protocol("LocalTalk Link Access Protocol", "LLAP", "llap"); + proto_register_field_array(proto_llap, hf_llap, array_length(hf_llap)); + + proto_ddp = proto_register_protocol("Datagram Delivery Protocol", "DDP", "ddp"); + proto_register_field_array(proto_ddp, hf_ddp, array_length(hf_ddp)); + + proto_nbp = proto_register_protocol("Name Binding Protocol", "NBP", "nbp"); + proto_register_field_array(proto_nbp, hf_nbp, array_length(hf_nbp)); + + proto_atp = proto_register_protocol("AppleTalk Transaction Protocol packet", "ATP", "atp"); + proto_register_field_array(proto_atp, hf_atp, array_length(hf_atp)); + + proto_asp = proto_register_protocol("AppleTalk Session Protocol", "ASP", "asp"); + proto_register_field_array(proto_asp, hf_asp, array_length(hf_asp)); + + proto_pap = proto_register_protocol("Printer Access Protocol", "PAP", "apap"); + proto_register_field_array(proto_pap, hf_pap, array_length(hf_pap)); + + proto_zip = proto_register_protocol("Zone Information Protocol", "ZIP", "zip"); + proto_register_field_array(proto_zip, hf_zip, array_length(hf_zip)); + + atp_module = prefs_register_protocol(proto_atp, NULL); + prefs_register_bool_preference(atp_module, "desegment", + "Reassemble ATP messages spanning multiple DDP packets", + "Whether the ATP dissector should reassemble messages spanning multiple DDP packets", + &atp_defragment); + + proto_rtmp = proto_register_protocol("Routing Table Maintenance Protocol", + "RTMP", "rtmp"); + proto_register_field_array(proto_rtmp, hf_rtmp, array_length(hf_rtmp)); + + proto_register_subtree_array(ett, array_length(ett)); + + /* subdissector code */ + ddp_dissector_table = register_dissector_table("ddp.type", "DDP packet type", + FT_UINT8, BASE_HEX); +} + +void +proto_reg_handoff_atalk(void) +{ + dissector_handle_t nbp_handle, rtmp_request_handle; + dissector_handle_t atp_handle; + dissector_handle_t zip_ddp_handle; + dissector_handle_t rtmp_data_handle, llap_handle; + + ddp_handle = create_dissector_handle(dissect_ddp, proto_ddp); + dissector_add("ethertype", ETHERTYPE_ATALK, ddp_handle); + dissector_add("chdlctype", ETHERTYPE_ATALK, ddp_handle); + dissector_add("ppp.protocol", PPP_AT, ddp_handle); + dissector_add("null.type", BSD_AF_APPLETALK, ddp_handle); + dissector_add("arcnet.protocol_id", ARCNET_PROTO_APPLETALK, ddp_handle); + + nbp_handle = create_dissector_handle(dissect_nbp, proto_nbp); + dissector_add("ddp.type", DDP_NBP, nbp_handle); + + atp_handle = create_dissector_handle(dissect_atp, proto_atp); + dissector_add("ddp.type", DDP_ATP, atp_handle); + + asp_handle = create_dissector_handle(dissect_asp, proto_asp); + pap_handle = create_dissector_handle(dissect_pap, proto_pap); + + rtmp_request_handle = create_dissector_handle(dissect_rtmp_request, proto_rtmp); + rtmp_data_handle = create_dissector_handle(dissect_rtmp_data, proto_rtmp); + dissector_add("ddp.type", DDP_RTMPREQ, rtmp_request_handle); + dissector_add("ddp.type", DDP_RTMPDATA, rtmp_data_handle); + + zip_ddp_handle = create_dissector_handle(dissect_ddp_zip, proto_zip); + dissector_add("ddp.type", DDP_ZIP, zip_ddp_handle); + + zip_atp_handle = create_dissector_handle(dissect_atp_zip, proto_zip); + + llap_handle = create_dissector_handle(dissect_llap, proto_llap); + dissector_add("wtap_encap", WTAP_ENCAP_LOCALTALK, llap_handle); + + register_init_routine( atp_init); + register_init_routine( &asp_reinit); + + afp_handle = find_dissector("afp"); + data_handle = find_dissector("data"); +} diff --git a/src/main/resource/testFiles/wireshark/prevFiles/prev_bb4516_9597f6_epan#sigcomp-udvm.c b/src/main/resource/testFiles/wireshark/prevFiles/prev_bb4516_9597f6_epan#sigcomp-udvm.c new file mode 100644 index 0000000..0074bc1 --- /dev/null +++ b/src/main/resource/testFiles/wireshark/prevFiles/prev_bb4516_9597f6_epan#sigcomp-udvm.c @@ -0,0 +1,3204 @@ +/* sigcomp-udvm.c + * Routines making up the Universal Decompressor Virtual Machine (UDVM) used for + * Signaling Compression (SigComp) dissection. + * Copyright 2004, Anders Broman + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * References: + * http://www.ietf.org/rfc/rfc3320.txt?number=3320 + * http://www.ietf.org/rfc/rfc3321.txt?number=3321 + * Useful links : + * http://www.ietf.org/internet-drafts/draft-ietf-rohc-sigcomp-impl-guide-05.txt + * http://www.ietf.org/internet-drafts/draft-ietf-rohc-sigcomp-sip-01.txt + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "packet.h" +#include "strutil.h" +#include "sigcomp-udvm.h" +#include "sigcomp_state_hdlr.h" +#include "sha1.h" +#include "crc16.h" + +#define SIGCOMP_INSTR_DECOMPRESSION_FAILURE 0 +#define SIGCOMP_INSTR_AND 1 +#define SIGCOMP_INSTR_OR 2 +#define SIGCOMP_INSTR_NOT 3 +#define SIGCOMP_INSTR_LSHIFT 4 +#define SIGCOMP_INSTR_RSHIFT 5 +#define SIGCOMP_INSTR_ADD 6 +#define SIGCOMP_INSTR_SUBTRACT 7 +#define SIGCOMP_INSTR_MULTIPLY 8 +#define SIGCOMP_INSTR_DIVIDE 9 +#define SIGCOMP_INSTR_REMAINDER 10 +#define SIGCOMP_INSTR_SORT_ASCENDING 11 +#define SIGCOMP_INSTR_SORT_DESCENDING 12 +#define SIGCOMP_INSTR_SHA_1 13 +#define SIGCOMP_INSTR_LOAD 14 +#define SIGCOMP_INSTR_MULTILOAD 15 +#define SIGCOMP_INSTR_PUSH 16 +#define SIGCOMP_INSTR_POP 17 +#define SIGCOMP_INSTR_COPY 18 +#define SIGCOMP_INSTR_COPY_LITERAL 19 +#define SIGCOMP_INSTR_COPY_OFFSET 20 +#define SIGCOMP_INSTR_MEMSET 21 +#define SIGCOMP_INSTR_JUMP 22 +#define SIGCOMP_INSTR_COMPARE 23 +#define SIGCOMP_INSTR_CALL 24 +#define SIGCOMP_INSTR_RETURN 25 +#define SIGCOMP_INSTR_SWITCH 26 +#define SIGCOMP_INSTR_CRC 27 +#define SIGCOMP_INSTR_INPUT_BYTES 28 +#define SIGCOMP_INSTR_INPUT_BITS 29 +#define SIGCOMP_INSTR_INPUT_HUFFMAN 30 +#define SIGCOMP_INSTR_STATE_ACCESS 31 +#define SIGCOMP_INSTR_STATE_CREATE 32 +#define SIGCOMP_INSTR_STATE_FREE 33 +#define SIGCOMP_INSTR_OUTPUT 34 +#define SIGCOMP_INSTR_END_MESSAGE 35 + + +static gboolean print_level_1; +static gboolean print_level_2; +static gboolean print_level_3; +static gint show_instr_detail_level; + +/* Internal result code values of decompression failures */ +const value_string result_code_vals[] = { + { 0, "No decomprssion failure" }, + { 1, "Partial state length less than 6 or greater than 20 bytes long" }, + { 2, "No state match" }, + { 3, "state_begin + state_length > size of state" }, + { 4, "Operand_2 is Zero" }, + { 5, "Switch statement failed j >= n" }, + { 6, "Atempt to jump outside of UDVM memory" }, + { 7, "L in input-bits > 16" }, + { 8, "input_bit_order > 7" }, + { 9, "Instruction Decompression failure encountered" }, + {10, "Input huffman failed j > n" }, + {11, "Input bits requested beyond end of message" }, + {12, "more than four state creation requests are made before the END-MESSAGE instruction" }, + {13, "state_retention_priority is 65535" }, + {14, "Input bytes requested beond end of message" }, + {15, "Maximum number of UDVM cycles reached" }, + {16, "UDVM stack underflow" }, + { 255, "This branch isn't coded yet" }, + { 0, NULL } +}; + +static int decode_udvm_literal_operand(guint8 *buff,guint operand_address, guint16 *value); +static int dissect_udvm_reference_operand(guint8 *buff,guint operand_address, guint16 *value, guint *result_dest); +static int decode_udvm_multitype_operand(guint8 *buff,guint operand_address,guint16 *value); +static int decode_udvm_address_operand(guint8 *buff,guint operand_address, guint16 *value,guint current_address); +static int decomp_dispatch_get_bits(tvbuff_t *message_tvb,proto_tree *udvm_tree,guint8 bit_order, + guint8 *buff,guint16 *old_input_bit_order, guint16 *remaining_bits, + guint16 *input_bits, guint *input_address, guint16 length, guint16 *result_code,guint msg_end); + + +tvbuff_t* +decompress_sigcomp_message(tvbuff_t *bytecode_tvb, tvbuff_t *message_tvb, packet_info *pinfo, + proto_tree *udvm_tree, gint udvm_mem_dest, + gint print_flags, gint hf_id, + gint header_len, + gint byte_code_state_len, gint byte_code_id_len, + gint udvm_start_ip) +{ + tvbuff_t *decomp_tvb; + guint8 buff[UDVM_MEMORY_SIZE]; + char string[2],*strp; + guint8 *out_buff; /* Largest allowed size for a message is 65535 */ + guint32 i = 0; + guint16 n = 0; + guint16 m = 0; + guint16 x; + guint k = 0; + guint16 H; + guint16 oldH; + guint offset = 0; + guint result_dest; + guint code_length =0; + guint8 current_instruction; + guint current_address; + guint operand_address; + guint input_address; + guint16 output_address = 0; + guint next_operand_address; + guint8 octet; + guint8 msb; + guint8 lsb; + guint16 byte_copy_right; + guint16 byte_copy_left; + guint16 input_bit_order; + guint16 stack_location; + guint16 stack_fill; + guint16 result; + guint msg_end = tvb_reported_length_remaining(message_tvb, 0); + guint16 result_code = 0; + guint16 old_input_bit_order = 0; + guint16 remaining_bits = 0; + guint16 input_bits = 0; + guint8 bit_order = 0; + gboolean outside_huffman_boundaries = TRUE; + gboolean print_in_loop = FALSE; + guint16 instruction_address; + guint8 no_of_state_create = 0; + guint16 state_length_buff[5]; + guint16 state_address_buff[5]; + guint16 state_instruction_buff[5]; + guint16 state_minimum_access_length_buff[5]; + guint16 state_state_retention_priority_buff[5]; + guint32 used_udvm_cycles = 0; + guint cycles_per_bit; + guint maximum_UDVM_cycles; + guint8 *sha1buff; + unsigned char sha1_digest_buf[STATE_BUFFER_SIZE]; + sha1_context ctx; + + + /* UDVM operand variables */ + guint16 length; + guint16 at_address; + guint16 destination; + guint16 address; + guint16 value; + guint16 p_id_start; + guint16 p_id_length; + guint16 state_begin; + guint16 state_length; + guint16 state_address; + guint16 state_instruction; + guint16 operand_1; + guint16 operand_2; + guint16 value_1; + guint16 value_2; + guint16 at_address_1; + guint16 at_address_2; + guint16 at_address_3; + guint16 j; + guint16 bits_n; + guint16 lower_bound_n; + guint16 upper_bound_n; + guint16 uncompressed_n; + guint16 position; + guint16 ref_destination; /* could I have used $destination ? */ + guint16 multy_offset; + guint16 output_start; + guint16 output_length; + guint16 minimum_access_length; + guint16 state_retention_priority; + guint16 requested_feedback_location; + guint16 returned_parameters_location; + guint16 start_value; + + + /* Set print parameters */ + print_level_1 = FALSE; + print_level_2 = FALSE; + print_level_3 = FALSE; + show_instr_detail_level = 0; + + + + switch( print_flags ) { + case 0: + break; + + case 1: + print_level_1 = TRUE; + show_instr_detail_level = 1; + break; + case 2: + print_level_1 = TRUE; + print_level_2 = TRUE; + show_instr_detail_level = 1; + break; + case 3: + print_level_1 = TRUE; + print_level_2 = TRUE; + print_level_3 = TRUE; + show_instr_detail_level = 2; + break; + default: + print_level_1 = TRUE; + show_instr_detail_level = 1; + break; + } + + + + + + /* UDVM memory must be initialised to zero */ + memset(buff, 0, UDVM_MEMORY_SIZE); + /* Set initial UDVM data + * The first 32 bytes of UDVM memory are then initialized to special + * values as illustrated in Figure 5. + * + * 0 7 8 15 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | UDVM_memory_size | 0 - 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | cycles_per_bit | 2 - 3 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SigComp_version | 4 - 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | partial_state_ID_length | 6 - 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | state_length | 8 - 9 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * : reserved : 10 - 31 + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Figure 5: Initializing Useful Values in UDVM memory + */ + /* UDVM_memory_size */ + buff[0] = (UDVM_MEMORY_SIZE >> 8) & 0x00FF; + buff[1] = UDVM_MEMORY_SIZE & 0x00FF; + /* cycles_per_bit */ + buff[2] = 0; + buff[3] = 16; + /* SigComp_version */ + buff[4] = 0; + buff[5] = 1; + /* partial_state_ID_length */ + buff[6] = (byte_code_id_len >> 8) & 0x00FF; + buff[7] = byte_code_id_len & 0x00FF; + /* state_length */ + buff[8] = (byte_code_state_len >> 8) & 0x00FF; + buff[9] = byte_code_state_len & 0x00FF; + + code_length = tvb_reported_length_remaining(bytecode_tvb, 0); + + cycles_per_bit = buff[2] << 8; + cycles_per_bit = cycles_per_bit | buff[3]; + /* + * maximum_UDVM_cycles = (8 * n + 1000) * cycles_per_bit + */ + maximum_UDVM_cycles = (( 8 * (header_len + msg_end) ) + 1000) * cycles_per_bit; + + proto_tree_add_text(udvm_tree, bytecode_tvb, offset, 1,"maximum_UDVM_cycles(%u) = (( 8 * msg_end(%u) ) + 1000) * cycles_per_bit(%u)",maximum_UDVM_cycles,msg_end,cycles_per_bit); + proto_tree_add_text(udvm_tree, bytecode_tvb, offset, 1,"Message Length: %u,Byte code length: %u, Maximum UDVM cycles: %u",msg_end,code_length,maximum_UDVM_cycles); + + /* Load bytecode into UDVM starting at "udvm_mem_dest" */ + i = udvm_mem_dest; + if ( print_level_3 ) + proto_tree_add_text(udvm_tree, bytecode_tvb, offset, 1,"Load bytecode into UDVM starting at %u",i); + while ( code_length > offset ) { + buff[i] = tvb_get_guint8(bytecode_tvb, offset); + if ( print_level_3 ) + proto_tree_add_text(udvm_tree, bytecode_tvb, offset, 1, + " Addr: %u Instruction code(0x%0x) ", i, buff[i]); + + i++; + offset++; + + } + /* Largest allowed size for a message is 65535 */ + out_buff = g_malloc(65535); + /* Start executing code */ + current_address = udvm_start_ip; + input_address = 0; + operand_address = 0; + + proto_tree_add_text(udvm_tree, bytecode_tvb, offset, 1,"UDVM EXECUTION STARTED at Address: %u Message size %u", + current_address, msg_end); + +execute_next_instruction: + + if ( used_udvm_cycles > maximum_UDVM_cycles ){ + result_code = 15; + goto decompression_failure; + } + current_instruction = buff[current_address]; + + switch ( current_instruction ) { + case SIGCOMP_INSTR_DECOMPRESSION_FAILURE: + used_udvm_cycles++; + if ( result_code == 0 ) + result_code = 9; + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## DECOMPRESSION-FAILURE(0)", + current_address); + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Ethereal UDVM diagnostic: %s.", + val_to_str(result_code, result_code_vals,"Unknown (%u)")); + if ( output_address > 0 ){ + /* At least something got decompressed, show it */ + decomp_tvb = tvb_new_real_data(out_buff,output_address,output_address); + /* Arrange that the allocated packet data copy be freed when the + * tvbuff is freed. + */ + tvb_set_free_cb( decomp_tvb, g_free ); + /* Add the tvbuff to the list of tvbuffs to which the tvbuff we + * were handed refers, so it'll get cleaned up when that tvbuff + * is cleaned up. + */ + tvb_set_child_real_data_tvbuff(message_tvb,decomp_tvb); + add_new_data_source(pinfo, decomp_tvb, "Decompressed SigComp message(Incomplete)"); + proto_tree_add_text(udvm_tree, decomp_tvb, 0, -1,"SigComp message Decompression failure"); + return decomp_tvb; + } + g_free(out_buff); + return NULL; + break; + + case SIGCOMP_INSTR_AND: /* 1 AND ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## AND(1) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## AND (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 & operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_OR: /* 2 OR ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## OR(2) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## OR (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 | operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_NOT: /* 3 NOT ($operand_1) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## NOT(3) ($operand_1)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## NOT (operand_1=%u)", + current_address, operand_1); + } + /* execute the instruction */ + result = operand_1 ^ 0xffff; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_LSHIFT: /* 4 LSHIFT ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## LSHIFT(4) ($operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## LSHIFT (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 << operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + + break; + case SIGCOMP_INSTR_RSHIFT: /* 5 RSHIFT ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## RSHIFT(5) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## RSHIFT (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 >> operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_ADD: /* 6 ADD ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## ADD(6) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## ADD (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 + operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + + case SIGCOMP_INSTR_SUBTRACT: /* 7 SUBTRACT ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SUBTRACT(7) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SUBTRACT (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 - operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_MULTIPLY: /* 8 MULTIPLY ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ##MULTIPLY(8) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## MULTIPLY (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* + * execute the instruction + * MULTIPLY (m, n) := m * n (modulo 2^16) + */ + if ( operand_2 == 0){ + result_code = 4; + goto decompression_failure; + } + result = operand_1 * operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_DIVIDE: /* 9 DIVIDE ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## DIVIDE(9) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## DIVIDE (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* + * execute the instruction + * DIVIDE (m, n) := floor(m / n) + * Decompression failure occurs if a DIVIDE or REMAINDER instruction + * encounters an operand_2 that is zero. + */ + if ( operand_2 == 0){ + result_code = 4; + goto decompression_failure; + } + result = operand_1 / operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_REMAINDER: /* 10 REMAINDER ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## REMAINDER(10) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## REMAINDER (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* + * execute the instruction + * REMAINDER (m, n) := m - n * floor(m / n) + * Decompression failure occurs if a DIVIDE or REMAINDER instruction + * encounters an operand_2 that is zero. + */ + if ( operand_2 == 0){ + result_code = 4; + goto decompression_failure; + } + result = operand_1 - operand_2 * (operand_1 / operand_2); + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_SORT_ASCENDING: /* 11 SORT-ASCENDING (%start, %n, %k) */ + /* + * used_udvm_cycles = 1 + k * (ceiling(log2(k)) + n) + */ + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SORT-ASCENDING(11) (start, n, k))", + current_address); + } + operand_address = current_address + 1; + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Execution of this instruction is NOT implemented"); + /* + * used_udvm_cycles = 1 + k * (ceiling(log2(k)) + n) + */ + break; + + case SIGCOMP_INSTR_SORT_DESCENDING: /* 12 SORT-DESCENDING (%start, %n, %k) */ + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SORT-DESCENDING(12) (start, n, k))", + current_address); + } + operand_address = current_address + 1; + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Execution of this instruction is NOT implemented"); + /* + * used_udvm_cycles = 1 + k * (ceiling(log2(k)) + n) + */ + break; + case SIGCOMP_INSTR_SHA_1: /* 13 SHA-1 (%position, %length, %destination) */ + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SHA-1(13) (position, length, destination)", + current_address); + } + operand_address = current_address + 1; + /* %position */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &position); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u position %u", + operand_address, position); + } + operand_address = next_operand_address; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + /* $destination */ + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &ref_destination, &result_dest); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u $destination %u", + operand_address, ref_destination); + } + current_address = next_operand_address; + used_udvm_cycles = used_udvm_cycles + 1 + length; + + n = 0; + k = position; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, 0, -1, + "byte_copy_right = %u", byte_copy_right); + } + + sha1_starts( &ctx ); + + while (n= byte_copy_right ) { + k = byte_copy_left; + } + } + + sha1_finish( &ctx, sha1_digest_buf ); + + k = ref_destination; + + for ( n=0; n< STATE_BUFFER_SIZE; n++ ) { + + buff[k] = sha1_digest_buf[n]; + + k = ( k + 1 ) & 0xffff; + n++; + + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + } + + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, 0, -1, + "Calculated SHA-1: %s", + bytes_to_str(sha1_digest_buf, STATE_BUFFER_SIZE)); + } + + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_LOAD: /* 14 LOAD (%address, %value) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## LOAD(14) (%%address, %%value)", + current_address); + } + operand_address = current_address + 1; + /* %address */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Address %u", + operand_address, address); + } + operand_address = next_operand_address; + /* %value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value); + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## LOAD (%%address=%u, %%value=%u)", + current_address, address, value); + } + lsb = value & 0xff; + msb = value >> 8; + + buff[address] = msb; + buff[address + 1] = lsb; + + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, value); + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading bytes at %u Value %u 0x%x", + address, value, value); + } + used_udvm_cycles++; + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_MULTILOAD: /* 15 MULTILOAD (%address, #n, %value_0, ..., %value_n-1) */ + /* RFC 3320: + * The MULTILOAD instruction sets a contiguous block of 2-byte words in + * the UDVM memory to specified values. + * Hmm what if the value to load only takes one byte ? Chose to always load two bytes. + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## MULTILOAD(15) (%%address, #n, value_0, ..., value_n-1)", + current_address); + } + operand_address = current_address + 1; + /* %address */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Address %u", + operand_address, address); + } + operand_address = next_operand_address; + + /* #n */ + next_operand_address = decode_udvm_literal_operand(buff,operand_address, &n); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u n %u", + operand_address, n); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## MULTILOAD (%%address=%u, #n=%u, value_0, ..., value_%d)", + current_address, address, n, n-1); + } + operand_address = next_operand_address; + used_udvm_cycles = used_udvm_cycles + 1 + n; + while ( n > 0) { + n = n - 1; + /* %value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value); + lsb = value & 0xff; + msb = value >> 8; + + buff[address] = msb; + buff[address + 1] = lsb; + /* debug + */ + length = next_operand_address - operand_address; + + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, "Addr: %u Value %5u - Loading bytes at %5u Value %5u 0x%x", + operand_address, value, address, value, value); + } + address = address + 2; + operand_address = next_operand_address; + } + current_address = next_operand_address; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_PUSH: /* 16 PUSH (%value) */ + if (show_instr_detail_level == 2){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## PUSH(16) (value)", + current_address); + } + operand_address = current_address + 1; + /* %value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value); + if (show_instr_detail_level == 2){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, value); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## PUSH (value=%u)", + current_address, value); + } + current_address = next_operand_address; + + /* Push the value address onto the stack */ + stack_location = (buff[70] << 8) | buff[71]; + stack_fill = (buff[stack_location] << 8) + | buff[(stack_location+1) & 0xFFFF]; + address = (stack_location + stack_fill * 2 + 2) & 0xFFFF; + + buff[address] = (value >> 8) & 0x00FF; + buff[(address+1) & 0xFFFF] = value & 0x00FF; + + stack_fill = (stack_fill + 1) & 0xFFFF; + buff[stack_location] = (stack_fill >> 8) & 0x00FF; + buff[(stack_location+1) & 0xFFFF] = stack_fill & 0x00FF; + + used_udvm_cycles++; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_POP: /* 17 POP (%address) */ + if (show_instr_detail_level == 2){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## POP(16) (value)", + current_address); + } + operand_address = current_address + 1; + /* %value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &destination); + if (show_instr_detail_level == 2){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, destination); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## POP (address=%u)", + current_address, destination); + } + current_address = next_operand_address; + + /* Pop value from the top of the stack */ + stack_location = (buff[70] << 8) | buff[71]; + stack_fill = (buff[stack_location] << 8) + | buff[(stack_location+1) & 0xFFFF]; + if (stack_fill == 0) + { + result_code = 16; + goto decompression_failure; + } + + stack_fill = (stack_fill - 1) & 0xFFFF; + buff[stack_location] = (stack_fill >> 8) & 0x00FF; + buff[(stack_location+1) & 0xFFFF] = stack_fill & 0x00FF; + + address = (stack_location + stack_fill * 2 + 2) & 0xFFFF; + value = (buff[address] << 8) + | buff[(address+1) & 0xFFFF]; + + /* ... and store the popped value. */ + buff[destination] = (value >> 8) & 0x00FF; + buff[(destination+1) & 0xFFFF] = value & 0x00FF; + + used_udvm_cycles++; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_COPY: /* 18 COPY (%position, %length, %destination) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY(18) (position, length, destination)", + current_address); + } + operand_address = current_address + 1; + /* %position */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &position); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u position %u", + operand_address, position); + } + operand_address = next_operand_address; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + /* %destination */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &destination); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Destination %u", + operand_address, destination); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY (position=%u, length=%u, destination=%u)", + current_address, position, length, destination); + } + current_address = next_operand_address; + /* + * 8.4. Byte copying + * : + * The string of bytes is copied in ascending order of memory address, + * respecting the bounds set by byte_copy_left and byte_copy_right. + * More precisely, if a byte is copied from/to Address m then the next + * byte is copied from/to Address n where n is calculated as follows: + * + * Set k := m + 1 (modulo 2^16) + * If k = byte_copy_right then set n := byte_copy_left, else set n := k + * + */ + + n = 0; + k = destination; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_right = %u", byte_copy_right); + } + + while ( n < length ){ + buff[k] = buff[position]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Copying value: %u (0x%x) to Addr: %u", + buff[position], buff[position], k); + } + position = ( position + 1 ) & 0xffff; + k = ( k + 1 ) & 0xffff; + n++; + + /* + * Check for circular buffer wrapping after the positions are + * incremented. If either started at BCR then they should continue + * to increment beyond BCR. + */ + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + if ( position == byte_copy_right ){ + position = byte_copy_left; + } + } + used_udvm_cycles = used_udvm_cycles + 1 + length; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_COPY_LITERAL: /* 19 COPY-LITERAL (%position, %length, $destination) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY-LITERAL(19) (position, length, $destination)", + current_address); + } + operand_address = current_address + 1; + /* %position */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &position); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u position %u", + operand_address, position); + } + operand_address = next_operand_address; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + + /* $destination */ + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &ref_destination, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u destination %u", + operand_address, ref_destination); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY-LITERAL (position=%u, length=%u, $destination=%u)", + current_address, position, length, destination); + } + current_address = next_operand_address; + + + /* + * 8.4. Byte copying + * : + * The string of bytes is copied in ascending order of memory address, + * respecting the bounds set by byte_copy_left and byte_copy_right. + * More precisely, if a byte is copied from/to Address m then the next + * byte is copied from/to Address n where n is calculated as follows: + * + * Set k := m + 1 (modulo 2^16) + * If k = byte_copy_right then set n := byte_copy_left, else set n := k + * + */ + + n = 0; + k = ref_destination; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_right = %u", byte_copy_right); + } + while ( n < length ){ + + buff[k] = buff[position]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Copying value: %u (0x%x) to Addr: %u", + buff[position], buff[position], k); + } + position = ( position + 1 ) & 0xffff; + k = ( k + 1 ) & 0xffff; + n++; + + /* + * Check for circular buffer wrapping after the positions are + * incremented. It is important that k cannot be left set + * to BCR. Also, if either started at BCR then they should continue + * to increment beyond BCR. + */ + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + if ( position == byte_copy_right ){ + position = byte_copy_left; + } + } + buff[result_dest] = k >> 8; + buff[result_dest + 1] = k & 0x00ff; + + used_udvm_cycles = used_udvm_cycles + 1 + length; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_COPY_OFFSET: /* 20 COPY-OFFSET (%offset, %length, $destination) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY-OFFSET(20) (offset, length, $destination)", + current_address); + } + operand_address = current_address + 1; + /* %offset */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &multy_offset); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u offset %u", + operand_address, multy_offset); + } + operand_address = next_operand_address; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + + /* $destination */ + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &ref_destination, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u $destination %u", + operand_address, ref_destination); + } + + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY-OFFSET (offset=%u, length=%u, $destination=%u)", + current_address, multy_offset, length, result_dest); + } + current_address = next_operand_address; + + /* Execute the instruction: + * To derive the value of the position operand, starting at the memory + * address specified by destination, the UDVM counts backwards a total + * of offset memory addresses. + * + * If the memory address specified in byte_copy_left is reached, the + * next memory address is taken to be (byte_copy_right - 1) modulo 2^16. + */ + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + + /* + * In order to work out the position, simple arithmetic is tricky + * to apply because there some nasty corner cases. A simple loop + * is inefficient but the logic is simple. + * + * FUTURE: This could be optimised. + */ + for (position = ref_destination, i = 0; i < multy_offset; i++) + { + if ( position == byte_copy_left ) + { + position = (byte_copy_right - 1) & 0xffff; + } + else + { + position = (position - 1) & 0xffff; + } + } + + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_left = %u byte_copy_right = %u position= %u", + byte_copy_left, byte_copy_right, position); + } + /* The COPY-OFFSET instruction then behaves as a COPY-LITERAL + * instruction, taking the value of the position operand to be the last + * memory address reached in the above step. + */ + + /* + * 8.4. Byte copying + * : + * The string of bytes is copied in ascending order of memory address, + * respecting the bounds set by byte_copy_left and byte_copy_right. + * More precisely, if a byte is copied from/to Address m then the next + * byte is copied from/to Address n where n is calculated as follows: + * + * Set k := m + 1 (modulo 2^16) + * If k = byte_copy_right then set n := byte_copy_left, else set n := k + * + */ + + n = 0; + k = ref_destination; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_left = %u byte_copy_right = %u", byte_copy_left, byte_copy_right); + } + while ( n < length ){ + buff[k] = buff[position]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Copying value: %5u (0x%x) from Addr: %u to Addr: %u", + buff[position], buff[position],(position), k); + } + n++; + k = ( k + 1 ) & 0xffff; + position = ( position + 1 ) & 0xffff; + + /* + * Check for circular buffer wrapping after the positions are + * incremented. It is important that k cannot be left set + * to BCR. Also, if either started at BCR then they should continue + * to increment beyond BCR. + */ + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + if ( position == byte_copy_right ){ + position = byte_copy_left; + } + } + buff[result_dest] = k >> 8; + buff[result_dest + 1] = k & 0x00ff; + used_udvm_cycles = used_udvm_cycles + 1 + length; + goto execute_next_instruction; + + break; + case SIGCOMP_INSTR_MEMSET: /* 21 MEMSET (%address, %length, %start_value, %offset) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## MEMSET(21) (address, length, start_value, offset)", + current_address); + } + operand_address = current_address + 1; + + /* %address */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Address %u", + operand_address, address); + } + operand_address = next_operand_address; + + /* %length, */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + /* %start_value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &start_value); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u start_value %u", + operand_address, start_value); + } + operand_address = next_operand_address; + + /* %offset */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &multy_offset); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u offset %u", + operand_address, multy_offset); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## MEMSET (address=%u, length=%u, start_value=%u, offset=%u)", + current_address, address, length, start_value, multy_offset); + } + current_address = next_operand_address; + /* exetute the instruction + * The sequence of values used by the MEMSET instruction is specified by + * the following formula: + * + * Seq[n] := (start_value + n * offset) modulo 256 + */ + n = 0; + k = address; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_left = %u byte_copy_right = %u", byte_copy_left, byte_copy_right); + } + while ( n < length ){ + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + buff[k] = (start_value + ( n * multy_offset)) & 0xff; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Storing value: %u (0x%x) at Addr: %u", + buff[k], buff[k], k); + } + k = ( k + 1 ) & 0xffff; + n++; + }/* end while */ + used_udvm_cycles = used_udvm_cycles + 1 + length; + goto execute_next_instruction; + break; + + + case SIGCOMP_INSTR_JUMP: /* 22 JUMP (@address) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## JUMP(22) (@address)", + current_address); + } + operand_address = current_address + 1; + /* @address */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_address_operand(buff,operand_address, &at_address, current_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## JUMP (@address=%u)", + current_address, at_address); + } + current_address = at_address; + used_udvm_cycles++; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_COMPARE: /* 23 */ + /* COMPARE (%value_1, %value_2, @address_1, @address_2, @address_3) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COMPARE(23) (value_1, value_2, @address_1, @address_2, @address_3)", + current_address); + } + operand_address = current_address + 1; + + /* %value_1 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value_1); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, value_1); + } + operand_address = next_operand_address; + + /* %value_2 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, value_2); + } + operand_address = next_operand_address; + + /* @address_1 */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address_1); + at_address_1 = ( current_address + at_address_1) & 0xffff; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address_1); + } + operand_address = next_operand_address; + + + /* @address_2 */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address_2); + at_address_2 = ( current_address + at_address_2) & 0xffff; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address_2); + } + operand_address = next_operand_address; + + /* @address_3 */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address_3); + at_address_3 = ( current_address + at_address_3) & 0xffff; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address_3); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COMPARE (value_1=%u, value_2=%u, @address_1=%u, @address_2=%u, @address_3=%u)", + current_address, value_1, value_2, at_address_1, at_address_2, at_address_3); + } + /* execute the instruction + * If value_1 < value_2 then the UDVM continues instruction execution at + * the memory address specified by address 1. If value_1 = value_2 then + * it jumps to the address specified by address_2. If value_1 > value_2 + * then it jumps to the address specified by address_3. + */ + if ( value_1 < value_2 ) + current_address = at_address_1; + if ( value_1 == value_2 ) + current_address = at_address_2; + if ( value_1 > value_2 ) + current_address = at_address_3; + used_udvm_cycles++; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_CALL: /* 24 CALL (@address) (PUSH addr )*/ + if (show_instr_detail_level == 2){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## CALL(24) (@address) (PUSH addr )", + current_address); + } + operand_address = current_address + 1; + /* @address */ + next_operand_address = decode_udvm_address_operand(buff,operand_address, &at_address, current_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## CALL (@address=%u)", + current_address, at_address); + } + current_address = next_operand_address; + + /* Push the current address onto the stack */ + stack_location = (buff[70] << 8) | buff[71]; + stack_fill = (buff[stack_location] << 8) + | buff[(stack_location+1) & 0xFFFF]; + address = (stack_location + stack_fill * 2 + 2) & 0xFFFF; + buff[address] = (current_address >> 8) & 0x00FF; + buff[(address+1) & 0xFFFF] = current_address & 0x00FF; + + stack_fill = (stack_fill + 1) & 0xFFFF; + buff[stack_location] = (stack_fill >> 8) & 0x00FF; + buff[(stack_location+1) & 0xFFFF] = stack_fill & 0x00FF; + + /* ... and jump to the destination address */ + current_address = at_address; + + used_udvm_cycles++; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_RETURN: /* 25 POP and return */ + if (print_level_1 || show_instr_detail_level == 1){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## POP(25) and return", + current_address); + } + + /* Pop value from the top of the stack */ + stack_location = (buff[70] << 8) | buff[71]; + stack_fill = (buff[stack_location] << 8) + | buff[(stack_location+1) & 0xFFFF]; + if (stack_fill == 0) + { + result_code = 16; + goto decompression_failure; + } + + stack_fill = (stack_fill - 1) & 0xFFFF; + buff[stack_location] = (stack_fill >> 8) & 0x00FF; + buff[(stack_location+1) & 0xFFFF] = stack_fill & 0x00FF; + + address = (stack_location + stack_fill * 2 + 2) & 0xFFFF; + at_address = (buff[address] << 8) + | buff[(address+1) & 0xFFFF]; + + /* ... and set the PC to the popped value */ + current_address = at_address; + + used_udvm_cycles++; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_SWITCH: /* 26 SWITCH (#n, %j, @address_0, @address_1, ... , @address_n-1) */ + /* + * When a SWITCH instruction is encountered the UDVM reads the value of + * j. It then continues instruction execution at the address specified + * by address j. + * + * Decompression failure occurs if j specifies a value of n or more, or + * if the address lies beyond the overall UDVM memory size. + */ + instruction_address = current_address; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SWITCH (#n, j, @address_0, @address_1, ... , @address_n-1))", + current_address); + } + operand_address = current_address + 1; + /* #n + * Number of addresses in the instruction + */ + next_operand_address = decode_udvm_literal_operand(buff,operand_address, &n); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u n %u", + operand_address, n); + } + operand_address = next_operand_address; + /* %j */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &j); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u j %u", + operand_address, j); + } + operand_address = next_operand_address; + m = 0; + while ( m < n ){ + /* @address_n-1 */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address_1); + at_address_1 = ( instruction_address + at_address_1) & 0xffff; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address_1); + } + if ( j == m ){ + current_address = at_address_1; + } + operand_address = next_operand_address; + m++; + } + /* Check decompression failure */ + if ( ( j == n ) || ( j > n )){ + result_code = 5; + goto decompression_failure; + } + if ( current_address > UDVM_MEMORY_SIZE ){ + result_code = 6; + goto decompression_failure; + } + used_udvm_cycles = used_udvm_cycles + 1 + n; + + goto execute_next_instruction; + + break; + case SIGCOMP_INSTR_CRC: /* 27 CRC (%value, %position, %length, @address) */ + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## CRC (value, position, length, @address)", + current_address); + } + + operand_address = current_address + 1; + + /* %value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, value); + } + operand_address = next_operand_address; + + /* %position */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &position); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u position %u", + operand_address, position); + } + operand_address = next_operand_address; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + /* @address */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address); + at_address = ( current_address + at_address) & 0xffff; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + used_udvm_cycles = used_udvm_cycles + 1 + length; + + n = 0; + k = position; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + result = 0; + + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, 0, -1, + "byte_copy_right = %u", byte_copy_right); + } + + while (n= byte_copy_right ) { + k = byte_copy_left; + } + } + + result = result ^ 0xffff; + + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, "Calculated CRC %u", result); + } + if (result != value){ + current_address = at_address; + } + else { + current_address = next_operand_address; + } + goto execute_next_instruction; + break; + + + case SIGCOMP_INSTR_INPUT_BYTES: /* 28 INPUT-BYTES (%length, %destination, @address) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-BYTES(28) length, destination, @address)", + current_address); + } + operand_address = current_address + 1; + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + /* %destination */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &destination); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Destination %u", + operand_address, destination); + } + operand_address = next_operand_address; + + /* @address */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address); + at_address = ( current_address + at_address) & 0xffff; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-BYTES length=%u, destination=%u, @address=%u)", + current_address, length, destination, at_address); + } + /* execute the instruction TODO insert checks + * RFC 3320 : + * + * 0 7 8 15 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | byte_copy_left | 64 - 65 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | byte_copy_right | 66 - 67 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | input_bit_order | 68 - 69 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | stack_location | 70 - 71 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Figure 7: Memory addresses of the UDVM registers + * : + * 8.4. Byte copying + * : + * The string of bytes is copied in ascending order of memory address, + * respecting the bounds set by byte_copy_left and byte_copy_right. + * More precisely, if a byte is copied from/to Address m then the next + * byte is copied from/to Address n where n is calculated as follows: + * + * Set k := m + 1 (modulo 2^16) + * If k = byte_copy_right then set n := byte_copy_left, else set n := k + * + */ + + n = 0; + k = destination; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_right = %u", byte_copy_right); + } + /* clear out remaining bits if any */ + remaining_bits = 0; + input_bits=0; + /* operand_address used as dummy */ + while ( n < length ){ + if (input_address > ( msg_end - 1)){ + current_address = at_address; + result_code = 14; + goto execute_next_instruction; + } + + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + octet = tvb_get_guint8(message_tvb, input_address); + buff[k] = octet; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Loading value: %u (0x%x) at Addr: %u", octet, octet, k); + } + input_address++; + /* + * If the instruction requests data that lies beyond the end of the + * SigComp message, no data is returned. Instead the UDVM moves program + * execution to the address specified by the address operand. + */ + + + k = ( k + 1 ) & 0xffff; + n++; + } + used_udvm_cycles = used_udvm_cycles + 1 + length; + current_address = next_operand_address; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_INPUT_BITS:/* 29 INPUT-BITS (%length, %destination, @address) */ + /* + * The length operand indicates the requested number of bits. + * Decompression failure occurs if this operand does not lie between 0 + * and 16 inclusive. + * + * The destination operand specifies the memory address to which the + * compressed data should be copied. Note that the requested bits are + * interpreted as a 2-byte integer ranging from 0 to 2^length - 1, as + * explained in Section 8.2. + * + * If the instruction requests data that lies beyond the end of the + * SigComp message, no data is returned. Instead the UDVM moves program + * execution to the address specified by the address operand. + */ + + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-BITS(29) (length, destination, @address)", + current_address); + } + operand_address = current_address + 1; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u length %u", + operand_address, length); + } + operand_address = next_operand_address; + /* %destination */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &destination); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Destination %u", + operand_address, destination); + } + operand_address = next_operand_address; + + /* @address */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_address_operand(buff,operand_address, &at_address, current_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-BITS length=%u, destination=%u, @address=%u)", + current_address, length, destination, at_address); + } + current_address = next_operand_address; + + /* + * Execute actual instr. + * The input_bit_order register contains the following three flags: + * + * 0 7 8 15 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | reserved |F|H|P| 68 - 69 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + input_bit_order = buff[68] << 8; + input_bit_order = input_bit_order | buff[69]; + /* + * If the instruction requests data that lies beyond the end of the + * SigComp message, no data is returned. Instead the UDVM moves program + * execution to the address specified by the address operand. + */ + + if ( length > 16 ){ + result_code = 7; + goto decompression_failure; + } + if ( input_bit_order > 7 ){ + result_code = 8; + goto decompression_failure; + } + + /* + * Transfer F bit to bit_order to tell decomp dispatcher which bit order to use + */ + bit_order = ( input_bit_order & 0x0004 ) >> 2; + value = decomp_dispatch_get_bits( message_tvb, udvm_tree, bit_order, + buff, &old_input_bit_order, &remaining_bits, + &input_bits, &input_address, length, &result_code, msg_end); + if ( result_code == 11 ){ + used_udvm_cycles = used_udvm_cycles + 1; + current_address = at_address; + goto execute_next_instruction; + } + msb = value >> 8; + lsb = value & 0x00ff; + buff[destination] = msb; + buff[destination + 1]=lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Loading value: %u (0x%x) at Addr: %u, remaining_bits: %u", value, value, destination, remaining_bits); + } + + used_udvm_cycles = used_udvm_cycles + 1; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_INPUT_HUFFMAN: /* 30 */ + /* + * INPUT-HUFFMAN (%destination, @address, #n, %bits_1, %lower_bound_1, + * %upper_bound_1, %uncompressed_1, ... , %bits_n, %lower_bound_n, + * %upper_bound_n, %uncompressed_n) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-HUFFMAN (destination, @address, #n, bits_1, lower_bound_1,upper_bound_1, uncompressed_1, ... , bits_n, lower_bound_n,upper_bound_n, uncompressed_n)", + current_address); + } + operand_address = current_address + 1; + + /* %destination */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &destination); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Destination %u", + operand_address, destination); + } + operand_address = next_operand_address; + + /* @address */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_address_operand(buff,operand_address, &at_address, current_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + operand_address = next_operand_address; + + /* #n */ + next_operand_address = decode_udvm_literal_operand(buff,operand_address, &n); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u n %u", + operand_address, n); + } + operand_address = next_operand_address; + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-HUFFMAN (destination=%u, @address=%u, #n=%u, bits_1, lower_1,upper_1, unc_1, ... , bits_%d, lower_%d,upper_%d, unc_%d)", + current_address, destination, at_address, n, n, n, n, n); + } + + used_udvm_cycles = used_udvm_cycles + 1 + n; + + /* + * Note that if n = 0 then the INPUT-HUFFMAN instruction is ignored and + * program execution resumes at the following instruction. + * Decompression failure occurs if (bits_1 + ... + bits_n) > 16. + * + * In all other cases, the behavior of the INPUT-HUFFMAN instruction is + * defined below: + * + * 1. Set j := 1 and set H := 0. + * + * 2. Request bits_j compressed bits. Interpret the returned bits as an + * integer k from 0 to 2^bits_j - 1, as explained in Section 8.2. + * + * 3. Set H := H * 2^bits_j + k. + * + * 4. If data is requested that lies beyond the end of the SigComp + * message, terminate the INPUT-HUFFMAN instruction and move program + * execution to the memory address specified by the address operand. + * + * 5. If (H < lower_bound_j) or (H > upper_bound_j) then set j := j + 1. + * Then go back to Step 2, unless j > n in which case decompression + * failure occurs. + * + * 6. Copy (H + uncompressed_j - lower_bound_j) modulo 2^16 to the + * memory address specified by the destination operand. + * + */ + /* + * The input_bit_order register contains the following three flags: + * + * 0 7 8 15 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | reserved |F|H|P| 68 - 69 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Transfer H bit to bit_order to tell decomp dispatcher which bit order to use + */ + input_bit_order = buff[68] << 8; + input_bit_order = input_bit_order | buff[69]; + bit_order = ( input_bit_order & 0x0002 ) >> 1; + + j = 1; + H = 0; + m = n; + outside_huffman_boundaries = TRUE; + print_in_loop = print_level_3; + while ( m > 0 ){ + /* %bits_n */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &bits_n); + if (print_in_loop ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u bits_n %u", + operand_address, bits_n); + } + operand_address = next_operand_address; + + /* %lower_bound_n */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &lower_bound_n); + if (print_in_loop ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u lower_bound_n %u", + operand_address, lower_bound_n); + } + operand_address = next_operand_address; + /* %upper_bound_n */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &upper_bound_n); + if (print_in_loop ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u upper_bound_n %u", + operand_address, upper_bound_n); + } + operand_address = next_operand_address; + /* %uncompressed_n */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &uncompressed_n); + if (print_in_loop ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u uncompressed_n %u", + operand_address, uncompressed_n); + } + operand_address = next_operand_address; + /* execute instruction */ + if ( outside_huffman_boundaries ) { + /* + * 2. Request bits_j compressed bits. Interpret the returned bits as an + * integer k from 0 to 2^bits_j - 1, as explained in Section 8.2. + */ + k = decomp_dispatch_get_bits( message_tvb, udvm_tree, bit_order, + buff, &old_input_bit_order, &remaining_bits, + &input_bits, &input_address, bits_n, &result_code, msg_end); + if ( result_code == 11 ){ + /* + * 4. If data is requested that lies beyond the end of the SigComp + * message, terminate the INPUT-HUFFMAN instruction and move program + * execution to the memory address specified by the address operand. + */ + current_address = at_address; + goto execute_next_instruction; + } + + /* + * 3. Set H := H * 2^bits_j + k. + * [In practice is a shift+OR operation.] + */ + oldH = H; + H = (H << bits_n) | k; + if (print_level_3 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Set H(%u) := H(%u) * 2^bits_j(%u) + k(%u)", + H ,oldH, 1< upper_bound_j) then set j := j + 1. + * Then go back to Step 2, unless j > n in which case decompression + * failure occurs. + */ + if ((H < lower_bound_n) || (H > upper_bound_n)){ + outside_huffman_boundaries = TRUE; + }else{ + outside_huffman_boundaries = FALSE; + print_in_loop = FALSE; + /* + * 6. Copy (H + uncompressed_j - lower_bound_j) modulo 2^16 to the + * memory address specified by the destination operand. + */ + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + " H(%u) = H(%u) + uncompressed_n(%u) - lower_bound_n(%u)", + (H + uncompressed_n - lower_bound_n ),H, uncompressed_n, lower_bound_n); + } + H = H + uncompressed_n - lower_bound_n; + msb = H >> 8; + lsb = H & 0x00ff; + buff[destination] = msb; + buff[destination + 1]=lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Loading H: %u (0x%x) at Addr: %u,j = %u remaining_bits: %u", + H, H, destination,( n - m + 1 ), remaining_bits); + } + + } + + + } + m = m - 1; + } + if ( outside_huffman_boundaries ) { + result_code = 10; + goto decompression_failure; + } + + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_STATE_ACCESS: /* 31 */ + /* STATE-ACCESS (%partial_identifier_start, %partial_identifier_length, + * %state_begin, %state_length, %state_address, %state_instruction) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-ACCESS(31) (partial_identifier_start, partial_identifier_length,state_begin, state_length, state_address, state_instruction)", + current_address); + } + operand_address = current_address + 1; + + /* + * %partial_identifier_start + */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &p_id_start); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u partial_identifier_start %u", + operand_address, p_id_start); + } + operand_address = next_operand_address; + + /* + * %partial_identifier_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &p_id_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u partial_identifier_length %u", + operand_address, p_id_length); + } + /* + * %state_begin + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_begin); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_begin %u", + operand_address, state_begin); + } + /* + * %state_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_length %u", + operand_address, state_length); + } + /* + * %state_address + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_address %u", + operand_address, state_address); + } + /* + * %state_instruction + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_instruction); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_instruction %u", + operand_address, state_instruction); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-ACCESS(31) (partial_identifier_start=%u, partial_identifier_length=%u,state_begin=%u, state_length=%u, state_address=%u, state_instruction=%u)", + current_address, p_id_start, p_id_length, state_begin, state_length, state_address, state_instruction); + } + current_address = next_operand_address; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_right = %u, byte_copy_left = %u", byte_copy_right,byte_copy_left); + } + + result_code = udvm_state_access(message_tvb, udvm_tree, buff, p_id_start, p_id_length, state_begin, &state_length, + &state_address, &state_instruction, hf_id); + if ( result_code != 0 ){ + goto decompression_failure; + } + used_udvm_cycles = used_udvm_cycles + 1 + state_length; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_STATE_CREATE: /* 32 */ + /* + * STATE-CREATE (%state_length, %state_address, %state_instruction, + * %minimum_access_length, %state_retention_priority) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-CREATE(32) (state_length, state_address, state_instruction,minimum_access_length, state_retention_priority)", + current_address); + } + operand_address = current_address + 1; + + /* + * %state_length + */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_length %u", + operand_address, state_length); + } + /* + * %state_address + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_address %u", + operand_address, state_address); + } + /* + * %state_instruction + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_instruction); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_instruction %u", + operand_address, state_instruction); + } + operand_address = next_operand_address; + /* + * %minimum_access_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &minimum_access_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u minimum_access_length %u", + operand_address, minimum_access_length); + } + operand_address = next_operand_address; + /* + * %state_retention_priority + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_retention_priority); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_retention_priority %u", + operand_address, state_retention_priority); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-CREATE(32) (state_length=%u, state_address=%u, state_instruction=%u,minimum_access_length=%u, state_retention_priority=%u)", + current_address, state_length, state_address, state_instruction,minimum_access_length, state_retention_priority); + } + current_address = next_operand_address; + /* Execute the instruction + * TODO Implement the instruction + * RFC3320: + * Note that the new state item cannot be created until a valid + * compartment identifier has been returned by the application. + * Consequently, when a STATE-CREATE instruction is encountered the UDVM + * simply buffers the five supplied operands until the END-MESSAGE + * instruction is reached. The steps taken at this point are described + * in Section 9.4.9. + * + * Decompression failure MUST occur if more than four state creation + * requests are made before the END-MESSAGE instruction is encountered. + * Decompression failure also occurs if the minimum_access_length does + * not lie between 6 and 20 inclusive, or if the + * state_retention_priority is 65535. + */ + no_of_state_create++; + if ( no_of_state_create > 4 ){ + result_code = 12; + goto decompression_failure; + } + if (( minimum_access_length < 6 ) || ( minimum_access_length > STATE_BUFFER_SIZE )){ + result_code = 1; + goto decompression_failure; + } + if ( state_retention_priority == 65535 ){ + result_code = 13; + goto decompression_failure; + } + state_length_buff[no_of_state_create] = state_length; + state_address_buff[no_of_state_create] = state_address; + state_instruction_buff[no_of_state_create] = state_instruction; + state_minimum_access_length_buff[no_of_state_create] = minimum_access_length; + state_state_retention_priority_buff[no_of_state_create] = state_retention_priority; + used_udvm_cycles = used_udvm_cycles + 1 + state_length; + /* Debug */ + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + n = 0; + k = state_address; + while ( n < state_length ){ + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + string[0]= buff[k]; + string[1]= '\0'; + if (print_level_3 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + " Addr: %5u State value: %u (0x%x) ASCII(%s)", + k,buff[k],buff[k],string); + } + k = ( k + 1 ) & 0xffff; + n++; + } + /* End debug */ + + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_STATE_FREE: /* 33 */ + /* + * STATE-FREE (%partial_identifier_start, %partial_identifier_length) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-FREE (partial_identifier_start, partial_identifier_length)", + current_address); + } + operand_address = current_address + 1; + /* + * %partial_identifier_start + */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &p_id_start); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u partial_identifier_start %u", + operand_address, p_id_start); + } + operand_address = next_operand_address; + + /* + * %partial_identifier_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &p_id_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u partial_identifier_length %u", + operand_address, p_id_length); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-FREE (partial_identifier_start=%u, partial_identifier_length=%u)", + current_address, p_id_start, p_id_length); + } + current_address = next_operand_address; + + /* Execute the instruction: + * TODO implement it + */ + udvm_state_free(buff,p_id_start,p_id_length); + used_udvm_cycles++; + + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_OUTPUT: /* 34 OUTPUT (%output_start, %output_length) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## OUTPUT(34) (output_start, output_length)", + current_address); + } + operand_address = current_address + 1; + /* + * %output_start + */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &output_start); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u output_start %u", + operand_address, output_start); + } + operand_address = next_operand_address; + /* + * %output_length + */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &output_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u output_length %u", + operand_address, output_length); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## OUTPUT (output_start=%u, output_length=%u)", + current_address, output_start, output_length); + } + current_address = next_operand_address; + + /* + * Execute instruction + * 8.4. Byte copying + * : + * The string of bytes is copied in ascending order of memory address, + * respecting the bounds set by byte_copy_left and byte_copy_right. + * More precisely, if a byte is copied from/to Address m then the next + * byte is copied from/to Address n where n is calculated as follows: + * + * Set k := m + 1 (modulo 2^16) + * If k = byte_copy_right then set n := byte_copy_left, else set n := k + * + */ + + n = 0; + k = output_start; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_3 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + " byte_copy_right = %u", byte_copy_right); + } + while ( n < output_length ){ + + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + out_buff[output_address] = buff[k]; + string[0]= buff[k]; + string[1]= '\0'; + strp = string; + if (print_level_3 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + " Output value: %u (0x%x) ASCII(%s) from Addr: %u ,output to dispatcher position %u", + buff[k],buff[k],format_text(strp,1), k,output_address); + } + k = ( k + 1 ) & 0xffff; + output_address ++; + n++; + } + used_udvm_cycles = used_udvm_cycles + 1 + output_length; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_END_MESSAGE: /* 35 */ + /* + * END-MESSAGE (%requested_feedback_location, + * %returned_parameters_location, %state_length, %state_address, + * %state_instruction, %minimum_access_length, + * %state_retention_priority) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## END-MESSAGE (requested_feedback_location,state_instruction, minimum_access_length,state_retention_priority)", + current_address); + } + operand_address = current_address + 1; + + /* %requested_feedback_location */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &requested_feedback_location); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u requested_feedback_location %u", + operand_address, requested_feedback_location); + } + operand_address = next_operand_address; + /* returned_parameters_location */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &returned_parameters_location); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u returned_parameters_location %u", + operand_address, returned_parameters_location); + } + operand_address = next_operand_address; + /* + * %state_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_length %u", + operand_address, state_length); + } + /* + * %state_address + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_address %u", + operand_address, state_address); + } + /* + * %state_instruction + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_instruction); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_instruction %u", + operand_address, state_instruction); + } + + /* + * %minimum_access_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &minimum_access_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u minimum_access_length %u", + operand_address, minimum_access_length); + } + + /* + * %state_retention_priority + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_retention_priority); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_retention_priority %u", + operand_address, state_retention_priority); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## END-MESSAGE (requested_feedback_location=%u, returned_parameters_location=%u, state_length=%u, state_address=%u, state_instruction=%u, minimum_access_length=%u, state_retention_priority=%u)", + current_address, requested_feedback_location, returned_parameters_location, state_length, state_address, state_instruction, minimum_access_length,state_retention_priority); + } + current_address = next_operand_address; + /* TODO: This isn't currently totaly correct as END_INSTRUCTION might not create state */ + no_of_state_create++; + if ( no_of_state_create > 4 ){ + result_code = 12; + goto decompression_failure; + } + state_length_buff[no_of_state_create] = state_length; + state_address_buff[no_of_state_create] = state_address; + state_instruction_buff[no_of_state_create] = state_instruction; + /* Not used ? */ + state_minimum_access_length_buff[no_of_state_create] = minimum_access_length; + state_state_retention_priority_buff[no_of_state_create] = state_retention_priority; + + /* Execute the instruction + */ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"no_of_state_create %u",no_of_state_create); + if ( no_of_state_create != 0 ){ + memset(sha1_digest_buf, 0, STATE_BUFFER_SIZE); + n = 1; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + while ( n < no_of_state_create + 1 ){ + sha1buff = g_malloc(state_length_buff[n]+8); + sha1buff[0] = state_length_buff[n] >> 8; + sha1buff[1] = state_length_buff[n] & 0xff; + sha1buff[2] = state_address_buff[n] >> 8; + sha1buff[3] = state_address_buff[n] & 0xff; + sha1buff[4] = state_instruction_buff[n] >> 8; + sha1buff[5] = state_instruction_buff[n] & 0xff; + sha1buff[6] = state_minimum_access_length_buff[n] >> 8; + sha1buff[7] = state_minimum_access_length_buff[n] & 0xff; + if (print_level_3 ){ + for( x=0; x < 8; x++){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"sha1buff %u 0x%x", + x,sha1buff[x]); + } + } + k = state_address_buff[n]; + for( x=0; x < state_length_buff[n]; x++) + { + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + sha1buff[8+x] = buff[k]; + k = ( k + 1 ) & 0xffff; + } + + sha1_starts( &ctx ); + sha1_update( &ctx, (guint8 *) sha1buff, state_length_buff[n] + 8); + sha1_finish( &ctx, sha1_digest_buf ); + if (print_level_3 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"SHA1 digest %s",bytes_to_str(sha1_digest_buf, STATE_BUFFER_SIZE)); + + } + udvm_state_create(sha1buff, sha1_digest_buf, state_minimum_access_length_buff[n]); + proto_tree_add_text(udvm_tree,bytecode_tvb, 0, -1,"### Creating state ###"); + proto_tree_add_string(udvm_tree,hf_id, bytecode_tvb, 0, 0, bytes_to_str(sha1_digest_buf, state_minimum_access_length_buff[n])); + + n++; + + } + } + + + + /* At least something got decompressed, show it */ + decomp_tvb = tvb_new_real_data(out_buff,output_address,output_address); + /* Arrange that the allocated packet data copy be freed when the + * tvbuff is freed. + */ + tvb_set_free_cb( decomp_tvb, g_free ); + + tvb_set_child_real_data_tvbuff(message_tvb,decomp_tvb); + add_new_data_source(pinfo, decomp_tvb, "Decompressed SigComp message"); + /* + proto_tree_add_text(udvm_tree, decomp_tvb, 0, -1,"SigComp message Decompressed"); + */ + used_udvm_cycles = used_udvm_cycles + 1 + state_length; + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"maximum_UDVM_cycles %u used_udvm_cycles %u", + maximum_UDVM_cycles, used_udvm_cycles); + return decomp_tvb; + break; + + default: + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," ### Addr %u Invalid instruction: %u (0x%x)", + current_address,current_instruction,current_instruction); + break; + } + g_free(out_buff); + return NULL; +decompression_failure: + + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"DECOMPRESSION FAILURE: %s", + val_to_str(result_code, result_code_vals,"Unknown (%u)")); + g_free(out_buff); + return NULL; + +} + + /* The simplest operand type is the literal (#), which encodes a + * constant integer from 0 to 65535 inclusive. A literal operand may + * require between 1 and 3 bytes depending on its value. + * Bytecode: Operand value: Range: + * 0nnnnnnn N 0 - 127 + * 10nnnnnn nnnnnnnn N 0 - 16383 + * 11000000 nnnnnnnn nnnnnnnn N 0 - 65535 + * + * Figure 8: Bytecode for a literal (#) operand + * + */ +static int +decode_udvm_literal_operand(guint8 *buff,guint operand_address, guint16 *value) +{ + guint bytecode; + guint16 operand; + guint test_bits; + guint offset = operand_address; + guint8 temp_data; + + bytecode = buff[operand_address]; + test_bits = bytecode >> 7; + if (test_bits == 1){ + test_bits = bytecode >> 6; + if (test_bits == 2){ + /* + * 10nnnnnn nnnnnnnn N 0 - 16383 + */ + temp_data = buff[operand_address] & 0x1f; + operand = temp_data << 8; + temp_data = buff[operand_address + 1]; + operand = operand | temp_data; + *value = operand; + offset = offset + 2; + + }else{ + /* + * 111000000 nnnnnnnn nnnnnnnn N 0 - 65535 + */ + offset ++; + temp_data = buff[operand_address] & 0x1f; + operand = temp_data << 8; + temp_data = buff[operand_address + 1]; + operand = operand | temp_data; + *value = operand; + offset = offset + 2; + + } + }else{ + /* + * 0nnnnnnn N 0 - 127 + */ + operand = ( bytecode & 0x7f); + *value = operand; + offset ++; + } + + return offset; + +} + +/* + * The second operand type is the reference ($), which is always used to + * access a 2-byte value located elsewhere in the UDVM memory. The + * bytecode for a reference operand is decoded to be a constant integer + * from 0 to 65535 inclusive, which is interpreted as the memory address + * containing the actual value of the operand. + * Bytecode: Operand value: Range: + * + * 0nnnnnnn memory[2 * N] 0 - 65535 + * 10nnnnnn nnnnnnnn memory[2 * N] 0 - 65535 + * 11000000 nnnnnnnn nnnnnnnn memory[N] 0 - 65535 + * + * Figure 9: Bytecode for a reference ($) operand + */ +static int +dissect_udvm_reference_operand(guint8 *buff,guint operand_address, guint16 *value,guint *result_dest) +{ + guint bytecode; + guint16 operand; + guint offset = operand_address; + guint test_bits; + guint8 temp_data; + guint16 temp_data16; + + bytecode = buff[operand_address]; + test_bits = bytecode >> 7; + if (test_bits == 1){ + test_bits = bytecode >> 6; + if (test_bits == 2){ + /* + * 10nnnnnn nnnnnnnn memory[2 * N] 0 - 65535 + */ + temp_data = buff[operand_address] & 0x3f; + operand = temp_data << 8; + temp_data = buff[operand_address + 1]; + operand = operand | temp_data; + operand = (operand * 2); + *result_dest = operand; + temp_data16 = buff[operand] << 8; + temp_data16 = temp_data16 | buff[operand+1]; + *value = temp_data16; + offset = offset + 2; + + }else{ + /* + * 11000000 nnnnnnnn nnnnnnnn memory[N] 0 - 65535 + */ + operand_address++; + operand = buff[operand_address] << 8; + operand = operand | buff[operand_address + 1]; + *result_dest = operand; + temp_data16 = buff[operand] << 8; + temp_data16 = temp_data16 | buff[operand+1]; + *value = temp_data16; + offset = offset + 3; + + } + }else{ + /* + * 0nnnnnnn memory[2 * N] 0 - 65535 + */ + operand = ( bytecode & 0x7f); + operand = (operand * 2); + *result_dest = operand; + temp_data16 = buff[operand] << 8; + temp_data16 = temp_data16 | buff[operand+1]; + *value = temp_data16; + offset ++; + } + + return offset; +} + + /* RFC3320 + * Figure 10: Bytecode for a multitype (%) operand + * Bytecode: Operand value: Range: HEX val + * 00nnnnnn N 0 - 63 0x00 + * 01nnnnnn memory[2 * N] 0 - 65535 0x40 + * 1000011n 2 ^ (N + 6) 64 , 128 0x86 + * 10001nnn 2 ^ (N + 8) 256 , ... , 32768 0x88 + * 111nnnnn N + 65504 65504 - 65535 0xe0 + * 1001nnnn nnnnnnnn N + 61440 61440 - 65535 0x90 + * 101nnnnn nnnnnnnn N 0 - 8191 0xa0 + * 110nnnnn nnnnnnnn memory[N] 0 - 65535 0xc0 + * 10000000 nnnnnnnn nnnnnnnn N 0 - 65535 0x80 + * 10000001 nnnnnnnn nnnnnnnn memory[N] 0 - 65535 0x81 + */ +static int +decode_udvm_multitype_operand(guint8 *buff,guint operand_address, guint16 *value) +{ + guint test_bits; + guint bytecode; + guint offset = operand_address; + guint16 operand; + guint32 result; + guint8 temp_data; + guint16 temp_data16; + guint16 memmory_addr = 0; + + bytecode = buff[operand_address]; + test_bits = ( bytecode & 0xc0 ) >> 6; + switch (test_bits ){ + case 0: + /* + * 00nnnnnn N 0 - 63 + */ + operand = buff[operand_address]; + /* debug + *g_warning("Reading 0x%x From address %u",operand,offset); + */ + *value = operand; + offset ++; + break; + case 1: + /* + * 01nnnnnn memory[2 * N] 0 - 65535 + */ + memmory_addr = ( bytecode & 0x3f) * 2; + temp_data16 = buff[memmory_addr] << 8; + temp_data16 = temp_data16 | buff[memmory_addr+1]; + *value = temp_data16; + offset ++; + break; + case 2: + /* Check tree most significant bits */ + test_bits = ( bytecode & 0xe0 ) >> 5; + if ( test_bits == 5 ){ + /* + * 101nnnnn nnnnnnnn N 0 - 8191 + */ + temp_data = buff[operand_address] & 0x1f; + operand = temp_data << 8; + temp_data = buff[operand_address + 1]; + operand = operand | temp_data; + *value = operand; + offset = offset + 2; + }else{ + test_bits = ( bytecode & 0xf0 ) >> 4; + if ( test_bits == 9 ){ + /* + * 1001nnnn nnnnnnnn N + 61440 61440 - 65535 + */ + temp_data = buff[operand_address] & 0x0f; + operand = temp_data << 8; + temp_data = buff[operand_address + 1]; + operand = operand | temp_data; + operand = operand + 61440; + *value = operand; + offset = offset + 2; + }else{ + test_bits = ( bytecode & 0x08 ) >> 3; + if ( test_bits == 1){ + /* + * 10001nnn 2 ^ (N + 8) 256 , ... , 32768 + */ + + result = 1 << ((buff[operand_address] & 0x07) + 8); + operand = result & 0xffff; + *value = operand; + offset ++; + }else{ + test_bits = ( bytecode & 0x0e ) >> 1; + if ( test_bits == 3 ){ + /* + * 1000 011n 2 ^ (N + 6) 64 , 128 + */ + result = 1 << ((buff[operand_address] & 0x01) + 6); + operand = result & 0xffff; + *value = operand; + offset ++; + }else{ + /* + * 1000 0000 nnnnnnnn nnnnnnnn N 0 - 65535 + * 1000 0001 nnnnnnnn nnnnnnnn memory[N] 0 - 65535 + */ + offset ++; + temp_data16 = buff[operand_address + 1] << 8; + temp_data16 = temp_data16 | buff[operand_address + 2]; + /* debug + * g_warning("Reading 0x%x From address %u",temp_data16,operand_address); + */ + if ( (bytecode & 0x01) == 1 ){ + memmory_addr = temp_data16; + temp_data16 = buff[memmory_addr] << 8; + temp_data16 = temp_data16 | buff[memmory_addr+1]; + } + *value = temp_data16; + offset = offset +2; + } + + + } + } + } + break; + + case 3: + test_bits = ( bytecode & 0x20 ) >> 5; + if ( test_bits == 1 ){ + /* + * 111nnnnn N + 65504 65504 - 65535 + */ + operand = ( buff[operand_address] & 0x1f) + 65504; + *value = operand; + offset ++; + }else{ + /* + * 110nnnnn nnnnnnnn memory[N] 0 - 65535 + */ + memmory_addr = buff[operand_address] & 0x1f; + memmory_addr = memmory_addr << 8; + memmory_addr = memmory_addr | buff[operand_address + 1]; + temp_data16 = buff[memmory_addr] << 8; + temp_data16 = temp_data16 | buff[memmory_addr+1]; + *value = temp_data16; + /* debug + * g_warning("Reading 0x%x From address %u",temp_data16,memmory_addr); + */ + offset = offset +2; + } + + default : + break; + } + return offset; +} + /* + * + * The fourth operand type is the address (@). This operand is decoded + * as a multitype operand followed by a further step: the memory address + * of the UDVM instruction containing the address operand is added to + * obtain the correct operand value. So if the operand value from + * Figure 10 is D then the actual operand value of an address is + * calculated as follows: + * + * operand_value = (memory_address_of_instruction + D) modulo 2^16 + * + * Address operands are always used in instructions that control program + * flow, because they ensure that the UDVM bytecode is position- + * independent code (i.e., it will run independently of where it is + * placed in the UDVM memory). + */ +static int +decode_udvm_address_operand(guint8 *buff,guint operand_address, guint16 *value,guint current_address) +{ + guint32 result; + guint16 value1; + guint next_opreand_address; + + next_opreand_address = decode_udvm_multitype_operand(buff, operand_address, &value1); + result = value1 & 0xffff; + result = result + current_address; + *value = result & 0xffff; + return next_opreand_address; +} + + +/* + * This is a lookup table used to reverse the bits in a byte. + */ +static guint8 reverse [] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + + +static int +decomp_dispatch_get_bits( + tvbuff_t *message_tvb, + proto_tree *udvm_tree, + guint8 bit_order, + guint8 *buff, + guint16 *old_input_bit_order, + guint16 *remaining_bits, + guint16 *input_bits, + guint *input_address, + guint16 length, + guint16 *result_code, + guint msg_end) +{ + guint16 input_bit_order; + guint16 bits_still_required = length; + guint16 value = 0; + guint8 octet; + gint extra_bytes_available = msg_end - *input_address; + gint p_bit; + gint prev_p_bit = *old_input_bit_order & 0x0001; + gint bits_to_use = 0; + + + input_bit_order = buff[68] << 8; + input_bit_order = input_bit_order | buff[69]; + *result_code = 0; + p_bit = (input_bit_order & 0x0001) != 0; + + /* + * Discard any spare bits. + * Note: We take care to avoid remaining_bits having the value of 8. + */ + if (prev_p_bit != p_bit) + { + *remaining_bits = 0; + *old_input_bit_order = input_bit_order; + } + + /* + * Check we can suppy the required number of bits now, before we alter + * the input buffer's state. + */ + if (*remaining_bits + extra_bytes_available * 8 < length) + { + *result_code = 11; + return 0xfbad; + } + + /* Note: This is never called with length > 16, so the following loop + * never loops more than three time. */ + while (bits_still_required > 0) + { + /* + * We only put anything into input_bits if we know we will remove + * at least one bit. That ensures we can simply discard the spare + * bits if the P-bit changes. + */ + if (*remaining_bits == 0) + { + octet = tvb_get_guint8(message_tvb, *input_address); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, message_tvb, *input_address , 1, + " Geting value: %u (0x%x) From Addr: %u", octet, octet, *input_address); + } + *input_address = *input_address + 1; + + if (p_bit != 0) + { + octet = reverse[octet]; + } + *input_bits = octet; + *remaining_bits = 8; + } + + /* Add some more bits to the accumulated value. */ + bits_to_use = bits_still_required < *remaining_bits ? bits_still_required : *remaining_bits; + bits_still_required -= bits_to_use; + + *input_bits <<= bits_to_use; /* Shift bits into MSByte */ + value = (value << bits_to_use) /* Then add to the accumulated value */ + | ((*input_bits >> 8) & 0xFF); + *remaining_bits -= bits_to_use; + *input_bits &= 0x00FF; /* Leave just the remaining bits */ + } + + if (bit_order != 0) + { + /* Bit reverse the entire word. */ + guint16 lsb = reverse[(value >> 8) & 0xFF]; + guint16 msb = reverse[value & 0xFF]; + + value = ((msb << 8) | lsb) >> (16 - length); + } + + return value; +} + + +/* end udvm */ + diff --git a/src/main/resource/testFiles/wireshark/prevFiles/prev_c3a535_1153ff_print.c b/src/main/resource/testFiles/wireshark/prevFiles/prev_c3a535_1153ff_print.c new file mode 100644 index 0000000..950e139 --- /dev/null +++ b/src/main/resource/testFiles/wireshark/prevFiles/prev_c3a535_1153ff_print.c @@ -0,0 +1,1737 @@ +/* print.c + * Routines for printing packet analysis trees. + * + * $Id$ + * + * Gilbert Ramirez + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "packet-range.h" +#include "print.h" +#include "isprint.h" +#include "ps.h" +#include "version_info.h" +#include +#include +#include +#include +#include + +#define PDML_VERSION "0" +#define PSML_VERSION "0" + +typedef struct { + int level; + print_stream_t *stream; + gboolean success; + GSList *src_list; + print_dissections_e print_dissections; + gboolean print_hex_for_data; + packet_char_enc encoding; + epan_dissect_t *edt; +} print_data; + +typedef struct { + int level; + FILE *fh; + GSList *src_list; + epan_dissect_t *edt; +} write_pdml_data; + +typedef struct { + output_fields_t* fields; + epan_dissect_t *edt; +} write_field_data_t; + +struct _output_fields { + gboolean print_header; + gchar separator; + gchar occurrence; + gchar aggregator; + GPtrArray* fields; + GHashTable* field_indicies; + emem_strbuf_t** field_values; + gchar quote; +}; + +GHashTable *output_only_tables = NULL; + +static gboolean write_headers = FALSE; + +static const gchar* get_field_hex_value(GSList* src_list, field_info *fi); +static void proto_tree_print_node(proto_node *node, gpointer data); +static void proto_tree_write_node_pdml(proto_node *node, gpointer data); +static const guint8 *get_field_data(GSList *src_list, field_info *fi); +static void write_pdml_field_hex_value(write_pdml_data *pdata, field_info *fi); +static gboolean print_hex_data_buffer(print_stream_t *stream, const guchar *cp, + guint length, packet_char_enc encoding); +static void ps_clean_string(unsigned char *out, const unsigned char *in, + int outbuf_size); +static void print_escaped_xml(FILE *fh, const char *unescaped_string); + +static void print_pdml_geninfo(proto_tree *tree, FILE *fh); + +static void proto_tree_get_node_field_values(proto_node *node, gpointer data); + +static FILE * +open_print_dest(int to_file, const char *dest) +{ + FILE *fh; + + /* Open the file or command for output */ + if (to_file) + fh = ws_fopen(dest, "w"); + else + fh = popen(dest, "w"); + + return fh; +} + +static gboolean +close_print_dest(int to_file, FILE *fh) +{ + /* Close the file or command */ + if (to_file) + return (fclose(fh) == 0); + else + return (pclose(fh) == 0); +} + +#define MAX_PS_LINE_LENGTH 256 + +gboolean +proto_tree_print(print_args_t *print_args, epan_dissect_t *edt, + print_stream_t *stream) +{ + print_data data; + + /* Create the output */ + data.level = 0; + data.stream = stream; + data.success = TRUE; + data.src_list = edt->pi.data_src; + data.encoding = edt->pi.fd->flags.encoding; + data.print_dissections = print_args->print_dissections; + /* If we're printing the entire packet in hex, don't + print uninterpreted data fields in hex as well. */ + data.print_hex_for_data = !print_args->print_hex; + data.edt = edt; + + proto_tree_children_foreach(edt->tree, proto_tree_print_node, &data); + return data.success; +} + +#define MAX_INDENT 160 + +/* Print a tree's data, and any child nodes. */ +static +void proto_tree_print_node(proto_node *node, gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + print_data *pdata = (print_data*) data; + const guint8 *pd; + gchar label_str[ITEM_LABEL_LENGTH]; + gchar *label_ptr; + + g_assert(fi && "dissection with an invisible proto tree?"); + + /* Don't print invisible entries. */ + if (PROTO_ITEM_IS_HIDDEN(node)) + return; + + /* Give up if we've already gotten an error. */ + if (!pdata->success) + return; + + /* was a free format label produced? */ + if (fi->rep) { + label_ptr = fi->rep->representation; + } + else { /* no, make a generic label */ + label_ptr = label_str; + proto_item_fill_label(fi, label_str); + } + + if (PROTO_ITEM_IS_GENERATED(node)) { + label_ptr = g_strdup_printf("[%s]", label_ptr); + } + + if (!print_line(pdata->stream, pdata->level, label_ptr)) { + pdata->success = FALSE; + return; + } + + /* + * If -O is specified, only display the protocols which are in the + * lookup table. Only check on the first level: once we start printing + * a tree, print the rest of the subtree. Otherwise we won't print + * subitems whose abbreviation doesn't match the protocol--for example + * text items (whose abbreviation is simply "text"). + */ + if (output_only_tables != NULL && pdata->level == 0 + && g_hash_table_lookup(output_only_tables, fi->hfinfo->abbrev) == NULL) { + pdata->success = TRUE; + return; + } + + if (PROTO_ITEM_IS_GENERATED(node)) { + g_free(label_ptr); + } + + /* If it's uninterpreted data, dump it (unless our caller will + be printing the entire packet in hex). */ + if (fi->hfinfo->id == proto_data && pdata->print_hex_for_data) { + /* + * Find the data for this field. + */ + pd = get_field_data(pdata->src_list, fi); + if (pd) { + if (!print_hex_data_buffer(pdata->stream, pd, + fi->length, pdata->encoding)) { + pdata->success = FALSE; + return; + } + } + } + + /* If we're printing all levels, or if this node is one with a + subtree and its subtree is expanded, recurse into the subtree, + if it exists. */ + g_assert(fi->tree_type >= -1 && fi->tree_type < num_tree_types); + if (pdata->print_dissections == print_dissections_expanded || + (pdata->print_dissections == print_dissections_as_displayed && + fi->tree_type >= 0 && tree_is_expanded[fi->tree_type])) { + if (node->first_child != NULL) { + pdata->level++; + proto_tree_children_foreach(node, + proto_tree_print_node, pdata); + pdata->level--; + if (!pdata->success) + return; + } + } +} + +#define PDML2HTML_XSL "pdml2html.xsl" +void +write_pdml_preamble(FILE *fh, const gchar* filename) +{ + time_t t=time(NULL); + char *ts=asctime(localtime(&t)); + ts[strlen(ts)-1]=0; /* overwrite \n */ + + fputs("\n", fh); + fputs("\n", fh); + fprintf(fh, "\n", get_datafile_dir()); + fputs("\n", PACKAGE, VERSION, ts, filename ? filename : ""); +} + +void +proto_tree_write_pdml(epan_dissect_t *edt, FILE *fh) +{ + write_pdml_data data; + + /* Create the output */ + data.level = 0; + data.fh = fh; + data.src_list = edt->pi.data_src; + data.edt = edt; + + fprintf(fh, "\n"); + + /* Print a "geninfo" protocol as required by PDML */ + print_pdml_geninfo(edt->tree, fh); + + proto_tree_children_foreach(edt->tree, proto_tree_write_node_pdml, + &data); + + fprintf(fh, "\n\n"); +} + +/* Write out a tree's data, and any child nodes, as PDML */ +static void +proto_tree_write_node_pdml(proto_node *node, gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + write_pdml_data *pdata = (write_pdml_data*) data; + const gchar *label_ptr; + gchar label_str[ITEM_LABEL_LENGTH]; + char *dfilter_string; + size_t chop_len; + int i; + gboolean wrap_in_fake_protocol; + + g_assert(fi && "dissection with an invisible proto tree?"); + + /* Will wrap up top-level field items inside a fake protocol wrapper to + preserve the PDML schema */ + wrap_in_fake_protocol = + (((fi->hfinfo->type != FT_PROTOCOL) || + (fi->hfinfo->id == proto_data)) && + (pdata->level == 0)); + + /* Indent to the correct level */ + for (i = -1; i < pdata->level; i++) { + fputs(" ", pdata->fh); + } + + if (wrap_in_fake_protocol) { + /* Open fake protocol wrapper */ + fputs("\n", pdata->fh); + + /* Indent to increased level before writint out field */ + pdata->level++; + for (i = -1; i < pdata->level; i++) { + fputs(" ", pdata->fh); + } + } + + /* Text label. It's printed as a field with no name. */ + if (fi->hfinfo->id == hf_text_only) { + /* Get the text */ + if (fi->rep) { + label_ptr = fi->rep->representation; + } + else { + label_ptr = ""; + } + + /* Show empty name since it is a required field */ + fputs("fh); + fputs("\" show=\"", pdata->fh); + print_escaped_xml(pdata->fh, label_ptr); + + fprintf(pdata->fh, "\" size=\"%d", fi->length); + if (node->parent && node->parent->finfo && (fi->start < node->parent->finfo->start)) { + fprintf(pdata->fh, "\" pos=\"%d", node->parent->finfo->start + fi->start); + } else { + fprintf(pdata->fh, "\" pos=\"%d", fi->start); + } + + fputs("\" value=\"", pdata->fh); + write_pdml_field_hex_value(pdata, fi); + + if (node->first_child != NULL) { + fputs("\">\n", pdata->fh); + } + else { + fputs("\"/>\n", pdata->fh); + } + } + + /* Uninterpreted data, i.e., the "Data" protocol, is + * printed as a field instead of a protocol. */ + else if (fi->hfinfo->id == proto_data) { + + /* Write out field with data */ + fputs("fh); + write_pdml_field_hex_value(pdata, fi); + fputs("\"/>\n", pdata->fh); + } + /* Normal protocols and fields */ + else { + if (fi->hfinfo->type == FT_PROTOCOL) { + fputs("fh); + } + else { + fputs("fh); + } + print_escaped_xml(pdata->fh, fi->hfinfo->abbrev); + +#if 0 + /* PDML spec, see: + * http://www.nbee.org/doku.php?id=netpdl:pdml_specification + * + * the show fields contains things in 'human readable' format + * showname: contains only the name of the field + * show: contains only the data of the field + * showdtl: contains additional details of the field data + * showmap: contains mappings of the field data (e.g. the hostname to an IP address) + * + * XXX - the showname shouldn't contain the field data itself + * (like it's contained in the fi->rep->representation). + * Unfortunately, we don't have the field data representation for + * all fields, so this isn't currently possible */ + fputs("\" showname=\"", pdata->fh); + print_escaped_xml(pdata->fh, fi->hfinfo->name); +#endif + + if (fi->rep) { + fputs("\" showname=\"", pdata->fh); + print_escaped_xml(pdata->fh, fi->rep->representation); + } + else { + label_ptr = label_str; + proto_item_fill_label(fi, label_str); + fputs("\" showname=\"", pdata->fh); + print_escaped_xml(pdata->fh, label_ptr); + } + + if (PROTO_ITEM_IS_HIDDEN(node)) + fprintf(pdata->fh, "\" hide=\"yes"); + + fprintf(pdata->fh, "\" size=\"%d", fi->length); + if (node->parent && node->parent->finfo && (fi->start < node->parent->finfo->start)) { + fprintf(pdata->fh, "\" pos=\"%d", node->parent->finfo->start + fi->start); + } else { + fprintf(pdata->fh, "\" pos=\"%d", fi->start); + } +/* fprintf(pdata->fh, "\" id=\"%d", fi->hfinfo->id);*/ + + /* show, value, and unmaskedvalue attributes */ + switch (fi->hfinfo->type) + { + case FT_PROTOCOL: + break; + case FT_NONE: + fputs("\" show=\"\" value=\"", pdata->fh); + break; + default: + /* XXX - this is a hack until we can just call + * fvalue_to_string_repr() for *all* FT_* types. */ + dfilter_string = proto_construct_match_selected_string(fi, + pdata->edt); + if (dfilter_string != NULL) { + chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */ + + /* XXX - Remove double-quotes. Again, once we + * can call fvalue_to_string_repr(), we can + * ask it not to produce the version for + * display-filters, and thus, no + * double-quotes. */ + if (dfilter_string[strlen(dfilter_string)-1] == '"') { + dfilter_string[strlen(dfilter_string)-1] = '\0'; + chop_len++; + } + + fputs("\" show=\"", pdata->fh); + print_escaped_xml(pdata->fh, &dfilter_string[chop_len]); + } + + /* + * XXX - should we omit "value" for any fields? + * What should we do for fields whose length is 0? + * They might come from a pseudo-header or from + * the capture header (e.g., time stamps), or + * they might be generated fields. + */ + if (fi->length > 0) { + fputs("\" value=\"", pdata->fh); + + if (fi->hfinfo->bitmask!=0) { + fprintf(pdata->fh, "%X", fvalue_get_uinteger(&fi->value)); + fputs("\" unmaskedvalue=\"", pdata->fh); + write_pdml_field_hex_value(pdata, fi); + } + else { + write_pdml_field_hex_value(pdata, fi); + } + } + } + + if (node->first_child != NULL) { + fputs("\">\n", pdata->fh); + } + else if (fi->hfinfo->id == proto_data) { + fputs("\">\n", pdata->fh); + } + else { + fputs("\"/>\n", pdata->fh); + } + } + + /* We always print all levels for PDML. Recurse here. */ + if (node->first_child != NULL) { + pdata->level++; + proto_tree_children_foreach(node, + proto_tree_write_node_pdml, pdata); + pdata->level--; + } + + /* Take back the extra level we added for fake wrapper protocol */ + if (wrap_in_fake_protocol) { + pdata->level--; + } + + if (node->first_child != NULL) { + /* Indent to correct level */ + for (i = -1; i < pdata->level; i++) { + fputs(" ", pdata->fh); + } + /* Close off current element */ + if (fi->hfinfo->id != proto_data) { /* Data protocol uses simple tags */ + if (fi->hfinfo->type == FT_PROTOCOL) { + fputs("\n", pdata->fh); + } + else { + fputs("\n", pdata->fh); + } + } + } + + /* Close off fake wrapper protocol */ + if (wrap_in_fake_protocol) { + fputs("\n", pdata->fh); + } +} + +/* Print info for a 'geninfo' pseudo-protocol. This is required by + * the PDML spec. The information is contained in Wireshark's 'frame' protocol, + * but we produce a 'geninfo' protocol in the PDML to conform to spec. + * The 'frame' protocol follows the 'geninfo' protocol in the PDML. */ +static void +print_pdml_geninfo(proto_tree *tree, FILE *fh) +{ + guint32 num, len, caplen; + nstime_t *timestamp; + GPtrArray *finfo_array; + field_info *frame_finfo; + + /* Get frame protocol's finfo. */ + finfo_array = proto_find_finfo(tree, proto_frame); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + frame_finfo = (field_info *)finfo_array->pdata[0]; + g_ptr_array_free(finfo_array, TRUE); + + /* frame.number --> geninfo.num */ + finfo_array = proto_find_finfo(tree, hf_frame_number); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + num = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* frame.frame_len --> geninfo.len */ + finfo_array = proto_find_finfo(tree, hf_frame_len); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + len = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* frame.cap_len --> geninfo.caplen */ + finfo_array = proto_find_finfo(tree, hf_frame_capture_len); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + caplen = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* frame.time --> geninfo.timestamp */ + finfo_array = proto_find_finfo(tree, hf_frame_arrival_time); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + timestamp = (nstime_t *)fvalue_get(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* Print geninfo start */ + fprintf(fh, +" \n", + frame_finfo->length); + + /* Print geninfo.num */ + fprintf(fh, +" \n", + num, num, frame_finfo->length); + + /* Print geninfo.len */ + fprintf(fh, +" \n", + len, len, frame_finfo->length); + + /* Print geninfo.caplen */ + fprintf(fh, +" \n", + caplen, caplen, frame_finfo->length); + + /* Print geninfo.timestamp */ + fprintf(fh, +" \n", + abs_time_to_str(timestamp, ABSOLUTE_TIME_LOCAL, TRUE), (int) timestamp->secs, timestamp->nsecs, frame_finfo->length); + + /* Print geninfo end */ + fprintf(fh, +" \n"); +} + +void +write_pdml_finale(FILE *fh) +{ + fputs("\n", fh); +} + +void +write_psml_preamble(FILE *fh) +{ + fputs("\n", fh); + fputs("\n", PACKAGE, VERSION); + write_headers = TRUE; +} + +void +proto_tree_write_psml(epan_dissect_t *edt, FILE *fh) +{ + gint i; + + /* if this is the first packet, we have to create the PSML structure output */ + if(write_headers) { + fprintf(fh, "\n"); + + for(i=0; i < edt->pi.cinfo->num_cols; i++) { + fprintf(fh, "
"); + print_escaped_xml(fh, edt->pi.cinfo->col_title[i]); + fprintf(fh, "
\n"); + } + + fprintf(fh, "
\n\n"); + + write_headers = FALSE; + } + + fprintf(fh, "\n"); + + for(i=0; i < edt->pi.cinfo->num_cols; i++) { + fprintf(fh, "
"); + print_escaped_xml(fh, edt->pi.cinfo->col_data[i]); + fprintf(fh, "
\n"); + } + + fprintf(fh, "
\n\n"); +} + +void +write_psml_finale(FILE *fh) +{ + fputs("
\n", fh); +} + +void +write_csv_preamble(FILE *fh _U_) +{ + write_headers = TRUE; +} + +static gchar *csv_massage_str(const gchar *source, const gchar *exceptions) +{ + gchar *csv_str; + gchar *tmp_str; + + csv_str = g_strescape(source, exceptions); + tmp_str = csv_str; + while ( (tmp_str = strstr(tmp_str, "\\\"")) != NULL ) + *tmp_str = '\"'; + return csv_str; +} + +static void csv_write_str(const char *str, char sep, FILE *fh) +{ + gchar *csv_str; + + csv_str = csv_massage_str(str, NULL); + fprintf(fh, "\"%s\"%c", csv_str, sep); + g_free(csv_str); +} + +void +proto_tree_write_csv(epan_dissect_t *edt, FILE *fh) +{ + gint i; + + /* if this is the first packet, we have to write the CSV header */ + if(write_headers) { + for(i=0; i < edt->pi.cinfo->num_cols - 1; i++) + csv_write_str(edt->pi.cinfo->col_title[i], ',', fh); + csv_write_str(edt->pi.cinfo->col_title[i], '\n', fh); + write_headers = FALSE; + } + + for(i=0; i < edt->pi.cinfo->num_cols - 1; i++) + csv_write_str(edt->pi.cinfo->col_data[i], ',', fh); + csv_write_str(edt->pi.cinfo->col_data[i], '\n', fh); +} + +void +write_csv_finale(FILE *fh _U_) +{ + +} + +void +write_carrays_preamble(FILE *fh _U_) +{ + +} + +void +proto_tree_write_carrays(guint32 num, FILE *fh, epan_dissect_t *edt) +{ + guint32 i = 0, src_num = 0; + GSList *src_le; + data_source *src; + tvbuff_t *tvb; + const char *name; + const guchar *cp; + guint length; + char ascii[9]; + + for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) { + memset(ascii, 0, sizeof(ascii)); + src = (data_source *)src_le->data; + tvb = src->tvb; + length = tvb_length(tvb); + if (length == 0) + continue; + + cp = tvb_get_ptr(tvb, 0, length); + + name = get_data_source_name(src); + if (name) + fprintf(fh, "/* %s */\n", name); + if (src_num) { + fprintf(fh, "static const unsigned char pkt%u_%u[%u] = {\n", + num, src_num, length); + } else { + fprintf(fh, "static const unsigned char pkt%u[%u] = {\n", + num, length); + } + src_num++; + + for (i = 0; i < length; i++) { + fprintf(fh, "0x%02x", *(cp + i)); + ascii[i % 8] = isprint(*(cp + i)) ? *(cp + i) : '.'; + + if (i == (length - 1)) { + guint rem; + rem = length % 8; + if (rem) { + guint j; + for ( j = 0; j < 8 - rem; j++ ) + fprintf(fh, " "); + } + fprintf(fh, " /* %s */\n};\n\n", ascii); + break; + } + + if (!((i + 1) % 8)) { + fprintf(fh, ", /* %s */\n", ascii); + memset(ascii, 0, sizeof(ascii)); + } + else { + fprintf(fh, ", "); + } + } + } +} + +void +write_carrays_finale(FILE *fh _U_) +{ + +} + +/* + * Find the data source for a specified field, and return a pointer + * to the data in it. Returns NULL if the data is out of bounds. + */ +static const guint8 * +get_field_data(GSList *src_list, field_info *fi) +{ + GSList *src_le; + data_source *src; + tvbuff_t *src_tvb; + gint length, tvbuff_length; + + for (src_le = src_list; src_le != NULL; src_le = src_le->next) { + src = (data_source *)src_le->data; + src_tvb = src->tvb; + if (fi->ds_tvb == src_tvb) { + /* + * Found it. + * + * XXX - a field can have a length that runs past + * the end of the tvbuff. Ideally, that should + * be fixed when adding an item to the protocol + * tree, but checking the length when doing + * that could be expensive. Until we fix that, + * we'll do the check here. + */ + tvbuff_length = tvb_length_remaining(src_tvb, + fi->start); + if (tvbuff_length < 0) { + return NULL; + } + length = fi->length; + if (length > tvbuff_length) + length = tvbuff_length; + return tvb_get_ptr(src_tvb, fi->start, length); + } + } + g_assert_not_reached(); + return NULL; /* not found */ +} + +/* Print a string, escaping out certain characters that need to + * escaped out for XML. */ +static void +print_escaped_xml(FILE *fh, const char *unescaped_string) +{ + const char *p; + char temp_str[8]; + + for (p = unescaped_string; *p != '\0'; p++) { + switch (*p) { + case '&': + fputs("&", fh); + break; + case '<': + fputs("<", fh); + break; + case '>': + fputs(">", fh); + break; + case '"': + fputs(""", fh); + break; + case '\'': + fputs("'", fh); + break; + default: + if (g_ascii_isprint(*p)) + fputc(*p, fh); + else { + g_snprintf(temp_str, sizeof(temp_str), "\\x%x", (guint8)*p); + fputs(temp_str, fh); + } + } + } +} + +static void +write_pdml_field_hex_value(write_pdml_data *pdata, field_info *fi) +{ + int i; + const guint8 *pd; + + if (!fi->ds_tvb) + return; + + if (fi->length > tvb_length_remaining(fi->ds_tvb, fi->start)) { + fprintf(pdata->fh, "field length invalid!"); + return; + } + + /* Find the data for this field. */ + pd = get_field_data(pdata->src_list, fi); + + if (pd) { + /* Print a simple hex dump */ + for (i = 0 ; i < fi->length; i++) { + fprintf(pdata->fh, "%02x", pd[i]); + } + } +} + +gboolean +print_hex_data(print_stream_t *stream, epan_dissect_t *edt) +{ + gboolean multiple_sources; + GSList *src_le; + data_source *src; + tvbuff_t *tvb; + const char *name; + char *line; + const guchar *cp; + guint length; + + /* + * Set "multiple_sources" iff this frame has more than one + * data source; if it does, we need to print the name of + * the data source before printing the data from the + * data source. + */ + multiple_sources = (edt->pi.data_src->next != NULL); + + for (src_le = edt->pi.data_src; src_le != NULL; + src_le = src_le->next) { + src = (data_source *)src_le->data; + tvb = src->tvb; + if (multiple_sources) { + name = get_data_source_name(src); + print_line(stream, 0, ""); + line = g_strdup_printf("%s:", name); + print_line(stream, 0, line); + g_free(line); + } + length = tvb_length(tvb); + if (length == 0) + return TRUE; + cp = tvb_get_ptr(tvb, 0, length); + if (!print_hex_data_buffer(stream, cp, length, + edt->pi.fd->flags.encoding)) + return FALSE; + } + return TRUE; +} + +/* + * This routine is based on a routine created by Dan Lasley + * . + * + * It was modified for Wireshark by Gilbert Ramirez and others. + */ + +#define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */ +#define BYTES_PER_LINE 16 /* max byte values printed on a line */ +#define HEX_DUMP_LEN (BYTES_PER_LINE*3) + /* max number of characters hex dump takes - + 2 digits plus trailing blank */ +#define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE) + /* number of characters those bytes take; + 3 characters per byte of hex dump, + 2 blanks separating hex from ASCII, + 1 character per byte of ASCII dump */ +#define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN) + /* number of characters per line; + offset, 2 blanks separating offset + from data dump, data dump */ + +static gboolean +print_hex_data_buffer(print_stream_t *stream, const guchar *cp, + guint length, packet_char_enc encoding) +{ + register unsigned int ad, i, j, k, l; + guchar c; + guchar line[MAX_LINE_LEN + 1]; + unsigned int use_digits; + static guchar binhex[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + if (!print_line(stream, 0, "")) + return FALSE; + + /* + * How many of the leading digits of the offset will we supply? + * We always supply at least 4 digits, but if the maximum offset + * won't fit in 4 digits, we use as many digits as will be needed. + */ + if (((length - 1) & 0xF0000000) != 0) + use_digits = 8; /* need all 8 digits */ + else if (((length - 1) & 0x0F000000) != 0) + use_digits = 7; /* need 7 digits */ + else if (((length - 1) & 0x00F00000) != 0) + use_digits = 6; /* need 6 digits */ + else if (((length - 1) & 0x000F0000) != 0) + use_digits = 5; /* need 5 digits */ + else + use_digits = 4; /* we'll supply 4 digits */ + + ad = 0; + i = 0; + j = 0; + k = 0; + while (i < length) { + if ((i & 15) == 0) { + /* + * Start of a new line. + */ + j = 0; + l = use_digits; + do { + l--; + c = (ad >> (l*4)) & 0xF; + line[j++] = binhex[c]; + } while (l != 0); + line[j++] = ' '; + line[j++] = ' '; + memset(line+j, ' ', DATA_DUMP_LEN); + + /* + * Offset in line of ASCII dump. + */ + k = j + HEX_DUMP_LEN + 2; + } + c = *cp++; + line[j++] = binhex[c>>4]; + line[j++] = binhex[c&0xf]; + j++; + if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) { + c = EBCDIC_to_ASCII1(c); + } + line[k++] = c >= ' ' && c < 0x7f ? c : '.'; + i++; + if ((i & 15) == 0 || i == length) { + /* + * We'll be starting a new line, or + * we're finished printing this buffer; + * dump out the line we've constructed, + * and advance the offset. + */ + line[k] = '\0'; + if (!print_line(stream, 0, line)) + return FALSE; + ad += 16; + } + } + return TRUE; +} + +static +void ps_clean_string(unsigned char *out, const unsigned char *in, + int outbuf_size) +{ + int rd, wr; + char c; + + if (in == NULL) { + out[0] = '\0'; + return; + } + + for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) { + c = in[rd]; + switch (c) { + case '(': + case ')': + case '\\': + out[wr] = '\\'; + out[++wr] = c; + break; + + default: + out[wr] = c; + break; + } + + if (c == 0) { + break; + } + } +} + +/* Some formats need stuff at the beginning of the output */ +gboolean +print_preamble(print_stream_t *self, gchar *filename) +{ + return (self->ops->print_preamble)(self, filename); +} + +gboolean +print_line(print_stream_t *self, int indent, const char *line) +{ + return (self->ops->print_line)(self, indent, line); +} + +/* Insert bookmark */ +gboolean +print_bookmark(print_stream_t *self, const gchar *name, const gchar *title) +{ + return (self->ops->print_bookmark)(self, name, title); +} + +gboolean +new_page(print_stream_t *self) +{ + return (self->ops->new_page)(self); +} + +/* Some formats need stuff at the end of the output */ +gboolean +print_finale(print_stream_t *self) +{ + return (self->ops->print_finale)(self); +} + +gboolean +destroy_print_stream(print_stream_t *self) +{ + return (self->ops->destroy)(self); +} + +typedef struct { + int to_file; + FILE *fh; +} output_text; + +static gboolean +print_preamble_text(print_stream_t *self _U_, gchar *filename _U_) +{ + /* do nothing */ + return TRUE; /* always succeeds */ +} + +static gboolean +print_line_text(print_stream_t *self, int indent, const char *line) +{ + output_text *output = (output_text *)self->data; + char space[MAX_INDENT+1]; + int i; + int num_spaces; + + /* Prepare the tabs for printing, depending on tree level */ + num_spaces = indent * 4; + if (num_spaces > MAX_INDENT) { + num_spaces = MAX_INDENT; + } + for (i = 0; i < num_spaces; i++) { + space[i] = ' '; + } + /* The string is NUL-terminated */ + space[num_spaces] = '\0'; + + fputs(space, output->fh); + fputs(line, output->fh); + putc('\n', output->fh); + return !ferror(output->fh); +} + +static gboolean +print_bookmark_text(print_stream_t *self _U_, const gchar *name _U_, + const gchar *title _U_) +{ + /* do nothing */ + return TRUE; +} + +static gboolean +new_page_text(print_stream_t *self) +{ + output_text *output = (output_text *)self->data; + + fputs("\f", output->fh); + return !ferror(output->fh); +} + +static gboolean +print_finale_text(print_stream_t *self _U_) +{ + /* do nothing */ + return TRUE; /* always succeeds */ +} + +static gboolean +destroy_text(print_stream_t *self) +{ + output_text *output = (output_text *)self->data; + gboolean ret; + + ret = close_print_dest(output->to_file, output->fh); + g_free(output); + g_free(self); + return ret; +} + +static const print_stream_ops_t print_text_ops = { + print_preamble_text, + print_line_text, + print_bookmark_text, + new_page_text, + print_finale_text, + destroy_text +}; + +static print_stream_t * +print_stream_text_alloc(int to_file, FILE *fh) +{ + print_stream_t *stream; + output_text *output; + + output = (output_text *)g_malloc(sizeof *output); + output->to_file = to_file; + output->fh = fh; + stream = (print_stream_t *)g_malloc(sizeof (print_stream_t)); + stream->ops = &print_text_ops; + stream->data = output; + + return stream; +} + +print_stream_t * +print_stream_text_new(int to_file, const char *dest) +{ + FILE *fh; + + fh = open_print_dest(to_file, dest); + if (fh == NULL) + return NULL; + + return print_stream_text_alloc(to_file, fh); +} + +print_stream_t * +print_stream_text_stdio_new(FILE *fh) +{ + return print_stream_text_alloc(TRUE, fh); +} + +typedef struct { + int to_file; + FILE *fh; +} output_ps; + +static gboolean +print_preamble_ps(print_stream_t *self, gchar *filename) +{ + output_ps *output = (output_ps *)self->data; + unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */ + + print_ps_preamble(output->fh); + + fputs("%% the page title\n", output->fh); + ps_clean_string(psbuffer, filename, MAX_PS_LINE_LENGTH); + fprintf(output->fh, "/ws_pagetitle (%s - Wireshark " VERSION "%s) def\n", psbuffer, wireshark_svnversion); + fputs("\n", output->fh); + return !ferror(output->fh); +} + +static gboolean +print_line_ps(print_stream_t *self, int indent, const char *line) +{ + output_ps *output = (output_ps *)self->data; + unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */ + + ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH); + fprintf(output->fh, "%d (%s) putline\n", indent, psbuffer); + return !ferror(output->fh); +} + +static gboolean +print_bookmark_ps(print_stream_t *self, const gchar *name, const gchar *title) +{ + output_ps *output = (output_ps *)self->data; + unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */ + + /* + * See the Adobe "pdfmark reference": + * + * http://partners.adobe.com/asn/acrobat/docs/pdfmark.pdf + * + * The pdfmark stuff tells code that turns PostScript into PDF + * things that it should do. + * + * The /OUT stuff creates a bookmark that goes to the + * destination with "name" as the name and "title" as the title. + * + * The "/DEST" creates the destination. + */ + ps_clean_string(psbuffer, title, MAX_PS_LINE_LENGTH); + fprintf(output->fh, "[/Dest /%s /Title (%s) /OUT pdfmark\n", name, + psbuffer); + fputs("[/View [/XYZ -4 currentpoint matrix currentmatrix matrix defaultmatrix\n", + output->fh); + fputs("matrix invertmatrix matrix concatmatrix transform exch pop 20 add null]\n", + output->fh); + fprintf(output->fh, "/Dest /%s /DEST pdfmark\n", name); + return !ferror(output->fh); +} + +static gboolean +new_page_ps(print_stream_t *self) +{ + output_ps *output = (output_ps *)self->data; + + fputs("formfeed\n", output->fh); + return !ferror(output->fh); +} + +static gboolean +print_finale_ps(print_stream_t *self) +{ + output_ps *output = (output_ps *)self->data; + + print_ps_finale(output->fh); + return !ferror(output->fh); +} + +static gboolean +destroy_ps(print_stream_t *self) +{ + output_ps *output = (output_ps *)self->data; + gboolean ret; + + ret = close_print_dest(output->to_file, output->fh); + g_free(output); + g_free(self); + return ret; +} + +static const print_stream_ops_t print_ps_ops = { + print_preamble_ps, + print_line_ps, + print_bookmark_ps, + new_page_ps, + print_finale_ps, + destroy_ps +}; + +static print_stream_t * +print_stream_ps_alloc(int to_file, FILE *fh) +{ + print_stream_t *stream; + output_ps *output; + + output = (output_ps *)g_malloc(sizeof *output); + output->to_file = to_file; + output->fh = fh; + stream = (print_stream_t *)g_malloc(sizeof (print_stream_t)); + stream->ops = &print_ps_ops; + stream->data = output; + + return stream; +} + +print_stream_t * +print_stream_ps_new(int to_file, const char *dest) +{ + FILE *fh; + + fh = open_print_dest(to_file, dest); + if (fh == NULL) + return NULL; + + return print_stream_ps_alloc(to_file, fh); +} + +print_stream_t * +print_stream_ps_stdio_new(FILE *fh) +{ + return print_stream_ps_alloc(TRUE, fh); +} + +output_fields_t* output_fields_new(void) +{ + output_fields_t* fields = g_new(output_fields_t, 1); + fields->print_header = FALSE; + fields->separator = '\t'; + fields->occurrence = 'a'; + fields->aggregator = ','; + fields->fields = NULL; /*Do lazy initialisation */ + fields->field_indicies = NULL; + fields->field_values = NULL; + fields->quote='\0'; + return fields; +} + +gsize output_fields_num_fields(output_fields_t* fields) +{ + g_assert(fields); + + if(NULL == fields->fields) { + return 0; + } else { + return fields->fields->len; + } +} + +void output_fields_free(output_fields_t* fields) +{ + g_assert(fields); + + if(NULL != fields->field_indicies) { + /* Keys are stored in fields->fields, values are + * integers. + */ + g_hash_table_destroy(fields->field_indicies); + } + if(NULL != fields->fields) { + gsize i; + for(i = 0; i < fields->fields->len; ++i) { + gchar* field = (gchar *)g_ptr_array_index(fields->fields,i); + g_free(field); + } + g_ptr_array_free(fields->fields, TRUE); + } + + g_free(fields); +} + +void output_fields_add(output_fields_t* fields, const gchar* field) +{ + gchar* field_copy; + + g_assert(fields); + g_assert(field); + + + if(NULL == fields->fields) { + fields->fields = g_ptr_array_new(); + } + + field_copy = g_strdup(field); + + g_ptr_array_add(fields->fields, field_copy); +} + +gboolean output_fields_set_option(output_fields_t* info, gchar* option) +{ + const gchar* option_name; + const gchar* option_value; + + g_assert(info); + g_assert(option); + + if('\0' == *option) { + return FALSE; /* Is this guarded against by option parsing? */ + } + option_name = strtok(option,"="); + if (!option_name) { + return FALSE; + } + option_value = option + strlen(option_name) + 1; + if(0 == strcmp(option_name, "header")) { + switch(NULL == option_value ? '\0' : *option_value) { + case 'n': + info->print_header = FALSE; + break; + case 'y': + info->print_header = TRUE; + break; + default: + return FALSE; + } + return TRUE; + } + + if(0 == strcmp(option_name,"separator")) { + switch(NULL == option_value ? '\0' : *option_value) { + case '\0': + return FALSE; + case '/': + switch(*++option_value) { + case 't': + info->separator = '\t'; + break; + case 's': + info->separator = ' '; + break; + default: + info->separator = '\\'; + } + break; + default: + info->separator = *option_value; + break; + } + return TRUE; + } + + if(0 == strcmp(option_name, "occurrence")) { + switch(NULL == option_value ? '\0' : *option_value) { + case 'f': + case 'l': + case 'a': + info->occurrence = *option_value; + break; + default: + return FALSE; + } + return TRUE; + } + + if(0 == strcmp(option_name,"aggregator")) { + switch(NULL == option_value ? '\0' : *option_value) { + case '\0': + return FALSE; + case '/': + switch(*++option_value) { + case 's': + info->aggregator = ' '; + break; + default: + info->aggregator = '\\'; + } + break; + default: + info->aggregator = *option_value; + break; + } + return TRUE; + } + + if(0 == strcmp(option_name, "quote")) { + switch(NULL == option_value ? '\0' : *option_value) { + default: /* Fall through */ + case '\0': + info->quote='\0'; + return FALSE; + case 'd': + info->quote='"'; + break; + case 's': + info->quote='\''; + break; + case 'n': + info->quote='\0'; + break; + } + return TRUE; + } + + return FALSE; +} + +void output_fields_list_options(FILE *fh) +{ + fprintf(fh, "TShark: The available options for field output \"E\" are:\n"); + fputs("header=y|n Print field abbreviations as first line of output (def: N: no)\n", fh); + fputs("separator=/t|/s| Set the separator to use;\n \"/t\" = tab, \"/s\" = space (def: /t: tab)\n", fh); + fputs("occurrence=f|l|a Select the occurrence of a field to use;\n \"f\" = first, \"l\" = last, \"a\" = all (def: a: all)\n", fh); + fputs("aggregator=,|/s| Set the aggregator to use;\n \",\" = comma, \"/s\" = space (def: ,: comma)\n", fh); + fputs("quote=d|s|n Print either d: double-quotes, s: single quotes or \n n: no quotes around field values (def: n: none)\n", fh); +} + + +void write_fields_preamble(output_fields_t* fields, FILE *fh) +{ + gsize i; + + g_assert(fields); + g_assert(fh); + + if(!fields->print_header) { + return; + } + + for(i = 0; i < fields->fields->len; ++i) { + const gchar* field = (const gchar *)g_ptr_array_index(fields->fields,i); + if(i != 0 ) { + fputc(fields->separator, fh); + } + fputs(field, fh); + } + fputc('\n', fh); +} + +static void proto_tree_get_node_field_values(proto_node *node, gpointer data) +{ + write_field_data_t *call_data; + field_info *fi; + gpointer field_index; + + call_data = (write_field_data_t *)data; + fi = PNODE_FINFO(node); + + g_assert(fi && "dissection with an invisible proto tree?"); + + field_index = g_hash_table_lookup(call_data->fields->field_indicies, fi->hfinfo->abbrev); + if(NULL != field_index) { + const gchar* value; + + value = get_node_field_value(fi, call_data->edt); /* ep_alloced string */ + + if(NULL != value && '\0' != *value) { + guint actual_index; + actual_index = GPOINTER_TO_UINT(field_index); + /* Unwrap change made to disambiguiate zero / null */ + if ( call_data->fields->field_values[actual_index - 1] == NULL ) { + call_data->fields->field_values[actual_index - 1] = ep_strbuf_new(value); + } else if ( call_data->fields->occurrence == 'l' ) { + /* print only the value of the last occurrence of the field */ + ep_strbuf_printf(call_data->fields->field_values[actual_index - 1],"%s",value); + } else if ( call_data->fields->occurrence == 'a' ) { + /* print the value of all accurrences of the field */ + ep_strbuf_append_printf(call_data->fields->field_values[actual_index - 1], + "%c%s",call_data->fields->aggregator,value); + } + } + } + + /* Recurse here. */ + if (node->first_child != NULL) { + proto_tree_children_foreach(node, proto_tree_get_node_field_values, + call_data); + } +} + +void proto_tree_write_fields(output_fields_t* fields, epan_dissect_t *edt, FILE *fh) +{ + gsize i; + + write_field_data_t data; + + g_assert(fields); + g_assert(edt); + g_assert(fh); + + data.fields = fields; + data.edt = edt; + + if(NULL == fields->field_indicies) { + /* Prepare a lookup table from string abbreviation for field to its index. */ + fields->field_indicies = g_hash_table_new(g_str_hash, g_str_equal); + + i = 0; + while( i < fields->fields->len) { + gchar* field = (gchar *)g_ptr_array_index(fields->fields, i); + /* Store field indicies +1 so that zero is not a valid value, + * and can be distinguished from NULL as a pointer. + */ + ++i; + g_hash_table_insert(fields->field_indicies, field, GUINT_TO_POINTER(i)); + } + } + + /* Buffer to store values for this packet */ + fields->field_values = ep_alloc_array0(emem_strbuf_t*, fields->fields->len); + + proto_tree_children_foreach(edt->tree, proto_tree_get_node_field_values, + &data); + + for(i = 0; i < fields->fields->len; ++i) { + if(0 != i) { + fputc(fields->separator, fh); + } + if(NULL != fields->field_values[i]) { + if(fields->quote != '\0') { + fputc(fields->quote, fh); + } + fputs(fields->field_values[i]->str, fh); + if(fields->quote != '\0') { + fputc(fields->quote, fh); + } + } + } +} + +void write_fields_finale(output_fields_t* fields _U_ , FILE *fh _U_) +{ + /* Nothing to do */ +} + +/* Returns an ep_alloced string or a static constant*/ +const gchar* get_node_field_value(field_info* fi, epan_dissect_t* edt) +{ + if (fi->hfinfo->id == hf_text_only) { + /* Text label. + * Get the text */ + if (fi->rep) { + return fi->rep->representation; + } + else { + return get_field_hex_value(edt->pi.data_src, fi); + } + } + else if (fi->hfinfo->id == proto_data) { + /* Uninterpreted data, i.e., the "Data" protocol, is + * printed as a field instead of a protocol. */ + return get_field_hex_value(edt->pi.data_src, fi); + } + else { + /* Normal protocols and fields */ + gchar *dfilter_string; + size_t chop_len; + + switch (fi->hfinfo->type) + { + case FT_PROTOCOL: + /* Print out the full details for the protocol. */ + if (fi->rep) { + return fi->rep->representation; + } else { + /* Just print out the protocol abbreviation */ + return fi->hfinfo->abbrev;; + } + case FT_NONE: + /* Return "1" so that the presence of a field of type + * FT_NONE can be checked when using -T fields */ + return "1"; + default: + /* XXX - this is a hack until we can just call + * fvalue_to_string_repr() for *all* FT_* types. */ + dfilter_string = proto_construct_match_selected_string(fi, + edt); + if (dfilter_string != NULL) { + chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */ + + /* XXX - Remove double-quotes. Again, once we + * can call fvalue_to_string_repr(), we can + * ask it not to produce the version for + * display-filters, and thus, no + * double-quotes. */ + if (dfilter_string[strlen(dfilter_string)-1] == '"') { + dfilter_string[strlen(dfilter_string)-1] = '\0'; + chop_len++; + } + + return &(dfilter_string[chop_len]); + } else { + return get_field_hex_value(edt->pi.data_src, fi); + } + } + } +} + +static const gchar* +get_field_hex_value(GSList* src_list, field_info *fi) +{ + const guint8 *pd; + + if (!fi->ds_tvb) + return NULL; + + if (fi->length > tvb_length_remaining(fi->ds_tvb, fi->start)) { + return "field length invalid!"; + } + + /* Find the data for this field. */ + pd = get_field_data(src_list, fi); + + if (pd) { + int i; + gchar* buffer; + gchar* p; + int len; + const int chars_per_byte = 2; + + len = chars_per_byte * fi->length; + buffer = ep_alloc_array(gchar, len + 1); + buffer[len] = '\0'; /* Ensure NULL termination in bad cases */ + p = buffer; + /* Print a simple hex dump */ + for (i = 0 ; i < fi->length; i++) { + g_snprintf(p, chars_per_byte+1, "%02x", pd[i]); + p += chars_per_byte; + } + return buffer; + } else { + return NULL; + } +} diff --git a/src/main/resource/testFiles/wireshark/revFiles/02dff2_2fba8c_epan#dissectors#packet-usb.c b/src/main/resource/testFiles/wireshark/revFiles/02dff2_2fba8c_epan#dissectors#packet-usb.c new file mode 100644 index 0000000..2d5de72 --- /dev/null +++ b/src/main/resource/testFiles/wireshark/revFiles/02dff2_2fba8c_epan#dissectors#packet-usb.c @@ -0,0 +1,3495 @@ +/* packet-usb.c + * + * $Id$ + * + * USB basic dissector + * By Paolo Abeni + * Ronnie Sahlberg 2006 + * + * http://www.usb.org/developers/docs/usb_20_122909-2.zip + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include "config.h" + +#include +#include "isprint.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "packet-usb.h" +#include "packet-usb-hid.h" + +/* protocols and header fields */ +static int proto_usb = -1; + +/* Linux USB pseudoheader fields */ +static int hf_usb_urb_id = -1; +static int hf_usb_urb_type = -1; +static int hf_usb_transfer_type = -1; +static int hf_usb_endpoint_number = -1; +static int hf_usb_endpoint_direction = -1; +static int hf_usb_endpoint_number_value = -1; +static int hf_usb_device_address = -1; +static int hf_usb_bus_id = -1; +static int hf_usb_setup_flag = -1; +static int hf_usb_data_flag = -1; +static int hf_usb_urb_ts_sec = -1; +static int hf_usb_urb_ts_usec = -1; +static int hf_usb_urb_status = -1; +static int hf_usb_urb_len = -1; +static int hf_usb_urb_data_len = -1; + +static int hf_usb_request = -1; +static int hf_usb_request_unknown_class = -1; +static int hf_usb_value = -1; +static int hf_usb_index = -1; +static int hf_usb_length = -1; +/* static int hf_usb_data_len = -1; */ +static int hf_usb_capdata = -1; +static int hf_usb_wFeatureSelector = -1; +static int hf_usb_wInterface = -1; +static int hf_usb_wStatus = -1; +static int hf_usb_wFrameNumber = -1; + +static int hf_usb_iso_error_count = -1; +static int hf_usb_iso_numdesc = -1; +static int hf_usb_iso_status = -1; +static int hf_usb_iso_off = -1; +static int hf_usb_iso_len = -1; +static int hf_usb_iso_pad = -1; +static int hf_usb_iso_data = -1; + +static int hf_usb_bmRequestType = -1; +static int hf_usb_bmRequestType_direction = -1; +static int hf_usb_bmRequestType_type = -1; +static int hf_usb_bmRequestType_recipient = -1; +static int hf_usb_bDescriptorType = -1; +static int hf_usb_descriptor_index = -1; +static int hf_usb_language_id = -1; +static int hf_usb_bLength = -1; +static int hf_usb_bcdUSB = -1; +static int hf_usb_bDeviceClass = -1; +static int hf_usb_bDeviceSubClass = -1; +static int hf_usb_bDeviceProtocol = -1; +static int hf_usb_bMaxPacketSize0 = -1; +static int hf_usb_idVendor = -1; +static int hf_usb_idProduct = -1; +static int hf_usb_bcdDevice = -1; +static int hf_usb_iManufacturer = -1; +static int hf_usb_iProduct = -1; +static int hf_usb_iSerialNumber = -1; +static int hf_usb_bNumConfigurations = -1; +static int hf_usb_wLANGID = -1; +static int hf_usb_bString = -1; +static int hf_usb_bInterfaceNumber = -1; +static int hf_usb_bAlternateSetting = -1; +static int hf_usb_bNumEndpoints = -1; +static int hf_usb_bInterfaceClass = -1; +static int hf_usb_bInterfaceSubClass = -1; +static int hf_usb_bInterfaceProtocol = -1; +static int hf_usb_iInterface = -1; +static int hf_usb_bEndpointAddress = -1; +static int hf_usb_bmAttributes = -1; +static int hf_usb_bEndpointAttributeTransfer = -1; +static int hf_usb_bEndpointAttributeSynchonisation = -1; +static int hf_usb_bEndpointAttributeBehaviour = -1; +static int hf_usb_wMaxPacketSize = -1; +static int hf_usb_wMaxPacketSize_size = -1; +static int hf_usb_wMaxPacketSize_slots = -1; +static int hf_usb_bInterval = -1; +static int hf_usb_wTotalLength = -1; +static int hf_usb_bNumInterfaces = -1; +static int hf_usb_bConfigurationValue = -1; +static int hf_usb_iConfiguration = -1; +static int hf_usb_bMaxPower = -1; +static int hf_usb_configuration_bmAttributes = -1; +static int hf_usb_configuration_legacy10buspowered = -1; +static int hf_usb_configuration_selfpowered = -1; +static int hf_usb_configuration_remotewakeup = -1; +static int hf_usb_bEndpointAddress_direction = -1; +static int hf_usb_bEndpointAddress_number = -1; +static int hf_usb_response_in = -1; +static int hf_usb_time = -1; +static int hf_usb_request_in = -1; +static int hf_usb_bFirstInterface = -1; +static int hf_usb_bInterfaceCount = -1; +static int hf_usb_bFunctionClass = -1; +static int hf_usb_bFunctionSubClass = -1; +static int hf_usb_bFunctionProtocol = -1; +static int hf_usb_iFunction = -1; + +static gint usb_hdr = -1; +static gint usb_setup_hdr = -1; +static gint usb_isodesc = -1; +static gint ett_usb_endpoint = -1; +static gint ett_usb_setup_bmrequesttype = -1; +static gint ett_descriptor_device = -1; +static gint ett_configuration_bmAttributes = -1; +static gint ett_configuration_bEndpointAddress = -1; +static gint ett_endpoint_bmAttributes = -1; +static gint ett_endpoint_wMaxPacketSize = -1; + +static const int *usb_endpoint_fields[] = { + &hf_usb_endpoint_direction, + &hf_usb_endpoint_number_value, + NULL +}; + +static int usb_tap = -1; +static gboolean try_heuristics = TRUE; + +static dissector_table_t usb_bulk_dissector_table; +static dissector_table_t usb_control_dissector_table; +static dissector_table_t usb_interrupt_dissector_table; +static dissector_table_t usb_descriptor_dissector_table; + +static heur_dissector_list_t heur_bulk_subdissector_list; +static heur_dissector_list_t heur_control_subdissector_list; +static heur_dissector_list_t heur_interrupt_subdissector_list; + +static emem_tree_t *device_to_protocol_table = NULL; +static emem_tree_t *device_to_product_table = NULL; + +static dissector_table_t device_to_dissector; +static dissector_table_t protocol_to_dissector; +static dissector_table_t product_to_dissector; + +typedef struct _device_product_data_t { + guint16 vendor; + guint16 product; + guint bus_id; + guint device_address; +} device_product_data_t; + +typedef struct _device_protocol_data_t { + guint32 protocol; + guint bus_id; + guint device_address; +} device_protocol_data_t; + + +/* http://www.usb.org/developers/docs/USB_LANGIDs.pdf */ +static const value_string usb_langid_vals[] = { + {0x0000, "no language specified"}, + {0x0401, "Arabic (Saudi Arabia)"}, + {0x0402, "Bulgarian"}, + {0x0403, "Catalan"}, + {0x0404, "Chinese (Taiwan)"}, + {0x0405, "Czech"}, + {0x0406, "Danish"}, + {0x0407, "German (Standard)"}, + {0x0408, "Greek"}, + {0x0409, "English (United States)"}, + {0x040a, "Spanish (Traditional Sort)"}, + {0x040b, "Finnish"}, + {0x040c, "French (Standard)"}, + {0x040d, "Hebrew"}, + {0x040e, "Hungarian"}, + {0x040f, "Icelandic"}, + {0x0410, "Italian (Standard)"}, + {0x0411, "Japanese"}, + {0x0412, "Korean"}, + {0x0413, "Dutch (Netherlands)"}, + {0x0414, "Norwegian (Bokmal)"}, + {0x0415, "Polish"}, + {0x0416, "Portuguese (Brazil)"}, + {0x0418, "Romanian"}, + {0x0419, "Russian"}, + {0x041a, "Croatian"}, + {0x041b, "Slovak"}, + {0x041c, "Albanian"}, + {0x041d, "Swedish"}, + {0x041e, "Thai"}, + {0x041f, "Turkish"}, + {0x0420, "Urdu (Pakistan)"}, + {0x0421, "Indonesian"}, + {0x0422, "Ukrainian"}, + {0x0423, "Belarussian"}, + {0x0424, "Slovenian"}, + {0x0425, "Estonian"}, + {0x0426, "Latvian"}, + {0x0427, "Lithuanian"}, + {0x0429, "Farsi"}, + {0x042a, "Vietnamese"}, + {0x042b, "Armenian"}, + {0x042c, "Azeri (Latin)"}, + {0x042d, "Basque"}, + {0x042f, "Macedonian"}, + {0x0430, "Sutu"}, + {0x0436, "Afrikaans"}, + {0x0437, "Georgian"}, + {0x0438, "Faeroese"}, + {0x0439, "Hindi"}, + {0x043e, "Malay (Malaysian)"}, + {0x043f, "Kazakh"}, + {0x0441, "Swahili (Kenya)"}, + {0x0443, "Uzbek (Latin)"}, + {0x0444, "Tatar (Tatarstan)"}, + {0x0445, "Bengali"}, + {0x0446, "Punjabi"}, + {0x0447, "Gujarati"}, + {0x0448, "Oriya"}, + {0x0449, "Tamil"}, + {0x044a, "Telugu"}, + {0x044b, "Kannada"}, + {0x044c, "Malayalam"}, + {0x044d, "Assamese"}, + {0x044e, "Marathi"}, + {0x044f, "Sanskrit"}, + {0x0455, "Burmese"}, + {0x0457, "Konkani"}, + {0x0458, "Manipuri"}, + {0x0459, "Sindhi"}, + {0x04ff, "HID (Usage Data Descriptor)"}, + {0x0801, "Arabic (Iraq)"}, + {0x0804, "Chinese (PRC)"}, + {0x0807, "German (Switzerland)"}, + {0x0809, "English (United Kingdom)"}, + {0x080a, "Spanish (Mexican)"}, + {0x080c, "French (Belgian)"}, + {0x0810, "Italian (Switzerland)"}, + {0x0812, "Korean (Johab)"}, + {0x0813, "Dutch (Belgium)"}, + {0x0814, "Norwegian (Nynorsk)"}, + {0x0816, "Portuguese (Standard)"}, + {0x081a, "Serbian (Latin)"}, + {0x081d, "Swedish (Finland)"}, + {0x0820, "Urdu (India)"}, + {0x0827, "Lithuanian (Classic)"}, + {0x082c, "Azeri (Cyrillic)"}, + {0x083e, "Malay (Brunei Darussalam)"}, + {0x0843, "Uzbek (Cyrillic)"}, + {0x0860, "Kashmiri (India)"}, + {0x0861, "Nepali (India)"}, + {0x0c01, "Arabic (Egypt)"}, + {0x0c04, "Chinese (Hong Kong SAR, PRC)"}, + {0x0c07, "German (Austria)"}, + {0x0c09, "English (Australian)"}, + {0x0c0a, "Spanish (Modern Sort)"}, + {0x0c0c, "French (Canadian)"}, + {0x0c1a, "Serbian (Cyrillic)"}, + {0x1001, "Arabic (Libya)"}, + {0x1004, "Chinese (Singapore)"}, + {0x1007, "German (Luxembourg)"}, + {0x1009, "English (Canadian)"}, + {0x100a, "Spanish (Guatemala)"}, + {0x100c, "French (Switzerland)"}, + {0x1401, "Arabic (Algeria)"}, + {0x1404, "Chinese (Macau SAR)"}, + {0x1407, "German (Liechtenstein)"}, + {0x1409, "English (New Zealand)"}, + {0x140a, "Spanish (Costa Rica)"}, + {0x140c, "French (Luxembourg)"}, + {0x1801, "Arabic (Morocco)"}, + {0x1809, "English (Ireland)"}, + {0x180a, "Spanish (Panama)"}, + {0x180c, "French (Monaco)"}, + {0x1c01, "Arabic (Tunisia)"}, + {0x1c09, "English (South Africa)"}, + {0x1c0a, "Spanish (Dominican Republic)"}, + {0x2001, "Arabic (Oman)"}, + {0x2009, "English (Jamaica)"}, + {0x200a, "Spanish (Venezuela)"}, + {0x2401, "Arabic (Yemen)"}, + {0x2409, "English (Caribbean)"}, + {0x240a, "Spanish (Colombia)"}, + {0x2801, "Arabic (Syria)"}, + {0x2809, "English (Belize)"}, + {0x280a, "Spanish (Peru)"}, + {0x2c01, "Arabic (Jordan)"}, + {0x2c09, "English (Trinidad)"}, + {0x2c0a, "Spanish (Argentina)"}, + {0x3001, "Arabic (Lebanon)"}, + {0x3009, "English (Zimbabwe)"}, + {0x300a, "Spanish (Ecuador)"}, + {0x3401, "Arabic (Kuwait)"}, + {0x3409, "English (Philippines)"}, + {0x340a, "Spanish (Chile)"}, + {0x3801, "Arabic (U.A.E.)"}, + {0x380a, "Spanish (Uruguay)"}, + {0x3c01, "Arabic (Bahrain)"}, + {0x3c0a, "Spanish (Paraguay)"}, + {0x4001, "Arabic (Qatar)"}, + {0x400a, "Spanish (Bolivia)"}, + {0x440a, "Spanish (El Salvador)"}, + {0x480a, "Spanish (Honduras)"}, + {0x4c0a, "Spanish (Nicaragua)"}, + {0x500a, "Spanish (Puerto Rico)"}, + {0xf0ff, "HID (Vendor Defined 1)"}, + {0xf4ff, "HID (Vendor Defined 2)"}, + {0xf8ff, "HID (Vendor Defined 3)"}, + {0xfcff, "HID (Vendor Defined 4)"}, + {0, NULL} +}; +value_string_ext usb_langid_vals_ext = VALUE_STRING_EXT_INIT(usb_langid_vals); + +static const value_string usb_class_vals[] = { + {IF_CLASS_DEVICE, "Device"}, + {IF_CLASS_AUDIO, "Audio"}, + {IF_CLASS_COMMUNICATIONS, "Communications and CDC Control"}, + {IF_CLASS_HID, "HID"}, + {IF_CLASS_PHYSICAL, "Physical"}, + {IF_CLASS_IMAGE, "Imaging"}, + {IF_CLASS_PRINTER, "Printer"}, + {IF_CLASS_MASS_STORAGE, "Mass Storage"}, + {IF_CLASS_HUB, "Hub"}, + {IF_CLASS_CDC_DATA, "CDC-Data"}, + {IF_CLASS_SMART_CARD, "Smart Card"}, + {IF_CLASS_CONTENT_SECURITY, "Content Security"}, + {IF_CLASS_VIDEO, "Video"}, + {IF_CLASS_PERSONAL_HEALTHCARE, "Personal Healthcare"}, + {IF_CLASS_AUDIO_VIDEO, "Audio/Video Devices"}, + {IF_CLASS_DIAGNOSTIC_DEVICE, "Diagnostic Device"}, + {IF_CLASS_WIRELESS_CONTROLLER, "Wireless Controller"}, + {IF_CLASS_MISCELLANEOUS, "Miscellaneous"}, + {IF_CLASS_APPLICATION_SPECIFIC, "Application Specific"}, + {IF_CLASS_VENDOR_SPECIFIC, "Vendor Specific"}, + {0, NULL} +}; +static value_string_ext usb_class_vals_ext = VALUE_STRING_EXT_INIT(usb_class_vals); + +/* use usb class, subclass and protocol id together + http://www.usb.org/developers/defined_class*/ +static const value_string usb_protocols[] = { + {0x000000, "Use class code info from Interface Descriptors"}, + {0x060101, "Still Imaging"}, + {0x090000, "Full speed Hub"}, + {0x090001, "Hi-speed hub with single TT"}, + {0x090002, "Hi-speed hub with multiple TTs"}, + {0x0D0000, "Content Security"}, + {0x100100, "AVControl Interface"}, + {0x100200, "AVData Video Streaming Interface"}, + {0x100300, "AVData Audio Streaming Interface"}, + {0xDC0101, "USB2 Compliance Device"}, + {0xE00101, "Bluetooth Programming Interface"}, + {0xE00102, "UWB Radio Control Interface"}, + {0xE00103, "Remote NDIS"}, + {0xE00104, "Bluetooth AMP Controller"}, + {0xE00201, "Host Wire Adapter Control/Data interface"}, + {0xE00202, "Device Wire Adapter Control/Data interface"}, + {0xE00203, "Device Wire Adapter Isochronous interface"}, + {0xEF0101, "Active Sync device"}, + {0xEF0102, "Palm Sync"}, + {0xEF0201, "Interface Association Descriptor"}, + {0xEF0202, "Wire Adapter Multifunction Peripheral programming interface"}, + {0xEF0301, "Cable Based Association Framework"}, + {0xFE0101, "Device Firmware Upgrade"}, + {0xFE0200, "IRDA Bridge device"}, + {0xFE0300, "USB Test and Measurement Device"}, + {0xFE0301, "USB Test and Measurement Device conforming to the USBTMC USB488"}, + {0, NULL} +}; + +static const value_string usb_transfer_type_vals[] = { + {URB_CONTROL, "URB_CONTROL"}, + {URB_ISOCHRONOUS, "URB_ISOCHRONOUS"}, + {URB_INTERRUPT, "URB_INTERRUPT"}, + {URB_BULK, "URB_BULK"}, + {0, NULL} +}; + +static const value_string usb_transfer_type_and_direction_vals[] = { + {URB_CONTROL, "URB_CONTROL out"}, + {URB_ISOCHRONOUS, "URB_ISOCHRONOUS out"}, + {URB_INTERRUPT, "URB_INTERRUPT out"}, + {URB_BULK, "URB_BULK out"}, + {URB_CONTROL | URB_TRANSFER_IN, "URB_CONTROL in"}, + {URB_ISOCHRONOUS | URB_TRANSFER_IN, "URB_ISOCHRONOUS in"}, + {URB_INTERRUPT | URB_TRANSFER_IN, "URB_INTERRUPT in"}, + {URB_BULK | URB_TRANSFER_IN, "URB_BULK in"}, + {0, NULL} +}; + +static const value_string usb_endpoint_direction_vals[] = { + {0, "OUT"}, + {1, "IN"}, + {0, NULL} +}; + +static const value_string usb_urb_type_vals[] = { + {URB_SUBMIT, "URB_SUBMIT"}, + {URB_COMPLETE, "URB_COMPLETE"}, + {URB_ERROR, "URB_ERROR"}, + {0, NULL} +}; + +extern value_string_ext ext_usb_vendors_vals; +extern value_string_ext ext_usb_products_vals; + +/* + * Descriptor types. + */ +#define USB_DT_DEVICE 1 +#define USB_DT_CONFIG 2 +#define USB_DT_STRING 3 +#define USB_DT_INTERFACE 4 +#define USB_DT_ENDPOINT 5 +#define USB_DT_DEVICE_QUALIFIER 6 +#define USB_DT_OTHER_SPEED_CONFIG 7 +#define USB_DT_INTERFACE_POWER 8 +/* these are from a minor usb 2.0 revision (ECN) */ +#define USB_DT_OTG 9 +#define USB_DT_DEBUG 10 +#define USB_DT_INTERFACE_ASSOCIATION 11 +/* these are from the Wireless USB spec */ +#define USB_DT_SECURITY 12 +#define USB_DT_KEY 13 +#define USB_DT_ENCRYPTION_TYPE 14 +#define USB_DT_BOS 15 +#define USB_DT_DEVICE_CAPABILITY 16 +#define USB_DT_WIRELESS_ENDPOINT_COMP 17 +#define USB_DT_HID 33 +#define USB_DT_RPIPE 34 +#define USB_DT_SUPERSPEED_ENDPOINT_COMPANION 48 + +static const value_string descriptor_type_vals[] = { + {USB_DT_DEVICE, "DEVICE"}, + {USB_DT_CONFIG, "CONFIGURATION"}, + {USB_DT_STRING, "STRING"}, + {USB_DT_INTERFACE, "INTERFACE"}, + {USB_DT_ENDPOINT, "ENDPOINT"}, + {USB_DT_DEVICE_QUALIFIER, "DEVICE QUALIFIER"}, + {USB_DT_OTHER_SPEED_CONFIG, "OTHER_SPEED CONFIG"}, + {USB_DT_INTERFACE_POWER, "INTERFACE POWER"}, + {USB_DT_OTG, "OTG"}, + {USB_DT_DEBUG, "DEBUG"}, + {USB_DT_INTERFACE_ASSOCIATION, "INTERFACE ASSOCIATION"}, + {USB_DT_SECURITY, "SECURITY"}, + {USB_DT_KEY, "KEY"}, + {USB_DT_ENCRYPTION_TYPE, "ENCRYPTION TYPE"}, + {USB_DT_BOS, "BOS"}, + {USB_DT_DEVICE_CAPABILITY, "DEVICE CAPABILITY"}, + {USB_DT_WIRELESS_ENDPOINT_COMP, "WIRELESS ENDPOINT COMP"}, + {USB_DT_HID, "HID"}, + {USB_DT_RPIPE, "RPIPE"}, + {USB_DT_SUPERSPEED_ENDPOINT_COMPANION, "SUPERSPEED USB ENDPOINT COMPANION"}, + {0,NULL} +}; +static value_string_ext descriptor_type_vals_ext = VALUE_STRING_EXT_INIT(descriptor_type_vals); + +/* + * Feature selectors. + */ +#define USB_FS_ENDPOINT_HALT 0 +#define USB_FS_DEVICE_REMOTE_WAKEUP 1 +#define USB_FS_TEST_MODE 2 + +static const value_string usb_feature_selector_vals[] = { + {USB_FS_ENDPOINT_HALT, "ENDPOINT HALT"}, + {USB_FS_DEVICE_REMOTE_WAKEUP, "DEVICE REMOTE WAKEUP"}, + {USB_FS_TEST_MODE, "TEST MODE"}, + {0, NULL} +}; + +static const value_string usb_bmAttributes_transfer_vals[] = { + {0x00, "Control-Transfer"}, + {0x01, "Isochronous-Transfer"}, + {0x02, "Bulk-Transfer"}, + {0x03, "Interrupt-Transfer"}, + {0, NULL} +}; + +static const value_string usb_bmAttributes_sync_vals[] = { + {0x00, "No Sync"}, + {0x01, "Asynchronous"}, + {0x02, "Adaptive"}, + {0x03, "Synchronous"}, + {0, NULL} +}; + +static const value_string usb_bmAttributes_behaviour_vals[] = { + {0x00, "Data-Endpoint"}, + {0x01, "Explicit Feedback-Endpoint"}, + {0x02, "Implicit Feedback-Data-Endpoint"}, + {0x03, "Reserved"}, + {0, NULL} +}; + +static const value_string usb_wMaxPacketSize_slots_vals[] = { + {0x00, "1"}, + {0x01, "2"}, + {0x02, "3"}, + {0x03, "Reserved"}, + {0, NULL} +}; + +/* from linux/include/asm-generic/errno.h */ +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ + + +/* from linux/include/asm-generic/errno.h*/ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ +#define ECANCELED 125 /* Operation Canceled */ +#define ENOKEY 126 /* Required key not available */ +#define EKEYEXPIRED 127 /* Key has expired */ +#define EKEYREVOKED 128 /* Key has been revoked */ +#define EKEYREJECTED 129 /* Key was rejected by service */ + +/* for robust mutexes */ +#define EOWNERDEAD 130 /* Owner died */ +#define ENOTRECOVERABLE 131 /* State not recoverable */ + + +/* Note: sorted in (unsigned) ascending order */ +static const value_string usb_urb_status_vals[] = { + { -ENOTRECOVERABLE, "State not recoverable (-ENOTRECOVERABLE)" }, + { -EOWNERDEAD, "Owner died (-EOWNERDEAD)" }, + { -EKEYREJECTED, "Key was rejected by service (-EKEYREJECTED)" }, + { -EKEYREVOKED, "Key has been revoked (-EKEYREVOKED)" }, + { -EKEYEXPIRED, "Key has expired (-EKEYEXPIRED)" }, + { -ENOKEY, "Required key not available (-ENOKEY)" }, + { -ECANCELED, "Operation Canceled (-ECANCELED)" }, + { -EMEDIUMTYPE, "Wrong medium type (-EMEDIUMTYPE)" }, + { -ENOMEDIUM, "No medium found (-ENOMEDIUM)" }, + { -EDQUOT, "Quota exceeded (-EDQUOT)" }, + { -EREMOTEIO, "Remote I/O error (-EREMOTEIO)" }, + { -EISNAM, "Is a named type file (-EISNAM)" }, + { -ENAVAIL, "No XENIX semaphores available (-ENAVAIL)" }, + { -ENOTNAM, "Not a XENIX named type file (-ENOTNAM)" }, + { -EUCLEAN, "Structure needs cleaning (-EUCLEAN)" }, + { -ESTALE, "Stale NFS file handle (-ESTALE)" }, + { -EINPROGRESS, "Operation now in progress (-EINPROGRESS)" }, + { -EALREADY, "Operation already in progress (-EALREADY)" }, + { -EHOSTUNREACH, "No route to host (-EHOSTUNREACH)" }, + { -EHOSTDOWN, "Host is down (-EHOSTDOWN)" }, + { -ECONNREFUSED, "Connection refused (-ECONNREFUSED)" }, + { -ETIMEDOUT, "Connection timed out (-ETIMEDOUT)" }, + { -ETOOMANYREFS, "Too many references: cannot splice (-ETOOMANYREFS)" }, + { -ESHUTDOWN, "Cannot send after transport endpoint shutdown (-ESHUTDOWN)" }, + { -ENOTCONN, "Transport endpoint is not connected (-ENOTCONN)" }, + { -EISCONN, "Transport endpoint is already connected (-EISCONN)" }, + { -ENOBUFS, "No buffer space available (-ENOBUFS)" }, + { -ECONNRESET, "Connection reset by peer (-ECONNRESET)" }, + { -ECONNABORTED, "Software caused connection abort (-ECONNABORTED)" }, + { -ENETRESET, "Network dropped connection because of reset (-ENETRESET)" }, + { -ENETUNREACH, "Network is unreachable (-ENETUNREACH)" }, + { -ENETDOWN, "Network is down (-ENETDOWN)" }, + { -EADDRNOTAVAIL, "Cannot assign requested address (-EADDRNOTAVAIL)" }, + { -EADDRINUSE, "Address already in use (-EADDRINUSE)" }, + { -EAFNOSUPPORT, "Address family not supported by protocol (-EAFNOSUPPORT)" }, + { -EPFNOSUPPORT, "Protocol family not supported (-EPFNOSUPPORT)" }, + { -EOPNOTSUPP, "Operation not supported on transport endpoint (-EOPNOTSUPP)" }, + { -ESOCKTNOSUPPORT, "Socket type not supported (-ESOCKTNOSUPPORT)" }, + { -EPROTONOSUPPORT, "Protocol not supported (-EPROTONOSUPPORT)" }, + { -ENOPROTOOPT, "Protocol not available (-ENOPROTOOPT)" }, + { -EPROTOTYPE, "Protocol wrong type for socket (-EPROTOTYPE)" }, + { -EMSGSIZE, "Message too long (-EMSGSIZE)" }, + { -EDESTADDRREQ, "Destination address required (-EDESTADDRREQ)" }, + { -ENOTSOCK, "Socket operation on non-socket (-ENOTSOCK)" }, + { -EUSERS, "Too many users (-EUSERS)" }, + { -ESTRPIPE, "Streams pipe error (-ESTRPIPE)" }, + { -ERESTART, "Interrupted system call should be restarted (-ERESTART)" }, + { -EILSEQ, "Illegal byte sequence (-EILSEQ)" }, + { -ELIBEXEC, "Cannot exec a shared library directly (-ELIBEXEC)" }, + { -ELIBMAX, "Attempting to link in too many shared libraries (-ELIBMAX)" }, + { -ELIBSCN, ".lib section in a.out corrupted (-ELIBSCN)" }, + { -ELIBBAD, "Accessing a corrupted shared library (-ELIBBAD)" }, + { -ELIBACC, "Can not access a needed shared library (-ELIBACC)" }, + { -EREMCHG, "Remote address changed (-EREMCHG)" }, + { -EBADFD, "File descriptor in bad state (-EBADFD)" }, + { -ENOTUNIQ, "Name not unique on network (-ENOTUNIQ)" }, + { -EOVERFLOW, "Value too large for defined data type (-EOVERFLOW)" }, + { -EBADMSG, "Not a data message (-EBADMSG)" }, + { -EDOTDOT, "RFS specific error (-EDOTDOT)" }, + { -EMULTIHOP, "Multihop attempted (-EMULTIHOP)" }, + { -EPROTO, "Protocol error (-EPROTO)" }, + { -ECOMM, "Communication error on send (-ECOMM)" }, + { -ESRMNT, "Srmount error (-ESRMNT)" }, + { -EADV, "Advertise error (-EADV)" }, + { -ENOLINK, "Link has been severed (-ENOLINK)" }, + { -EREMOTE, "Object is remote (-EREMOTE)" }, + { -ENOPKG, "Package not installed (-ENOPKG)" }, + { -ENONET, "Machine is not on the network (-ENONET)" }, + { -ENOSR, "Out of streams resources (-ENOSR)" }, + { -ETIME, "Timer expired (-ETIME)" }, + { -ENODATA, "No data available (-ENODATA)" }, + { -ENOSTR, "Device not a stream (-ENOSTR)" }, + { -EBFONT, "Bad font file format (-EBFONT)" }, + { -58, "(-58 \?\?\?)" }, /* dummy so that there are no "gaps" */ + { -EBADSLT, "Invalid slot (-EBADSLT)" }, + { -EBADRQC, "Invalid request code (-EBADRQC)" }, + { -ENOANO, "No anode (-ENOANO)" }, + { -EXFULL, "Exchange full (-EXFULL)" }, + { -EBADR, "Invalid request descriptor (-EBADR)" }, + { -EBADE, "Invalid exchange (-EBADE)" }, + { -EL2HLT, "Level 2 halted (-EL2HLT)" }, + { -ENOCSI, "No CSI structure available (-ENOCSI)" }, + { -EUNATCH, "Protocol driver not attached (-EUNATCH)" }, + { -ELNRNG, "Link number out of range (-ELNRNG)" }, + { -EL3RST, "Level 3 reset (-EL3RST)" }, + { -EL3HLT, "Level 3 halted (-EL3HLT)" }, + { -EL2NSYNC, "Level 2 not synchronized (-EL2NSYNC)" }, + { -ECHRNG, "Channel number out of range (-ECHRNG)" }, + { -EIDRM, "Identifier removed (-EIDRM)" }, + { -ENOMSG, "No message of desired type (-ENOMSG)" }, + { -41, "(-41 \?\?\?)" }, /* dummy so that there are no "gaps" */ + { -ELOOP, "Too many symbolic links encountered (-ELOOP)" }, + { -ENOTEMPTY, "Directory not empty (-ENOTEMPTY)" }, + { -ENOSYS, "Function not implemented (-ENOSYS)" }, + { -ENOLCK, "No record locks available (-ENOLCK)" }, + { -ENAMETOOLONG, "File name too long (-ENAMETOOLONG)" }, + { -EDEADLK, "Resource deadlock would occur (-EDEADLK)" }, + { -ERANGE, "Math result not representable (-ERANGE)" }, + { -EDOM, "Math argument out of domain of func (-EDOM)" }, + { -EPIPE, "Broken pipe (-EPIPE)" }, + { -EMLINK, "Too many links (-EMLINK)" }, + { -EROFS, "Read-only file system (-EROFS)" }, + { -ESPIPE, "Illegal seek (-ESPIPE)" }, + { -ENOSPC, "No space left on device (-ENOSPC)" }, + { -EFBIG, "File too large (-EFBIG)" }, + { -ETXTBSY, "Text file busy (-ETXTBSY)" }, + { -ENOTTY, "Not a typewriter (-ENOTTY)" }, + { -EMFILE, "Too many open files (-EMFILE)" }, + { -ENFILE, "File table overflow (-ENFILE)" }, + { -EINVAL, "Invalid argument (-EINVAL)" }, + { -EISDIR, "Is a directory (-EISDIR)" }, + { -ENOTDIR, "Not a directory (-ENOTDIR)" }, + { -ENODEV, "No such device (-ENODEV)" }, + { -EXDEV, "Cross-device link (-EXDEV)" }, + { -EEXIST, "File exists (-EEXIST)" }, + { -EBUSY, "Device or resource busy (-EBUSY)" }, + { -ENOTBLK, "Block device required (-ENOTBLK)" }, + { -EFAULT, "Bad address (-EFAULT)" }, + { -EACCES, "Permission denied (-EACCES)" }, + { -ENOMEM, "Out of memory (-ENOMEM)" }, + { -EAGAIN, "Try again (-EAGAIN)" }, + { -ECHILD, "No child processes (-ECHILD)" }, + { -EBADF, "Bad file number (-EBADF)" }, + { -ENOEXEC, "Exec format error (-ENOEXEC)" }, + { -E2BIG, "Argument list too long (-E2BIG)" }, + { -ENXIO, "No such device or address (-ENXIO)" }, + { -EIO, "I/O error (-EIO)" }, + { -EINTR, "Interrupted system call (-EINTR)" }, + { -ESRCH, "No such process (-ESRCH)" }, + { -ENOENT, "No such file or directory (-ENOENT)" }, + { -EPERM, "Operation not permitted (-EPERM)" }, + { 0, "Success"}, + { 0, NULL } +}; +static value_string_ext usb_urb_status_vals_ext = VALUE_STRING_EXT_INIT(usb_urb_status_vals); + + +static usb_conv_info_t * +get_usb_conv_info(conversation_t *conversation) +{ + usb_conv_info_t *usb_conv_info; + + /* do we have conversation specific data ? */ + usb_conv_info = (usb_conv_info_t *)conversation_get_proto_data(conversation, proto_usb); + if (!usb_conv_info) { + /* no not yet so create some */ + usb_conv_info = se_new0(usb_conv_info_t); + usb_conv_info->interfaceClass = IF_CLASS_UNKNOWN; + usb_conv_info->interfaceSubclass = IF_SUBCLASS_UNKNOWN; + usb_conv_info->interfaceProtocol = IF_PROTOCOL_UNKNOWN; + usb_conv_info->deviceVendor = DEV_VENDOR_UNKNOWN; + usb_conv_info->deviceProduct = DEV_PRODUCT_UNKNOWN; + usb_conv_info->transactions = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "usb transactions"); + + conversation_add_proto_data(conversation, proto_usb, usb_conv_info); + } + + return usb_conv_info; +} + +static conversation_t * +get_usb_conversation(packet_info *pinfo, + address *src_addr, address *dst_addr, + guint32 src_endpoint, guint32 dst_endpoint) +{ + conversation_t *conversation; + + /* + * Do we have a conversation for this connection? + */ + conversation = find_conversation(pinfo->fd->num, + src_addr, dst_addr, + pinfo->ptype, + src_endpoint, dst_endpoint, 0); + if (conversation) { + return conversation; + } + + /* We don't yet have a conversation, so create one. */ + conversation = conversation_new(pinfo->fd->num, + src_addr, dst_addr, + pinfo->ptype, + src_endpoint, dst_endpoint, 0); + return conversation; +} + +/* Fetch or create usb_conv_info for a specified interface. */ +usb_conv_info_t * +get_usb_iface_conv_info(packet_info *pinfo, guint8 interface_num) +{ + conversation_t *conversation; + guint32 if_port; + + if_port = htolel(INTERFACE_PORT | interface_num); + + if (pinfo->srcport == NO_ENDPOINT) { + conversation = get_usb_conversation(pinfo, &pinfo->src, &pinfo->dst, pinfo->srcport, if_port); + } else { + conversation = get_usb_conversation(pinfo, &pinfo->src, &pinfo->dst, if_port, pinfo->destport); + } + + return get_usb_conv_info(conversation); +} + + +/* SETUP dissectors */ + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / CLEAR FEATURE + */ + + +/* 9.4.1 */ +static int +dissect_usb_setup_clear_feature_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* feature selector */ + proto_tree_add_item(tree, hf_usb_wFeatureSelector, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero/interface/endpoint */ + /* XXX - check based on request type */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* length */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_clear_feature_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / GET CONFIGURATION + */ + + +/* 9.4.2 */ +static int +dissect_usb_setup_get_configuration_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + proto_tree_add_item(tree, hf_usb_bConfigurationValue, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / GET DESCRIPTOR + */ + +void dissect_usb_descriptor_header(proto_tree *tree, + tvbuff_t *tvb, int offset) +{ + /* bLength */ + proto_tree_add_item(tree, + hf_usb_bLength, + tvb, offset, 1, ENC_LITTLE_ENDIAN); + + /* bDescriptorType */ + proto_tree_add_item(tree, hf_usb_bDescriptorType, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); +} + +/* 9.6.2 */ +static int +dissect_usb_device_qualifier_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id, guint device_address) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + proto_item *nitem = NULL; + int old_offset = offset; + guint32 protocol; + const gchar *description; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "DEVICE QUALIFIER DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + /* bcdUSB */ + proto_tree_add_item(tree, hf_usb_bcdUSB, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + protocol = tvb_get_ntoh24(tvb, offset); + description = val_to_str_const(protocol, usb_protocols, ""); + + /* bDeviceClass */ + proto_tree_add_item(tree, hf_usb_bDeviceClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bDeviceSubClass */ + proto_tree_add_item(tree, hf_usb_bDeviceSubClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bDeviceProtocol */ + nitem = proto_tree_add_item(tree, hf_usb_bDeviceProtocol, tvb, offset, 1, ENC_LITTLE_ENDIAN); + if (*description) + proto_item_append_text(nitem, " (%s)", description); + offset += 1; + + if (!pinfo->fd->flags.visited) { + guint k_bus_id; + guint k_device_address; + guint k_frame_number; + emem_tree_key_t key[4]; + device_protocol_data_t *device_protocol_data; + + k_frame_number = pinfo->fd->num; + k_device_address = device_address; + k_bus_id = bus_id; + + key[0].length = 1; + key[0].key = &k_device_address; + key[1].length = 1; + key[1].key = &k_bus_id; + key[2].length = 1; + key[2].key = &k_frame_number; + key[3].length = 0; + key[3].key = NULL; + + device_protocol_data = se_new(device_protocol_data_t); + device_protocol_data->protocol = protocol; + device_protocol_data->bus_id = bus_id; + device_protocol_data->device_address = device_address; + se_tree_insert32_array(device_to_protocol_table, key, device_protocol_data); + } + + /* bMaxPacketSize0 */ + proto_tree_add_item(tree, hf_usb_bMaxPacketSize0, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bNumConfigurations */ + proto_tree_add_item(tree, hf_usb_bNumConfigurations, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* one reserved byte */ + offset += 1; + + if (item) { + proto_item_set_len(item, offset-old_offset); + } + + return offset; +} + +/* 9.6.1 */ +static int +dissect_usb_device_descriptor(packet_info *pinfo, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + guint bus_id, guint device_address) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + proto_item *nitem = NULL; + int old_offset = offset; + guint32 protocol; + const gchar *description; + guint16 vendor_id; + guint32 product; + guint16 product_id; + usb_conv_info_t *usb_conv_info; + guint8 *field_description; + gint field_description_length; + header_field_info *hfi; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "DEVICE DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + usb_conv_info=(usb_conv_info_t *)pinfo->usb_conv_info; + + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + /* bcdUSB */ + proto_tree_add_item(tree, hf_usb_bcdUSB, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + protocol = tvb_get_ntoh24(tvb, offset); + description = val_to_str_const(protocol, usb_protocols, ""); + + /* bDeviceClass */ + proto_tree_add_item(tree, hf_usb_bDeviceClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bDeviceSubClass */ + proto_tree_add_item(tree, hf_usb_bDeviceSubClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bDeviceProtocol */ + nitem = proto_tree_add_item(tree, hf_usb_bDeviceProtocol, tvb, offset, 1, ENC_LITTLE_ENDIAN); + if (*description) + proto_item_append_text(nitem, " (%s)", description); + offset += 1; + + /* bMaxPacketSize0 */ + proto_tree_add_item(tree, hf_usb_bMaxPacketSize0, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* idVendor */ + proto_tree_add_item(tree, hf_usb_idVendor, tvb, offset, 2, ENC_LITTLE_ENDIAN); + vendor_id = tvb_get_letohs(tvb, offset); + usb_conv_info->deviceVendor = vendor_id; + offset += 2; + + /* idProduct */ + nitem = proto_tree_add_item(tree, hf_usb_idProduct, tvb, offset, 2, ENC_LITTLE_ENDIAN); + product_id = tvb_get_letohs(tvb, offset); + usb_conv_info->deviceProduct = product_id; + product = vendor_id << 16 | product_id; + + hfi = proto_registrar_get_nth(hf_usb_idProduct); + field_description_length = (gint)strlen(hfi->name) + 14; + field_description = (guint8 *)ep_alloc(field_description_length); + g_strlcpy(field_description, hfi->name, field_description_length); + g_strlcat(field_description, ": %s (0x%04x)", field_description_length); + + proto_item_set_text(nitem, field_description, + val_to_str_ext_const(product, &ext_usb_products_vals, "Unknown"), + product_id); + offset += 2; + + if (!pinfo->fd->flags.visited) { + guint k_bus_id; + guint k_device_address; + guint k_frame_number; + emem_tree_key_t key[4]; + device_product_data_t *device_product_data; + device_protocol_data_t *device_protocol_data; + + k_frame_number = pinfo->fd->num; + k_device_address = device_address; + k_bus_id = bus_id; + + key[0].length = 1; + key[0].key = &k_device_address; + key[1].length = 1; + key[1].key = &k_bus_id; + key[2].length = 1; + key[2].key = &k_frame_number; + key[3].length = 0; + key[3].key = NULL; + + device_product_data = se_new(device_product_data_t); + device_product_data->vendor = vendor_id; + device_product_data->product = product_id; + device_product_data->bus_id = bus_id; + device_product_data->device_address = device_address; + se_tree_insert32_array(device_to_product_table, key, device_product_data); + + device_protocol_data = se_new(device_protocol_data_t); + device_protocol_data->protocol = protocol; + device_protocol_data->bus_id = bus_id; + device_protocol_data->device_address = device_address; + + se_tree_insert32_array(device_to_protocol_table, key, device_protocol_data); + } + + /* bcdDevice */ + proto_tree_add_item(tree, hf_usb_bcdDevice, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* iManufacturer */ + proto_tree_add_item(tree, hf_usb_iManufacturer, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* iProduct */ + proto_tree_add_item(tree, hf_usb_iProduct, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* iSerialNumber */ + proto_tree_add_item(tree, hf_usb_iSerialNumber, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bNumConfigurations */ + proto_tree_add_item(tree, hf_usb_bNumConfigurations, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + if (item) { + proto_item_set_len(item, offset-old_offset); + } + + return offset; +} + +/* 9.6.7 */ +static int +dissect_usb_string_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info _U_) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + int old_offset = offset; + guint8 len; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "STRING DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + len = tvb_get_guint8(tvb, offset); + /* The USB spec says that the languages / the string are UTF16 and not + 0-terminated, i.e. the length field must contain an even number */ + if (len & 0x1) { + proto_item *len_item; + + /* bLength */ + len_item = proto_tree_add_item(tree, hf_usb_bLength, tvb, offset, 1, ENC_LITTLE_ENDIAN); + expert_add_info_format(pinfo, len_item, PI_PROTOCOL, PI_WARN, + "Invalid STRING DESCRIPTOR Length (must be even)"); + + /* bDescriptorType */ + proto_tree_add_item(tree, hf_usb_bDescriptorType, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); + } + else + dissect_usb_descriptor_header(tree, tvb, offset); + + offset += 2; + + if (!usb_trans_info->u.get_descriptor.index) { + /* list of languanges */ + while(len>(offset-old_offset)) { + /* wLANGID */ + proto_tree_add_item(tree, hf_usb_wLANGID, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset+=2; + } + } else { + char *str; + guint8 str_len; + + /* Make sure that tvb_get_ephemeral_unicode_string() gets an even + string length even if the length field contains an (invalid) + odd number. + */ + str_len = (len-2) & ~0x1; + + /* unicode string */ + str = tvb_get_ephemeral_unicode_string(tvb, offset, str_len, ENC_LITTLE_ENDIAN); + proto_tree_add_string(tree, hf_usb_bString, tvb, offset, len-2, str); + offset += len-2; + } + + if (item) { + proto_item_set_len(item, offset-old_offset); + } + + return offset; +} + + + +/* 9.6.5 */ +static int +dissect_usb_interface_descriptor(packet_info *pinfo, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + const char *class_str = NULL; + int old_offset = offset; + guint8 len; + guint8 interface_num; + guint8 alt_setting; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "INTERFACE DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + len = tvb_get_guint8(tvb, offset); + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + /* bInterfaceNumber */ + interface_num = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_usb_bInterfaceNumber, tvb, offset, 1, ENC_LITTLE_ENDIAN); + usb_conv_info->interfaceNum = interface_num; + offset += 1; + + /* bAlternateSetting */ + alt_setting = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_usb_bAlternateSetting, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bNumEndpoints */ + proto_tree_add_item(tree, hf_usb_bNumEndpoints, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bInterfaceClass */ + proto_tree_add_item(tree, hf_usb_bInterfaceClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + /* save the class so we can access it later in the endpoint descriptor */ + usb_conv_info->interfaceClass = tvb_get_guint8(tvb, offset); + + class_str = val_to_str_ext(usb_conv_info->interfaceClass, &usb_class_vals_ext, "unknown (0x%X)"); + proto_item_append_text(item, " (%u.%u): class %s", interface_num, alt_setting, class_str); + + if (!pinfo->fd->flags.visited && (alt_setting == 0)) { + /* Register conversation for this interface in case CONTROL messages are sent to it */ + usb_trans_info->interface_info = get_usb_iface_conv_info(pinfo, interface_num); + usb_trans_info->interface_info->interfaceClass = tvb_get_guint8(tvb, offset); + /* save information useful to class-specific dissectors */ + usb_trans_info->interface_info->interfaceSubclass = tvb_get_guint8(tvb, offset+1); + usb_trans_info->interface_info->interfaceProtocol = tvb_get_guint8(tvb, offset+2); + usb_trans_info->interface_info->deviceVendor = usb_conv_info->deviceVendor; + usb_trans_info->interface_info->deviceProduct = usb_conv_info->deviceProduct; + } + offset += 1; + + /* bInterfaceSubClass */ + proto_tree_add_item(tree, hf_usb_bInterfaceSubClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + /* save the subclass so we can access it later in class-specific descriptors */ + usb_conv_info->interfaceSubclass = tvb_get_guint8(tvb, offset); + offset += 1; + + /* bInterfaceProtocol */ + proto_tree_add_item(tree, hf_usb_bInterfaceProtocol, tvb, offset, 1, ENC_LITTLE_ENDIAN); + usb_conv_info->interfaceProtocol = tvb_get_guint8(tvb, offset); + offset += 1; + + /* iInterface */ + proto_tree_add_item(tree, hf_usb_iInterface, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + if (item) { + proto_item_set_len(item, len); + } + if (offset < old_offset+len) { + /* skip unknown records */ + offset = old_offset + len; + } + + return offset; +} + +/* 9.6.6 */ +static const true_false_string tfs_endpoint_direction = { + "IN Endpoint", + "OUT Endpoint" +}; + +void dissect_usb_endpoint_address(proto_tree *tree, tvbuff_t *tvb, int offset) +{ + proto_item *endpoint_item = NULL; + proto_tree *endpoint_tree = NULL; + guint8 endpoint; + + if (tree) { + endpoint_item = proto_tree_add_item(tree, hf_usb_bEndpointAddress, tvb, offset, 1, ENC_LITTLE_ENDIAN); + endpoint_tree = proto_item_add_subtree(endpoint_item, ett_configuration_bEndpointAddress); + } + endpoint = tvb_get_guint8(tvb, offset)&0x0f; + proto_tree_add_item(endpoint_tree, hf_usb_bEndpointAddress_direction, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_item_append_text(endpoint_item, " %s", (tvb_get_guint8(tvb, offset)&0x80)?"IN":"OUT"); + proto_tree_add_item(endpoint_tree, hf_usb_bEndpointAddress_number, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_item_append_text(endpoint_item, " Endpoint:%d", endpoint); +} + +static int +dissect_usb_endpoint_descriptor(packet_info *pinfo, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + proto_item *ep_attrib_item = NULL; + proto_tree *ep_attrib_tree = NULL; + proto_item *ep_pktsize_item; + proto_tree *ep_pktsize_tree; + int old_offset = offset; + guint8 endpoint; + guint8 ep_type; + guint8 len; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "ENDPOINT DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + len = tvb_get_guint8(tvb, offset); + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + endpoint = tvb_get_guint8(tvb, offset)&0x0f; + dissect_usb_endpoint_address(tree, tvb, offset); + offset += 1; + + /* Together with class from the interface descriptor we know what kind + * of class the device at endpoint is. + * Make sure a conversation exists for this endpoint and attach a + * usb_conv_into_t structure to it. + * + * All endpoints for the same interface descriptor share the same + * usb_conv_info structure. + */ + if ((!pinfo->fd->flags.visited)&&usb_trans_info->interface_info) { + conversation_t *conversation; + + if (pinfo->destport == NO_ENDPOINT) { + static address tmp_addr; + static usb_address_t usb_addr; + + /* Create a new address structure that points to the same device + * but the new endpoint. + */ + usb_addr.device = ((usb_address_t *)(pinfo->src.data))->device; + usb_addr.endpoint = htolel(endpoint); + SET_ADDRESS(&tmp_addr, AT_USB, USB_ADDR_LEN, (char *)&usb_addr); + conversation = get_usb_conversation(pinfo, &tmp_addr, &pinfo->dst, usb_addr.endpoint, pinfo->destport); + } else { + static address tmp_addr; + static usb_address_t usb_addr; + + /* Create a new address structure that points to the same device + * but the new endpoint. + */ + usb_addr.device = ((usb_address_t *)(pinfo->dst.data))->device; + usb_addr.endpoint = htolel(endpoint); + SET_ADDRESS(&tmp_addr, AT_USB, USB_ADDR_LEN, (char *)&usb_addr); + conversation = get_usb_conversation(pinfo, &pinfo->src, &tmp_addr, pinfo->srcport, usb_addr.endpoint); + } + + conversation_add_proto_data(conversation, proto_usb, usb_trans_info->interface_info); + } + + /* bmAttributes */ + ep_type = ENDPOINT_TYPE(tvb_get_guint8(tvb, offset)); + if (tree) { + ep_attrib_item = proto_tree_add_item(tree, hf_usb_bmAttributes, tvb, offset, 1, ENC_LITTLE_ENDIAN); + ep_attrib_tree = proto_item_add_subtree(ep_attrib_item, ett_endpoint_bmAttributes); + } + proto_tree_add_item(ep_attrib_tree, hf_usb_bEndpointAttributeTransfer, tvb, offset, 1, ENC_LITTLE_ENDIAN); + /* isochronous only */ + proto_tree_add_item(ep_attrib_tree, hf_usb_bEndpointAttributeSynchonisation, tvb, offset, 1, ENC_LITTLE_ENDIAN); + /* isochronous only */ + proto_tree_add_item(ep_attrib_tree, hf_usb_bEndpointAttributeBehaviour, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* wMaxPacketSize */ + ep_pktsize_item = proto_tree_add_item(tree, hf_usb_wMaxPacketSize, tvb, offset, 2, ENC_LITTLE_ENDIAN); + ep_pktsize_tree = proto_item_add_subtree(ep_pktsize_item, ett_endpoint_wMaxPacketSize); + if ((ep_type == ENDPOINT_TYPE_INTERRUPT) || (ep_type == ENDPOINT_TYPE_ISOCHRONOUS)) { + proto_tree_add_item(ep_pktsize_tree, hf_usb_wMaxPacketSize_slots, tvb, offset, 2, ENC_LITTLE_ENDIAN); + } + proto_tree_add_item(ep_pktsize_tree, hf_usb_wMaxPacketSize_size, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset+=2; + + /* bInterval */ + proto_tree_add_item(tree, hf_usb_bInterval, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + if (item) { + proto_item_set_len(item, len); + } + if (offset < old_offset+len) { + /* skip unknown records */ + offset = old_offset + len; + } + + return offset; +} + +/* ECN */ +static int +dissect_usb_interface_assn_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + int old_offset = offset; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "INTERFACE ASSOCIATION DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + /* bFirstInterface */ + proto_tree_add_item(tree, hf_usb_bFirstInterface, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bInterfaceCount */ + proto_tree_add_item(tree, hf_usb_bInterfaceCount, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bFunctionClass */ + proto_tree_add_item(tree, hf_usb_bFunctionClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bFunctionSubclass */ + proto_tree_add_item(tree, hf_usb_bFunctionSubClass, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bFunctionProtocol */ + proto_tree_add_item(tree, hf_usb_bFunctionProtocol, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* iFunction */ + proto_tree_add_item(tree, hf_usb_iFunction, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + if (item) { + proto_item_set_len(item, offset-old_offset); + } + + return offset; +} + +static int +dissect_usb_unknown_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + guint8 bLength; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "UNKNOWN DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + bLength = tvb_get_guint8(tvb, offset); + dissect_usb_descriptor_header(tree, tvb, offset); + offset += bLength; + + if (item) { + proto_item_set_len(item, bLength); + } + + return offset; +} + +/* 9.6.3 */ +static const true_false_string tfs_mustbeone = { + "Must be 1 for USB 1.1 and higher", + "FIXME: Is this a USB 1.0 device" +}; +static const true_false_string tfs_selfpowered = { + "This device is SELF-POWERED", + "This device is powered from the USB bus" +}; +static const true_false_string tfs_remotewakeup = { + "This device supports REMOTE WAKEUP", + "This device does NOT support remote wakeup" +}; +static int +dissect_usb_configuration_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + int old_offset = offset; + guint16 len; + proto_item *flags_item = NULL; + proto_tree *flags_tree = NULL; + guint8 flags; + proto_item *power_item; + guint8 power; + gboolean truncation_expected; + + usb_conv_info->interfaceClass = IF_CLASS_UNKNOWN; + usb_conv_info->interfaceSubclass = IF_SUBCLASS_UNKNOWN; + usb_conv_info->interfaceProtocol = IF_PROTOCOL_UNKNOWN; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, -1, "CONFIGURATION DESCRIPTOR"); + tree = proto_item_add_subtree(item, ett_descriptor_device); + } + + dissect_usb_descriptor_header(tree, tvb, offset); + offset += 2; + + /* wTotalLength */ + proto_tree_add_item(tree, hf_usb_wTotalLength, tvb, offset, 2, ENC_LITTLE_ENDIAN); + len = tvb_get_letohs(tvb, offset); + offset+=2; + + /* bNumInterfaces */ + proto_tree_add_item(tree, hf_usb_bNumInterfaces, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bConfigurationValue */ + proto_tree_add_item(tree, hf_usb_bConfigurationValue, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* iConfiguration */ + proto_tree_add_item(tree, hf_usb_iConfiguration, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + /* bmAttributes */ + if (tree) { + flags_item = proto_tree_add_item(tree, hf_usb_configuration_bmAttributes, tvb, offset, 1, ENC_LITTLE_ENDIAN); + flags_tree = proto_item_add_subtree(flags_item, ett_configuration_bmAttributes); + } + flags = tvb_get_guint8(tvb, offset); + proto_tree_add_item(flags_tree, hf_usb_configuration_legacy10buspowered, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(flags_tree, hf_usb_configuration_selfpowered, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_item_append_text(flags_item, " %sSELF-POWERED", (flags&0x40)?"":"NOT "); + proto_tree_add_item(flags_tree, hf_usb_configuration_remotewakeup, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_item_append_text(flags_item, " %sREMOTE-WAKEUP", (flags&0x20)?"":"NO "); + offset += 1; + + /* bMaxPower */ + power_item = proto_tree_add_item(tree, hf_usb_bMaxPower, tvb, offset, 1, ENC_LITTLE_ENDIAN); + power = tvb_get_guint8(tvb, offset); + proto_item_append_text(power_item, " (%dmA)", power*2); + offset += 1; + + /* initialize interface_info to NULL */ + usb_trans_info->interface_info = NULL; + + truncation_expected = (usb_trans_info->setup.wLength < len); + + /* decode any additional interface and endpoint descriptors */ + while(len>(offset-old_offset)) { + guint8 next_type; + guint8 next_len = 0; + gint remaining_tvb, remaining_len; + tvbuff_t *next_tvb = NULL; + + /* Handle truncated descriptors appropriately */ + remaining_tvb = tvb_length_remaining(tvb, offset); + if (remaining_tvb > 0) { + next_len = tvb_get_guint8(tvb, offset); + remaining_len = len - (offset - old_offset); + if ((next_len < 3) || (next_len > remaining_len)) { + item = proto_tree_add_text(parent_tree, tvb, offset, 1, + "Invalid descriptor length: %u", next_len); + proto_item_set_len(item, 1); + expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR, + "Invalid descriptor length: %u", next_len); + item = NULL; + break; + } + } + + if ((remaining_tvb == 0) || (next_len > remaining_tvb)) { + if (!truncation_expected) { + THROW(ReportedBoundsError); + } + break; + } + + next_type = tvb_get_guint8(tvb, offset+1); + switch(next_type) { + case USB_DT_INTERFACE: + offset = dissect_usb_interface_descriptor(pinfo, parent_tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_ENDPOINT: + offset = dissect_usb_endpoint_descriptor(pinfo, parent_tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_INTERFACE_ASSOCIATION: + offset = dissect_usb_interface_assn_descriptor(pinfo, parent_tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + default: + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (dissector_try_uint(usb_descriptor_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, parent_tree)) { + offset += next_len; + } else { + offset = dissect_usb_unknown_descriptor(pinfo, parent_tree, tvb, offset, usb_trans_info, usb_conv_info); + } + break; + /* was: return offset; */ + } + } + + if (item) { + proto_item_set_len(item, offset-old_offset); + } + + /* Clear any class association from the Control endpoint. + * We need the association temporarily, to establish + * context for class-specific descriptor dissectors, + * but the association must not persist beyond this function. + * If it did, all traffic on the Control endpoint would be labeled + * as belonging to the class of the last INTERFACE descriptor, + * which would be especially inappropriate for composite devices. + */ + usb_conv_info->interfaceClass = IF_CLASS_UNKNOWN; + usb_conv_info->interfaceSubclass = IF_SUBCLASS_UNKNOWN; + usb_conv_info->interfaceProtocol = IF_PROTOCOL_UNKNOWN; + + return offset; +} + +/* 9.4.3 */ +static int +dissect_usb_setup_get_descriptor_request(packet_info *pinfo, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* descriptor index */ + proto_tree_add_item(tree, hf_usb_descriptor_index, tvb, offset, 1, ENC_LITTLE_ENDIAN); + usb_trans_info->u.get_descriptor.index = tvb_get_guint8(tvb, offset); + offset += 1; + + /* descriptor type */ + proto_tree_add_item(tree, hf_usb_bDescriptorType, tvb, offset, 1, ENC_LITTLE_ENDIAN); + usb_trans_info->u.get_descriptor.type = tvb_get_guint8(tvb, offset); + offset += 1; + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, " %s", + val_to_str_ext(usb_trans_info->u.get_descriptor.type, &descriptor_type_vals_ext, "Unknown type %u")); + } + + /* language id */ + proto_tree_add_item(tree, hf_usb_language_id, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset+=2; + + /* length */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_get_descriptor_response(packet_info *pinfo, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info, + guint bus_id, guint device_address) +{ + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, " %s", + val_to_str_ext(usb_trans_info->u.get_descriptor.type, &descriptor_type_vals_ext, "Unknown type %u")); + } + switch(usb_trans_info->u.get_descriptor.type) { + case USB_DT_DEVICE: + offset = dissect_usb_device_descriptor(pinfo, tree, tvb, offset, usb_trans_info, bus_id, device_address); + break; + case USB_DT_CONFIG: + offset = dissect_usb_configuration_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_STRING: + offset = dissect_usb_string_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_INTERFACE: + offset = dissect_usb_interface_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_ENDPOINT: + offset = dissect_usb_endpoint_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + case USB_DT_DEVICE_QUALIFIER: + offset = dissect_usb_device_qualifier_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info, bus_id, device_address); + break; + case USB_DT_RPIPE: + if (usb_conv_info->interfaceClass == IF_CLASS_HID) { + offset = dissect_usb_hid_get_report_descriptor(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info); + break; + } + /* else fall through as default/unknown */ + default: + /* XXX dissect the descriptor coming back from the device */ + proto_tree_add_text(tree, tvb, offset, -1, "GET DESCRIPTOR data (unknown descriptor type %u)", usb_trans_info->u.get_descriptor.type); + offset = tvb_length(tvb); + break; + } + + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / GET INTERFACE + */ + + +/* 9.4.4 */ +static int +dissect_usb_setup_get_interface_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* zero */ + proto_tree_add_item(tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* interface */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* length */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_get_interface_response(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* alternate setting */ + proto_tree_add_item(tree, hf_usb_bAlternateSetting, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / GET STATUS + */ + + +/* 9.4.5 */ +static int +dissect_usb_setup_get_status_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* zero */ + proto_tree_add_item(tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero/interface/endpoint */ + /* XXX - check based on request type */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* length */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_get_status_response(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* status */ + /* XXX - show bits */ + proto_tree_add_item(tree, hf_usb_wStatus, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / SET ADDRESS + */ + + +/* 9.4.6 */ +static int +dissect_usb_setup_set_address_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* device address */ + proto_tree_add_item(tree, hf_usb_device_address, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_set_address_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / SET CONFIGURATION + */ + + +/* 9.4.7 */ +static int +dissect_usb_setup_set_configuration_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* configuration value */ + proto_tree_add_item(tree, hf_usb_bConfigurationValue, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_set_configuration_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / SET FEATURE + */ + + +/* 9.4.9 */ +static int +dissect_usb_setup_set_feature_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* feature selector */ + proto_tree_add_item(tree, hf_usb_wFeatureSelector, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero/interface/endpoint or test selector */ + /* XXX - check based on request type */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_set_feature_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / SET INTERFACE + */ + + +/* 9.4.10 */ +static int +dissect_usb_setup_set_interface_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* alternate setting */ + proto_tree_add_item(tree, hf_usb_bAlternateSetting, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 2; + + /* interface */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* zero */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_set_interface_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + return offset; +} + + +/* + * These dissectors are used to dissect the setup part and the data + * for URB_CONTROL_INPUT / SYNCH FRAME + */ + + +/* 9.4.11 */ +static int +dissect_usb_setup_synch_frame_request(packet_info *pinfo _U_, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* zero */ + proto_tree_add_item(tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* endpoint */ + /* XXX */ + proto_tree_add_item(tree, hf_usb_wInterface, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + /* two */ + proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + +static int +dissect_usb_setup_synch_frame_response(packet_info *pinfo _U_, proto_tree *tree _U_, + tvbuff_t *tvb _U_, int offset, + usb_trans_info_t *usb_trans_info _U_, + usb_conv_info_t *usb_conv_info _U_, + guint bus_id _U_, guint device_address _U_) +{ + /* frame number */ + proto_tree_add_item(tree, hf_usb_wFrameNumber, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + return offset; +} + + +typedef int (*usb_setup_dissector)(packet_info *pinfo, proto_tree *tree, + tvbuff_t *tvb, int offset, + usb_trans_info_t *usb_trans_info, + usb_conv_info_t *usb_conv_info, + guint bus_id, guint device_address); + +typedef struct _usb_setup_dissector_table_t { + guint8 request; + usb_setup_dissector dissector; + +} usb_setup_dissector_table_t; +#define USB_SETUP_GET_STATUS 0 +#define USB_SETUP_CLEAR_FEATURE 1 +#define USB_SETUP_SET_FEATURE 3 +#define USB_SETUP_SET_ADDRESS 5 +#define USB_SETUP_GET_DESCRIPTOR 6 +#define USB_SETUP_SET_DESCRIPTOR 7 +#define USB_SETUP_GET_CONFIGURATION 8 +#define USB_SETUP_SET_CONFIGURATION 9 +#define USB_SETUP_GET_INTERFACE 10 +#define USB_SETUP_SET_INTERFACE 11 +#define USB_SETUP_SYNCH_FRAME 12 +#define USB_SETUP_SET_SEL 48 +#define USB_SETUP_SET_ISOCH_DELAY 49 + +static const usb_setup_dissector_table_t setup_request_dissectors[] = { + {USB_SETUP_GET_STATUS, dissect_usb_setup_get_status_request}, + {USB_SETUP_CLEAR_FEATURE, dissect_usb_setup_clear_feature_request}, + {USB_SETUP_SET_FEATURE, dissect_usb_setup_set_feature_request}, + {USB_SETUP_SET_ADDRESS, dissect_usb_setup_set_address_request}, + {USB_SETUP_GET_DESCRIPTOR, dissect_usb_setup_get_descriptor_request}, + {USB_SETUP_SET_CONFIGURATION, dissect_usb_setup_set_configuration_request}, + {USB_SETUP_GET_INTERFACE, dissect_usb_setup_get_interface_request}, + {USB_SETUP_SET_INTERFACE, dissect_usb_setup_set_interface_request}, + {USB_SETUP_SYNCH_FRAME, dissect_usb_setup_synch_frame_request}, + {0, NULL} +}; + +static const usb_setup_dissector_table_t setup_response_dissectors[] = { + {USB_SETUP_GET_STATUS, dissect_usb_setup_get_status_response}, + {USB_SETUP_CLEAR_FEATURE, dissect_usb_setup_clear_feature_response}, + {USB_SETUP_SET_FEATURE, dissect_usb_setup_set_feature_response}, + {USB_SETUP_SET_ADDRESS, dissect_usb_setup_set_address_response}, + {USB_SETUP_GET_DESCRIPTOR, dissect_usb_setup_get_descriptor_response}, + {USB_SETUP_GET_CONFIGURATION, dissect_usb_setup_get_configuration_response}, + {USB_SETUP_SET_CONFIGURATION, dissect_usb_setup_set_configuration_response}, + {USB_SETUP_GET_INTERFACE, dissect_usb_setup_get_interface_response}, + {USB_SETUP_SET_INTERFACE, dissect_usb_setup_set_interface_response}, + {USB_SETUP_SYNCH_FRAME, dissect_usb_setup_synch_frame_response}, + {0, NULL} +}; + +static const value_string setup_request_names_vals[] = { + {USB_SETUP_GET_STATUS, "GET STATUS"}, + {USB_SETUP_CLEAR_FEATURE, "CLEAR FEATURE"}, + {USB_SETUP_SET_FEATURE, "SET FEATURE"}, + {USB_SETUP_SET_ADDRESS, "SET ADDRESS"}, + {USB_SETUP_GET_DESCRIPTOR, "GET DESCRIPTOR"}, + {USB_SETUP_SET_DESCRIPTOR, "SET DESCRIPTOR"}, + {USB_SETUP_GET_CONFIGURATION, "GET CONFIGURATION"}, + {USB_SETUP_SET_CONFIGURATION, "SET CONFIGURATION"}, + {USB_SETUP_GET_INTERFACE, "GET INTERFACE"}, + {USB_SETUP_SET_INTERFACE, "SET INTERFACE"}, + {USB_SETUP_SYNCH_FRAME, "SYNCH FRAME"}, + {USB_SETUP_SET_SEL, "SET SEL"}, + {USB_SETUP_SET_ISOCH_DELAY, "SET ISOCH DELAY"}, + {0, NULL} +}; + + +static const true_false_string tfs_bmrequesttype_direction = { + "Device-to-host", + "Host-to-device" +}; + +static const value_string bmrequesttype_type_vals[] = { + {RQT_SETUP_TYPE_STANDARD, "Standard"}, + {RQT_SETUP_TYPE_CLASS, "Class"}, + {RQT_SETUP_TYPE_VENDOR, "Vendor"}, + {0, NULL} +}; + +static const value_string bmrequesttype_recipient_vals[] = { + {RQT_SETUP_RECIPIENT_DEVICE, "Device" }, + {RQT_SETUP_RECIPIENT_INTERFACE, "Interface" }, + {RQT_SETUP_RECIPIENT_ENDPOINT, "Endpoint" }, + {RQT_SETUP_RECIPIENT_OTHER, "Other" }, + {0, NULL } +}; + +static int +dissect_usb_bmrequesttype(proto_tree *parent_tree, tvbuff_t *tvb, int offset, int *type) +{ + proto_item *item = NULL; + proto_tree *tree = NULL; + + if (parent_tree) { + item = proto_tree_add_item(parent_tree, hf_usb_bmRequestType, tvb, offset, 1, ENC_LITTLE_ENDIAN); + tree = proto_item_add_subtree(item, ett_usb_setup_bmrequesttype); + } + + *type = USB_TYPE(tvb_get_guint8(tvb, offset)); + proto_tree_add_item(tree, hf_usb_bmRequestType_direction, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(tree, hf_usb_bmRequestType_type, tvb, offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(tree, hf_usb_bmRequestType_recipient, tvb, offset, 1, ENC_LITTLE_ENDIAN); + + return ++offset; +} + +/* Adds the Linux USB pseudo header fields to the tree. + * NOTE: The multi-byte fields in this header, and the pseudo-header + * extension, are in host-endian format so we can't + * use proto_tree_add_item() nor the tvb_get_xyz() routines and is + * the reason for the tvb_memcpy() and proto_tree_add_uint[64]() + * pairs below. */ +static void +dissect_linux_usb_pseudo_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint *bus_id, guint *device_address) +{ + guint8 transfer_type; + guint8 endpoint_number; + guint8 transfer_type_and_direction; + guint8 type; + guint8 flag[2]; + guint16 val16; + guint32 val32; + guint64 val64; + + tvb_memcpy(tvb, (guint8 *)&val64, 0, 8); + proto_tree_add_uint64(tree, hf_usb_urb_id, tvb, 0, 8, val64); + + /* show the event type of this URB as string and as a character */ + type = tvb_get_guint8(tvb, 8); + proto_tree_add_uint_format_value(tree, hf_usb_urb_type, tvb, 8, 1, + type, "%s ('%c')", val_to_str(type, usb_urb_type_vals, "Unknown %d"), + isprint(type) ? type : '.'); + proto_tree_add_item(tree, hf_usb_transfer_type, tvb, 9, 1, ENC_BIG_ENDIAN); + + if (check_col(pinfo->cinfo, COL_INFO)) { + transfer_type = tvb_get_guint8(tvb, 9); + endpoint_number = tvb_get_guint8(tvb, 10); + transfer_type_and_direction = (transfer_type & 0x7F) | (endpoint_number & 0x80); + col_append_str(pinfo->cinfo, COL_INFO, + val_to_str(transfer_type_and_direction, usb_transfer_type_and_direction_vals, "Unknown type %x")); + } + + proto_tree_add_bitmask(tree, tvb, 10, hf_usb_endpoint_number, ett_usb_endpoint, usb_endpoint_fields, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_usb_device_address, tvb, 11, 1, ENC_BIG_ENDIAN); + *device_address = tvb_get_guint8(tvb, 11); + + tvb_memcpy(tvb, (guint8 *)&val16, 12, 2); + proto_tree_add_uint(tree, hf_usb_bus_id, tvb, 12, 2, val16); + *bus_id = tvb_get_letohs(tvb, 12); + + /* Right after the pseudo header we always have + * sizeof(struct usb_device_setup_hdr) bytes. The content of these + * bytes only have meaning in case setup_flag == 0. + */ + flag[0] = tvb_get_guint8(tvb, 14); + flag[1] = '\0'; + if (flag[0] == 0) { + proto_tree_add_string(tree, hf_usb_setup_flag, tvb, 14, 1, "relevant (0)"); + } else { + proto_tree_add_string_format_value(tree, hf_usb_setup_flag, tvb, + 14, 1, flag, "not relevant ('%c')", isprint(flag[0]) ? flag[0]: '.'); + } + + flag[0] = tvb_get_guint8(tvb, 15); + flag[1] = '\0'; + if (flag[0] == 0) { + proto_tree_add_string(tree, hf_usb_data_flag, tvb, 15, 1, "present (0)"); + } else { + proto_tree_add_string_format_value(tree, hf_usb_data_flag, tvb, + 15, 1, flag, "not present ('%c')", isprint(flag[0]) ? flag[0] : '.'); + } + + tvb_memcpy(tvb, (guint8 *)&val64, 16, 8); + proto_tree_add_uint64(tree, hf_usb_urb_ts_sec, tvb, 16, 8, val64); + + tvb_memcpy(tvb, (guint8 *)&val32, 24, 4); + proto_tree_add_uint(tree, hf_usb_urb_ts_usec, tvb, 24, 4, val32); + + tvb_memcpy(tvb, (guint8 *)&val32, 28, 4); + proto_tree_add_int(tree, hf_usb_urb_status, tvb, 28, 4, val32); + + tvb_memcpy(tvb, (guint8 *)&val32, 32, 4); + proto_tree_add_uint(tree, hf_usb_urb_len, tvb, 32, 4, val32); + + tvb_memcpy(tvb, (guint8 *)&val32, 36, 4); + proto_tree_add_uint(tree, hf_usb_urb_data_len, tvb, 36, 4, val32); +} + +/* + * XXX - put these into the protocol tree as appropriate. + */ +static int +dissect_linux_usb_pseudo_header_ext(tvbuff_t *tvb, int offset, + packet_info *pinfo _U_, + proto_tree *tree _U_) +{ + guint32 ndesc; + + offset += 4; /* interval */ + offset += 4; /* start_frame */ + offset += 4; /* copy of URB's transfer flags */ + + tvb_memcpy(tvb, (guint8 *)&ndesc, offset, 4); + offset += 4; + + /* + * Isochronous descriptors. Each one is 16 bytes long. + */ + offset += ndesc*16; + + return offset; +} + +static void +dissect_linux_usb_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent, + gboolean header_len_64_bytes) +{ + unsigned int offset = 0; + int type, endpoint; + guint8 setup_flag; + proto_tree *tree = NULL; + guint32 tmp_addr; + static usb_address_t src_addr, dst_addr; /* has to be static due to SET_ADDRESS */ + guint32 src_endpoint, dst_endpoint; + gboolean is_request; + usb_conv_info_t *usb_conv_info; + usb_trans_info_t *usb_trans_info = NULL; + conversation_t *conversation; + usb_tap_data_t *tap_data; + guint bus_id = 0; + guint device_address = 0; + usb_data_t *usb_data; + void *pd_save; + tvbuff_t *next_tvb = NULL; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "USB"); + + /* add usb hdr*/ + if (parent) { + proto_item *ti; + ti = proto_tree_add_protocol_format(parent, proto_usb, tvb, 0, + header_len_64_bytes ? 64 : 48, "USB URB"); + tree = proto_item_add_subtree(ti, usb_hdr); + } + + dissect_linux_usb_pseudo_header(tvb, pinfo, tree, &bus_id, &device_address); + is_request = (tvb_get_guint8(tvb, 8) == URB_SUBMIT) ? TRUE : FALSE; + type = tvb_get_guint8(tvb, 9); + endpoint = tvb_get_guint8(tvb, 10) & (~URB_TRANSFER_IN); + tmp_addr = tvb_get_guint8(tvb, 11); + setup_flag = tvb_get_guint8(tvb, 14); + offset += 40; /* skip first part of the pseudo-header */ + + usb_data = se_new(usb_data_t); + usb_data->bus_id = bus_id; + usb_data->device_address = device_address; + usb_data->endpoint = endpoint; + + if (tvb_get_guint8(tvb, 10) & URB_TRANSFER_IN) { + usb_data->direction = P2P_DIR_RECV; + } else { + usb_data->direction = P2P_DIR_SENT; + } + + pd_save = pinfo->private_data; + pinfo->private_data = usb_data; + + /* Set up addresses and ports. */ + if (is_request) { + src_addr.device = 0xffffffff; + src_addr.endpoint = src_endpoint = NO_ENDPOINT; + dst_addr.device = htolel(tmp_addr); + dst_addr.endpoint = dst_endpoint = htolel(endpoint); + } else { + src_addr.device = htolel(tmp_addr); + src_addr.endpoint = src_endpoint = htolel(endpoint); + dst_addr.device = 0xffffffff; + dst_addr.endpoint = dst_endpoint = NO_ENDPOINT; + } + + SET_ADDRESS(&pinfo->net_src, AT_USB, USB_ADDR_LEN, (char *)&src_addr); + SET_ADDRESS(&pinfo->src, AT_USB, USB_ADDR_LEN, (char *)&src_addr); + SET_ADDRESS(&pinfo->net_dst, AT_USB, USB_ADDR_LEN, (char *)&dst_addr); + SET_ADDRESS(&pinfo->dst, AT_USB, USB_ADDR_LEN, (char *)&dst_addr); + pinfo->ptype = PT_USB; + pinfo->srcport = src_endpoint; + pinfo->destport = dst_endpoint; + + conversation = get_usb_conversation(pinfo, &pinfo->src, &pinfo->dst, pinfo->srcport, pinfo->destport); + + usb_conv_info = get_usb_conv_info(conversation); + pinfo->usb_conv_info = usb_conv_info; + + + /* request/response matching so we can keep track of transaction specific + * data. + */ + if (is_request) { + /* this is a request */ + usb_trans_info = (usb_trans_info_t *)se_tree_lookup32(usb_conv_info->transactions, pinfo->fd->num); + if (!usb_trans_info) { + usb_trans_info = se_new0(usb_trans_info_t); + usb_trans_info->request_in = pinfo->fd->num; + usb_trans_info->req_time = pinfo->fd->abs_ts; + usb_trans_info->header_len_64 = header_len_64_bytes; + + se_tree_insert32(usb_conv_info->transactions, pinfo->fd->num, usb_trans_info); + } + usb_conv_info->usb_trans_info = usb_trans_info; + + if (usb_trans_info->response_in) { + proto_item *ti; + + ti = proto_tree_add_uint(tree, hf_usb_response_in, tvb, 0, 0, usb_trans_info->response_in); + PROTO_ITEM_SET_GENERATED(ti); + } + } else { + /* this is a response */ + if (pinfo->fd->flags.visited) { + usb_trans_info = (usb_trans_info_t *)se_tree_lookup32(usb_conv_info->transactions, pinfo->fd->num); + } else { + usb_trans_info = (usb_trans_info_t *)se_tree_lookup32_le(usb_conv_info->transactions, pinfo->fd->num); + if (usb_trans_info) { + usb_trans_info->response_in = pinfo->fd->num; + se_tree_insert32(usb_conv_info->transactions, pinfo->fd->num, usb_trans_info); + } + } + usb_conv_info->usb_trans_info = usb_trans_info; + + if (usb_trans_info && usb_trans_info->request_in) { + proto_item *ti; + nstime_t t, deltat; + + ti = proto_tree_add_uint(tree, hf_usb_request_in, tvb, 0, 0, usb_trans_info->request_in); + PROTO_ITEM_SET_GENERATED(ti); + + t = pinfo->fd->abs_ts; + nstime_delta(&deltat, &t, &usb_trans_info->req_time); + ti = proto_tree_add_time(tree, hf_usb_time, tvb, 0, 0, &deltat); + PROTO_ITEM_SET_GENERATED(ti); + } + } + + tap_data = ep_new(usb_tap_data_t); + tap_data->urb_type = tvb_get_guint8(tvb, 8); + tap_data->transfer_type = (guint8)type; + tap_data->conv_info = usb_conv_info; + tap_data->trans_info = usb_trans_info; + + if (type != URB_CONTROL) { + tap_queue_packet(usb_tap, pinfo, tap_data); + } + + switch(type) { + case URB_BULK: + { + proto_item *item; + + item = proto_tree_add_uint(tree, hf_usb_bInterfaceClass, tvb, 0, 0, usb_conv_info->interfaceClass); + PROTO_ITEM_SET_GENERATED(item); + + /* Skip setup/isochronous header - it's not applicable */ + offset += 8; + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) + offset = dissect_linux_usb_pseudo_header_ext(tvb, offset, pinfo, tree); + + if (tvb_reported_length_remaining(tvb, offset)) { + pinfo->usb_conv_info = usb_conv_info; + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (try_heuristics && dissector_try_heuristic(heur_bulk_subdissector_list, next_tvb, pinfo, parent, NULL)) { + return; + } + else if (dissector_try_uint(usb_bulk_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, parent)) { + return; + } + } + } + break; + case URB_INTERRUPT: + { + proto_item *item; + + item = proto_tree_add_uint(tree, hf_usb_bInterfaceClass, tvb, 0, 0, usb_conv_info->interfaceClass); + PROTO_ITEM_SET_GENERATED(item); + + /* Skip setup/isochronous header - it's not applicable */ + offset += 8; + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) + offset = dissect_linux_usb_pseudo_header_ext(tvb, offset, pinfo, tree); + + if (tvb_reported_length_remaining(tvb, offset)) { + pinfo->usb_conv_info = usb_conv_info; + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (try_heuristics && dissector_try_heuristic(heur_interrupt_subdissector_list, next_tvb, pinfo, parent, NULL)) { + return; + } + else if (dissector_try_uint(usb_interrupt_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, parent)) { + return; + } + } + } + break; + case URB_CONTROL: + { + const usb_setup_dissector_table_t *tmp; + usb_setup_dissector dissector; + proto_item *ti = NULL; + proto_tree *setup_tree = NULL; + int type_2; + + if (is_request) { + if (setup_flag == 0) { + /* this is a request */ + + /* Dissect the setup header - it's applicable */ + + ti = proto_tree_add_protocol_format(parent, proto_usb, tvb, offset, 8, "URB setup"); + setup_tree = proto_item_add_subtree(ti, usb_setup_hdr); + usb_trans_info->setup.requesttype = tvb_get_guint8(tvb, offset); + offset = dissect_usb_bmrequesttype(setup_tree, tvb, offset, &type_2); + + + /* read the request code and spawn off to a class specific + * dissector if found + */ + usb_trans_info->setup.request = tvb_get_guint8(tvb, offset); + usb_trans_info->setup.wValue = tvb_get_letohs(tvb, offset+1); + usb_trans_info->setup.wIndex = tvb_get_letohs(tvb, offset+3); + usb_trans_info->setup.wLength = tvb_get_letohs(tvb, offset+5); + + if (type_2 != RQT_SETUP_TYPE_CLASS) { + tap_queue_packet(usb_tap, pinfo, tap_data); + } + + switch (type_2) { + + case RQT_SETUP_TYPE_STANDARD: + /* + * This is a standard request which is managed by this + * dissector + */ + proto_tree_add_item(setup_tree, hf_usb_request, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s Request", + val_to_str(usb_trans_info->setup.request, setup_request_names_vals, "Unknown type %x")); + } + + dissector = NULL; + for(tmp = setup_request_dissectors;tmp->dissector;tmp++) { + if (tmp->request == usb_trans_info->setup.request) { + dissector = tmp->dissector; + break; + } + } + + if (dissector) { + offset = dissector(pinfo, setup_tree, tvb, offset, usb_trans_info, + usb_conv_info, bus_id, device_address); + } else { + proto_tree_add_item(setup_tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + } + break; + + case RQT_SETUP_TYPE_CLASS: + /* Make sure we have the proper conversation */ + if (USB_RECIPIENT(usb_trans_info->setup.requesttype) == RQT_SETUP_RECIPIENT_INTERFACE) { + guint8 interface_num = usb_trans_info->setup.wIndex & 0xff; + usb_conv_info = get_usb_iface_conv_info(pinfo, interface_num); + usb_conv_info->usb_trans_info = usb_trans_info; + pinfo->usb_conv_info = usb_conv_info; + } else if (USB_RECIPIENT(usb_trans_info->setup.requesttype) == RQT_SETUP_RECIPIENT_ENDPOINT) { + static address endpoint_addr; + endpoint = usb_trans_info->setup.wIndex & 0x0f; + + dst_addr.endpoint = dst_endpoint = htolel(endpoint); + SET_ADDRESS(&endpoint_addr, AT_USB, USB_ADDR_LEN, (char *)&dst_addr); + + conversation = get_usb_conversation(pinfo, &pinfo->src, &endpoint_addr, pinfo->srcport, dst_endpoint); + usb_conv_info = get_usb_conv_info(conversation); + usb_conv_info->usb_trans_info = usb_trans_info; + pinfo->usb_conv_info = usb_conv_info; + } + + tap_data->conv_info = usb_conv_info; + tap_data->trans_info = usb_trans_info; + tap_queue_packet(usb_tap, pinfo, tap_data); + + ti = proto_tree_add_uint(tree, hf_usb_bInterfaceClass, tvb, 0, 0, usb_conv_info->interfaceClass); + PROTO_ITEM_SET_GENERATED(ti); + + /* Try to find a class specific dissector */ + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (try_heuristics && dissector_try_heuristic(heur_control_subdissector_list, next_tvb, pinfo, setup_tree, NULL)) { + return; + } + if (dissector_try_uint(usb_control_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, setup_tree)) { + return; + } + /* Else no class dissector, just display generic fields */ + proto_tree_add_item(setup_tree, hf_usb_request_unknown_class, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(setup_tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + break; + + default: + proto_tree_add_item(setup_tree, hf_usb_request_unknown_class, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(setup_tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + } + } else { + /* Skip setup/isochronous header - it's not applicable */ + offset += 8; + } + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) + offset = dissect_linux_usb_pseudo_header_ext(tvb, offset, pinfo, tree); + + if (tvb_reported_length_remaining(tvb, offset) != 0) { + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (try_heuristics && dissector_try_heuristic(heur_control_subdissector_list, next_tvb, pinfo, parent, NULL)) { + return; + } + if (dissector_try_uint(usb_control_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, parent)) { + return; + } + } + } else { + /* this is a response */ + + /* Skip setup header - it's never applicable for responses */ + offset += 8; + + /* Make sure we have the proper conversation */ + if (usb_trans_info) { + if (USB_TYPE(usb_trans_info->setup.requesttype) == RQT_SETUP_TYPE_CLASS) { + if (USB_RECIPIENT(usb_trans_info->setup.requesttype) == RQT_SETUP_RECIPIENT_INTERFACE) { + guint8 interface_num = usb_trans_info->setup.wIndex & 0xff; + usb_conv_info = get_usb_iface_conv_info(pinfo, interface_num); + usb_conv_info->usb_trans_info = usb_trans_info; + pinfo->usb_conv_info = usb_conv_info; + } else if (USB_RECIPIENT(usb_trans_info->setup.requesttype) == RQT_SETUP_RECIPIENT_ENDPOINT) { + static address endpoint_addr; + endpoint = usb_trans_info->setup.wIndex & 0x0f; + + src_addr.endpoint = src_endpoint = htolel(endpoint); + SET_ADDRESS(&endpoint_addr, AT_USB, USB_ADDR_LEN, (char *)&src_addr); + + conversation = get_usb_conversation(pinfo, &endpoint_addr, &pinfo->dst, src_endpoint, pinfo->destport); + usb_conv_info = get_usb_conv_info(conversation); + usb_conv_info->usb_trans_info = usb_trans_info; + pinfo->usb_conv_info = usb_conv_info; + } + } + } + + tap_data->conv_info = usb_conv_info; + tap_data->trans_info = usb_trans_info; + tap_queue_packet(usb_tap, pinfo, tap_data); + ti = proto_tree_add_uint(tree, hf_usb_bInterfaceClass, tvb, 0, 0, usb_conv_info->interfaceClass); + PROTO_ITEM_SET_GENERATED(ti); + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) + offset = dissect_linux_usb_pseudo_header_ext(tvb, offset, pinfo, tree); + + if (usb_trans_info) { + /* Try to find a class specific dissector */ + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (try_heuristics && dissector_try_heuristic(heur_control_subdissector_list, next_tvb, pinfo, parent, NULL)) { + return; + } + if (dissector_try_uint(usb_control_dissector_table, usb_conv_info->interfaceClass, next_tvb, pinfo, parent)) { + return; + } + + type_2 = USB_TYPE(usb_trans_info->setup.requesttype); + switch (type_2) { + + case RQT_SETUP_TYPE_STANDARD: + /* + * This is a standard response which is managed by this + * dissector + */ + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s Response", + val_to_str(usb_conv_info->usb_trans_info->setup.request, + setup_request_names_vals, "Unknown type %x")); + } + + dissector = NULL; + for(tmp = setup_response_dissectors;tmp->dissector;tmp++) { + if (tmp->request == usb_conv_info->usb_trans_info->setup.request) { + dissector = tmp->dissector; + break; + } + } + + if (dissector) { + offset = dissector(pinfo, parent, tvb, offset, usb_conv_info->usb_trans_info, + usb_conv_info, bus_id, device_address); + } else { + if (tvb_reported_length_remaining(tvb, offset) != 0) { + proto_tree_add_text(parent, tvb, offset, -1, "CONTROL response data"); + offset += tvb_length_remaining(tvb, offset); + } + } + break; + default: + if (tvb_reported_length_remaining(tvb, offset) != 0) { + proto_tree_add_text(parent, tvb, offset, -1, "CONTROL response data"); + offset += tvb_length_remaining(tvb, offset); + } + break; + } + } else { + /* no matching request available */ + if (tvb_reported_length_remaining(tvb, offset) != 0) { + proto_tree_add_text(parent, tvb, offset, -1, "CONTROL response data"); + offset += tvb_length_remaining(tvb, offset); + } + } + } + } + break; + case URB_ISOCHRONOUS: + { + guint32 iso_numdesc = 0; + proto_item *tii; + tii = proto_tree_add_uint(tree, hf_usb_bInterfaceClass, tvb, offset, 0, usb_conv_info->interfaceClass); + PROTO_ITEM_SET_GENERATED(tii); + /* All fields which belong to Linux usbmon headers are in host-endian + * byte order. The fields coming from the USB communication are in little + * endian format (see usb_20.pdf, chapter 8.1 Byte/Bit ordering). + * + * When a capture file is transfered to a host with different endianness + * than packet was captured then the necessary swapping happens in + * wiretap/pcap-common.c, pcap_process_linux_usb_pseudoheader(). + */ + + if (setup_flag == 0) { + proto_item *ti; + proto_tree *setup_tree; + int type_2; + + /* Dissect the setup header - it's applicable */ + + ti = proto_tree_add_protocol_format(parent, proto_usb, tvb, offset, 8, "URB setup"); + setup_tree = proto_item_add_subtree(ti, usb_setup_hdr); + + offset = dissect_usb_bmrequesttype(setup_tree, tvb, offset, &type_2); + proto_tree_add_item(setup_tree, hf_usb_request, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(setup_tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + } else { + + /* Process ISO related fields (usbmon_packet.iso). The fields are + * in host endian byte order so use tvb_memcopy() and + * proto_tree_add_uint() pair. + */ + guint32 val32; + + tvb_memcpy(tvb, (guint8 *)&val32, offset, 4); + proto_tree_add_uint(tree, hf_usb_iso_error_count, tvb, offset, 4, val32); + offset += 4; + + tvb_memcpy(tvb, (guint8 *)&iso_numdesc, offset, 4); + proto_tree_add_uint(tree, hf_usb_iso_numdesc, tvb, offset, 4, iso_numdesc); + offset += 4; + } + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) { + guint32 ndesc; + + offset += 4; /* interval */ + offset += 4; /* start_frame */ + offset += 4; /* copy of URB's transfer flags */ + + tvb_memcpy(tvb, (guint8 *)&ndesc, offset, 4); + offset += 4; + + } + + if (setup_flag != 0) { + proto_tree *urb_tree; + guint32 i; + unsigned int data_base; + guint32 iso_status; + guint32 iso_off; + guint32 iso_len; + guint32 iso_pad; + + data_base = offset + iso_numdesc * 16; + urb_tree = tree; + for (i = 0; i != iso_numdesc; i++) { + /* Fetch ISO descriptor fields stored in host + * endian byte order. + */ + tvb_memcpy(tvb, (guint8 *)&iso_status, offset, 4); + tvb_memcpy(tvb, (guint8 *)&iso_off, offset+4, 4); + tvb_memcpy(tvb, (guint8 *)&iso_len, offset+8, 4); + + if (parent) { + proto_item *ti; + if (iso_len > 0) { + ti = proto_tree_add_protocol_format(urb_tree, proto_usb, tvb, offset, + 16, "USB isodesc %u [%s] (%u bytes)", i, + val_to_str_ext(iso_status, &usb_urb_status_vals_ext, "Error %d"), iso_len); + } else { + ti = proto_tree_add_protocol_format(urb_tree, proto_usb, tvb, offset, + 16, "USB isodesc %u [%s]", i, val_to_str_ext(iso_status, &usb_urb_status_vals_ext, "Error %d")); + } + tree = proto_item_add_subtree(ti, usb_isodesc); + } + + proto_tree_add_int(tree, hf_usb_iso_status, tvb, offset, 4, iso_status); + offset += 4; + + proto_tree_add_uint(tree, hf_usb_iso_off, tvb, offset, 4, iso_off); + offset += 4; + + proto_tree_add_uint(tree, hf_usb_iso_len, tvb, offset, 4, iso_len); + offset += 4; + + /* When the ISO status is OK and there is ISO data and this ISO data is + * fully captured then show this data. + */ + if (!iso_status && iso_len && data_base + iso_off + iso_len <= tvb_length(tvb)) + proto_tree_add_item(tree, hf_usb_iso_data, tvb, data_base + iso_off, iso_len, ENC_NA); + + tvb_memcpy(tvb, (guint8 *)&iso_pad, offset, 4); + proto_tree_add_uint(tree, hf_usb_iso_pad, tvb, offset, 4, iso_pad); + offset += 4; + } + } + + } + break; + + default: + /* dont know */ + if (setup_flag == 0) { + proto_item *ti; + proto_tree *setup_tree; + int type_2; + + /* Dissect the setup header - it's applicable */ + + ti = proto_tree_add_protocol_format(parent, proto_usb, tvb, offset, 8, "URB setup"); + setup_tree = proto_item_add_subtree(ti, usb_setup_hdr); + + offset = dissect_usb_bmrequesttype(setup_tree, tvb, offset, &type_2); + proto_tree_add_item(setup_tree, hf_usb_request, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(setup_tree, hf_usb_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_index, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(setup_tree, hf_usb_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + } else { + /* Skip setup/isochronous header - it's not applicable */ + offset += 8; + } + + /* + * If this has a 64-byte header, process the extra 16 bytes of + * pseudo-header information. + */ + if (header_len_64_bytes) + offset = dissect_linux_usb_pseudo_header_ext(tvb, offset, pinfo, tree); + + break; + } + + next_tvb = tvb_new_subset_remaining(tvb, offset); + + if (!dissector_try_uint(device_to_dissector, (guint32) (bus_id << 8 | device_address), next_tvb, pinfo, parent)) { + emem_tree_key_t key[4]; + guint32 k_frame_number; + guint32 k_device_address; + guint32 k_bus_id; + device_protocol_data_t *device_protocol_data; + + k_frame_number = pinfo->fd->num; + k_device_address = device_address; + k_bus_id = bus_id; + + key[0].length = 1; + key[0].key = &k_device_address; + key[1].length = 1; + key[1].key = &k_bus_id; + key[2].length = 1; + key[2].key = &k_frame_number; + key[3].length = 0; + key[3].key = NULL; + + device_protocol_data = (device_protocol_data_t *)se_tree_lookup32_array_le(device_to_protocol_table, key); + if (device_protocol_data && device_protocol_data->bus_id == bus_id && + device_protocol_data->device_address == device_address && + dissector_try_uint(protocol_to_dissector, (guint32) device_protocol_data->protocol, next_tvb, pinfo, parent)) { + offset += tvb_length_remaining(tvb, offset); + } else { + device_product_data_t *device_product_data; + + device_product_data = (device_product_data_t *)se_tree_lookup32_array_le(device_to_product_table, key); + if (device_product_data && device_product_data->bus_id == bus_id && + device_product_data->device_address == device_address && + dissector_try_uint(product_to_dissector, (guint32) (device_product_data->vendor << 16 | device_product_data->product), next_tvb, pinfo, parent)) { + offset += tvb_length_remaining(tvb, offset); + } + } + } else { + offset += tvb_length_remaining(tvb, offset); + } + + if (tvb_reported_length_remaining(tvb, offset) != 0) { + /* There is leftover capture data to add (padding?) */ + proto_tree_add_item(parent, hf_usb_capdata, tvb, offset, -1, ENC_NA); + } + + pinfo->private_data = pd_save; +} + +static void +dissect_linux_usb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent) +{ + dissect_linux_usb_common(tvb, pinfo, parent, FALSE); +} + +static void +dissect_linux_usb_mmapped(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent) +{ + dissect_linux_usb_common(tvb, pinfo, parent, TRUE); +} + +void +proto_register_usb(void) +{ + module_t *usb_module; + static hf_register_info hf[] = { + + /* USB packet pseudoheader members */ + { &hf_usb_urb_id, + { "URB id", "usb.urb_id", + FT_UINT64, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_urb_type, + { "URB type", "usb.urb_type", + FT_UINT8, BASE_DEC, VALS(usb_urb_type_vals), 0x0, + NULL, HFILL }}, + + { &hf_usb_transfer_type, + { "URB transfer type", "usb.transfer_type", + FT_UINT8, BASE_HEX, VALS(usb_transfer_type_vals), 0x0, + NULL, HFILL }}, + + { &hf_usb_endpoint_number, + { "Endpoint", "usb.endpoint_number", + FT_UINT8, BASE_HEX, NULL, 0x0, + "USB endpoint number", HFILL }}, + + { &hf_usb_endpoint_direction, + { "Direction", "usb.endpoint_number.direction", + FT_UINT8, BASE_DEC, VALS(usb_endpoint_direction_vals), 0x80, + "USB endpoint direction", HFILL }}, + + { &hf_usb_endpoint_number_value, + { "Endpoint value", "usb.endpoint_number.endpoint", + FT_UINT8, BASE_DEC, NULL, 0x7F, + "USB endpoint value", HFILL }}, + + { &hf_usb_device_address, + { "Device", "usb.device_address", + FT_UINT8, BASE_DEC, NULL, 0x0, + "USB device address", HFILL }}, + + { &hf_usb_bus_id, + { "URB bus id", "usb.bus_id", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_setup_flag, + { "Device setup request", "usb.setup_flag", + FT_STRING, BASE_NONE, NULL, 0x0, + "USB device setup request is relevant (0) or not", HFILL }}, + + { &hf_usb_data_flag, + { "Data", "usb.data_flag", + FT_STRING, BASE_NONE, NULL, 0x0, + "USB data is present (0) or not", HFILL }}, + + { &hf_usb_urb_ts_sec, + { "URB sec", "usb.urb_ts_sec", + FT_UINT64, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_urb_ts_usec, + { "URB usec", "usb.urb_ts_usec", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_urb_status, + { "URB status", "usb.urb_status", + FT_INT32, BASE_DEC|BASE_EXT_STRING, &usb_urb_status_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_urb_len, + { "URB length [bytes]", "usb.urb_len", + FT_UINT32, BASE_DEC, NULL, 0x0, + "URB length in bytes", HFILL }}, + + { &hf_usb_urb_data_len, + { "Data length [bytes]", "usb.data_len", + FT_UINT32, BASE_DEC, NULL, 0x0, + "URB data length in bytes", HFILL }}, + + { &hf_usb_bmRequestType, + { "bmRequestType", "usb.bmRequestType", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_request, + { "bRequest", "usb.setup.bRequest", + FT_UINT8, BASE_DEC, VALS(setup_request_names_vals), 0x0, + NULL, HFILL }}, + + /* Same as hf_usb_request but no descriptive text */ + { &hf_usb_request_unknown_class, + { "bRequest", "usb.setup.bRequest", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_value, + { "wValue", "usb.setup.wValue", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_index, + { "wIndex", "usb.setup.wIndex", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_length, + { "wLength", "usb.setup.wLength", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wFeatureSelector, + { "wFeatureSelector", "usb.setup.wFeatureSelector", + FT_UINT16, BASE_DEC, VALS(usb_feature_selector_vals), 0x0, + NULL, HFILL }}, + + { &hf_usb_wInterface, + { "wInterface", "usb.setup.wInterface", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wStatus, + { "wStatus", "usb.setup.wStatus", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wFrameNumber, + { "wFrameNumber", "usb.setup.wFrameNumber", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + /* --------------------------------- */ + { &hf_usb_iso_error_count, /* host endian byte order */ + { "ISO error count", "usb.iso.error_count", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iso_numdesc, /* host endian byte order */ + { "Number of ISO descriptors", "usb.iso.numdesc", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + /* fields of struct mon_bin_isodesc from linux/drivers/usb/mon/mon_bin.c */ + { &hf_usb_iso_status, /* host endian byte order */ + { "Status", "usb.iso.iso_status", + FT_INT32, BASE_DEC|BASE_EXT_STRING, &usb_urb_status_vals_ext, 0x0, + "ISO descriptor status", HFILL }}, + + { &hf_usb_iso_off, /* host endian byte order */ + { "Offset [bytes]", "usb.iso.iso_off", + FT_UINT32, BASE_DEC, NULL, 0x0, + "ISO data offset in bytes starting from the end of the last ISO descriptor", HFILL }}, + + { &hf_usb_iso_len, /* host endian byte order */ + { "Length [bytes]", "usb.iso.iso_len", + FT_UINT32, BASE_DEC, NULL, 0x0, + "ISO data length in bytes", HFILL }}, + + { &hf_usb_iso_pad, /* host endian byte order */ + { "Padding", "usb.iso.pad", + FT_UINT32, BASE_HEX, NULL, 0x0, + "Padding field of ISO descriptor structure", HFILL }}, + + { &hf_usb_iso_data, + {"ISO Data", "usb.iso.data", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + /* --------------------------------- */ +#if 0 + { &hf_usb_data_len, + {"Application Data Length", "usb.data.length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, +#endif + + { &hf_usb_capdata, + {"Leftover Capture Data", "usb.capdata", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Padding added by the USB capture system", HFILL }}, + + { &hf_usb_bmRequestType_direction, + { "Direction", "usb.bmRequestType.direction", + FT_BOOLEAN, 8, TFS(&tfs_bmrequesttype_direction), USB_DIR_IN, + NULL, HFILL }}, + + { &hf_usb_bmRequestType_type, + { "Type", "usb.bmRequestType.type", + FT_UINT8, BASE_HEX, VALS(bmrequesttype_type_vals), USB_TYPE_MASK, + NULL, HFILL }}, + + { &hf_usb_bmRequestType_recipient, + { "Recipient", "usb.bmRequestType.recipient", + FT_UINT8, BASE_HEX, VALS(bmrequesttype_recipient_vals), 0x1f, + NULL, HFILL }}, + + { &hf_usb_bDescriptorType, + { "bDescriptorType", "usb.bDescriptorType", + FT_UINT8, BASE_DEC|BASE_EXT_STRING, &descriptor_type_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_descriptor_index, + { "Descriptor Index", "usb.DescriptorIndex", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_language_id, + { "Language Id", "usb.LanguageId", + FT_UINT16, BASE_HEX|BASE_EXT_STRING,&usb_langid_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_bLength, + { "bLength", "usb.bLength", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bcdUSB, + { "bcdUSB", "usb.bcdUSB", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bDeviceClass, + { "bDeviceClass", "usb.bDeviceClass", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, &usb_class_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_bDeviceSubClass, + { "bDeviceSubClass", "usb.bDeviceSubClass", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bDeviceProtocol, + { "bDeviceProtocol", "usb.bDeviceProtocol", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bMaxPacketSize0, + { "bMaxPacketSize0", "usb.bMaxPacketSize0", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_idVendor, + { "idVendor", "usb.idVendor", + FT_UINT16, BASE_HEX | BASE_EXT_STRING, &ext_usb_vendors_vals, 0x0, + NULL, HFILL }}, + + { &hf_usb_idProduct, + { "idProduct", "usb.idProduct", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bcdDevice, + { "bcdDevice", "usb.bcdDevice", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iManufacturer, + { "iManufacturer", "usb.iManufacturer", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iProduct, + { "iProduct", "usb.iProduct", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iSerialNumber, + { "iSerialNumber", "usb.iSerialNumber", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bNumConfigurations, + { "bNumConfigurations", "usb.bNumConfigurations", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wLANGID, + { "wLANGID", "usb.wLANGID", + FT_UINT16, BASE_HEX|BASE_EXT_STRING,&usb_langid_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_bString, + { "bString", "usb.bString", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bInterfaceNumber, + { "bInterfaceNumber", "usb.bInterfaceNumber", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bAlternateSetting, + { "bAlternateSetting", "usb.bAlternateSetting", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bNumEndpoints, + { "bNumEndpoints", "usb.bNumEndpoints", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bInterfaceClass, + { "bInterfaceClass", "usb.bInterfaceClass", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, &usb_class_vals_ext, 0x0, + NULL, HFILL }}, + + { &hf_usb_bInterfaceSubClass, + { "bInterfaceSubClass", "usb.bInterfaceSubClass", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bInterfaceProtocol, + { "bInterfaceProtocol", "usb.bInterfaceProtocol", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iInterface, + { "iInterface", "usb.iInterface", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bEndpointAddress, + { "bEndpointAddress", "usb.bEndpointAddress", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_configuration_bmAttributes, + { "Configuration bmAttributes", "usb.configuration.bmAttributes", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bmAttributes, + { "bmAttributes", "usb.bmAttributes", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bEndpointAttributeTransfer, + { "Transfertype", "usb.bmAttributes.transfer", + FT_UINT8, BASE_HEX, VALS(usb_bmAttributes_transfer_vals), 0x03, + NULL, HFILL }}, + + { &hf_usb_bEndpointAttributeSynchonisation, + { "Synchronisationtype", "usb.bmAttributes.sync", + FT_UINT8, BASE_HEX, VALS(usb_bmAttributes_sync_vals), 0x0c, + NULL, HFILL }}, + + { &hf_usb_bEndpointAttributeBehaviour, + { "Behaviourtype", "usb.bmAttributes.behaviour", + FT_UINT8, BASE_HEX, VALS(usb_bmAttributes_behaviour_vals), 0x30, + NULL, HFILL }}, + + { &hf_usb_wMaxPacketSize, + { "wMaxPacketSize", "usb.wMaxPacketSize", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wMaxPacketSize_size, + { "Maximum Packet Size", "usb.wMaxPacketSize.size", + FT_UINT16, BASE_DEC, NULL, 0x3FF, + NULL, HFILL }}, + + { &hf_usb_wMaxPacketSize_slots, + { "Transactions per microframe", "usb.wMaxPacketSize.slots", + FT_UINT16, BASE_DEC, VALS(usb_wMaxPacketSize_slots_vals), (3<<11), + NULL, HFILL }}, + + { &hf_usb_bInterval, + { "bInterval", "usb.bInterval", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_wTotalLength, + { "wTotalLength", "usb.wTotalLength", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bNumInterfaces, + { "bNumInterfaces", "usb.bNumInterfaces", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bConfigurationValue, + { "bConfigurationValue", "usb.bConfigurationValue", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_iConfiguration, + { "iConfiguration", "usb.iConfiguration", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_bMaxPower, + { "bMaxPower", "usb.bMaxPower", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_usb_configuration_legacy10buspowered, + { "Must be 1", "usb.configuration.legacy10buspowered", + FT_BOOLEAN, 8, TFS(&tfs_mustbeone), 0x80, + "Legacy USB 1.0 bus powered", HFILL }}, + + { &hf_usb_configuration_selfpowered, + { "Self-Powered", "usb.configuration.selfpowered", + FT_BOOLEAN, 8, TFS(&tfs_selfpowered), 0x40, + NULL, HFILL }}, + + { &hf_usb_configuration_remotewakeup, + { "Remote Wakeup", "usb.configuration.remotewakeup", + FT_BOOLEAN, 8, TFS(&tfs_remotewakeup), 0x20, + NULL, HFILL }}, + + { &hf_usb_bEndpointAddress_number, + { "Endpoint Number", "usb.bEndpointAddress.number", + FT_UINT8, BASE_HEX, NULL, 0x0f, + NULL, HFILL }}, + + { &hf_usb_bEndpointAddress_direction, + { "Direction", "usb.bEndpointAddress.direction", + FT_BOOLEAN, 8, TFS(&tfs_endpoint_direction), 0x80, + NULL, HFILL }}, + + { &hf_usb_request_in, + { "Request in", "usb.request_in", + FT_FRAMENUM, BASE_NONE, NULL, 0, + "The request to this packet is in this packet", HFILL }}, + + { &hf_usb_time, + { "Time from request", "usb.time", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0, + "Time between Request and Response for USB cmds", HFILL }}, + + { &hf_usb_response_in, + { "Response in", "usb.response_in", + FT_FRAMENUM, BASE_NONE, NULL, 0, + "The response to this packet is in this packet", HFILL }}, + + { &hf_usb_bFirstInterface, + { "bFirstInterface", "usb.bFirstInterface", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_usb_bInterfaceCount, + { "bInterfaceCount", + "usb.bInterfaceCount", FT_UINT8, BASE_DEC, + NULL, 0x0, NULL, HFILL }}, + + { &hf_usb_bFunctionClass, + { "bFunctionClass", "usb.bFunctionClass", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, &usb_class_vals_ext, 0x0, NULL, HFILL }}, + + { &hf_usb_bFunctionSubClass, + { "bFunctionSubClass", + "usb.bFunctionSubClass", FT_UINT8, BASE_HEX, + NULL, 0x0, NULL, HFILL }}, + + { &hf_usb_bFunctionProtocol, + { "bFunctionProtocol", "usb.bFunctionProtocol", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + + { &hf_usb_iFunction, + { "iFunction", + "usb.iFunction", FT_UINT8, BASE_DEC, + NULL, 0x0, NULL, HFILL }}, + }; + + static gint *usb_subtrees[] = { + &usb_hdr, + &usb_setup_hdr, + &usb_isodesc, + &ett_usb_endpoint, + &ett_usb_setup_bmrequesttype, + &ett_descriptor_device, + &ett_configuration_bmAttributes, + &ett_configuration_bEndpointAddress, + &ett_endpoint_bmAttributes, + &ett_endpoint_wMaxPacketSize + }; + + device_to_product_table = se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "usb device_address, bus_id and frame number to vendor_product"); + device_to_protocol_table = se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "usb device_address, bus_id and frame number to class_subclass_protocol"); + device_to_dissector = register_dissector_table("usb.device", "USB device", FT_UINT32, BASE_HEX); + protocol_to_dissector = register_dissector_table("usb.protocol", "USB protocol", FT_UINT32, BASE_HEX); + product_to_dissector = register_dissector_table("usb.product", "USB product", FT_UINT32, BASE_HEX); + + proto_usb = proto_register_protocol("USB", "USB", "usb"); + proto_register_field_array(proto_usb, hf, array_length(hf)); + proto_register_subtree_array(usb_subtrees, array_length(usb_subtrees)); + register_dissector("usb", dissect_linux_usb, proto_usb); + + usb_bulk_dissector_table = register_dissector_table("usb.bulk", + "USB bulk endpoint", FT_UINT8, BASE_DEC); + register_heur_dissector_list("usb.bulk", &heur_bulk_subdissector_list); + usb_control_dissector_table = register_dissector_table("usb.control", + "USB control endpoint", FT_UINT8, BASE_DEC); + register_heur_dissector_list("usb.control", &heur_control_subdissector_list); + usb_interrupt_dissector_table = register_dissector_table("usb.interrupt", + "USB interrupt endpoint", FT_UINT8, BASE_DEC); + register_heur_dissector_list("usb.interrupt", &heur_interrupt_subdissector_list); + usb_descriptor_dissector_table = register_dissector_table("usb.descriptor", + "USB descriptor", FT_UINT8, BASE_DEC); + + usb_module = prefs_register_protocol(proto_usb, NULL); + prefs_register_bool_preference(usb_module, "try_heuristics", + "Try heuristic sub-dissectors", + "Try to decode a packet using a heuristic sub-dissector before " + "attempting to dissect the packet using the \"usb.bulk\", \"usb.interrupt\" or " + "\"usb.control\" dissector tables.", &try_heuristics); + + usb_tap = register_tap("usb"); +} + +void +proto_reg_handoff_usb(void) +{ + dissector_handle_t linux_usb_handle; + dissector_handle_t linux_usb_mmapped_handle; + + linux_usb_handle = create_dissector_handle(dissect_linux_usb, proto_usb); + linux_usb_mmapped_handle = create_dissector_handle(dissect_linux_usb_mmapped, + proto_usb); + + dissector_add_uint("wtap_encap", WTAP_ENCAP_USB_LINUX, linux_usb_handle); + dissector_add_uint("wtap_encap", WTAP_ENCAP_USB_LINUX_MMAPPED, linux_usb_mmapped_handle); +} diff --git a/src/main/resource/testFiles/wireshark/revFiles/1a76dd_1d1a48_epan#dissectors#packet-ua3g.c b/src/main/resource/testFiles/wireshark/revFiles/1a76dd_1d1a48_epan#dissectors#packet-ua3g.c new file mode 100644 index 0000000..dfb0c4d --- /dev/null +++ b/src/main/resource/testFiles/wireshark/revFiles/1a76dd_1d1a48_epan#dissectors#packet-ua3g.c @@ -0,0 +1,4547 @@ +/* packet-ua3g.c + * Routines for UA/UDP (Universal Alcatel over UDP) packet dissection. + * Copyright 2012, Alcatel-Lucent Enterprise + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include + +#include + +#include "epan/packet.h" +#include "epan/emem.h" +#include "packet-uaudp.h" + + +/*----------------------------------------------------------------------------- + Globals + ---------------------------------------------------------------------------*/ + +#if 0 +static dissector_table_t ua3g_opcode_dissector_table; +#endif + + +static int proto_ua3g = -1; +static gint ett_ua3g = -1; +static gint ett_ua3g_body = -1; +static gint ett_ua3g_param = -1; +static gint ett_ua3g_param_sub = -1; +static gint ett_ua3g_option = -1; + +static int hf_ua3g_length = -1; +static int hf_ua3g_opcode_sys = -1; +static int hf_ua3g_opcode_term = -1; +static int hf_ua3g_opcode_production_test = -1; +static int hf_ua3g_opcode_subservice_reset = -1; +static int hf_ua3g_opcode_are_you_there = -1; +static int hf_ua3g_opcode_set_speaker_vol = -1; +static int hf_ua3g_opcode_trace_on = -1; +static int hf_ua3g_ip = -1; +static int hf_ua3g_ip_cs = -1; +static int hf_ua3g_command_led = -1; +static int hf_ua3g_command_lcd_line = -1; +static int hf_ua3g_main_voice_mode = -1; +static int hf_ua3g_command_set_clck = -1; +static int hf_ua3g_external_ringing_command= -1; +static int hf_ua3g_lcd_cursor = -1; +static int hf_ua3g_command_beep = -1; +static int hf_ua3g_command_sidetone = -1; +static int hf_ua3g_command_mute = -1; +static int hf_ua3g_command_feedback = -1; +static int hf_ua3g_command_audio_config = -1; +static int hf_ua3g_command_key_release = -1; +static int hf_ua3g_command_amplified_handset = -1; +static int hf_ua3g_command_loudspeaker = -1; +static int hf_ua3g_command_announce = -1; +static int hf_ua3g_command_ring = -1; +static int hf_ua3g_command_ua_dwl_protocol = -1; +static int hf_ua3g_command_unsolicited_msg = -1; +static int hf_ua3g_ip_device_routing_stop_rtp_parameter = -1; +static int hf_ua3g_ip_device_routing_stop_rtp_parameter_length = -1; +static int hf_ua3g_ip_device_routing_stop_rtp_parameter_value_num = -1; +static int hf_ua3g_ip_device_routing_stop_rtp_parameter_value_bytes = -1; +/* Generated from convert_proto_tree_add_text.pl */ +static int hf_ua3g_ip_device_routing_start_tone_direction = -1; +static int hf_ua3g_ip_device_routing_start_tone_num_entries = -1; +static int hf_ua3g_ip_device_routing_def_tones_num_entries = -1; +static int hf_ua3g_cs_ip_device_routing_cmd00_characteristic_number = -1; +static int hf_ua3g_subdevice_msg_subdev_type = -1; +static int hf_ua3g_unsolicited_msg_next_byte_of_bad_segment = -1; +static int hf_ua3g_ip_device_routing_start_tone_identification = -1; +static int hf_ua3g_ip_device_routing_def_tones_level_2 = -1; +static int hf_ua3g_r_w_peripheral_content = -1; +static int hf_ua3g_subdevice_metastate_subchannel_address = -1; +static int hf_ua3g_subdevice_parameter_bytes = -1; +static int hf_ua3g_subdevice_msg_parameter_bytes = -1; +static int hf_ua3g_set_clck_timer_pos_call_timer_column_number = -1; +static int hf_ua3g_unsolicited_msg_segment_failure_s = -1; +static int hf_ua3g_ip_device_routing_reset_parameter = -1; +static int hf_ua3g_ip_device_routing_get_param_req_parameter = -1; +static int hf_ua3g_set_lcd_contrast_driver_number = -1; +static int hf_ua3g_dwl_special_char_character_number = -1; +static int hf_ua3g_cs_ip_device_routing_cmd00_vta_type = -1; +static int hf_ua3g_ua_dwl_protocol_cause = -1; +static int hf_ua3g_audio_padded_path_emission_padded_level = -1; +static int hf_ua3g_set_clck_timer_pos_clock_column_number = -1; +static int hf_ua3g_segment_msg_num_remaining = -1; +static int hf_ua3g_ip_device_routing_digit_value = -1; +static int hf_ua3g_super_msg_data = -1; +static int hf_ua3g_unsolicited_msg_hardware_version = -1; +static int hf_ua3g_voice_channel_announce = -1; +static int hf_ua3g_ring_silent = -1; +static int hf_ua3g_audio_config_handsfree_return = -1; +static int hf_ua3g_dwl_dtmf_clck_format_inter_digit_pause_time = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_length = -1; +static int hf_ua3g_unsolicited_msg_opcode_bad_segment = -1; +static int hf_ua3g_unsolicited_msg_firmware_version_loader = -1; +static int hf_ua3g_debug_in_line = -1; +static int hf_ua3g_voice_channel_b_microphones = -1; +static int hf_ua3g_beep_beep_number = -1; +static int hf_ua3g_main_voice_mode_tune = -1; +static int hf_ua3g_super_msg_length = -1; +static int hf_ua3g_ip_device_routing_redirect_parameter = -1; +static int hf_ua3g_unsolicited_msg_next_byte_of_bad_command = -1; +static int hf_ua3g_unsolicited_msg_self_test_result = -1; +static int hf_ua3g_beep_on_off = -1; +static int hf_ua3g_ua_dwl_protocol_binary_length = -1; +static int hf_ua3g_ring_speaker_level = -1; +static int hf_ua3g_voice_channel_channel_mode = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_length = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter = -1; +static int hf_ua3g_subdevice_metastate_new_metastate = -1; +static int hf_ua3g_unsolicited_msg_other_information_2 = -1; +static int hf_ua3g_set_lcd_contrast_contrast_value = -1; +static int hf_ua3g_unsolicited_msg_vta_type = -1; +static int hf_ua3g_ua_dwl_protocol_packet_number = -1; +static int hf_ua3g_unsolicited_msg_segment_failure_l = -1; +static int hf_ua3g_voice_channel_b_ear_piece = -1; +static int hf_ua3g_subdevice_msg_subdev_address = -1; +static int hf_ua3g_ring_progressive = -1; +static int hf_ua3g_ua_dwl_protocol_item_version = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_length = -1; +static int hf_ua3g_dwl_dtmf_clck_format_minimum_on_time = -1; +static int hf_ua3g_ring_melody = -1; +static int hf_ua3g_ua_dwl_protocol_item_identifier = -1; +static int hf_ua3g_main_voice_mode_speaker_volume = -1; +static int hf_ua3g_ip_device_routing_listen_rtp_parameter_length = -1; +static int hf_ua3g_ringing_cadence_length = -1; +static int hf_ua3g_software_reset = -1; +static int hf_ua3g_feedback_level = -1; +static int hf_ua3g_ip_phone_warmstart = -1; +static int hf_ua3g_subdevice_opcode = -1; +static int hf_ua3g_unsolicited_msg_device_event = -1; +static int hf_ua3g_segment_message_data = -1; +static int hf_ua3g_main_voice_mode_sending_level = -1; +static int hf_ua3g_subdevice_msg_subdevice_opcode = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter = -1; +static int hf_ua3g_audio_padded_path_reception_padded_level = -1; +static int hf_ua3g_ua_dwl_protocol_force_mode = -1; +static int hf_ua3g_lcd_line_cmd_starting_column = -1; +static int hf_ua3g_subdevice_address = -1; +static int hf_ua3g_ip_device_routing_pause_restart_rtp_parameter = -1; +static int hf_ua3g_audio_config_ignored = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options = -1; +static int hf_ua3g_main_voice_mode_cadence = -1; +static int hf_ua3g_segment_msg_length = -1; +static int hf_ua3g_ua_dwl_protocol_acknowledge = -1; +static int hf_ua3g_command_led_number = -1; +static int hf_ua3g_set_clck_timer_pos_call_timer_line_number = -1; +static int hf_ua3g_unsolicited_msg_segment_failure_t = -1; +static int hf_ua3g_ip_device_routing_start_tone_duration = -1; +static int hf_ua3g_unsolicited_msg_other_information_1 = -1; +static int hf_ua3g_unsolicited_msg_firmware_datas_patch_version = -1; +static int hf_ua3g_ring_beep_number = -1; +static int hf_ua3g_feedback_duration = -1; +static int hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_length = -1; +static int hf_ua3g_audio_config_law = -1; +static int hf_ua3g_ua_dwl_protocol_checksum = -1; +static int hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_value = -1; +static int hf_ua3g_audio_config_handsfree_handsfree = -1; +static int hf_ua3g_ringing_cadence_cadence = -1; +static int hf_ua3g_lcd_cursor_line_number = -1; +static int hf_ua3g_ip_device_routing_def_tones_level_1 = -1; +static int hf_ua3g_unsolicited_msg_opcode_of_bad_command = -1; +static int hf_ua3g_ua_dwl_protocol_download_ack_status = -1; +static int hf_ua3g_voice_channel_main_voice = -1; +static int hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_length = -1; +static int hf_ua3g_icon_cmd_segment = -1; +static int hf_ua3g_cs_ip_device_routing_cmd01_incident_0 = -1; +static int hf_ua3g_beep_destination = -1; +static int hf_ua3g_ip_device_routing_def_tones_frequency_1 = -1; +static int hf_ua3g_unsolicited_msg_datas_version = -1; +static int hf_ua3g_dwl_dtmf_clck_format_dtmf_country_adaptation = -1; +static int hf_ua3g_ringing_cadence_on_off = -1; +static int hf_ua3g_audio_config_volume_level = -1; +static int hf_ua3g_voice_channel_b_general = -1; +static int hf_ua3g_beep_terminator = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter = -1; +static int hf_ua3g_unsolicited_msg_firmware_version_bootloader = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter = -1; +static int hf_ua3g_ip_device_routing_start_rtp_direction = -1; +static int hf_ua3g_set_clck_timer_pos_clock_line_number = -1; +static int hf_ua3g_voice_channel_b_loud_speaker = -1; +static int hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter = -1; +static int hf_ua3g_on_off_level_level_on_loudspeaker = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_length = -1; +static int hf_ua3g_main_voice_mode_microphone_volume = -1; +static int hf_ua3g_sidetone_level = -1; +static int hf_ua3g_beep_number_of_notes = -1; +static int hf_ua3g_unsolicited_msg_segment_failure_num = -1; +static int hf_ua3g_dwl_special_char_byte = -1; +static int hf_ua3g_ring_cadence = -1; +static int hf_ua3g_unsolicited_msg_device_type = -1; +static int hf_ua3g_voice_channel_codec = -1; +static int hf_ua3g_ip_device_routing_redirect_parameter_length = -1; +static int hf_ua3g_ip_device_routing_listen_rtp_parameter = -1; +static int hf_ua3g_beep_cadence = -1; +static int hf_ua3g_voice_channel_voice_channel = -1; +static int hf_ua3g_unsolicited_msg_other_information = -1; +static int hf_ua3g_ip_device_routing_def_tones_frequency_2 = -1; +static int hf_ua3g_digit_dialed_digit_value = -1; +static int hf_ua3g_unsolicited_msg_subdevice_address = -1; +static int hf_ua3g_ua_dwl_protocol_packet_download_end_ack_ok_status = -1; +static int hf_ua3g_r_w_peripheral_address = -1; +static int hf_ua3g_icon_cmd_icon_number = -1; +static int hf_ua3g_dwl_dtmf_clck_format_clock_time_format = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_length = -1; +static int hf_ua3g_i_m_here_id_code = -1; +static int hf_ua3g_ua_dwl_protocol_item_version_nc = -1; +static int hf_ua3g_unsolicited_msg_firmware_version = -1; +static int hf_ua3g_segment_msg_segment = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_bootloader = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_data = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_customization = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_localization = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_code = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_noe_update_sip = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_value = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_bad_sec_mode = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_cust_name = -1; +static int hf_ua3g_ip_device_routing_reset_parameter_l10n_name = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_ip = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_compressor = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_value = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_enabler = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_send_qos = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_dtmf_sending = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_rfc2198 = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_srtp_encryption = -1; +static int hf_ua3g_ip_device_routing_start_rtp_parameter_uint = -1; +static int hf_ua3g_ip_device_routing_redirect_parameter_ip = -1; +static int hf_ua3g_ip_device_routing_redirect_parameter_uint = -1; +static int hf_ua3g_ip_device_routing_redirect_parameter_value = -1; +static int hf_ua3g_ip_device_routing_listen_rtp_parameter_ip = -1; +static int hf_ua3g_ip_device_routing_listen_rtp_parameter_port = -1; +static int hf_ua3g_ip_device_routing_listen_rtp_parameter_value = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_compressor = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_err_string = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_tftp_backup_ip = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_set_pc_port_status = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_record_rtp_auth = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_security_flag_filter = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_uint = -1; +static int hf_ua3g_ip_device_routing_set_param_req_parameter_value = -1; +static int hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_uint = -1; +static int hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_remote_ip = -1; +static int hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_uint = -1; +static int hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_value = -1; +static int hf_ua3g_main_voice_mode_handset_level = -1; +static int hf_ua3g_main_voice_mode_headset_level = -1; +static int hf_ua3g_main_voice_mode_handsfree_level = -1; +static int hf_ua3g_audio_config_dpi_chan_ua_tx1 = -1; +static int hf_ua3g_audio_config_dpi_chan_ua_tx2 = -1; +static int hf_ua3g_audio_config_dpi_chan_gci_tx1 = -1; +static int hf_ua3g_audio_config_dpi_chan_gci_tx2 = -1; +static int hf_ua3g_audio_config_dpi_chan_cod_tx = -1; +static int hf_ua3g_audio_config_audio_circuit_dth = -1; +static int hf_ua3g_audio_config_audio_circuit_dtr = -1; +static int hf_ua3g_audio_config_audio_circuit_dtf = -1; +static int hf_ua3g_audio_config_audio_circuit_str = -1; +static int hf_ua3g_audio_config_audio_circuit_ahp1 = -1; +static int hf_ua3g_audio_config_audio_circuit_ahp2 = -1; +static int hf_ua3g_audio_config_audio_circuit_ath = -1; +static int hf_ua3g_audio_config_audio_circuit_atr = -1; +static int hf_ua3g_audio_config_audio_circuit_atf = -1; +static int hf_ua3g_audio_config_audio_circuit_alm = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_group_listen = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_attenuation = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_stay_in_send = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mtx = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mrc = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_trans_threshold = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_trans_threshold = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_recv_threshold = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_recv_threshold = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_med_recv_threshold = -1; +static int hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_high_recv_threshold = -1; +static int hf_ua3g_ua_dwl_protocol_files_inc_boot_binary = -1; +static int hf_ua3g_ua_dwl_protocol_files_inc_loader_binary = -1; +static int hf_ua3g_ua_dwl_protocol_files_inc_appli_binary = -1; +static int hf_ua3g_ua_dwl_protocol_files_inc_data_binary = -1; +static int hf_ua3g_ua_dwl_protocol_model_selection_a = -1; +static int hf_ua3g_ua_dwl_protocol_model_selection_b = -1; +static int hf_ua3g_ua_dwl_protocol_model_selection_c = -1; +static int hf_ua3g_ua_dwl_protocol_model_selection_country_ver = -1; +static int hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe1 = -1; +static int hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe2 = -1; +static int hf_ua3g_ua_dwl_protocol_memory_sizes_flash = -1; +static int hf_ua3g_ua_dwl_protocol_memory_sizes_ext_ram = -1; +static int hf_ua3g_unsolicited_msg_char_num_vta_subtype = -1; +static int hf_ua3g_unsolicited_msg_char_num_generation = -1; +static int hf_ua3g_unsolicited_msg_char_num_design = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_vta_type = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_design = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_subtype = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_hard_config_chip = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_hard_config_flash = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_config_ram = -1; +static int hf_ua3g_unsolicited_msg_hardware_config_hard_config_ip = -1; +static int hf_ua3g_unsolicited_msg_hook_status = -1; +static int hf_ua3g_special_key_shift = -1; +static int hf_ua3g_special_key_ctrl = -1; +static int hf_ua3g_special_key_alt = -1; +static int hf_ua3g_special_key_cmd = -1; +static int hf_ua3g_special_key_shift_prime = -1; +static int hf_ua3g_special_key_ctrl_prime = -1; +static int hf_ua3g_special_key_alt_prime = -1; +static int hf_ua3g_special_key_cmd_prime = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_call_timer = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_blink = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_call_timer_control = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_call_timer_display = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_time_of_day_display = -1; +static int hf_ua3g_lcd_line_cmd_lcd_options_suspend_display_refresh = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_firmware_version = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_ip = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_uint = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_bytes = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_mac_address = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_uint = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_value = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_speed = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_duplex = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_speed = -1; +static int hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_duplex = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip1 = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip2 = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_ip = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_string = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_default_codec = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_vad = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_ece = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_voice_mode = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_delay_distribution = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_bfi = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021Q_used = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021P_priority = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_vlan_id = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_diffserv = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution_200ms = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_rtp_lost = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_uint = -1; +static int hf_ua3g_cs_ip_device_routing_cmd03_parameter_jitter_depth_distribution = -1; + +/* Definition of opcodes */ +/* System To Terminal */ +#define SC_NOP 0x00 +#define SC_PRODUCTION_TEST 0x01 /* IP Phone */ +#define SC_SUBDEVICE_ESCAPE 0x02 /* IP Phone */ +#define SC_SOFT_RESET 0x03 +#define SC_IP_PHONE_WARMSTART 0x04 /* IP Phone */ +#define SC_HE_ROUTING 0x05 /* IP Phone - NOT EXPECTED */ +#define SC_SUBDEVICE_RESET 0x06 +#define SC_LOOPBACK_ON 0x07 /* IP Phone & UA NOE */ +#define SC_LOOPBACK_OFF 0x08 /* IP Phone & UA NOE */ +#define SC_VIDEO_ROUTING 0x09 /* IP Phone - NOT EXPECTED */ +#define SC_SUPER_MSG 0x0B +#define SC_SEGMENT_MSG 0x0C +#define SC_REMOTE_UA_ROUTING 0x0D /* IP Phone - NOT EXPECTED */ +#define SC_VERY_REMOTE_UA_ROUTING 0x0E /* IP Phone - NOT EXPECTED */ +#define SC_OSI_ROUTING 0x0F /* IP Phone - NOT EXPECTED */ +#define SC_ABC_A_ROUTING 0x11 /* IP Phone - NOT EXPECTED */ +#define SC_IBS_ROUTING 0x12 /* IP Phone - NOT EXPECTED */ +#define SC_IP_DEVICE_ROUTING 0x13 +#define SC_M_REFLEX_HUB_ROUTING 0x14 /* IP Phone - NOT EXPECTED */ +#if 0 +#define SC_NOE_CS_ROUTING 0x15 /* Decoded by packet-noe.c */ +#define SC_NOE_PS_ROUTING 0x16 /* Decoded by packet-noe.c */ +#endif +#define SC_SUPER_MSG_2 0x17 +#define SC_DEBUG_IN_LINE 0x18 +#define SC_LED_COMMAND 0x21 /* IP Phone */ +#define SC_START_BUZZER 0x22 /* VTA */ +#define SC_STOP_BUZZER 0x23 /* VTA */ +#define SC_ENABLE_DTMF 0x24 /* Only IP NOE */ +#define SC_DISABLE_DTMF 0x25 /* Only IP NOE */ +#define SC_CLEAR_LCD_DISP 0x26 /* IP Phone */ +#define SC_LCD_LINE_1_CMD 0x27 /* IP Phone */ +#define SC_LCD_LINE_2_CMD 0x28 /* IP Phone */ +#define SC_MAIN_VOICE_MODE 0x29 +#define SC_VERSION_INQUIRY 0x2A +#define SC_ARE_YOU_THERE 0x2B /* IP Phone & UA NOE */ +#define SC_SUBDEVICE_METASTATE 0x2C +#define SC_VTA_STATUS_INQUIRY 0x2D /* IP Phone */ +#define SC_SUBDEVICE_STATE 0x2E +#define SC_DWL_DTMF_CLCK_FORMAT 0x30 /* IP Phone */ +#define SC_SET_CLCK 0x31 /* IP Phone */ +#define SC_VOICE_CHANNEL 0x32 /* IP Phone & UA NOE */ +#define SC_EXTERNAL_RINGING 0x33 +#define SC_LCD_CURSOR 0x35 /* IP Phone */ +#define SC_DWL_SPECIAL_CHAR 0x36 /* IP Phone */ +#define SC_SET_CLCK_TIMER_POS 0x38 /* IP Phone */ +#define SC_SET_LCD_CONTRAST 0x39 /* IP Phone */ +#define SC_AUDIO_IDLE 0x3A +#define SC_SET_SPEAKER_VOL 0x3B /* IP Phone */ +#define SC_BEEP 0x3C +#define SC_SIDETONE 0x3D +#define SC_RINGING_CADENCE 0x3E +#define SC_MUTE 0x3F +#define SC_FEEDBACK 0x40 +#define SC_KEY_RELEASE 0x41 /* IP Phone */ +#define SC_TRACE_ON 0x42 /* IP Phone - NOT EXPECTED */ +#define SC_TRACE_OFF 0x43 /* IP Phone - NOT EXPECTED */ +#define SC_READ_PERIPHERAL 0x44 /* IP Phone - NOT EXPECTED */ +#define SC_WRITE_PERIPHERAL 0x45 /* IP Phone - NOT EXPECTED */ +#define SC_ALL_ICONS_OFF 0x46 /* IP Phone */ +#define SC_ICON_CMD 0x47 /* IP Phone */ +#define SC_AMPLIFIED_HANDSET 0x48 /* IP Phone */ +#define SC_AUDIO_CONFIG 0x49 +#define SC_AUDIO_PADDED_PATH 0x4A /* IP Phone */ +#define SC_RELEASE_RADIO_LINK 0x4B /* IP Phone - NOT EXPECTED */ +#define SC_DECT_HANDOVER 0x4C /* IP Phone - NOT EXPECTED */ +#define SC_LOUDSPEAKER 0x4D +#define SC_ANNOUNCE 0x4E +#define SC_RING 0x4F +#define SC_UA_DWL_PROTOCOL 0x50 /* Only UA NOE */ + +/* Terminal To System */ +#define CS_NOP_ACK 0x00 +#define CS_HANDSET_OFFHOOK 0x01 /* IP Phone */ +#define CS_HANDSET_ONHOOK 0x02 /* IP Phone */ +#define CS_DIGIT_DIALED 0x03 /* IP Phone */ +#define CS_SUBDEVICE_MSG 0x04 +#define CS_HE_ROUTING 0x05 /* IP Phone - NOT EXPECTED */ +#define CS_LOOPBACK_ON 0x06 /* IP Phone & UA NOE */ +#define CS_LOOPBACK_OFF 0x07 /* IP Phone & UA NOE */ +#define CS_VIDEO_ROUTING 0x09 /* IP Phone - NOT EXPECTED */ +#define CS_WARMSTART_ACK 0x0A /* IP Phone */ +#define CS_SUPER_MSG 0x0B /* IP Phone - NOT EXPECTED */ +#define CS_SEGMENT_MSG 0x0C +#define CS_REMOTE_UA_ROUTING 0x0D /* IP Phone - NOT EXPECTED */ +#define CS_VERY_REMOTE_UA_R 0x0E /* IP Phone - NOT EXPECTED */ +#define CS_OSI_ROUTING 0x0F /* IP Phone - NOT EXPECTED */ +#define CS_ABC_A_ROUTING 0x11 /* IP Phone - NOT EXPECTED */ +#define CS_IBS_ROUTING 0x12 /* IP Phone - NOT EXPECTED */ +#define CS_IP_DEVICE_ROUTING 0x13 +#if 0 +#define CS_NOE_CS_ROUTING 0x15 /* Decoded by packet-noe.c */ +#define CS_NOE_PS_ROUTING 0x16 /* Decoded by packet-noe.c */ +#endif +#define CS_SUPER_MSG_2 0x17 +#define CS_DEBUG_IN_LINE 0x18 +#define CS_NON_DIGIT_KEY_PUSHED 0x20 /* IP Phone */ +#define CS_VERSION_RESPONSE 0x21 +#define CS_I_M_HERE 0x22 +#define CS_RSP_STATUS_INQUIRY 0x23 /* IP Phone */ +#define CS_SUBDEVICE_STATE 0x24 +#define CS_DIGIT_KEY_RELEASED 0x26 /* IP Phone */ +#define CS_TRACE_ON_ACK 0x27 /* IP Phone */ +#define CS_TRACE_OFF_ACK 0x28 /* IP Phone */ +#define CS_SPECIAL_KEY_STATUS 0x29 /* IP Phone */ +#define CS_KEY_RELEASED 0x2A /* IP Phone */ +#define CS_PERIPHERAL_CONTENT 0x2B /* IP Phone */ +#define CS_TM_KEY_PUSHED 0x2D /* IP Phone */ +#define CS_UA_DWL_PROTOCOL 0x50 /* Only UA NOE */ +#define CS_UNSOLICITED_MSG 0x9F + +/* System To Terminal Opcodes */ +static const value_string opcodes_vals_sys[] = +{ + {SC_NOP , "NOP"}, + {SC_PRODUCTION_TEST , "Production Test"}, /* IP Phone */ + {SC_SUBDEVICE_ESCAPE , "Subdevice Escape To Subdevice"}, /* IP Phone */ + {SC_SOFT_RESET , "Software Reset"}, + {SC_IP_PHONE_WARMSTART , "IP-Phone Warmstart"}, /* IP Phone */ + {SC_HE_ROUTING , "HE Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_SUBDEVICE_RESET , "Subdevice Reset"}, + {SC_LOOPBACK_ON , "Loopback On"}, + {SC_LOOPBACK_OFF , "Loopback Off"}, + {SC_VIDEO_ROUTING , "Video Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_SUPER_MSG , "Super Message"}, + {SC_SEGMENT_MSG , "Segment Message"}, + {SC_REMOTE_UA_ROUTING , "Remote UA Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_VERY_REMOTE_UA_ROUTING , "Very Remote UA Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_OSI_ROUTING , "OSI Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_ABC_A_ROUTING , "ABC-A Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_IBS_ROUTING , "IBS Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_IP_DEVICE_ROUTING , "IP Device Routing"}, + {SC_M_REFLEX_HUB_ROUTING , "Mutli-Reflex Hub Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_SUPER_MSG_2 , "Super Message 2"}, + {SC_DEBUG_IN_LINE , "Debug In Line"}, + {SC_LED_COMMAND , "Led Command"}, /* IP Phone */ + {SC_START_BUZZER , "Start Buzzer"}, /* VTA */ + {SC_STOP_BUZZER , "Stop Buzzer"}, /* VTA */ + {SC_ENABLE_DTMF , "Enable DTMF"}, + {SC_DISABLE_DTMF , "Disable DTMF"}, + {SC_CLEAR_LCD_DISP , "Clear LCD Display"}, /* IP Phone */ + {SC_LCD_LINE_1_CMD , "LCD Line 1 Commands"}, /* IP Phone */ + {SC_LCD_LINE_2_CMD , "LCD Line 2 Commands"}, /* IP Phone */ + {SC_MAIN_VOICE_MODE , "Main Voice Mode"}, + {SC_VERSION_INQUIRY , "Version Inquiry"}, + {SC_ARE_YOU_THERE , "Are You There?"}, + {SC_SUBDEVICE_METASTATE , "Subdevice Metastate"}, + {SC_VTA_STATUS_INQUIRY , "VTA Status Inquiry"}, /* IP Phone */ + {SC_SUBDEVICE_STATE , "Subdevice State?"}, + {SC_DWL_DTMF_CLCK_FORMAT , "Download DTMF & Clock Format"}, /* IP Phone */ + {SC_SET_CLCK , "Set Clock"}, /* IP Phone */ + {SC_VOICE_CHANNEL , "Voice Channel"}, /* IP Phone & UA NOE */ + {SC_EXTERNAL_RINGING , "External Ringing"}, + {SC_LCD_CURSOR , "LCD Cursor"}, /* IP Phone */ + {SC_DWL_SPECIAL_CHAR , "Download Special Character"}, /* IP Phone */ + {SC_SET_CLCK_TIMER_POS , "Set Clock/Timer Position"}, /* IP Phone */ + {SC_SET_LCD_CONTRAST , "Set LCD Contrast"}, /* IP Phone */ + {SC_AUDIO_IDLE , "Audio Idle"}, + {SC_SET_SPEAKER_VOL , "Set Speaker Volume"}, /* IP Phone */ + {SC_BEEP , "Beep"}, + {SC_SIDETONE , "Sidetone"}, + {SC_RINGING_CADENCE , "Set Programmable Ringing Cadence"}, + {SC_MUTE , "Mute"}, + {SC_FEEDBACK , "Feedback"}, + {SC_KEY_RELEASE , "Key Release"}, /* IP Phone */ + {SC_TRACE_ON , "Trace On"}, /* IP Phone - NOT EXPECTED */ + {SC_TRACE_OFF , "Trace Off"}, /* IP Phone - NOT EXPECTED */ + {SC_READ_PERIPHERAL , "Read Peripheral"}, /* IP Phone - NOT EXPECTED */ + {SC_WRITE_PERIPHERAL , "Write Peripheral"}, /* IP Phone - NOT EXPECTED */ + {SC_ALL_ICONS_OFF , "All Icons Off"}, /* IP Phone */ + {SC_ICON_CMD , "Icon Command"}, /* IP Phone */ + {SC_AMPLIFIED_HANDSET , "Amplified Handset (Boost)"}, /* IP Phone */ + {SC_AUDIO_CONFIG , "Audio Config"}, + {SC_AUDIO_PADDED_PATH , "Audio Padded Path"}, /* IP Phone */ + {SC_RELEASE_RADIO_LINK , "Release Radio Link"}, /* IP Phone - NOT EXPECTED */ + {SC_DECT_HANDOVER , "DECT External Handover Routing Code"}, /* IP Phone - NOT EXPECTED */ + {SC_LOUDSPEAKER , "Loudspeaker"}, + {SC_ANNOUNCE , "Announce"}, + {SC_RING , "Ring"}, + {SC_UA_DWL_PROTOCOL , "UA Download Protocol"}, + {0, NULL} +}; +static value_string_ext opcodes_vals_sys_ext = VALUE_STRING_EXT_INIT(opcodes_vals_sys); + +/* Terminal To System Opcodes */ +static const value_string opcodes_vals_term[] = +{ + {CS_NOP_ACK , "NOP Acknowledge"}, + {CS_HANDSET_OFFHOOK , "Handset Offhook"}, /* IP Phone */ + {CS_HANDSET_ONHOOK , "Hansdet Onhook"}, /* IP Phone */ + {CS_DIGIT_DIALED , "Digital Dialed"}, /* IP Phone */ + {CS_SUBDEVICE_MSG , "Subdevice Message"}, + {CS_HE_ROUTING , "HE Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_LOOPBACK_ON , "Loopback On Acknowledge"}, /* Same as CS To Terminal */ + {CS_LOOPBACK_OFF , "Loopback Off Acknowledge"}, /* Same as CS To Terminal */ + {CS_VIDEO_ROUTING , "Video Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_WARMSTART_ACK , "Warmstart Acknowledge"}, /* IP Phone */ + {CS_SUPER_MSG , "Super Message"}, /* IP Phone - NOT EXPECTED */ + {CS_SEGMENT_MSG , "Segment Message"}, /* Same as CS To Terminal */ + {CS_REMOTE_UA_ROUTING , "Remote UA Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_VERY_REMOTE_UA_R , "Very Remote UA Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_OSI_ROUTING , "OSI Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_ABC_A_ROUTING , "ABC-A Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_IBS_ROUTING , "IBS Routing Response Code"}, /* IP Phone - NOT EXPECTED */ + {CS_IP_DEVICE_ROUTING , "IP Device Routing"}, + {CS_SUPER_MSG_2 , "Super Message 2"}, /* Same as CS To Terminal */ + {CS_DEBUG_IN_LINE , "Debug Message"}, + {CS_NON_DIGIT_KEY_PUSHED , "Non-Digit Key Pushed"}, /* IP Phone */ + {CS_VERSION_RESPONSE , "Version Information"}, + {CS_I_M_HERE , "I'm Here Response"}, + {CS_RSP_STATUS_INQUIRY , "Response To Status Inquiry"}, /* IP Phone */ + {CS_SUBDEVICE_STATE , "Subdevice State Response"}, + {CS_DIGIT_KEY_RELEASED , "Digit Key Released"}, /* IP Phone */ + {CS_TRACE_ON_ACK , "Trace On Acknowledge"}, /* IP Phone - NOT EXPECTED */ + {CS_TRACE_OFF_ACK , "Trace Off Acknowledge"}, /* IP Phone - NOT EXPECTED */ + {CS_SPECIAL_KEY_STATUS , "Special Key Status"}, /* IP Phone */ + {CS_KEY_RELEASED , "Key Released"}, /* IP Phone */ + {CS_PERIPHERAL_CONTENT , "Peripheral Content"}, /* IP Phone - NOT EXPECTED */ + {CS_TM_KEY_PUSHED , "TM Key Pushed"}, /* IP Phone - NOT EXPECTED */ + {CS_UA_DWL_PROTOCOL , "Download Protocol"}, + {CS_UNSOLICITED_MSG , "Unsolicited Message"}, + {0, NULL} +}; +static value_string_ext opcodes_vals_term_ext = VALUE_STRING_EXT_INIT(opcodes_vals_term); + +static const value_string str_digit[] = { + { 0, "0"}, + { 1, "1"}, + { 2, "2"}, + { 3, "3"}, + { 4, "4"}, + { 5, "5"}, + { 6, "6"}, + { 7, "7"}, + { 8, "8"}, + { 9, "9"}, + {10, "*"}, + {11, "#"}, + {12, "A"}, + {13, "B"}, + {14, "C"}, + {15, "D"}, + {16, "Flash"}, + {0, NULL} +}; +static value_string_ext str_digit_ext = VALUE_STRING_EXT_INIT(str_digit); + +#define STR_ON_OFF(arg) ((arg) ? "On" : "Off") +#define STR_YES_NO(arg) ((arg) ? "Yes" : "No") + + +static const value_string str_device_type[] = { + {0x00, "Voice Terminal Adaptor"}, + {0, NULL} +}; + + +/*----------------------------------------------------------------------------- + VERSION NUMBER COMPUTER - This function computes a version number (S.SZ.AB) from a 16 bits number + ---------------------------------------------------------------------------*/ +static void +version_number_computer( gchar *result, guint32 hexa_version ) +{ + int release, vers, fix; + + release = (int)(hexa_version / 10000); + vers = (int)((hexa_version % 10000) / 100); + fix = (hexa_version % 10000) % 100; + g_snprintf( result, ITEM_LABEL_LENGTH, "%d.%02d.%02d", release, vers, fix); +} + + +/*----------------------------------------------------------------------------- + Function for UA3G message with opcode and one parameter + + PRODUCTION TEST - 01h (MESSAGE FROM THE SYSTEM) + SUBDEVICE RESET - 06h (MESSAGE FROM THE SYSTEM) + ARE YOU THERE - 2Bh - IPhone & UA NOE (MESSAGE FROM THE SYSTEM) + SET SPEAKER VOLUME - 3Bh (MESSAGE FROM THE SYSTEM) + TRACE ON - 42h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_with_one_parameter(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset, guint length, + int hf_opcode) +{ + if (length == 0) + return; + + proto_tree_add_item(tree, hf_opcode, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SUBDEVICE ESCAPE TO SUBDEVICE - 02h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_subdevice_escape(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + proto_tree_add_item(tree, hf_ua3g_subdevice_address, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_subdevice_opcode, tvb, offset+1, 1, ENC_NA); + if (length > 2) { + proto_tree_add_item(tree, hf_ua3g_subdevice_parameter_bytes, tvb, offset+2, length-2, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + SOFTWARE RESET - 03h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string software_reset_verswitch_vals[] = { + {0x00, "Reset Without Version Switch"}, + {0x01, "Reset With Version Switch"}, + {0, NULL} +}; + +static void +decode_software_reset(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + if (length == 0) + return; + + proto_tree_add_item(tree, hf_ua3g_software_reset, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + IP-PHONE WARMSTART - 04h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_ip_phone_warmstart[] = { + {0x00, "Run In UA2G Emulation Mode"}, + {0x01, "Run In Full UA3G Mode"}, + {0, NULL} +}; + +static void +decode_ip_phone_warmstart(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + if (length == 0) + return; + + proto_tree_add_item(tree, hf_ua3g_ip_phone_warmstart, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SUPER MESSAGE - 0Bh (MESSAGE FROM THE SYSTEM) + SUPER MESSAGE 2 - 17h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_super_msg(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length, guint8 opcode) +{ + proto_tree *ua3g_body_tree = tree; + int j = 0, parameter_length; + + if (!ua3g_body_tree) + return; + + while (length > 0) { + if (opcode == 0x17) { + parameter_length = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_super_msg_length, tvb, offset, 2, + parameter_length, "Length %d: %d", j++, parameter_length); + offset += 2; + length -= 2; + } else { + parameter_length = tvb_get_guint8(tvb, offset); + proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_super_msg_length, tvb, offset, 1, + parameter_length, "Length %d: %d", j++, parameter_length); + offset++; + length--; + } + + if (parameter_length > 0) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_super_msg_data, tvb, offset, parameter_length, ENC_NA); + offset += parameter_length; + length -= parameter_length; + } + } +} + + +/*----------------------------------------------------------------------------- + SEGMENT MESSAGE - 0Ch (MESSAGE FROM THE TERMINAL AND FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +const true_false_string tfs_segment_msg_segment = { "First Segment", "Subsequent Segment" }; + +static void +decode_segment_msg(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + guint8 val; + + if (!tree) + return; + + val = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_ua3g_segment_msg_segment, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_segment_msg_num_remaining, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (val & 0x80) { + proto_tree_add_item(tree, hf_ua3g_segment_msg_length, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + } + + if (length > 0) { + proto_tree_add_item(tree, hf_ua3g_segment_message_data, tvb, offset, length, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + IP DEVICE ROUTING - 13h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_ip_device_routing[] = { + {0x00, "Reset"}, + {0x01, "Start RTP"}, + {0x02, "Stop RTP"}, + {0x03, "Redirect"}, + {0x04, "Tone Definition"}, + {0x05, "Start Tone"}, + {0x06, "Stop Tone"}, + {0x07, "Start Listen RTP"}, + {0x08, "Stop Listen RTP"}, + {0x09, "Get Parameters Value"}, + {0x0A, "Set Parameters Value"}, + {0x0B, "Send Digit"}, + {0x0C, "Pause RTP"}, + {0x0D, "Restart RTP"}, + {0x0E, "Start Record RTP"}, + {0x0F, "Stop Record RTP"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_reset_vals[] = { + {0x00, "Update Mode"}, + {0x01, "Bad Sec Mode"}, + {0x02, "Customization Name"}, + {0x03, "Localization Name"}, + {0, NULL} +}; + +static const value_string reset_param_bad_sec_mode[] = { + {0x01, "Binary is full, CS is secured, but terminal running in clear mode"}, + {0, NULL} +}; + +static const value_string start_rtp_str_direction[] = { + {0x00, "Terminal Input"}, + {0x01, "Terminal Output"}, + {0x02, "Terminal Input/Output (Both Directions)"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_start_rtp_vals[] = { + {0x00, "Local UDP Port"}, + {0x01, "Remote IP Address"}, + {0x02, "Remote UDP Port"}, + {0x03, "Type Of Service"}, + {0x04, "Compressor"}, + {0x05, "Payload Concatenation (ms)"}, + {0x06, "Echo Cancellation Enabler"}, + {0x07, "Silence Suppression Enabler"}, + {0x08, "802.1 Q User Priority"}, + {0x09, "Reserved"}, + {0x0a, "Post Filtering Enabler"}, + {0x0b, "High Pass Filtering Enabler"}, + {0x0c, "Remote SSRC"}, + {0x0d, "Must Send QOS Tickets"}, + {0x0e, "Local Identifier"}, + {0x0f, "Distant Identifier"}, + {0x10, "Destination For RTCP Sender Reports - Port Number"}, + {0x11, "Destination For RTCP Sender Reports - IP Address"}, + {0x12, "Destination For RTCP Receiver Reports - Port Number"}, + {0x13, "Destination For RTCP Receiver Reports - IP Address"}, + {0x14, "Channel Number"}, + {0x15, "DTMF Sending"}, + {0x16, "Payload Type Of Redundancy"}, + {0x17, "Payload Type Of DTMF Events"}, + {0x18, "Enable / Disable RFC 2198"}, + {0x31, "SRTP Encryption Enable For This Communication"}, + {0x32, "Master Key For SRTP Session"}, + {0x33, "Master Salt Key For SRTP Session"}, + {0x34, "Master key for output stream of SRTP session"}, + {0x35, "Master salt key for output stream of SRTP session"}, + {0x36, "Integrity checking enabled for this communication"}, + {0x37, "MKI value for SRTP packets in input stream"}, + {0x38, "MKI value for SRTP packets in output stream"}, + {0x50, "MD5 Authentication"}, + {0, NULL} +}; +static value_string_ext ip_device_routing_cmd_start_rtp_vals_ext = VALUE_STRING_EXT_INIT(ip_device_routing_cmd_start_rtp_vals); + +static const val64_string str_start_rtp_compressor[] = { + {0x00, "G.711 A-law"}, + {0x01, "G.711 mu-law"}, + {0x0F, "G.723.1 5.3kbps"}, + {0x10, "G.723.1 6.3kbps"}, + {0x11, "G.729A 8 kbps"}, + {0, NULL} +}; + +static const value_string str_set_param_req_compressor[] = { + {0x00, "G.711 A-law"}, + {0x01, "G.711 mu-law"}, + {0x0F, "G.723.1 5.3kbps"}, + {0x10, "G.723.1 6.3kbps"}, + {0x11, "G.729A 8 kbps"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_stop_rtp_vals[] = { + {0x0E, "Local Identifier"}, + {0x0F, "Distant Identifier"}, + {0x14, "Canal Identifier"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_redirect_vals[] = { + {0x00, "Remote MainCPU Server IP Address"}, + {0x01, "Remote MainCPU Server Port"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_listen_rtp_vals[] = { + {0x00, "Remote IP Address "}, + {0x01, "Remote UDP Port In "}, + {0x02, "Remote UDP Port Out "}, + {0x03, "Remote IP Address Out"}, + {0x04, "Canal Number"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_set_param_req_vals[] = { + {0x00 , "QOS IP TOS"}, + {0x01 , "QOS 8021 VLID"}, + {0x02 , "QOS 8021 PRI"}, + {0x03 , "SNMP MIB2 SysContact"}, + {0x04 , "SNMP MIB2 SysName"}, + {0x05 , "SNMP MIB2 SysLocation"}, + {0x06 , "Default Compressor"}, + {0x07 , "Error String Net Down"}, + {0x08 , "Error String Cable PB"}, + {0x09 , "Error String Try Connect"}, + {0x0A , "Error String Connected"}, + {0x0B , "Error String Reset"}, + {0x0C , "Error String Duplicate IP Address"}, + {0x0D , "SNMP MIB Community"}, + {0x0E , "TFTP Backup Sec Mode"}, + {0x0F , "TFTP Backup IP Address"}, + {0x10 , "Set MMI Password"}, + {0x11 , "Set PC Port Status"}, + {0x12 , "Record RTP Authorization"}, + {0x13 , "Security Flags"}, + {0x14 , "ARP Spoofing"}, + {0x15 , "Session Param"}, + {0x30 , "MD5 Authentication"}, + {0, NULL} +}; +static value_string_ext ip_device_routing_cmd_set_param_req_vals_ext = VALUE_STRING_EXT_INIT(ip_device_routing_cmd_set_param_req_vals); + +static const value_string ip_device_routing_cmd_pause_restart_vals[] = { + {0x14, "Canal Identifier"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_record_rtp_vals[] = { + {0x00 , "Recorder Index"}, + {0x01 , "Remote IP Address"}, + {0x02 , "Remote UDP Port In"}, + {0x03 , "Remote UDP Port Out"}, + {0x04 , "Remote IP Address Out"}, + {0x05 , "Local UDP Port In"}, + {0x06 , "Local UDP Port Out"}, + {0x07 , "Type Of Service"}, + {0x08 , "Master Key For SRTP Session"}, + {0x09 , "Master Salt Key For SRTP Session"}, + {0x30 , "MD5 Authentication"}, + {0, NULL} +}; + +static const value_string ip_device_routing_tone_direction_vals[] = { + {0x00, "On The Phone"}, + {0x40, "To The Network"}, + {0x80, "On The Phone and To The Network"}, + {0, NULL} +}; + +static const value_string ip_device_routing_cmd_get_param_req_vals[] = { + {0x00 , "Firmware Version"}, + {0x01 , "Firmware Version"}, + {0x02 , "DHCP IP Address"}, + {0x03 , "Local IP Address"}, + {0x04 , "Subnetwork Mask"}, + {0x05 , "Router IP Address"}, + {0x06 , "TFTP IP Address"}, + {0x07 , "MainCPU IP Address"}, + {0x08 , "Default Codec"}, + {0x09 , "Ethernet Drivers Config"}, + {0x0A , "MAC Address"}, + {0, NULL} +}; + +static const value_string str_set_pc_port_status[] = { + {0x00, "No PC Port Security"}, + {0x01, "Block PC Port"}, + {0x02, "Filter VLAN"}, + {0, NULL} +}; + +static const value_string str_enable_feature[] = { + {0x00, "Disable Feature"}, + {0x01, "Enable Feature"}, + {0, NULL} +}; + +static void +decode_ip_device_routing(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command; + proto_tree *ua3g_body_tree = tree, *ua3g_param_tree, *ua3g_param_subtree; + proto_item *ua3g_param_item; + int parameter_length, parameter_id; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_ip_device_routing, "Unknown")); + + if (!ua3g_body_tree) + return; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (command) { + case 0x00: /* RESET */ + { + if (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_const(parameter_id, ip_device_routing_cmd_reset_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + guint8 param; + switch (parameter_id) { + case 0x00: /* Update Mode */ + + param = tvb_get_guint8(tvb, offset); + if ((param & 0x80) == 0x00) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_noe_update, tvb, offset, 1, ENC_NA); + ua3g_param_subtree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param_sub); + + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_bootloader, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_data, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_customization, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_localization, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_code, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_subtree, hf_ua3g_ip_device_routing_reset_parameter_noe_update_sip, tvb, offset, 1, ENC_NA); + + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + + break; + case 0x01: /* Bad_Sec_Mode */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_bad_sec_mode, tvb, offset, 1, ENC_NA); + break; + case 0x02: /* Cust_Name */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_cust_name, tvb, offset, parameter_length, ENC_NA|ENC_ASCII); + break; + case 0x03: /* L10N_Name */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_l10n_name, tvb, offset, parameter_length, ENC_NA|ENC_ASCII); + break; + default: + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_reset_parameter_value, tvb, offset, parameter_length, ENC_NA); + break; + } + + /*offset += parameter_length; + length -= parameter_length;*/ + } + } + break; + } + case 0x01: /* START RTP */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_start_rtp_direction, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_start_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_ext_const(parameter_id, &ip_device_routing_cmd_start_rtp_vals_ext, "Unknown")); + + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x01: /* Remote IP Address */ + case 0x11: /* Destination For RTCP Sender Reports - IP Address */ + case 0x13: /* Destination For RTCP Receiver Reports - IP Address */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x04: /* Compressor */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_compressor, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x06: /* Echo Cancelation Enabler */ + case 0x07: /* Silence Suppression Enabler */ + case 0x0A: /* Post Filtering Enabler */ + case 0x0B: /* High Pass Filtering Enabler */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_enabler, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x0D: /* Must Send QOS Tickets */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_send_qos, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x0E: /* Local Identifier */ + case 0x0F: /* Distant Identifier */ + break; + case 0x15: /* DTMF Sending */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_dtmf_sending, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x18: /* Enable / Disable RFC 2198 */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_rfc2198, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x31: /* SRTP Encryption Enable For This Communication */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_srtp_encryption, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + case 0x00: /* Local UDP Port */ + case 0x02: /* Remote UDP Port */ + case 0x03: /* Type Of Service */ + case 0x05: /* Payload Concatenation */ + case 0x08: /* 802.1 Q User Priority */ + case 0x09: /* Reserved */ + case 0x0C: /* Remote SSRC */ + case 0x10: /* Destination For RTCP Sender Reports - Port Number */ + case 0x12: /* Destination For RTCP Receiver Reports - Port Number */ + case 0x14: /* Channel Number */ + case 0x16: /* Payload Type For Redundancy */ + case 0x17: /* Payload Type For DTMF Events */ + case 0x32: /* Master Key For SRTP Session */ + case 0x33: /* Master Salt Key For SRTP Session */ + case 0x34: /* Master key for output stream of SRTP session */ + case 0x35: /* Master salt key for output stream of SRTP session */ + case 0x36: /* Integrity checking enabled for this communication */ + case 0x37: /* MKI value for SRTP packets in input stream */ + case 0x38: /* MKI value for SRTP packets in output stream */ + case 0x50: /* MD5 Authentication */ + default: + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + } + case 0x02: /* STOP_RTP */ + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_const(parameter_id, ip_device_routing_cmd_stop_rtp_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter_value_num, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_stop_rtp_parameter_value_bytes, tvb, offset, parameter_length, ENC_NA); + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x03: /* REDIRECT */ + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_redirect_parameter, + tvb, offset, parameter_length + 2, parameter_id, + "%s", val_to_str_const(parameter_id, ip_device_routing_cmd_redirect_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_redirect_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_redirect_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x00: /* Remote MainCPU Server IP Address */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_redirect_parameter_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x01: /* Remote MainCPU Server Port */ + default: + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_redirect_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_redirect_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x04: /* DEF_TONES */ + { + int i, tone_nb_entries; + guint16 frequency_1, frequency_2; + signed char level_1, level_2; + + tone_nb_entries = tvb_get_guint8(tvb, offset); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_def_tones_num_entries, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0 && tone_nb_entries) { + for (i = 1; i <= tone_nb_entries; i++) { + frequency_1 = tvb_get_ntohs(tvb, offset); + level_1 = (signed char)(tvb_get_guint8(tvb, offset + 2)) / 2; + frequency_2 = tvb_get_ntohs(tvb, offset + 3); + level_2 = (signed char)(tvb_get_guint8(tvb, offset + 5)) / 2; + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 6, + "Tone Pair %d: %d Hz at %d dB / %d Hz at %d dB", + i, frequency_1, level_1, frequency_2, level_2); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_def_tones_frequency_1, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + proto_tree_add_int(ua3g_param_tree, hf_ua3g_ip_device_routing_def_tones_level_1, tvb, offset, 1, level_1); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_def_tones_frequency_2, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + proto_tree_add_int(ua3g_param_tree, hf_ua3g_ip_device_routing_def_tones_level_2, tvb, offset, 1, level_2); + offset++; + length--; + } + } + break; + } + case 0x05: /* START TONE */ + { + guint8 ii, tone_nb_entries, tone_id; +#if 0 + guint8 tone_direction, tone_id, tone_duration tone_silence; +#endif + int tone_duration; + + tone_nb_entries = tvb_get_guint8(tvb, offset); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_start_tone_direction, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_start_tone_num_entries, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0 && tone_nb_entries) { + for (ii = 0; ii < tone_nb_entries; ii++) { + tone_id = tvb_get_guint8(tvb, offset); + tone_duration = tvb_get_ntohs(tvb, offset + 1); +#if 0 + tone_duration = tvb_get_guint8(tvb, offset + 1); + tone_silence = tvb_get_guint8(tvb, offset + 2); +#endif + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 6, +#if 0 + "Tone Pair %d: Id: %d, Duration: %d ms, Silence: %d ms", + ii+1, tone_id, tone_duration, tone_silence); +#endif + "Tone Pair %d: Id: %d, Duration: %d ms", + ii+1, tone_id, tone_duration); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_tone_identification, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_tone_duration, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + +#if 0 + proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, + "Duration: %d ms", tone_duration); + offset++; + length--; + + proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, + "Silence: %d ms", tone_silence); + offset++; + length--; +#endif + } + } + break; + } + case 0x07: /* START LISTEN RTP */ + case 0x08: /* STOP LISTEN RTP */ + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_const(parameter_id, ip_device_routing_cmd_listen_rtp_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x00: /* Remote IP Address - Not for start listening rtp */ + case 0x03: /* Remote IP Address Out - Not for start listening rtp */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter_ip, tvb, offset, 1, ENC_NA); + break; + case 0x01: /* Remote UDP Port In - Not for start listening rtp */ + case 0x02: /* Remote UDP Port Out - Not for start listening rtp */ + case 0x04: /* Canal Number */ + default: + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter_port, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_listen_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x09: /* GET_PARAM_REQ */ + while (length > 0) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_get_param_req_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + break; + + case 0x0A: /* SET_PARAM_REQ */ + { + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_set_param_req_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_ext_const(parameter_id, &ip_device_routing_cmd_set_param_req_vals_ext, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x06: /* Compressor */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_compressor, tvb, offset, 1, ENC_NA); + break; + case 0x07: /* ERR STRING NET DOWN */ + case 0x08: /* ERR STRING CABLE PB */ + case 0x09: /* ERR STRING TRY CONNECT */ + case 0x0A: /* ERR STRING CONNECTED */ + case 0x0B: /* ERR STRING RESET */ + case 0x0C: /* ERR STRING DUPLICATE IP ADDRESS */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_err_string, tvb, offset, parameter_length, ENC_NA|ENC_ASCII); + break; + case 0x0F: /* TFTP BACKUP IP ADDR */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_tftp_backup_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x11: /* Set PC Port status */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_set_pc_port_status, tvb, offset, 1, ENC_NA); + break; + case 0x12: /* Record RTP Authorization */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_record_rtp_auth, tvb, offset, 1, ENC_NA); + break; + case 0x13: /* Security Flags */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_security_flag_filter, tvb, offset, 1, ENC_NA); + break; + case 0x00: /* QOS IP TOS */ + case 0x01: /* QOS 8021 VLID */ + case 0x02: /* QOS 8021 PRI */ + case 0x03: /* SNMP MIB2 SYSCONTACT */ + case 0x04: /* SNMP MIB2 SYSNAME */ + case 0x05: /* SNMP MIB2 SYSLOCATION */ + case 0x0D: /* SNMP MIB COMMUNITY */ + case 0x0E: /* TFTP BACKUP SEC MODE */ + case 0x10: /* SET MMI PASSWORD */ + case 0x14: /* ARP Spoofing */ + case 0x15: /* Session Param */ + case 0x30: /* MD5 Authentication */ + default: + if ((parameter_length > 0) && (parameter_length <= 8)) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else if (parameter_length > 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_set_param_req_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + } + case 0x0B: /* SEND_DIGIT */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_device_routing_digit_value, tvb, offset, 1, ENC_NA); + break; + + case 0x0C: /* PAUSE_RTP */ + case 0x0D: /* RESTART_RTP */ + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_pause_restart_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_const(parameter_id, ip_device_routing_cmd_pause_restart_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_pause_restart_rtp_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x0E: /* START_RECORD_RTP */ + case 0x0F: /* STOP RECORD RTP */ + while (length > 0) { + + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter, tvb, offset, + parameter_length + 2, parameter_id, "%s", + val_to_str_const(parameter_id, ip_device_routing_cmd_record_rtp_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x01: /* Remote IP Address */ + case 0x04: /* Remote IP Address Out */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_remote_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x00: /* Recorder Index */ + case 0x02: /* Remote UDP Port In */ + case 0x03: /* Remote UDP Port Out */ + case 0x05: /* Local UDP Port In */ + case 0x06: /* Local UDP Port Out */ + case 0x07: /* Type Of Service */ + case 0x08: /* Master Key For SRTP Session */ + case 0x09: /* Master Salt Key For SRTP Session */ + case 0x30: /* MD5 Authentication */ + default: + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x06: /* STOP TONE */ + default: + { + break; + } + } +} + + +/*----------------------------------------------------------------------------- + DEBUG IN LINE - 18h (MESSAGE FROM THE TERMINAL AND FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_debug_in_line(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + proto_tree_add_item(tree, hf_ua3g_debug_in_line, tvb, offset, length, ENC_NA|ENC_ASCII); +} + + +/*----------------------------------------------------------------------------- + LED COMMAND - 21h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_led[] = { + {0x00, "Led Off"}, + {0x01, "Led On"}, + {0x02, "Red Led Fast Flash"}, + {0x03, "Red Led Slow Flash"}, + {0x04, "Green Led On"}, + {0x05, "Green Led Fast Flash"}, + {0x06, "Green Led Slow Flash"}, + {0x07, "All Led Off"}, + {0, NULL} +}; + +static void +decode_led_command(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset) +{ + int command; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_led, "Unknown")); + + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_command_led, tvb, offset, 1, ENC_NA); + + if ((command >= 0) && (command < 7)) { + proto_tree_add_item(tree, hf_ua3g_command_led_number, tvb, offset+1, 1, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + LCD LINE 1 COMMANDS - 27h (MESSAGE FROM THE SYSTEM) + LCD LINE 2 COMMANDS - 28h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_lcd_line[] = { + {0, "Clear Line & Write From Column"}, + {1, "Write From Column"}, + {2, "Append To Current Line"}, + {0, NULL} +}; + +static const value_string str_call_timer_ctrl[] = { + {0x00, "Call Timer Status Not Changed"}, + {0x01, "Stop Call Timer"}, + {0x02, "Start Call Timer From Current Value"}, + {0x03, "Initialize And Call Timer"}, + {0, NULL} +}; + +static void +decode_lcd_line_cmd(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command, column_n; + const gchar* command_str; + proto_tree *ua3g_body_tree = tree, *ua3g_param_tree, *ua3g_option_tree; + proto_item *ua3g_param_item, *ua3g_option_item; + emem_strbuf_t *strbuf; + + command = tvb_get_guint8(tvb, offset) & 0x03; + column_n = tvb_get_guint8(tvb, offset + 1); + command_str = val_to_str_const(command, str_command_lcd_line, "Unknown"); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s %d", command_str, column_n); + + if (!ua3g_body_tree) + return; + + strbuf = ep_strbuf_new_label("\""); + + ep_strbuf_append_printf(strbuf, "%s\"", tvb_format_text(tvb, offset + 2, length - 2)); + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, + length, "%s %d: %s", + command_str, column_n, strbuf->str); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_command_lcd_line, tvb, offset, 1, ENC_NA); + ua3g_option_item = proto_tree_add_item(ua3g_param_tree, hf_ua3g_lcd_line_cmd_lcd_options, tvb, offset, 1, ENC_NA); + ua3g_option_tree = proto_item_add_subtree(ua3g_option_item, ett_ua3g_option); + + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_call_timer, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_blink, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_call_timer_control, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_call_timer_display, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_time_of_day_display, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_option_tree, hf_ua3g_lcd_line_cmd_lcd_options_suspend_display_refresh, tvb, offset, 1, ENC_NA); + + offset++; + length--; + + if (command != 3) + proto_tree_add_item(ua3g_param_tree, hf_ua3g_lcd_line_cmd_starting_column, tvb, offset, 1, ENC_NA); + else + proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Unused"); + + offset++; + length--; + proto_tree_add_text(ua3g_param_tree, tvb, offset, length, "ASCII Char: %s", strbuf->str); +} + + +/*----------------------------------------------------------------------------- + MAIN VOICE MODE - 29h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_main_voice_mode[] = { + {0x00, "Idle"}, + {0x01, "Handset"}, + {0x02, "Group Listening"}, + {0x03, "On Hook Dial"}, + {0x04, "Handsfree"}, + {0x05, "Announce Loudspeaker"}, + {0x06, "Ringing"}, + {0x10, "Idle"}, + {0x11, "Handset"}, + {0x12, "Headset"}, + {0x13, "Handsfree"}, + {0, NULL} +}; + +static const value_string str_cadence[] = { + {0x00, "Standard Ringing"}, + {0x01, "Double Burst"}, + {0x02, "Triple Burst"}, + {0x03, "Continuous Ringing"}, + {0x04, "Priority Attendant Ringing"}, + {0x05, "Regular Attendant Ringing"}, + {0x06, "Programmable Cadence"}, + {0x07, "Programmable Cadence"}, + {0, NULL} +}; + +static void +decode_main_voice_mode(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 mode; + proto_tree *ua3g_body_tree = tree; + + mode = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(mode, str_main_voice_mode, "Unknown")); + + if (!ua3g_body_tree) + return; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_main_voice_mode, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (mode) { + case 0x06: /* Ringing */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_main_voice_mode_tune, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_main_voice_mode_cadence, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + /* FALLTHROUGH */ + case 0x02: /* Group Listening */ + case 0x03: /* On Hook Dial */ + case 0x04: /* Handsfree */ + case 0x05: /* Announce Loudspeaker */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_main_voice_mode_speaker_volume, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (length > 0) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_main_voice_mode_microphone_volume, tvb, offset, 1, ENC_NA); + } + break; + } + case 0x11: /* Handset */ + { + signed char level; + + level = (signed char)(tvb_get_guint8(tvb, offset)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_handset_level, tvb, offset, 1, level); + + level = (signed char)(tvb_get_guint8(tvb, offset+1)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_sending_level, tvb, offset+1, 1, level); + break; + } + case 0x12: /* Headset */ + { + signed char level; + + level = (signed char)(tvb_get_guint8(tvb, offset)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_headset_level, tvb, offset, 1, level); + + level = (signed char)(tvb_get_guint8(tvb, offset+1)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_sending_level, tvb, offset+1, 1, level); + break; + } + case 0x13: /* Handsfree */ + { + signed char level; + + level = (signed char)(tvb_get_guint8(tvb, offset)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_handsfree_level, tvb, offset, 1, level); + + level = (signed char)(tvb_get_guint8(tvb, offset+1)) / 2; + proto_tree_add_int(ua3g_body_tree, hf_ua3g_main_voice_mode_sending_level, tvb, offset+1, 1, level); + break; + } + case 0x00: /* Idle */ + case 0x01: /* Handset */ + case 0x10: /* Idle */ + default: + { + break; + } + } +} + + +/*----------------------------------------------------------------------------- + SUBDEVICE METASTATE - 2Ch (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_new_metastate[] = { + {0x00, "Disable"}, + {0x01, "Active"}, + {0x02, "Wake Up"}, + {0, NULL} +}; + +static void +decode_subdevice_metastate(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset) +{ + proto_tree_add_item(tree, hf_ua3g_subdevice_metastate_subchannel_address, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_subdevice_metastate_new_metastate, tvb, offset+1, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + Download DTMF & CLOCK FORMAT - 30h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_clock_format[] = { + {0, "Europe"}, + {1, "US"}, + {0, NULL} +}; + +static void +decode_dwl_dtmf_clck_format(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset, guint length) +{ + proto_tree_add_item(tree, hf_ua3g_dwl_dtmf_clck_format_minimum_on_time, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_dwl_dtmf_clck_format_inter_digit_pause_time, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_dwl_dtmf_clck_format_clock_time_format, tvb, offset+2, 1, ENC_NA); + + if (length > 2) + proto_tree_add_item(tree, hf_ua3g_dwl_dtmf_clck_format_dtmf_country_adaptation, tvb, offset+3, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SET CLOCK - 31h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_set_clck[] = { + {0x00, "Set Current Time/Call Timer"}, + {0x01, "Set Current Time"}, + {0x02, "Set Call Timer"}, + {0, NULL} +}; + +static const value_string str_call_timer[] = { + {1, "Call Timer "}, + {0, NULL} +}; + +static void +decode_set_clck(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command; + int hour, minute, second, call_timer; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_set_clck, "Unknown")); + + proto_tree_add_item(tree, hf_ua3g_command_set_clck, tvb, offset, 1, ENC_NA); + offset++; + length--; + call_timer = 0; + + switch (command) { + case 0x02: /* Timer Form */ + { + call_timer = 1; + } + /* FALLTHROUGH */ + case 0x00: /* Set Current Time/Call Timer */ + case 0x01: /* Set Current Time */ + { + while (length > 0) { + hour = tvb_get_guint8(tvb, offset); + minute = tvb_get_guint8(tvb, offset + 1); + second = tvb_get_guint8(tvb, offset + 2); + + proto_tree_add_text(tree, tvb, offset, 3, + "%s: %d:%d:%d", + val_to_str_const(call_timer, str_call_timer, "Current Time"), hour, minute, second); + offset += 3; + length -= 3; + + call_timer = 1; + } + } + default: + { + break; + } + } + +} + + +/*----------------------------------------------------------------------------- + VOICE CHANNEL - 32h - (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_voice_channel[] = { + {0x00, "No"}, + {0x01, "B1"}, + {0x02, "B2"}, + {0x03, "B3"}, + {0, NULL} +}; + +static const true_false_string tfs_voice_channel_channel_mode = { "Write 00 to Voice Channel", "Normal Voice Channel Mode" }; +static const true_false_string tfs_voice_channel_codec = { "Write Quiet To Codec", "Normal Codec Operation" }; +static const true_false_string tfs_voice_channel_voice_channel = { "Use B3 As Voice Channel", "Use B1 As Voice Channel" }; + +static void +decode_voice_channel(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + if (length == 1) { + proto_tree_add_item(tree, hf_ua3g_voice_channel_channel_mode, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_codec, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_voice_channel, tvb, offset, 1, ENC_NA); + } else if (length == 2) { + proto_tree_add_item(tree, hf_ua3g_voice_channel_main_voice, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_announce, tvb, offset+1, 1, ENC_NA); + } else if (length == 4) { + proto_tree_add_item(tree, hf_ua3g_voice_channel_b_general, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_b_loud_speaker, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_b_ear_piece, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_voice_channel_b_microphones, tvb, offset+3, 1, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + EXTERNAL RINGING - 33h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_ext_ring_cmd[] = { + {0x00, "Turn Off"}, + {0x01, "Turn On"}, + {0x02, "Follow The Normal Ringing"}, + {0, NULL} +}; + +static void +decode_external_ringing(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset) +{ + guint8 command; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_ext_ring_cmd, "Unknown")); + + proto_tree_add_item(tree, hf_ua3g_external_ringing_command, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + LCD CURSOR - 35h - (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_lcd_cursor(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo, guint offset) +{ + const gchar* str_on_off_val = STR_ON_OFF(tvb_get_guint8(tvb, offset + 1) & 0x02); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", str_on_off_val); + + proto_tree_add_item(tree, hf_ua3g_lcd_cursor_line_number, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_lcd_cursor, tvb, offset+1, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + DOWNLOAD SPECIAL CHARACTER - 36h - (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_dwl_special_char(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset, guint length) +{ + int i; + + while (length > 0) { + proto_tree_add_item(tree, hf_ua3g_dwl_special_char_character_number, tvb, offset, 1, ENC_NA); + offset++; + length--; + for (i = 1; i <= 8; i++) { + proto_tree_add_item(tree, hf_ua3g_dwl_special_char_byte, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + } +} + + +/*----------------------------------------------------------------------------- + SET CLOCK/TIMER POSITION - 38h - (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_set_clck_timer_pos(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset) +{ + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_set_clck_timer_pos_clock_line_number, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_set_clck_timer_pos_clock_column_number, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_set_clck_timer_pos_call_timer_line_number, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_set_clck_timer_pos_call_timer_column_number, tvb, offset+3, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SET LCD CONTRAST - 39h - (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_driver_number[] = { + {0x00, "Display"}, + {0x01, "Icon"}, + {0, NULL} +}; + +static void +decode_set_lcd_contrast(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset) +{ + proto_tree_add_item(tree, hf_ua3g_set_lcd_contrast_driver_number, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_set_lcd_contrast_contrast_value, tvb, offset+1, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + BEEP - 3Ch (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_beep[] = { + {0x01, "Beep Once"}, + {0x02, "Beep Start"}, + {0x03, "Stop Beep"}, + {0x04, "Start Beep"}, + {0x05, "Define Beep"}, + {0, NULL} +}; + +static const value_string str_beep_start_destination[] = { + {0x01, "Ear-Piece"}, + {0x02, "Loudspeaker"}, + {0x03, "Ear-Piece and Loudspeaker"}, + {0, NULL} +}; + +static const value_string str_start_beep_destination[] = { + {0x01, "Handset"}, + {0x02, "Headset"}, + {0x04, "Loudspeaker"}, + {0x08, "Announce Loudspeaker"}, + {0x10, "Handsfree"}, + {0, NULL} +}; + +static const value_string str_beep_freq_sample_nb[] = { + {0x00, "Frequency"}, + {0xFF, "Audio Sample Number"}, + {0, NULL} +}; +static const value_string str_beep_duration[] = { + {0x00, "Duration "}, + {0xFF, "Duration (Ignored)"}, + {0, NULL} +}; +static const value_string str_beep_terminator[] = { + {0xFD, "Stop"}, + {0xFE, "Loop"}, + {0xFF, "Infinite"}, + {0, NULL} +}; + +static void +decode_beep(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + if (length > 0) { /* All cases except classical beep */ + guint8 command; + proto_tree *ua3g_body_tree = tree; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_beep, "Unknown")); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_command_beep, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (command) { + case 0x01: /* Beep Once */ + case 0x02: /* Beep Start */ + { + int i = 0; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_destination, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0) { + guint8 val; + + i++; + val = (tvb_get_guint8(tvb, offset) & 0x7F) * 10; + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_on_off, tvb, offset, 1, ENC_NA); + proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_beep_cadence, tvb, offset, 1, val, + "Cadence T%d: %d ms", i, val); + offset++; + length--; + } + break; + } + case 0x04: /* Start Beep */ + { + guint8 beep_dest; + emem_strbuf_t *strbuf; + int i; + + beep_dest = tvb_get_guint8(tvb, offset); + + strbuf = ep_strbuf_new_label(NULL); + + for (i = 0; i < 5; i++) { + ep_strbuf_append(strbuf, + val_to_str_const(beep_dest & (0x01 << i), str_start_beep_destination, "")); + } + + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "Destination: %s", strbuf->str); + offset++; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_beep_number, tvb, offset, 1, ENC_NA); + break; + } + case 0x05: + { + int i, nb_of_notes, beep_number; + + beep_number = tvb_get_guint8(tvb, offset); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_beep_number, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (beep_number <= 0x44) + beep_number = 0x00; + else + beep_number = 0xFF; + + nb_of_notes = tvb_get_guint8(tvb, offset); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_number_of_notes, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0) { + for (i = 1; i <= nb_of_notes; i++) { + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s %d: %d", + val_to_str_const(beep_number, str_beep_freq_sample_nb, "Unknown"), + i, tvb_get_guint8(tvb, offset)); + offset++; + length--; + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Level %d: %d", + i, tvb_get_guint8(tvb, offset)); + offset++; + length--; + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s %d: %x", + val_to_str_const(beep_number, str_beep_duration, "Unknown"), + i, tvb_get_guint8(tvb, offset)); + offset++; + length--; + } + proto_tree_add_item(ua3g_body_tree, hf_ua3g_beep_terminator, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + break; + } + case 0x03: /* Stop Beep */ + default: + { + break; + } + } + } else { /* Classical Beep */ + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": Classical Beep"); + } +} + + +/*----------------------------------------------------------------------------- + SIDETONE ON / OFF - 3Dh (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_sidetone(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, guint offset) +{ + guint8 command; + const gchar* command_str; + + command = tvb_get_guint8(tvb, offset); + command_str = STR_ON_OFF(command); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", command_str); + + proto_tree_add_item(tree, hf_ua3g_command_sidetone, tvb, offset, 1, ENC_NA); + + if (command == 0x01) { + proto_tree_add_int(tree, hf_ua3g_sidetone_level, tvb, offset+1, 1, + (signed char)(tvb_get_guint8(tvb, offset+1) / 2)); + } +} + + +/*----------------------------------------------------------------------------- + SET PROGRAMMABLE RINGING CADENCE - 3Eh (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_ringing_cadence(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset, guint length) +{ + int i = 0; + guint16 cadence_length; + + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_ringing_cadence_cadence, tvb, offset, 1, ENC_NA); + offset++; + length--; + + while (length > 0) { + i++; + proto_tree_add_item(tree, hf_ua3g_ringing_cadence_on_off, tvb, offset, 1, ENC_NA); + cadence_length = ((tvb_get_guint8(tvb, offset) & 0x7F) * 10); + proto_tree_add_uint_format(tree, hf_ua3g_ringing_cadence_length, tvb, offset, 1, cadence_length, + "Length %d : %d ms", i, cadence_length); + offset++; + length--; + } +} + + +/*----------------------------------------------------------------------------- + MUTE ON / OFF - 3Fh (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_mute[] = { + {0x00, "Microphone Disable"}, + {0x01, "Microphone Enable"}, + {0, NULL} +}; + +static void +decode_mute(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, guint offset) +{ + guint8 command; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_mute, "Unknown")); + + proto_tree_add_item(tree, hf_ua3g_command_mute, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + FEEDBACK ON / OFF - 40h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_feedback(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command; + const gchar* command_str; + + command = tvb_get_guint8(tvb, offset); + command_str = STR_ON_OFF(command); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", command_str); + + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_command_feedback, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (command == 0x01) { + proto_tree_add_int(tree, hf_ua3g_feedback_level, tvb, offset, 1, + (signed char)(tvb_get_guint8(tvb, offset) / 2)); + offset++; + length--; + + if (length > 0) { + proto_tree_add_uint_format_value(tree, hf_ua3g_feedback_duration, tvb, offset, 1, + tvb_get_guint8(tvb, offset) * 10, "%d ms", tvb_get_guint8(tvb, offset) * 10); + } + } +} + + +/*----------------------------------------------------------------------------- + READ PERIPHERAL - 44h (MESSAGE FROM THE SYSTEM) + WRITE PERIPHERAL - 45h (MESSAGE FROM THE SYSTEM) + PERIPHERAL CONTENT - 2Bh (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static void +decode_r_w_peripheral(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset, guint length) +{ + proto_tree_add_item(tree, hf_ua3g_r_w_peripheral_address, tvb, offset, 2, ENC_BIG_ENDIAN); + + if (length > 2) { + proto_tree_add_item(tree, hf_ua3g_r_w_peripheral_content, tvb, offset+2, 1, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + ICON COMMAND - 47h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_icon_cmd_state[] = { + {0x00, "Off"}, + {0x01, "Slow Flash"}, + {0x02, "Not Used"}, + {0x03, "Steady On"}, + {0, NULL} +}; + +static void +decode_icon_cmd(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, guint offset) +{ + guint8 byte0, byte1, bytex; + int i; + + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_icon_cmd_icon_number, tvb, offset, 1, ENC_NA); + + byte0 = tvb_get_guint8(tvb, offset+1); + byte1 = tvb_get_guint8(tvb, offset+2); + + for (i = 0; i < 8; i++) { + bytex = + ((byte0 >> i) & 0x01) * 2 + + ((byte1 >> i) & 0x01); + proto_tree_add_uint_format(tree, hf_ua3g_icon_cmd_segment, tvb, offset+1, 2, bytex, + "Segment %d: %s (%d)", i, val_to_str_const(bytex, str_icon_cmd_state, "Unknown"), bytex); + } +} + + +/*----------------------------------------------------------------------------- + AUDIO CONFIGURATION - 49h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_audio_config[] = { + {0x00, "Audio Coding"}, + {0x01, "DPI Channel Allocations"}, + {0x02, "Loudspeaker Volume Adjust"}, + {0x03, "Audio Circuit Configuration"}, + {0x04, "Handsfree Parameters"}, + {0x05, "Loudspeaker Acoustic Parameters"}, + {0x06, "Device Configuration"}, + {0, NULL} +}; + +static const value_string str_audio_coding_law[] = { + {0x00, "A Law"}, + {0x01, "m Law"}, + {0, NULL} +}; + +static const value_string str_device_configuration[] = { + { 0, "Handset Device "}, + { 1, "Headset Device "}, + { 2, "Loudspeaker Device "}, + { 3, "Announce Loudspeaker Device"}, + { 4, "Handsfree Device "}, + { 0, NULL } +}; + +const true_false_string tfs_audio_config_handsfree_return = { "Return Loss Active", "Return Loss Normal" }; +const true_false_string tfs_audio_config_handsfree_handsfree = { "More Full Duplex", "Handsfree Normal" }; + +static void +decode_audio_config(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command; + proto_tree *ua3g_body_tree = tree; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_audio_config, "Unknown")); + + if (!ua3g_body_tree) + return; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_command_audio_config, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (command) { + case 0x00: /* Audio Coding */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_ignored, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_law, tvb, offset+1, 1, ENC_NA); + break; + case 0x01: /* DPI Channel Allocations */ + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_dpi_chan_ua_tx1, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_dpi_chan_ua_tx2, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_dpi_chan_gci_tx1, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_dpi_chan_gci_tx2, tvb, offset+3, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_dpi_chan_cod_tx, tvb, offset+4, 1, ENC_NA); + break; + case 0x02: /* Loudspeaker Volume Adjust */ + { + int i; + for (i = 1; i < 8; i++) { + proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_audio_config_volume_level, tvb, offset, + 1, tvb_get_guint8(tvb, offset), "Volume Level %d: %d", + i, tvb_get_guint8(tvb, offset)); + offset++; + length--; + } + break; + } + case 0x03: /* Audio Circuit Configuration */ + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_dth, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_dtr, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_dtf, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_str, tvb, offset+3, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_ahp1, tvb, offset+4, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_ahp2, tvb, offset+5, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_ath, tvb, offset+6, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_atr, tvb, offset+7, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_atf, tvb, offset+8, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_audio_circuit_alm, tvb, offset+9, 1, ENC_NA); + break; + case 0x04: /* Handsfree Parameters */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_handsfree_return, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_handsfree_handsfree, tvb, offset, 1, ENC_NA); + break; + case 0x05: /* Loudspeaker Acoustic Parameters */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_group_listen, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_attenuation, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_stay_in_send, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mtx, tvb, offset+3, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mrc, tvb, offset+4, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_trans_threshold, tvb, offset+5, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_trans_threshold, tvb, offset+6, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_recv_threshold, tvb, offset+7, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_recv_threshold, tvb, offset+8, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_med_recv_threshold, tvb, offset+9, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_high_recv_threshold, tvb, offset+10, 1, ENC_NA); + break; + case 0x06: /* Device Configuration */ + { + static const gchar *str_device_values[] = { + " Internal", + " Rj9 Plug", + " Jack Plug", + " Bluetooth Link", + " USB Link" + }; + emem_strbuf_t *strbuf; + guint8 device_values; + int j; + int device_index = 0; + + strbuf = ep_strbuf_new_label(NULL); + + while (length > 0) { + + device_values = tvb_get_guint8(tvb, offset); + + ep_strbuf_truncate(strbuf, 0); + + if (device_values != 0) { + for (j = 0; j < 5; j++) { + if (device_values & (0x01 << j)) { + ep_strbuf_append(strbuf, str_device_values[j]); + } + } + } else { + ep_strbuf_append(strbuf, " None"); + } + + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "%s:%s", + val_to_str_const(device_index, str_device_configuration, "Unknown"), + strbuf->str); + offset++; + length--; + device_index++; + } + break; + } + default: + { + break; + } + } +} + + +/*----------------------------------------------------------------------------- + AUDIO PADDED PATH - 4Ah (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_audio_padded_path(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset) +{ + proto_tree_add_item(tree, hf_ua3g_audio_padded_path_emission_padded_level, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_audio_padded_path_reception_padded_level, tvb, offset+1, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + KEY RELEASE ON / OFF - 41h (MESSAGE FROM THE SYSTEM) + AMPLIFIED HANDSET (BOOST) - 48h (MESSAGE FROM THE SYSTEM) + LOUDSPEAKER ON / OFF - 4Dh (MESSAGE FROM THE SYSTEM) + ANNOUNCE ON / OFF - 4Eh (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_on_off_level(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length, int hf_opcode) +{ + guint8 command; + const gchar* command_str; + + command = tvb_get_guint8(tvb, offset); + command_str = STR_ON_OFF(command); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", command_str); + + proto_tree_add_item(tree, hf_opcode, tvb, offset, 1, ENC_NA); + + if (length > 1) { + if (command == 0x01) { + proto_tree_add_item(tree, hf_ua3g_on_off_level_level_on_loudspeaker, tvb, offset+1, 1, ENC_NA); + } + } +} + + +/*----------------------------------------------------------------------------- + RING ON / OFF - 4Fh (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_ring(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, guint offset) +{ + guint8 command; + const gchar* command_str; + + command = tvb_get_guint8(tvb, offset); + command_str = STR_ON_OFF(command); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", command_str); + + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_command_ring, tvb, offset, 1, ENC_NA); + + if (command == 0x01) { + proto_tree_add_item(tree, hf_ua3g_ring_melody, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_ring_cadence, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_ring_speaker_level, tvb, offset+3, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_ring_beep_number, tvb, offset+4, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_ring_silent, tvb, offset+5, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_ring_progressive, tvb, offset+5, 1, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + UA DOWNLOAD PROTOCOL - 50h - Only for UA NOE (MESSAGE FROM THE TERMINAL AND FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static const value_string str_command_ua_dwl_protocol[] = { + {0x00, "Downloading Suggest"}, + {0x01, "Downloading Request"}, + {0x02, "Downloading Acknowledge"}, + {0x03, "Downloading Data"}, + {0x04, "Downloading End"}, + {0x05, "Downloading End Acknowledge"}, + {0x06, "Downloading ISO Checksum"}, + {0x07, "Downloading ISO Checksum Acknowledge"}, + {0, NULL} +}; + +static const value_string str_download_req_force_mode[] = { + {0x00, "System Accept All Refusals"}, + {0x01, "Force Software Lock"}, + {0, NULL} +}; +static const value_string str_download_req_item_id[] = { + {0x00, "Patches File"}, + {0x01, "Application Binary"}, + {0x02, "Datas Binary"}, + {0, NULL} +}; +static const value_string str_download_req_mode_selection_country[] = { + {0x00, "No Check"}, + {0x01, "For All Countries Except Chinese"}, + {0x02, "For Chinese"}, + {0, NULL} +}; + +static const value_string str_download_ack_status[] = { + {0x00, "Ok (Binary Item Downloading In \"Normal\" Progress)"}, + {0x01, "Hardware Failure: Flash Failure"}, + {0x02, "Not Enough Place To Store The Downloaded Binary"}, + {0x03, "Wrong Seq Number On Latest Received Download_Data Message"}, + {0x04, "Wrong Packet Number On Latest Received Download_Data Message"}, + {0x05, "Download Refusal Terminal (Validation Purpose)"}, + {0x06, "Download Refusal Terminal (Development Purpose)"}, + {0x10, "Download Refusal: Hardware Cause (Unknown Flash Device, Incompatible Hardware)"}, + {0x11, "Download Refusal: No Loader Available Into The Terminal"}, + {0x12, "Download Refusal: Software Lock"}, + {0x13, "Download Refusal: Wrong Parameter Into Download Request"}, + {0x20, "Wrong Packet Number On Latest Received Downloading_Data Message"}, + {0x21, "Compress Header Invalid"}, + {0x22, "Decompress Error"}, + {0x23, "Binary Header Invalid"}, + {0x24, "Binary Check Error: Flash Write Error Or Binary Is Invalid"}, + {0x25, "Error Already Signaled - No More Data Accepted"}, + {0x26, "No Downloading In Progress"}, + {0x27, "Too Many Bytes Received (More Than Size Given Into The Download_Req Message)"}, + {0xFF, "Undefined Error"}, + {0, NULL} +}; +static value_string_ext str_download_ack_status_ext = VALUE_STRING_EXT_INIT(str_download_ack_status); + +static const value_string str_download_end_ack_ok[] = { + {0x00, "Ok"}, + {0x01, "Hardware Failure: Flash Problems"}, + {0x02, "Not Enough Place To Store The Downloaded Binary"}, + {0, NULL} +}; + +static const value_string str_iso_checksum_ack_status[] = { + {0x00, "The Checksum Matches"}, + {0x25, "Error Detected And Already Signaled"}, + {0x30, "Checksum Error (All Bytes Received)"}, + {0x31, "Checksum Error (Bytes Missing)"}, + {0, NULL} +}; + +static const value_string str_mem_size[] = { + {0x00, "No Check"}, + {0x01, "128 Kbytes"}, + {0x02, "256 Kbytes"}, + {0x03, "512 Kbytes"}, + {0x04, "1 Mbytes"}, + {0x05, "2 Mbytes"}, + {0x06, "4 Mbytes"}, + {0x07, "8 Mbytes"}, + {0, NULL} +}; + + +static void +decode_ua_dwl_protocol(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length) +{ + guint8 command; + proto_tree *ua3g_body_tree = tree, *ua3g_param_tree; + proto_item *ua3g_param_item; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_ua_dwl_protocol, "Unknown")); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_command_ua_dwl_protocol, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (command) { + case 0x00: /* Downloading Suggest (MESSAGE FROM THE TERMINAL) */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_item_identifier, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_item_version_nc, tvb, offset+1, 2, ENC_BIG_ENDIAN); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_cause, tvb, offset+3, 1, ENC_NA); + break; + case 0x01: /* Downloading Request (MESSAGE FROM THE SYSTEM) */ + { + static const gchar *str_bin_info[] = { + "Uncompressed Binary", + "LZO Compressed Binary" + }; + + if (length > 7) { /* Not R1 */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_force_mode, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_item_identifier, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_item_version, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + if (length > 2) { /* Not R1 */ + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Files Included"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_files_inc_boot_binary, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_files_inc_loader_binary, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_files_inc_appli_binary, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_files_inc_data_binary, + tvb, offset, 1, ENC_NA); + offset++; + length--; + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Model Selection"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_model_selection_a, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_model_selection_b, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_model_selection_c, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_model_selection_country_ver, + tvb, offset, 1, ENC_NA); + offset++; + length--; + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Hardware Selection"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe1, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe2, + tvb, offset, 1, ENC_NA); + offset++; + length--; + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Memory Sizes Required"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_memory_sizes_flash, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_ua_dwl_protocol_memory_sizes_ext_ram, + tvb, offset, 1, ENC_NA); + offset++; + length--; + } else { /* R1 */ + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "Binary Information: %s, Country/Operator/CLient Identifier ?", + str_bin_info[tvb_get_guint8(tvb, offset) & 0x01]); + offset++; + length--; + } + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_binary_length, tvb, offset, 3, ENC_BIG_ENDIAN); + break; + } + case 0x02: /* Downloading Acknowledge (MESSAGE FROM THE TERMINAL) */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_packet_number, tvb, offset, 2, ENC_BIG_ENDIAN); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_download_ack_status, tvb, offset+2, 1, ENC_NA); + break; + case 0x03: /* Downloading Data (MESSAGE FROM THE SYSTEM) */ + { + int i = 1; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_packet_number, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + while (length > 0) { + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "Packet Number %3d: %d", i, tvb_get_guint8(tvb, offset)); + offset++; + length--; + i++; + } + break; + } + case 0x05: /* Downloading End Acknowledge (MESSAGE FROM THE TERMINAL) */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_packet_download_end_ack_ok_status, tvb, offset, 1, ENC_NA); + break; + case 0x06: /* Downloading Iso Checksum (MESSAGE FROM THE SYSTEM) */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_checksum, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x07: /* Downloading ISO Checksum Acknowledge (MESSAGE FROM THE TERMINAL) */ + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ua_dwl_protocol_acknowledge, tvb, offset, 1, ENC_NA); + break; + case 0x04: /* Downloading End (MESSAGE FROM THE SYSTEM) */ + default: + break; + } +} + + +/*----------------------------------------------------------------------------- + DIGIT DIALED - 03h (MESSAGE FROM THE SYSTEM) + ---------------------------------------------------------------------------*/ +static void +decode_digit_dialed(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, guint offset) +{ + proto_tree_add_item(tree, hf_ua3g_digit_dialed_digit_value, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SUBDEVICE_MSG - 04h (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static void +decode_subdevice_msg(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ + if (!tree) + return; + + proto_tree_add_item(tree, hf_ua3g_subdevice_msg_subdev_type, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_subdevice_msg_subdev_address, tvb, offset, 1, ENC_NA); + + proto_tree_add_item(tree, hf_ua3g_subdevice_msg_subdevice_opcode, tvb, offset+1, 1, ENC_NA); + + if (length > 2) { + proto_tree_add_item(tree, hf_ua3g_subdevice_msg_parameter_bytes, tvb, offset+2, length-2, ENC_NA); + } +} + + +/*----------------------------------------------------------------------------- + IP DEVICE ROUTING - 13h (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static const value_string str_command_cs_ip_device_routing[] = { + {0x00, "Init"}, + {0x01, "Incident"}, + {0x02, "Get Parameters Value Response"}, + {0x03, "QOS Ticket RSP"}, + {0, NULL} +}; + +static const value_string str_cs_ip_device_routing_vta_type[] = { + {0x20, "NOE A"}, + {0x21, "NOE B"}, + {0x22, "NOE C"}, + {0x23, "NOE D"}, + {0, NULL} +}; + +static const value_string str_cs_ip_device_routing_08_compressor[] = { + {0x00, "G.711 A-law"}, + {0x01, "G.711 mu-law"}, + {0x0F, "G.723.1 5.3kbps"}, + {0x10, "G.723.1 6.3kbps"}, + {0x11, "G.729A 8 kbps"}, + {0, NULL} +}; + +static const val64_string str_cs_ip_device_routing_0F_compressor[] = { + {0x00, "G.711 A-law"}, + {0x01, "G.711 mu-law"}, + {0x02, "G.723.1 6.3kbps"}, + {0x03, "G.729"}, + {0x04, "G.723.1 5.3kbps"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_03_parameter_id_vals[] = { + {0x01, "Date Of End Of Communication"}, + {0x02, "Node Number"}, + {0x03, "Ticket Protocol Version"}, + {0x06, "Equipment Type"}, + {0x08, "Local IP Address"}, + {0x09, "Distant IP Address"}, + {0x0A, "Local ID"}, + {0x0B, "Distant ID"}, + {0x0C, "Call Duration (second)"}, + {0x0D, "Local SSRC"}, + {0x0E, "Distant SSRC"}, + {0x0F, "Codec"}, + {0x10, "VAD"}, + {0x11, "ECE"}, + {0x12, "Voice Mode"}, + {0x13, "Transmitted Framing (ms)"}, + {0x14, "Received Framing (ms)"}, + {0x15, "Framing Changes"}, + {0x16, "Number Of RTP Packets Received"}, + {0x17, "Number Of RTP Packets Sent"}, + {0x18, "Number Of RTP Packets Lost"}, + {0x19, "Total Silence Detected (second)"}, + {0x1A, "Number Of SID Received"}, + {0x1B, "Delay Distribution"}, + {0x1C, "Maximum Delay (ms)"}, + {0x1D, "Number Of DTMF Received"}, + {0x1E, "Consecutive BFI"}, + {0x1F, "BFI Distribution"}, + {0x20, "Jitter Depth Distribution"}, + {0x21, "Number Of ICMP Host Unreachable"}, + {0x26, "Firmware Version"}, + {0x29, "DSP Framing (ms)"}, + {0x2A, "Transmitter SID"}, + {0x2D, "Minimum Delay (ms)"}, + {0x2E, "802.1 Q Used"}, + {0x2F, "802.1p Priority"}, + {0x30, "VLAN Id"}, + {0x31, "DiffServ"}, + {0x3D, "200 ms BFI Distribution"}, + {0x3E, "Consecutive RTP Lost"}, + {0, NULL} +}; +static value_string_ext cs_ip_device_routing_03_parameter_id_vals_ext = VALUE_STRING_EXT_INIT(cs_ip_device_routing_03_parameter_id_vals); + +static const value_string cs_ip_device_routing_03_parameter_id_tab_vals[] = { + {0x1B, "Range: Value"}, + {0x1F, "Range: Value"}, + {0x20, "Jitter: Value"}, + {0x3D, "Contents: Value"}, + {0x3E, "Contents: Value"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_cmd03_first_byte_vals[] = { + {0x01, "IP-Phone"}, + {0x02, "Appli-PC"}, + {0x03, "Coupler OmniPCX Enterprise"}, + {0x04, "Coupler OmniPCX Office"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_cmd03_second_byte_vals[] = { + {0x0101, "IP-Phone V2"}, + {0x0102, "NOE-IP"}, + {0x0200, "4980 Softphone (PCMM2)"}, + {0x0201, "WebSoftphoneIP"}, + {0x0300, "INTIP"}, + {0x0301, "GD"}, + {0x0302, "eVA"}, + {0, NULL} +}; + +static const val64_string cs_ip_device_routing_cmd03_voice_mode_vals[] = { + {0x50, "Idle"}, + {0x51, "Handset"}, + {0x52, "Group Listening"}, + {0x53, "On Hook Dial"}, + {0x54, "Handsfree"}, + {0x55, "Headset"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_delay_distribution_range_vals[] = { + {0, "0-40 "}, + {1, "40-80 "}, + {2, "80-150 "}, + {3, "150-250 "}, + {4, "250 and +"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_consecutive_bfi_range_vals[] = { + {0, "0"}, + {1, "1"}, + {2, "2"}, + {3, "3"}, + {4, "4"}, + {5, "5"}, + {6, "5"}, + {7, "7"}, + {8, "8"}, + {9, "9"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_bfi_distribution_range_vals[] = { + {0, "0 "}, + {1, "0-1 "}, + {2, "1-2 "}, + {3, "2-3 "}, + {4, "3 and +"}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_200ms_bfi_distribution_range_vals[] = { + {0, "< 10 % "}, + {1, "< 20 % "}, + {2, "< 40 % "}, + {3, "< 60 % "}, + {4, ">= 60 % "}, + {0, NULL} +}; + +static const value_string cs_ip_device_routing_consecutive_rtp_lost_range_vals[] = { + {0, "1 "}, + {1, "2 "}, + {2, "3 "}, + {3, "4 "}, + {4, "5 and more"}, + {0, NULL} +}; + +static void +decode_cs_ip_device_routing(proto_tree *tree _U_, tvbuff_t *tvb, + packet_info *pinfo, guint offset, guint length) +{ + guint8 command; + proto_tree *ua3g_body_tree = tree, *ua3g_param_tree; + proto_item *ua3g_param_item; + int i, parameter_id, parameter_length; + + command = tvb_get_guint8(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_cs_ip_device_routing, "Unknown")); + + if (!ua3g_body_tree) + return; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_ip_cs, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (command) { + case 0x00: + proto_tree_add_item(ua3g_body_tree, hf_ua3g_cs_ip_device_routing_cmd00_vta_type, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_cs_ip_device_routing_cmd00_characteristic_number, tvb, offset+1, 1, ENC_NA); + break; + case 0x01: + { + int j = 0; + if (length == 1) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_cs_ip_device_routing_cmd01_incident_0, tvb, offset, 1, ENC_NA); + } else { + while (length >0) { + j++; + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "Parameter %d Identifier: %d", + j, tvb_get_guint8(tvb, offset)); + offset++; + length--; + } + } + break; + } + case 0x02: + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_guint8(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter, tvb, offset, + parameter_id, parameter_length + 2, + "%s", val_to_str_const(parameter_id, ip_device_routing_cmd_get_param_req_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_length, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x00: /* Firmware Version */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_firmware_version, tvb, offset, 2, ENC_BIG_ENDIAN); + break; + case 0x01: /* Firmware Version */ + case 0x02: /* DHCP IP Address */ + case 0x03: /* Local IP Address */ + case 0x04: /* Subnetwork Mask */ + case 0x05: /* Router IP Address */ + case 0x06: /* TFTP IP Address */ + case 0x07: /* Main CPU Address */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x08: /* Default Codec */ + { + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_bytes, tvb, offset, parameter_length, ENC_NA); + } + break; + } + case 0x09: /* Ethernet Drivers Config */ + { + if (parameter_length == 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_speed, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_duplex, tvb, offset+1, 1, ENC_NA); + } else if (parameter_length == 4) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_speed, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_duplex, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_speed, tvb, offset+2, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_duplex, tvb, offset+3, 1, ENC_NA); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + case 0x0A: /* MAC Address */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_mac_address, tvb, offset, 6, ENC_NA); + break; + default: + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd02_parameter_value, tvb, offset, parameter_length, ENC_NA); + } + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + case 0x03: + { + int framing_rtp = 0; + + while (length > 0) { + parameter_id = tvb_get_guint8(tvb, offset); + parameter_length = tvb_get_ntohs(tvb, offset + 1); + + ua3g_param_item = proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter, tvb, offset, + parameter_length+3, parameter_id, "%s", + val_to_str_const(parameter_id, cs_ip_device_routing_03_parameter_id_tab_vals, "Unknown")); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_length, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + if (parameter_length > 0) { + switch (parameter_id) { + case 0x06: /* Type Of Equipment */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip1, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip2, tvb, offset, 2, ENC_BIG_ENDIAN); + break; + case 0x08: /* Local IP Address */ + case 0x09: /* Distant IP Address */ + case 0x26: /* Firmware Version */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + break; + case 0x0A: + case 0x0B: + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_string, tvb, offset, parameter_length, ENC_NA|ENC_ASCII); + break; + case 0x0F: /* Default Codec */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_default_codec, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_default_codec, tvb, offset, 8, ENC_BIG_ENDIAN); + /* XXX - add as expert info ep_strbuf_append(strbuf, "Parameter Value Too Long (more than 64 bits)"); */ + } + break; + case 0x10: /* VAD */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_vad, tvb, offset, 1, ENC_NA); + break; + case 0x11: /* ECE */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_ece, tvb, offset, 1, ENC_NA); + break; + case 0x12: /* Voice Mode */ + if (parameter_length <= 8) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_voice_mode, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + } else { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_voice_mode, tvb, offset, 8, ENC_BIG_ENDIAN); + /* XXX - add as expert info ep_strbuf_append(strbuf, "Parameter Value Too Long (more than 64 bits)"); */ + } + break; + case 0x1B: /* Delay Distribution */ + for (i = 0; i < parameter_length; i += 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_delay_distribution, tvb, offset+i, 2, ENC_BIG_ENDIAN); + } + break; + case 0x1E: /* Consecutive BFI */ + for (i = 0; i < parameter_length; i += 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_bfi, tvb, offset+i, 2, ENC_BIG_ENDIAN); + } + break; + case 0x1F: /* BFI Distribution */ + for (i = 0; i < parameter_length; i += 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution, tvb, offset+i, 2, ENC_BIG_ENDIAN); + } + break; + case 0x20: /* Jitter Depth Distribution */ + for (i = 0; i < parameter_length / 4; i+=4) { + proto_tree_add_uint_format(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_jitter_depth_distribution, + tvb, offset+i, 4, tvb_get_ntohl(tvb, offset+i), "Jitter Depth Distribution: +/- %3d ms: %d", + ((2 * i/4) + 1) * framing_rtp / 2, tvb_get_ntohl(tvb, offset+i)); + } + break; + case 0x2E: /* 802.1 Q Used */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021Q_used, tvb, offset, 1, ENC_NA); + break; + case 0x2F: /* 802.1p Priority */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021P_priority, tvb, offset, 1, ENC_NA); + break; + case 0x30: /* VLAN Id */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_vlan_id, tvb, offset, 2, ENC_BIG_ENDIAN); + break; + case 0x31: /* DiffServ */ + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_diffserv, tvb, offset, 1, ENC_NA); + break; + case 0x3D: /* 200 ms BFI Distribution */ + for (i = 0; i < parameter_length; i += 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution_200ms, tvb, offset+i, 2, ENC_BIG_ENDIAN); + } + break; + case 0x3E: /* Consecutive RTP Lost */ + for (i = 0; i < parameter_length; i += 2) { + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_rtp_lost, tvb, offset+i, 2, ENC_BIG_ENDIAN); + } + break; + case 0x14: /* Received Framing (ms) */ + { + /* XXX: What is the point of this assignment? */ + framing_rtp = tvb_get_guint8(tvb, offset + 3); + /* FALLTHROUGH */ + } + case 0x01: /* Date Of End Of Communication */ + case 0x02: /* Node Number */ + case 0x03: /* Ticket Protocol Version */ + case 0x0C: /* Call Duration (second) */ + case 0x0D: /* Local SSRC */ + case 0x0E: /* Distant SSRC */ + case 0x13: /* Transmitted Framing (ms) */ + case 0x15: /* Framing Changes */ + case 0x16: /* Number Of RTP Packets Received */ + case 0x17: /* Number Of RTP Packets Sent */ + case 0x18: /* Number Of RTP Packets Lost */ + case 0x19: /* Total Silence Detected (second) */ + case 0x1A: /* Number Of SID Received */ + case 0x1C: /* Maximum Delay (ms) */ + case 0x1D: /* Number Of DTMF Received */ + case 0x21: /* Number Of ICMP Host Unreachable */ + case 0x29: /* DSP Framing (ms) */ + case 0x2A: /* Transmitter SID */ + case 0x2D: /* Minimum Delay (ms) */ + default: + proto_tree_add_item(ua3g_param_tree, hf_ua3g_cs_ip_device_routing_cmd03_parameter_uint, tvb, offset, parameter_length, ENC_BIG_ENDIAN); + break; + } + + offset += parameter_length; + length -= parameter_length; + } + } + break; + } + default: + break; + } +} + + +/*----------------------------------------------------------------------------- + UNSOLICITED MESSAGE - 9Fh/1Fh (MESSAGE FROM THE TERMINAL) + VERSION RESPONSE - 21h (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static const value_string str_command_unsolicited_msg[] = { + {0x00, "Hardware Reset Acknowledge"}, + {0x01, "Software Reset Acknowledge"}, + {0x02, "Illegal Command Received"}, + {0x05, "Subdevice Down"}, + {0x06, "Segment Failure"}, + {0x07, "UA Device Event"}, + {0, NULL} +}; + +static const value_string str_unsolicited_msg_vta_type[] = { + {0x03, "4035"}, + {0x04, "4020"}, + {0x05, "4010"}, + {0x20, "NOE A"}, + {0x21, "NOE B"}, + {0x22, "NOE C"}, + {0x23, "NOE D"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_other_info_1[] = { + {0x00, "Link Is TDM"}, + {0x01, "Link Is IP"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_other_info_2[] = { + {0x00, "Download Allowed"}, + {0x01, "Download Refused"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_config_ip[] = { + {0x00, "Export Binary (No Thales)"}, + {0x01, "Full Binary (Thales)"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_config_chip[] = { + {0x01, "Ivanoe 1"}, + {0x02, "Ivanoe 2"}, + {0x03, "Reserved"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_config_flash[] = { + {0x00, "No Flash"}, + {0x01, "128 Kbytes"}, + {0x02, "256 Kbytes"}, + {0x03, "512 Kbytes"}, + {0x04, "1 Mbytes"}, + {0x05, "2 Mbytes"}, + {0x06, "4 Mbytes"}, + {0x07, "8 Mbytes"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_config_ram[] = { + {0x00, "No External RAM"}, + {0x01, "128 Kbytes"}, + {0x02, "256 Kbytes"}, + {0x03, "512 Kbytes"}, + {0x04, "1 Mbytes"}, + {0x05, "2 Mbytes"}, + {0x06, "4 Mbytes"}, + {0x07, "8 Mbytes"}, + {0, NULL} +}; + +static const value_string str_unsolicited_msg_subtype[] = { + {0x03, "2x40"}, + {0x04, "1x20"}, + {0x05, "1x20"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_generation[] = { + {0x02, "3"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_design[] = { + {0x00, "Alpha"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_vta_type[] = { + {0x03, "MR2 (4035)"}, + {0x05, "VLE (4010)"}, + {0x07, "LE (4020)"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_design[] = { + {0x06, "Alpha"}, + {0, NULL} +}; +static const value_string str_unsolicited_msg_hard_subtype[] = { + {0x06, "2x40"}, + {0x07, "1x20"}, + {0x08, "1x20"}, + {0, NULL} +}; + +static void +decode_unsolicited_msg(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, + guint offset, guint length, guint8 opcode) +{ + guint8 command; + proto_tree *ua3g_body_tree = tree, *ua3g_param_tree; + proto_item *ua3g_param_item; + + command = tvb_get_guint8(tvb, offset); + + if (opcode != 0x21) { + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(command, str_command_unsolicited_msg, "Unknown")); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_command_unsolicited_msg, tvb, offset, 1, ENC_NA); + offset++; + length--; + } else { + command = 0xFF; /* Opcode = 0x21 */ + } + + switch (command) + { + case 0x00: /* Hardware Reset Acknowledge */ + case 0x01: /* Software Reset Acknowledge */ + case 0xFF: /* Opcode = 0x21 : Version Response */ + { + int link, vta_type; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_device_type, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_firmware_version, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + length -= 2; + + if (opcode != 0x21) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_self_test_result, tvb, offset, 1, ENC_NA); + offset++; + length--; + } + + vta_type = tvb_get_guint8(tvb, offset); + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_vta_type, tvb, offset, 1, ENC_NA); + offset++; + length--; + + switch (vta_type) + { + case 0x03: + case 0x04: + case 0x05: + { + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Characteristic Number"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_char_num_vta_subtype, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_char_num_generation, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_char_num_design, + tvb, offset, 1, ENC_NA); + offset++; + length--; + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_other_information, tvb, offset, 1, ENC_NA); + offset++; + length--; + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Hardware Configuration"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_vta_type, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_design, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_subtype, + tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (opcode != 0x21) { + proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, + "Hook Status/BCM Version: %s Hook", + STR_ON_OFF(tvb_get_guint8(tvb, offset))); + offset++; + length--; + + } + break; + } + case 0x20: + case 0x21: + case 0x22: + case 0x23: + default: + { + link = tvb_get_guint8(tvb, offset); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_other_information_1, tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (link == 0x00) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_hardware_version, tvb, offset, 1, ENC_NA); + offset++; + length--; + + ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Hardware Configuration"); + ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_hard_config_chip, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_hard_config_flash, + tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_param_tree, hf_ua3g_unsolicited_msg_hardware_config_config_ram, + tvb, offset, 1, ENC_NA); + offset++; + length--; + } else { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_other_information_2, tvb, offset, 1, ENC_NA); + offset++; + length--; + + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_hardware_config_hard_config_ip, + tvb, offset, 1, ENC_NA); + offset++; + length--; + } + + if (opcode != 0x21) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_hook_status, + tvb, offset, 1, ENC_NA); + offset++; + length--; + + if (length > 0) { + if (link == 0x00) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_firmware_datas_patch_version, + tvb, offset, 2, ENC_BIG_ENDIAN); + if (length > 2) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_firmware_version_loader, tvb, + offset+2, 2, ENC_BIG_ENDIAN); + } + } else { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_datas_version, tvb, offset, 2, ENC_BIG_ENDIAN); + + if (length > 2) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_firmware_version_bootloader, + tvb, offset+2, 2, ENC_BIG_ENDIAN); + } + } + } + } + break; + } + } + break; + } + case 0x02: /* Illegal Command Received */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_opcode_of_bad_command, tvb, offset, 1, ENC_NA); + + if (length > 1) { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_next_byte_of_bad_command, tvb, offset+1, length-1, ENC_NA); + } + break; + } + case 0x05: /* Subdevice Down */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_subdevice_address, tvb, offset, 1, ENC_NA); + break; + } + case 0x06: /* Segment Failure */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_segment_failure_t, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_segment_failure_num, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_segment_failure_s, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_segment_failure_l, tvb, offset, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_opcode_bad_segment, tvb, offset+1, 1, ENC_NA); + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_next_byte_of_bad_segment, tvb, offset+2, 1, ENC_NA); + break; + } + case 0x07: /* UA Device Event */ + { + proto_tree_add_item(ua3g_body_tree, hf_ua3g_unsolicited_msg_device_event, tvb, offset, 1, ENC_NA); + break; + } + default: + { + break; + } + } +} + + +/*----------------------------------------------------------------------------- + NON-DIGIT KEY PUSHED - 20h (MESSAGE FROM THE TERMINAL) + DIGIT KEY RELEASED - 26h (MESSAGE FROM THE TERMINAL) + KEY RELEASED - 2Ah (MESSAGE FROM THE TERMINAL) + TM KEY PUSHED - 2Dh (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static void +decode_key_number(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint length) +{ +#if 0 + proto_tree *ua3g_body_tree; + static const value_string str_first_parameter[] = { + {0x01, "Production Test Command"}, + {0x06, "Reserved For Compatibility"}, + {0x3B, "Volume"}, + {0x42, "Subdevice Address"}, + {0, NULL} + }; +#endif + + if (!tree) + return; + + if (length > 0) { + proto_tree_add_text(tree, tvb, offset, length, + "Key Number: Row %d, Column %d", + (tvb_get_guint8(tvb, offset) & 0xF0), (tvb_get_guint8(tvb, offset) & 0x0F)); + } +} + + +/*----------------------------------------------------------------------------- + I'M HERE - 22h - Only for UA NOE (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static void +decode_i_m_here(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, guint offset) +{ + proto_tree_add_item(tree, hf_ua3g_i_m_here_id_code, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + RESPONSE STATUS INQUIRY - 23h (MESSAGE FROM THE TERMINAL) + SPECIAL KEY STATUS - 29h (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static const value_string str_special_key_parameters[] = { + {0x00, "Not Received Default In Effect"}, + {0x02, "Downloaded Values In Effect"}, + {0, NULL} +}; + +const true_false_string tfs_released_pressed = { "Released", "Pressed" }; + +static void +decode_special_key(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, + guint offset, guint8 opcode) +{ + if (!tree) + return; + + if (opcode == 0x23) { + proto_tree_add_text(tree, tvb, offset, 1, + "Parameters Received for DTMF: %s", + val_to_str_const((tvb_get_guint8(tvb, offset) & 0x02), str_special_key_parameters, "Unknown")); + proto_tree_add_text(tree, tvb, offset, 1, + "Hookswitch Status: %shook", + STR_ON_OFF(tvb_get_guint8(tvb, offset) & 0x01)); + offset++; + } + + proto_tree_add_item(tree, hf_ua3g_special_key_shift, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_ctrl, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_alt, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_cmd, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_shift_prime, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_ctrl_prime, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_alt_prime, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_ua3g_special_key_cmd_prime, tvb, offset, 1, ENC_NA); +} + + +/*----------------------------------------------------------------------------- + SUBDEVICE STATE ENQUIRY - 24h (MESSAGE FROM THE TERMINAL) + ---------------------------------------------------------------------------*/ +static void +decode_subdevice_state(proto_tree *tree, tvbuff_t *tvb, + packet_info *pinfo _U_, guint offset) +{ + guint8 info; + int i; + + for (i = 0; i <= 7; i++) { + info = tvb_get_guint8(tvb, offset); + proto_tree_add_text(tree, tvb, offset, 1, + "Subdevice %d State: %d", + i, info & 0x0F); + i++; + proto_tree_add_text(tree, tvb, offset, 1, + "Subdevice %d State: %d", + i, (info & 0xF0) >> 4); + offset++; + } +} + + +/*----------------------------------------------------------------------------- + UA3G DISSECTOR + ---------------------------------------------------------------------------*/ +static int +dissect_ua3g(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + gint offset = 0; + proto_item *ua3g_item, *ua3g_body_item; + proto_tree *ua3g_tree, *ua3g_body_tree; + gint length; + guint8 opcode; + const gchar* opcode_str; + e_ua_direction message_direction = (e_ua_direction)data; + + ua3g_item = proto_tree_add_item(tree, proto_ua3g, tvb, 0, -1, ENC_NA); + ua3g_tree = proto_item_add_subtree(ua3g_item, ett_ua3g); + + /* Length of the UA Message */ + length = tvb_get_letohs(tvb, offset); + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " - UA3G Message:"); + + proto_tree_add_uint(ua3g_tree, hf_ua3g_length, tvb, offset, 2, length); + offset += 2; + + /* Opcode of the UA Message */ + opcode = tvb_get_guint8(tvb, offset); + if (opcode != 0x9f) + opcode = (opcode & 0x7f); + + /* Useful for a research in wireshark */ + if (message_direction == SYS_TO_TERM) { + proto_tree_add_uint(ua3g_tree, hf_ua3g_opcode_sys, tvb, offset, 1, opcode); + opcode_str = val_to_str_ext_const(opcode, &opcodes_vals_sys_ext, "Unknown"); + } else { + proto_tree_add_uint(ua3g_tree, hf_ua3g_opcode_term, tvb, offset, 1, opcode); + opcode_str = val_to_str_ext_const(opcode, &opcodes_vals_term_ext, "Unknown"); + } + + offset++; + length--; + + /* add text to the frame "INFO" column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " %s", opcode_str); + + proto_item_append_text(ua3g_item, ", %s", opcode_str); + + ua3g_body_item = proto_tree_add_text(ua3g_tree, tvb, offset, length, "UA3G Body"); + ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body); + + if (message_direction == SYS_TO_TERM) { + switch (opcode) { + case SC_PRODUCTION_TEST: /* 0x01 */ + { + decode_with_one_parameter(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_opcode_production_test); + break; + } + case SC_SUBDEVICE_RESET: /* 0x06 */ + { + decode_with_one_parameter(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_opcode_subservice_reset); + break; + } + case SC_ARE_YOU_THERE: /* 0x2B */ + { + decode_with_one_parameter(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_opcode_are_you_there); + break; + } + case SC_SET_SPEAKER_VOL: /* 0x3B */ + { + decode_with_one_parameter(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_opcode_set_speaker_vol); + break; + } + case SC_TRACE_ON: /* 0x42 */ + { + decode_with_one_parameter(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_opcode_trace_on); + break; + } + case SC_SUBDEVICE_ESCAPE: /* 0x02 */ + { + decode_subdevice_escape(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SOFT_RESET: /* 0x03 */ + { + decode_software_reset(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_IP_PHONE_WARMSTART: /* 0x04 */ + { + decode_ip_phone_warmstart(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SUPER_MSG: /* 0x0B */ + case SC_SUPER_MSG_2: /* 0x17 */ + { + decode_super_msg(ua3g_body_tree, tvb, pinfo, offset, length, opcode); + break; + } + case SC_SEGMENT_MSG: /* 0x0C */ + { + decode_segment_msg(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_IP_DEVICE_ROUTING: /* 0x13 */ + { + decode_ip_device_routing(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_DEBUG_IN_LINE: /* 0x18 */ + { + decode_debug_in_line(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_LED_COMMAND: /* 0x21 */ + { + decode_led_command(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_LCD_LINE_1_CMD: /* 0x27 */ + case SC_LCD_LINE_2_CMD: /* 0x28 */ + { + decode_lcd_line_cmd(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_MAIN_VOICE_MODE: /* 0x29 */ + { + decode_main_voice_mode(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SUBDEVICE_METASTATE: /* 0x2C */ + { + decode_subdevice_metastate(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_DWL_DTMF_CLCK_FORMAT: /* 0x30 */ + { + decode_dwl_dtmf_clck_format(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SET_CLCK: /* 0x31 */ + { + decode_set_clck(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_VOICE_CHANNEL: /* 0x32 */ + { + decode_voice_channel(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_EXTERNAL_RINGING: /* 0x33 */ + { + decode_external_ringing(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_LCD_CURSOR: /* 0x35 */ + { + decode_lcd_cursor(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_DWL_SPECIAL_CHAR: /* 0x36 */ + { + decode_dwl_special_char(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SET_CLCK_TIMER_POS: /* 0x38 */ + { + decode_set_clck_timer_pos(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_SET_LCD_CONTRAST: /* 0x39 */ + { + decode_set_lcd_contrast(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_BEEP: /* 0x3C */ + { + decode_beep(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_SIDETONE: /* 0x3D */ + { + decode_sidetone(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_RINGING_CADENCE: /* 0x3E */ + { + decode_ringing_cadence(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_MUTE: /* 0x3F */ + { + decode_mute(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_FEEDBACK: /* 0x40 */ + { + decode_feedback(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_READ_PERIPHERAL: /* 0x44 */ + case SC_WRITE_PERIPHERAL: /* 0x45 */ + { + decode_r_w_peripheral(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_ICON_CMD: /* 0x47 */ + { + decode_icon_cmd(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_AUDIO_CONFIG: /* 0x49 */ + { + decode_audio_config(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case SC_AUDIO_PADDED_PATH: /* 0x4A */ + { + decode_audio_padded_path(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_KEY_RELEASE: /* 0x41 */ + { + decode_on_off_level(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_command_key_release); + break; + } + case SC_AMPLIFIED_HANDSET: /* 0x48 */ + { + decode_on_off_level(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_command_amplified_handset); + break; + } + case SC_LOUDSPEAKER: /* 0x4D */ + { + decode_on_off_level(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_command_loudspeaker); + break; + } + case SC_ANNOUNCE: /* 0x4E */ + { + decode_on_off_level(ua3g_body_tree, tvb, pinfo, offset, length, hf_ua3g_command_announce); + break; + } + case SC_RING: /* 0x4F */ + { + decode_ring(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case SC_UA_DWL_PROTOCOL: /* 0x50 */ + { + decode_ua_dwl_protocol(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + /* Case for UA3G message with only opcode (No body) */ + case SC_NOP: /* 0x00 */ + case SC_HE_ROUTING: /* 0x05 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_LOOPBACK_ON: /* 0x07 */ + case SC_LOOPBACK_OFF: /* 0x08 */ + case SC_VIDEO_ROUTING: /* 0x09 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_REMOTE_UA_ROUTING: /* 0x0D NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_VERY_REMOTE_UA_ROUTING: /* 0x0E NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_OSI_ROUTING: /* 0x0F NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_ABC_A_ROUTING: /* 0x11 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_IBS_ROUTING: /* 0x12 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_M_REFLEX_HUB_ROUTING: /* 0x14 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case SC_START_BUZZER: /* 0x22 */ + case SC_STOP_BUZZER: /* 0x23 */ + case SC_ENABLE_DTMF: /* 0x24 */ + case SC_DISABLE_DTMF: /* 0x25 */ + case SC_CLEAR_LCD_DISP: /* 0x26 */ + case SC_VERSION_INQUIRY: /* 0x2A */ + case SC_VTA_STATUS_INQUIRY: /* 0x2D */ + case SC_SUBDEVICE_STATE: /* 0x2E */ + case SC_AUDIO_IDLE: /* 0x3A */ + case SC_TRACE_OFF: /* 0x43 */ + case SC_ALL_ICONS_OFF: /* 0x46 */ + case SC_RELEASE_RADIO_LINK: /* 0x4B */ + case SC_DECT_HANDOVER: /* 0x4C NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + default: + { + break; + } + } + } + if (message_direction == TERM_TO_SYS) { + switch (opcode) { + case CS_DIGIT_DIALED: /* 0x03 */ + { + decode_digit_dialed(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case CS_SUBDEVICE_MSG: /* 0x04 */ + { + decode_subdevice_msg(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_SUPER_MSG: /* 0x0B */ + case CS_SUPER_MSG_2: /* 0x17 */ + { + decode_super_msg(ua3g_body_tree, tvb, pinfo, offset, length, opcode); + break; + } + case CS_SEGMENT_MSG: /* 0x0C */ + { + decode_segment_msg(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_IP_DEVICE_ROUTING: /* 0x13 */ + { + decode_cs_ip_device_routing(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_DEBUG_IN_LINE: /* 0x18 */ + { + decode_debug_in_line(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_NON_DIGIT_KEY_PUSHED: /* 0x20 Key translation not sure */ + case CS_DIGIT_KEY_RELEASED: /* 0x26 Key translation not sure */ + case CS_KEY_RELEASED: /* 0x2A */ + case CS_TM_KEY_PUSHED: /* 0x2D Key translation not sure */ + { + decode_key_number(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_UNSOLICITED_MSG: /* 0x9F (0x1F) */ + case CS_VERSION_RESPONSE: /* 0x21 */ + { + decode_unsolicited_msg(ua3g_body_tree, tvb, pinfo, offset, length, opcode); + break; + } + case CS_I_M_HERE: /* 0x22 */ + { + decode_i_m_here(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case CS_RSP_STATUS_INQUIRY: /* 0x23 */ + case CS_SPECIAL_KEY_STATUS: /* 0x29 */ + { + decode_special_key(ua3g_body_tree, tvb, pinfo, offset, opcode); + break; + } + case CS_SUBDEVICE_STATE: /* 0x24 */ + { + decode_subdevice_state(ua3g_body_tree, tvb, pinfo, offset); + break; + } + case CS_PERIPHERAL_CONTENT: /* 0x2B */ + { + decode_r_w_peripheral(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + case CS_UA_DWL_PROTOCOL: /* 0x50 */ + { + decode_ua_dwl_protocol(ua3g_body_tree, tvb, pinfo, offset, length); + break; + } + /* Case for UA3G message with only opcode (No body) */ + case CS_NOP_ACK: /* 0x00 */ + case CS_HANDSET_OFFHOOK: /* 0x01 */ + case CS_HANDSET_ONHOOK: /* 0x02 */ + case CS_HE_ROUTING: /* 0x05 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_LOOPBACK_ON: /* 0x06 */ + case CS_LOOPBACK_OFF: /* 0x07 */ + case CS_VIDEO_ROUTING: /* 0x09 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_WARMSTART_ACK: /* 0x0A */ + case CS_REMOTE_UA_ROUTING: /* 0x0D NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_VERY_REMOTE_UA_R: /* 0x0E NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_OSI_ROUTING: /* 0x0F NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_ABC_A_ROUTING: /* 0x11 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_IBS_ROUTING: /* 0x12 NOT DECODED - No description in 3AK 29000 0556 DSZZA */ + case CS_TRACE_ON_ACK: /* 0x27 */ + case CS_TRACE_OFF_ACK: /* 0x28 */ + default: + { + break; + } + } + } + + return tvb_length(tvb); +} + + +/*----------------------------------------------------------------------------- + DISSECTORS REGISTRATION FUNCTIONS + ---------------------------------------------------------------------------*/ +void +proto_register_ua3g(void) +{ + static hf_register_info hf[] = + { + { &hf_ua3g_length, + { "Length", "ua3g.length", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_sys, + { "Opcode", "ua3g.opcode", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, &opcodes_vals_sys_ext, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_term, + { "Opcode", "ua3g.opcode", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, &opcodes_vals_term_ext, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_production_test, + { "Production Test Command", "ua3g.production_test", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_subservice_reset, + { "Reserved For Compatibility", "ua3g.subservice_reset", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_are_you_there, + { "Temporization", "ua3g.are_you_there", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_set_speaker_vol, + { "Volume", "ua3g.set_speaker_vol", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_opcode_trace_on, + { "Subdevice Address", "ua3g.trace_on", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip, + { "IP Device Routing", "ua3g.ip", + FT_UINT8, BASE_HEX, VALS(str_command_ip_device_routing), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip_cs, + { "IP Device Routing", "ua3g.ip.cs", + FT_UINT8, BASE_HEX, VALS(str_command_cs_ip_device_routing), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_led, + { "Led Command", "ua3g.command.led", + FT_UINT8, BASE_HEX, VALS(str_command_led), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_lcd_line, + { "LCD Line Command", "ua3g.command.lcd_line", + FT_UINT8, BASE_HEX, VALS(str_command_lcd_line), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_main_voice_mode, + { "Voice Mode", "ua3g.command.main_voice_mode", + FT_UINT8, BASE_HEX, VALS(str_main_voice_mode), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_set_clck, + { "Set Clock", "ua3g.command.set_clck", + FT_UINT8, BASE_HEX, VALS(str_command_set_clck), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_external_ringing_command, + { "External Ringing Command", "ua3g.command.external_ringing", + FT_UINT8, BASE_HEX, VALS(str_ext_ring_cmd), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_lcd_cursor, + { "Cursor", "ua3g.lcd_cursor", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x02, + NULL, HFILL } + }, + { &hf_ua3g_command_beep, + { "Beep", "ua3g.command.beep", + FT_UINT8, BASE_HEX, VALS(str_command_beep), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_sidetone, + { "Sidetone", "ua3g.command.sidetone", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_mute, + { "Microphone", "ua3g.command.mute", + FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x01, + NULL, HFILL } + }, + { &hf_ua3g_command_feedback, + { "Feedback", "ua3g.command.feedback", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_audio_config, + { "Audio Config", "ua3g.command.audio_config", + FT_UINT8, BASE_HEX, VALS(str_command_audio_config), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_key_release, + { "Key Release", "ua3g.command.key_release", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_amplified_handset, + { "Amplified Handset (Boost)", "ua3g.command.amplified_handset", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_loudspeaker, + { "Loudspeaker", "ua3g.command.loudspeaker", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_announce, + { "Announce", "ua3g.command.announce", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_ring, + { "Ring", "ua3g.command.ring", + FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_ua_dwl_protocol, + { "UA Download Protocol", "ua3g.command.ua_dwl_protocol", + FT_UINT8, BASE_HEX, VALS(str_command_ua_dwl_protocol), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_command_unsolicited_msg, + { "Unsolicited Message", "ua3g.command.unsolicited_msg", + FT_UINT8, BASE_HEX, VALS(str_command_unsolicited_msg), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip_device_routing_stop_rtp_parameter, + { "Parameter", "ua3g.ip.stop_rtp.parameter", + FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_stop_rtp_vals), 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip_device_routing_stop_rtp_parameter_length, + { "Length", "ua3g.ip.stop_rtp.parameter.length", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip_device_routing_stop_rtp_parameter_value_num, + { "Value", "ua3g.ip.stop_rtp.parameter.value.num", + FT_UINT64, BASE_HEX, NULL, 0x00, + NULL, HFILL } + }, + { &hf_ua3g_ip_device_routing_stop_rtp_parameter_value_bytes, + { "Value", "ua3g.ip.stop_rtp.parameter.value.bytes", + FT_BYTES, BASE_NONE, NULL, 0x00, + NULL, HFILL } + }, + /* Generated from convert_proto_tree_add_text.pl */ + { &hf_ua3g_subdevice_address, { "Subdevice Address", "ua3g.subdevice.address", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL }}, + { &hf_ua3g_subdevice_opcode, { "Subdevice Opcode", "ua3g.subdevice.opcode", FT_UINT8, BASE_HEX, NULL, 0x7F, NULL, HFILL }}, + { &hf_ua3g_subdevice_parameter_bytes, { "Parameter Bytes", "ua3g.subdevice.parameter_bytes", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_software_reset, { "Software Reset", "ua3g.software_reset", FT_UINT8, BASE_DEC, VALS(software_reset_verswitch_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_phone_warmstart, { "IP Phone Warmstart", "ua3g.ip_phone_warmstart", FT_UINT8, BASE_DEC, VALS(str_command_ip_phone_warmstart), 0x0, NULL, HFILL }}, + { &hf_ua3g_super_msg_length, { "Length", "ua3g.super_msg.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_super_msg_data, { "Data", "ua3g.super_msg.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_segment_msg_num_remaining, { "Number Of Remaining Segments", "ua3g.segment_msg.num_remaining", FT_UINT8, BASE_DEC, NULL, 0x7F, NULL, HFILL }}, + { &hf_ua3g_segment_msg_length, { "Length", "ua3g.segment_msg.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_segment_message_data, { "Segment Message Data", "ua3g.segment_message.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter, { "Parameter", "ua3g.ip.reset.parameter", FT_UINT8, BASE_DEC, VALS(ip_device_routing_cmd_reset_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_length, { "Length", "ua3g.ip.reset.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_direction, { "Direction", "ua3g.ip.start_rtp.direction", FT_UINT8, BASE_DEC, VALS(start_rtp_str_direction), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter, { "Parameter", "ua3g.ip.start_rtp.parameter", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &ip_device_routing_cmd_start_rtp_vals_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_length, { "Length", "ua3g.ip.start_rtp.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_redirect_parameter, { "Parameter", "ua3g.ip.redirect.parameter", FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_redirect_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_redirect_parameter_length, { "Length", "ua3g.ip.redirect.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_def_tones_num_entries, { "Number Of Entries", "ua3g.ip.def_tones.num_entries", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_def_tones_frequency_1, { "Frequency 1 (Hz)", "ua3g.ip.def_tones.frequency_1", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_def_tones_level_1, { "Level 1 (dB)", "ua3g.ip.def_tones.level_1", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_def_tones_frequency_2, { "Frequency 2 (Hz)", "ua3g.ip.def_tones.frequency_2", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_def_tones_level_2, { "Level 2 (dB)", "ua3g.ip.def_tones.level_2", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_tone_direction, { "Direction", "ua3g.ip.start_tone.direction", FT_UINT8, BASE_DEC, VALS(ip_device_routing_tone_direction_vals), 0xC0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_tone_num_entries, { "Number of entries", "ua3g.ip.start_tone.num_entries", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_tone_identification, { "Identification", "ua3g.ip.start_tone.identification", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_tone_duration, { "Duration (ms)", "ua3g.ip.start_tone.duration", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_listen_rtp_parameter, { "Parameter", "ua3g.ip.listen_rtp.parameter", FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_listen_rtp_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_listen_rtp_parameter_length, { "Length", "ua3g.ip.listen_rtp.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_get_param_req_parameter, { "Parameter", "ua3g.ip.get_param_req.parameter", FT_UINT8, BASE_DEC, VALS(ip_device_routing_cmd_get_param_req_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter, { "Parameter", "ua3g.ip.set_param_req.parameter", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &ip_device_routing_cmd_set_param_req_vals_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_length, { "Length", "ua3g.ip.set_param_req.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_digit_value, { "Digit Value", "ua3g.ip.digit_value", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &str_digit_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_pause_restart_rtp_parameter, { "Parameter", "ua3g.ip.pause_restart_rtp.parameter", FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_pause_restart_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_length, { "Length", "ua3g.ip.pause_restart_rtp.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_value, { "Value", "ua3g.ip.pause_restart_rtp.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter, { "Parameter", "ua3g.ip.start_stop_record_rtp.parameter", FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_record_rtp_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_length, { "Length", "ua3g.ip.start_stop_record_rtp.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_debug_in_line, { "Text String With Debug", "ua3g.debug_in_line", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_command_led_number, { "Led Number", "ua3g.command.led.number", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options, { "LCD Options", "ua3g.command.lcd_line.lcd_options", FT_UINT8, BASE_HEX, NULL, 0xFC, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_starting_column, { "Starting Column", "ua3g.command.lcd_line.starting_column", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_tune, { "Tune", "ua3g.main_voice_mode.tune", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_cadence, { "Cadence", "ua3g.main_voice_mode.cadence", FT_UINT8, BASE_DEC, VALS(str_cadence), 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_speaker_volume, { "Speaker Volume", "ua3g.main_voice_mode.speaker_volume", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_microphone_volume, { "Microphone Volume", "ua3g.main_voice_mode.microphone_volume", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_sending_level, { "Sending Level (dB)", "ua3g.main_voice_mode.sending_level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_handset_level, { "Receiving Level (dB)", "ua3g.main_voice_mode.handset_level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_headset_level, { "Receiving Level (dB)", "ua3g.main_voice_mode.headset_level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_main_voice_mode_handsfree_level, { "Sending Level (dB)", "ua3g.main_voice_mode.handsfree_level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_metastate_subchannel_address, { "Subchannel Address", "ua3g.subdevice_metastate.subchannel_address", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_metastate_new_metastate, { "New Metastate", "ua3g.subdevice_metastate.new_metastate", FT_UINT8, BASE_DEC, VALS(str_new_metastate), 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_dtmf_clck_format_minimum_on_time, { "Minimum 'ON' Time (ms)", "ua3g.dwl_dtmf_clck_format.minimum_on_time", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_dtmf_clck_format_inter_digit_pause_time, { "Inter-Digit Pause Time (ms)", "ua3g.dwl_dtmf_clck_format.inter_digit_pause_time", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_dtmf_clck_format_clock_time_format, { "Clock Time Format", "ua3g.dwl_dtmf_clck_format.clock_time_format", FT_UINT8, BASE_DEC, VALS(str_clock_format), 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_dtmf_clck_format_dtmf_country_adaptation, { "DTMF Country Adaptation", "ua3g.dwl_dtmf_clck_format.dtmf_country_adaptation", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_channel_mode, { "Channel Mode", "ua3g.voice_channel.channel_mode", FT_BOOLEAN, 8, TFS(&tfs_voice_channel_channel_mode), 0x01, NULL, HFILL }}, + { &hf_ua3g_voice_channel_codec, { "Codec", "ua3g.voice_channel.codec", FT_BOOLEAN, 8, TFS(&tfs_voice_channel_codec), 0x02, NULL, HFILL }}, + { &hf_ua3g_voice_channel_voice_channel, { "Voice Channel", "ua3g.voice_channel.voice_channel", FT_BOOLEAN, 8, TFS(&tfs_voice_channel_voice_channel), 0x04, NULL, HFILL }}, + { &hf_ua3g_voice_channel_main_voice, { "Main Voice", "ua3g.voice_channel.main_voice", FT_UINT8, BASE_DEC, VALS(str_voice_channel), 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_announce, { "Announce", "ua3g.voice_channel.announce", FT_UINT8, BASE_DEC, VALS(str_voice_channel), 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_b_general, { "B General", "ua3g.voice_channel.b_general", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_b_loud_speaker, { "B Loud Speaker", "ua3g.voice_channel.b_loud_speaker", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_b_ear_piece, { "B Ear Piece", "ua3g.voice_channel.b_ear_piece", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_voice_channel_b_microphones, { "B Microphones", "ua3g.voice_channel.b_microphones", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_lcd_cursor_line_number, { "Line Number", "ua3g.lcd_cursor.line_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_special_char_character_number, { "Character Number", "ua3g.dwl_special_char.character_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_dwl_special_char_byte, { "Byte", "ua3g.dwl_special_char.byte", FT_UINT8, BASE_DEC, NULL, 0xFF, NULL, HFILL }}, + { &hf_ua3g_set_clck_timer_pos_clock_line_number, { "Clock Line Number", "ua3g.set_clck_timer_pos.clock_line_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_set_clck_timer_pos_clock_column_number, { "Clock Column Number", "ua3g.set_clck_timer_pos.clock_column_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_set_clck_timer_pos_call_timer_line_number, { "Call Timer Line Number", "ua3g.set_clck_timer_pos.call_timer_line_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_set_clck_timer_pos_call_timer_column_number, { "Call Timer Column Number", "ua3g.set_clck_timer_pos.call_timer_column_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_set_lcd_contrast_driver_number, { "Driver Number", "ua3g.set_lcd_contrast.driver_number", FT_UINT8, BASE_DEC, VALS(str_driver_number), 0x0, NULL, HFILL }}, + { &hf_ua3g_set_lcd_contrast_contrast_value, { "Contrast Value", "ua3g.set_lcd_contrast.contrast_value", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_beep_destination, { "Destination", "ua3g.command.beep.destination", FT_UINT8, BASE_DEC, VALS(str_beep_start_destination), 0x0, NULL, HFILL }}, + { &hf_ua3g_beep_on_off, { "On / Off", "ua3g.command.beep.on_off", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x80, NULL, HFILL }}, + { &hf_ua3g_beep_cadence, { "Cadence", "ua3g.command.beep.cadence", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_beep_beep_number, { "Beep Number", "ua3g.command.beep.beep_number", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_beep_number_of_notes, { "Number Of Notes", "ua3g.command.beep.number_of_notes", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_beep_terminator, { "Terminator", "ua3g.command.beep.terminator", FT_UINT8, BASE_DEC, VALS(str_beep_terminator), 0x0, NULL, HFILL }}, + { &hf_ua3g_sidetone_level, { "Level", "ua3g.command.sidetone.level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ringing_cadence_cadence, { "Cadence", "ua3g.ringing_cadence.cadence", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ringing_cadence_on_off, { "On / Off", "ua3g.ringing_cadence.on_off", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x80, NULL, HFILL }}, + { &hf_ua3g_ringing_cadence_length, { "Length (ms)", "ua3g.ringing_cadence.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_feedback_level, { "Level (dB)", "ua3g.command.feedback.level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_feedback_duration, { "Duration (ms)", "ua3g.command.feedback.duration", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_r_w_peripheral_address, { "Address", "ua3g.r_w_peripheral.address", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_r_w_peripheral_content, { "Content", "ua3g.r_w_peripheral.content", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_icon_cmd_icon_number, { "Icon Number", "ua3g.icon_cmd.icon_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_icon_cmd_segment, { "Segment", "ua3g.icon_cmd.segment", FT_UINT16, BASE_DEC, VALS(str_icon_cmd_state), 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_ignored, { "Ignored", "ua3g.command.audio_config.ignored", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_law, { "Law", "ua3g.command.audio_config.law", FT_UINT8, BASE_DEC, VALS(str_audio_coding_law), 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_volume_level, { "Volume Level", "ua3g.command.audio_config.volume_level", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_handsfree_return, { "Return", "ua3g.command.audio_config.handsfree_return", FT_BOOLEAN, 8, TFS(&tfs_audio_config_handsfree_return), 0x01, NULL, HFILL }}, + { &hf_ua3g_audio_config_handsfree_handsfree, { "Handsfree", "ua3g.command.audio_config.handsfree", FT_BOOLEAN, 8, TFS(&tfs_audio_config_handsfree_handsfree), 0x02, NULL, HFILL }}, + { &hf_ua3g_audio_padded_path_emission_padded_level, { "Emission Padded Level", "ua3g.audio_padded_path.emission_padded_level", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_padded_path_reception_padded_level, { "Reception Padded Level", "ua3g.audio_padded_path.reception_padded_level", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_on_off_level_level_on_loudspeaker, { "Level on Loudspeaker (dB)", "ua3g.on_off_level.level_on_loudspeaker", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ring_melody, { "Melody", "ua3g.command.ring.melody", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ring_cadence, { "Cadence", "ua3g.command.ring.cadence", FT_UINT8, BASE_DEC, VALS(str_cadence), 0x0, NULL, HFILL }}, + { &hf_ua3g_ring_speaker_level, { "Speaker level (dB)", "ua3g.command.ring.speaker_level", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ring_beep_number, { "Beep number", "ua3g.command.ring.beep_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ring_silent, { "Silent", "ua3g.command.ring.silent", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x80, NULL, HFILL }}, + { &hf_ua3g_ring_progressive, { "Progressive", "ua3g.command.ring.progressive", FT_UINT8, BASE_DEC, NULL, 0x03, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_item_identifier, { "Item Identifier", "ua3g.ua_dwl_protocol.item_identifier", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_cause, { "Cause", "ua3g.ua_dwl_protocol.cause", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_force_mode, { "Force Mode", "ua3g.ua_dwl_protocol.force_mode", FT_UINT8, BASE_DEC, VALS(str_download_req_force_mode), 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_item_version, { "Item Version", "ua3g.ua_dwl_protocol.item_version", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_item_version_nc, { "Item Version", "ua3g.ua_dwl_protocol.item_version", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_binary_length, { "Binary Length", "ua3g.ua_dwl_protocol.binary_length", FT_UINT24, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_packet_number, { "Packet Number", "ua3g.ua_dwl_protocol.packet_number", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_download_ack_status, { "Status", "ua3g.ua_dwl_protocol.download_ack_status", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &str_download_ack_status_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_packet_download_end_ack_ok_status, { "Status", "ua3g.ua_dwl_protocol_packet.download_end_ack_ok_status", FT_UINT8, BASE_DEC, VALS(str_download_end_ack_ok), 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_checksum, { "Checksum", "ua3g.ua_dwl_protocol.checksum", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_acknowledge, { "Acknowledge", "ua3g.ua_dwl_protocol.acknowledge", FT_UINT8, BASE_DEC, VALS(str_iso_checksum_ack_status), 0x0, NULL, HFILL }}, + { &hf_ua3g_digit_dialed_digit_value, { "Digit Value", "ua3g.digit_dialed.digit_value", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &str_digit_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_msg_subdev_type, { "Subdev Type", "ua3g.subdevice_msg.subdev_type", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_msg_subdev_address, { "Subdev Address", "ua3g.subdevice_msg.subdev_address", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_msg_subdevice_opcode, { "Subdevice Opcode", "ua3g.subdevice_msg.subdevice_opcode", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_subdevice_msg_parameter_bytes, { "Parameter Bytes", "ua3g.subdevice_msg.parameter_bytes", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd00_vta_type, { "VTA Type", "ua3g.ip.cs.cmd00.vta_type", FT_UINT8, BASE_DEC, VALS(str_cs_ip_device_routing_vta_type), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd00_characteristic_number, { "Characteristic Number", "ua3g.ip.cs.cmd00.characteristic_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd01_incident_0, { "Incident 0", "ua3g.ip.cs.cmd01.incident_0", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter, { "Parameter", "ua3g.ip.cs.cmd02.parameter", FT_UINT8, BASE_HEX, VALS(ip_device_routing_cmd_get_param_req_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_length, { "Length", "ua3g.ip.cs.cmd02.parameter.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter, { "Parameter", "ua3g.ip.cs.cmd03.parameter", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cs_ip_device_routing_03_parameter_id_vals_ext, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_length, { "Length", "ua3g.ip.cs.cmd03.parameter.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_device_type, { "Device Type", "ua3g.unsolicited_msg.device_type", FT_UINT8, BASE_DEC, VALS(str_device_type), 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_self_test_result, { "Self-Test Result", "ua3g.unsolicited_msg.self_test_result", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_vta_type, { "VTA Type", "ua3g.unsolicited_msg.vta_type", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_vta_type), 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_other_information, { "Other Information", "ua3g.unsolicited_msg.other_information", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_other_info_2), 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_other_information_1, { "Other Information 1", "ua3g.unsolicited_msg.other_information_1", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_other_info_1), 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_version, { "Hardware Version", "ua3g.unsolicited_msg.hardware_version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_other_information_2, { "Other Information 2", "ua3g.unsolicited_msg.other_information_2", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_other_info_2), 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_firmware_datas_patch_version, { "Firmware Datas Patch Version", "ua3g.unsolicited_msg.firmware_datas_patch_version", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_firmware_version_loader, { "Firmware Version (Loader)", "ua3g.unsolicited_msg.firmware_version_loader", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_firmware_version, { "Firmware Version", "ua3g.unsolicited_msg.firmware_version", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_datas_version, { "Datas Version", "ua3g.unsolicited_msg.datas_version", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_firmware_version_bootloader, { "Firmware Version (Bootloader)", "ua3g.unsolicited_msg.firmware_version_bootloader", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_opcode_of_bad_command, { "Opcode Of Bad Command", "ua3g.unsolicited_msg.opcode_of_bad_command", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_next_byte_of_bad_command, { "Next Byte Of Bad Command", "ua3g.unsolicited_msg.next_byte_of_bad_command", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_subdevice_address, { "Subdevice Address", "ua3g.unsolicited_msg.subdevice_address", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_segment_failure_t, { "T", "ua3g.unsolicited_msg.segment_failure.t", FT_BOOLEAN, 8, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_segment_failure_num, { "Num", "ua3g.unsolicited_msg.segment_failurenum", FT_BOOLEAN, 8, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_segment_failure_s, { "/S", "ua3g.unsolicited_msg.segment_failure.s", FT_BOOLEAN, 8, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_segment_failure_l, { "L", "ua3g.unsolicited_msg.segment_failure.l", FT_BOOLEAN, 8, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_opcode_bad_segment, { "Opcode Bad Segment", "ua3g.unsolicited_msg.opcode_bad_segment", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_next_byte_of_bad_segment, { "Next Byte Of Bad Segment", "ua3g.unsolicited_msg.next_byte_of_bad_segment", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_device_event, { "Device Event", "ua3g.unsolicited_msg.device_event", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_i_m_here_id_code, { "Id Code", "ua3g.i_m_here.id_code", FT_UINT8, BASE_DEC, VALS(str_device_type), 0x0, NULL, HFILL }}, + { &hf_ua3g_segment_msg_segment, { "F/S", "ua3g.segment_msg.segment", FT_BOOLEAN, 8, TFS(&tfs_segment_msg_segment), 0x80, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update, { "NOE Update Mode", "ua3g.ip.reset.parameter.noe_update", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_bootloader, { "Bootloader", "ua3g.ip.reset.parameter.noe_update.bootloader", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x01, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_data, { "Data", "ua3g.ip.reset.parameter.noe_update.data", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x02, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_customization, { "Customization", "ua3g.ip.reset.parameter.noe_update.customization", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x04, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_localization, { "Localization", "ua3g.ip.reset.parameter.noe_update.localization", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x08, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_code, { "Code", "ua3g.ip.reset.parameter.noe_update.code", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x10, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_noe_update_sip, { "SIP", "ua3g.ip.reset.parameter.noe_update.sip", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x20, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_bad_sec_mode, { "Bad Sec Mode", "ua3g.ip.reset.parameter.bad_sec_mode", FT_UINT8, BASE_DEC, VALS(reset_param_bad_sec_mode), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_value, { "Value", "ua3g.ip.reset.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_cust_name, { "Cust_Name", "ua3g.ip.reset.parameter.cust_name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_reset_parameter_l10n_name, { "L10N_Name", "ua3g.ip.reset.parameter.l10n_name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_value, { "Value", "ua3g.ip.start_rtp.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_ip, { "IP", "ua3g.ip.start_rtp.parameter.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_compressor, { "Compressor", "ua3g.ip.start_rtp.parameter.compressor", FT_UINT64, BASE_DEC|BASE_VAL64_STRING, VALS(str_start_rtp_compressor), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_enabler, { "Enabler", "ua3g.ip.start_rtp.parameter.enabler", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_send_qos, { "Must Send QOS Tickets", "ua3g.ip.start_rtp.parameter.enabler", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_uint, { "Value", "ua3g.ip.start_rtp.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_dtmf_sending, { "Send DTMF", "ua3g.ip.start_rtp.parameter.dtmf_sending", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_rfc2198, { "Enable RFC 2198", "ua3g.ip.start_rtp.parameter.rfc2198", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_rtp_parameter_srtp_encryption, { "Enable SRTP Encryption", "ua3g.ip.start_rtp.parameter.srtp_encryption", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_redirect_parameter_value, { "Value", "ua3g.ip.redirect.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_redirect_parameter_ip, { "IP", "ua3g.ip.redirect.parameter.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_redirect_parameter_uint, { "Value", "ua3g.ip.redirect.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_listen_rtp_parameter_value, { "Value", "ua3g.ip.listen_rtp.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_listen_rtp_parameter_ip, { "IP", "ua3g.ip.listen_rtp.parameter.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_listen_rtp_parameter_port, { "Port", "ua3g.ip.listen_rtp.parameter.port", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_value, { "Value", "ua3g.ip.set_param_req.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_tftp_backup_ip, { "TFTP Backup IP", "ua3g.ip.set_param_req.parameter.tftp_backup_ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_uint, { "Value", "ua3g.ip.set_param_req.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_err_string, { "Value", "ua3g.ip.set_param_req.parameter.err_string", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_compressor, { "Compressor", "ua3g.ip.set_param_req.parameter.compressor", FT_UINT8, BASE_DEC, VALS(str_set_param_req_compressor), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_set_pc_port_status, { "Set PC Port status", "ua3g.ip.set_param_req.parameter.set_pc_port_status", FT_UINT8, BASE_DEC, VALS(str_set_pc_port_status), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_record_rtp_auth, { "Record RTP Authorization", "ua3g.ip.set_param_req.parameter.record_rtp_auth", FT_UINT8, BASE_DEC, VALS(str_enable_feature), 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_set_param_req_parameter_security_flag_filter, { "Filtering", "ua3g.ip.set_param_req.parameter.security_flag.filter", FT_BOOLEAN, 8, TFS(&tfs_active_inactive), 0x01, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_pause_restart_rtp_parameter_uint, { "Value", "ua3g.ip.pause_restart_rtp.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_value, { "Value", "ua3g.ip.start_stop_record_rtp.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_remote_ip, { "Remote IP", "ua3g.ip.start_stop_record_rtp.parameter.remote_ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ip_device_routing_start_stop_record_rtp_parameter_uint, { "Value", "ua3g.ip.start_stop_record_rtp.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_dpi_chan_ua_tx1, { "UA Channel UA-TX1", "ua3g.command.audio_config.dpi_chan.ua_tx1", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_dpi_chan_ua_tx2, { "UA Channel UA-TX2", "ua3g.command.audio_config.dpi_chan.ua_tx2", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_dpi_chan_gci_tx1, { "GCI Channel GCI-TX1", "ua3g.command.audio_config.dpi_chan.gci_tx1", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_dpi_chan_gci_tx2, { "GCI Channel GCI-TX2", "ua3g.command.audio_config.dpi_chan.gci_tx2", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_dpi_chan_cod_tx, { "Codec Channel COD-TX", "ua3g.command.audio_config.dpi_chan.cod_tx", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_dth, { "Anti-Distortion Coeff 1(DTH)", "ua3g.command.audio_config.audio_circuit.dth", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_dtr, { "Anti-Distortion Coeff 2(DTR)", "ua3g.command.audio_config.audio_circuit.dtr", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_dtf, { "Anti-Distortion Coeff 3(DTF)", "ua3g.command.audio_config.audio_circuit.dtf", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_str, { "Sidetone Attenuation (STR)", "ua3g.command.audio_config.audio_circuit.str", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_ahp1, { "Anti-Larsen Coeff 1 (AHP1)", "ua3g.command.audio_config.audio_circuit.ahp1", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_ahp2, { "Anti-Larsen Coeff 2 (AHP2)", "ua3g.command.audio_config.audio_circuit.ahp2", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_ath, { "Anti-Larsen Coeff 3 (ATH)", "ua3g.command.audio_config.audio_circuit.ath", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_atr, { "Anti-Larsen Coeff 4 (ATR)", "ua3g.command.audio_config.audio_circuit.atr", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_atf, { "Anti-Larsen Coeff 5 (ATF)", "ua3g.command.audio_config.audio_circuit.atf", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_audio_circuit_alm, { "Anti-Larsen Coeff 6 (ALM)", "ua3g.command.audio_config.audio_circuit.alm", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_group_listen, { "Group Listening Attenuation Constant", "ua3g.command.audio_config.loudspeaker_aco_param.group_listen", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_attenuation, { "Handsfree Attenuation Constant", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_attenuation", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_stay_in_send, { "Handsfree Number Of ms To Stay In Send State Before Going To Another State", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_stay_in_send", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mtx, { "Handsfree Number Of Positions To Shift Right MTx", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_shift_right_mtx", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_shift_right_mrc, { "Handsfree Number Of Positions To Shift Right MRc", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_shift_right_mrc", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_trans_threshold, { "Handsfree Idle Transmission Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_idle_trans_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_trans_threshold, { "Handsfree Low Transmission Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_low_trans_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_idle_recv_threshold, { "Handsfree Idle Reception Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_idle_recv_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_low_recv_threshold, { "Handsfree Low Reception Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_low_recv_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_med_recv_threshold, { "Handsfree Medium Reception Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_med_recv_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_audio_config_loudspeaker_aco_param_handsfree_high_recv_threshold, { "Handsfree High Reception Threshold", "ua3g.command.audio_config.loudspeaker_aco_param.handsfree_high_recv_threshold", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_files_inc_boot_binary, { "Boot Binary Included", "ua3g.ua_dwl_protocol.files_inc.boot_binary", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x01, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_files_inc_loader_binary, { "Loader Binary Included", "ua3g.ua_dwl_protocol.files_inc.loader_binary", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x02, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_files_inc_appli_binary, { "Appli Binary Included", "ua3g.ua_dwl_protocol.files_inc.appli_binary", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x04, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_files_inc_data_binary, { "Datas Binary Included", "ua3g.ua_dwl_protocol.files_inc.data_binary", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x08, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_model_selection_a, { "For A Model", "ua3g.ua_dwl_protocol.model_selection.a", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x01, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_model_selection_b, { "For B Model", "ua3g.ua_dwl_protocol.model_selection.b", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x02, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_model_selection_c, { "For C Model", "ua3g.ua_dwl_protocol.model_selection.c", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x04, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_model_selection_country_ver, { "Country Version", "ua3g.ua_dwl_protocol.model_selection.country_ver", FT_UINT8, BASE_DEC, VALS(str_download_req_mode_selection_country), 0xE0, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe1, { "For Ivanoe 1", "ua3g.ua_dwl_protocol.hardware_selection.ivanoe1", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x01, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_hardware_selection_ivanoe2, { "For Ivanoe 2", "ua3g.ua_dwl_protocol.hardware_selection.ivanoe2", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x02, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_memory_sizes_flash, { "Flash Min Size", "ua3g.ua_dwl_protocol.memory_sizes.flash", FT_UINT8, BASE_DEC, VALS(str_mem_size), 0x07, NULL, HFILL }}, + { &hf_ua3g_ua_dwl_protocol_memory_sizes_ext_ram, { "External Ram Min Size", "ua3g.ua_dwl_protocol.memory_sizes.ext_ram", FT_UINT8, BASE_DEC, VALS(str_mem_size), 0x38, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_char_num_vta_subtype, { "VTA SubType", "ua3g.unsolicited_msg.char_num.vta_subtype", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_subtype), 0xC0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_char_num_generation, { "Generation", "ua3g.unsolicited_msg.char_num.generation", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_generation), 0x38, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_char_num_design, { "Design", "ua3g.unsolicited_msg.char_num.design", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_design), 0x07, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_vta_type, { "VTA Type", "ua3g.unsolicited_msg.hardware_config.vta_type", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_vta_type), 0xE0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_design, { "Design", "ua3g.unsolicited_msg.hardware_config.design", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_design), 0x1C, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_subtype, { "VTA SubType", "ua3g.unsolicited_msg.hardware_config.subtype", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_subtype), 0x03, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_hard_config_chip, { "Chip Id", "ua3g.unsolicited_msg.hardware_config.hard_config_chip", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_config_chip), 0x03, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_hard_config_flash, { "Flash Size", "ua3g.unsolicited_msg.hardware_config.hard_config_flash", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_config_flash), 0x1C, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_config_ram, { "External RAM Size", "ua3g.unsolicited_msg.hardware_config.config_ram", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_config_ram), 0xE0, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hardware_config_hard_config_ip, { "Hardware Configuration", "ua3g.unsolicited_msg.hardware_config.ip", FT_UINT8, BASE_DEC, VALS(str_unsolicited_msg_hard_config_ip), 0x01, NULL, HFILL }}, + { &hf_ua3g_unsolicited_msg_hook_status, { "Hook Status", "ua3g.unsolicited_msg.hook_status", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x00, NULL, HFILL }}, + { &hf_ua3g_special_key_shift, { "Shift", "ua3g.special_key.shift", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x01, NULL, HFILL }}, + { &hf_ua3g_special_key_ctrl, { "Ctrl", "ua3g.special_key.ctrl", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x02, NULL, HFILL }}, + { &hf_ua3g_special_key_alt, { "Alt", "ua3g.special_key.alt", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x04, NULL, HFILL }}, + { &hf_ua3g_special_key_cmd, { "Cmd", "ua3g.special_key.cmd", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x08, NULL, HFILL }}, + { &hf_ua3g_special_key_shift_prime, { "Shift'", "ua3g.special_key.shift_prime", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x10, NULL, HFILL }}, + { &hf_ua3g_special_key_ctrl_prime, { "Ctrl'", "ua3g.special_key.ctrl_prime", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x20, NULL, HFILL }}, + { &hf_ua3g_special_key_alt_prime, { "Alt'", "ua3g.special_key.alt_prime", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x40, NULL, HFILL }}, + { &hf_ua3g_special_key_cmd_prime, { "Cmd'", "ua3g.special_key.cmd_prime", FT_BOOLEAN, 8, TFS(&tfs_released_pressed), 0x80, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_call_timer, { "Call Timer", "ua3g.lcd_line_cmd.lcd_options.call_timer", FT_UINT8, BASE_DEC, VALS(str_call_timer_ctrl), 0x03, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_blink, { "Blink", "ua3g.lcd_line_cmd.lcd_options.blink", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x04, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_call_timer_control, { "Call Timer Control", "ua3g.lcd_line_cmd.lcd_options.call_timer_control", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x10, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_call_timer_display, { "Call Timer Display", "ua3g.lcd_line_cmd.lcd_options.call_timer_display", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x20, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_time_of_day_display, { "Time Of Day Display", "ua3g.lcd_line_cmd.lcd_options.time_of_day_display", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x40, NULL, HFILL }}, + { &hf_ua3g_lcd_line_cmd_lcd_options_suspend_display_refresh, { "Suspend Display Refresh", "ua3g.lcd_line_cmd.lcd_options.suspend_display_refresh", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0x80, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_value, { "Value", "ua3g.ip.cs.cmd02.parameter.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_ip, { "IP", "ua3g.ip.cs.cmd02.parameter.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_mac_address, { "MAC Address", "ua3g.ip.cs.cmd02.parameter.mac_address", FT_ETHER, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_uint, { "Value", "ua3g.ip.cs.cmd02.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_bytes, { "Default Codec", "ua3g.ip.cs.cmd02.parameter.default_codec.bytes", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_default_codec_uint, { "Default Codec", "ua3g.ip.cs.cmd02.parameter.default_codec.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_firmware_version, { "Firmware Version", "ua3g.ip.cs.cmd02.parameter.firmware_version", FT_UINT16, BASE_CUSTOM, version_number_computer, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_speed, { "Port Lan Speed", "ua3g.ip.cs.cmd02.parameter.eth_driver_config.port_lan_speed", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_lan_duplex, { "Port Lan Duplex", "ua3g.ip.cs.cmd02.parameter.eth_driver_config.port_lan_duplex", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_speed, { "Port PC Speed", "ua3g.ip.cs.cmd02.parameter.eth_driver_config.port_pc_speed", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd02_parameter_eth_driver_config_port_pc_duplex, { "Port PC Duplex", "ua3g.ip.cs.cmd02.parameter.eth_driver_config.port_pc_duplex", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_ip, { "IP", "ua3g.ip.cs.cmd03.parameter.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_string, { "IP", "ua3g.ip.cs.cmd03.parameter.string", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip1, { "Type Of Equipment (first byte)", "ua3g.ip.cs.cmd03.parameter.type_of_equip1", FT_UINT8, BASE_DEC, VALS(cs_ip_device_routing_cmd03_first_byte_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_type_of_equip2, { "Type Of Equipment (second byte)", "ua3g.ip.cs.cmd03.parameter.type_of_equip2", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_cmd03_second_byte_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_default_codec, { "Default Codec", "ua3g.ip.cs.cmd03.parameter.default_codec", FT_UINT64, BASE_DEC|BASE_VAL64_STRING, VALS(str_cs_ip_device_routing_0F_compressor), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_vad, { "VAD", "ua3g.ip.cs.cmd03.parameter.vad", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_ece, { "ECE", "ua3g.ip.cs.cmd03.parameter.ece", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_voice_mode, { "Voice Mode", "ua3g.ip.cs.cmd03.parameter.voice_mode", FT_UINT64, BASE_DEC|BASE_VAL64_STRING, VALS(cs_ip_device_routing_cmd03_voice_mode_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_delay_distribution, { "Delay Distribution", "ua3g.ip.cs.cmd03.parameter.delay_distribution", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_delay_distribution_range_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_bfi, { "Consecutive BFI", "ua3g.ip.cs.cmd03.parameter.consecutive_bfi", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_consecutive_bfi_range_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution, { "BFI Distribution", "ua3g.ip.cs.cmd03.parameter.bfi_distribution", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_bfi_distribution_range_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021Q_used, { "802.1 Q Used", "ua3g.ip.cs.cmd03.parameter.8021Q_used", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_8021P_priority, { "802.1p Priority", "ua3g.ip.cs.cmd03.parameter.8021P_priority", FT_UINT8, BASE_DEC, NULL, 0x07, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_vlan_id, { "VLAN Id", "ua3g.ip.cs.cmd03.parameter.vlan_id", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_diffserv, { "DiffServ", "ua3g.ip.cs.cmd03.parameter.diffserv", FT_UINT8, BASE_DEC, NULL, 0xFC, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_bfi_distribution_200ms, { "200 ms BFI Distribution", "ua3g.ip.cs.cmd03.parameter.bfi_distribution_200ms", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_200ms_bfi_distribution_range_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_uint, { "Value", "ua3g.ip.cs.cmd03.parameter.uint", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_consecutive_rtp_lost, { "Consecutive RTP Lost", "ua3g.ip.cs.cmd03.parameter.consecutive_rtp_lost", FT_UINT16, BASE_DEC, VALS(cs_ip_device_routing_consecutive_rtp_lost_range_vals), 0x0, NULL, HFILL }}, + { &hf_ua3g_cs_ip_device_routing_cmd03_parameter_jitter_depth_distribution, { "Jitter Depth Distribution", "ua3g.ip.cs.cmd03.parameter.jitter_depth_distribution", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + }; + + static gint *ett[] = + { + &ett_ua3g, + &ett_ua3g_body, + &ett_ua3g_param, + &ett_ua3g_param_sub, + &ett_ua3g_option, + }; + + /* UA3G dissector registration */ + proto_ua3g = proto_register_protocol("UA3G Message", "UA3G", "ua3g"); + + proto_register_field_array(proto_ua3g, hf, array_length(hf)); + + new_register_dissector("ua3g", dissect_ua3g, proto_ua3g); + + /* Common subtree array registration */ + proto_register_subtree_array(ett, array_length(ett)); +} + +void proto_reg_handoff_ua3g(void) +{ +#if 0 /* Future */ + dissector_handle_t handle_ua3g = find_dissector("ua3g"); + + /* hooking of UA3G on UA */ + + dissector_add_uint("ua.opcode", 0x15, handle_ua3g); +#endif +} diff --git a/src/main/resource/testFiles/wireshark/revFiles/2cc860_12fddb_packet-rtps.c b/src/main/resource/testFiles/wireshark/revFiles/2cc860_12fddb_packet-rtps.c new file mode 100644 index 0000000..451310a --- /dev/null +++ b/src/main/resource/testFiles/wireshark/revFiles/2cc860_12fddb_packet-rtps.c @@ -0,0 +1,1993 @@ +/* packet-rtps.c + * Routines for Real-Time Publish-Subscribe Protocol (RTPS) dissection + * + * Copyright 2003, LUKAS POKORNY + * PETR SMOLIK + * ZDENEK SEBEK + * + * Czech Technical University in Prague + * Faculty of Electrical Engineering + * Department of Control Engineering + * + * version: 2004/04/15 9:40:45 + * dedication to Kj :] + * + * $Id: packet-rtps.c,v 1.9 2004/04/19 22:41:19 guy Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Copied from packet-udp.c, packet-tftp.c, packet-x25.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * *********************************************************************** */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + + +/* *********************************************************************** * + RTPS protocol was developed by Real Time Innovation, Inc. + + Protocol specifikation and documenation you can find on these addresses: + + http://www.rti.com/ + + http://www.rti.com/products/ndds/literature.html + + http://www.schneider-electric.com.au/Products/Automation/TF_RealTime/ + /technical%20library/specifications/WireProtocolExternal.pdf + + + * *********************************************************************** */ + + + +/* redefine types because of definitions in 'packet-rtps.h' */ +/* +#define u_int8_t guint8 +#define int8_t gint8 + +#define u_int16_t guint16 +#define int16_t gint16 + +#define u_int32_t guint32 +#define int32_t gint32 +*/ + +#include "packet-rtps.h" + +/* number of APP_KIND byte in packet header */ +#define APP_KIND_BYTE 15 + + +/* definitions of flags */ +#define FLAG_E 0x01 +#define FLAG_F 0x02 +#define FLAG_I 0x02 +#define FLAG_M 0x02 +#define FLAG_P 0x02 +#define FLAG_A 0x04 +#define FLAG_H 0x08 + + +/* submessageId's ranges */ +#define SUBMSG_ID_MIN PAD +#define SUBMSG_ID_MAX INFO_DST + +/* Vendor specific submessageId's ranges */ +#define VENDOR_SUBMSG_ID_MIN 0x80 +#define VENDOR_SUBMSG_ID_MAX 0xff + +/* *********************************************************************** */ + + +/* initialize the protocol and registered fields */ +static int proto_rtps = -1; +static int hf_rtps_submessage_id = -1; +static int hf_rtps_submessage_flags = -1; +static int hf_rtps_octets_to_next_header = -1; +static int hf_rtps_parameter_id = -1; +static int hf_rtps_parameter_length = -1; +static int hf_rtps_issue_data = -1; + +/* Initialize the subtree pointers */ +static gint ett_rtps = -1; +static gint ett_rtps_submessage = -1; +static gint ett_rtps_bitmap = -1; +static gint ett_rtps_parameter_sequence = -1; +static gint ett_rtps_parameter = -1; + +/* Functions declarations */ +static void dissect_PAD(tvbuff_t *tvb,gint offset,guint8 flags, + int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_VAR(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_ISSUE(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_ACK(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_HEARTBEAT(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_GAP(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_INFO_TS(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_INFO_SRC(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_INFO_REPLY(tvbuff_t *tvb,gint offset,guint8 flags, + gboolean little_endian,int next_submsg_offset, + proto_tree *rtps_submessage_tree); +static void dissect_INFO_DST(tvbuff_t *tvb,gint offset,guint8 flags, + int next_submsg_offset, + proto_tree *rtps_submessage_tree); + +static guint16 get_guint16(tvbuff_t *tvb, gint offset, gboolean little_endian); +static guint32 get_guint32(tvbuff_t *tvb, gint offset, gboolean little_endian); + +static char *protocol_version_to_string(gint offset,tvbuff_t *tvb,char *buff); +static char *vendor_id_to_string(gint offset, tvbuff_t *tvb, char *buff); + +static char *host_id_to_string(gint offset,tvbuff_t *tvb, char buff[]); +static char *app_id_to_string(gint offset,tvbuff_t *tvb,char buff[]); +static char *object_id_to_string(gint offset, tvbuff_t *tvb, char buff[]); + +static char *IP_to_string(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]); +static char *port_to_string(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]); +static char *get_NtpTime(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]); + +static void get_bitmap(tvbuff_t *tvb, gint *p_offset, gboolean little_endian, + gint next_submsg, proto_tree *tree); + +static void get_parameter_sequence(tvbuff_t *tvb, gint *p_offset, + gboolean little_endian, + gint next_submsg_offset, proto_tree *tree); + +static gint seq_nr_to_string( gint offset, gboolean little_endian, tvbuff_t *tvb, + SequenceNumber *p_seqNumber); + +static const value_string submessage_id_vals[] = { + { PAD, "PAD" }, + { VAR, "VAR" }, + { ISSUE, "ISSUE" }, + { ACK, "ACK" }, + { HEARTBEAT, "HEARTBEAT" }, + { GAP, "GAP" }, + { INFO_TS, "INFO_TS" }, + { INFO_SRC, "INFO_SRC" }, + { INFO_REPLY, "INFO_REPLY" }, + { INFO_DST, "INFO_DST" }, + { APP_QUIT, "APP_QUIT" }, + { 0, NULL } +}; + +static const value_string parameter_id_vals[] = { + { PID_PAD, "PID_PAD" }, + { PID_SENTINEL, "PID_SENTINEL" }, + { PID_EXPIRATION_TIME, "PID_EXPIRATION_TIME" }, + { PID_PERSISTENCE, "PID_PERSISTENCE" }, + { PID_MINIMUM_SEPARATION, "PID_MINIMUM_SEPARATION" }, + { PID_TOPIC, "PID_TOPIC" }, + { PID_STRENGTH, "PID_STRENGTH" }, + { PID_TYPE_NAME, "PID_TYPE_NAME" }, + { PID_TYPE_CHECKSUM, "PID_TYPE_CHECKSUM" }, + { RTPS_PID_TYPE2_NAME, "RTPS_PID_TYPE2_NAME" }, + { RTPS_PID_TYPE2_CHECKSUM, "RTPS_PID_TYPE2_CHECKSUM" }, + { PID_METATRAFFIC_MULTICAST_IPADDRESS, "PID_METATRAFFIC_MULTICAST_IPADDRESS" }, + { PID_APP_IPADDRESS, "PID_APP_IPADDRESS" }, + { PID_METATRAFFIC_UNICAST_PORT, "PID_METATRAFFIC_UNICAST_PORT" }, + { PID_USERDATA_UNICAST_PORT, "PID_USERDATA_UNICAST_PORT" }, + { PID_IS_RELIABLE, "PID_IS_RELIABLE" }, + { PID_EXPECTS_ACK, "PID_EXPECTS_ACK" }, + { PID_USERDATA_MULTICAST_IPADDRESS, "PID_USERDATA_MULTICAST_IPADDRESS" }, + { PID_MANAGER_KEY, "PID_MANAGER_KEY" }, + { PID_SEND_QUEUE_SIZE, "PID_SEND_QUEUE_SIZE" }, + { PID_RELIABILITY_ENABLED, "PID_RELIABILITY_ENABLED" }, + { PID_PROTOCOL_VERSION, "PID_PROTOCOL_VERSION" }, + { PID_VENDOR_ID, "PID_VENDOR_ID" }, + { PID_VARGAPPS_SEQUENCE_NUMBER_LAST, "PID_VARGAPPS_SEQUENCE_NUMBER_LAST" }, + { PID_RECV_QUEUE_SIZE, "PID_RECV_QUEUE_SIZE" }, + { PID_RELIABILITY_OFFERED, "PID_RELIABILITY_OFFERED" }, + { PID_RELIABILITY_REQUESTED, "PID_RELIABILITY_REQUESTED" }, + { 0, NULL } +}; + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * Code to actually dissect the packets * + * * + * *********************************************************************** */ + +static gboolean +dissect_rtps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_item *ti; + proto_tree *rtps_tree=NULL; + gint offset = 0; + gint appKind; + proto_tree *rtps_submessage_tree; + guint8 submessageId; + guint8 flags; + gboolean little_endian; + int next_submsg; + int count_msg_type[11]; + char buff[200], buff_tmp[30];/* buffers */ + + /* offset is the byte offset of 'tvb' at which the new tvbuff + should start. The first byte is the 0th byte. */ + + /* --- making disition if protocol is RTPS protocol --- */ + if (!tvb_bytes_exist(tvb, offset, 4)) return FALSE; + if (tvb_get_guint8(tvb,offset++) != 'R') return FALSE; + if (tvb_get_guint8(tvb,offset++) != 'T') return FALSE; + if (tvb_get_guint8(tvb,offset++) != 'P') return FALSE; + if (tvb_get_guint8(tvb,offset++) != 'S') return FALSE; + + /* --- Make entries in Protocol column ---*/ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTPS"); + + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + memset(count_msg_type, 0, sizeof(count_msg_type)); + + if (tree) { + + /* create display subtree for the protocol */ + ti = proto_tree_add_item(tree, proto_rtps, tvb, 0, -1, FALSE); + rtps_tree = proto_item_add_subtree(ti, ett_rtps); + + /* Protocol Version */ + proto_tree_add_text(rtps_tree, tvb, offset, 2, + "Protocol RTPS, version %s", + protocol_version_to_string(offset, tvb, buff)); + offset +=2; + + /* Vendor Id */ + proto_tree_add_text(rtps_tree, tvb, offset, 2, + "VendorId: %s", + vendor_id_to_string(offset, tvb, buff)); + offset +=2; + + /* Host Id */ + proto_tree_add_text(rtps_tree, tvb, offset, 4, + "HostId: %s", + host_id_to_string(offset,tvb,buff)); + offset +=4; + + /* App Id */ + proto_tree_add_text(rtps_tree, tvb, offset, 4, + "App ID: %s", + app_id_to_string(offset, tvb, buff)); + + } + + /* offset behind RTPS's Header */ + offset=16; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + submessageId = tvb_get_guint8(tvb, offset); + if (submessageId & 0x80) { + ti = proto_tree_add_text(tree, tvb, offset, -1, "Submessage: %s", + val_to_str(submessageId, submessage_id_vals, + "Vendor-specific (0x%02X)")); + } else { + ti = proto_tree_add_text(tree, tvb, offset, -1, "Submessage: %s", + val_to_str(submessageId, submessage_id_vals, + "Unknown (0x%02X)")); + } + rtps_submessage_tree = proto_item_add_subtree(ti, ett_rtps_submessage); + if (submessageId & 0x80) { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_submessage_id, + tvb, offset, 1, submessageId, + "Submessage Id: Vendor-specific (0x%02x)", + submessageId); + } else { + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_id, + tvb, offset, 1, submessageId); + } + + flags = tvb_get_guint8(tvb, offset + 1); + /* E flag |XXXX|HAPE| => masks with 000000001b = 1 */ + if ((flags & FLAG_E) != 0) little_endian = TRUE; + else little_endian = FALSE; + + next_submsg = get_guint16(tvb, offset + 2, little_endian); + proto_item_set_len(ti, next_submsg); + + switch (submessageId) + { + case PAD: + if (tree) + dissect_PAD(tvb, offset + 1, flags, next_submsg, + rtps_submessage_tree); + count_msg_type[0]++; + break; + case VAR: + if (tree) + dissect_VAR(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[1]++; + break; + case ISSUE: + if (tree) + dissect_ISSUE(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[2]++; + break; + case ACK: + if (tree) + dissect_ACK(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[3]++; + break; + case HEARTBEAT: + if (tree) + dissect_HEARTBEAT(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[4]++; + break; + case GAP: + if (tree) + dissect_GAP(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[5]++; + break; + case INFO_TS: + if (tree) + dissect_INFO_TS(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[6]++; + break; + case INFO_SRC: + if (tree) + dissect_INFO_SRC(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[7]++; + break; + case INFO_REPLY: + if (tree) + dissect_INFO_REPLY(tvb, offset + 1, flags, little_endian, next_submsg, + rtps_submessage_tree); + count_msg_type[8]++; + break; + case INFO_DST: + if (tree) + dissect_INFO_DST(tvb, offset + 1, flags, next_submsg, + rtps_submessage_tree); + count_msg_type[9]++; + break; + default: + if (tree) { + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset + 1, 1, flags); + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset + 2, 2, next_submsg); + } + break; + } + + /* next submessage's offset */ + offset += next_submsg+4; + + } + + /* --- and Info column on summary display ---*/ + + if (check_col(pinfo->cinfo, COL_INFO)) + { + appKind = tvb_get_guint8(tvb, APP_KIND_BYTE); + + if (appKind == MANAGEDAPPLICATION ) {sprintf(buff,"App: ");} + if (appKind == MANAGER) {sprintf(buff,"Man: ");} + if (appKind == AID_UNKNOWN) {sprintf(buff,"Unknown:");} + + if (appKind != MANAGEDAPPLICATION && appKind != MANAGER && + appKind != AID_UNKNOWN) {sprintf(buff,"ERROR in APP type");} + + /* -- counts of submessages - for Information Frame */ + if (count_msg_type[0]>0) { + sprintf(buff_tmp,"PAD(%d) ",count_msg_type[0]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[1]>0) { + sprintf(buff_tmp,"VAR(%d) ",count_msg_type[1]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[2]>0) { + sprintf(buff_tmp,"ISSUE(%d) ",count_msg_type[2]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[3]>0) { + sprintf(buff_tmp,"ACK(%d) ",count_msg_type[3]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[4]>0) { + sprintf(buff_tmp,"HEARTBEAT(%d) ",count_msg_type[4]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[5]>0) { + sprintf(buff_tmp,"GAP(%d) ",count_msg_type[5]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[6]>0) { + sprintf(buff_tmp,"INFO_TS(%d) ",count_msg_type[6]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[7]>0) { + sprintf(buff_tmp, "INFO_SRC(%d) ",count_msg_type[7]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[8]>0) { + sprintf(buff_tmp,"INFO_REPLY(%d) ",count_msg_type[8]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[9]>0) { + sprintf(buff_tmp,"INFO_DST(%d) ",count_msg_type[9]); + strcat(buff,buff_tmp); + } + + if (count_msg_type[10]>0) { + sprintf(buff_tmp,"vendor specific(%d) ",count_msg_type[10]); + strcat(buff,buff_tmp); + } + + col_add_fstr(pinfo->cinfo, COL_INFO, buff); + + } + + + return TRUE; + +} /* end dissect_rtps(...) */ + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get 16 bit from the stream * + * * + * *********************************************************************** */ + +static guint16 get_guint16(tvbuff_t *tvb, gint offset, gboolean little_endian) +{ + guint16 value; + + if (little_endian) + value = tvb_get_letohs(tvb, offset); + else + value = tvb_get_ntohs(tvb, offset); + + return(value); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get 32 bit from the stream * + * * + * *********************************************************************** */ + +static guint32 get_guint32(tvbuff_t *tvb, gint offset, gboolean little_endian) +{ + guint32 value; + + if (little_endian) + value = tvb_get_letohl(tvb, offset); + else + value = tvb_get_ntohl(tvb, offset); + + return(value); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Protocol version * + * * + * *********************************************************************** */ + +static char * +protocol_version_to_string(gint offset,tvbuff_t *tvb,char *buff) +{ + guint8 major, minor; + + /* protocol verzion = major.minor */ + major = tvb_get_guint8(tvb, offset); + minor = tvb_get_guint8(tvb, (offset+1)); + + sprintf(buff,"%d.%d", major, minor); + return(buff); + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Vendor Id * + * * + * *********************************************************************** */ + +static char * +vendor_id_to_string(gint offset, tvbuff_t *tvb, char *buff) +{ + guint8 major, minor; + VendorId vendorId_rti; + + VENDOR_ID_RTI(vendorId_rti); + + major = tvb_get_guint8(tvb, offset); + minor = tvb_get_guint8(tvb, (offset+1)); + + if (major == vendorId_rti.major && + minor == vendorId_rti.minor) + { sprintf(buff,"Real-Time Innovations,Inc.,CA,USA"); + return(buff); } + + sprintf(buff,"Vendor unknown"); + return(buff); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get IP Address * + * * + * *********************************************************************** */ + +static char * +IP_to_string(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]) +{ + IPAddress ip; + guint8 a = 0, b = 0, c = 0, d = 0; /* IP Adresss = a.b.c.d */ + + ip = get_guint32(tvb, offset, little_endian); + /* get_guint32() - reads + endian conversion */ + a = (ip >> 24); + b = (ip >> 16) & 0xff; + c = (ip >> 8) & 0xff; + d = ip & 0xff; + + sprintf(buff,"%d.%d.%d.%d", a, b, c, d); + return(buff); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Port * + * * + * *********************************************************************** */ + +static char * +port_to_string(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]) +{ + Port port = get_guint32(tvb, offset, little_endian); + /* get_guint32() - reads + endian conversion */ + + if (port == PORT_INVALID) + sprintf(buff,"PORT_INVALID"); + else + sprintf(buff,"0x%X",port); + + return(buff); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get NTP Time * + * * + * *********************************************************************** */ + +static char * +get_NtpTime(gint offset,tvbuff_t *tvb,gboolean little_endian,char buff[]) +{ + NtpTime ntpTime; + float time; + + /* get_guint32() - reads + endian conversion */ + ntpTime.seconds = get_guint32(tvb, offset, little_endian); + ntpTime.fraction = get_guint32(tvb, (offset + 4), little_endian); + time = (float) ntpTime.seconds + (ntpTime.fraction / 2^(32)); + + sprintf(buff,"%f", time); + return(buff); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Host Id * + * * + * *********************************************************************** */ + +static char * +host_id_to_string(gint offset,tvbuff_t *tvb, char buff[]) +{ + guint32 hostId = tvb_get_ntohl(tvb, offset); + /* get_ntohl() automaticaly convert data to BIG ENDIAN */ + + sprintf(buff,"0x%X", hostId); + return(buff); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get AppID * + * * + * *********************************************************************** */ + +static char * +app_id_to_string(gint offset,tvbuff_t *tvb,char buff[]) +{ + guint32 appId = tvb_get_ntohl(tvb, offset); + /* get_ntohl() automaticaly convert data to BIG ENDIAN */ + + /* Instance Id */ + guint32 instanceId = (appId >> 8); + /* applicatin Kind */ + guint8 appKind = (appId & 0xff); + + if (appKind == MANAGEDAPPLICATION) + { + sprintf(buff,"Managed App, InstanceId: 0x%X",instanceId); + return(buff); + } + + if (appKind == MANAGER) + { + sprintf(buff,"Manager, InstanceId: 0x%X",instanceId); + return(buff); + } + + sprintf(buff,"Unknown"); + return(buff); + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Object_Id (32 bit) * + * * + * *********************************************************************** */ + +static char * +object_id_to_string(gint offset, tvbuff_t *tvb, char buff[]) +{ + guint32 objectId = tvb_get_ntohl(tvb, offset); + /* get_ntohl() automaticaly convert data to BIG ENDIAN */ + + if (objectId == OID_UNKNOWN) { sprintf(buff,"Unknown ObjectId"); + return(buff);} + if (objectId == OID_APP) { sprintf(buff,"applicationSelf"); + return(buff);} + if (objectId == OID_WRITE_APPSELF){ sprintf(buff,"writerApplicationSelf"); + return(buff);} + if (objectId == OID_WRITE_APP) { sprintf(buff,"writerApplications"); + return(buff);} + if (objectId == OID_READ_APP) { sprintf(buff,"readerApplications"); + return(buff);} + if (objectId == OID_WRITE_MGR) { sprintf(buff,"writerManagers"); + return(buff);} + if (objectId == OID_READ_MGR) { sprintf(buff,"readerManagers "); + return(buff);} + if (objectId == OID_WRITE_PUBL) { sprintf(buff,"writerPublications"); + return(buff);} + if (objectId == OID_READ_PUBL) { sprintf(buff,"readerPublications"); + return(buff);} + if (objectId == OID_WRITE_SUBS) { sprintf(buff,"writerSubscriptions"); + return(buff);} + if (objectId == OID_READ_SUBS) { sprintf(buff,"readerSubscriptions"); + return(buff);} + + /* nothing from the possibilites above */ + sprintf(buff,"instanceId: 0x%X, objKind: 0x%X", + (objectId >> 8),(objectId & 0xff)); + return(buff); + +/* for the future +//Kind +#define OID_APPLICATION 0x01 +#define OID_CSTWRITER 0x02 +#define OID_PUBLICATION 0x03 +#define OID_SUBSCRIPTION 0x04 +#define OID_CSTREADER 0x07 +// +#define OID_USEROBJ 0x00 +#define OID_RESUSEROBJ 0x40 +#define OID_METAOBJ 0x80 +#define OID_RESMETAOBJ 0xC0 +*/ +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get Sequence Number (64 bit) * + * * + * *********************************************************************** */ + +static gint +seq_nr_to_string(gint offset, gboolean little_endian, tvbuff_t *tvb, + SequenceNumber *p_seqNumber) +{ + p_seqNumber->high = get_guint32(tvb, offset, little_endian); + p_seqNumber->low = get_guint32(tvb, offset + 4, little_endian); + + return(1); +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * get_Bitmap * + * * + * *********************************************************************** */ + +static void +get_bitmap(tvbuff_t *tvb, gint *p_offset, gboolean little_endian, + gint next_submsg, proto_tree *tree) +{ + proto_item *ti; + proto_tree *rtps_bitmap_tree; + gint i = 0; + gint offset = *p_offset; + SequenceNumber sequenceNumber; + guint32 num_bits; + guint num_longs; + + /* making subtree for the bitmap */ + ti = proto_tree_add_text(tree,tvb,offset,(next_submsg-offset),"Bitmap"); + rtps_bitmap_tree = proto_item_add_subtree(ti, ett_rtps_bitmap); + + /* SekvenceNumber bitmapBase */ + seq_nr_to_string(offset, little_endian, tvb, &sequenceNumber); + proto_tree_add_text(rtps_bitmap_tree, tvb, offset, 8, + "bitmapBase: 0x%X%X", + sequenceNumber.high, sequenceNumber.low); + offset +=8; + + num_bits = get_guint32(tvb, offset, little_endian); + proto_tree_add_text(rtps_bitmap_tree, tvb, offset, 4, + "numBits: %u", + num_bits); + offset += 4; + + if (num_bits+31 < num_bits) + num_longs = UINT_MAX; /* overflow */ + else + num_longs = (num_bits+31)/32; + while (num_longs != 0) + { + if (next_submsg-offset < 4) + { + proto_tree_add_text(rtps_bitmap_tree, tvb, offset, next_submsg-offset, + "bitmap[%d]: < 4 bytes remain in message", i); + offset = next_submsg; + break; + } + proto_tree_add_text(rtps_bitmap_tree, tvb, offset, 4, + "bitmap[%d]: 0x%08X", + i, get_guint32(tvb, offset, little_endian)); + offset +=4; + ++i; + --num_longs; + } /* end while */ + + *p_offset = offset; +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * dissect submessage: PAD * + * * + * (this submessage has no meaning and it is always valid) * + * *********************************************************************** */ + +static void +dissect_PAD(tvbuff_t *tvb, gint offset, guint8 flags, + int next_submsg_offset, proto_tree *rtps_submessage_tree) +{ + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset += 1; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * dissect submessage: VAR * + * * + * *********************************************************************** */ + +static void +dissect_VAR(tvbuff_t *tvb, gint offset, guint8 flags, gboolean little_endian, + int next_submsg_offset, proto_tree *rtps_submessage_tree) +{ + int min_len; + char buff[200]; + SequenceNumber writerSeqNumber; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + min_len = 20; + if ((flags & FLAG_H) != 0) + min_len += 8; + if ((flags & FLAG_P) != 0) + min_len += 4; + if (next_submsg_offset < min_len) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= %u)", + next_submsg_offset, min_len); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* readerObjectId*/ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Reader Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* writerObjectId*/ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Writer Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset+=4; + + /* H flag |XXXX|HAPE| => masks with 00001000b = 8 */ + if ((flags & FLAG_H) != 0) + { + /* HostId */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Host ID: %s", + host_id_to_string(offset,tvb,buff)); + offset+=4; + + /* App Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "App ID: %s", + app_id_to_string(offset, tvb, buff)); + offset +=4; + } + + /* Object Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* WriterSequence Number */ + seq_nr_to_string(offset, little_endian, tvb, &writerSeqNumber); + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "WriterSeqNumber: 0x%X%X", + writerSeqNumber.high, writerSeqNumber.low); + offset +=8; + + /* P flag |XXXX|HAPE| => masks with 00000010b = 2 */ + if ((flags & FLAG_P) != 0) + { + get_parameter_sequence(tvb, &offset, little_endian, next_submsg_offset, + rtps_submessage_tree); + } +} + +/* *********************************************************************** */ + +/* *********************************************************************** * + * * + * get_ParameterSequence * + * * + * *********************************************************************** */ + + +static void +get_parameter_sequence(tvbuff_t *tvb, gint *p_offset, gboolean little_endian, + gint next_submsg_offset, proto_tree *tree) +{ + proto_item *ti; + proto_tree *rtps_parameter_sequence_tree; + proto_tree *rtps_parameter_tree; + gint offset = *p_offset; + guint16 parameter, param_length; + gint str_length; + SequenceNumber seqNumber; + char buff_tmp[MAX_PATHNAME]; + int i; + char sep; + + ti = proto_tree_add_text(tree, tvb, offset, (next_submsg_offset - offset), + "Parameters:"); + rtps_parameter_sequence_tree = proto_item_add_subtree(ti, + ett_rtps_parameter_sequence); + for (;;) + { + if (next_submsg_offset-offset < 2) + { + proto_tree_add_text(rtps_parameter_sequence_tree, tvb, offset, + next_submsg_offset-offset, + "Parameter: < 2 bytes remain in message"); + offset = next_submsg_offset; + break; + } + parameter = get_guint16(tvb, offset, little_endian); + ti = proto_tree_add_text(rtps_parameter_sequence_tree, tvb, offset, 2, + "%s", + val_to_str(parameter, parameter_id_vals, + "Unknown parameter (0x%04X)")); + rtps_parameter_tree = proto_item_add_subtree(ti, ett_rtps_parameter); + proto_tree_add_uint(rtps_parameter_tree, hf_rtps_parameter_id, + tvb, offset, 2, parameter); + offset +=2; + if (next_submsg_offset-offset < 2) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, + next_submsg_offset-offset, + "Parameter length: < 2 bytes remain in message"); + offset = next_submsg_offset; + proto_item_set_end(ti, tvb, offset); + break; + } + param_length = get_guint16(tvb, offset, little_endian); + proto_tree_add_uint(rtps_parameter_tree, hf_rtps_parameter_length, + tvb, offset, 2, param_length); + offset +=2; + + if (parameter == PID_SENTINEL) { + proto_item_set_end(ti, tvb, offset); + break; + } + + if (next_submsg_offset-offset < param_length) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, + next_submsg_offset-offset, + "Parameter value: < %u bytes remain in message", + param_length); + offset = next_submsg_offset; + proto_item_set_end(ti, tvb, offset); + break; + } + proto_item_set_end(ti, tvb, offset + param_length); + + switch (parameter) + { + case PID_PAD: + proto_item_append_text(ti, ": -"); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Padding"); + break; + + case PID_EXPIRATION_TIME: + if (param_length < 8) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 8"); + } + else + { + char *ntp_time_str; + + ntp_time_str = get_NtpTime(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, ": %s", ntp_time_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Expiration time: %s", ntp_time_str); + } + break; + + case PID_PERSISTENCE: + if (param_length < 8) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 8"); + } + else + { + char *ntp_time_str; + + ntp_time_str = get_NtpTime(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, ": %s", ntp_time_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Persistence: %s", ntp_time_str); + } + break; + + case PID_MINIMUM_SEPARATION: + if (param_length < 8) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 8"); + } + else + { + char *ntp_time_str; + + ntp_time_str = get_NtpTime(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, ": %s", ntp_time_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Minimum separation: %s", ntp_time_str); + } + break; + + case PID_TOPIC: /* --- ?? funguje spravne ?? */ + str_length = tvb_strnlen(tvb, offset, param_length); + if (str_length == -1) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: Terminating zero missing"); + } + else + { + char *str; + + str = tvb_format_text(tvb, offset, str_length); + proto_item_append_text(ti, ": %s", str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Topic: %s", str); + } + break; + + case PID_STRENGTH: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 strength; + + strength = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": 0x%X", strength); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Strength: 0x%X", strength); + } + break; + + case PID_TYPE_NAME: /* --- ?? funguje spravne ?? */ + str_length = tvb_strnlen(tvb, offset, param_length); + if (str_length == -1) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: Terminating zero missing"); + } + else + { + char *str; + + str = tvb_format_text(tvb, offset, str_length); + proto_item_append_text(ti, ": %s", str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Type name: %s", str); + } + break; + + case PID_TYPE_CHECKSUM: + /* nacitam jako UNSIGNED - nemuze to byt i zaporne cislo?? */ + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 checksum; + + checksum = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": 0x%X", checksum); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Checksum: 0x%X", checksum); + } + break; + + case RTPS_PID_TYPE2_NAME: + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Parameter data"); + break; + + case RTPS_PID_TYPE2_CHECKSUM: + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Parameter data"); + break; + + case PID_METATRAFFIC_MULTICAST_IPADDRESS: + i = 0; + sep = ':'; + while (param_length >= 4) + { + char *ip_string; + + ip_string = IP_to_string(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, "%c %s", sep, ip_string); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Address[%d]: %s", i, ip_string); + ++i; + offset +=4; + sep = ','; + param_length -= 4; /* decrement count */ + } + offset += param_length; + break; + + case PID_APP_IPADDRESS: + i = 0; + sep = ':'; + while (param_length >= 4) + { + char *ip_string; + + ip_string = IP_to_string(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, "%c %s", sep, ip_string); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Address[%d]: %s", i, ip_string); + ++i; + offset +=4; + sep = ','; + param_length -= 4; /* decrement count */ + } + offset += param_length; + break; + + case PID_METATRAFFIC_UNICAST_PORT: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + char *port_str; + + port_str = port_to_string(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, ": %s", port_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Port: %s", port_str); + } + break; + + case PID_USERDATA_UNICAST_PORT: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + char *port_str; + + port_str = port_to_string(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, ": %s", port_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Port: %s", port_str); + } + break; + + case PID_EXPECTS_ACK: + if (param_length < 1) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 1"); + } + else + { + if (tvb_get_guint8(tvb, offset) == 0) + { + proto_item_append_text(ti, ": No"); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "ACK expected: No"); + } + else + { + proto_item_append_text(ti, ": Yes"); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "ACK expected: Yes"); + } + } + break; + + case PID_USERDATA_MULTICAST_IPADDRESS: + i = 0; + sep = ':'; + while (param_length >= 4) + { + char *ip_string; + + ip_string = IP_to_string(offset, tvb, little_endian,buff_tmp); + proto_item_append_text(ti, "%c %s", sep, ip_string); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Address[%d]: %s", i, ip_string); + ++i; + offset +=4; + param_length -= 4; /* decrement count */ + } + offset += param_length; + break; + + case PID_MANAGER_KEY: + i = 0; + sep = ':'; + while (param_length >= 4) + { + guint32 manager_key; + + manager_key = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, "%c 0x%X", sep, manager_key); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Key[%d]: 0x%X", i, manager_key); + ++i; + offset +=4; + sep = ','; + param_length -= 4; /* decrement count */ + } + offset += param_length; + break; + + case PID_SEND_QUEUE_SIZE: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 send_queue_size; + + send_queue_size = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": %u", send_queue_size); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Send queue size: %u", send_queue_size); + } + break; + + case PID_PROTOCOL_VERSION: + if (param_length < 2) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 2"); + } + else + { + char *protocol_version_str; + + protocol_version_str = protocol_version_to_string(offset, tvb, buff_tmp); + proto_item_append_text(ti, ": %s", protocol_version_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Protocol version: %s", protocol_version_str); + } + break; + + case PID_VENDOR_ID: + if (param_length < 2) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 2"); + } + else + { + char *vendor_id_str; + + vendor_id_str = vendor_id_to_string(offset, tvb, buff_tmp); + proto_item_append_text(ti, ": %s", vendor_id_str); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Vendor ID: %s", vendor_id_str); + } + break; + + case PID_VARGAPPS_SEQUENCE_NUMBER_LAST: + if (param_length < 8) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 8"); + } + else + { + seq_nr_to_string(offset, little_endian, tvb, &seqNumber); + proto_item_append_text(ti, ": 0x%X%X", + seqNumber.high, seqNumber.low); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Sequence number: 0x%X%X", + seqNumber.high, seqNumber.low); + } + break; + + case PID_RECV_QUEUE_SIZE: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 recv_queue_size; + + recv_queue_size = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": %u", recv_queue_size); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Receive queue size: %u", recv_queue_size); + } + break; + + case PID_RELIABILITY_OFFERED: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 reliability_offered; + + reliability_offered = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": 0x%X", reliability_offered); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Reliability offered: 0x%X", reliability_offered); + } + break; + + case PID_RELIABILITY_REQUESTED: + if (param_length < 4) + { + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Bad parameter: length < 4"); + } + else + { + guint32 reliability_requested; + + reliability_requested = get_guint32(tvb, offset, little_endian); + proto_item_append_text(ti, ": 0x%X", reliability_requested); + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Reliability requested: 0x%X", reliability_requested); + } + break; + + default: + proto_tree_add_text(rtps_parameter_tree, tvb, offset, param_length, + "Unknown parameter value"); + break; + } /* end switch */ + + offset += param_length; + } + + *p_offset = offset; +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: ISSUE * + * * + * *********************************************************************** */ + /* hotovo 12.01.04 - JEN OTESTOVAT :] */ +static void +dissect_ISSUE(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + int min_len; + char buff[40]; + SequenceNumber sequenceNumber; /* type struct */ + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + min_len = 16; + if ((flags & FLAG_P) != 0) + min_len += 4; + if (next_submsg_offset < min_len) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= %u)", + next_submsg_offset, min_len); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Reader Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Reader Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Writer Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Writer Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Sequence Number */ + seq_nr_to_string(offset, little_endian, tvb, &sequenceNumber); + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "firstSeqNumber: 0x%X%X", + sequenceNumber.high, sequenceNumber.low); + offset += 8; + + /* Parameters */ +/* *********************************************************************** * + * - for future extension of the protocol - in * + * implementation of RTPS 1.0 can ignore the content * + * *********************************************************************** */ + + /* -- P flag |XXXX|HAPE| => masks with 00000010b = 2 */ + if ((flags & FLAG_P) != 0) + { + get_parameter_sequence(tvb, &offset, little_endian, next_submsg_offset, + rtps_submessage_tree); + } + + /* Issue Data */ + proto_tree_add_item(rtps_submessage_tree, hf_rtps_issue_data, tvb, + offset, (next_submsg_offset - offset), FALSE); + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: ACK * + * * + * *********************************************************************** */ + /* hotovo 12.01.04 - JEN OTESTOVAT :] */ +static void +dissect_ACK(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[40]; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + if (next_submsg_offset < 20) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 20)", + next_submsg_offset); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Reader Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Reader Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Writer Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Writer Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + get_bitmap(tvb,&offset,little_endian,next_submsg_offset,rtps_submessage_tree); + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: HEARTBEAT * + * * + * *********************************************************************** */ + /* hotovo 12.01.04 - JEN OTESTOVAT :] */ +static void +dissect_HEARTBEAT(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[40]; + SequenceNumber sequenceNumber; /* type struct */ + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + if (next_submsg_offset < 24) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 24)", + next_submsg_offset); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Reader Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Reader Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Writer Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Writer Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* firstSeqNumber */ + seq_nr_to_string(offset, little_endian, tvb, &sequenceNumber); + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "firstSeqNumber: 0x%X%X", + sequenceNumber.high, sequenceNumber.low); + offset +=8; + + /* lastSeqNumber */ + seq_nr_to_string(offset, little_endian, tvb, &sequenceNumber); + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "lastSeqNumber: 0x%X%X", + sequenceNumber.high, sequenceNumber.low); + offset +=8; + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: GAP * + * * + * *********************************************************************** */ + /* hotovo 12.01.04 - JEN OTESTOVAT :] */ +static void +dissect_GAP(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[40]; + SequenceNumber sequenceNumber; /* type struct */ + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + if (next_submsg_offset < 28) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 28)", + next_submsg_offset); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Reader Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Reader Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Writer Object ID */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Writer Object ID: %s ", + object_id_to_string(offset, tvb, buff)); + offset +=4; + + /* Sequence Number */ + seq_nr_to_string(offset, little_endian, tvb, &sequenceNumber); + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "firstSeqNumber: 0x%X%X", + sequenceNumber.high, sequenceNumber.low); + offset +=8; + + get_bitmap(tvb,&offset,little_endian,next_submsg_offset,rtps_submessage_tree); + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: INFO_TS * + * * + * *********************************************************************** */ + /* hotovo 12.01.04 - JEN OTESTOVAT :] */ + +static void +dissect_INFO_TS(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[10]; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + /* npTimestamp - valid if flag I = 1 * + * |XXXX|XXIE| => masks with 00000010b = 2 */ + if ((flags & FLAG_I) != 0) + { + if (next_submsg_offset < 8) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 8)", + next_submsg_offset); + return; + } + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* npTimestamp - valid if flag I = 1 * + * |XXXX|XXIE| => masks with 00000010b = 2 */ + if ((flags & FLAG_I) != 0) + { + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 8, + "ntpTimestamp: %s (sec)", + get_NtpTime(offset, tvb, little_endian,buff)); + offset +=8; + } + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: INFO_SRC * + * * + * *********************************************************************** */ +/* hotovo 12.01.04 JEN OTESTOVAT :] */ +static void +dissect_INFO_SRC(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[200]; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + if (next_submsg_offset < 16) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 16)", + next_submsg_offset); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* IPAddress */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "appIP address: %s", + IP_to_string(offset, tvb, little_endian,buff)); + offset +=4; + + /* Protocol Version */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 2, + "Protocol RTPS version %s -new", + protocol_version_to_string(offset, tvb, buff)); + offset +=2; + + /* Vendor Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 2, + "VendorId: %s -new", + vendor_id_to_string(offset, tvb, buff)); + offset +=2; + + /* Host Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Host ID: %s", + host_id_to_string(offset,tvb,buff)); + offset+=4; + + /* App Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "App ID: %s-new", + app_id_to_string(offset, tvb, buff)); + offset +=4; + +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: INFO_REPLY * + * * + * *********************************************************************** */ + /* hotovo 11.01.04 :] */ +static void +dissect_INFO_REPLY(tvbuff_t *tvb, gint offset, guint8 flags, + gboolean little_endian, int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + int min_len; + char buff_ip[10], buff_port[10]; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + /* 'multicastReplyAdress' and 'multicastReplyPort' are * + * parts of submessage INFO REPLY which are available * + * only when FLAG M=1 flags: XXXX XXME */ + + if ((flags & FLAG_M) != 0) + min_len = 16; + else + min_len = 8; + if (next_submsg_offset < min_len) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= %u)", + next_submsg_offset, min_len); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Unicat Reply IPAddress */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Unicast Reply IP Adress: %s", + IP_to_string(offset, tvb, little_endian,buff_ip)); + offset +=4; + + + /* Unicast Reply Port */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Unicast Reply IP Port: %s", + port_to_string(offset, tvb, little_endian,buff_port)); + offset +=4; + + + /* 'multicastReplyAdress' and 'multicastReplyPort' are * + * parts of submessage INFO REPLY which are available * + * only when FLAG M=1 flags: XXXX XXME */ + + if ((flags & FLAG_M) != 0) + { + /* Multicast Reply IPAddress */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Multicast Reply IP Adress: %s", + IP_to_string(offset, tvb, little_endian,buff_ip)); + offset +=4; + + /* Multicast Reply Port */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Multicast Reply IP Port: %s", + port_to_string(offset, tvb, little_endian,buff_port)); + offset +=4; + + } +} + +/* *********************************************************************** */ + + +/* *********************************************************************** * + * * + * subdissector for submessage: INFO_DST * + * * + * *********************************************************************** */ + /* HOTOVO 12.01.04 - JEN OTESOVAT :]*/ +static void +dissect_INFO_DST(tvbuff_t *tvb, gint offset, guint8 flags, + int next_submsg_offset, + proto_tree *rtps_submessage_tree) +{ + char buff[200]; + + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_submessage_flags, + tvb, offset, 1, flags); + offset +=1; + + if (next_submsg_offset < 8) + { + proto_tree_add_uint_format(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset, + "Octets to next header: %u (bogus, must be >= 8)", + next_submsg_offset); + return; + } + proto_tree_add_uint(rtps_submessage_tree, hf_rtps_octets_to_next_header, + tvb, offset, 2, next_submsg_offset); + offset +=2; + next_submsg_offset += offset; + + /* Host Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "Host ID: %s", + host_id_to_string(offset,tvb,buff)); + offset+=4; + + /* App Id */ + proto_tree_add_text(rtps_submessage_tree, tvb, offset, 4, + "App ID: %s-new", + app_id_to_string(offset, tvb, buff)); + offset +=4; + +} + +/* *********************************************************************** * + * * + * Register the protocol with Ethereal * + * * + * *********************************************************************** */ + +void proto_register_rtps(void) +{ + static hf_register_info hf[] = { + + { &hf_rtps_submessage_id, + { "Submessage Id", "rtps.submessage_id", + FT_UINT8, BASE_HEX, VALS(submessage_id_vals), 0x0, + "Submessage flags", HFILL }}, + + { &hf_rtps_submessage_flags, + { "Submessage flags", "rtps.submessage_flags", + FT_UINT8, BASE_HEX, NULL, 0x0, + "Submessage flags", HFILL }}, + + { &hf_rtps_octets_to_next_header, + { "Octets to next header", "rtps.octets_to_next_header", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Octets to next header", HFILL }}, + + { &hf_rtps_parameter_id, + { "Parameter Id", "rtps.parameter_id", + FT_UINT16, BASE_HEX, VALS(parameter_id_vals), 0x0, + "Parameter Id", HFILL }}, + + { &hf_rtps_parameter_length, + { "Parameter Length", "rtps.parameter_length", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Parameter Length", HFILL }}, + + { &hf_rtps_issue_data, + { "User Data", "rtps.issue_data", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Issue Data", HFILL }}, + }; + + static gint *ett[] = { + &ett_rtps, + &ett_rtps_submessage, + &ett_rtps_bitmap, + &ett_rtps_parameter_sequence, + &ett_rtps_parameter, + }; + + proto_rtps = proto_register_protocol("Real-Time Publish-Subscribe Wire Protocol", + "RTPS", "rtps"); + proto_register_field_array(proto_rtps, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + +} + + +void +proto_reg_handoff_rtps(void) +{ + heur_dissector_add("udp", dissect_rtps, proto_rtps); +} + diff --git a/src/main/resource/testFiles/wireshark/revFiles/3f2283_9e184d_epan#dissectors#packet-bacapp.c b/src/main/resource/testFiles/wireshark/revFiles/3f2283_9e184d_epan#dissectors#packet-bacapp.c new file mode 100644 index 0000000..47d3b71 --- /dev/null +++ b/src/main/resource/testFiles/wireshark/revFiles/3f2283_9e184d_epan#dissectors#packet-bacapp.c @@ -0,0 +1,5741 @@ +/* packet-bacapp.c + * Routines for BACnet (APDU) dissection + * Copyright 2001, Hartmut Mueller , FH Dortmund + * Enhanced by Steve Karg, 2005, , Atlanta + * Enhanced by Herbert Lischka, 2005, , Berlin + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Copied from README.developer,v 1.23 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "packet-bacapp.h" + +/* some necessary forward function prototypes */ +static guint +fApplicationTypesEnumerated (tvbuff_t *tvb, proto_tree *tree, guint offset, + const gchar *label, const value_string *vs); + +static const char *bacapp_unknown_service_str = "unknown service"; +static const char *ASHRAE_Reserved_Fmt = "(%d) Reserved for Use by ASHRAE"; +static const char *Vendor_Proprietary_Fmt = "(%d) Vendor Proprietary Value"; + +static const value_string +BACnetTypeName[] = { + {0, "Confirmed-Request "}, + {1, "Unconfirmed-Request "}, + {2, "SimpleACK "}, + {3, "ComplexACK "}, + {4, "SegmentACK "}, + {5, "Error "}, + {6, "Reject "}, + {7, "Abort "}, + {0, NULL } +}; + +static const true_false_string segments_follow = { + "Segmented Request", + "Unsegmented Request" +}; + +static const true_false_string more_follow = { + "More Segments Follow", + "No More Segments Follow" +}; + +static const true_false_string segmented_accept = { + "Segmented Response accepted", + "Segmented Response not accepted" +}; + +static const true_false_string +BACnetTagClass = { + "Context Specific Tag", + "Application Tag" +}; + +static const value_string +BACnetMaxSegmentsAccepted [] = { + {0,"Unspecified"}, + {1,"2 segments"}, + {2,"4 segments"}, + {3,"8 segments"}, + {4,"16 segments"}, + {5,"32 segments"}, + {6,"64 segments"}, + {7,"Greater than 64 segments"}, + {0,NULL } +}; + +static const value_string +BACnetMaxAPDULengthAccepted [] = { + {0,"Up to MinimumMessageSize (50 octets)"}, + {1,"Up to 128 octets"}, + {2,"Up to 206 octets (fits in a LonTalk frame)"}, + {3,"Up to 480 octets (fits in an ARCNET frame)"}, + {4,"Up to 1024 octets"}, + {5,"Up to 1476 octets (fits in an ISO 8802-3 frame)"}, + {6,"reserved by ASHRAE"}, + {7,"reserved by ASHRAE"}, + {8,"reserved by ASHRAE"}, + {9,"reserved by ASHRAE"}, + {10,"reserved by ASHRAE"}, + {11,"reserved by ASHRAE"}, + {12,"reserved by ASHRAE"}, + {13,"reserved by ASHRAE"}, + {14,"reserved by ASHRAE"}, + {15,"reserved by ASHRAE"}, + {0,NULL} +}; + +static const value_string +BACnetRejectReason [] = { + {0,"other"}, + {1,"buffer-overflow"}, + {2,"inconsistent-parameters"}, + {3,"invalid-parameter-data-type"}, + {4,"invalid-tag"}, + {5,"missing-required-parameter"}, + {6,"parameter-out-of-range"}, + {7,"too-many-arguments"}, + {8,"undefined-enumeration"}, + {9,"unrecognized-service"}, + {0,NULL} +}; + +static const value_string +BACnetApplicationTagNumber [] = { + {0,"Null"}, + {1,"Boolean"}, + {2,"Unsigned Integer"}, + {3,"Signed Integer (2's complement notation)"}, + {4,"Real (ANSI/IEE-754 floating point)"}, + {5,"Double (ANSI/IEE-754 double precision floating point)"}, + {6,"Octet String"}, + {7,"Character String"}, + {8,"Bit String"}, + {9,"Enumerated"}, + {10,"Date"}, + {11,"Time"}, + {12,"BACnetObjectIdentifier"}, + {13,"reserved by ASHRAE"}, + {14,"reserved by ASHRAE"}, + {15,"reserved by ASHRAE"}, + {0,NULL} +}; + +static const value_string +BACnetAction [] = { + {0,"direct"}, + {1,"reverse"}, + {0,NULL} +}; + +static const value_string +BACnetFileAccessMethod [] = { + {0,"record-access"}, + {1,"stream-access"}, + {0,NULL} +}; + +/* For some reason, BACnet defines the choice parameter + in the file read and write services backwards from the + BACnetFileAccessMethod enumeration. +*/ +static const value_string +BACnetFileAccessOption [] = { + {0,"stream access"}, + {1,"record access"}, + {0,NULL} +}; + +static const value_string +BACnetFileStartOption [] = { + {0, "File Start Position: "}, + {1, "File Start Record: "}, + {0, NULL} +}; + +static const value_string +BACnetFileRequestCount [] = { + {0, "Requested Octet Count: "}, + {1, "Requested Record Count: "}, + {0, NULL} +}; + +static const value_string +BACnetFileWriteInfo [] = { + {0, "File Data: "}, + {1, "Record Count: "}, + {0, NULL} +}; + +static const value_string +BACnetAbortReason [] = { + {0,"other"}, + {1,"buffer-overflow"}, + {2,"invalid-apdu-in-this-state"}, + {3,"preempted-by-higher-priority-task"}, + {4,"segmentation-not-supported"}, + {0,NULL} +}; + +static const value_string +BACnetLifeSafetyMode [] = { + {0,"off"}, + {1,"on"}, + {2,"test"}, + {3,"manned"}, + {4,"unmanned"}, + {5,"armed"}, + {6,"disarmed"}, + {7,"prearmed"}, + {8,"slow"}, + {9,"fast"}, + {10,"disconnected"}, + {11,"enabled"}, + {12,"disabled"}, + {13,"atomic-release-disabled"}, + {14,"default"}, + {0,NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetLifeSafetyOperation [] = { + {0,"none"}, + {1,"silence"}, + {2,"silence-audible"}, + {3,"silence-visual"}, + {4,"reset"}, + {5,"reset-alarm"}, + {6,"reset-fault"}, + {7,"unsilence"}, + {8,"unsilence-audible"}, + {9,"unsilence-visual"}, + {0,NULL} +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetLimitEnable [] = { + {0,"lowLimitEnable"}, + {1,"highLimitEnable"}, + {0,NULL} +}; + +static const value_string +BACnetLifeSafetyState [] = { + {0,"quiet"}, + {1,"pre-alarm"}, + {2,"alarm"}, + {3,"fault"}, + {4,"fault-pre-alarm"}, + {5,"fault-alarm"}, + {6,"not-ready"}, + {7,"active"}, + {8,"tamper"}, + {9,"test-alarm"}, + {10,"test-active"}, + {11,"test-fault"}, + {12,"test-fault-alarm"}, + {13,"holdup"}, + {14,"duress"}, + {15,"tamper-alarm"}, + {16,"abnormal"}, + {17,"emergency-power"}, + {18,"delayed"}, + {19,"blocked"}, + {20,"local-alarm"}, + {21,"general-alarm"}, + {22,"supervisory"}, + {23,"test-supervisory"}, + {0,NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetConfirmedServiceChoice [] = { + {0,"acknowledgeAlarm"}, + {1,"confirmedCOVNotification"}, + {2,"confirmedEventNotification"}, + {3,"getAlarmSummary"}, + {4,"getEnrollmentSummary"}, + {5,"subscribeCOV"}, + {6,"atomicReadFile"}, + {7,"atomicWriteFile"}, + {8,"addListElement"}, + {9,"removeListElement"}, + {10,"createObject"}, + {11,"deleteObject"}, + {12,"readProperty"}, + {13,"readPropertyConditional"}, + {14,"readPropertyMultiple"}, + {15,"writeProperty"}, + {16,"writePropertyMultiple"}, + {17,"deviceCommunicationControl"}, + {18,"confirmedPrivateTransfer"}, + {19,"confirmedTextMessage"}, + {20,"reinitializeDevice"}, + {21,"vtOpen"}, + {22,"vtClose"}, + {23,"vtData"}, + {24,"authenticate"}, + {25,"requestKey"}, + {26,"readRange"}, + {27,"lifeSafetyOperation"}, + {28,"subscribeCOVProperty"}, + {29,"getEventInformation"}, + {30,"reserved by ASHRAE"}, + {0, NULL} +}; + +static const value_string +BACnetReliability [] = { + {0,"no-fault-detected"}, + {1,"no-sensor"}, + {2,"over-range"}, + {3,"under-range"}, + {4,"open-loop"}, + {5,"shorted-loop"}, + {6,"no-output"}, + {7,"unreliable-other"}, + {8,"process-error"}, + {9,"multi-state-fault"}, + {0,NULL} +}; + +static const value_string +BACnetUnconfirmedServiceChoice [] = { + {0,"i-Am"}, + {1,"i-Have"}, + {2,"unconfirmedCOVNotification"}, + {3,"unconfirmedEventNotification"}, + {4,"unconfirmedPrivateTransfer"}, + {5,"unconfirmedTextMessage"}, + {6,"timeSynchronization"}, + {7,"who-Has"}, + {8,"who-Is"}, + {9,"utcTimeSynchonization"}, + {0,NULL} +}; + +static const value_string +BACnetUnconfirmedServiceRequest [] = { + {0,"i-Am-Request"}, + {1,"i-Have-Request"}, + {2,"unconfirmedCOVNotification-Request"}, + {3,"unconfirmedEventNotification-Request"}, + {4,"unconfirmedPrivateTransfer-Request"}, + {5,"unconfirmedTextMessage-Request"}, + {6,"timeSynchronization-Request"}, + {7,"who-Has-Request"}, + {8,"who-Is-Request"}, + {9,"utcTimeSynchonization-Request"}, + {0,NULL} +}; + +static const value_string +BACnetObjectType [] = { + {0,"analog-input object"}, + {1,"analog-output object"}, + {2,"analog-value object"}, + {3,"binary-input object"}, + {4,"binary-output object"}, + {5,"binary-value object"}, + {6,"calendar object"}, + {7,"command object"}, + {8,"device object"}, + {9,"event-enrollment object"}, + {10,"file object"}, + {11,"group object"}, + {12,"loop object"}, + {13,"multi-state-input object"}, + {14,"multi-state-output object"}, + {15,"notification-class object"}, + {16,"program object"}, + {17,"schedule object"}, + {18,"averaging object"}, + {19,"multi-state-value object"}, + {20,"trend-log object"}, + {21,"life-safety-point object"}, + {22,"life-safety-zone object"}, + {23,"accumulator object"}, + {24,"pulse-converter object"}, + {0, NULL} +/* Enumerated values 0-127 are reserved for definition by ASHRAE. + Enumerated values 128-1023 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetEngineeringUnits [] = { + {0,"Sq Meters"}, + {1,"Sq Feet"}, + {2,"Milliamperes"}, + {3,"Amperes"}, + {4,"Ohms"}, + {5,"Volts"}, + {6,"Kilovolts"}, + {7,"Megavolts"}, + {8,"Volt Amperes"}, + {9,"Kilovolt Amperes"}, + {10,"Megavolt Amperes"}, + {11,"Volt Amperes Reactive"}, + {12,"Kilovolt Amperes Reactive"}, + {13,"Megavolt Amperes Ractive"}, + {14,"Degrees Phase"}, + {15,"Power Factor"}, + {16,"Joules"}, + {17,"Kilojoules"}, + {18,"Watt Hours"}, + {19,"Kilowatt Hours"}, + {20,"BTUs"}, + {21,"Therms"}, + {22,"Ton Hours"}, + {23,"Joules Per Kg Dry Air"}, + {24,"BTUs Per Pound Dry Air"}, + {25,"Cycles Per Hour"}, + {26,"Cycles Per Minute"}, + {27,"Hertz"}, + {28,"Gramms Of Water Per Kilogram Dry Air"}, + {29,"Relative Humidity"}, + {30,"Millimeters"}, + {31,"Meters"}, + {32,"Inches"}, + {33,"Feed"}, + {34,"Watts Per Sq Foot"}, + {35,"Watts Per Sq meter"}, + {36,"Lumens"}, + {37,"Lux"}, + {38,"Foot Candels"}, + {39,"Kilograms"}, + {40,"Pounds Mass"}, + {41,"Tons"}, + {42,"Kgs per Second"}, + {43,"Kgs Per Minute"}, + {44,"Kgs Per Hour"}, + {45,"Pounds Mass Per Minute"}, + {46,"Pounds Mass Per Hour"}, + {47,"Watt"}, + {48,"Kilowatts"}, + {49,"Megawatts"}, + {50,"BTUs Per Hour"}, + {51,"Horsepower"}, + {52,"Tons Refrigeration"}, + {53,"Pascals"}, + {54,"Kilopascals"}, + {55,"Bars"}, + {56,"Pounds Force Per Square Inch"}, + {57,"Centimeters Of Water"}, + {58,"Inches Of Water"}, + {59,"Millimeters Of Mercury"}, + {60,"Centimeters Of Mercury"}, + {61,"Inches Of Mercury"}, + {62,"Degrees Celsius"}, + {63,"Degress Kelvin"}, + {64,"Degrees Fahrenheit"}, + {65,"Degree Days Celsius"}, + {66,"Degree Days Fahrenheit"}, + {67,"Years"}, + {68,"Months"}, + {69,"Weeks"}, + {70,"Days"}, + {71,"Hours"}, + {72,"Minutes"}, + {73,"Seconds"}, + {74,"Meters Per Second"}, + {75,"Kilometers Per Hour"}, + {76,"Feed Per Second"}, + {77,"Feet Per Minute"}, + {78,"Miles Per Hour"}, + {79,"Cubic Feet"}, + {80,"Cubic Meters"}, + {81,"Imperial Gallons"}, + {82,"Liters"}, + {83,"US Gallons"}, + {84,"Cubic Feet Per Minute"}, + {85,"Cubic Meters Per Second"}, + {86,"Imperial Gallons Per Minute"}, + {87,"Liters Per Second"}, + {88,"Liters Per Minute"}, + {89,"US Gallons Per Minute"}, + {90,"Degrees Angular"}, + {91,"Degrees Celsius Per Hour"}, + {92,"Degrees Celsius Per Minute"}, + {93,"Degrees Fahrenheit Per Hour"}, + {94,"Degrees Fahrenheit Per Minute"}, + {95,"No Units"}, + {96,"Parts Per Million"}, + {97,"Parts Per Billion"}, + {98,"Percent"}, + {99,"Pecent Per Second"}, + {100,"Per Minute"}, + {101,"Per Second"}, + {102,"Psi Per Degree Fahrenheit"}, + {103,"Radians"}, + {104,"Revolutions Per Min"}, + {105,"Currency1"}, + {106,"Currency2"}, + {107,"Currency3"}, + {108,"Currency4"}, + {109,"Currency5"}, + {110,"Currency6"}, + {111,"Currency7"}, + {112,"Currency8"}, + {113,"Currency9"}, + {114,"Currency10"}, + {115,"Sq Inches"}, + {116,"Sq Centimeters"}, + {117,"BTUs Per Pound"}, + {118,"Centimeters"}, + {119,"Pounds Mass Per Second"}, + {120,"Delta Degrees Fahrenheit"}, + {121,"Delta Degrees Kelvin"}, + {122,"Kilohms"}, + {123,"Megohms"}, + {124,"Millivolts"}, + {125,"Kilojoules Per Kg"}, + {126,"Megajoules"}, + {127,"Joules Per Degree Kelvin"}, + {128,"Joules Per Kg Degree Kelvin"}, + {129,"Kilohertz"}, + {130,"Megahertz"}, + {131,"Per Hour"}, + {132,"Milliwatts"}, + {133,"Hectopascals"}, + {134,"Millibars"}, + {135,"Cubic Meters Per Hour"}, + {136,"Liters Per Hour"}, + {137,"KWatt Hours Per Square Meter"}, + {138,"KWatt Hours Per Square Foot"}, + {139,"Megajoules Per Square Meter"}, + {140,"Megajoules Per Square Foot"}, + {141,"Watts Per Sq Meter Degree Kelvin"}, + {142,"Cubic Feet Per Second"}, + {143,"Percent Obstruction Per Foot"}, + {144,"Percent Obstruction Per Meter"}, + {145,"milliohms"}, + {146,"megawatt-hours"}, + {147,"kilo-btus"}, + {148,"mega-btus"}, + {149,"kilojoules-per-kilogram-dry-air"}, + {150,"megajoules-per-kilogram-dry-air"}, + {151,"kilojoules-per-degree-Kelvin"}, + {152,"megajoules-per-degree-Kelvin"}, + {153,"newton"}, + {154,"grams-per-second"}, + {155,"grams-per-minute"}, + {156,"tons-per-hour"}, + {157,"kilo-btus-per-hour"}, + {158,"hundredths-seconds"}, + {159,"milliseconds"}, + {160,"newton-meters"}, + {161,"millimeters-per-second"}, + {162,"millimeters-per-minute"}, + {163,"meters-per-minute"}, + {164,"meters-per-hour"}, + {165,"cubic-meters-per-minute"}, + {166,"meters-per-second-per-second"}, + {167,"amperes-per-meter"}, + {168,"amperes-per-square-meter"}, + {169,"ampere-square-meters"}, + {170,"farads"}, + {171,"henrys"}, + {172,"ohm-meters"}, + {173,"siemens"}, + {174,"siemens-per-meter"}, + {175,"teslas"}, + {176,"volts-per-degree-Kelvin"}, + {177,"volts-per-meter"}, + {178,"webers"}, + {179,"candelas"}, + {180,"candelas-per-square-meter"}, + {181,"degrees-Kelvin-per-hour"}, + {182,"degrees-Kelvin-per-minute"}, + {183,"joule-seconds"}, + {184,"radians-per-second"}, + {185,"square-meters-per-Newton"}, + {186,"kilograms-per-cubic-meter"}, + {187,"newton-seconds"}, + {188,"newtons-per-meter"}, + {189,"watts-per-meter-per-degree-Kelvin"}, + {0,NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetErrorCode [] = { + {0,"other"}, + {1,"authentication-failed"}, + {2,"character-set-not-supported"}, + {3,"configuration-in-progress"}, + {4,"device-busy"}, + {5,"file-access-denied"}, + {6,"incompatible-security-levels"}, + {7,"inconsistent-parameters"}, + {8,"inconsistent-selection-criterion"}, + {9,"invalid-data-type"}, + {10,"invalid-file-access-method"}, + {11,"invalid-file-start-position"}, + {12,"invalid-operator-name"}, + {13,"invalid-parameter-data-type"}, + {14,"invalid-time-stamp"}, + {15,"key-generation-error"}, + {16,"missing-required-parameter"}, + {17,"no-objects-of-specified-type"}, + {18,"no-space-for-object"}, + {19,"no-space-to-add-list-element"}, + {20,"no-space-to-write-property"}, + {21,"no-vt-sessions-available"}, + {22,"property-is-not-a-list"}, + {23,"object-deletion-not-permitted"}, + {24,"object-identifier-already-exists"}, + {25,"operational-problem"}, + {26,"password-failure"}, + {27,"read-access-denied"}, + {28,"security-not-supported"}, + {29,"service-request-denied"}, + {30,"timeout"}, + {31,"unknown-object"}, + {32,"unknown-property"}, + {33,"removed enumeration"}, + {34,"unknown-vt-class"}, + {35,"unknown-vt-session"}, + {36,"unsupported-object-type"}, + {37,"value-out-of-range"}, + {38,"vt-session-already-closed"}, + {39,"vt-session-termination-failure"}, + {40,"write-access-denied"}, + {41,"character-set-not-supported"}, + {42,"invalid-array-index"}, + {43,"cov-subscription-failed"}, + {44,"not-cov-property"}, + {45,"optional-functionaltity-not-supported"}, + {46,"invalid-configuration-data"}, + {47,"datatype-not-supported"}, + {48,"duplicate-name"}, + {49,"duplicate-object-id"}, + {50,"property-is-not-an-array"}, + {0, NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to the + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetPropertyIdentifier [] = { + {0,"acked-transition"}, + {1,"ack-required"}, + {2,"action"}, + {3,"action-text"}, + {4,"active-text"}, + {5,"active-vt-session"}, + {6,"alarm-value"}, + {7,"alarm-values"}, + {8,"all"}, + {9,"all-write-successful"}, + {10,"apdu-segment-timeout"}, + {11,"apdu-timeout"}, + {12,"application-software-version"}, + {13,"archive"}, + {14,"bias"}, + {15,"change-of-state-count"}, + {16,"change-of-state-time"}, + {17,"notification-class"}, + {18,"the property in this place was deleted"}, + {19,"controlled-variable-reference"}, + {20,"controlled-variable-units"}, + {21,"controlled-variable-value"}, + {22,"cov-increment"}, + {23,"datelist"}, + {24,"daylights-savings-status"}, + {25,"deadband"}, + {26,"derivative-constant"}, + {27,"derivative-constant-units"}, + {28,"description"}, + {29,"description-of-halt"}, + {30,"device-address-binding"}, + {31,"device-type"}, + {32,"effective-period"}, + {33,"elapsed-active-time"}, + {34,"error-limit"}, + {35,"event-enable"}, + {36,"event-state"}, + {37,"event-type"}, + {38,"exception-schedule"}, + {39,"fault-values"}, + {40,"feedback-value"}, + {41,"file-access-method"}, + {42,"file-size"}, + {43,"file-type"}, + {44,"firmware-revision"}, + {45,"high-limit"}, + {46,"inactive-text"}, + {47,"in-progress"}, + {48,"instance-of"}, + {49,"integral-constant"}, + {50,"integral-constant-units"}, + {51,"issue-confirmed-notifications"}, + {52,"limit-enable"}, + {53,"list-of-group-members"}, + {54,"list-of-object-property-references"}, + {55,"list-of-session-keys"}, + {56,"local-date"}, + {57,"local-time"}, + {58,"location"}, + {59,"low-limit"}, + {60,"manipulated-variable-reference"}, + {61,"maximum-output"}, + {62,"max-apdu-length-accepted"}, + {63,"max-info-frames"}, + {64,"max-master"}, + {65,"max-pres-value"}, + {66,"minimum-off-time"}, + {67,"minimum-on-time"}, + {68,"minimum-output"}, + {69,"min-pres-value"}, + {70,"model-name"}, + {71,"modification-date"}, + {72,"notify-type"}, + {73,"number-of-APDU-retries"}, + {74,"number-of-states"}, + {75,"object-identifier"}, + {76,"object-list"}, + {77,"object-name"}, + {78,"object-property-reference"}, + {79,"object-type"}, + {80,"optional"}, + {81,"out-of-service"}, + {82,"output-units"}, + {83,"event-parameters"}, + {84,"polarity"}, + {85,"present-value"}, + {86,"priority"}, + {87,"priority-array"}, + {88,"priority-for-writing"}, + {89,"process-identifier"}, + {90,"program-change"}, + {91,"program-location"}, + {92,"program-state"}, + {93,"proportional-constant"}, + {94,"proportional-constant-units"}, + {95,"protocol-conformance-class"}, + {96,"protocol-object-types-supported"}, + {97,"protocol-services-supported"}, + {98,"protocol-version"}, + {99,"read-only"}, + {100,"reason-for-halt"}, + {101,"recipient"}, + {102,"recipient-list"}, + {103,"reliability"}, + {104,"relinquish-default"}, + {105,"required"}, + {106,"resolution"}, + {107,"segmentation-supported"}, + {108,"setpoint"}, + {109,"setpoint-reference"}, + {110,"state-text"}, + {111,"status-flags"}, + {112,"system-status"}, + {113,"time-delay"}, + {114,"time-of-active-time-reset"}, + {115,"time-of-state-count-reset"}, + {116,"time-synchronization-recipients"}, + {117,"units"}, + {118,"update-interval"}, + {119,"utc-offset"}, + {120,"vendor-identifier"}, + {121,"vendor-name"}, + {122,"vt-class-supported"}, + {123,"weekly-schedule"}, + {124,"attempted-samples"}, + {125,"average-value"}, + {126,"buffer-size"}, + {127,"client-cov-increment"}, + {128,"cov-resubscription-interval"}, + {129,"current-notify-time"}, + {130,"event-time-stamp"}, + {131,"log-buffer"}, + {132,"log-device-object-property"}, + {133,"log-enable"}, + {134,"log-interval"}, + {135,"maximum-value"}, + {136,"minimum-value"}, + {137,"notification-threshold"}, + {138,"previous-notify-time"}, + {139,"protocol-revision"}, + {140,"records-since-notification"}, + {141,"record-count"}, + {142,"start-time"}, + {143,"stop-time"}, + {144,"stop-when-full"}, + {145,"total-record-count"}, + {146,"valid-samples"}, + {147,"window-interval"}, + {148,"window-samples"}, + {149,"maximum-value-time-stamp"}, + {150,"minimum-value-time-stamp"}, + {151,"variance-value"}, + {152,"active-cov-subscriptions"}, + {153,"backup-failure-timeout"}, + {154,"configuration-files"}, + {155,"database-revision"}, + {156,"direct-reading"}, + {157,"last-restore-time"}, + {158,"maintenance-required"}, + {159,"member-of"}, + {160,"mode"}, + {161,"operation-expected"}, + {162,"setting"}, + {163,"silenced"}, + {164,"tracking-value"}, + {165,"zone-members"}, + {166,"life-safety-alarm-values"}, + {167,"max-segments-accepted"}, + {168,"profile-name"}, + {169,"auto-slave-discovery"}, + {170,"manual-slave-address-binding"}, + {171,"slave-address-binding"}, + {172,"slave-proxy-enable"}, + {173,"last-notify-time"}, + {174,"schedule-default"}, + {175,"accepted-modes"}, + {176,"adjust-value"}, + {177,"count"}, + {178,"count-before-change"}, + {179,"count-change-time"}, + {180,"cov-period"}, + {181,"input-reference"}, + {182,"limit-monitoring-interval"}, + {183,"logging-device"}, + {184,"logging-record"}, + {185,"prescale"}, + {186,"pulse-rate"}, + {187,"scale"}, + {188,"scale-factor"}, + {189,"update-time"}, + {190,"value-before-change"}, + {191,"value-set"}, + {192,"value-change-time"}, + {0, NULL} +/* Enumerated values 0-511 are reserved for definition by ASHRAE. + Enumerated values 512-4194303 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetBinaryPV [] = { + {0,"inactive"}, + {1,"active"}, + {0,NULL} +}; + + +static const value_string +BACnetCharacterSet [] = { + {0,"ANSI X3.4"}, + {1,"IBM/Microsoft DBCS"}, + {2,"JIS C 6226"}, + {3,"ISO 10646(UCS-4)"}, + {4,"ISO 10646(UCS-2)"}, + {5,"ISO 18859-1"}, + {0,NULL} +}; + +static const value_string +BACnetStatusFlags [] = { + {0,"in-alarm"}, + {1,"fault"}, + {2,"overridden"}, + {3,"out-of-service"}, + {0,NULL} +}; + +static const value_string +BACnetMessagePriority [] = { + {0,"normal"}, + {1,"urgent"}, + {0,NULL} +}; + +static const value_string +BACnetAcknowledgementFilter [] = { + {0,"all"}, + {1,"acked"}, + {2,"not-acked"}, + {0,NULL} +}; + +static const value_string +BACnetResultFlags [] = { + {0,"firstitem"}, + {1,"lastitem"}, + {2,"moreitems"}, + {0,NULL} +}; + +static const value_string +BACnetRelationSpecifier [] = { + {0,"equal"}, + {1,"not-equal"}, + {2,"less-than"}, + {3,"greater-than"}, + {4,"less-than-or-equal"}, + {5,"greater-than-or-equal"}, + {0,NULL} +}; + +static const value_string +BACnetSelectionLogic [] = { + {0,"and"}, + {1,"or"}, + {2,"all"}, + {0,NULL} +}; + +static const value_string +BACnetEventStateFilter [] = { + {0,"offnormal"}, + {1,"fault"}, + {2,"normal"}, + {3,"all"}, + {4,"active"}, + {0,NULL} +}; + +static const value_string +BACnetEventTransitionBits [] = { + {0,"to-offnormal"}, + {1,"to-fault"}, + {2,"to-normal"}, + {0,NULL} +}; + +static const value_string +BACnetSegmentation [] = { + {0,"segmented-both"}, + {1,"segmented-transmit"}, + {2,"segmented-receive"}, + {3,"no-segmentation"}, + {0,NULL} +}; + +static const value_string +BACnetSilencedState [] = { + {0,"unsilenced"}, + {1,"audible-silenced"}, + {2,"visible-silenced"}, + {3,"all-silenced"}, + {0,NULL} +}; + +static const value_string +BACnetDeviceStatus [] = { + {0,"operational"}, + {1,"operational-read-only"}, + {2,"download-required"}, + {3,"download-in-progress"}, + {4,"non-operational"}, + {5,"backup-in-progress"}, + {0,NULL} +}; + +static const value_string +BACnetEnableDisable [] = { + {0,"enable"}, + {1,"disable"}, + {2,"disable-initiation"}, + {0,NULL} +}; + +static const value_string +months [] = { + {1,"January" }, + {2,"February" }, + {3,"March" }, + {4,"April" }, + {5,"May" }, + {6,"June" }, + {7,"July" }, + {8,"August" }, + {9,"September" }, + {10,"October" }, + {11,"November" }, + {12,"December" }, + {255,"any month" }, + {0,NULL } +}; + +static const value_string +weekofmonth [] = { + {1,"days numbered 1-7" }, + {2,"days numbered 8-14" }, + {3,"days numbered 15-21" }, + {4,"days numbered 22-28" }, + {5,"days numbered 29-31" }, + {6,"last 7 days of this month" }, + {255,"any week of this month" }, + {0,NULL } +}; + +static const value_string +days [] = { + {1,"Monday" }, + {2,"Tuesday" }, + {3,"Wednesday" }, + {4,"Thursday" }, + {5,"Friday" }, + {6,"Saturday" }, + {7,"Sunday" }, + {255,"any day of week" }, + {0,NULL }, +}; + +static const value_string +BACnetErrorClass [] = { + {0,"device" }, + {1,"object" }, + {2,"property" }, + {3,"resources" }, + {4,"security" }, + {5,"services" }, + {6,"vt" }, + {0,NULL }, +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetVTClass [] = { + {0,"default-terminal" }, + {1,"ansi-x3-64" }, + {2,"dec-vt52" }, + {3,"dec-vt100" }, + {4,"dec-vt200" }, + {5,"hp-700-94" }, + {6,"ibm-3130" }, + {0,NULL }, +}; + +static const value_string +BACnetEventType [] = { + {0,"change-of-bitstring" }, + {1,"change-of-state" }, + {2,"change-of-value" }, + {3,"command-failure" }, + {4,"floating-limit" }, + {5,"out-of-range" }, + {6,"complex-event-type" }, + {7,"buffer-ready" }, + {8,"change-of-life-safety" }, + {9,"extended" }, + {10,"buffer-ready" }, + {11,"unsigned-range" }, + {0,NULL }, +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. + It is expected that these enumerated values will correspond + to the use of the complex-event-type CHOICE [6] of the + BACnetNotificationParameters production. */ +}; + +static const value_string +BACnetEventState [] = { + {0,"normal" }, + {1,"fault" }, + {2,"offnormal" }, + {3,"high-limit" }, + {4,"low-limit" }, + {5,"life-safety-alarm" }, + {0,NULL }, +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetLogStatus [] = { + {0,"log-disabled" }, + {1,"buffer-purged" }, + {0,NULL }, +}; + +static const value_string +BACnetMaintenance [] = { + {0,"none" }, + {1,"periodic-test" }, + {2,"need-service-operational" }, + {3,"need-service-inoperative" }, + {0,NULL }, +}; + +static const value_string +BACnetNotifyType [] = { + {0,"alarm" }, + {1,"event" }, + {2,"ack-notification" }, + {0,NULL }, +}; + +static const value_string +BACnetServicesSupported [] = { + {0,"acknowledgeAlarm"}, + {1,"confirmedCOVNotification"}, + {2,"confirmedEventNotification"}, + {3,"getAlarmSummary"}, + {4,"getEnrollmentSummary"}, + {5,"subscribeCOV"}, + {6,"atomicReadFile"}, + {7,"atomicWriteFile"}, + {8,"addListElement"}, + {9,"removeListElement"}, + {10,"createObject"}, + {11,"deleteObject"}, + {12,"readProperty"}, + {13,"readPropertyConditional"}, + {14,"readPropertyMultiple"}, + {15,"writeProperty"}, + {16,"writePropertyMultiple"}, + {17,"deviceCommunicationControl"}, + {18,"confirmedPrivateTransfer"}, + {19,"confirmedTextMessage"}, + {20,"reinitializeDevice"}, + {21,"vtOpen"}, + {22,"vtClose"}, + {23,"vtData"}, + {24,"authenticate"}, + {25,"requestKey"}, + {26,"i-Am"}, + {27,"i-Have"}, + {28,"unconfirmedCOVNotification"}, + {29,"unconfirmedEventNotification"}, + {30,"unconfirmedPrivateTransfer"}, + {31,"unconfirmedTextMessage"}, + {32,"timeSynchronization"}, + {33,"who-Has"}, + {34,"who-Is"}, + {35,"readRange"}, + {36,"utcTimeSynchronization"}, + {37,"lifeSafetyOperation"}, + {38,"subscribeCOVProperty"}, + {39,"getEventInformation"}, + {0, NULL} +}; + +static const value_string +BACnetPropertyStates [] = { + {0,"boolean-value"}, + {1,"binary-value"}, + {2,"event-type"}, + {3,"polarity"}, + {4,"program-change"}, + {5,"program-state"}, + {6,"reason-for-halt"}, + {7,"reliability"}, + {8,"state"}, + {9,"system-status"}, + {10,"units"}, + {11,"unsigned-value"}, + {12,"life-safety-mode"}, + {13,"life-safety-state"}, + {0,NULL} +/* Tag values 0-63 are reserved for definition by ASHRAE. + Tag values of 64-254 may be used by others to accommodate + vendor specific properties that have discrete or enumerated values, + subject to the constraints described in Clause 23. */ +}; + +static const value_string +BACnetProgramError [] = { + {0,"normal"}, + {1,"load-failed"}, + {2,"internal"}, + {3,"program"}, + {4,"other"}, + {0,NULL} +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetProgramRequest [] = { + {0,"ready"}, + {1,"load"}, + {2,"run"}, + {3,"halt"}, + {4,"restart"}, + {4,"unload"}, + {0,NULL} +}; + +static const value_string +BACnetProgramState [] = { + {0,"idle"}, + {1,"loading"}, + {2,"running"}, + {3,"waiting"}, + {4,"halted"}, + {4,"unloading"}, + {0,NULL} +}; + +static const value_string +BACnetReinitializedStateOfDevice [] = { + {0,"coldstart"}, + {1,"warmstart"}, + {2,"startbackup"}, + {3,"endbackup"}, + {4,"startrestore"}, + {5,"endrestore"}, + {6,"abortrestore"}, + {0,NULL} +}; + +static const value_string +BACnetPolarity [] = { + {0,"normal"}, + {1,"reverse"}, + {0,NULL} +}; + +static const value_string +BACnetTagNames[] = { + { 5, "Extended Value" }, + { 6, "Opening Tag" }, + { 7, "Closing Tag" }, + { 0, NULL } +}; + +static const value_string +BACnetReadRangeOptions[] = { + { 3, "range byPosition" }, + { 4, "range byTime" }, + { 5, "range timeRange" }, + { 6, "range bySequenceNumber" }, + { 7, "range byTime" }, + { 0, NULL } +}; + +static int proto_bacapp = -1; +static int hf_bacapp_type = -1; +static int hf_bacapp_pduflags = -1; +static int hf_bacapp_SEG = -1; +static int hf_bacapp_MOR = -1; +static int hf_bacapp_SA = -1; +static int hf_bacapp_response_segments = -1; +static int hf_bacapp_max_adpu_size = -1; +static int hf_bacapp_invoke_id = -1; +static int hf_bacapp_objectType = -1; +static int hf_bacapp_instanceNumber = -1; +static int hf_bacapp_sequence_number = -1; +static int hf_bacapp_window_size = -1; +static int hf_bacapp_service = -1; +static int hf_bacapp_NAK = -1; +static int hf_bacapp_SRV = -1; +static int hf_BACnetRejectReason = -1; +static int hf_BACnetAbortReason = -1; +static int hf_BACnetApplicationTagNumber = -1; +static int hf_BACnetContextTagNumber = -1; +static int hf_BACnetExtendedTagNumber = -1; +static int hf_BACnetNamedTag = -1; +static int hf_BACnetTagClass = -1; +static int hf_BACnetCharacterSet = -1; +static int hf_bacapp_tag = -1; +static int hf_bacapp_tag_lvt = -1; +static int hf_bacapp_tag_value8 = -1; +static int hf_bacapp_tag_value16 = -1; +static int hf_bacapp_tag_value32 = -1; +static int hf_bacapp_tag_ProcessId = -1; +static int hf_bacapp_tag_initiatingObjectType = -1; +static int hf_bacapp_vpart = -1; + +static int hf_bacapp_uservice = -1; + + +static gint ett_bacapp = -1; +static gint ett_bacapp_control = -1; +static gint ett_bacapp_tag = -1; +static gint ett_bacapp_list = -1; +static gint ett_bacapp_value = -1; + +static dissector_handle_t data_handle; +static gint32 propertyIdentifier = -1; +static guint32 object_type = 4096; + +static guint8 bacapp_flags = 0; +static guint8 bacapp_seq = 0; + +/* Used when there are ranges of reserved and proprietary enumerations */ +static const char* +val_to_split_str(guint32 val, guint32 split_val, const value_string *vs, + const char *fmt, const char *split_fmt) +{ + if (val < split_val) + return val_to_str(val, vs, fmt); + else + return val_to_str(val, vs, split_fmt); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns true if the extended value is used */ +static gboolean tag_is_extended_value(guint8 tag) +{ + return (tag & 0x07) == 5; +} + +static gboolean tag_is_opening(guint8 tag) +{ + return (tag & 0x07) == 6; +} + +static gboolean tag_is_closing(guint8 tag) +{ + return (tag & 0x07) == 7; +} + +/* from clause 20.2.1.1 Class + class bit shall be one for context specific tags */ +/* returns true if the tag is context specific */ +static gboolean tag_is_context_specific(guint8 tag) +{ + return (tag & 0x08) != 0; +} + +static gboolean tag_is_extended_tag_number(guint8 tag) +{ + return ((tag & 0xF0) == 0xF0); +} + +static guint32 object_id_type(guint32 object_identifier) +{ + return ((object_identifier >> 22) & 0x3FF); +} + +static guint32 object_id_instance(guint32 object_identifier) +{ + return (object_identifier & 0x3FFFFF); +} + +static guint +fTagNo (tvbuff_t *tvb, guint offset) +{ + return (guint)(tvb_get_guint8(tvb, offset) >> 4); +} + +static gboolean +fUnsigned32 (tvbuff_t *tvb, guint offset, guint32 lvt, guint32 *val) +{ + gboolean valid = TRUE; + + switch (lvt) { + case 1: + *val = tvb_get_guint8(tvb, offset); + break; + case 2: + *val = tvb_get_ntohs(tvb, offset); + break; + case 3: + *val = tvb_get_ntoh24(tvb, offset); + break; + case 4: + *val = tvb_get_ntohl(tvb, offset); + break; + default: + valid = FALSE; + break; + } + + return valid; +} + +static gboolean +fUnsigned64 (tvbuff_t *tvb, guint offset, guint32 lvt, guint64 *val) +{ + gboolean valid = FALSE; + gint64 value = 0; + guint8 data, i; + + if (lvt && (lvt <= 8)) { + valid = TRUE; + data = tvb_get_guint8(tvb, offset); + for (i = 0; i < lvt; i++) { + data = tvb_get_guint8(tvb, offset+i); + value = (value << 8) + data; + } + *val = value; + } + + return valid; +} + +/* BACnet Signed Value uses 2's compliment notation, but with a twist: + All signed integers shall be encoded in the smallest number of octets + possible. That is, the first octet of any multi-octet encoded value + shall not be X'00' if the most significant bit (bit 7) of the second + octet is 0, and the first octet shall not be X'FF' if the most + significant bit of the second octet is 1. ASHRAE-135-2004-20.2.5 */ +static gboolean +fSigned64 (tvbuff_t *tvb, guint offset, guint32 lvt, gint64 *val) +{ + gboolean valid = FALSE; + gint64 value = 0; + guint8 data, i; + + /* we can only handle 7 bytes for a 64-bit value due to signed-ness */ + if (lvt && (lvt <= 7)) { + valid = TRUE; + data = tvb_get_guint8(tvb, offset); + if ((data & 0x80) != 0) + value = (-1 << 8) | data; + else + value = data; + for (i = 1; i < lvt; i++) { + data = tvb_get_guint8(tvb, offset+i); + value = (value << 8) + data; + } + *val = value; + } + + return valid; +} + +static guint +fTagHeaderTree (tvbuff_t *tvb, proto_tree *tree, guint offset, + guint8 *tag_no, guint8* tag_info, guint32 *lvt) +{ + guint8 tag; + guint8 value; + guint tag_len = 1; + guint lvt_len = 1; /* used for tree display of lvt */ + guint lvt_offset; /* used for tree display of lvt */ + proto_item *ti; + proto_tree *subtree; + + lvt_offset = offset; + tag = tvb_get_guint8(tvb, offset); + *tag_info = 0; + *lvt = tag & 0x07; + /* To solve the problem of lvt values of 6/7 being indeterminate - it */ + /* can mean open/close tag or length of 6/7 after the length is */ + /* computed below - store whole tag info, not just context bit. */ + if (tag_is_context_specific(tag)) *tag_info = tag & 0x0F; + *tag_no = tag >> 4; + if (tag_is_extended_tag_number(tag)) { + *tag_no = tvb_get_guint8(tvb, offset + tag_len++); + } + if (tag_is_extended_value(tag)) { /* length is more than 4 Bytes */ + lvt_offset += tag_len; + value = tvb_get_guint8(tvb, lvt_offset); + tag_len++; + if (value == 254) { /* length is encoded with 16 Bits */ + *lvt = tvb_get_ntohs(tvb, lvt_offset+1); + tag_len += 2; + lvt_len += 2; + } else if (value == 255) { /* length is encoded with 32 Bits */ + *lvt = tvb_get_ntohl(tvb, lvt_offset+1); + tag_len += 4; + lvt_len += 4; + } else + *lvt = value; + } + if (tree) + { + if (tag_is_closing(tag) || tag_is_opening(tag)) + ti = proto_tree_add_text(tree, tvb, offset, tag_len, + "%s: %u", match_strval( + tag & 0x07, BACnetTagNames), + *tag_no); + else if (tag_is_context_specific(tag)) { + ti = proto_tree_add_text(tree, tvb, offset, tag_len, + "Context Tag: %u, Length/Value/Type: %u", + *tag_no, *lvt); + } else + ti = proto_tree_add_text(tree, tvb, offset, tag_len, + "Application Tag: %s, Length/Value/Type: %u", + val_to_str(*tag_no, + BACnetApplicationTagNumber, + ASHRAE_Reserved_Fmt), + *lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + /* details if needed */ + proto_tree_add_item(subtree, hf_BACnetTagClass, tvb, offset, 1, FALSE); + if (tag_is_extended_tag_number(tag)) { + proto_tree_add_uint_format(subtree, + hf_BACnetContextTagNumber, + tvb, offset, 1, tag, + "Extended Tag Number"); + proto_tree_add_item(subtree, + hf_BACnetExtendedTagNumber, + tvb, offset + 1, 1, FALSE); + } else { + if (tag_is_context_specific(tag)) + proto_tree_add_item(subtree, + hf_BACnetContextTagNumber, + tvb, offset, 1, FALSE); + else + proto_tree_add_item(subtree, + hf_BACnetApplicationTagNumber, + tvb, offset, 1, FALSE); + } + if (tag_is_closing(tag) || tag_is_opening(tag)) + proto_tree_add_item(subtree, + hf_BACnetNamedTag, + tvb, offset, 1, FALSE); + else if (tag_is_extended_value(tag)) { + proto_tree_add_item(subtree, + hf_BACnetNamedTag, + tvb, offset, 1, FALSE); + proto_tree_add_uint(subtree, hf_bacapp_tag_lvt, + tvb, lvt_offset, lvt_len, *lvt); + } else + proto_tree_add_uint(subtree, hf_bacapp_tag_lvt, + tvb, lvt_offset, lvt_len, *lvt); + } + + return tag_len; +} + +static guint +fTagHeader (tvbuff_t *tvb, guint offset, guint8 *tag_no, guint8* tag_info, + guint32 *lvt) +{ + return fTagHeaderTree (tvb, NULL, offset, tag_no, tag_info, lvt); +} + +static guint +fNullTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_item *ti; + proto_tree *subtree; + + ti = proto_tree_add_text(tree, tvb, offset, 1, "%sNULL", label); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset + 1; +} + +static guint +fBooleanTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt = 0; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + guint bool_len = 1; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_info && lvt == 1) + { + lvt = tvb_get_guint8(tvb, offset+1); + ++bool_len; + } + + ti = proto_tree_add_text(tree, tvb, offset, bool_len, + "%s%s", label, lvt == 0 ? "FALSE" : "TRUE"); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset + bool_len; +} + +static guint +fUnsignedTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint64 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + /* only support up to an 8 byte (64-bit) integer */ + if (fUnsigned64 (tvb, offset + tag_len, lvt, &val)) + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s(Unsigned) %" PRIu64, label, val); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s - %u octets (Unsigned)", label, lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +/* set split_val to zero when not needed */ +static guint +fEnumeratedTagSplit (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label, + const value_string *vs, guint32 split_val) +{ + guint32 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + /* only support up to a 4 byte (32-bit) enumeration */ + if (fUnsigned32 (tvb, offset+tag_len, lvt, &val)) { + if (vs) + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s %s", label, val_to_split_str(val, split_val, vs, + ASHRAE_Reserved_Fmt,Vendor_Proprietary_Fmt)); + else + ti =proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s %u", label, val); + } else { + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s - %u octets (enumeration)", label, lvt); + } + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fEnumeratedTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label, + const value_string *vs) +{ + return fEnumeratedTagSplit (tvb, tree, offset, label, vs, 0); +} + +static guint +fSignedTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + gint64 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (fSigned64 (tvb, offset + tag_len, lvt, &val)) + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s(Signed) %" PRId64, label, val); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s - %u octets (Signed)", label, lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fRealTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + gfloat f_val = 0.0; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, offset, &tag_no, &tag_info, &lvt); + f_val = tvb_get_ntohieee_float(tvb, offset+tag_len); + ti = proto_tree_add_text(tree, tvb, offset, 4+tag_len, + "%s%f (Real)", label, f_val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+4; +} + +static guint +fDoubleTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + gdouble d_val = 0.0; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, offset, &tag_no, &tag_info, &lvt); + d_val = tvb_get_ntohieee_double(tvb, offset+tag_len); + ti = proto_tree_add_text(tree, tvb, offset, 8+tag_len, + "%s%lf (Double)", label, d_val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+8; +} + +static guint +fProcessId (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32 (tvb, offset+tag_len, lvt, &val)) + ti = proto_tree_add_uint(tree, hf_bacapp_tag_ProcessId, + tvb, offset, lvt+tag_len, val); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "Process Identifier - %u octets (Signed)", lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + offset += tag_len + lvt; + + return offset; +} + +static guint +fTimeSpan (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32 (tvb, offset+tag_len, lvt, &val)) + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s (hh.mm.ss): %d.%02d.%02d%s", + label, + (val / 3600), ((val % 3600) / 60), (val % 60), + val == 0 ? " (indefinite)" : ""); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s - %u octets (Signed)", label, lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fWeekNDay (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint32 month, weekOfMonth, dayOfWeek; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + month = tvb_get_guint8(tvb, offset+tag_len); + weekOfMonth = tvb_get_guint8(tvb, offset+tag_len+1); + dayOfWeek = tvb_get_guint8(tvb, offset+tag_len+2); + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, "%s %s, %s", + val_to_str(month, months, "month (%d) not found"), + val_to_str(weekOfMonth, weekofmonth, "week of month (%d) not found"), + val_to_str(dayOfWeek, days, "day of week (%d) not found")); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fDate (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint32 year, month, day, weekday; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + year = tvb_get_guint8(tvb, offset+tag_len); + month = tvb_get_guint8(tvb, offset+tag_len+1); + day = tvb_get_guint8(tvb, offset+tag_len+2); + weekday = tvb_get_guint8(tvb, offset+tag_len+3); + if ((year == 255) && (day == 255) && (month == 255) && (weekday == 255)) + { + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%sany", label); + } + else if (year != 255) + { + year += 1900; + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s%s %d, %d, (Day of Week = %s)", + label, val_to_str(month, + months, + "month (%d) not found"), + day, year, val_to_str(weekday, + days, + "(%d) not found")); + } + else + { + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s%s %d, any year, (Day of Week = %s)", + label, val_to_str(month, months, "month (%d) not found"), + day, val_to_str(weekday, days, "(%d) not found")); + } + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fTime (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint32 hour, minute, second, msec, lvt; + guint8 tag_no, tag_info; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + hour = tvb_get_guint8(tvb, offset+tag_len); + minute = tvb_get_guint8(tvb, offset+tag_len+1); + second = tvb_get_guint8(tvb, offset+tag_len+2); + msec = tvb_get_guint8(tvb, offset+tag_len+3); + if ((hour == 255) && (minute == 255) && (second == 255) && (msec == 255)) + ti = proto_tree_add_text(tree, tvb, offset, + lvt+tag_len, "%sany", label); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "%s%d:%02d:%02d.%d %s = %02d:%02d:%02d.%d", + label, + hour > 12 ? hour - 12 : hour, + minute, second, msec, + hour > 12 ? "P.M." : "A.M.", + hour, minute, second, msec); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fDateTime (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + proto_tree *subtree = tree; + proto_item *tt; + + if (label != NULL) { + tt = proto_tree_add_text (subtree, tvb, offset, 1, "%s", label); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + } + offset = fDate (tvb,subtree,offset,"Date: "); + return fTime (tvb,subtree,offset,"Time: "); +} + +static guint +fTimeValue (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* closing Tag, but not for me */ + return offset; + } + offset = fTime (tvb,tree,offset,"Time: "); + offset = fApplicationTypes(tvb, tree, offset, "Value: "); + } + return offset; +} + +static guint +fCalendaryEntry (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + + switch (fTagNo(tvb, offset)) { + case 0: /* Date */ + offset = fDate (tvb, tree, offset, "Date: "); + break; + case 1: /* dateRange */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateRange (tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* BACnetWeekNDay */ + offset = fWeekNDay (tvb, tree, offset); + break; + default: + return offset; + break; + } + + return offset; +} + +static guint +fTimeStamp (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + if (tvb_length_remaining(tvb, offset) > 0) { /* don't loop, it's a CHOICE */ + switch (fTagNo(tvb, offset)) { + case 0: /* time */ + offset = fTime (tvb, tree, offset, "timestamp: "); + break; + case 1: /* sequenceNumber */ + offset = fUnsignedTag (tvb, tree, offset, "sequence Number: "); + break; + case 2: /* dateTime */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime (tvb, tree, offset, "timestamp: "); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + break; + } + } + return offset; +} + +#if 0 +static guint +fSetpointReference (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* setpointReference */ + offset = fBACnetObjectPropertyReference (tvb,tree,offset); + break; + default: + return offset; + break; + } + } + return offset; +} +#endif + +#if 0 +static guint +fClientCOV (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) > 0) { + offset = fApplicationTypes(tvb,tree,offset, "increment: "); + } + return offset; +} + +static guint +fDestination (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) > 0) { + offset = fApplicationTypesEnumerated(tvb,tree,offset, + "valid Days: ", days); + offset = fTime (tvb,tree,offset,"from time: "); + offset = fTime (tvb,tree,offset,"to time: "); + offset = fRecipient (tvb,tree,offset); + offset = fProcessId (tvb,tree,offset); + offset = fApplicationTypes (tvb,tree,offset, + "issue confirmed notifications: "); + offset = fApplicationTypesEnumerated (tvb,tree,offset, + "transitions: ", BACnetEventTransitionBits); + } + return offset; +} + +#endif + +static guint +fOctetString (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label, guint32 lvt) +{ + gchar *tmp; + guint start = offset; + guint8 tag_no, tag_info; + proto_tree* subtree = tree; + proto_item* ti = 0; + + offset += fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + if (lvt > 0) + { + tmp = tvb_bytes_to_str(tvb, offset, lvt); + ti = proto_tree_add_text(tree, tvb, offset, lvt, "%s %s", label, tmp); + offset += lvt; + } + + if (ti) + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + + fTagHeaderTree(tvb, subtree, start, &tag_no, &tag_info, &lvt); + + return offset; +} + +static guint +fAddress (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint offs; + + offset = fUnsignedTag (tvb, tree, offset, "network-number"); + offs = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (lvt == 0) { + proto_tree_add_text(tree, tvb, offset, offs, "mac-address: broadcast"); + offset += offs; + } else + offset = fOctetString (tvb, tree, offset, "mac-address: ", lvt); + return offset; +} + +static guint +fSessionKey (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fOctetString (tvb,tree,offset,"session key: ", 8); + return fAddress (tvb,tree,offset); +} + +static guint +fObjectIdentifier (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_length; + proto_item *ti; + proto_tree *subtree; + guint32 object_id; + + tag_length = fTagHeader(tvb, offset, &tag_no, &tag_info, &lvt); + object_id = tvb_get_ntohl(tvb,offset+tag_length); + object_type = object_id_type(object_id); + ti = proto_tree_add_text(tree, tvb, offset, tag_length + 4, + "ObjectIdentifier: %s, %u", + val_to_split_str(object_type, + 128, + BACnetObjectType, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), + object_id_instance(object_id)); + /* here are the details of how we arrived at the above text */ + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset += tag_length; + proto_tree_add_item(subtree, hf_bacapp_objectType, tvb, offset, 4, FALSE); + proto_tree_add_item(subtree, hf_bacapp_instanceNumber, tvb, offset, 4, FALSE); + offset += 4; + + return offset; +} + +static guint +fRecipient (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + + fTagHeader(tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_no < 2) { + if (tag_no == 0) { /* device */ + offset = fObjectIdentifier (tvb, tree, offset); + } + else { /* address */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAddress (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + } + } + return offset; +} + +static guint +fRecipientProcess (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* recipient */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fRecipient (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* processId */ + offset = fProcessId (tvb, tree, offset); + lastoffset = offset; + break; + default: + break; + } + } + return offset; +} + +static guint +fAddressBinding (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fObjectIdentifier (tvb, tree, offset); + return fAddress (tvb, tree, offset); +} + +static guint +fActionCommand (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + continue; + } + switch (tag_no) { + + case 0: /* deviceIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 2: /* propertyIdentifier */ + offset = fPropertyIdentifier (tvb, subtree, offset); + break; + case 3: /* propertyArrayIndex */ + offset = fUnsignedTag (tvb,subtree,offset,"Property Array Index: "); + break; + case 4: /* propertyValue */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "propertyValue"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + case 5: /* priority */ + offset = fUnsignedTag (tvb,subtree,offset,"Priority: "); + break; + case 6: /* postDelay */ + offset = fUnsignedTag (tvb,subtree,offset,"Post Delay: "); + break; + case 7: /* quitOnFailure */ + offset = fBooleanTag(tvb, subtree, offset, + "Quit On Failure: "); + break; + case 8: /* writeSuccessful */ + offset = fBooleanTag(tvb, subtree, offset, + "Write Successful: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fActionList (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fActionCommand (tvb,tree,offset); +} + +static guint +fPropertyIdentifier (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + propertyIdentifier = 0; /* global Variable */ + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32 (tvb, offset+tag_len, lvt, (guint32 *)&propertyIdentifier)) + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "property Identifier: %s", + val_to_split_str(propertyIdentifier, 512, + BACnetPropertyIdentifier, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + else + ti = proto_tree_add_text(tree, tvb, offset, lvt+tag_len, + "Property Identifier - %u octets", lvt); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fCharacterString (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info, character_set; + guint32 lvt, l; + size_t inbytesleft, outbytesleft = 512; + guint offs, extra = 1; + guint8 *str_val; + guint8 bf_arr[512], *out = &bf_arr[0]; + proto_item *ti; + proto_tree *subtree; + guint start = offset; + + if (tvb_length_remaining(tvb, offset) > 0) { + + offs = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + character_set = tvb_get_guint8(tvb, offset+offs); + /* Account for code page if DBCS */ + if (character_set == 1) + { + extra = 3; + } + offset += (offs+extra); + lvt -= (extra); + + do { + l = inbytesleft = min(lvt, 255); + /* + * XXX - are we guaranteed that these encoding + * names correspond, on *all* platforms with + * iconv(), to the encodings we want? + * If not (and perhaps even if so), we should + * perhaps have our own iconv() implementation, + * with a different name, so that we control the + * encodings it supports and the names of those + * encodings. + * + * We should also handle that in the general + * string handling code, rather than making it + * specific to the BACAPP dissector, as many + * other dissectors need to handle various + * character encodings. + */ + str_val = tvb_get_ephemeral_string(tvb, offset, l); + /** this decoding may be not correct for multi-byte characters, Lka */ + switch (character_set) { + case 0x00: /* ANSI_X3.4 */ + fConvertXXXtoUTF8(str_val, &inbytesleft, out, &outbytesleft, "ANSI_X3.4"); + break; + case 1: /* IBM/MICROSOFT DBCS */ + out = str_val; + break; + case 2: /* JIS C 6226 */ + out = str_val; + break; + case 3: /* UCS-4 */ + fConvertXXXtoUTF8(str_val, &inbytesleft, out, &outbytesleft, "UCS-4BE"); + break; + case 4: /* UCS-2 */ + fConvertXXXtoUTF8(str_val, &inbytesleft, out, &outbytesleft, "UCS-2BE"); + break; + case 5: /* ISO8859-1 */ + fConvertXXXtoUTF8(str_val, &inbytesleft, out, &outbytesleft, "ISO8859-1"); + break; + default: + out = str_val; + break; + } + ti = proto_tree_add_text(tree, tvb, offset, l, "%s'%s'", label, out); + lvt-=l; + offset+=l; + } while (lvt > 0); + + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + + fTagHeaderTree (tvb, subtree, start, &tag_no, &tag_info, &lvt); + proto_tree_add_item(subtree, hf_BACnetCharacterSet, tvb, start+offs, 1, FALSE); + if (character_set == 1) + { + proto_tree_add_text(subtree, tvb, start+offs+1, 2, "Code Page: %d", tvb_get_ntohs(tvb, start+offs+1)); + } + } + return offset; +} + +static guint +fBitStringTagVS (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label, + const value_string *src) +{ + guint8 tag_no, tag_info, tmp; + gint j, unused, skip; + guint offs; + guint32 lvt, i, numberOfBytes; + guint8 bf_arr[256]; + + offs = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + numberOfBytes = lvt-1; /* Ignore byte for unused bit count */ + offset+=offs; + unused = tvb_get_guint8(tvb, offset); /* get the unused Bits */ + skip = 0; + for (i = 0; i < numberOfBytes; i++) { + tmp = tvb_get_guint8(tvb, (offset)+i+1); + if (i == numberOfBytes-1) { skip = unused; } + for (j = 0; j < 8-skip; j++) { + if (src != NULL) { + if (tmp & (1 << (7 - j))) + proto_tree_add_text(tree, tvb, + offset+i+1, 1, + "%s%s = TRUE", + label, + val_to_str((guint) (i*8 +j), + src, + ASHRAE_Reserved_Fmt)); + else + proto_tree_add_text(tree, tvb, + offset+i+1, 1, + "%s%s = FALSE", + label, + val_to_str((guint) (i*8 +j), + src, + ASHRAE_Reserved_Fmt)); + + } else { + bf_arr[min(255,(i*8)+j)] = tmp & (1 << (7 - j)) ? '1' : '0'; + } + } + } + + if (src == NULL) + { + bf_arr[min(255,numberOfBytes*8-unused)] = 0; + proto_tree_add_text(tree, tvb, offset, lvt, "%sB'%s'", label, bf_arr); + } + + offset+=lvt; + + return offset; +} + +static guint +fBitStringTag (tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + return fBitStringTagVS (tvb, tree, offset, label, NULL); +} + +/* handles generic application types, as well as enumerated and enumerations + with reserved and proprietarty ranges (split) */ +static guint +fApplicationTypesEnumeratedSplit (tvbuff_t *tvb, proto_tree *tree, guint offset, + const gchar *label, const value_string *src, guint32 split_val) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + + if (tvb_length_remaining(tvb, offset) > 0) { + + tag_len = fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (!tag_is_context_specific(tag_info)) + { + switch (tag_no) { + case 0: /** NULL 20.2.2 */ + offset = fNullTag(tvb, tree, offset, label); + break; + case 1: /** BOOLEAN 20.2.3 */ + offset = fBooleanTag(tvb, tree, offset, label); + break; + case 2: /** Unsigned Integer 20.2.4 */ + offset = fUnsignedTag(tvb, tree, offset, label); + break; + case 3: /** Signed Integer 20.2.5 */ + offset = fSignedTag(tvb, tree, offset, label); + break; + case 4: /** Real 20.2.6 */ + offset = fRealTag(tvb, tree, offset, label); + break; + case 5: /** Double 20.2.7 */ + offset = fDoubleTag(tvb, tree, offset, label); + break; + case 6: /** Octet String 20.2.8 */ + offset = fOctetString (tvb, tree, offset, label, lvt); + break; + case 7: /** Character String 20.2.9 */ + offset = fCharacterString (tvb,tree,offset,label); + break; + case 8: /** Bit String 20.2.10 */ + offset = fBitStringTagVS (tvb, tree, offset, label, src); + break; + case 9: /** Enumerated 20.2.11 */ + offset = fEnumeratedTagSplit (tvb, tree, offset, label, src, split_val); + break; + case 10: /** Date 20.2.12 */ + offset = fDate (tvb, tree, offset, label); + break; + case 11: /** Time 20.2.13 */ + offset = fTime (tvb, tree, offset, label); + break; + case 12: /** BACnetObjectIdentifier 20.2.14 */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 13: /* reserved for ASHRAE */ + case 14: + case 15: + proto_tree_add_text(tree, tvb, offset, lvt+tag_len, "%s'reserved for ASHRAE'", label); + offset+=lvt+tag_len; + break; + default: + break; + } + + } + } + return offset; +} + +static guint +fApplicationTypesEnumerated (tvbuff_t *tvb, proto_tree *tree, guint offset, + const gchar *label, const value_string *vs) +{ + return fApplicationTypesEnumeratedSplit(tvb, tree, offset, label, vs, 0); +} + +static guint +fApplicationTypes (tvbuff_t *tvb, proto_tree *tree, guint offset, + const gchar *label) +{ + return fApplicationTypesEnumeratedSplit(tvb, tree, offset, label, NULL, 0); +} + +static guint +fContextTaggedValue(tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + gint tvb_len; + + (void)label; + tag_len = fTagHeader(tvb, offset, &tag_no, &tag_info, &lvt); + /* cap the the suggested length in case of bad data */ + tvb_len = tvb_length_remaining(tvb, offset+tag_len); + if ((tvb_len >= 0) && ((guint32)tvb_len < lvt)) + { + lvt = tvb_len; + } + ti = proto_tree_add_text(tree, tvb, offset+tag_len, lvt, + "Context Value (as %u DATA octets)", lvt); + + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset + tag_len + lvt; +} + +static guint +fAbstractSyntaxNType (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0, depth = 0; + char ar[256]; + + if (propertyIdentifier >= 0) + { + g_snprintf (ar, sizeof(ar), "%s: ", + val_to_split_str(propertyIdentifier, 512, + BACnetPropertyIdentifier, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + } + else + { + g_snprintf (ar, sizeof(ar), "Abstract Type: "); + } + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* closing tag, but not for me */ + if (depth <= 0) return offset; + } + + /* Application Tags */ + switch (propertyIdentifier) { + case 2: /* BACnetActionList */ + offset = fActionList (tvb,tree,offset); + break; + case 30: /* BACnetAddressBinding */ + offset = fAddressBinding (tvb,tree,offset); + break; + case 55: /* list-of-session-keys */ + fSessionKey (tvb, tree, offset); + break; + case 79: /* object-type */ + case 96: /* protocol-object-types-supported */ + offset = fApplicationTypesEnumeratedSplit (tvb, tree, offset, ar, + BACnetObjectType, 128); + break; + case 97: /* Protocol-Services-Supported */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, ar, + BACnetServicesSupported); + break; + case 107: /* segmentation-supported */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, ar, + BACnetSegmentation); + break; + case 111: /* Status-Flags */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, ar, + BACnetStatusFlags); + break; + case 112: /* System-Status */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, ar, + BACnetDeviceStatus); + break; + case 117: /* units */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, ar, + BACnetEngineeringUnits); + break; + case 87: /* priority-array */ + offset = fPriorityArray (tvb, tree, offset); + break; + case 38: /* exception-schedule */ + if (object_type < 128) + { + offset = fSpecialEvent (tvb,tree,offset); + } + break; + case 123: /* weekly-schedule */ + if (object_type < 128) + { + offset = fWeeklySchedule (tvb,tree,offset); + } + break; + case 159: /* member-of */ + case 165: /* zone-members */ + fDeviceObjectReference (tvb, tree, offset); + break; + default: + if (tag_info) + { + if (tag_is_opening(tag_info)) + { + ++depth; + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + } + else if (tag_is_closing(tag_info)) + { + --depth; + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + } + else + { + offset = fContextTaggedValue(tvb, tree, offset, ar); + } + } + else + { + offset = fApplicationTypes (tvb, tree, offset, ar); + } + break; + } + } + return offset; + +} + +static guint +fPropertyValue (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 tagoffset) +{ + guint lastoffset = offset; + proto_item *tt; + proto_tree *subtree; + guint8 tag_no, tag_info; + guint32 lvt; + + offset = fPropertyReference(tvb, tree, offset, tagoffset, 0); + if (offset > lastoffset) + { + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_no == tagoffset+2) { /* Value - might not be present in ReadAccessResult */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(tree, tvb, offset, 1, "propertyValue"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + } + } + return offset; +} + +static guint +fBACnetPropertyValue (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fPropertyValue(tvb, tree, offset, 0); + if (offset > lastoffset) + { + /* detect optional priority + by looking to see if the next tag is context tag number 3 */ + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_context_specific(tag_info) && (tag_no == 3)) + offset = fUnsignedTag (tvb,tree,offset,"Priority: "); + } + } + return offset; +} + +static guint +fSubscribeCOVPropertyRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* ProcessId */ + offset = fUnsignedTag (tvb, tree, offset, "subscriber Process Id: "); + break; + case 1: /* monitored ObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 2: /* issueConfirmedNotifications */ + offset = fBooleanTag (tvb, tree, offset, "issue Confirmed Notifications: "); + break; + case 3: /* life time */ + offset = fTimeSpan (tvb,tree,offset,"life time"); + break; + case 4: /* monitoredPropertyIdentifier */ + offset = fBACnetPropertyReference (tvb, tree, offset, 0); + break; + case 5: /* covIncrement */ + offset = fRealTag (tvb, tree, offset, "COV Increment: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fSubscribeCOVRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fSubscribeCOVPropertyRequest(tvb, tree, offset); +} + +static guint +fWhoHas (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* deviceInstanceLowLimit */ + offset = fUnsignedTag (tvb, tree, offset, "device Instance Low Limit: "); + break; + case 1: /* deviceInstanceHighLimit */ + offset = fUnsignedTag (tvb, tree, offset, "device Instance High Limit: "); + break; + case 2: /* BACnetObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 3: /* messageText */ + offset = fCharacterString (tvb,tree,offset, "Object Name: "); + break; + default: + return offset; + } + } + return offset; +} + + +static guint +fDailySchedule (tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_opening(tag_info) && tag_no == 0) + { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); /* opening context tag 0 */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + /* should be closing context tag 0 */ + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + return offset; + } + + offset = fTimeValue (tvb, subtree, offset); + } + } + else if (tag_no == 0 && lvt == 0) + { + /* not sure null (empty array element) is legal */ + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + return offset; +} + +static guint +fWeeklySchedule (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint i=1; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; /* outer encoding will print out closing tag */ + } + tt = proto_tree_add_text(tree, tvb, offset, 0, val_to_str(i++, days, "day of week (%d) not found")); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset = fDailySchedule (tvb,subtree,offset); + } + return offset; +} + + +static guint +fUTCTimeSynchronizationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) <= 0) + return offset; + + return fDateTime (tvb, tree, offset, "UTC-Time: "); +} + +static guint +fTimeSynchronizationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) <= 0) + return offset; + + return fDateTime (tvb, tree, offset, NULL); +} + +static guint +fDateRange (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) <= 0) + return offset; + offset = fDate (tvb,tree,offset,"Start Date: "); + return fDate (tvb, tree, offset, "End Date: "); +} + +static guint +fConfirmedTextMessageRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + + case 0: /* textMessageSourceDevice */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 1: /* messageClass */ + switch (fTagNo(tvb, offset)) { + case 0: /* numeric */ + offset = fUnsignedTag (tvb, tree, offset, "message Class: "); + break; + case 1: /* character */ + offset = fCharacterString (tvb, tree, offset, "message Class: "); + break; + } + break; + case 2: /* messagePriority */ + offset = fEnumeratedTag (tvb, tree, offset, "message Priority: ", + BACnetMessagePriority); + break; + case 3: /* message */ + offset = fCharacterString (tvb, tree, offset, "message: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fUnconfirmedTextMessageRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fConfirmedTextMessageRequest(tvb, tree, offset); +} + +static guint +fConfirmedPrivateTransferRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + /* exit loop if nothing happens inside */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + if (tag_no == 2) /* Make sure it's the expected tag */ + { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + continue; + } + else + { + break; /* End loop if incorrect closing tag */ + } + } + switch (tag_no) { + + case 0: /* vendorID */ + offset = fUnsignedTag (tvb, subtree, offset, "vendor ID: "); + break; + case 1: /* serviceNumber */ + offset = fUnsignedTag (tvb, subtree, offset, "service Number: "); + break; + case 2: /*serviceParameters */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "service Parameters"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + propertyIdentifier = -1; + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + return offset; + } + } + return offset; +} + +static guint +fUnconfirmedPrivateTransferRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fConfirmedPrivateTransferRequest(tvb, tree, offset); +} + +static guint +fConfirmedPrivateTransferAck(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fConfirmedPrivateTransferRequest(tvb, tree, offset); +} + +static guint +fLifeSafetyOperationRequest(tvbuff_t *tvb, proto_tree *tree, guint offset, const gchar *label) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + if (label != NULL) { + tt = proto_tree_add_text (subtree, tvb, offset, 1, "%s", label); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + } + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: /* subscriberProcessId */ + offset = fUnsignedTag (tvb, subtree, offset, "requesting Process Id: "); + break; + case 1: /* requestingSource */ + offset = fCharacterString (tvb, tree, offset, "requesting Source: "); + break; + case 2: /* request */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "request: ", BACnetLifeSafetyOperation, 64); + break; + case 3: /* objectId */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint fBACnetPropertyStates(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + switch (fTagNo(tvb, offset)) + { + case 0: + offset = fBooleanTag (tvb, tree, offset, "boolean-value: "); + break; + case 1: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "binary-value: ", BACnetBinaryPV, 2); + break; + case 2: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "event-type: ", BACnetEventType, 12); + break; + case 3: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "polarity: ", BACnetPolarity, 2); + break; + case 4: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "program-change: ", BACnetProgramRequest, 5); + break; + case 5: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "program-state: ", BACnetProgramState, 5); + break; + case 6: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "reason-for-halt: ", BACnetProgramError, 5); + break; + case 7: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "reliability: ", BACnetReliability, 10); + break; + case 8: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "state: ", BACnetEventState, 64); + break; + case 9: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "system-status: ", BACnetDeviceStatus, 64); + break; + case 10: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "units: ", BACnetEngineeringUnits, 2); + break; + case 11: + offset = fUnsignedTag(tvb, tree, offset, "unsigned-value: "); + break; + case 12: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "life-safety-mode: ", BACnetLifeSafetyMode, 64); + break; + case 13: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "life-safety-state: ", BACnetLifeSafetyState, 64); + break; + default: + break; + } + return offset; +} + +static guint +fNotificationParameters (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = offset; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + tt = proto_tree_add_text(subtree, tvb, offset, 0, "notification parameters"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + /* Opeing tag for parameter choice */ + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: /* change-of-bitstring */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fBitStringTag (tvb, subtree, offset, + "referenced-bitstring: "); + break; + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + default: + break; + } + } + break; + case 1: /* change-of-state */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyStates(tvb, subtree, offset); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + lastoffset = offset; + break; + default: + break; + } + } + break; + case 2: /* change-of-value */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: + offset = fBitStringTag (tvb, subtree, offset, + "changed-bits: "); + break; + case 1: + offset = fRealTag (tvb, subtree, offset, + "changed-value: "); + break; + default: + break; + } + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + default: + break; + } + } + break; + case 3: /* command-failure */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* "command-value: " */ + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + case 2: /* "feedback-value: " */ + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + default: + break; + } + } + break; + case 4: /* floating-limit */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fRealTag (tvb, subtree, offset, "reference-value: "); + break; + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fRealTag (tvb, subtree, offset, "setpoint-value: "); + break; + case 3: + offset = fRealTag (tvb, subtree, offset, "error-limit: "); + default: + break; + } + } + break; + case 5: /* out-of-range */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fRealTag (tvb, subtree, offset, "exceeding-value: "); + break; + case 1: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fRealTag (tvb, subtree, offset, "deadband: "); + break; + case 3: + offset = fRealTag (tvb, subtree, offset, "exceeded-limit: "); + default: + break; + } + } + break; + case 6: + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset =fBACnetPropertyValue (tvb,subtree,offset); + } + break; + case 7: /* buffer-ready */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fObjectIdentifier (tvb, subtree, offset); /* buffer-device */ + break; + case 1: + offset = fObjectIdentifier (tvb, subtree, offset); /* buffer-object */ + break; + case 2: + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime (tvb, subtree, offset, "previous-notification: "); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 3: + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime (tvb, subtree, offset, "current-notification: "); + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + default: + break; + } + } + break; + case 8: /* change-of-life-safety */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fEnumeratedTagSplit (tvb, subtree, offset, + "new-state: ", BACnetLifeSafetyState, 256); + break; + case 1: + offset = fEnumeratedTagSplit (tvb, subtree, offset, + "new-mode: ", BACnetLifeSafetyState, 256); + break; + case 2: + offset = fBitStringTagVS (tvb, subtree, offset, + "status-flags: ", BACnetStatusFlags); + case 3: + offset = fEnumeratedTagSplit (tvb, subtree, offset, + "operation-expected: ", BACnetLifeSafetyOperation, 64); + default: + break; + } + } + break; + default: + break; + } + /* Closing tag for parameter choice */ + offset += fTagHeaderTree(tvb, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset; +} + +#if 0 +static guint +fEventParameter (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* change-of-bitstring */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: + offset = fBitStringTag (tvb, tree, offset, "bitmask: "); + break; + case 2: /* SEQUENCE OF BIT STRING */ + offset = fBitStringTagVS (tvb, tree, offset, + "bitstring value: ", BACnetEventTransitionBits); + break; + default: + return offset; + } + } + break; + case 1: /* change-of-state */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: /* SEQUENCE OF BACnetPropertyStates */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "value: ", BACnetPropertyStates, 64); + break; + default: + return offset; + } + } + break; + case 2: /* change-of-value */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: /* don't loop it, it's a CHOICE */ + switch (fTagNo(tvb, offset)) { + case 0: + offset = fBitStringTag (tvb, tree, offset, "bitmask: "); + break; + case 1: + offset = fRealTag (tvb, tree, offset, + "referenced Property Increment: "); + break; + default: + return offset; + } + default: + return offset; + } + } + break; + case 3: /* command-failure */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: + offset = fDeviceObjectPropertyReference (tvb,tree,offset); + default: + return offset; + } + } + break; + case 4: /* floating-limit */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: + offset = fDeviceObjectPropertyReference (tvb,tree,offset); + break; + case 2: + offset = fRealTag (tvb, tree, offset, "low diff limit: "); + break; + case 3: + offset = fRealTag (tvb, tree, offset, "high diff limit: "); + break; + case 4: + offset = fRealTag (tvb, tree, offset, "deadband: "); + break; + default: + return offset; + } + } + break; + case 5: /* out-of-range */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: + offset = fRealTag (tvb, tree, offset, "low limit: "); + break; + case 2: + offset = fRealTag (tvb, tree, offset, "high limit: "); + break; + case 3: + offset = fRealTag (tvb, tree, offset, "deadband: "); + break; + default: + return offset; + } + } + break; + case 6: + offset = fBACnetPropertyValue (tvb,tree,offset); + break; + case 7: /* buffer-ready */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fUnsignedTag (tvb,tree,offset,"notification threshold"); + break; + case 1: + offset = fUnsignedTag (tvb,tree,offset, + "previous notification count: "); + break; + default: + return offset; + } + } + break; + case 8: /* change-of-life-safety */ + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan (tvb, tree, offset, "Time Delay"); + break; + case 1: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "life safety alarm value: ", BACnetLifeSafetyState, 256); + break; + case 2: + offset = fEnumeratedTagSplit (tvb, tree, offset, + "alarm value: ", BACnetLifeSafetyState, 256); + break; + case 3: + offset = fDeviceObjectPropertyReference (tvb, tree, offset); + break; + default: + return offset; + } + } + break; + default: + return offset; + } + } + return offset; +} + +static guint +fLogRecord (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* timestamp */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime (tvb,tree,offset,NULL); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* logDatum: don't loop, it's a CHOICE */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: /* logStatus */ + offset = fEnumeratedTag (tvb, tree, offset, + "log status: ", BACnetLogStatus); + break; + case 1: + offset = fBooleanTag (tvb, tree, offset, "boolean-value: "); + break; + case 2: + offset = fRealTag (tvb, tree, offset, "real value: "); + break; + case 3: + offset = fUnsignedTag (tvb, tree, offset, "enum value: "); + break; + case 4: + offset = fUnsignedTag (tvb, tree, offset, "unsigned value: "); + break; + case 5: + offset = fSignedTag (tvb, tree, offset, "signed value: "); + break; + case 6: + offset = fBitStringTag (tvb, tree, offset, "bitstring value: "); + break; + case 7: + offset = fNullTag(tvb, tree, offset, "null value: "); + break; + case 8: + offset = fError (tvb,tree,offset); + break; + case 9: + offset = fRealTag (tvb, tree, offset, "time change: "); + break; + case 10: /* any Value */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: + offset = fEnumeratedTag (tvb, tree, offset, + "status Flags: ", BACnetStatusFlags); + break; + default: + return offset; + } + } + return offset; +} +#endif + +static guint +fConfirmedEventNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* ProcessId */ + offset = fProcessId (tvb,tree,offset); + break; + case 1: /* initiating ObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 2: /* event ObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 3: /* time stamp */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* notificationClass */ + offset = fUnsignedTag (tvb, tree, offset, "Notification Class: "); + break; + case 5: /* Priority */ + offset = fUnsignedTag (tvb, tree, offset, "Priority: "); + break; + case 6: /* EventType */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "Event Type: ", BACnetEventType, 64); + break; + case 7: /* messageText */ + offset = fCharacterString (tvb, tree, offset, "message Text: "); + break; + case 8: /* NotifyType */ + offset = fEnumeratedTag (tvb, tree, offset, + "Notify Type: ", BACnetNotifyType); + break; + case 9: /* ackRequired */ + offset = fBooleanTag (tvb, tree, offset, "ack Required: "); + break; + case 10: /* fromState */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "from State: ", BACnetEventState, 64); + break; + case 11: /* toState */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "to State: ", BACnetEventState, 64); + break; + case 12: /* NotificationParameters */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fNotificationParameters (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + return offset; +} + +static guint +fUnconfirmedEventNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fConfirmedEventNotificationRequest (tvb, tree, offset); +} + +static guint +fConfirmedCOVNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + lastoffset = offset; + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* ProcessId */ + offset = fProcessId (tvb,tree,offset); + break; + case 1: /* initiating DeviceId */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 2: /* monitored ObjectId */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 3: /* time remaining */ + offset = fTimeSpan (tvb, tree, offset, "Time remaining"); + break; + case 4: /* List of Values */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "list of Values"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyValue (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fUnconfirmedCOVNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fConfirmedCOVNotificationRequest (tvb, tree, offset); +} + +static guint +fAcknowledgeAlarmRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* acknowledgingProcessId */ + offset = fUnsignedTag (tvb, tree, offset, "acknowledging Process Id: "); + break; + case 1: /* eventObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 2: /* eventStateAcknowledged */ + offset = fEnumeratedTagSplit (tvb, tree, offset, + "event State Acknowledged: ", BACnetEventState, 64); + break; + case 3: /* timeStamp */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp(tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* acknowledgementSource */ + offset = fCharacterString (tvb, tree, offset, "acknowledgement Source: "); + break; + case 5: /* timeOfAcknowledgement */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp(tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fGetAlarmSummaryAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes (tvb, tree, offset, "Object Identifier: "); + offset = fApplicationTypesEnumeratedSplit (tvb, tree, offset, + "alarm State: ", BACnetEventState, 64); + offset = fApplicationTypesEnumerated (tvb, tree, offset, + "acknowledged Transitions: ", BACnetEventTransitionBits); + } + return offset; +} + +static guint +fGetEnrollmentSummaryRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* acknowledgmentFilter */ + offset = fEnumeratedTag (tvb, tree, offset, + "acknowledgment Filter: ", BACnetAcknowledgementFilter); + break; + case 1: /* eventObjectId - OPTIONAL */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fRecipientProcess (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* eventStateFilter */ + offset = fEnumeratedTag (tvb, tree, offset, + "event State Filter: ", BACnetEventStateFilter); + break; + case 3: /* eventTypeFilter - OPTIONAL */ + offset = fEnumeratedTag (tvb, tree, offset, + "event Type Filter: ", BACnetEventType); + break; + case 4: /* priorityFilter */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fUnsignedTag (tvb, tree, offset, "min Priority: "); + offset = fUnsignedTag (tvb, tree, offset, "max Priority: "); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 5: /* notificationClassFilter - OPTIONAL */ + offset = fUnsignedTag (tvb, tree, offset, "notification Class Filter: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fGetEnrollmentSummaryAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes (tvb, tree, offset, "Object Identifier: "); + offset = fApplicationTypesEnumeratedSplit (tvb, tree, offset, + "event Type: ", BACnetEventType, 64); + offset = fApplicationTypesEnumerated (tvb, tree, offset, + "event State: ", BACnetEventState); + offset = fApplicationTypes (tvb, tree, offset, "Priority: "); + offset = fApplicationTypes (tvb, tree, offset, "Notification Class: "); + } + + return offset; +} + +static guint +fGetEventInformationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (tvb_length_remaining(tvb, offset) > 0) { + if (fTagNo(tvb, offset) == 0) { + offset = fObjectIdentifier (tvb, tree, offset); + } + } + return offset; +} + +static guint +flistOfEventSummaries (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* ObjectId */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 1: /* eventState */ + offset = fEnumeratedTag (tvb, tree, offset, + "event State: ", BACnetEventStateFilter); + break; + case 2: /* acknowledgedTransitions */ + offset = fEnumeratedTag (tvb, tree, offset, + "acknowledged Transitions: ", BACnetEventTransitionBits); + break; + case 3: /* eventTimeStamps */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp (tvb, tree, offset); + offset = fTimeStamp (tvb, tree, offset); + offset = fTimeStamp (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* notifyType */ + offset = fEnumeratedTag (tvb, tree, offset, + "Notify Type: ", BACnetNotifyType); + break; + case 5: /* eventEnable */ + offset = fEnumeratedTag (tvb, tree, offset, + "event Enable: ", BACnetEventTransitionBits); + break; + case 6: /* eventPriorities */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fUnsignedTag (tvb, tree, offset, "event Priority: "); + offset = fUnsignedTag (tvb, tree, offset, "event Priority: "); + offset = fUnsignedTag (tvb, tree, offset, "event Priority: "); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + lastoffset = offset; + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fGetEventInformationACK (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* listOfEventSummaries */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = flistOfEventSummaries (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* moreEvents */ + offset = fBooleanTag (tvb, tree, offset, "more Events: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fAddListElementRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* ObjectId */ + offset = fBACnetObjectPropertyReference (tvb, subtree, offset); + break; + case 3: /* listOfElements */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "listOfElements"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fDeleteObjectRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fObjectIdentifier (tvb, tree, offset); +} + +static guint +fDeviceCommunicationControlRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* timeDuration */ + offset = fUnsignedTag (tvb,tree,offset,"time Duration: "); + break; + case 1: /* enable-disable */ + offset = fEnumeratedTag (tvb, tree, offset, "enable-disable: ", + BACnetEnableDisable); + break; + case 2: /* password - OPTIONAL */ + offset = fCharacterString (tvb, tree, offset, "Password: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fReinitializeDeviceRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* reinitializedStateOfDevice */ + offset = fEnumeratedTag (tvb, tree, offset, + "reinitialized State Of Device: ", + BACnetReinitializedStateOfDevice); + break; + case 1: /* password - OPTIONAL */ + offset = fCharacterString (tvb, tree, offset, "Password: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fVtOpenRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fApplicationTypesEnumerated (tvb, tree, offset, + "vtClass: ", BACnetVTClass); + return fApplicationTypes (tvb,tree,offset,"local VT Session ID: "); +} + +static guint +fVtOpenAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fApplicationTypes (tvb,tree,offset,"remote VT Session ID: "); +} + +static guint +fVtCloseRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset= fApplicationTypes (tvb,tree,offset,"remote VT Session ID: "); + } + return offset; +} + +static guint +fVtDataRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset= fApplicationTypes (tvb,tree,offset,"VT Session ID: "); + offset = fApplicationTypes (tvb, tree, offset, "VT New Data: "); + return fApplicationTypes (tvb,tree,offset,"VT Data Flag: ");; +} + +static guint +fVtDataAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* BOOLEAN */ + offset = fBooleanTag (tvb, tree, offset, "all New Data Accepted: "); + break; + case 1: /* Unsigned OPTIONAL */ + offset = fUnsignedTag (tvb, tree, offset, "accepted Octet Count: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fAuthenticateRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* Unsigned32 */ + offset = fUnsignedTag (tvb, tree, offset, "pseudo Random Number: "); + break; + case 1: /* expected Invoke ID Unsigned8 OPTIONAL */ + proto_tree_add_item(tree, hf_bacapp_invoke_id, tvb, offset++, 1, TRUE); + break; + case 2: /* Chararacter String OPTIONAL */ + offset = fCharacterString (tvb, tree, offset, "operator Name: "); + break; + case 3: /* Chararacter String OPTIONAL */ + offset = fCharacterString (tvb, tree, offset, "operator Password: "); + break; + case 4: /* Boolean OPTIONAL */ + offset = fBooleanTag (tvb, tree, offset, "start Encyphered Session: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fAuthenticateAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fApplicationTypes (tvb, tree, offset, "modified Random Number: "); +} + +static guint +fRequestKeyRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fObjectIdentifier (tvb, tree, offset); /* Requesting Device Identifier */ + offset = fAddress (tvb, tree, offset); + offset = fObjectIdentifier (tvb, tree, offset); /* Remote Device Identifier */ + return fAddress (tvb, tree, offset); +} + +static guint +fRemoveListElementRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + /* Same as AddListElement request after service choice */ + return fAddListElementRequest(tvb, tree, offset); +} + +static guint +fReadPropertyRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fBACnetObjectPropertyReference(tvb, tree, offset); +} + +static guint +fReadPropertyAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + continue; + } + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* propertyIdentifier */ + offset = fPropertyIdentifier (tvb, subtree, offset); + break; + case 2: /* propertyArrayIndex */ + offset = fSignedTag (tvb, subtree, offset, "property Array Index: "); + break; + case 3: /* propertyValue */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "propertyValue"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + break; + } + } + return offset; +} + +static guint +fWritePropertyRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* propertyIdentifier */ + offset = fPropertyIdentifier (tvb, subtree, offset); + break; + case 2: /* propertyArrayIndex */ + offset = fSignedTag (tvb, subtree, offset, "property Array Index: "); + break; + case 3: /* propertyValue */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "propertyValue"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + case 4: /* Priority (only used for write) */ + offset = fUnsignedTag (tvb, subtree, offset, "Priority: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fWriteAccessSpecification (tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + continue; + } + + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* listOfPropertyValues */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyValue (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + return offset; + } + } + return offset; +} + +static guint +fWritePropertyMultipleRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + if (offset >= tvb_reported_length(tvb)) + return offset; + + return fWriteAccessSpecification (tvb, tree, offset); +} + +static guint +fPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 tagoffset, guint8 list) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* closing Tag, but not for me */ + return offset; + } + switch (tag_no-tagoffset) { + case 0: /* PropertyIdentifier */ + offset = fPropertyIdentifier (tvb, tree, offset); + break; + case 1: /* propertyArrayIndex */ + offset = fUnsignedTag (tvb, tree, offset, "property Array Index: "); + if (list != 0) break; /* Continue decoding if this may be a list */ + default: + lastoffset = offset; /* Set loop end condition */ + break; + } + } + return offset; +} + +static guint +fBACnetPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 list) +{ + return fPropertyReference(tvb, tree, offset, 0, list); +} + +static guint +fBACnetObjectPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* ObjectIdentifier */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 1: /* PropertyIdentifier and propertyArrayIndex */ + offset = fPropertyReference (tvb, tree, offset, 1, 0); + default: + lastoffset = offset; /* Set loop end condition */ + break; + } + } + return offset; +} + +#if 0 +static guint +fObjectPropertyValue (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree* subtree = tree; + proto_item* tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + continue; + } + switch (tag_no) { + case 0: /* ObjectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* PropertyIdentifier */ + offset = fPropertyIdentifier (tvb, subtree, offset); + break; + case 2: /* propertyArrayIndex */ + offset = fUnsignedTag (tvb, subtree, offset, "property Array Index: "); + break; + case 3: /* Value */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "propertyValue"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + break; + } + FAULT; + break; + case 4: /* Priority */ + offset = fUnsignedTag (tvb, subtree, offset, "Priority: "); + break; + default: + break; + } + } + return offset; +} +#endif + +static guint +fDeviceObjectPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* ObjectIdentifier */ + offset = fBACnetObjectPropertyReference (tvb, tree, offset); + break; + case 3: /* deviceIdentifier - OPTIONAL */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fPriorityArray (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + char i, ar[256]; + + if (offset >= tvb_reported_length(tvb)) + return offset; + + for (i = 1; i <= 16; i++) { + g_snprintf (ar, sizeof(ar), "%s[%d]: ", + val_to_split_str(87 , 512, + BACnetPropertyIdentifier, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), + i); + /* DMR Should be fAbstractNSyntax, but that's where we came from! */ + offset = fApplicationTypes(tvb, tree, offset, ar); + } + return offset; +} + +static guint +fDeviceObjectReference (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb,offset)) { + case 0: /* deviceIdentifier - OPTIONAL */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + case 1: /* ObjectIdentifier */ + offset = fObjectIdentifier (tvb, tree, offset); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fSpecialEvent (tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + continue; + } + + switch (tag_no) { + case 0: /* calendaryEntry */ + if (tag_is_opening(tag_info)) + { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fCalendaryEntry (tvb, subtree, offset); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + break; + case 1: /* calendarReference */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 2: /* list of BACnetTimeValue */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeValue (tvb, subtree, offset); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + } + FAULT; + break; + case 3: /* eventPriority */ + offset = fUnsignedTag (tvb, subtree, offset, "event priority: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fSelectionCriteria (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* stop when we hit outer closing tag */ + continue; + } + + switch (fTagNo(tvb,offset)) { + case 0: /* propertyIdentifier */ + offset = fPropertyIdentifier (tvb, tree, offset); + break; + case 1: /* propertyArrayIndex */ + offset = fUnsignedTag (tvb, tree, offset, "property Array Index: "); + break; + case 2: /* relationSpecifier */ + offset = fEnumeratedTag (tvb, tree, offset, + "relation Specifier: ", BACnetRelationSpecifier); + break; + case 3: /* comparisonValue */ + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, tree, offset); + offset += fTagHeaderTree (tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fObjectSelectionCriteria (tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* stop when we hit outer closing tag */ + continue; + } + + switch (tag_no) { + case 0: /* selectionLogic */ + offset = fEnumeratedTag (tvb, subtree, offset, + "selection Logic: ", BACnetSelectionLogic); + break; + case 1: /* listOfSelectionCriteria */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fSelectionCriteria (tvb, subtree, offset); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + } + FAULT; + break; + default: + return offset; + } + } + return offset; +} + + +static guint +fReadPropertyConditionalRequest(tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + if (tag_is_opening(tag_info) && tag_no < 2) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 0: /* objectSelectionCriteria */ + offset = fObjectSelectionCriteria (tvb, subtree, offset); + break; + case 1: /* listOfPropertyReferences */ + offset = fBACnetPropertyReference (tvb, subtree, offset, 1); + break; + default: + return offset; + } + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + } + return offset; +} + +static guint +fReadAccessSpecification (tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, + &tag_info, &lvt); + continue; + } + + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* listOfPropertyReferences */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyReference (tvb, subtree, offset, 1); + break; + } + FAULT; + break; + default: + return offset; + } + } + return offset; +} + +static guint +fReadAccessResult (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no; + guint8 tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, + &tag_no, &tag_info, &lvt); + if (tag_no == 4 || tag_no == 5) subtree = tree; /* Value and error have extra subtree */ + continue; + } + + switch (tag_no) { + case 0: /* objectSpecifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + case 1: /* list of Results */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "listOfResults"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + break; + } + FAULT; + break; + case 2: /* propertyIdentifier */ + offset = fPropertyValue(tvb, subtree, offset, 2); + break; + case 5: /* propertyAccessError */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "propertyAccessError"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + /* Error Code follows */ + offset = fError(tvb, subtree, offset); + break; + } + FAULT; + break; + default: + return offset; + } + } + return offset; +} + + +static guint +fReadPropertyConditionalAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + /* listOfReadAccessResults */ + return fReadAccessResult (tvb, tree, offset); +} + + +static guint +fCreateObjectRequest(tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while ((tvb_length_remaining(tvb, offset) > 0) && (offset > lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + if (tag_no < 2) + { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 0: /* objectSpecifier */ + switch (fTagNo(tvb, offset)) { /* choice of objectType or objectIdentifier */ + case 0: /* objectType */ + offset = fEnumeratedTagSplit (tvb, subtree, offset, "Object Type: ", BACnetObjectType, 128); + break; + case 1: /* objectIdentifier */ + offset = fObjectIdentifier (tvb, subtree, offset); + break; + default: + break; + } + break; + case 1: /* propertyValue */ + if (tag_is_opening(tag_info)) { + offset = fBACnetPropertyValue (tvb, subtree, offset); + break; + } + FAULT; + break; + default: + break; + } + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + } + return offset; +} + +static guint +fCreateObjectAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fObjectIdentifier (tvb, tree, offset); +} + +static guint +fReadRangeRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + offset = fBACnetObjectPropertyReference(tvb, subtree, offset); + + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + /* optional range choice */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, val_to_str(tag_no, BACnetReadRangeOptions, "unknown range option")); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 3: /* range byPosition */ + case 6: /* range bySequenceNumber, 2004 spec */ + offset = fApplicationTypes (tvb, subtree, offset, "reference Index: "); + offset = fApplicationTypes (tvb, subtree, offset, "reference Count: "); + break; + case 4: /* range byTime - deprecated in 2004 */ + case 7: /* 2004 spec */ + offset = fDateTime(tvb, subtree, offset, "reference Date/Time: "); + offset = fApplicationTypes (tvb, subtree, offset, "reference Count: "); + break; + case 5: /* range timeRange - deprecated in 2004 */ + offset = fDateTime(tvb, subtree, offset, "beginning Time: "); + offset = fDateTime(tvb, subtree, offset, "ending Time: "); + break; + default: + break; + } + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + return offset; +} + +static guint +fReadRangeAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + offset = fBACnetObjectPropertyReference(tvb, subtree, offset); + + /* resultFlags */ + offset = fEnumeratedTag (tvb, tree, offset, "result Flags: ", BACnetResultFlags); + + /* itemCount */ + offset = fUnsignedTag (tvb, subtree, offset, "item Count: "); + + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + /* itemData */ + if (tag_is_opening(tag_info)) { + tt = proto_tree_add_text(subtree, tvb, offset, 1, "itemData"); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, subtree, offset); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + + if (fTagNo(tvb, offset) == 6) { /* firstSequenceNumber - OPTIONAL */ + offset = fUnsignedTag (tvb, subtree, offset, "first Sequence Number: "); + } + + return offset; +} + +static guint fAccessMethod(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint32 lvt; + guint8 tag_no, tag_info; + proto_item* tt; + proto_tree* subtree = NULL; + + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + if (tag_is_opening(tag_info)) + { + tt = proto_tree_add_text(tree, tvb, offset, 1, val_to_str(tag_no, BACnetFileAccessOption, "invalid access method")); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fApplicationTypes (tvb, subtree, offset, val_to_str(tag_no, BACnetFileStartOption, "invalid option")); + offset = fApplicationTypes (tvb, subtree, offset, val_to_str(tag_no, BACnetFileWriteInfo, "unknown option")); + + if (tag_no == 1) + { + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) + { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes (tvb, subtree, offset, "Record Data: "); + } + } + + if ((bacapp_flags & BACAPP_MORE_SEGMENTS) == 0) + { + /* More Flag is not set, so we can look for closing tag in this segment */ + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + } + } + return offset; +} + +static guint +fAtomicReadFileRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_item *tt; + + offset = fObjectIdentifier (tvb, tree, offset); + + fTagHeader (tvb, offset, &tag_no, &tag_info, &lvt); + + if (tag_is_opening(tag_info)) + { + tt = proto_tree_add_text(subtree, tvb, offset, 1, val_to_str(tag_no, BACnetFileAccessOption, "unknown access method")); + subtree = proto_item_add_subtree(tt, ett_bacapp_value); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fSignedTag (tvb, subtree, offset, val_to_str(tag_no, BACnetFileStartOption, "unknown option")); + offset = fUnsignedTag (tvb, subtree, offset, val_to_str(tag_no, BACnetFileRequestCount, "unknown option")); + offset += fTagHeaderTree (tvb, subtree, offset, &tag_no, &tag_info, &lvt); + } + return offset; +} + +static guint +fAtomicWriteFileRequest(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + + offset = fObjectIdentifier (tvb, tree, offset); /* file Identifier */ + offset = fAccessMethod(tvb, tree, offset); + + return offset; +} + +static guint +fAtomicWriteFileAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint tag_no = fTagNo(tvb, offset); + return fSignedTag (tvb, tree, offset, val_to_str(tag_no, BACnetFileStartOption, "unknown option")); +} + +static guint +fAtomicReadFileAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fApplicationTypes (tvb, tree, offset, "End Of File: "); + offset = fAccessMethod(tvb, tree, offset); + + return offset; +} + +static guint +fReadPropertyMultipleRequest(tvbuff_t *tvb, proto_tree *subtree, guint offset) +{ + return fReadAccessSpecification (tvb,subtree,offset); +} + +static guint +fReadPropertyMultipleAck (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + return fReadAccessResult (tvb,tree,offset); +} + +static guint +fConfirmedServiceRequest (tvbuff_t *tvb, proto_tree *tree, guint offset, gint service_choice) +{ + if (tvb_length_remaining(tvb,offset) <= 0) + return offset; + + switch (service_choice) { + case 0: /* acknowledgeAlarm */ + offset = fAcknowledgeAlarmRequest (tvb, tree, offset); + break; + case 1: /* confirmedCOVNotification */ + offset = fConfirmedCOVNotificationRequest (tvb, tree, offset); + break; + case 2: /* confirmedEventNotification */ + offset = fConfirmedEventNotificationRequest (tvb, tree, offset); + break; + case 3: /* confirmedGetAlarmSummary conveys no parameters */ + break; + case 4: /* getEnrollmentSummaryRequest */ + offset = fGetEnrollmentSummaryRequest (tvb, tree, offset); + break; + case 5: /* subscribeCOVRequest */ + offset = fSubscribeCOVRequest(tvb, tree, offset); + break; + case 6: /* atomicReadFile-Request */ + offset = fAtomicReadFileRequest(tvb, tree, offset); + break; + case 7: /* atomicWriteFile-Request */ + offset = fAtomicWriteFileRequest(tvb, tree, offset); + break; + case 8: /* AddListElement-Request */ + offset = fAddListElementRequest(tvb, tree, offset); + break; + case 9: /* removeListElement-Request */ + offset = fRemoveListElementRequest(tvb, tree, offset); + break; + case 10: /* createObjectRequest */ + offset = fCreateObjectRequest(tvb, tree, offset); + break; + case 11: /* deleteObject */ + offset = fDeleteObjectRequest(tvb, tree, offset); + break; + case 12: + offset = fReadPropertyRequest(tvb, tree, offset); + break; + case 13: + offset = fReadPropertyConditionalRequest(tvb, tree, offset); + break; + case 14: + offset = fReadPropertyMultipleRequest(tvb, tree, offset); + break; + case 15: + offset = fWritePropertyRequest(tvb, tree, offset); + break; + case 16: + offset = fWritePropertyMultipleRequest(tvb, tree, offset); + break; + case 17: + offset = fDeviceCommunicationControlRequest(tvb, tree, offset); + break; + case 18: + offset = fConfirmedPrivateTransferRequest(tvb, tree, offset); + break; + case 19: + offset = fConfirmedTextMessageRequest(tvb, tree, offset); + break; + case 20: + offset = fReinitializeDeviceRequest(tvb, tree, offset); + break; + case 21: + offset = fVtOpenRequest(tvb, tree, offset); + break; + case 22: + offset = fVtCloseRequest (tvb, tree, offset); + break; + case 23: + offset = fVtDataRequest (tvb, tree, offset); + break; + case 24: + offset = fAuthenticateRequest (tvb, tree, offset); + break; + case 25: + offset = fRequestKeyRequest (tvb, tree, offset); + break; + case 26: + offset = fReadRangeRequest (tvb, tree, offset); + break; + case 27: + offset = fLifeSafetyOperationRequest(tvb, tree, offset, NULL); + break; + case 28: + offset = fSubscribeCOVPropertyRequest(tvb, tree, offset); + break; + case 29: + offset = fGetEventInformationRequest (tvb, tree, offset); + break; + default: + return offset; + break; + } + return offset; +} + +static guint +fConfirmedServiceAck (tvbuff_t *tvb, proto_tree *tree, guint offset, gint service_choice) +{ + if (tvb_length_remaining(tvb,offset) <= 0) + return offset; + + switch (service_choice) { + case 3: /* confirmedEventNotificationAck */ + offset = fGetAlarmSummaryAck (tvb, tree, offset); + break; + case 4: /* getEnrollmentSummaryAck */ + offset = fGetEnrollmentSummaryAck (tvb, tree, offset); + break; + case 6: /* atomicReadFile */ + offset = fAtomicReadFileAck (tvb, tree, offset); + break; + case 7: /* atomicReadFileAck */ + offset = fAtomicWriteFileAck (tvb, tree, offset); + break; + case 10: /* createObject */ + offset = fCreateObjectAck (tvb, tree, offset); + break; + case 12: + offset = fReadPropertyAck (tvb, tree, offset); + break; + case 13: + offset = fReadPropertyConditionalAck (tvb, tree, offset); + break; + case 14: + offset = fReadPropertyMultipleAck (tvb, tree, offset); + break; + case 18: + offset = fConfirmedPrivateTransferAck(tvb, tree, offset); + break; + case 21: + offset = fVtOpenAck (tvb, tree, offset); + break; + case 23: + offset = fVtDataAck (tvb, tree, offset); + break; + case 24: + offset = fAuthenticateAck (tvb, tree, offset); + break; + case 26: + offset = fReadRangeAck (tvb, tree, offset); + break; + case 29: + offset = fGetEventInformationACK (tvb, tree, offset); + break; + default: + return offset; + } + return offset; +} + +static guint +fIAmRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + /* BACnetObjectIdentifier */ + offset = fApplicationTypes (tvb, tree, offset, "BACnet Object Identifier: "); + + /* MaxAPDULengthAccepted */ + offset = fApplicationTypes (tvb, tree, offset, "Maximum ADPU Length Accepted: "); + + /* segmentationSupported */ + offset = fApplicationTypesEnumerated (tvb, tree, offset, + "Segmentation Supported: ", BACnetSegmentation); + + /* vendor ID */ + return fUnsignedTag (tvb, tree, offset, "Vendor ID: "); +} + +static guint +fIHaveRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + /* BACnetDeviceIdentifier */ + offset = fApplicationTypes (tvb, tree, offset, "Device Identifier: "); + + /* BACnetObjectIdentifier */ + offset = fApplicationTypes (tvb, tree, offset, "Object Identifier: "); + + /* ObjectName */ + return fApplicationTypes (tvb, tree, offset, "Object Name: "); + +} + +static guint +fWhoIsRequest (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* DeviceInstanceRangeLowLimit Optional */ + offset = fUnsignedTag (tvb, tree, offset, "Device Instance Range Low Limit: "); + break; + case 1: /* DeviceInstanceRangeHighLimit Optional but required if DeviceInstanceRangeLowLimit is there */ + offset = fUnsignedTag (tvb, tree, offset, "Device Instance Range High Limit: "); + break; + default: + return offset; + break; + } + } + return offset; +} + +static guint +fUnconfirmedServiceRequest (tvbuff_t *tvb, proto_tree *tree, guint offset, gint service_choice) +{ + if (tvb_length_remaining(tvb,offset) <= 0) + return offset; + + switch (service_choice) { + case 0: /* I-Am-Request */ + offset = fIAmRequest (tvb, tree, offset); + break; + case 1: /* i-Have Request */ + offset = fIHaveRequest (tvb, tree, offset); + break; + case 2: /* unconfirmedCOVNotification */ + offset = fUnconfirmedCOVNotificationRequest (tvb, tree, offset); + break; + case 3: /* unconfirmedEventNotification */ + offset = fUnconfirmedEventNotificationRequest (tvb, tree, offset); + break; + case 4: /* unconfirmedPrivateTransfer */ + offset = fUnconfirmedPrivateTransferRequest(tvb, tree, offset); + break; + case 5: /* unconfirmedTextMessage */ + offset = fUnconfirmedTextMessageRequest(tvb, tree, offset); + break; + case 6: /* timeSynchronization */ + offset = fTimeSynchronizationRequest (tvb, tree, offset); + break; + case 7: /* who-Has */ + offset = fWhoHas (tvb, tree, offset); + break; + case 8: /* who-Is */ + offset = fWhoIsRequest (tvb, tree, offset); + break; + case 9: /* utcTimeSynchronization */ + offset = fUTCTimeSynchronizationRequest (tvb, tree, offset); + break; + default: + break; + } + return offset; +} + +static guint +fStartConfirmed(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset, guint8 ack, + gint *svc, proto_item **tt) +{ + proto_item *tc; + proto_tree *bacapp_tree_control; + gint tmp, bacapp_type; + guint extra = 2; + + bacapp_seq = 0; + tmp = (gint) tvb_get_guint8(tvb, offset); + bacapp_type = (tmp >> 4) & 0x0f; + bacapp_flags = tmp & 0x0f; + + if (ack == 0) { + extra = 3; + } + *svc = (gint) tvb_get_guint8(tvb, offset+extra); + if (bacapp_flags & 0x08) + *svc = (gint) tvb_get_guint8(tvb, offset+extra+2); + + proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset, 1, TRUE); + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_pduflags, tvb, offset, 1, TRUE); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp_control); + + proto_tree_add_item(bacapp_tree_control, hf_bacapp_SEG, tvb, offset, 1, TRUE); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_MOR, tvb, offset, 1, TRUE); + if (ack == 0) /* The following are for ConfirmedRequest, not Complex ack */ + { + proto_tree_add_item(bacapp_tree_control, hf_bacapp_SA, tvb, offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_response_segments, tvb, + offset, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_max_adpu_size, tvb, + offset, 1, TRUE); + } + offset++; + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, offset++, 1, TRUE); + if (bacapp_flags & 0x08) { + bacapp_seq = tvb_get_guint8(tvb, offset); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_sequence_number, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_window_size, tvb, + offset++, 1, TRUE); + } + *tt = proto_tree_add_item(bacapp_tree, hf_bacapp_service, tvb, + offset++, 1, TRUE); + return offset; +} + +static guint +fConfirmedRequestPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Confirmed-Request */ + /* ASHRAE 135-2001 20.1.2 */ + gint svc; + proto_item *tt = 0; + + offset = fStartConfirmed(tvb, bacapp_tree, offset, 0, &svc, &tt); + if (bacapp_seq > 0) /* Can't handle continuation segments, so just treat as data */ + { + proto_tree_add_text(bacapp_tree, tvb, offset, 0, "(continuation)"); + return offset; + } + else + { + /* Service Request follows... Variable Encoding 20.2ff */ + return fConfirmedServiceRequest (tvb, bacapp_tree, offset, svc); + } +} + +static guint +fUnconfirmedRequestPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Unconfirmed-Request-PDU */ + /* ASHRAE 135-2001 20.1.3 */ + + gint tmp; + + proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, TRUE); + + tmp = tvb_get_guint8(tvb, offset); + proto_tree_add_item(bacapp_tree, hf_bacapp_uservice, tvb, + offset++, 1, TRUE); + /* Service Request follows... Variable Encoding 20.2ff */ + return fUnconfirmedServiceRequest (tvb, bacapp_tree, offset, tmp); +} + +static guint +fSimpleAckPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Simple-Ack-PDU */ + /* ASHRAE 135-2001 20.1.4 */ + + proto_item *tc; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, TRUE); + + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_service, tvb, + offset++, 1, TRUE); + return offset; +} + +static guint +fComplexAckPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Complex-Ack-PDU */ + /* ASHRAE 135-2001 20.1.5 */ + gint svc; + proto_item *tt = 0; + + offset = fStartConfirmed(tvb, bacapp_tree, offset, 1, &svc, &tt); + + if (bacapp_seq > 0) /* Can't handle continuation segments, so just treat as data */ + { + proto_tree_add_text(bacapp_tree, tvb, offset, 0, "(continuation)"); + return offset; + } + else + { + /* Service ACK follows... */ + return fConfirmedServiceAck (tvb, bacapp_tree, offset, svc); + } +} + + +static guint +fSegmentAckPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-SegmentAck-PDU */ + /* ASHRAE 135-2001 20.1.6 */ + + proto_item *tc; + proto_tree *bacapp_tree_control; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset, 1, TRUE); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree, hf_bacapp_NAK, tvb, offset, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_SRV, tvb, offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_sequence_number, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_window_size, tvb, + offset++, 1, TRUE); + return offset; +} + +static guint fContextTaggedError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_info = 0; + guint8 parsed_tag = 0; + guint32 lvt = 0; + offset += fTagHeaderTree(tvb, tree, offset, &parsed_tag, &tag_info, &lvt); + offset = fError(tvb, tree, offset); + return offset + fTagHeaderTree(tvb, tree, offset, &parsed_tag, &tag_info, &lvt); +} + +static guint +fConfirmedPrivateTransferError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no = 0, tag_info = 0; + guint lvt = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* errorType */ + offset = fContextTaggedError(tvb,tree,offset); + break; + case 1: /* vendorID */ + offset = fUnsignedTag (tvb,tree,offset,"vendor ID: "); + break; + case 2: /* serviceNumber */ + offset = fUnsignedTag (tvb,tree,offset,"service Number: "); + break; + case 3: /* errorParameters */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType (tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fCreateObjectError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* errorType */ + offset = fContextTaggedError(tvb,tree,offset); + break; + case 1: /* firstFailedElementNumber */ + offset = fUnsignedTag (tvb,tree,offset,"first failed element number: "); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fChangeListError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + /* Identical to CreateObjectError */ + return fCreateObjectError(tvb, tree, offset); +} + +static guint +fVTCloseError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + if (fTagNo(tvb, offset) == 0) + { + /* errorType */ + offset = fContextTaggedError(tvb,tree,offset); + if (fTagNo(tvb, offset) == 1) + { + /* listOfVTSessionIdentifiers [OPTIONAL] */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fVtCloseRequest (tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + } + } + /* should report bad packet if initial tag wasn't 0 */ + return offset; +} + +static guint +fWritePropertyMultipleError(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + while ((tvb_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* errorType */ + offset = fContextTaggedError(tvb,tree,offset); + break; + case 1: /* firstFailedWriteAttempt */ + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetObjectPropertyReference(tvb, tree, offset); + offset += fTagHeaderTree(tvb, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + } + return offset; +} + +static guint +fError (tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + offset = fApplicationTypesEnumeratedSplit (tvb, tree, offset, + "error Class: ", BACnetErrorClass, 64); + return fApplicationTypesEnumeratedSplit (tvb, tree, offset, + "error Code: ", BACnetErrorCode, 256); +} + +static guint +fBACnetError (tvbuff_t *tvb, proto_tree *tree, guint offset, guint service) +{ + switch (service) { + case 8: /* no break here !!!! */ + case 9: + offset = fChangeListError (tvb, tree, offset); + break; + case 10: + offset = fCreateObjectError (tvb,tree,offset); + break; + case 16: + offset = fWritePropertyMultipleError (tvb,tree,offset); + break; + case 18: + offset = fConfirmedPrivateTransferError (tvb,tree,offset); + case 22: + offset = fVTCloseError (tvb,tree,offset); + default: + return fError (tvb, tree, offset); + break; + } + return offset; +} + +static guint +fErrorPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Error-PDU */ + /* ASHRAE 135-2001 20.1.7 */ + + proto_item *tc, *tt; + proto_tree *bacapp_tree_control; + guint8 tmp; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, TRUE); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, TRUE); + tmp = tvb_get_guint8(tvb, offset); + tt = proto_tree_add_item(bacapp_tree, hf_bacapp_service, tvb, + offset++, 1, TRUE); + /* Error Handling follows... */ + return fBACnetError (tvb, bacapp_tree, offset, tmp); +} + +static guint +fRejectPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Reject-PDU */ + /* ASHRAE 135-2001 20.1.8 */ + + proto_item *tc; + proto_tree *bacapp_tree_control; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, TRUE); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_BACnetRejectReason, tvb, + offset++, 1, TRUE); + return offset; +} + +static guint +fAbortPDU(tvbuff_t *tvb, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Abort-PDU */ + /* ASHRAE 135-2001 20.1.9 */ + + proto_item *tc; + proto_tree *bacapp_tree_control; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset, 1, TRUE); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree, hf_bacapp_SRV, tvb, offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, TRUE); + proto_tree_add_item(bacapp_tree, hf_BACnetAbortReason, tvb, + offset++, 1, TRUE); + return offset; +} + +void +dissect_bacapp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + gint8 tmp, bacapp_type; + tvbuff_t *next_tvb; + guint offset = 0; + guint8 bacapp_service, bacapp_reason; + guint8 bacapp_invoke_id; + proto_item *ti; + proto_tree *bacapp_tree; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet-APDU"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, "BACnet APDU "); + + tmp = (gint) tvb_get_guint8(tvb, 0); + bacapp_type = (tmp >> 4) & 0x0f; + + /* show some descriptive text in the INFO column */ + if (check_col(pinfo->cinfo, COL_INFO)) + { + col_clear(pinfo->cinfo, COL_INFO); + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(bacapp_type, BACnetTypeName, "#### unknown APDU ##### ")); + switch (bacapp_type) + { + case BACAPP_TYPE_CONFIRMED_SERVICE_REQUEST: + /* segmented messages have 2 additional bytes */ + if (tmp & BACAPP_SEGMENTED_REQUEST) + { + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 4); + bacapp_service = tvb_get_guint8(tvb, offset + 5); + } + else + { + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 2); + bacapp_service = tvb_get_guint8(tvb, offset + 3); + } + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_str(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str)); + break; + case BACAPP_TYPE_UNCONFIRMED_SERVICE_REQUEST: + bacapp_service = tvb_get_guint8(tvb, offset + 1); + col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", + val_to_str(bacapp_service, + BACnetUnconfirmedServiceChoice, + bacapp_unknown_service_str)); + break; + case BACAPP_TYPE_SIMPLE_ACK: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_service = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_str(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str)); + break; + case BACAPP_TYPE_COMPLEX_ACK: + /* segmented messages have 2 additional bytes */ + if (tmp & BACAPP_SEGMENTED_REQUEST) + { + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 3); + bacapp_service = tvb_get_guint8(tvb, offset + 4); + } + else + { + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_service = tvb_get_guint8(tvb, offset + 2); + } + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_str(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str)); + break; + case BACAPP_TYPE_SEGMENT_ACK: + /* nothing more to add */ + break; + case BACAPP_TYPE_ERROR: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_service = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_str(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str)); + break; + case BACAPP_TYPE_REJECT: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_reason = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_split_str(bacapp_reason, + 64, + BACnetRejectReason, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + break; + case BACAPP_TYPE_ABORT: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_reason = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "[invoke:%d]: %s", + bacapp_invoke_id, + val_to_split_str(bacapp_reason, + 64, + BACnetAbortReason, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + break; + /* UNKNOWN */ + default: + /* nothing more to add */ + break; + } + } + + if (tree) { + ti = proto_tree_add_item(tree, proto_bacapp, tvb, offset, -1, FALSE); + bacapp_tree = proto_item_add_subtree(ti, ett_bacapp); + + /* ASHRAE 135-2001 20.1.1 */ + switch (bacapp_type) { + case BACAPP_TYPE_CONFIRMED_SERVICE_REQUEST: /* BACnet-Confirmed-Service-Request */ + offset = fConfirmedRequestPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_UNCONFIRMED_SERVICE_REQUEST: /* BACnet-Unconfirmed-Request-PDU */ + offset = fUnconfirmedRequestPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_SIMPLE_ACK: /* BACnet-Simple-Ack-PDU */ + offset = fSimpleAckPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_COMPLEX_ACK: /* BACnet-Complex-Ack-PDU */ + offset = fComplexAckPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_SEGMENT_ACK: /* BACnet-SegmentAck-PDU */ + offset = fSegmentAckPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_ERROR: /* BACnet-Error-PDU */ + offset = fErrorPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_REJECT: /* BACnet-Reject-PDU */ + offset = fRejectPDU(tvb, bacapp_tree, offset); + break; + case BACAPP_TYPE_ABORT: /* BACnet-Abort-PDU */ + offset = fAbortPDU(tvb, bacapp_tree, offset); + break; + } + } + + next_tvb = tvb_new_subset(tvb,offset,-1,tvb_length_remaining(tvb,offset)); + call_dissector(data_handle,next_tvb, pinfo, tree); +} + +void +proto_register_bacapp(void) +{ + static hf_register_info hf[] = { + { &hf_bacapp_type, + { "APDU Type", "bacapp.type", + FT_UINT8, BASE_DEC, VALS(BACnetTypeName), 0xf0, "APDU Type", HFILL } + }, + { &hf_bacapp_pduflags, + { "PDU Flags", "bacapp.pduflags", + FT_UINT8, BASE_HEX, NULL, 0x0f, "PDU Flags", HFILL } + }, + { &hf_bacapp_SEG, + { "Segmented Request", "bacapp.segmented_request", + FT_BOOLEAN, 8, TFS(&segments_follow), 0x08, "Segmented Request", HFILL } + }, + { &hf_bacapp_MOR, + { "More Segments", "bacapp.more_segments", + FT_BOOLEAN, 8, TFS(&more_follow), 0x04, "More Segments Follow", HFILL } + }, + { &hf_bacapp_SA, + { "SA", "bacapp.SA", + FT_BOOLEAN, 8, TFS(&segmented_accept), 0x02, "Segmented Response accepted", HFILL } + }, + { &hf_bacapp_max_adpu_size, + { "Size of Maximum ADPU accepted", "bacapp.max_adpu_size", + FT_UINT8, BASE_DEC, VALS(BACnetMaxAPDULengthAccepted), 0x0f, "Size of Maximum ADPU accepted", HFILL } + }, + { &hf_bacapp_response_segments, + { "Max Response Segments accepted", "bacapp.response_segments", + FT_UINT8, BASE_DEC, VALS(BACnetMaxSegmentsAccepted), 0x70, "Max Response Segments accepted", HFILL } + }, + { &hf_bacapp_objectType, + { "Object Type", "bacapp.objectType", + FT_UINT32, BASE_DEC, VALS(BACnetObjectType), 0xffc00000, "Object Type", HFILL } + }, + { &hf_bacapp_instanceNumber, + { "Instance Number", "bacapp.instance_number", + FT_UINT32, BASE_DEC, NULL, 0x003fffff, "Instance Number", HFILL } + }, + { &hf_bacapp_invoke_id, + { "Invoke ID", "bacapp.invoke_id", + FT_UINT8, BASE_DEC, NULL, 0, "Invoke ID", HFILL } + }, + { &hf_bacapp_sequence_number, + { "Sequence Number", "bacapp.sequence_number", + FT_UINT8, BASE_DEC, NULL, 0, "Sequence Number", HFILL } + }, + { &hf_bacapp_window_size, + { "Proposed Window Size", "bacapp.window_size", + FT_UINT8, BASE_DEC, NULL, 0, "Proposed Window Size", HFILL } + }, + { &hf_bacapp_service, + { "Service Choice", "bacapp.confirmed_service", + FT_UINT8, BASE_DEC, VALS(BACnetConfirmedServiceChoice), 0x00, "Service Choice", HFILL } + }, + { &hf_bacapp_uservice, + { "Unconfirmed Service Choice", "bacapp.unconfirmed_service", + FT_UINT8, BASE_DEC, VALS(BACnetUnconfirmedServiceChoice), 0x00, "Unconfirmed Service Choice", HFILL } + }, + { &hf_bacapp_NAK, + { "NAK", "bacapp.NAK", + FT_BOOLEAN, 8, NULL, 0x02, "negativ ACK", HFILL } + }, + { &hf_bacapp_SRV, + { "SRV", "bacapp.SRV", + FT_BOOLEAN, 8, NULL, 0x01, "Server", HFILL } + }, + { &hf_BACnetRejectReason, + { "Reject Reason", "bacapp.reject_reason", + FT_UINT8, BASE_DEC, VALS(BACnetRejectReason), 0x00, "Reject Reason", HFILL } + }, + { &hf_BACnetAbortReason, + { "Abort Reason", "bacapp.abort_reason", + FT_UINT8, BASE_DEC, VALS(BACnetAbortReason), 0x00, "Abort Reason", HFILL } + }, + { &hf_bacapp_vpart, + { "BACnet APDU variable part:", "bacapp.variable_part", + FT_NONE, 0, NULL, 0, "BACnet APDU variable part", HFILL } + }, + { &hf_bacapp_tag, + { "BACnet Tag", + "bacapp.tag", + FT_BYTES, BASE_HEX, NULL, 0, + "BACnet Tag", HFILL } + }, + { &hf_BACnetApplicationTagNumber, + { "Application Tag Number", + "bacapp.application_tag_number", + FT_UINT8, BASE_DEC, VALS(&BACnetApplicationTagNumber), 0xF0, + "Application Tag Number", HFILL } + }, + { &hf_BACnetContextTagNumber, + { "Context Tag Number", + "bacapp.context_tag_number", + FT_UINT8, BASE_DEC, NULL, 0xF0, + "Context Tag Number", HFILL } + }, + { &hf_BACnetExtendedTagNumber, + { "Extended Tag Number", + "bacapp.extended_tag_number", + FT_UINT8, BASE_DEC, NULL, 0, + "Extended Tag Number", HFILL } + }, + { &hf_BACnetNamedTag, + { "Named Tag", + "bacapp.named_tag", + FT_UINT8, BASE_DEC, VALS(&BACnetTagNames), 0x07, + "Named Tag", HFILL } + }, + { &hf_BACnetCharacterSet, + { "String Character Set", + "bacapp.string_character_set", + FT_UINT8, BASE_DEC, VALS(&BACnetCharacterSet),0, + "String Character Set", HFILL } + }, + { &hf_BACnetTagClass, + { "Tag Class", "bacapp.tag_class", + FT_BOOLEAN, 8, TFS(&BACnetTagClass), 0x08, "Tag Class", HFILL } + }, + { &hf_bacapp_tag_lvt, + { "Length Value Type", + "bacapp.LVT", + FT_UINT8, BASE_DEC, NULL, 0, + "Length Value Type", HFILL } + }, + { &hf_bacapp_tag_value8, + { "Tag Value", + "bacapp.tag_value8", + FT_UINT8, BASE_DEC, NULL, 0, + "Tag Value", HFILL } + }, + { &hf_bacapp_tag_value16, + { "Tag Value 16-bit", + "bacapp.tag_value16", + FT_UINT16, BASE_DEC, NULL, 0, + "Tag Value 16-bit", HFILL } + }, + { &hf_bacapp_tag_value32, + { "Tag Value 32-bit", + "bacapp.tag_value32", + FT_UINT32, BASE_DEC, NULL, 0, + "Tag Value 32-bit", HFILL } + }, + { &hf_bacapp_tag_ProcessId, + { "ProcessIdentifier", "bacapp.processId", + FT_UINT32, BASE_DEC, NULL, 0, "Process Identifier", HFILL } + }, + { &hf_bacapp_tag_initiatingObjectType, + { "ObjectType", "bacapp.objectType", + FT_UINT16, BASE_DEC, VALS(BACnetObjectType), 0x00, "Object Type", HFILL } + }, + }; + static gint *ett[] = { + &ett_bacapp, + &ett_bacapp_control, + &ett_bacapp_tag, + &ett_bacapp_list, + &ett_bacapp_value, + }; + proto_bacapp = proto_register_protocol("Building Automation and Control Network APDU", + "BACapp", "bacapp"); + + proto_register_field_array(proto_bacapp, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + register_dissector("bacapp", dissect_bacapp, proto_bacapp); + +} + +void +proto_reg_handoff_bacapp(void) +{ + data_handle = find_dissector("data"); +} + +guint32 +fConvertXXXtoUTF8 (const guint8 *in, size_t *inbytesleft, guint8 *out, size_t *outbytesleft, const gchar *fromcoding) +{ /* I don't want to let in and out be modified */ +#ifdef HAVE_CONFIG_H +#if HAVE_ICONV + guint32 i; + iconv_t icd; + const guint8 *inp = in; + guint8 *outp = out; + const guint8 **inpp = &inp; + guint8 **outpp = &outp; + + if ((icd = iconv_open ("UTF-8", fromcoding)) != (iconv_t) -1) { + i = iconv (icd, (char**) inpp, inbytesleft, (char**) outpp, outbytesleft); + *outpp[0] = '\0'; + iconv_close (icd); + return i; + } + +#endif +#endif + + memcpy (out, in, *inbytesleft); + out[*inbytesleft] = '\0'; + *outbytesleft -= *inbytesleft; + *inbytesleft = 0; + + return 0; +} + diff --git a/src/main/resource/testFiles/wireshark/revFiles/606866_c79cf4_epan#dissectors#packet-atalk.c b/src/main/resource/testFiles/wireshark/revFiles/606866_c79cf4_epan#dissectors#packet-atalk.c new file mode 100644 index 0000000..8f8dfb9 --- /dev/null +++ b/src/main/resource/testFiles/wireshark/revFiles/606866_c79cf4_epan#dissectors#packet-atalk.c @@ -0,0 +1,2494 @@ +/* packet-atalk.c + * Routines for AppleTalk packet disassembly: LLAP, DDP, NBP, ATP, ASP, + * RTMP, PAP. + * + * $Id$ + * + * Simon Wilkinson + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "packet-atalk.h" +#include "packet-afp.h" + +/* Tables for reassembly of fragments. */ +static GHashTable *atp_fragment_table = NULL; +static GHashTable *atp_reassembled_table = NULL; + +/* desegmentation of ATP */ +static gboolean atp_defragment = TRUE; + +static dissector_handle_t afp_handle; + +static int proto_llap = -1; +static int hf_llap_dst = -1; +static int hf_llap_src = -1; +static int hf_llap_type = -1; + +static int proto_ddp = -1; +static int hf_ddp_hopcount = -1; +static int hf_ddp_len = -1; +static int hf_ddp_checksum = -1; +static int hf_ddp_dst = -1; +static int hf_ddp_dst_net = -1; +static int hf_ddp_src = -1; +static int hf_ddp_src_net = -1; +static int hf_ddp_dst_node = -1; +static int hf_ddp_src_node = -1; +static int hf_ddp_dst_socket = -1; +static int hf_ddp_src_socket = -1; +static int hf_ddp_type = -1; + +static dissector_handle_t ddp_handle; + +/* -------------------------------------- + * ATP protocol parameters + * from netatalk/include/atalk/atp.h + */ +#define ATP_MAXDATA (578+4) /* maximum ATP data size */ +#define ATP_BUFSIZ 587 /* maximum packet size */ +#define ATP_HDRSIZE 5 /* includes DDP type field */ + +#define ATP_TRELMASK 0x07 /* mask all but TREL */ +#define ATP_RELTIME 30 /* base release timer (in secs) */ + +#define ATP_TREL30 0x0 /* release time codes */ +#define ATP_TREL1M 0x1 /* these are passed in flags of */ +#define ATP_TREL2M 0x2 /* atp_sreq call, and set in the */ +#define ATP_TREL4M 0x3 /* packet control info. */ +#define ATP_TREL8M 0x4 + +/* flags for ATP options (and control byte) +*/ +#define ATP_XO 0x20 /* (1<<5) eXactly Once mode */ +#define ATP_EOM 0x10 /* (1<<4) End Of Message */ +#define ATP_STS 0x08 /* (1<<3) Transaction Status */ + +/* function codes +*/ +#define ATP_FUNCMASK (3<<6) /* mask all but function */ + +#define ATP_TREQ 1 /* (1<<6) Trans. REQuest */ +#define ATP_TRESP 2 /* (2<<6) Trans. RESPonse */ +#define ATP_TREL 3 /* (3<<6) Trans. RELease */ + +/* ------------------------- */ +static dissector_handle_t asp_handle; +static dissector_handle_t pap_handle; + +static int proto_atp = -1; +static int hf_atp_ctrlinfo = -1; /* guint8_t control information */ +static int hf_atp_function = -1; /* bits 7,6 function */ +static int hf_atp_xo = -1; /* bit 5 exactly-once */ +static int hf_atp_eom = -1; /* bit 4 end-of-message */ +static int hf_atp_sts = -1; /* bit 3 send transaction status */ +static int hf_atp_treltimer = -1; /* bits 2,1,0 TRel timeout indicator */ + +static int hf_atp_bitmap = -1; /* guint8_t bitmap or sequence number */ +static int hf_atp_tid = -1; /* guint16_t transaction id. */ +static int hf_atp_user_bytes = -1; + +static int hf_atp_segments = -1; +static int hf_atp_segment = -1; +static int hf_atp_segment_overlap = -1; +static int hf_atp_segment_overlap_conflict = -1; +static int hf_atp_segment_multiple_tails = -1; +static int hf_atp_segment_too_long_segment = -1; +static int hf_atp_segment_error = -1; +static int hf_atp_reassembled_in = -1; + +/* ------------------------- */ +static int proto_zip = -1; +static dissector_handle_t zip_atp_handle; + +static int hf_zip_function = -1; +static int hf_zip_atp_function = -1; +static int hf_zip_start_index = -1; +static int hf_zip_count = -1; +static int hf_zip_zero_value = -1; + +static int hf_zip_network_count = -1; +static int hf_zip_network = -1; +static int hf_zip_network_start = -1; +static int hf_zip_network_end = -1; + +static int hf_zip_flags = -1; + +static int hf_zip_flags_zone_invalid = -1; +static int hf_zip_flags_use_broadcast = -1; +static int hf_zip_flags_only_one_zone = -1; + +static int hf_zip_last_flag = -1; + +static int hf_zip_zone_name = -1; +static int hf_zip_default_zone = -1; + +static int hf_zip_multicast_length = -1; +static int hf_zip_multicast_address = -1; + +static const value_string zip_function_vals[] = { + {1, "Query"}, + {2, "Reply"}, + {5, "GetNetInfo request"}, + {6, "GetNetInfo reply"}, + {7, "notify"}, + {8, "Extended reply"}, + {0, NULL} +}; + +static const value_string zip_atp_function_vals[] = { + {7, "GetMyZone"}, + {8, "GetZoneList"}, + {9, "GetLocalZones"}, + {0, NULL} +}; + +static gint ett_zip = -1; +static gint ett_zip_flags = -1; +static gint ett_zip_zones_list = -1; +static gint ett_zip_network_list = -1; + +/* -------------------------------- + * from netatalk/include/atalk/ats.h + */ + +#define ASPFUNC_CLOSE 1 +#define ASPFUNC_CMD 2 +#define ASPFUNC_STAT 3 +#define ASPFUNC_OPEN 4 +#define ASPFUNC_TICKLE 5 +#define ASPFUNC_WRITE 6 +#define ASPFUNC_WRTCONT 7 +#define ASPFUNC_ATTN 8 + +#define ASP_HDRSIZ 4 +#define ASPERR_OK 0x0000 +#define ASPERR_BADVERS 0xfbd6 +#define ASPERR_BUFSMALL 0xfbd5 +#define ASPERR_NOSESS 0xfbd4 +#define ASPERR_NOSERV 0xfbd3 +#define ASPERR_PARM 0xfbd2 +#define ASPERR_SERVBUSY 0xfbd1 +#define ASPERR_SESSCLOS 0xfbd0 +#define ASPERR_SIZERR 0xfbcf +#define ASPERR_TOOMANY 0xfbce +#define ASPERR_NOACK 0xfbcd + +static int proto_asp = -1; +static int hf_asp_func = -1; +static int hf_asp_error = -1; +static int hf_asp_socket = -1; +static int hf_asp_version = -1; +static int hf_asp_session_id = -1; +static int hf_asp_zero_value = -1; +static int hf_asp_init_error = -1; +static int hf_asp_attn_code = -1; +static int hf_asp_seq = -1; +static int hf_asp_size = -1; + +/* status stuff same for asp and afp */ +static int hf_asp_server_name = -1; +static int hf_asp_server_type = -1; +static int hf_asp_server_vers = -1; +static int hf_asp_server_uams = -1; +static int hf_asp_server_icon = -1; +static int hf_asp_server_directory = -1; + +static int hf_asp_server_flag = -1; +static int hf_asp_server_flag_copyfile = -1; +static int hf_asp_server_flag_passwd = -1; +static int hf_asp_server_flag_no_save_passwd = -1; +static int hf_asp_server_flag_srv_msg = -1; +static int hf_asp_server_flag_srv_sig = -1; +static int hf_asp_server_flag_tcpip = -1; +static int hf_asp_server_flag_notify = -1; +static int hf_asp_server_flag_reconnect = -1; +static int hf_asp_server_flag_directory = -1; +static int hf_asp_server_flag_utf8_name = -1; +static int hf_asp_server_flag_fast_copy = -1; +static int hf_asp_server_signature = -1; +static int hf_asp_server_utf8_name_len = -1; +static int hf_asp_server_utf8_name = -1; + +static int hf_asp_server_addr_len = -1; +static int hf_asp_server_addr_type = -1; +static int hf_asp_server_addr_value = -1; + +static gint ett_asp_status = -1; +static gint ett_asp_uams = -1; +static gint ett_asp_vers = -1; +static gint ett_asp_addr = -1; +static gint ett_asp_addr_line = -1; +static gint ett_asp_directory = -1; +static gint ett_asp_utf8_name = -1; +static gint ett_asp_status_server_flag = -1; + +typedef struct { + guint32 conversation; + guint8 src[4]; + guint16 seq; +} asp_request_key; + +typedef struct { + guint8 value; /* command for asp, bitmap for atp */ +} asp_request_val; + +static GHashTable *asp_request_hash = NULL; + +/* Hash Functions */ +static gint asp_equal (gconstpointer v, gconstpointer v2) +{ + const asp_request_key *val1 = (const asp_request_key*)v; + const asp_request_key *val2 = (const asp_request_key*)v2; + + if (val1->conversation == val2->conversation && + val1->seq == val2->seq && + !memcmp(val1->src, val2->src, 4)) { + return 1; + } + return 0; +} + +static guint asp_hash (gconstpointer v) +{ + const asp_request_key *asp_key = (const asp_request_key*)v; + return asp_key->seq; +} + +/* ------------------------------------ */ +static GHashTable *atp_request_hash = NULL; + + +/* ------------------------------------ */ +static int proto_nbp = -1; +static int hf_nbp_op = -1; +static int hf_nbp_info = -1; +static int hf_nbp_count = -1; +static int hf_nbp_tid = -1; + +static int hf_nbp_node_net = -1; +static int hf_nbp_node_port = -1; +static int hf_nbp_node_node = -1; +static int hf_nbp_node_enum = -1; +static int hf_nbp_node_object = -1; +static int hf_nbp_node_type = -1; +static int hf_nbp_node_zone = -1; + +static int proto_rtmp = -1; +static int hf_rtmp_net = -1; +static int hf_rtmp_node_len = -1; +static int hf_rtmp_node = -1; +static int hf_rtmp_tuple_net = -1; +static int hf_rtmp_tuple_range_start = -1; +static int hf_rtmp_tuple_range_end = -1; +static int hf_rtmp_tuple_dist = -1; +static int hf_rtmp_function = -1; + +static gint ett_atp = -1; + +static gint ett_atp_segments = -1; +static gint ett_atp_segment = -1; +static gint ett_atp_info = -1; +static gint ett_asp = -1; +static gint ett_pap = -1; + +static gint ett_nbp = -1; +static gint ett_nbp_info = -1; +static gint ett_nbp_node = -1; +static gint ett_rtmp = -1; +static gint ett_rtmp_tuple = -1; +static gint ett_ddp = -1; +static gint ett_llap = -1; +static gint ett_pstring = -1; + +static const fragment_items atp_frag_items = { + &ett_atp_segment, + &ett_atp_segments, + &hf_atp_segments, + &hf_atp_segment, + &hf_atp_segment_overlap, + &hf_atp_segment_overlap_conflict, + &hf_atp_segment_multiple_tails, + &hf_atp_segment_too_long_segment, + &hf_atp_segment_error, + &hf_atp_reassembled_in, + "segments" +}; + +/* -------------------------------- */ + +#define PAPOpenConn 1 +#define PAPOpenConnReply 2 +#define PAPSendData 3 +#define PAPData 4 +#define PAPTickle 5 +#define PAPCloseConn 6 +#define PAPCloseConnReply 7 +#define PAPSendStatus 8 +#define PAPStatus 9 + +static int proto_pap = -1; + +static int hf_pap_connid = -1; +static int hf_pap_function = -1; +static int hf_pap_socket = -1; +static int hf_pap_quantum = -1; +static int hf_pap_waittime = -1; +static int hf_pap_result = -1; +static int hf_pap_status = -1; +static int hf_pap_seq = -1; +static int hf_pap_eof = -1; + +static int hf_pap_pad = -1; + +static const value_string pap_function_vals[] = { + {PAPOpenConn , "Open Connection Query"}, + {PAPOpenConnReply , "Open Connection Reply"}, + {PAPSendData , "Send Data"}, + {PAPData , "Data"}, + {PAPTickle , "Tickle"}, + {PAPCloseConn , "Close Connection Query"}, + {PAPCloseConnReply , "Close Connection reply"}, + {PAPSendStatus , "Send Status"}, + {PAPStatus , "Status"}, + + {0, NULL} +}; + +/* -------------------------------- */ + +static dissector_table_t ddp_dissector_table; + +static dissector_handle_t data_handle; + +#define DDP_SHORT_HEADER_SIZE 5 + +/* + * P = Padding, H = Hops, L = Len + * + * PPHHHHLL LLLLLLLL + * + * Assumes the argument is in host byte order. + */ +#define ddp_hops(x) ( ( x >> 10) & 0x3C ) +#define ddp_len(x) ( x & 0x03ff ) +typedef struct _e_ddp { + guint16 hops_len; /* combines pad, hops, and len */ + guint16 sum,dnet,snet; + guint8 dnode,snode; + guint8 dport,sport; + guint8 type; +} e_ddp; + +#define DDP_HEADER_SIZE 13 + + +static const value_string op_vals[] = { + {DDP_RTMPDATA, "AppleTalk Routing Table response or data" }, + {DDP_NBP, "AppleTalk Name Binding Protocol packet"}, + {DDP_ATP, "AppleTalk Transaction Protocol packet"}, + {DDP_AEP, "AppleTalk Echo Protocol packet"}, + {DDP_RTMPREQ, "AppleTalk Routing Table request"}, + {DDP_ZIP, "AppleTalk Zone Information Protocol packet"}, + {DDP_ADSP, "AppleTalk Data Stream Protocol"}, + {DDP_EIGRP, "Cisco EIGRP for AppleTalk"}, + {0, NULL} +}; + +static const value_string rtmp_function_vals[] = { + {1, "Request"}, + {2, "Route Data Request (split horizon processed)"}, + {3, "Route Data Request (no split horizon processing)"}, + {0, NULL} +}; + +#define NBP_BROADCAST 1 +#define NBP_LOOKUP 2 +#define NBP_FORWARD 4 +#define NBP_REPLY 3 + +static const value_string nbp_op_vals[] = { + {NBP_BROADCAST, "broadcast request"}, + {NBP_LOOKUP, "lookup"}, + {NBP_FORWARD, "forward request"}, + {NBP_REPLY, "reply"}, + {0, NULL} +}; + +static const value_string atp_function_vals[] = { + {ATP_TREQ ,"REQuest"}, + {ATP_TRESP ,"RESPonse"}, + {ATP_TREL ,"RELease"}, + {0, NULL} +}; + +static const value_string atp_trel_timer_vals[] = { + {0, "30 seconds"}, + {1, "1 minute"}, + {2, "2 minutes"}, + {3, "4 minutes"}, + {4, "8 minutes"}, + {0, NULL} +}; + +/* +*/ +static const value_string asp_func_vals[] = { + {ASPFUNC_CLOSE, "CloseSession" }, + {ASPFUNC_CMD, "Command" }, + {ASPFUNC_STAT, "GetStatus" }, + {ASPFUNC_OPEN, "OpenSession" }, + {ASPFUNC_TICKLE, "Tickle" }, + {ASPFUNC_WRITE, "Write" }, + {ASPFUNC_WRTCONT, "Write Cont" }, + {ASPFUNC_ATTN, "Attention" }, + {0, NULL } }; + +const value_string asp_error_vals[] = { + {AFP_OK , "success"}, + {AFPERR_ACCESS , "permission denied" }, + {AFPERR_AUTHCONT , "logincont" }, + {AFPERR_BADUAM , "uam doesn't exist" }, + {AFPERR_BADVERS , "bad afp version number" }, + {AFPERR_BITMAP , "invalid bitmap" }, + {AFPERR_CANTMOVE , "can't move file" }, + {AFPERR_DENYCONF , "file synchronization locks conflict" }, + {AFPERR_DIRNEMPT , "directory not empty" }, + {AFPERR_DFULL , "disk full" }, + {AFPERR_EOF , "end of file" }, + {AFPERR_BUSY , "FileBusy" }, + {AFPERR_FLATVOL , "volume doesn't support directories" }, + {AFPERR_NOITEM , "ItemNotFound" }, + {AFPERR_LOCK , "LockErr" }, + {AFPERR_MISC , "misc. err" }, + {AFPERR_NLOCK , "no more locks" }, + {AFPERR_NOSRVR , "no response by server at that address" }, + {AFPERR_EXIST , "object already exists" }, + {AFPERR_NOOBJ , "object not found" }, + {AFPERR_PARAM , "parameter error" }, + {AFPERR_NORANGE , "no range lock" }, + {AFPERR_RANGEOVR , "range overlap" }, + {AFPERR_SESSCLOS , "session closed" }, + {AFPERR_NOTAUTH , "user not authenticated" }, + {AFPERR_NOOP , "command not supported" }, + {AFPERR_BADTYPE , "object is the wrong type" }, + {AFPERR_NFILE , "too many files open" }, + {AFPERR_SHUTDOWN , "server is going down" }, + {AFPERR_NORENAME , "can't rename" }, + {AFPERR_NODIR , "couldn't find directory" }, + {AFPERR_ITYPE , "wrong icon type" }, + {AFPERR_VLOCK , "volume locked" }, + {AFPERR_OLOCK , "object locked" }, + {AFPERR_CTNSHRD , "share point contains a share point" }, + {AFPERR_NOID , "file thread not found" }, + {AFPERR_EXISTID , "file already has an id" }, + {AFPERR_DIFFVOL , "different volume" }, + {AFPERR_CATCHNG , "catalog has changed" }, + {AFPERR_SAMEOBJ , "source file == destination file" }, + {AFPERR_BADID , "non-existent file id" }, + {AFPERR_PWDSAME , "same password/can't change password" }, + {AFPERR_PWDSHORT , "password too short" }, + {AFPERR_PWDEXPR , "password expired" }, + {AFPERR_INSHRD , "folder being shared is inside a shared folder." }, + {AFPERR_INTRASH , "shared folder in trash." }, + {AFPERR_PWDCHNG , "password needs to be changed" }, + {AFPERR_PWDPOLCY , "password fails policy check" }, + {AFPERR_USRLOGIN , "user already logged on" }, + {0, NULL } }; + +/* + * XXX - do this with an FT_UINT_STRING? + * Unfortunately, you can't extract from an FT_UINT_STRING the string, + * which we'd want to do in order to put it into the "Data:" portion. + * + * Are these always in the Mac extended character set? + */ +static int dissect_pascal_string(tvbuff_t *tvb, int offset, proto_tree *tree, + int hf_index) +{ + int len; + char *tmp; + + len = tvb_get_guint8(tvb, offset); + offset++; + + if ( tree ) + { + proto_tree *item; + proto_tree *subtree; + + /* + * XXX - if we could do this inside the protocol tree + * code, we could perhaps avoid allocating and freeing + * this string buffer. + */ + tmp = (gchar*)tvb_get_ephemeral_string(tvb, offset, len); + item = proto_tree_add_string(tree, hf_index, tvb, offset-1, len+1, tmp); + + subtree = proto_item_add_subtree(item, ett_pstring); + proto_tree_add_text(subtree, tvb, offset-1, 1, "Length: %d", len); + proto_tree_add_text(subtree, tvb, offset, len, "Data: %s", tmp); + + } + offset += len; + + return offset; +} + +static void +dissect_rtmp_request(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { + proto_tree *rtmp_tree; + proto_item *ti; + guint8 function; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + function = tvb_get_guint8(tvb, 0); + + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(function, rtmp_function_vals, "Unknown function (%02x)")); + + if (tree) { + ti = proto_tree_add_item(tree, proto_rtmp, tvb, 0, 1, FALSE); + rtmp_tree = proto_item_add_subtree(ti, ett_rtmp); + + proto_tree_add_uint(rtmp_tree, hf_rtmp_function, tvb, 0, 1, function); + } +} + +static void +dissect_rtmp_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { + proto_tree *rtmp_tree; + proto_item *ti; + int offset = 0; + guint16 net; + guint8 nodelen,nodelen_bits; + guint16 node; /* might be more than 8 bits */ + int i; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + net = tvb_get_ntohs(tvb, offset); + nodelen_bits = tvb_get_guint8(tvb, offset+2); + if ( nodelen_bits <= 8 ) { + node = tvb_get_guint8(tvb, offset)+1; + nodelen = 1; + } else { + node = tvb_get_ntohs(tvb, offset); + nodelen = 2; + } + + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_fstr(pinfo->cinfo, COL_INFO, "Net: %u Node Len: %u Node: %u", + net, nodelen_bits, node); + + if (tree) { + ti = proto_tree_add_item(tree, proto_rtmp, tvb, offset, -1, FALSE); + rtmp_tree = proto_item_add_subtree(ti, ett_rtmp); + + proto_tree_add_uint(rtmp_tree, hf_rtmp_net, tvb, offset, 2, net); + proto_tree_add_uint(rtmp_tree, hf_rtmp_node_len, tvb, offset+2, 1, + nodelen_bits); + proto_tree_add_uint(rtmp_tree, hf_rtmp_node, tvb, offset+3, nodelen, + node); + offset += 3 + nodelen; + + i = 1; + while (tvb_offset_exists(tvb, offset)) { + proto_tree *tuple_item, *tuple_tree; + guint16 tuple_net; + guint8 tuple_dist; + guint16 tuple_range_end; + + tuple_net = tvb_get_ntohs(tvb, offset); + tuple_dist = tvb_get_guint8(tvb, offset+2); + + if (tuple_dist & 0x80) { + tuple_range_end = tvb_get_ntohs(tvb, offset+3); + tuple_item = proto_tree_add_text(rtmp_tree, tvb, offset, 6, + "Tuple %d: Range Start: %u Dist: %u Range End: %u", + i, tuple_net, tuple_dist&0x7F, tuple_range_end); + } else { + tuple_item = proto_tree_add_text(rtmp_tree, tvb, offset, 3, + "Tuple %d: Net: %u Dist: %u", + i, tuple_net, tuple_dist); + } + tuple_tree = proto_item_add_subtree(tuple_item, ett_rtmp_tuple); + + if (tuple_dist & 0x80) { + proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_range_start, tvb, offset, 2, + tuple_net); + } else { + proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_net, tvb, offset, 2, + tuple_net); + } + proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_dist, tvb, offset+2, 1, + tuple_dist & 0x7F); + + if (tuple_dist & 0x80) { + /* + * Extended network tuple. + */ + proto_tree_add_item(tuple_tree, hf_rtmp_tuple_range_end, tvb, offset+3, 2, + FALSE); + offset += 6; + } else + offset += 3; + + i++; + } + } +} + +static void +dissect_nbp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { + proto_tree *nbp_tree; + proto_tree *nbp_info_tree; + proto_item *ti, *info_item; + int offset = 0; + guint8 info; + guint op, count; + unsigned int i; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + info = tvb_get_guint8(tvb, offset); + op = info >> 4; + count = info & 0x0F; + + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_fstr(pinfo->cinfo, COL_INFO, "Op: %s Count: %u", + val_to_str(op, nbp_op_vals, "Unknown (0x%01x)"), count); + + if (tree) { + ti = proto_tree_add_item(tree, proto_nbp, tvb, offset, -1, FALSE); + nbp_tree = proto_item_add_subtree(ti, ett_nbp); + + info_item = proto_tree_add_uint_format(nbp_tree, hf_nbp_info, tvb, offset, 1, + info, + "Info: 0x%01X Operation: %s Count: %u", info, + val_to_str(op, nbp_op_vals, "Unknown (0x%01X)"), + count); + nbp_info_tree = proto_item_add_subtree(info_item, ett_nbp_info); + proto_tree_add_uint(nbp_info_tree, hf_nbp_op, tvb, offset, 1, info); + proto_tree_add_uint(nbp_info_tree, hf_nbp_count, tvb, offset, 1, info); + proto_tree_add_item(nbp_tree, hf_nbp_tid, tvb, offset+1, 1, FALSE); + offset += 2; + + for (i=0; icinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ATP"); + + ctrlinfo = tvb_get_guint8(tvb, offset); + bitmap = tvb_get_guint8(tvb, offset +1); + tid = tvb_get_ntohs(tvb, offset +2); + + t = bitmap; + while(t) { + nbe++; + t >>= 1; + } + + op = ctrlinfo >> 6; + + aspinfo.reply = (0x80 == (ctrlinfo & ATP_FUNCMASK))?1:0; + aspinfo.release = (0xC0 == (ctrlinfo & ATP_FUNCMASK))?1:0; + aspinfo.seq = tid; + aspinfo.code = 0; + query = (!aspinfo.reply && !aspinfo.release); + + conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + + if (conversation == NULL) + { + conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, pinfo->destport, 0); + } + + if (atp_defragment) { + asp_request_key request_key; + + request_key.conversation = conversation->index; + memcpy(request_key.src, (!aspinfo.reply)?pinfo->src.data:pinfo->dst.data, 4); + request_key.seq = aspinfo.seq; + + request_val = (asp_request_val *) g_hash_table_lookup(atp_request_hash, &request_key); + + if (!request_val && query && nbe > 1) { + asp_request_key *new_request_key; + + /* only save nbe packets if more than 1 requested + save some memory and help the defragmentation if tid wraparound, ie + we have both a request for 1 packet and a request for n packets, + hopefully most of the time ATP_EOM will be set in the last packet. + */ + new_request_key = se_alloc(sizeof(asp_request_key)); + *new_request_key = request_key; + + request_val = se_alloc(sizeof(asp_request_val)); + request_val->value = nbe; + + g_hash_table_insert(atp_request_hash, new_request_key,request_val); + } + } + + /* + ATP_EOM is not mandatory. Some implementations don't always set it + if the query is only one packet. + + So it needs to keep the number of packets asked in request. + */ + + if (aspinfo.reply) { + more_fragment = !(ATP_EOM & ctrlinfo) && request_val; + frag_number = bitmap; + } + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_clear(pinfo->cinfo, COL_INFO); + col_add_fstr(pinfo->cinfo, COL_INFO, "%s transaction %u", + val_to_str(op, atp_function_vals, "Unknown (0x%01x)"),tid); + if (more_fragment) + col_append_str(pinfo->cinfo, COL_INFO, " [fragment]"); + } + + if (tree) { + ti = proto_tree_add_item(tree, proto_atp, tvb, offset, -1, FALSE); + atp_tree = proto_item_add_subtree(ti, ett_atp); + proto_item_set_len(atp_tree, aspinfo.release?8:ATP_HDRSIZE -1); + + info_item = proto_tree_add_item(atp_tree, hf_atp_ctrlinfo, tvb, offset, 1, FALSE); + atp_info_tree = proto_item_add_subtree(info_item, ett_atp_info); + + proto_tree_add_item(atp_info_tree, hf_atp_function, tvb, offset, 1, FALSE); + proto_tree_add_item(atp_info_tree, hf_atp_xo, tvb, offset, 1, FALSE); + proto_tree_add_item(atp_info_tree, hf_atp_eom, tvb, offset, 1, FALSE); + proto_tree_add_item(atp_info_tree, hf_atp_sts, tvb, offset, 1, FALSE); + if ((ctrlinfo & (ATP_FUNCMASK|ATP_XO)) == (0x40|ATP_XO)) { + /* TReq with XO set */ + proto_tree_add_item(atp_info_tree, hf_atp_treltimer, tvb, offset, 1, FALSE); + } + if (query) { + proto_tree_add_text(atp_tree, tvb, offset +1, 1, + "Bitmap: 0x%02x %u packet(s) max", bitmap, nbe); + } + else { + proto_tree_add_item(atp_tree, hf_atp_bitmap, tvb, offset +1, 1, FALSE); + } + proto_tree_add_item(atp_tree, hf_atp_tid, tvb, offset +2, 2, FALSE); + + if (aspinfo.release) + proto_tree_add_item(atp_tree, hf_atp_user_bytes, tvb, offset +4, 4, FALSE); + + } + + if (aspinfo.release) + return; + + save_fragmented = pinfo->fragmented; + + /* FIXME + asp doesn't fit very well here + move asp back in atp? + */ + if (atp_defragment && aspinfo.reply && (more_fragment || frag_number != 0)) { + fragment_data *fd_head; + int hdr; + + hdr = ATP_HDRSIZE -1; + if (frag_number != 0) + hdr += 4; /* asp header */ + len = tvb_reported_length_remaining(tvb, hdr); + fd_head = fragment_add_seq_check(tvb, hdr, pinfo, tid, + atp_fragment_table, + atp_reassembled_table, + frag_number, + len, + more_fragment); + new_tvb = process_reassembled_data(tvb, ATP_HDRSIZE -1, pinfo, + "Reassembled ATP", fd_head, &atp_frag_items, + NULL, atp_tree); + } + else { + /* full packet */ + new_tvb = tvb_new_subset(tvb, ATP_HDRSIZE -1, -1,- 1); + } + + if (new_tvb) { + pinfo->private_data = &aspinfo; + /* if port == 6 it's not an ASP packet but a ZIP packet */ + if (pinfo->srcport == 6 || pinfo->destport == 6 ) + call_dissector(zip_atp_handle, new_tvb, pinfo, tree); + else { + /* XXX need a conversation_get_dissector function ? */ + if (!aspinfo.reply && !conversation->dissector_handle) { + dissector_handle_t sub; + + /* if it's a known ASP function call ASP dissector + else assume it's a PAP connection ID. + the test is wrong because PAP conn IDs overlapped with ASP fn + but I don't want to keep track of NBP msgs and open connection + port allocation. + */ + guint8 fn = tvb_get_guint8(new_tvb, 0); + + if (!fn || fn > ASPFUNC_ATTN) { + sub = pap_handle; + } + else { + sub = asp_handle; + } + call_dissector(sub, new_tvb, pinfo, tree); + conversation_set_dissector(conversation, sub); + } + else if (!try_conversation_dissector(&pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, new_tvb,pinfo, tree)) { + call_dissector(data_handle, new_tvb, pinfo, tree); + + } + } + } + else { + /* Just show this as a fragment. */ + new_tvb = tvb_new_subset (tvb, ATP_HDRSIZE -1, -1, -1); + call_dissector(data_handle, new_tvb, pinfo, tree); + } + pinfo->fragmented = save_fragmented; + return; +} + +/* + copy and paste from dsi + XXX - is the format of this reply dependent on the type of server, + with this format being the format for AFP servers? +*/ +static gint +dissect_asp_reply_get_status(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, gint offset) +{ + proto_tree *sub_tree; + proto_item *ti; + + guint16 ofs; + guint16 flag; + guint16 machine_ofs; + guint16 sign_ofs = 0; + guint16 adr_ofs = 0; + guint16 dir_ofs = 0; + guint16 utf_ofs = 0; + guint8 nbe; + guint8 len; + guint8 i; + + proto_tree *adr_tree; + char *tmp; + const guint8 *ip; + guint16 net; + guint8 node; + guint16 port; + + guint16 ulen; + + if (!tree) + return offset; + + ti = proto_tree_add_text(tree, tvb, offset, -1, "Get Status"); + tree = proto_item_add_subtree(ti, ett_asp_status); + + machine_ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_MACHOFF); + proto_tree_add_text(tree, tvb, offset +AFPSTATUS_MACHOFF, 2, "Machine offset: %u", machine_ofs); + if (machine_ofs) + machine_ofs += offset; + + ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_VERSOFF); + proto_tree_add_text(tree, tvb, offset +AFPSTATUS_VERSOFF, 2, "Version offset: %u", ofs); + + ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_UAMSOFF); + proto_tree_add_text(tree, tvb, offset +AFPSTATUS_UAMSOFF, 2, "UAMS offset: %u", ofs); + + ofs = tvb_get_ntohs(tvb, offset +AFPSTATUS_ICONOFF); + proto_tree_add_text(tree, tvb, offset +AFPSTATUS_ICONOFF, 2, "Icon offset: %u", ofs); + + ofs = offset +AFPSTATUS_FLAGOFF; + ti = proto_tree_add_item(tree, hf_asp_server_flag, tvb, ofs, 2, FALSE); + sub_tree = proto_item_add_subtree(ti, ett_asp_status_server_flag); + proto_tree_add_item(sub_tree, hf_asp_server_flag_copyfile , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_passwd , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_no_save_passwd, tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_srv_msg , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_srv_sig , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_tcpip , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_notify , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_reconnect , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_directory , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_utf8_name , tvb, ofs, 2, FALSE); + proto_tree_add_item(sub_tree, hf_asp_server_flag_fast_copy , tvb, ofs, 2, FALSE); + + proto_tree_add_item(tree, hf_asp_server_name, tvb, offset +AFPSTATUS_PRELEN, 1, FALSE); + + flag = tvb_get_ntohs(tvb, ofs); + if ((flag & AFPSRVRINFO_SRVSIGNATURE)) { + ofs = offset +AFPSTATUS_PRELEN +tvb_get_guint8(tvb, offset +AFPSTATUS_PRELEN) +1; + if ((ofs & 1)) + ofs++; + + sign_ofs = tvb_get_ntohs(tvb, ofs); + proto_tree_add_text(tree, tvb, ofs, 2, "Signature offset: %u", sign_ofs); + sign_ofs += offset; + ofs += 2; + + if ((flag & AFPSRVRINFO_TCPIP) && ofs < machine_ofs ) { + adr_ofs = tvb_get_ntohs(tvb, ofs); + proto_tree_add_text(tree, tvb, ofs, 2, "Network address offset: %u", adr_ofs); + adr_ofs += offset; + ofs += 2; + } + + if ((flag & AFPSRVRINFO_SRVDIRECTORY) && ofs < machine_ofs) { + dir_ofs = tvb_get_ntohs(tvb, ofs); + proto_tree_add_text(tree, tvb, ofs, 2, "Directory services offset: %u", dir_ofs); + dir_ofs += offset; + ofs += 2; + } + + if ((flag & AFPSRVRINFO_SRVUTF8) && ofs < machine_ofs) { + utf_ofs = tvb_get_ntohs(tvb, ofs); + proto_tree_add_text(tree, tvb, ofs, 2, "UTF-8 Server name offset: %u", utf_ofs); + utf_ofs += offset; + } + } + + if (machine_ofs) + proto_tree_add_item(tree, hf_asp_server_type, tvb, machine_ofs, 1, FALSE); + + ofs = offset +tvb_get_ntohs(tvb, offset +AFPSTATUS_VERSOFF); + if (ofs) { + nbe = tvb_get_guint8(tvb, ofs); + ti = proto_tree_add_text(tree, tvb, ofs, 1, "Version list: %u", nbe); + ofs++; + sub_tree = proto_item_add_subtree(ti, ett_asp_vers); + for (i = 0; i < nbe; i++) { + len = tvb_get_guint8(tvb, ofs); + proto_tree_add_item(sub_tree, hf_asp_server_vers, tvb, ofs, 1, FALSE); + ofs += len + 1; + } + } + + ofs = offset +tvb_get_ntohs(tvb, offset +AFPSTATUS_UAMSOFF); + if (ofs) { + nbe = tvb_get_guint8(tvb, ofs); + ti = proto_tree_add_text(tree, tvb, ofs, 1, "UAMS list: %u", nbe); + ofs++; + sub_tree = proto_item_add_subtree(ti, ett_asp_uams); + for (i = 0; i < nbe; i++) { + len = tvb_get_guint8(tvb, ofs); + proto_tree_add_item(sub_tree, hf_asp_server_uams, tvb, ofs, 1, FALSE); + ofs += len + 1; + } + } + + ofs = offset +tvb_get_ntohs(tvb, offset +AFPSTATUS_ICONOFF); + if (ofs) + proto_tree_add_item(tree, hf_asp_server_icon, tvb, ofs, 256, FALSE); + + if (sign_ofs) { + proto_tree_add_item(tree, hf_asp_server_signature, tvb, sign_ofs, 16, FALSE); + } + + if (adr_ofs) { + + ofs = adr_ofs; + nbe = tvb_get_guint8(tvb, ofs); + ti = proto_tree_add_text(tree, tvb, ofs, 1, "Address list: %u", nbe); + ofs++; + adr_tree = proto_item_add_subtree(ti, ett_asp_addr); + for (i = 0; i < nbe; i++) { + guint8 type; + + len = tvb_get_guint8(tvb, ofs); + type = tvb_get_guint8(tvb, ofs +1); + switch (type) { + case 1: /* IP */ + ip = tvb_get_ptr(tvb, ofs+2, 4); + ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ip %s", ip_to_str(ip)); + break; + case 2: /* IP + port */ + ip = tvb_get_ptr(tvb, ofs+2, 4); + port = tvb_get_ntohs(tvb, ofs+6); + ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ip %s:%u",ip_to_str(ip),port); + break; + case 3: /* DDP, atalk_addr_to_str want host order not network */ + net = tvb_get_ntohs(tvb, ofs+2); + node = tvb_get_guint8(tvb, ofs +4); + port = tvb_get_guint8(tvb, ofs +5); + ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ddp %u.%u:%u", + net, node, port); + break; + case 5: /* IP + port ssh tunnel */ + ip = tvb_get_ptr(tvb, ofs+2, 4); + port = tvb_get_ntohs(tvb, ofs+6); + ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "ip (ssh tunnel) %s:%u",ip_to_str(ip),port); + break; + case 4: /* DNS */ + if (len > 2) { + tmp = (gchar*)tvb_get_ephemeral_string(tvb, ofs +2, len -2); + ti = proto_tree_add_text(adr_tree, tvb, ofs, len, "dns %s", tmp); + break; + } + /* else fall to default malformed record */ + default: + ti = proto_tree_add_text(adr_tree, tvb, ofs, len,"Unknow type : %u", type); + break; + } + len -= 2; + sub_tree = proto_item_add_subtree(ti,ett_asp_addr_line); + proto_tree_add_item(sub_tree, hf_asp_server_addr_len, tvb, ofs, 1, FALSE); + ofs++; + proto_tree_add_item(sub_tree, hf_asp_server_addr_type, tvb, ofs, 1, FALSE); + ofs++; + proto_tree_add_item(sub_tree, hf_asp_server_addr_value,tvb, ofs, len, FALSE); + ofs += len; + } + } + + if (dir_ofs) { + ofs = dir_ofs; + nbe = tvb_get_guint8(tvb, ofs); + ti = proto_tree_add_text(tree, tvb, ofs, 1, "Directory services list: %u", nbe); + ofs++; + sub_tree = proto_item_add_subtree(ti, ett_asp_directory); + for (i = 0; i < nbe; i++) { + len = tvb_get_guint8(tvb, ofs); + proto_tree_add_item(sub_tree, hf_asp_server_directory, tvb, ofs, 1, FALSE); + ofs += len + 1; + } + } + if (utf_ofs) { + + ofs = utf_ofs; + ulen = tvb_get_ntohs(tvb, ofs); + tmp = (gchar*)tvb_get_ephemeral_string(tvb, ofs + 2, ulen); + ti = proto_tree_add_text(tree, tvb, ofs, ulen +2, "UTF8 server name: %s", tmp); + sub_tree = proto_item_add_subtree(ti, ett_asp_utf8_name); + proto_tree_add_uint(sub_tree, hf_asp_server_utf8_name_len, tvb, ofs, 2, ulen); + ofs += 2; + proto_tree_add_string(sub_tree, hf_asp_server_utf8_name, tvb, ofs, ulen, tmp); + ofs += ulen; + } + /* FIXME: offset is not updated */ + return offset; +} + +/* ----------------------------- + PAP protocol cf. inside appletalk chap. 10 +*/ +#define PAD(x) { proto_tree_add_item(pap_tree, hf_pap_pad, tvb, offset, x, FALSE); offset += x; } + +static void +dissect_pap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + int offset = 0; + guint8 fn; + guint8 connID; + proto_tree *pap_tree = NULL; + proto_item *ti; + int len; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "PAP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + if (tree) { + ti = proto_tree_add_item(tree, proto_pap, tvb, offset, -1, FALSE); + pap_tree = proto_item_add_subtree(ti, ett_pap); + } + + connID = tvb_get_guint8(tvb, offset); + proto_tree_add_item(pap_tree, hf_pap_connid, tvb, offset, 1, FALSE); + offset++; + + fn = tvb_get_guint8(tvb, offset); + proto_tree_add_item(pap_tree, hf_pap_function, tvb, offset, 1, FALSE); + offset++; + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s ID: %d", + val_to_str(fn, pap_function_vals, "Unknown (0x%01x)"), connID); + } + switch(fn) { + case PAPOpenConn: + PAD(2); + proto_tree_add_item(pap_tree, hf_pap_socket, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(pap_tree, hf_pap_quantum, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(pap_tree, hf_pap_waittime, tvb, offset, 2, FALSE); + offset += 2; + break; + + case PAPOpenConnReply: + PAD(2); + proto_tree_add_item(pap_tree, hf_pap_socket, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(pap_tree, hf_pap_quantum, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(pap_tree, hf_pap_result, tvb, offset, 2, FALSE); + offset += 2; + offset = dissect_pascal_string(tvb, offset, pap_tree, hf_pap_status); + break; + + case PAPSendData: + proto_tree_add_item(pap_tree, hf_pap_seq, tvb, offset, 2, FALSE); + offset += 2; + break; + + case PAPData: + proto_tree_add_item(pap_tree, hf_pap_eof, tvb, offset, 1, FALSE); + offset++; + PAD(1); + /* follow by data */ + len = tvb_reported_length_remaining(tvb,offset); + call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,len), pinfo, tree); + break; + + case PAPTickle: + case PAPCloseConn: + case PAPCloseConnReply: + PAD(2); + break; + + case PAPSendStatus: + PAD(2); + break; + + case PAPStatus: + PAD(2); + PAD(4); + offset = dissect_pascal_string(tvb, offset, pap_tree, hf_pap_status); + break; + + default: /* unknown */ + break; + } +} + +/* ----------------------------- + ASP protocol cf. inside appletalk chap. 11 +*/ +static struct aspinfo * +get_transaction(tvbuff_t *tvb, packet_info *pinfo) +{ + struct aspinfo *aspinfo = pinfo->private_data; + conversation_t *conversation; + asp_request_key request_key, *new_request_key; + asp_request_val *request_val; + guint8 fn; + + conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + + if (conversation == NULL) + { + conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, pinfo->destport, 0); + } + + request_key.conversation = conversation->index; + memcpy(request_key.src, (!aspinfo->reply)?pinfo->src.data:pinfo->dst.data, 4); + request_key.seq = aspinfo->seq; + + request_val = (asp_request_val *) g_hash_table_lookup( + asp_request_hash, &request_key); + if (!request_val && !aspinfo->reply ) { + fn = tvb_get_guint8(tvb, 0); + new_request_key = se_alloc(sizeof(asp_request_key)); + *new_request_key = request_key; + + request_val = se_alloc(sizeof(asp_request_val)); + request_val->value = fn; + + g_hash_table_insert(asp_request_hash, new_request_key, + request_val); + } + + if (!request_val) + return NULL; + + aspinfo->command = request_val->value; + return aspinfo; +} + + +static void +dissect_asp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + struct aspinfo *aspinfo; + int offset = 0; + proto_tree *asp_tree = NULL; + proto_item *ti; + guint8 fn; + int len; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ASP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + aspinfo = get_transaction(tvb, pinfo); + if (!aspinfo) + return; + + fn = (guint8) aspinfo->command; + + if (check_col(pinfo->cinfo, COL_INFO)) { + if (aspinfo->reply) + col_add_fstr(pinfo->cinfo, COL_INFO, "Reply tid %u",aspinfo->seq); + else + col_add_fstr(pinfo->cinfo, COL_INFO, "Function: %s tid %u", + val_to_str(fn, asp_func_vals, "Unknown (0x%01x)"), aspinfo->seq); + } + + if (tree) { + ti = proto_tree_add_item(tree, proto_asp, tvb, offset, -1, FALSE); + asp_tree = proto_item_add_subtree(ti, ett_asp); + } + if (!aspinfo->reply) { + tvbuff_t *new_tvb; + /* let the called deal with asp_tree == NULL */ + + proto_tree_add_item(asp_tree, hf_asp_func, tvb, offset, 1, FALSE); + offset++; + switch(fn) { + case ASPFUNC_OPEN: + proto_tree_add_item(asp_tree, hf_asp_socket, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_version, tvb, offset, 2, FALSE); + offset += 2; + break; + case ASPFUNC_TICKLE: + case ASPFUNC_CLOSE: + proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 2, FALSE); + offset +=2; + break; + case ASPFUNC_STAT: + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 2, FALSE); + offset += 2; + break; + case ASPFUNC_ATTN: + proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_attn_code, tvb, offset, 2, FALSE); + offset +=2; + break; + case ASPFUNC_CMD: + case ASPFUNC_WRITE: + proto_item_set_len(asp_tree, 4); + proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_seq, tvb, offset, 2, FALSE); + offset += 2; + len = tvb_reported_length_remaining(tvb,offset); + new_tvb = tvb_new_subset(tvb, offset,-1,len); + call_dissector(afp_handle, new_tvb, pinfo, tree); + break; + case ASPFUNC_WRTCONT: + proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_seq, tvb, offset, 2, FALSE); + offset += 2; + proto_tree_add_item(asp_tree, hf_asp_size, tvb, offset, 2, FALSE); + offset += 2; + break; + default: + proto_item_set_len(asp_tree, 4); + offset += 3; + len = tvb_reported_length_remaining(tvb,offset); + call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,len), pinfo, tree); + break; + } + } + else { + tvbuff_t *new_tvb; + + proto_tree_add_uint(asp_tree, hf_asp_func, tvb, 0, 0, fn); + switch(fn) { + case ASPFUNC_OPEN: + proto_tree_add_item(asp_tree, hf_asp_socket, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_session_id, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_init_error, tvb, offset, 2, FALSE); + offset += 2; + break; + case ASPFUNC_CLOSE: + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 2, FALSE); + offset += 2; + break; + case ASPFUNC_STAT: + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 4, FALSE); + offset += 4; + dissect_asp_reply_get_status(tvb, pinfo, asp_tree, offset); + break; + case ASPFUNC_CMD: + case ASPFUNC_WRITE: + proto_item_set_len(asp_tree, 4); + aspinfo->code = tvb_get_ntohl(tvb, offset); + proto_tree_add_item(asp_tree, hf_asp_error, tvb, offset, 4, FALSE); + offset += 4; + len = tvb_reported_length_remaining(tvb,offset); + new_tvb = tvb_new_subset(tvb, offset,-1,len); + call_dissector(afp_handle, new_tvb, pinfo, tree); + break; + case ASPFUNC_TICKLE: + case ASPFUNC_WRTCONT: + proto_tree_add_item(asp_tree, hf_asp_zero_value, tvb, offset, 4, FALSE); + /* fall */ + case ASPFUNC_ATTN: /* FIXME capture and spec disagree */ + default: + proto_item_set_len(asp_tree, 4); + offset += 4; + len = tvb_reported_length_remaining(tvb,offset); + call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,len), pinfo, tree); + break; + } + } +} + +/* ----------------------------- + ZIP protocol cf. inside appletalk chap. 8 +*/ +static void +dissect_atp_zip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + struct aspinfo *aspinfo; + int offset = 0; + proto_tree *zip_tree; + proto_tree *sub_tree; + proto_item *ti; + guint8 fn; + guint16 count; + guint8 len; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ZIP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + aspinfo = get_transaction(tvb, pinfo); + if (!aspinfo) + return; + + fn = (guint8) aspinfo->command; + + if (check_col(pinfo->cinfo, COL_INFO)) { + if (aspinfo->reply) + col_add_fstr(pinfo->cinfo, COL_INFO, "Reply tid %u",aspinfo->seq); + else + col_add_fstr(pinfo->cinfo, COL_INFO, "Function: %s tid %u", + val_to_str(fn, zip_atp_function_vals, "Unknown (0x%01x)"), aspinfo->seq); + } + + if (!tree) + return; + + ti = proto_tree_add_item(tree, proto_zip, tvb, offset, -1, FALSE); + zip_tree = proto_item_add_subtree(ti, ett_zip); + + if (!aspinfo->reply) { + proto_tree_add_item(zip_tree, hf_zip_atp_function, tvb, offset, 1, FALSE); + offset++; + switch(fn) { + case 7: /* start_index = 0 */ + case 8: + case 9: + proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(zip_tree, hf_zip_start_index, tvb, offset, 2, FALSE); + break; + } + } + else { + guint8 i; + + proto_tree_add_uint(zip_tree, hf_zip_atp_function, tvb, 0, 0, fn); + switch(fn) { + case 7: + case 8: + case 9: + proto_tree_add_item(zip_tree, hf_zip_last_flag, tvb, offset, 1, FALSE); + offset++; + + proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 1, FALSE); + offset++; + count = tvb_get_ntohs(tvb, offset); + ti = proto_tree_add_item(zip_tree, hf_zip_count, tvb, offset, 2, FALSE); + offset += 2; + sub_tree = proto_item_add_subtree(ti, ett_zip_zones_list); + for (i= 0; i < count; i++) { + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(sub_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + offset += len +1; + } + break; + } + } +} + +static void +dissect_ddp_zip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_tree *zip_tree = NULL; + proto_item *ti; + guint8 fn; + guint8 len; + gint offset = 0; + proto_tree *flag_tree; + proto_tree *sub_tree; + proto_tree *net_tree; + guint8 flag; + guint16 net; + guint8 i; + guint8 count; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ZIP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + fn = tvb_get_guint8(tvb, 0); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(fn, zip_function_vals, "Unknown ZIP function (%02x)")); + } + + if (!tree) + return; + + ti = proto_tree_add_item(tree, proto_zip, tvb, 0, -1, FALSE); + zip_tree = proto_item_add_subtree(ti, ett_zip); + + proto_tree_add_item(zip_tree, hf_zip_function, tvb, offset, 1,FALSE); + offset++; + /* fn 1,7,2,8 are not tested */ + switch (fn) { + case 1: /* Query */ + count = tvb_get_guint8(tvb, offset); + ti = proto_tree_add_item(zip_tree, hf_zip_network_count, tvb, offset, 1, FALSE); + offset++; + sub_tree = proto_item_add_subtree(ti, ett_zip_network_list); + for (i= 0; i < count; i++) { + proto_tree_add_item(sub_tree, hf_zip_network, tvb, offset, 2, FALSE); + offset += 2; + } + break; + case 7: /* Notify */ + flag = tvb_get_guint8(tvb, offset); + ti = proto_tree_add_text(zip_tree, tvb, offset , 1,"Flags : 0x%02x", flag); + flag_tree = proto_item_add_subtree(ti, ett_zip_flags); + proto_tree_add_item(flag_tree, hf_zip_flags_zone_invalid, tvb, offset, 1,FALSE); + proto_tree_add_item(flag_tree, hf_zip_flags_use_broadcast,tvb, offset, 1,FALSE); + proto_tree_add_item(flag_tree, hf_zip_flags_only_one_zone,tvb, offset, 1,FALSE); + offset++; + + proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 4, FALSE); + offset += 4; + + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + offset += len +1; + + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(zip_tree, hf_zip_multicast_length,tvb, offset, 1,FALSE); + offset++; + proto_tree_add_item(zip_tree, hf_zip_multicast_address,tvb, offset, len,FALSE); + offset += len; + + proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + break; + + case 2: /* Reply */ + case 8: /* Extended Reply */ + count = tvb_get_guint8(tvb, offset); + ti = proto_tree_add_item(zip_tree, hf_zip_network_count, tvb, offset, 1, FALSE); + offset++; + sub_tree = proto_item_add_subtree(ti, ett_zip_network_list); + for (i= 0; i < count; i++) { + net = tvb_get_ntohs(tvb, offset); + ti = proto_tree_add_text(zip_tree, tvb, offset , 2, "Zone for network : %u", net); + net_tree = proto_item_add_subtree(ti, ett_zip_network_list); + proto_tree_add_item(net_tree, hf_zip_network, tvb, offset, 2, FALSE); + offset += 2; + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(net_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + offset += len +1; + } + break; + + case 5 : /* GetNetInfo request */ + proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 1, FALSE); + offset++; + proto_tree_add_item(zip_tree, hf_zip_zero_value, tvb, offset, 4, FALSE); + offset += 4; + proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + break; + + case 6 : /* GetNetInfo reply */ + flag = tvb_get_guint8(tvb, offset); + ti = proto_tree_add_text(zip_tree, tvb, offset , 1,"Flags : 0x%02x", flag); + flag_tree = proto_item_add_subtree(ti, ett_zip_flags); + proto_tree_add_item(flag_tree, hf_zip_flags_zone_invalid, tvb, offset, 1,FALSE); + proto_tree_add_item(flag_tree, hf_zip_flags_use_broadcast,tvb, offset, 1,FALSE); + proto_tree_add_item(flag_tree, hf_zip_flags_only_one_zone,tvb, offset, 1,FALSE); + offset++; + + proto_tree_add_item(zip_tree, hf_zip_network_start, tvb, offset, 2, FALSE); + offset += 2; + + proto_tree_add_item(zip_tree, hf_zip_network_end, tvb, offset, 2, FALSE); + offset += 2; + + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(zip_tree, hf_zip_zone_name, tvb, offset, 1,FALSE); + offset += len +1; + + len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(zip_tree, hf_zip_multicast_length,tvb, offset, 1,FALSE); + offset++; + proto_tree_add_item(zip_tree, hf_zip_multicast_address,tvb, offset, len,FALSE); + offset += len; + if ((flag & 0x80) != 0) + proto_tree_add_item(zip_tree, hf_zip_default_zone, tvb, offset, 1,FALSE); + break; + + default: + break; + } +} + +static void +dissect_ddp_short(tvbuff_t *tvb, packet_info *pinfo, guint8 dnode, + guint8 snode, proto_tree *tree) +{ + guint16 len; + guint8 dport; + guint8 sport; + guint8 type; + proto_tree *ddp_tree = NULL; + proto_item *ti; + static struct atalk_ddp_addr src, dst; + tvbuff_t *new_tvb; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "DDP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + if (tree) { + ti = proto_tree_add_item(tree, proto_ddp, tvb, 0, DDP_SHORT_HEADER_SIZE, + FALSE); + ddp_tree = proto_item_add_subtree(ti, ett_ddp); + } + len = tvb_get_ntohs(tvb, 0); + if (tree) + proto_tree_add_uint(ddp_tree, hf_ddp_len, tvb, 0, 2, len); + dport = tvb_get_guint8(tvb, 2); + if (tree) + proto_tree_add_uint(ddp_tree, hf_ddp_dst_socket, tvb, 2, 1, dport); + sport = tvb_get_guint8(tvb, 3); + if (tree) + proto_tree_add_uint(ddp_tree, hf_ddp_src_socket, tvb, 3, 1, sport); + type = tvb_get_guint8(tvb, 4); + + src.net = 0; + src.node = snode; + dst.net = 0; + dst.node = dnode; + SET_ADDRESS(&pinfo->net_src, AT_ATALK, sizeof src, (guint8 *)&src); + SET_ADDRESS(&pinfo->src, AT_ATALK, sizeof src, (guint8 *)&src); + SET_ADDRESS(&pinfo->net_dst, AT_ATALK, sizeof dst, (guint8 *)&dst); + SET_ADDRESS(&pinfo->dst, AT_ATALK, sizeof dst, (guint8 *)&dst); + + pinfo->ptype = PT_DDP; + pinfo->destport = dport; + pinfo->srcport = sport; + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(type, op_vals, "Unknown DDP protocol (%02x)")); + } + if (tree) { + proto_tree_add_string_hidden(ddp_tree, hf_ddp_src, tvb, + 4, 3, atalk_addr_to_str(&src)); + proto_tree_add_string_hidden(ddp_tree, hf_ddp_dst, tvb, + 6, 3, atalk_addr_to_str(&dst)); + + proto_tree_add_uint(ddp_tree, hf_ddp_type, tvb, 4, 1, type); + } + new_tvb = tvb_new_subset(tvb, DDP_SHORT_HEADER_SIZE, -1, -1); + + if (!dissector_try_port(ddp_dissector_table, type, new_tvb, pinfo, tree)) + call_dissector(data_handle,new_tvb, pinfo, tree); +} + +static void +dissect_ddp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + e_ddp ddp; + proto_tree *ddp_tree; + proto_item *ti; + static struct atalk_ddp_addr src, dst; + tvbuff_t *new_tvb; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "DDP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + tvb_memcpy(tvb, (guint8 *)&ddp, 0, sizeof(e_ddp)); + ddp.dnet=g_ntohs(ddp.dnet); + ddp.snet=g_ntohs(ddp.snet); + ddp.sum=g_ntohs(ddp.sum); + ddp.hops_len=g_ntohs(ddp.hops_len); + + src.net = ddp.snet; + src.node = ddp.snode; + dst.net = ddp.dnet; + dst.node = ddp.dnode; + SET_ADDRESS(&pinfo->net_src, AT_ATALK, sizeof src, (guint8 *)&src); + SET_ADDRESS(&pinfo->src, AT_ATALK, sizeof src, (guint8 *)&src); + SET_ADDRESS(&pinfo->net_dst, AT_ATALK, sizeof dst, (guint8 *)&dst); + SET_ADDRESS(&pinfo->dst, AT_ATALK, sizeof dst, (guint8 *)&dst); + + pinfo->ptype = PT_DDP; + pinfo->destport = ddp.dport; + pinfo->srcport = ddp.sport; + + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(ddp.type, op_vals, "Unknown DDP protocol (%02x)")); + + if (tree) { + ti = proto_tree_add_item(tree, proto_ddp, tvb, 0, DDP_HEADER_SIZE, + FALSE); + ddp_tree = proto_item_add_subtree(ti, ett_ddp); + + proto_tree_add_string_hidden(ddp_tree, hf_ddp_src, tvb, + 4, 3, atalk_addr_to_str(&src)); + proto_tree_add_string_hidden(ddp_tree, hf_ddp_dst, tvb, + 6, 3, atalk_addr_to_str(&dst)); + + proto_tree_add_uint(ddp_tree, hf_ddp_hopcount, tvb, 0, 1, + ddp_hops(ddp.hops_len)); + proto_tree_add_uint(ddp_tree, hf_ddp_len, tvb, 0, 2, + ddp_len(ddp.hops_len)); + proto_tree_add_uint(ddp_tree, hf_ddp_checksum, tvb, 2, 2, + ddp.sum); + proto_tree_add_uint(ddp_tree, hf_ddp_dst_net, tvb, 4, 2, + ddp.dnet); + proto_tree_add_uint(ddp_tree, hf_ddp_src_net, tvb, 6, 2, + ddp.snet); + proto_tree_add_uint(ddp_tree, hf_ddp_dst_node, tvb, 8, 1, + ddp.dnode); + proto_tree_add_uint(ddp_tree, hf_ddp_src_node, tvb, 9, 1, + ddp.snode); + proto_tree_add_uint(ddp_tree, hf_ddp_dst_socket, tvb, 10, 1, + ddp.dport); + proto_tree_add_uint(ddp_tree, hf_ddp_src_socket, tvb, 11, 1, + ddp.sport); + proto_tree_add_uint(ddp_tree, hf_ddp_type, tvb, 12, 1, + ddp.type); + } + + new_tvb = tvb_new_subset(tvb, DDP_HEADER_SIZE, -1, -1); + + if (!dissector_try_port(ddp_dissector_table, ddp.type, new_tvb, pinfo, tree)) + call_dissector(data_handle,new_tvb, pinfo, tree); +} + +static const value_string llap_type_vals[] = { + {0x01, "Short DDP"}, + {0x02, "DDP" }, + {0x81, "Enquiry"}, + {0x82, "Acknowledgement"}, + {0x84, "RTS"}, + {0x85, "CTS"}, + {0, NULL} +}; + +void +capture_llap(packet_counts *ld) +{ + ld->other++; +} + +static void +dissect_llap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + guint8 dnode; + guint8 snode; + guint8 type; + proto_tree *llap_tree = NULL; + proto_item *ti; + tvbuff_t *new_tvb; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "LLAP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + if (tree) { + ti = proto_tree_add_item(tree, proto_llap, tvb, 0, 3, FALSE); + llap_tree = proto_item_add_subtree(ti, ett_llap); + } + + dnode = tvb_get_guint8(tvb, 0); + if (tree) + proto_tree_add_uint(llap_tree, hf_llap_dst, tvb, 0, 1, dnode); + snode = tvb_get_guint8(tvb, 1); + if (tree) + proto_tree_add_uint(llap_tree, hf_llap_src, tvb, 1, 1, snode); + type = tvb_get_guint8(tvb, 2); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_str(pinfo->cinfo, COL_INFO, + val_to_str(type, llap_type_vals, "Unknown LLAP type (%02x)")); + } + if (tree) + proto_tree_add_uint(llap_tree, hf_llap_type, tvb, 2, 1, type); + + new_tvb = tvb_new_subset(tvb, 3, -1, -1); + + switch (type) { + + case 0x01: + if (proto_is_protocol_enabled(find_protocol_by_id(proto_ddp))) { + pinfo->current_proto = "DDP"; + dissect_ddp_short(new_tvb, pinfo, dnode, snode, tree); + return; + } + + case 0x02: + if (call_dissector(ddp_handle, new_tvb, pinfo, tree)) + return; + } + call_dissector(data_handle,new_tvb, pinfo, tree); +} + +static void +atp_init(void) +{ + /* fragment */ + fragment_table_init(&atp_fragment_table); + reassembled_table_init(&atp_reassembled_table); + /* bitmap */ + if (atp_request_hash) + g_hash_table_destroy(atp_request_hash); + + atp_request_hash = g_hash_table_new(asp_hash, asp_equal); + +} + +static void +asp_reinit( void) +{ + + if (asp_request_hash) + g_hash_table_destroy(asp_request_hash); + + asp_request_hash = g_hash_table_new(asp_hash, asp_equal); + +} + +void +proto_register_atalk(void) +{ + static hf_register_info hf_llap[] = { + { &hf_llap_dst, + { "Destination Node", "llap.dst", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_llap_src, + { "Source Node", "llap.src", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_llap_type, + { "Type", "llap.type", FT_UINT8, BASE_HEX, VALS(llap_type_vals), 0x0, + "", HFILL }}, + }; + + static hf_register_info hf_ddp[] = { + { &hf_ddp_hopcount, + { "Hop count", "ddp.hopcount", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_len, + { "Datagram length", "ddp.len", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_checksum, + { "Checksum", "ddp.checksum", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_dst, + { "Destination address", "ddp.dst", FT_STRING, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_dst_net, + { "Destination Net", "ddp.dst.net", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_src, + { "Source address", "ddp.src", FT_STRING, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_src_net, + { "Source Net", "ddp.src.net", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_dst_node, + { "Destination Node", "ddp.dst.node", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_src_node, + { "Source Node", "ddp.src.node", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_dst_socket, + { "Destination Socket", "ddp.dst_socket", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_src_socket, + { "Source Socket", "ddp.src_socket", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_ddp_type, + { "Protocol type", "ddp.type", FT_UINT8, BASE_DEC, VALS(op_vals), 0x0, + "", HFILL }}, + }; + + static hf_register_info hf_nbp[] = { + { &hf_nbp_op, + { "Operation", "nbp.op", FT_UINT8, BASE_DEC, + VALS(nbp_op_vals), 0xF0, "Operation", HFILL }}, + { &hf_nbp_info, + { "Info", "nbp.info", FT_UINT8, BASE_HEX, + NULL, 0x0, "Info", HFILL }}, + { &hf_nbp_count, + { "Count", "nbp.count", FT_UINT8, BASE_DEC, + NULL, 0x0F, "Count", HFILL }}, + { &hf_nbp_node_net, + { "Network", "nbp.net", FT_UINT16, BASE_DEC, + NULL, 0x0, "Network", HFILL }}, + { &hf_nbp_node_node, + { "Node", "nbp.node", FT_UINT8, BASE_DEC, + NULL, 0x0, "Node", HFILL }}, + { &hf_nbp_node_port, + { "Port", "nbp.port", FT_UINT8, BASE_DEC, + NULL, 0x0, "Port", HFILL }}, + { &hf_nbp_node_enum, + { "Enumerator", "nbp.enum", FT_UINT8, BASE_DEC, + NULL, 0x0, "Enumerator", HFILL }}, + { &hf_nbp_node_object, + { "Object", "nbp.object", FT_STRING, BASE_DEC, + NULL, 0x0, "Object", HFILL }}, + { &hf_nbp_node_type, + { "Type", "nbp.type", FT_STRING, BASE_DEC, + NULL, 0x0, "Type", HFILL }}, + { &hf_nbp_node_zone, + { "Zone", "nbp.zone", FT_STRING, BASE_DEC, + NULL, 0x0, "Zone", HFILL }}, + { &hf_nbp_tid, + { "Transaction ID", "nbp.tid", FT_UINT8, BASE_DEC, + NULL, 0x0, "Transaction ID", HFILL }} + }; + + static hf_register_info hf_rtmp[] = { + { &hf_rtmp_net, + { "Net", "rtmp.net", FT_UINT16, BASE_DEC, + NULL, 0x0, "Net", HFILL }}, + { &hf_rtmp_node, + { "Node", "nbp.nodeid", FT_UINT8, BASE_DEC, + NULL, 0x0, "Node", HFILL }}, + { &hf_rtmp_node_len, + { "Node Length", "nbp.nodeid.length", FT_UINT8, BASE_DEC, + NULL, 0x0, "Node Length", HFILL }}, + { &hf_rtmp_tuple_net, + { "Net", "rtmp.tuple.net", FT_UINT16, BASE_DEC, + NULL, 0x0, "Net", HFILL }}, + { &hf_rtmp_tuple_range_start, + { "Range Start", "rtmp.tuple.range_start", FT_UINT16, BASE_DEC, + NULL, 0x0, "Range Start", HFILL }}, + { &hf_rtmp_tuple_range_end, + { "Range End", "rtmp.tuple.range_end", FT_UINT16, BASE_DEC, + NULL, 0x0, "Range End", HFILL }}, + { &hf_rtmp_tuple_dist, + { "Distance", "rtmp.tuple.dist", FT_UINT16, BASE_DEC, + NULL, 0x0, "Distance", HFILL }}, + { &hf_rtmp_function, + { "Function", "rtmp.function", FT_UINT8, BASE_DEC, + VALS(rtmp_function_vals), 0x0, "Request Function", HFILL }} + }; + + static hf_register_info hf_atp[] = { + { &hf_atp_ctrlinfo, + { "Control info", "atp.ctrlinfo", FT_UINT8, BASE_HEX, + NULL, 0, "control info", HFILL }}, + + { &hf_atp_function, + { "Function", "atp.function", FT_UINT8, BASE_DEC, + VALS(atp_function_vals), ATP_FUNCMASK, "function code", HFILL }}, + + + { &hf_atp_xo, + { "XO", "atp.xo", FT_BOOLEAN, 8, + NULL, ATP_XO, "Exactly-once flag", HFILL }}, + + { &hf_atp_eom, + { "EOM", "atp.eom", FT_BOOLEAN, 8, + NULL, ATP_EOM, "End-of-message", HFILL }}, + + { &hf_atp_sts, + { "STS", "atp.sts", FT_BOOLEAN, 8, + NULL, ATP_STS, "Send transaction status", HFILL }}, + + { &hf_atp_treltimer, + { "TRel timer", "atp.treltimer", FT_UINT8, BASE_DEC, + VALS(atp_trel_timer_vals), 0x07, "TRel timer", HFILL }}, + + { &hf_atp_bitmap, + { "Bitmap", "atp.bitmap", FT_UINT8, BASE_HEX, + NULL, 0x0, "Bitmap or sequence number", HFILL }}, + + { &hf_atp_tid, + { "TID", "atp.tid", FT_UINT16, BASE_DEC, + NULL, 0x0, "Transaction id", HFILL }}, + { &hf_atp_user_bytes, + { "User bytes", "atp.user_bytes", FT_UINT32, BASE_HEX, + NULL, 0x0, "User bytes", HFILL }}, + + { &hf_atp_segment_overlap, + { "Segment overlap", "atp.segment.overlap", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Segment overlaps with other segments", HFILL }}, + + { &hf_atp_segment_overlap_conflict, + { "Conflicting data in segment overlap", "atp.segment.overlap.conflict", + FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Overlapping segments contained conflicting data", HFILL }}, + + { &hf_atp_segment_multiple_tails, + { "Multiple tail segments found", "atp.segment.multipletails", + FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Several tails were found when desegmenting the packet", HFILL }}, + + { &hf_atp_segment_too_long_segment, + { "Segment too long", "atp.segment.toolongsegment", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Segment contained data past end of packet", HFILL }}, + + { &hf_atp_segment_error, + {"Desegmentation error", "atp.segment.error", FT_FRAMENUM, BASE_NONE, + NULL, 0x0, "Desegmentation error due to illegal segments", HFILL }}, + + { &hf_atp_segment, + { "ATP Fragment", "atp.fragment", FT_FRAMENUM, BASE_NONE, + NULL, 0x0, "ATP Fragment", HFILL }}, + + { &hf_atp_segments, + { "ATP Fragments", "atp.fragments", FT_NONE, BASE_NONE, + NULL, 0x0, "ATP Fragments", HFILL }}, + + { &hf_atp_reassembled_in, + { "Reassembled ATP in frame", "atp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, + "This ATP packet is reassembled in this frame", HFILL }} + }; + + static hf_register_info hf_asp[] = { + { &hf_asp_func, + { "asp function", "asp.function", FT_UINT8, BASE_DEC, + VALS(asp_func_vals), 0, "asp function", HFILL }}, + + { &hf_asp_error, + { "asp error", "asp.error", FT_INT32, BASE_DEC, + VALS(asp_error_vals), 0, "return error code", HFILL }}, + + { &hf_asp_version, + { "Version", "asp.version", FT_UINT16, BASE_HEX, + NULL, 0, "asp version", HFILL }}, + + { &hf_asp_attn_code, + { "Attn code", "asp.attn_code", FT_UINT16, BASE_HEX, + NULL, 0, "asp attention code", HFILL }}, + + { &hf_asp_init_error, + { "Error", "asp.init_error", FT_UINT16, BASE_DEC, + NULL, 0, "asp init error", HFILL }}, + + { &hf_asp_session_id, + { "Session ID", "asp.session_id", FT_UINT8, BASE_DEC, + NULL, 0, "asp session id", HFILL }}, + + { &hf_asp_socket, + { "Socket", "asp.socket", FT_UINT8, BASE_DEC, + NULL, 0, "asp socket", HFILL }}, + + { &hf_asp_seq, + { "Sequence", "asp.seq", FT_UINT16, BASE_DEC, + NULL, 0, "asp sequence number", HFILL }}, + + { &hf_asp_size, + { "size", "asp.size", FT_UINT16, BASE_DEC, + NULL, 0, "asp available size for reply", HFILL }}, + + { &hf_asp_zero_value, + { "Pad (0)", "asp.zero_value", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Pad", HFILL }}, + + /* asp ,dsi, afp */ + { &hf_asp_server_name, + { "Server name", "asp.server_name", + FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "Server name", HFILL }}, + + { &hf_asp_server_type, + { "Server type", "asp.server_type", + FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "Server type", HFILL }}, + + { &hf_asp_server_vers, + { "AFP version", "asp.server_vers", + FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "AFP version", HFILL }}, + + { &hf_asp_server_uams, + { "UAM", "asp.server_uams", + FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "UAM", HFILL }}, + + { &hf_asp_server_icon, + { "Icon bitmap", "asp.server_icon", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Server icon bitmap", HFILL }}, + + { &hf_asp_server_directory, + { "Directory service", "asp.server_directory", + FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "Server directory service", HFILL }}, + + { &hf_asp_server_signature, + { "Server signature", "asp.server_signature", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Server signature", HFILL }}, + + { &hf_asp_server_flag, + { "Flag", "asp.server_flag", + FT_UINT16, BASE_HEX, NULL, 0x0, + "Server capabilities flag", HFILL }}, + { &hf_asp_server_flag_copyfile, + { "Support copyfile", "asp.server_flag.copyfile", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_COPY, + "Server support copyfile", HFILL }}, + { &hf_asp_server_flag_passwd, + { "Support change password", "asp.server_flag.passwd", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_PASSWD, + "Server support change password", HFILL }}, + { &hf_asp_server_flag_no_save_passwd, + { "Don't allow save password", "asp.server_flag.no_save_passwd", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_NOSAVEPASSWD, + "Don't allow save password", HFILL }}, + { &hf_asp_server_flag_srv_msg, + { "Support server message", "asp.server_flag.srv_msg", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVMSGS, + "Support server message", HFILL }}, + { &hf_asp_server_flag_srv_sig, + { "Support server signature", "asp.server_flag.srv_sig", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVSIGNATURE, + "Support server signature", HFILL }}, + { &hf_asp_server_flag_tcpip, + { "Support TCP/IP", "asp.server_flag.tcpip", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_TCPIP, + "Server support TCP/IP", HFILL }}, + { &hf_asp_server_flag_notify, + { "Support server notifications", "asp.server_flag.notify", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVNOTIFY, + "Server support notifications", HFILL }}, + { &hf_asp_server_flag_reconnect, + { "Support server reconnect", "asp.server_flag.reconnect", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVRECONNECT, + "Server support reconnect", HFILL }}, + { &hf_asp_server_flag_directory, + { "Support directory services", "asp.server_flag.directory", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVDIRECTORY, + "Server support directory services", HFILL }}, + { &hf_asp_server_flag_utf8_name, + { "Support UTF8 server name", "asp.server_flag.utf8_name", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_SRVUTF8, + "Server support UTF8 server name", HFILL }}, + { &hf_asp_server_flag_fast_copy, + { "Support fast copy", "asp.server_flag.fast_copy", + FT_BOOLEAN, 16, NULL, AFPSRVRINFO_FASTBOZO, + "Server support fast copy", HFILL }}, + + { &hf_asp_server_addr_len, + { "Length", "asp.server_addr.len", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Address length.", HFILL }}, + + { &hf_asp_server_addr_type, + { "Type", "asp.server_addr.type", + FT_UINT8, BASE_DEC, VALS(afp_server_addr_type_vals), 0x0, + "Address type.", HFILL }}, + + { &hf_asp_server_addr_value, + { "Value", "asp.server_addr.value", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Address value", HFILL }}, + + { &hf_asp_server_utf8_name_len, + { "Server name length", "asp.server_utf8_name_len", + FT_UINT16, BASE_DEC, NULL, 0x0, + "UTF8 server name length", HFILL }}, + + { &hf_asp_server_utf8_name, + { "Server name (UTF8)", "asp.server_utf8_name", + FT_STRING, BASE_NONE, NULL, 0x0, + "Server name (UTF8)", HFILL }}, + }; + + static hf_register_info hf_zip[] = { + { &hf_zip_function, + { "Function", "zip.function", FT_UINT8, BASE_DEC, VALS(zip_function_vals), 0x0, + "ZIP function", HFILL }}, + + { &hf_zip_zero_value, + { "Pad (0)", "zip.zero_value",FT_BYTES, BASE_HEX, NULL, 0x0, + "Pad", HFILL }}, + + { &hf_zip_atp_function, + { "Function", "zip.atp_function", FT_UINT8, BASE_DEC, VALS(zip_atp_function_vals), 0x0, + "", HFILL }}, + + { &hf_zip_start_index, + { "Start index", "zip.start_index", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_zip_count, + { "Count", "zip.count", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_zip_network_count, + { "Count", "zip.network_count", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + { &hf_zip_network, + { "Network","zip.network", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + { &hf_zip_network_start, + { "Network start","zip.network_start", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + { &hf_zip_network_end, + { "Network end", "zip.network_end", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_zip_flags, + { "Flags", "zip.flags", FT_BOOLEAN, 8, NULL, 0xC0, + "", HFILL }}, + + { &hf_zip_last_flag, + { "Last Flag", "zip.last_flag", FT_BOOLEAN, 8, NULL, 0, + "Non zero if contains last zone name in the zone list", HFILL }}, + + { &hf_zip_flags_zone_invalid, + { "Zone invalid", "zip.flags.zone_invalid", FT_BOOLEAN, 8, NULL, 0x80, + "", HFILL }}, + + { &hf_zip_flags_use_broadcast, + { "Use broadcast","zip.flags.use_broadcast", FT_BOOLEAN, 8, NULL, 0x40, + "", HFILL }}, + + { &hf_zip_flags_only_one_zone, + { "Only one zone","zip.flags.only_one_zone", FT_BOOLEAN, 8, NULL, 0x20, + "", HFILL }}, + + { &hf_zip_zone_name, + { "Zone", "zip.zone_name", FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_zip_default_zone, + { "Default zone", "zip.default_zone",FT_UINT_STRING, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_zip_multicast_length, + { "Multicast length", "zip.multicast_length", FT_UINT8, BASE_DEC, NULL, 0x0, + "Multicast address length", HFILL }}, + + { &hf_zip_multicast_address, + { "Multicast address", "zip.multicast_address",FT_BYTES, BASE_HEX, NULL, 0x0, + "Multicast address", HFILL }}, + + }; + + static hf_register_info hf_pap[] = { + { &hf_pap_connid, + { "ConnID", "pap.connid", FT_UINT8, BASE_DEC, NULL, 0x0, + "PAP connection ID", HFILL }}, + + { &hf_pap_function, + { "Function", "pap.function", FT_UINT8, BASE_DEC, VALS(pap_function_vals), 0x0, + "PAP function", HFILL }}, + + { &hf_pap_socket, + { "Socket", "pap.socket", FT_UINT8, BASE_DEC, NULL, 0x0, + "ATP responding socket number", HFILL }}, + + { &hf_pap_quantum, + { "Quantum", "pap.quantum", FT_UINT8, BASE_DEC, NULL, 0x0, + "Flow quantum", HFILL }}, + + { &hf_pap_waittime, + { "Wait time", "pap.quantum", FT_UINT16, BASE_DEC, NULL, 0x0, + "Wait time", HFILL }}, + + { &hf_pap_result, + { "Result", "pap.quantum", FT_UINT16, BASE_DEC, NULL, 0x0, + "Result", HFILL }}, + + { &hf_pap_seq, + { "Sequence", "pap.seq", FT_UINT16, BASE_DEC, NULL, 0x0, + "Sequence number", HFILL }}, + + { &hf_pap_status, + { "Status", "pap.status", FT_STRING, BASE_DEC, NULL, 0x0, + "Printer status", HFILL }}, + + { &hf_pap_eof, + { "EOF", "pap.eof", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "EOF", HFILL }}, + + { &hf_pap_pad, + { "Pad", "pad.pad", FT_NONE, BASE_NONE, NULL, 0, + "Pad Byte", HFILL }}, + + }; + + static gint *ett[] = { + &ett_llap, + &ett_ddp, + &ett_atp, + &ett_atp_info, + &ett_atp_segments, + &ett_atp_segment, + &ett_asp, + &ett_pap, + + /* asp dsi afp */ + &ett_asp_status, + &ett_asp_status_server_flag, + &ett_asp_vers, + &ett_asp_uams, + &ett_asp_addr, + &ett_asp_addr_line, + &ett_asp_directory, + &ett_asp_utf8_name, + + &ett_nbp, + &ett_nbp_info, + &ett_nbp_node, + &ett_pstring, + &ett_rtmp, + &ett_rtmp_tuple, + + &ett_zip, + &ett_zip_flags, + &ett_zip_zones_list, + &ett_zip_network_list, + }; + module_t *atp_module; + + proto_llap = proto_register_protocol("LocalTalk Link Access Protocol", "LLAP", "llap"); + proto_register_field_array(proto_llap, hf_llap, array_length(hf_llap)); + + proto_ddp = proto_register_protocol("Datagram Delivery Protocol", "DDP", "ddp"); + proto_register_field_array(proto_ddp, hf_ddp, array_length(hf_ddp)); + + proto_nbp = proto_register_protocol("Name Binding Protocol", "NBP", "nbp"); + proto_register_field_array(proto_nbp, hf_nbp, array_length(hf_nbp)); + + proto_atp = proto_register_protocol("AppleTalk Transaction Protocol packet", "ATP", "atp"); + proto_register_field_array(proto_atp, hf_atp, array_length(hf_atp)); + + proto_asp = proto_register_protocol("AppleTalk Session Protocol", "ASP", "asp"); + proto_register_field_array(proto_asp, hf_asp, array_length(hf_asp)); + + proto_pap = proto_register_protocol("Printer Access Protocol", "PAP", "apap"); + proto_register_field_array(proto_pap, hf_pap, array_length(hf_pap)); + + proto_zip = proto_register_protocol("Zone Information Protocol", "ZIP", "zip"); + proto_register_field_array(proto_zip, hf_zip, array_length(hf_zip)); + + atp_module = prefs_register_protocol(proto_atp, NULL); + prefs_register_bool_preference(atp_module, "desegment", + "Reassemble ATP messages spanning multiple DDP packets", + "Whether the ATP dissector should reassemble messages spanning multiple DDP packets", + &atp_defragment); + + proto_rtmp = proto_register_protocol("Routing Table Maintenance Protocol", + "RTMP", "rtmp"); + proto_register_field_array(proto_rtmp, hf_rtmp, array_length(hf_rtmp)); + + proto_register_subtree_array(ett, array_length(ett)); + + /* subdissector code */ + ddp_dissector_table = register_dissector_table("ddp.type", "DDP packet type", + FT_UINT8, BASE_HEX); +} + +void +proto_reg_handoff_atalk(void) +{ + dissector_handle_t nbp_handle, rtmp_request_handle; + dissector_handle_t atp_handle; + dissector_handle_t zip_ddp_handle; + dissector_handle_t rtmp_data_handle, llap_handle; + + ddp_handle = create_dissector_handle(dissect_ddp, proto_ddp); + dissector_add("ethertype", ETHERTYPE_ATALK, ddp_handle); + dissector_add("chdlctype", ETHERTYPE_ATALK, ddp_handle); + dissector_add("ppp.protocol", PPP_AT, ddp_handle); + dissector_add("null.type", BSD_AF_APPLETALK, ddp_handle); + dissector_add("arcnet.protocol_id", ARCNET_PROTO_APPLETALK, ddp_handle); + + nbp_handle = create_dissector_handle(dissect_nbp, proto_nbp); + dissector_add("ddp.type", DDP_NBP, nbp_handle); + + atp_handle = create_dissector_handle(dissect_atp, proto_atp); + dissector_add("ddp.type", DDP_ATP, atp_handle); + + asp_handle = create_dissector_handle(dissect_asp, proto_asp); + pap_handle = create_dissector_handle(dissect_pap, proto_pap); + + rtmp_request_handle = create_dissector_handle(dissect_rtmp_request, proto_rtmp); + rtmp_data_handle = create_dissector_handle(dissect_rtmp_data, proto_rtmp); + dissector_add("ddp.type", DDP_RTMPREQ, rtmp_request_handle); + dissector_add("ddp.type", DDP_RTMPDATA, rtmp_data_handle); + + zip_ddp_handle = create_dissector_handle(dissect_ddp_zip, proto_zip); + dissector_add("ddp.type", DDP_ZIP, zip_ddp_handle); + + zip_atp_handle = create_dissector_handle(dissect_atp_zip, proto_zip); + + llap_handle = create_dissector_handle(dissect_llap, proto_llap); + dissector_add("wtap_encap", WTAP_ENCAP_LOCALTALK, llap_handle); + + register_init_routine( atp_init); + register_init_routine( &asp_reinit); + + afp_handle = find_dissector("afp"); + data_handle = find_dissector("data"); +} diff --git a/src/main/resource/testFiles/wireshark/revFiles/bb4516_9597f6_epan#sigcomp-udvm.c b/src/main/resource/testFiles/wireshark/revFiles/bb4516_9597f6_epan#sigcomp-udvm.c new file mode 100644 index 0000000..53b6c7a --- /dev/null +++ b/src/main/resource/testFiles/wireshark/revFiles/bb4516_9597f6_epan#sigcomp-udvm.c @@ -0,0 +1,3209 @@ +/* sigcomp-udvm.c + * Routines making up the Universal Decompressor Virtual Machine (UDVM) used for + * Signaling Compression (SigComp) dissection. + * Copyright 2004, Anders Broman + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * References: + * http://www.ietf.org/rfc/rfc3320.txt?number=3320 + * http://www.ietf.org/rfc/rfc3321.txt?number=3321 + * Useful links : + * http://www.ietf.org/internet-drafts/draft-ietf-rohc-sigcomp-impl-guide-05.txt + * http://www.ietf.org/internet-drafts/draft-ietf-rohc-sigcomp-sip-01.txt + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "packet.h" +#include "strutil.h" +#include "sigcomp-udvm.h" +#include "sigcomp_state_hdlr.h" +#include "sha1.h" +#include "crc16.h" +#include "except.h" + +#define SIGCOMP_INSTR_DECOMPRESSION_FAILURE 0 +#define SIGCOMP_INSTR_AND 1 +#define SIGCOMP_INSTR_OR 2 +#define SIGCOMP_INSTR_NOT 3 +#define SIGCOMP_INSTR_LSHIFT 4 +#define SIGCOMP_INSTR_RSHIFT 5 +#define SIGCOMP_INSTR_ADD 6 +#define SIGCOMP_INSTR_SUBTRACT 7 +#define SIGCOMP_INSTR_MULTIPLY 8 +#define SIGCOMP_INSTR_DIVIDE 9 +#define SIGCOMP_INSTR_REMAINDER 10 +#define SIGCOMP_INSTR_SORT_ASCENDING 11 +#define SIGCOMP_INSTR_SORT_DESCENDING 12 +#define SIGCOMP_INSTR_SHA_1 13 +#define SIGCOMP_INSTR_LOAD 14 +#define SIGCOMP_INSTR_MULTILOAD 15 +#define SIGCOMP_INSTR_PUSH 16 +#define SIGCOMP_INSTR_POP 17 +#define SIGCOMP_INSTR_COPY 18 +#define SIGCOMP_INSTR_COPY_LITERAL 19 +#define SIGCOMP_INSTR_COPY_OFFSET 20 +#define SIGCOMP_INSTR_MEMSET 21 +#define SIGCOMP_INSTR_JUMP 22 +#define SIGCOMP_INSTR_COMPARE 23 +#define SIGCOMP_INSTR_CALL 24 +#define SIGCOMP_INSTR_RETURN 25 +#define SIGCOMP_INSTR_SWITCH 26 +#define SIGCOMP_INSTR_CRC 27 +#define SIGCOMP_INSTR_INPUT_BYTES 28 +#define SIGCOMP_INSTR_INPUT_BITS 29 +#define SIGCOMP_INSTR_INPUT_HUFFMAN 30 +#define SIGCOMP_INSTR_STATE_ACCESS 31 +#define SIGCOMP_INSTR_STATE_CREATE 32 +#define SIGCOMP_INSTR_STATE_FREE 33 +#define SIGCOMP_INSTR_OUTPUT 34 +#define SIGCOMP_INSTR_END_MESSAGE 35 + + +static gboolean print_level_1; +static gboolean print_level_2; +static gboolean print_level_3; +static gint show_instr_detail_level; + +/* Internal result code values of decompression failures */ +const value_string result_code_vals[] = { + { 0, "No decomprssion failure" }, + { 1, "Partial state length less than 6 or greater than 20 bytes long" }, + { 2, "No state match" }, + { 3, "state_begin + state_length > size of state" }, + { 4, "Operand_2 is Zero" }, + { 5, "Switch statement failed j >= n" }, + { 6, "Atempt to jump outside of UDVM memory" }, + { 7, "L in input-bits > 16" }, + { 8, "input_bit_order > 7" }, + { 9, "Instruction Decompression failure encountered" }, + {10, "Input huffman failed j > n" }, + {11, "Input bits requested beyond end of message" }, + {12, "more than four state creation requests are made before the END-MESSAGE instruction" }, + {13, "state_retention_priority is 65535" }, + {14, "Input bytes requested beond end of message" }, + {15, "Maximum number of UDVM cycles reached" }, + {16, "UDVM stack underflow" }, + { 255, "This branch isn't coded yet" }, + { 0, NULL } +}; + +static int decode_udvm_literal_operand(guint8 *buff,guint operand_address, guint16 *value); +static int dissect_udvm_reference_operand(guint8 *buff,guint operand_address, guint16 *value, guint *result_dest); +static int decode_udvm_multitype_operand(guint8 *buff,guint operand_address,guint16 *value); +static int decode_udvm_address_operand(guint8 *buff,guint operand_address, guint16 *value,guint current_address); +static int decomp_dispatch_get_bits(tvbuff_t *message_tvb,proto_tree *udvm_tree,guint8 bit_order, + guint8 *buff,guint16 *old_input_bit_order, guint16 *remaining_bits, + guint16 *input_bits, guint *input_address, guint16 length, guint16 *result_code,guint msg_end); + + +tvbuff_t* +decompress_sigcomp_message(tvbuff_t *bytecode_tvb, tvbuff_t *message_tvb, packet_info *pinfo, + proto_tree *udvm_tree, gint udvm_mem_dest, + gint print_flags, gint hf_id, + gint header_len, + gint byte_code_state_len, gint byte_code_id_len, + gint udvm_start_ip) +{ + tvbuff_t *decomp_tvb; + guint8 buff[UDVM_MEMORY_SIZE]; + char string[2],*strp; + guint8 *out_buff; /* Largest allowed size for a message is 65535 */ + guint32 i = 0; + guint16 n = 0; + guint16 m = 0; + guint16 x; + guint k = 0; + guint16 H; + guint16 oldH; + guint offset = 0; + guint result_dest; + guint code_length =0; + guint8 current_instruction; + guint current_address; + guint operand_address; + guint input_address; + guint16 output_address = 0; + guint next_operand_address; + guint8 octet; + guint8 msb; + guint8 lsb; + guint16 byte_copy_right; + guint16 byte_copy_left; + guint16 input_bit_order; + guint16 stack_location; + guint16 stack_fill; + guint16 result; + guint msg_end = tvb_reported_length_remaining(message_tvb, 0); + guint16 result_code = 0; + guint16 old_input_bit_order = 0; + guint16 remaining_bits = 0; + guint16 input_bits = 0; + guint8 bit_order = 0; + gboolean outside_huffman_boundaries = TRUE; + gboolean print_in_loop = FALSE; + guint16 instruction_address; + guint8 no_of_state_create = 0; + guint16 state_length_buff[5]; + guint16 state_address_buff[5]; + guint16 state_instruction_buff[5]; + guint16 state_minimum_access_length_buff[5]; + guint16 state_state_retention_priority_buff[5]; + guint32 used_udvm_cycles = 0; + guint cycles_per_bit; + guint maximum_UDVM_cycles; + guint8 *sha1buff; + unsigned char sha1_digest_buf[STATE_BUFFER_SIZE]; + sha1_context ctx; + + + /* UDVM operand variables */ + guint16 length; + guint16 at_address; + guint16 destination; + guint16 address; + guint16 value; + guint16 p_id_start; + guint16 p_id_length; + guint16 state_begin; + guint16 state_length; + guint16 state_address; + guint16 state_instruction; + guint16 operand_1; + guint16 operand_2; + guint16 value_1; + guint16 value_2; + guint16 at_address_1; + guint16 at_address_2; + guint16 at_address_3; + guint16 j; + guint16 bits_n; + guint16 lower_bound_n; + guint16 upper_bound_n; + guint16 uncompressed_n; + guint16 position; + guint16 ref_destination; /* could I have used $destination ? */ + guint16 multy_offset; + guint16 output_start; + guint16 output_length; + guint16 minimum_access_length; + guint16 state_retention_priority; + guint16 requested_feedback_location; + guint16 returned_parameters_location; + guint16 start_value; + + + /* Set print parameters */ + print_level_1 = FALSE; + print_level_2 = FALSE; + print_level_3 = FALSE; + show_instr_detail_level = 0; + + + + switch( print_flags ) { + case 0: + break; + + case 1: + print_level_1 = TRUE; + show_instr_detail_level = 1; + break; + case 2: + print_level_1 = TRUE; + print_level_2 = TRUE; + show_instr_detail_level = 1; + break; + case 3: + print_level_1 = TRUE; + print_level_2 = TRUE; + print_level_3 = TRUE; + show_instr_detail_level = 2; + break; + default: + print_level_1 = TRUE; + show_instr_detail_level = 1; + break; + } + + + + + + /* UDVM memory must be initialised to zero */ + memset(buff, 0, UDVM_MEMORY_SIZE); + /* Set initial UDVM data + * The first 32 bytes of UDVM memory are then initialized to special + * values as illustrated in Figure 5. + * + * 0 7 8 15 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | UDVM_memory_size | 0 - 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | cycles_per_bit | 2 - 3 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SigComp_version | 4 - 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | partial_state_ID_length | 6 - 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | state_length | 8 - 9 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * : reserved : 10 - 31 + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Figure 5: Initializing Useful Values in UDVM memory + */ + /* UDVM_memory_size */ + buff[0] = (UDVM_MEMORY_SIZE >> 8) & 0x00FF; + buff[1] = UDVM_MEMORY_SIZE & 0x00FF; + /* cycles_per_bit */ + buff[2] = 0; + buff[3] = 16; + /* SigComp_version */ + buff[4] = 0; + buff[5] = 1; + /* partial_state_ID_length */ + buff[6] = (byte_code_id_len >> 8) & 0x00FF; + buff[7] = byte_code_id_len & 0x00FF; + /* state_length */ + buff[8] = (byte_code_state_len >> 8) & 0x00FF; + buff[9] = byte_code_state_len & 0x00FF; + + code_length = tvb_reported_length_remaining(bytecode_tvb, 0); + + cycles_per_bit = buff[2] << 8; + cycles_per_bit = cycles_per_bit | buff[3]; + /* + * maximum_UDVM_cycles = (8 * n + 1000) * cycles_per_bit + */ + maximum_UDVM_cycles = (( 8 * (header_len + msg_end) ) + 1000) * cycles_per_bit; + + proto_tree_add_text(udvm_tree, bytecode_tvb, offset, 1,"maximum_UDVM_cycles(%u) = (( 8 * msg_end(%u) ) + 1000) * cycles_per_bit(%u)",maximum_UDVM_cycles,msg_end,cycles_per_bit); + proto_tree_add_text(udvm_tree, bytecode_tvb, offset, 1,"Message Length: %u,Byte code length: %u, Maximum UDVM cycles: %u",msg_end,code_length,maximum_UDVM_cycles); + + /* Load bytecode into UDVM starting at "udvm_mem_dest" */ + i = udvm_mem_dest; + if ( print_level_3 ) + proto_tree_add_text(udvm_tree, bytecode_tvb, offset, 1,"Load bytecode into UDVM starting at %u",i); + while ( code_length > offset ) { + buff[i] = tvb_get_guint8(bytecode_tvb, offset); + if ( print_level_3 ) + proto_tree_add_text(udvm_tree, bytecode_tvb, offset, 1, + " Addr: %u Instruction code(0x%0x) ", i, buff[i]); + + i++; + offset++; + + } + /* Largest allowed size for a message is 65535 */ + out_buff = g_malloc(65535); + /* Start executing code */ + current_address = udvm_start_ip; + input_address = 0; + operand_address = 0; + + proto_tree_add_text(udvm_tree, bytecode_tvb, offset, 1,"UDVM EXECUTION STARTED at Address: %u Message size %u", + current_address, msg_end); + +execute_next_instruction: + + if ( used_udvm_cycles > maximum_UDVM_cycles ){ + result_code = 15; + goto decompression_failure; + } + current_instruction = buff[current_address]; + + switch ( current_instruction ) { + case SIGCOMP_INSTR_DECOMPRESSION_FAILURE: + used_udvm_cycles++; + if ( result_code == 0 ) + result_code = 9; + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## DECOMPRESSION-FAILURE(0)", + current_address); + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Ethereal UDVM diagnostic: %s.", + val_to_str(result_code, result_code_vals,"Unknown (%u)")); + if ( output_address > 0 ){ + /* At least something got decompressed, show it */ + decomp_tvb = tvb_new_real_data(out_buff,output_address,output_address); + /* Arrange that the allocated packet data copy be freed when the + * tvbuff is freed. + */ + tvb_set_free_cb( decomp_tvb, g_free ); + /* Add the tvbuff to the list of tvbuffs to which the tvbuff we + * were handed refers, so it'll get cleaned up when that tvbuff + * is cleaned up. + */ + tvb_set_child_real_data_tvbuff(message_tvb,decomp_tvb); + add_new_data_source(pinfo, decomp_tvb, "Decompressed SigComp message(Incomplete)"); + proto_tree_add_text(udvm_tree, decomp_tvb, 0, -1,"SigComp message Decompression failure"); + return decomp_tvb; + } + g_free(out_buff); + return NULL; + break; + + case SIGCOMP_INSTR_AND: /* 1 AND ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## AND(1) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## AND (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 & operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_OR: /* 2 OR ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## OR(2) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## OR (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 | operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_NOT: /* 3 NOT ($operand_1) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## NOT(3) ($operand_1)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## NOT (operand_1=%u)", + current_address, operand_1); + } + /* execute the instruction */ + result = operand_1 ^ 0xffff; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_LSHIFT: /* 4 LSHIFT ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## LSHIFT(4) ($operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## LSHIFT (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 << operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + + break; + case SIGCOMP_INSTR_RSHIFT: /* 5 RSHIFT ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## RSHIFT(5) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## RSHIFT (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 >> operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_ADD: /* 6 ADD ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## ADD(6) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## ADD (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 + operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + + case SIGCOMP_INSTR_SUBTRACT: /* 7 SUBTRACT ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SUBTRACT(7) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SUBTRACT (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* execute the instruction */ + result = operand_1 - operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_MULTIPLY: /* 8 MULTIPLY ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ##MULTIPLY(8) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## MULTIPLY (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* + * execute the instruction + * MULTIPLY (m, n) := m * n (modulo 2^16) + */ + if ( operand_2 == 0){ + result_code = 4; + goto decompression_failure; + } + result = operand_1 * operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_DIVIDE: /* 9 DIVIDE ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## DIVIDE(9) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## DIVIDE (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* + * execute the instruction + * DIVIDE (m, n) := floor(m / n) + * Decompression failure occurs if a DIVIDE or REMAINDER instruction + * encounters an operand_2 that is zero. + */ + if ( operand_2 == 0){ + result_code = 4; + goto decompression_failure; + } + result = operand_1 / operand_2; + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_REMAINDER: /* 10 REMAINDER ($operand_1, %operand_2) */ + used_udvm_cycles++; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## REMAINDER(10) (operand_1, operand_2)", + current_address); + } + /* $operand_1*/ + operand_address = current_address + 1; + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &operand_1, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_1 %u", + operand_address, operand_1); + } + operand_address = next_operand_address; + /* %operand_2*/ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &operand_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u operand_2 %u", + operand_address, operand_2); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## REMAINDER (operand_1=%u, operand_2=%u)", + current_address, operand_1, operand_2); + } + /* + * execute the instruction + * REMAINDER (m, n) := m - n * floor(m / n) + * Decompression failure occurs if a DIVIDE or REMAINDER instruction + * encounters an operand_2 that is zero. + */ + if ( operand_2 == 0){ + result_code = 4; + goto decompression_failure; + } + result = operand_1 - operand_2 * (operand_1 / operand_2); + lsb = result & 0xff; + msb = result >> 8; + buff[result_dest] = msb; + buff[result_dest+1] = lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading result %u at %u", + result, result_dest); + } + current_address = next_operand_address; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_SORT_ASCENDING: /* 11 SORT-ASCENDING (%start, %n, %k) */ + /* + * used_udvm_cycles = 1 + k * (ceiling(log2(k)) + n) + */ + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SORT-ASCENDING(11) (start, n, k))", + current_address); + } + operand_address = current_address + 1; + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Execution of this instruction is NOT implemented"); + /* + * used_udvm_cycles = 1 + k * (ceiling(log2(k)) + n) + */ + break; + + case SIGCOMP_INSTR_SORT_DESCENDING: /* 12 SORT-DESCENDING (%start, %n, %k) */ + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SORT-DESCENDING(12) (start, n, k))", + current_address); + } + operand_address = current_address + 1; + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Execution of this instruction is NOT implemented"); + /* + * used_udvm_cycles = 1 + k * (ceiling(log2(k)) + n) + */ + break; + case SIGCOMP_INSTR_SHA_1: /* 13 SHA-1 (%position, %length, %destination) */ + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SHA-1(13) (position, length, destination)", + current_address); + } + operand_address = current_address + 1; + /* %position */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &position); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u position %u", + operand_address, position); + } + operand_address = next_operand_address; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + /* $destination */ + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &ref_destination, &result_dest); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u $destination %u", + operand_address, ref_destination); + } + current_address = next_operand_address; + used_udvm_cycles = used_udvm_cycles + 1 + length; + + n = 0; + k = position; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, 0, -1, + "byte_copy_right = %u", byte_copy_right); + } + + sha1_starts( &ctx ); + + while (n= UDVM_MEMORY_SIZE) + THROW(ReportedBoundsError); + sha1_update( &ctx, &buff[k], handle_now ); + + k = ( k + handle_now ) & 0xffff; + n = ( n + handle_now ) & 0xffff; + + if ( k >= byte_copy_right ) { + k = byte_copy_left; + } + } + + sha1_finish( &ctx, sha1_digest_buf ); + + k = ref_destination; + + for ( n=0; n< STATE_BUFFER_SIZE; n++ ) { + + buff[k] = sha1_digest_buf[n]; + + k = ( k + 1 ) & 0xffff; + n++; + + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + } + + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, 0, -1, + "Calculated SHA-1: %s", + bytes_to_str(sha1_digest_buf, STATE_BUFFER_SIZE)); + } + + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_LOAD: /* 14 LOAD (%address, %value) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## LOAD(14) (%%address, %%value)", + current_address); + } + operand_address = current_address + 1; + /* %address */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Address %u", + operand_address, address); + } + operand_address = next_operand_address; + /* %value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value); + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## LOAD (%%address=%u, %%value=%u)", + current_address, address, value); + } + lsb = value & 0xff; + msb = value >> 8; + + buff[address] = msb; + buff[address + 1] = lsb; + + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, value); + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Loading bytes at %u Value %u 0x%x", + address, value, value); + } + used_udvm_cycles++; + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_MULTILOAD: /* 15 MULTILOAD (%address, #n, %value_0, ..., %value_n-1) */ + /* RFC 3320: + * The MULTILOAD instruction sets a contiguous block of 2-byte words in + * the UDVM memory to specified values. + * Hmm what if the value to load only takes one byte ? Chose to always load two bytes. + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## MULTILOAD(15) (%%address, #n, value_0, ..., value_n-1)", + current_address); + } + operand_address = current_address + 1; + /* %address */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Address %u", + operand_address, address); + } + operand_address = next_operand_address; + + /* #n */ + next_operand_address = decode_udvm_literal_operand(buff,operand_address, &n); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u n %u", + operand_address, n); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## MULTILOAD (%%address=%u, #n=%u, value_0, ..., value_%d)", + current_address, address, n, n-1); + } + operand_address = next_operand_address; + used_udvm_cycles = used_udvm_cycles + 1 + n; + while ( n > 0) { + n = n - 1; + /* %value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value); + lsb = value & 0xff; + msb = value >> 8; + + buff[address] = msb; + buff[address + 1] = lsb; + /* debug + */ + length = next_operand_address - operand_address; + + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, "Addr: %u Value %5u - Loading bytes at %5u Value %5u 0x%x", + operand_address, value, address, value, value); + } + address = address + 2; + operand_address = next_operand_address; + } + current_address = next_operand_address; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_PUSH: /* 16 PUSH (%value) */ + if (show_instr_detail_level == 2){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## PUSH(16) (value)", + current_address); + } + operand_address = current_address + 1; + /* %value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value); + if (show_instr_detail_level == 2){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, value); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## PUSH (value=%u)", + current_address, value); + } + current_address = next_operand_address; + + /* Push the value address onto the stack */ + stack_location = (buff[70] << 8) | buff[71]; + stack_fill = (buff[stack_location] << 8) + | buff[(stack_location+1) & 0xFFFF]; + address = (stack_location + stack_fill * 2 + 2) & 0xFFFF; + + buff[address] = (value >> 8) & 0x00FF; + buff[(address+1) & 0xFFFF] = value & 0x00FF; + + stack_fill = (stack_fill + 1) & 0xFFFF; + buff[stack_location] = (stack_fill >> 8) & 0x00FF; + buff[(stack_location+1) & 0xFFFF] = stack_fill & 0x00FF; + + used_udvm_cycles++; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_POP: /* 17 POP (%address) */ + if (show_instr_detail_level == 2){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## POP(16) (value)", + current_address); + } + operand_address = current_address + 1; + /* %value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &destination); + if (show_instr_detail_level == 2){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, destination); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## POP (address=%u)", + current_address, destination); + } + current_address = next_operand_address; + + /* Pop value from the top of the stack */ + stack_location = (buff[70] << 8) | buff[71]; + stack_fill = (buff[stack_location] << 8) + | buff[(stack_location+1) & 0xFFFF]; + if (stack_fill == 0) + { + result_code = 16; + goto decompression_failure; + } + + stack_fill = (stack_fill - 1) & 0xFFFF; + buff[stack_location] = (stack_fill >> 8) & 0x00FF; + buff[(stack_location+1) & 0xFFFF] = stack_fill & 0x00FF; + + address = (stack_location + stack_fill * 2 + 2) & 0xFFFF; + value = (buff[address] << 8) + | buff[(address+1) & 0xFFFF]; + + /* ... and store the popped value. */ + buff[destination] = (value >> 8) & 0x00FF; + buff[(destination+1) & 0xFFFF] = value & 0x00FF; + + used_udvm_cycles++; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_COPY: /* 18 COPY (%position, %length, %destination) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY(18) (position, length, destination)", + current_address); + } + operand_address = current_address + 1; + /* %position */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &position); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u position %u", + operand_address, position); + } + operand_address = next_operand_address; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + /* %destination */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &destination); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Destination %u", + operand_address, destination); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY (position=%u, length=%u, destination=%u)", + current_address, position, length, destination); + } + current_address = next_operand_address; + /* + * 8.4. Byte copying + * : + * The string of bytes is copied in ascending order of memory address, + * respecting the bounds set by byte_copy_left and byte_copy_right. + * More precisely, if a byte is copied from/to Address m then the next + * byte is copied from/to Address n where n is calculated as follows: + * + * Set k := m + 1 (modulo 2^16) + * If k = byte_copy_right then set n := byte_copy_left, else set n := k + * + */ + + n = 0; + k = destination; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_right = %u", byte_copy_right); + } + + while ( n < length ){ + buff[k] = buff[position]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Copying value: %u (0x%x) to Addr: %u", + buff[position], buff[position], k); + } + position = ( position + 1 ) & 0xffff; + k = ( k + 1 ) & 0xffff; + n++; + + /* + * Check for circular buffer wrapping after the positions are + * incremented. If either started at BCR then they should continue + * to increment beyond BCR. + */ + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + if ( position == byte_copy_right ){ + position = byte_copy_left; + } + } + used_udvm_cycles = used_udvm_cycles + 1 + length; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_COPY_LITERAL: /* 19 COPY-LITERAL (%position, %length, $destination) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY-LITERAL(19) (position, length, $destination)", + current_address); + } + operand_address = current_address + 1; + /* %position */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &position); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u position %u", + operand_address, position); + } + operand_address = next_operand_address; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + + /* $destination */ + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &ref_destination, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u destination %u", + operand_address, ref_destination); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY-LITERAL (position=%u, length=%u, $destination=%u)", + current_address, position, length, destination); + } + current_address = next_operand_address; + + + /* + * 8.4. Byte copying + * : + * The string of bytes is copied in ascending order of memory address, + * respecting the bounds set by byte_copy_left and byte_copy_right. + * More precisely, if a byte is copied from/to Address m then the next + * byte is copied from/to Address n where n is calculated as follows: + * + * Set k := m + 1 (modulo 2^16) + * If k = byte_copy_right then set n := byte_copy_left, else set n := k + * + */ + + n = 0; + k = ref_destination; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_right = %u", byte_copy_right); + } + while ( n < length ){ + + buff[k] = buff[position]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Copying value: %u (0x%x) to Addr: %u", + buff[position], buff[position], k); + } + position = ( position + 1 ) & 0xffff; + k = ( k + 1 ) & 0xffff; + n++; + + /* + * Check for circular buffer wrapping after the positions are + * incremented. It is important that k cannot be left set + * to BCR. Also, if either started at BCR then they should continue + * to increment beyond BCR. + */ + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + if ( position == byte_copy_right ){ + position = byte_copy_left; + } + } + buff[result_dest] = k >> 8; + buff[result_dest + 1] = k & 0x00ff; + + used_udvm_cycles = used_udvm_cycles + 1 + length; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_COPY_OFFSET: /* 20 COPY-OFFSET (%offset, %length, $destination) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY-OFFSET(20) (offset, length, $destination)", + current_address); + } + operand_address = current_address + 1; + /* %offset */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &multy_offset); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u offset %u", + operand_address, multy_offset); + } + operand_address = next_operand_address; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + + /* $destination */ + next_operand_address = dissect_udvm_reference_operand(buff, operand_address, &ref_destination, &result_dest); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u $destination %u", + operand_address, ref_destination); + } + + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COPY-OFFSET (offset=%u, length=%u, $destination=%u)", + current_address, multy_offset, length, result_dest); + } + current_address = next_operand_address; + + /* Execute the instruction: + * To derive the value of the position operand, starting at the memory + * address specified by destination, the UDVM counts backwards a total + * of offset memory addresses. + * + * If the memory address specified in byte_copy_left is reached, the + * next memory address is taken to be (byte_copy_right - 1) modulo 2^16. + */ + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + + /* + * In order to work out the position, simple arithmetic is tricky + * to apply because there some nasty corner cases. A simple loop + * is inefficient but the logic is simple. + * + * FUTURE: This could be optimised. + */ + for (position = ref_destination, i = 0; i < multy_offset; i++) + { + if ( position == byte_copy_left ) + { + position = (byte_copy_right - 1) & 0xffff; + } + else + { + position = (position - 1) & 0xffff; + } + } + + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_left = %u byte_copy_right = %u position= %u", + byte_copy_left, byte_copy_right, position); + } + /* The COPY-OFFSET instruction then behaves as a COPY-LITERAL + * instruction, taking the value of the position operand to be the last + * memory address reached in the above step. + */ + + /* + * 8.4. Byte copying + * : + * The string of bytes is copied in ascending order of memory address, + * respecting the bounds set by byte_copy_left and byte_copy_right. + * More precisely, if a byte is copied from/to Address m then the next + * byte is copied from/to Address n where n is calculated as follows: + * + * Set k := m + 1 (modulo 2^16) + * If k = byte_copy_right then set n := byte_copy_left, else set n := k + * + */ + + n = 0; + k = ref_destination; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_left = %u byte_copy_right = %u", byte_copy_left, byte_copy_right); + } + while ( n < length ){ + buff[k] = buff[position]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Copying value: %5u (0x%x) from Addr: %u to Addr: %u", + buff[position], buff[position],(position), k); + } + n++; + k = ( k + 1 ) & 0xffff; + position = ( position + 1 ) & 0xffff; + + /* + * Check for circular buffer wrapping after the positions are + * incremented. It is important that k cannot be left set + * to BCR. Also, if either started at BCR then they should continue + * to increment beyond BCR. + */ + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + if ( position == byte_copy_right ){ + position = byte_copy_left; + } + } + buff[result_dest] = k >> 8; + buff[result_dest + 1] = k & 0x00ff; + used_udvm_cycles = used_udvm_cycles + 1 + length; + goto execute_next_instruction; + + break; + case SIGCOMP_INSTR_MEMSET: /* 21 MEMSET (%address, %length, %start_value, %offset) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## MEMSET(21) (address, length, start_value, offset)", + current_address); + } + operand_address = current_address + 1; + + /* %address */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Address %u", + operand_address, address); + } + operand_address = next_operand_address; + + /* %length, */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + /* %start_value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &start_value); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u start_value %u", + operand_address, start_value); + } + operand_address = next_operand_address; + + /* %offset */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &multy_offset); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u offset %u", + operand_address, multy_offset); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## MEMSET (address=%u, length=%u, start_value=%u, offset=%u)", + current_address, address, length, start_value, multy_offset); + } + current_address = next_operand_address; + /* exetute the instruction + * The sequence of values used by the MEMSET instruction is specified by + * the following formula: + * + * Seq[n] := (start_value + n * offset) modulo 256 + */ + n = 0; + k = address; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_left = %u byte_copy_right = %u", byte_copy_left, byte_copy_right); + } + while ( n < length ){ + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + buff[k] = (start_value + ( n * multy_offset)) & 0xff; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Storing value: %u (0x%x) at Addr: %u", + buff[k], buff[k], k); + } + k = ( k + 1 ) & 0xffff; + n++; + }/* end while */ + used_udvm_cycles = used_udvm_cycles + 1 + length; + goto execute_next_instruction; + break; + + + case SIGCOMP_INSTR_JUMP: /* 22 JUMP (@address) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## JUMP(22) (@address)", + current_address); + } + operand_address = current_address + 1; + /* @address */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_address_operand(buff,operand_address, &at_address, current_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## JUMP (@address=%u)", + current_address, at_address); + } + current_address = at_address; + used_udvm_cycles++; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_COMPARE: /* 23 */ + /* COMPARE (%value_1, %value_2, @address_1, @address_2, @address_3) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COMPARE(23) (value_1, value_2, @address_1, @address_2, @address_3)", + current_address); + } + operand_address = current_address + 1; + + /* %value_1 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value_1); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, value_1); + } + operand_address = next_operand_address; + + /* %value_2 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value_2); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, value_2); + } + operand_address = next_operand_address; + + /* @address_1 */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address_1); + at_address_1 = ( current_address + at_address_1) & 0xffff; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address_1); + } + operand_address = next_operand_address; + + + /* @address_2 */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address_2); + at_address_2 = ( current_address + at_address_2) & 0xffff; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address_2); + } + operand_address = next_operand_address; + + /* @address_3 */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address_3); + at_address_3 = ( current_address + at_address_3) & 0xffff; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address_3); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## COMPARE (value_1=%u, value_2=%u, @address_1=%u, @address_2=%u, @address_3=%u)", + current_address, value_1, value_2, at_address_1, at_address_2, at_address_3); + } + /* execute the instruction + * If value_1 < value_2 then the UDVM continues instruction execution at + * the memory address specified by address 1. If value_1 = value_2 then + * it jumps to the address specified by address_2. If value_1 > value_2 + * then it jumps to the address specified by address_3. + */ + if ( value_1 < value_2 ) + current_address = at_address_1; + if ( value_1 == value_2 ) + current_address = at_address_2; + if ( value_1 > value_2 ) + current_address = at_address_3; + used_udvm_cycles++; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_CALL: /* 24 CALL (@address) (PUSH addr )*/ + if (show_instr_detail_level == 2){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## CALL(24) (@address) (PUSH addr )", + current_address); + } + operand_address = current_address + 1; + /* @address */ + next_operand_address = decode_udvm_address_operand(buff,operand_address, &at_address, current_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## CALL (@address=%u)", + current_address, at_address); + } + current_address = next_operand_address; + + /* Push the current address onto the stack */ + stack_location = (buff[70] << 8) | buff[71]; + stack_fill = (buff[stack_location] << 8) + | buff[(stack_location+1) & 0xFFFF]; + address = (stack_location + stack_fill * 2 + 2) & 0xFFFF; + buff[address] = (current_address >> 8) & 0x00FF; + buff[(address+1) & 0xFFFF] = current_address & 0x00FF; + + stack_fill = (stack_fill + 1) & 0xFFFF; + buff[stack_location] = (stack_fill >> 8) & 0x00FF; + buff[(stack_location+1) & 0xFFFF] = stack_fill & 0x00FF; + + /* ... and jump to the destination address */ + current_address = at_address; + + used_udvm_cycles++; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_RETURN: /* 25 POP and return */ + if (print_level_1 || show_instr_detail_level == 1){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## POP(25) and return", + current_address); + } + + /* Pop value from the top of the stack */ + stack_location = (buff[70] << 8) | buff[71]; + stack_fill = (buff[stack_location] << 8) + | buff[(stack_location+1) & 0xFFFF]; + if (stack_fill == 0) + { + result_code = 16; + goto decompression_failure; + } + + stack_fill = (stack_fill - 1) & 0xFFFF; + buff[stack_location] = (stack_fill >> 8) & 0x00FF; + buff[(stack_location+1) & 0xFFFF] = stack_fill & 0x00FF; + + address = (stack_location + stack_fill * 2 + 2) & 0xFFFF; + at_address = (buff[address] << 8) + | buff[(address+1) & 0xFFFF]; + + /* ... and set the PC to the popped value */ + current_address = at_address; + + used_udvm_cycles++; + goto execute_next_instruction; + + break; + + case SIGCOMP_INSTR_SWITCH: /* 26 SWITCH (#n, %j, @address_0, @address_1, ... , @address_n-1) */ + /* + * When a SWITCH instruction is encountered the UDVM reads the value of + * j. It then continues instruction execution at the address specified + * by address j. + * + * Decompression failure occurs if j specifies a value of n or more, or + * if the address lies beyond the overall UDVM memory size. + */ + instruction_address = current_address; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## SWITCH (#n, j, @address_0, @address_1, ... , @address_n-1))", + current_address); + } + operand_address = current_address + 1; + /* #n + * Number of addresses in the instruction + */ + next_operand_address = decode_udvm_literal_operand(buff,operand_address, &n); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u n %u", + operand_address, n); + } + operand_address = next_operand_address; + /* %j */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &j); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u j %u", + operand_address, j); + } + operand_address = next_operand_address; + m = 0; + while ( m < n ){ + /* @address_n-1 */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address_1); + at_address_1 = ( instruction_address + at_address_1) & 0xffff; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address_1); + } + if ( j == m ){ + current_address = at_address_1; + } + operand_address = next_operand_address; + m++; + } + /* Check decompression failure */ + if ( ( j == n ) || ( j > n )){ + result_code = 5; + goto decompression_failure; + } + if ( current_address > UDVM_MEMORY_SIZE ){ + result_code = 6; + goto decompression_failure; + } + used_udvm_cycles = used_udvm_cycles + 1 + n; + + goto execute_next_instruction; + + break; + case SIGCOMP_INSTR_CRC: /* 27 CRC (%value, %position, %length, @address) */ + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## CRC (value, position, length, @address)", + current_address); + } + + operand_address = current_address + 1; + + /* %value */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &value); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Value %u", + operand_address, value); + } + operand_address = next_operand_address; + + /* %position */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &position); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u position %u", + operand_address, position); + } + operand_address = next_operand_address; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + /* @address */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address); + at_address = ( current_address + at_address) & 0xffff; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + used_udvm_cycles = used_udvm_cycles + 1 + length; + + n = 0; + k = position; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + result = 0; + + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, 0, -1, + "byte_copy_right = %u", byte_copy_right); + } + + while (n= UDVM_MEMORY_SIZE) + THROW(ReportedBoundsError); + result = crc16_ccitt_seed(&buff[k], handle_now, result ^ 0xffff); + + k = ( k + handle_now ) & 0xffff; + n = ( n + handle_now ) & 0xffff; + + if ( k >= byte_copy_right ) { + k = byte_copy_left; + } + } + + result = result ^ 0xffff; + + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, "Calculated CRC %u", result); + } + if (result != value){ + current_address = at_address; + } + else { + current_address = next_operand_address; + } + goto execute_next_instruction; + break; + + + case SIGCOMP_INSTR_INPUT_BYTES: /* 28 INPUT-BYTES (%length, %destination, @address) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-BYTES(28) length, destination, @address)", + current_address); + } + operand_address = current_address + 1; + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Length %u", + operand_address, length); + } + operand_address = next_operand_address; + + /* %destination */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &destination); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Destination %u", + operand_address, destination); + } + operand_address = next_operand_address; + + /* @address */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &at_address); + at_address = ( current_address + at_address) & 0xffff; + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-BYTES length=%u, destination=%u, @address=%u)", + current_address, length, destination, at_address); + } + /* execute the instruction TODO insert checks + * RFC 3320 : + * + * 0 7 8 15 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | byte_copy_left | 64 - 65 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | byte_copy_right | 66 - 67 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | input_bit_order | 68 - 69 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | stack_location | 70 - 71 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Figure 7: Memory addresses of the UDVM registers + * : + * 8.4. Byte copying + * : + * The string of bytes is copied in ascending order of memory address, + * respecting the bounds set by byte_copy_left and byte_copy_right. + * More precisely, if a byte is copied from/to Address m then the next + * byte is copied from/to Address n where n is calculated as follows: + * + * Set k := m + 1 (modulo 2^16) + * If k = byte_copy_right then set n := byte_copy_left, else set n := k + * + */ + + n = 0; + k = destination; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_right = %u", byte_copy_right); + } + /* clear out remaining bits if any */ + remaining_bits = 0; + input_bits=0; + /* operand_address used as dummy */ + while ( n < length ){ + if (input_address > ( msg_end - 1)){ + current_address = at_address; + result_code = 14; + goto execute_next_instruction; + } + + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + octet = tvb_get_guint8(message_tvb, input_address); + buff[k] = octet; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Loading value: %u (0x%x) at Addr: %u", octet, octet, k); + } + input_address++; + /* + * If the instruction requests data that lies beyond the end of the + * SigComp message, no data is returned. Instead the UDVM moves program + * execution to the address specified by the address operand. + */ + + + k = ( k + 1 ) & 0xffff; + n++; + } + used_udvm_cycles = used_udvm_cycles + 1 + length; + current_address = next_operand_address; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_INPUT_BITS:/* 29 INPUT-BITS (%length, %destination, @address) */ + /* + * The length operand indicates the requested number of bits. + * Decompression failure occurs if this operand does not lie between 0 + * and 16 inclusive. + * + * The destination operand specifies the memory address to which the + * compressed data should be copied. Note that the requested bits are + * interpreted as a 2-byte integer ranging from 0 to 2^length - 1, as + * explained in Section 8.2. + * + * If the instruction requests data that lies beyond the end of the + * SigComp message, no data is returned. Instead the UDVM moves program + * execution to the address specified by the address operand. + */ + + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-BITS(29) (length, destination, @address)", + current_address); + } + operand_address = current_address + 1; + + /* %length */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u length %u", + operand_address, length); + } + operand_address = next_operand_address; + /* %destination */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &destination); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Destination %u", + operand_address, destination); + } + operand_address = next_operand_address; + + /* @address */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_address_operand(buff,operand_address, &at_address, current_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-BITS length=%u, destination=%u, @address=%u)", + current_address, length, destination, at_address); + } + current_address = next_operand_address; + + /* + * Execute actual instr. + * The input_bit_order register contains the following three flags: + * + * 0 7 8 15 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | reserved |F|H|P| 68 - 69 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + input_bit_order = buff[68] << 8; + input_bit_order = input_bit_order | buff[69]; + /* + * If the instruction requests data that lies beyond the end of the + * SigComp message, no data is returned. Instead the UDVM moves program + * execution to the address specified by the address operand. + */ + + if ( length > 16 ){ + result_code = 7; + goto decompression_failure; + } + if ( input_bit_order > 7 ){ + result_code = 8; + goto decompression_failure; + } + + /* + * Transfer F bit to bit_order to tell decomp dispatcher which bit order to use + */ + bit_order = ( input_bit_order & 0x0004 ) >> 2; + value = decomp_dispatch_get_bits( message_tvb, udvm_tree, bit_order, + buff, &old_input_bit_order, &remaining_bits, + &input_bits, &input_address, length, &result_code, msg_end); + if ( result_code == 11 ){ + used_udvm_cycles = used_udvm_cycles + 1; + current_address = at_address; + goto execute_next_instruction; + } + msb = value >> 8; + lsb = value & 0x00ff; + buff[destination] = msb; + buff[destination + 1]=lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Loading value: %u (0x%x) at Addr: %u, remaining_bits: %u", value, value, destination, remaining_bits); + } + + used_udvm_cycles = used_udvm_cycles + 1; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_INPUT_HUFFMAN: /* 30 */ + /* + * INPUT-HUFFMAN (%destination, @address, #n, %bits_1, %lower_bound_1, + * %upper_bound_1, %uncompressed_1, ... , %bits_n, %lower_bound_n, + * %upper_bound_n, %uncompressed_n) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-HUFFMAN (destination, @address, #n, bits_1, lower_bound_1,upper_bound_1, uncompressed_1, ... , bits_n, lower_bound_n,upper_bound_n, uncompressed_n)", + current_address); + } + operand_address = current_address + 1; + + /* %destination */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &destination); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u Destination %u", + operand_address, destination); + } + operand_address = next_operand_address; + + /* @address */ + /* operand_value = (memory_address_of_instruction + D) modulo 2^16 */ + next_operand_address = decode_udvm_address_operand(buff,operand_address, &at_address, current_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u @Address %u", + operand_address, at_address); + } + operand_address = next_operand_address; + + /* #n */ + next_operand_address = decode_udvm_literal_operand(buff,operand_address, &n); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u n %u", + operand_address, n); + } + operand_address = next_operand_address; + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## INPUT-HUFFMAN (destination=%u, @address=%u, #n=%u, bits_1, lower_1,upper_1, unc_1, ... , bits_%d, lower_%d,upper_%d, unc_%d)", + current_address, destination, at_address, n, n, n, n, n); + } + + used_udvm_cycles = used_udvm_cycles + 1 + n; + + /* + * Note that if n = 0 then the INPUT-HUFFMAN instruction is ignored and + * program execution resumes at the following instruction. + * Decompression failure occurs if (bits_1 + ... + bits_n) > 16. + * + * In all other cases, the behavior of the INPUT-HUFFMAN instruction is + * defined below: + * + * 1. Set j := 1 and set H := 0. + * + * 2. Request bits_j compressed bits. Interpret the returned bits as an + * integer k from 0 to 2^bits_j - 1, as explained in Section 8.2. + * + * 3. Set H := H * 2^bits_j + k. + * + * 4. If data is requested that lies beyond the end of the SigComp + * message, terminate the INPUT-HUFFMAN instruction and move program + * execution to the memory address specified by the address operand. + * + * 5. If (H < lower_bound_j) or (H > upper_bound_j) then set j := j + 1. + * Then go back to Step 2, unless j > n in which case decompression + * failure occurs. + * + * 6. Copy (H + uncompressed_j - lower_bound_j) modulo 2^16 to the + * memory address specified by the destination operand. + * + */ + /* + * The input_bit_order register contains the following three flags: + * + * 0 7 8 15 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | reserved |F|H|P| 68 - 69 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Transfer H bit to bit_order to tell decomp dispatcher which bit order to use + */ + input_bit_order = buff[68] << 8; + input_bit_order = input_bit_order | buff[69]; + bit_order = ( input_bit_order & 0x0002 ) >> 1; + + j = 1; + H = 0; + m = n; + outside_huffman_boundaries = TRUE; + print_in_loop = print_level_3; + while ( m > 0 ){ + /* %bits_n */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &bits_n); + if (print_in_loop ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u bits_n %u", + operand_address, bits_n); + } + operand_address = next_operand_address; + + /* %lower_bound_n */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &lower_bound_n); + if (print_in_loop ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u lower_bound_n %u", + operand_address, lower_bound_n); + } + operand_address = next_operand_address; + /* %upper_bound_n */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &upper_bound_n); + if (print_in_loop ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u upper_bound_n %u", + operand_address, upper_bound_n); + } + operand_address = next_operand_address; + /* %uncompressed_n */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &uncompressed_n); + if (print_in_loop ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u uncompressed_n %u", + operand_address, uncompressed_n); + } + operand_address = next_operand_address; + /* execute instruction */ + if ( outside_huffman_boundaries ) { + /* + * 2. Request bits_j compressed bits. Interpret the returned bits as an + * integer k from 0 to 2^bits_j - 1, as explained in Section 8.2. + */ + k = decomp_dispatch_get_bits( message_tvb, udvm_tree, bit_order, + buff, &old_input_bit_order, &remaining_bits, + &input_bits, &input_address, bits_n, &result_code, msg_end); + if ( result_code == 11 ){ + /* + * 4. If data is requested that lies beyond the end of the SigComp + * message, terminate the INPUT-HUFFMAN instruction and move program + * execution to the memory address specified by the address operand. + */ + current_address = at_address; + goto execute_next_instruction; + } + + /* + * 3. Set H := H * 2^bits_j + k. + * [In practice is a shift+OR operation.] + */ + oldH = H; + H = (H << bits_n) | k; + if (print_level_3 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," Set H(%u) := H(%u) * 2^bits_j(%u) + k(%u)", + H ,oldH, 1< upper_bound_j) then set j := j + 1. + * Then go back to Step 2, unless j > n in which case decompression + * failure occurs. + */ + if ((H < lower_bound_n) || (H > upper_bound_n)){ + outside_huffman_boundaries = TRUE; + }else{ + outside_huffman_boundaries = FALSE; + print_in_loop = FALSE; + /* + * 6. Copy (H + uncompressed_j - lower_bound_j) modulo 2^16 to the + * memory address specified by the destination operand. + */ + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + " H(%u) = H(%u) + uncompressed_n(%u) - lower_bound_n(%u)", + (H + uncompressed_n - lower_bound_n ),H, uncompressed_n, lower_bound_n); + } + H = H + uncompressed_n - lower_bound_n; + msb = H >> 8; + lsb = H & 0x00ff; + buff[destination] = msb; + buff[destination + 1]=lsb; + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " Loading H: %u (0x%x) at Addr: %u,j = %u remaining_bits: %u", + H, H, destination,( n - m + 1 ), remaining_bits); + } + + } + + + } + m = m - 1; + } + if ( outside_huffman_boundaries ) { + result_code = 10; + goto decompression_failure; + } + + current_address = next_operand_address; + goto execute_next_instruction; + break; + + case SIGCOMP_INSTR_STATE_ACCESS: /* 31 */ + /* STATE-ACCESS (%partial_identifier_start, %partial_identifier_length, + * %state_begin, %state_length, %state_address, %state_instruction) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-ACCESS(31) (partial_identifier_start, partial_identifier_length,state_begin, state_length, state_address, state_instruction)", + current_address); + } + operand_address = current_address + 1; + + /* + * %partial_identifier_start + */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &p_id_start); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u partial_identifier_start %u", + operand_address, p_id_start); + } + operand_address = next_operand_address; + + /* + * %partial_identifier_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &p_id_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u partial_identifier_length %u", + operand_address, p_id_length); + } + /* + * %state_begin + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_begin); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_begin %u", + operand_address, state_begin); + } + /* + * %state_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_length %u", + operand_address, state_length); + } + /* + * %state_address + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_address %u", + operand_address, state_address); + } + /* + * %state_instruction + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_instruction); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_instruction %u", + operand_address, state_instruction); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-ACCESS(31) (partial_identifier_start=%u, partial_identifier_length=%u,state_begin=%u, state_length=%u, state_address=%u, state_instruction=%u)", + current_address, p_id_start, p_id_length, state_begin, state_length, state_address, state_instruction); + } + current_address = next_operand_address; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_2 ){ + proto_tree_add_text(udvm_tree, message_tvb, input_address, 1, + " byte_copy_right = %u, byte_copy_left = %u", byte_copy_right,byte_copy_left); + } + + result_code = udvm_state_access(message_tvb, udvm_tree, buff, p_id_start, p_id_length, state_begin, &state_length, + &state_address, &state_instruction, hf_id); + if ( result_code != 0 ){ + goto decompression_failure; + } + used_udvm_cycles = used_udvm_cycles + 1 + state_length; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_STATE_CREATE: /* 32 */ + /* + * STATE-CREATE (%state_length, %state_address, %state_instruction, + * %minimum_access_length, %state_retention_priority) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-CREATE(32) (state_length, state_address, state_instruction,minimum_access_length, state_retention_priority)", + current_address); + } + operand_address = current_address + 1; + + /* + * %state_length + */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_length %u", + operand_address, state_length); + } + /* + * %state_address + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_address %u", + operand_address, state_address); + } + /* + * %state_instruction + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_instruction); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_instruction %u", + operand_address, state_instruction); + } + operand_address = next_operand_address; + /* + * %minimum_access_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &minimum_access_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u minimum_access_length %u", + operand_address, minimum_access_length); + } + operand_address = next_operand_address; + /* + * %state_retention_priority + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_retention_priority); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_retention_priority %u", + operand_address, state_retention_priority); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-CREATE(32) (state_length=%u, state_address=%u, state_instruction=%u,minimum_access_length=%u, state_retention_priority=%u)", + current_address, state_length, state_address, state_instruction,minimum_access_length, state_retention_priority); + } + current_address = next_operand_address; + /* Execute the instruction + * TODO Implement the instruction + * RFC3320: + * Note that the new state item cannot be created until a valid + * compartment identifier has been returned by the application. + * Consequently, when a STATE-CREATE instruction is encountered the UDVM + * simply buffers the five supplied operands until the END-MESSAGE + * instruction is reached. The steps taken at this point are described + * in Section 9.4.9. + * + * Decompression failure MUST occur if more than four state creation + * requests are made before the END-MESSAGE instruction is encountered. + * Decompression failure also occurs if the minimum_access_length does + * not lie between 6 and 20 inclusive, or if the + * state_retention_priority is 65535. + */ + no_of_state_create++; + if ( no_of_state_create > 4 ){ + result_code = 12; + goto decompression_failure; + } + if (( minimum_access_length < 6 ) || ( minimum_access_length > STATE_BUFFER_SIZE )){ + result_code = 1; + goto decompression_failure; + } + if ( state_retention_priority == 65535 ){ + result_code = 13; + goto decompression_failure; + } + state_length_buff[no_of_state_create] = state_length; + state_address_buff[no_of_state_create] = state_address; + state_instruction_buff[no_of_state_create] = state_instruction; + state_minimum_access_length_buff[no_of_state_create] = minimum_access_length; + state_state_retention_priority_buff[no_of_state_create] = state_retention_priority; + used_udvm_cycles = used_udvm_cycles + 1 + state_length; + /* Debug */ + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + n = 0; + k = state_address; + while ( n < state_length ){ + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + string[0]= buff[k]; + string[1]= '\0'; + if (print_level_3 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + " Addr: %5u State value: %u (0x%x) ASCII(%s)", + k,buff[k],buff[k],string); + } + k = ( k + 1 ) & 0xffff; + n++; + } + /* End debug */ + + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_STATE_FREE: /* 33 */ + /* + * STATE-FREE (%partial_identifier_start, %partial_identifier_length) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-FREE (partial_identifier_start, partial_identifier_length)", + current_address); + } + operand_address = current_address + 1; + /* + * %partial_identifier_start + */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &p_id_start); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u partial_identifier_start %u", + operand_address, p_id_start); + } + operand_address = next_operand_address; + + /* + * %partial_identifier_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &p_id_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u partial_identifier_length %u", + operand_address, p_id_length); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## STATE-FREE (partial_identifier_start=%u, partial_identifier_length=%u)", + current_address, p_id_start, p_id_length); + } + current_address = next_operand_address; + + /* Execute the instruction: + * TODO implement it + */ + udvm_state_free(buff,p_id_start,p_id_length); + used_udvm_cycles++; + + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_OUTPUT: /* 34 OUTPUT (%output_start, %output_length) */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## OUTPUT(34) (output_start, output_length)", + current_address); + } + operand_address = current_address + 1; + /* + * %output_start + */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &output_start); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u output_start %u", + operand_address, output_start); + } + operand_address = next_operand_address; + /* + * %output_length + */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &output_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u output_length %u", + operand_address, output_length); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## OUTPUT (output_start=%u, output_length=%u)", + current_address, output_start, output_length); + } + current_address = next_operand_address; + + /* + * Execute instruction + * 8.4. Byte copying + * : + * The string of bytes is copied in ascending order of memory address, + * respecting the bounds set by byte_copy_left and byte_copy_right. + * More precisely, if a byte is copied from/to Address m then the next + * byte is copied from/to Address n where n is calculated as follows: + * + * Set k := m + 1 (modulo 2^16) + * If k = byte_copy_right then set n := byte_copy_left, else set n := k + * + */ + + n = 0; + k = output_start; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + if (print_level_3 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + " byte_copy_right = %u", byte_copy_right); + } + while ( n < output_length ){ + + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + out_buff[output_address] = buff[k]; + string[0]= buff[k]; + string[1]= '\0'; + strp = string; + if (print_level_3 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + " Output value: %u (0x%x) ASCII(%s) from Addr: %u ,output to dispatcher position %u", + buff[k],buff[k],format_text(strp,1), k,output_address); + } + k = ( k + 1 ) & 0xffff; + output_address ++; + n++; + } + used_udvm_cycles = used_udvm_cycles + 1 + output_length; + goto execute_next_instruction; + break; + case SIGCOMP_INSTR_END_MESSAGE: /* 35 */ + /* + * END-MESSAGE (%requested_feedback_location, + * %returned_parameters_location, %state_length, %state_address, + * %state_instruction, %minimum_access_length, + * %state_retention_priority) + */ + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## END-MESSAGE (requested_feedback_location,state_instruction, minimum_access_length,state_retention_priority)", + current_address); + } + operand_address = current_address + 1; + + /* %requested_feedback_location */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &requested_feedback_location); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u requested_feedback_location %u", + operand_address, requested_feedback_location); + } + operand_address = next_operand_address; + /* returned_parameters_location */ + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &returned_parameters_location); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u returned_parameters_location %u", + operand_address, returned_parameters_location); + } + operand_address = next_operand_address; + /* + * %state_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_length %u", + operand_address, state_length); + } + /* + * %state_address + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_address); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_address %u", + operand_address, state_address); + } + /* + * %state_instruction + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_instruction); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_instruction %u", + operand_address, state_instruction); + } + + /* + * %minimum_access_length + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &minimum_access_length); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u minimum_access_length %u", + operand_address, minimum_access_length); + } + + /* + * %state_retention_priority + */ + operand_address = next_operand_address; + next_operand_address = decode_udvm_multitype_operand(buff, operand_address, &state_retention_priority); + if (show_instr_detail_level == 2 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"Addr: %u state_retention_priority %u", + operand_address, state_retention_priority); + } + if (show_instr_detail_level == 1) + { + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1, + "Addr: %u ## END-MESSAGE (requested_feedback_location=%u, returned_parameters_location=%u, state_length=%u, state_address=%u, state_instruction=%u, minimum_access_length=%u, state_retention_priority=%u)", + current_address, requested_feedback_location, returned_parameters_location, state_length, state_address, state_instruction, minimum_access_length,state_retention_priority); + } + current_address = next_operand_address; + /* TODO: This isn't currently totaly correct as END_INSTRUCTION might not create state */ + no_of_state_create++; + if ( no_of_state_create > 4 ){ + result_code = 12; + goto decompression_failure; + } + state_length_buff[no_of_state_create] = state_length; + state_address_buff[no_of_state_create] = state_address; + state_instruction_buff[no_of_state_create] = state_instruction; + /* Not used ? */ + state_minimum_access_length_buff[no_of_state_create] = minimum_access_length; + state_state_retention_priority_buff[no_of_state_create] = state_retention_priority; + + /* Execute the instruction + */ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"no_of_state_create %u",no_of_state_create); + if ( no_of_state_create != 0 ){ + memset(sha1_digest_buf, 0, STATE_BUFFER_SIZE); + n = 1; + byte_copy_right = buff[66] << 8; + byte_copy_right = byte_copy_right | buff[67]; + byte_copy_left = buff[64] << 8; + byte_copy_left = byte_copy_left | buff[65]; + while ( n < no_of_state_create + 1 ){ + sha1buff = g_malloc(state_length_buff[n]+8); + sha1buff[0] = state_length_buff[n] >> 8; + sha1buff[1] = state_length_buff[n] & 0xff; + sha1buff[2] = state_address_buff[n] >> 8; + sha1buff[3] = state_address_buff[n] & 0xff; + sha1buff[4] = state_instruction_buff[n] >> 8; + sha1buff[5] = state_instruction_buff[n] & 0xff; + sha1buff[6] = state_minimum_access_length_buff[n] >> 8; + sha1buff[7] = state_minimum_access_length_buff[n] & 0xff; + if (print_level_3 ){ + for( x=0; x < 8; x++){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"sha1buff %u 0x%x", + x,sha1buff[x]); + } + } + k = state_address_buff[n]; + for( x=0; x < state_length_buff[n]; x++) + { + if ( k == byte_copy_right ){ + k = byte_copy_left; + } + sha1buff[8+x] = buff[k]; + k = ( k + 1 ) & 0xffff; + } + + sha1_starts( &ctx ); + sha1_update( &ctx, (guint8 *) sha1buff, state_length_buff[n] + 8); + sha1_finish( &ctx, sha1_digest_buf ); + if (print_level_3 ){ + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"SHA1 digest %s",bytes_to_str(sha1_digest_buf, STATE_BUFFER_SIZE)); + + } + udvm_state_create(sha1buff, sha1_digest_buf, state_minimum_access_length_buff[n]); + proto_tree_add_text(udvm_tree,bytecode_tvb, 0, -1,"### Creating state ###"); + proto_tree_add_string(udvm_tree,hf_id, bytecode_tvb, 0, 0, bytes_to_str(sha1_digest_buf, state_minimum_access_length_buff[n])); + + n++; + + } + } + + + + /* At least something got decompressed, show it */ + decomp_tvb = tvb_new_real_data(out_buff,output_address,output_address); + /* Arrange that the allocated packet data copy be freed when the + * tvbuff is freed. + */ + tvb_set_free_cb( decomp_tvb, g_free ); + + tvb_set_child_real_data_tvbuff(message_tvb,decomp_tvb); + add_new_data_source(pinfo, decomp_tvb, "Decompressed SigComp message"); + /* + proto_tree_add_text(udvm_tree, decomp_tvb, 0, -1,"SigComp message Decompressed"); + */ + used_udvm_cycles = used_udvm_cycles + 1 + state_length; + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"maximum_UDVM_cycles %u used_udvm_cycles %u", + maximum_UDVM_cycles, used_udvm_cycles); + return decomp_tvb; + break; + + default: + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1," ### Addr %u Invalid instruction: %u (0x%x)", + current_address,current_instruction,current_instruction); + break; + } + g_free(out_buff); + return NULL; +decompression_failure: + + proto_tree_add_text(udvm_tree, bytecode_tvb, 0, -1,"DECOMPRESSION FAILURE: %s", + val_to_str(result_code, result_code_vals,"Unknown (%u)")); + g_free(out_buff); + return NULL; + +} + + /* The simplest operand type is the literal (#), which encodes a + * constant integer from 0 to 65535 inclusive. A literal operand may + * require between 1 and 3 bytes depending on its value. + * Bytecode: Operand value: Range: + * 0nnnnnnn N 0 - 127 + * 10nnnnnn nnnnnnnn N 0 - 16383 + * 11000000 nnnnnnnn nnnnnnnn N 0 - 65535 + * + * Figure 8: Bytecode for a literal (#) operand + * + */ +static int +decode_udvm_literal_operand(guint8 *buff,guint operand_address, guint16 *value) +{ + guint bytecode; + guint16 operand; + guint test_bits; + guint offset = operand_address; + guint8 temp_data; + + bytecode = buff[operand_address]; + test_bits = bytecode >> 7; + if (test_bits == 1){ + test_bits = bytecode >> 6; + if (test_bits == 2){ + /* + * 10nnnnnn nnnnnnnn N 0 - 16383 + */ + temp_data = buff[operand_address] & 0x1f; + operand = temp_data << 8; + temp_data = buff[operand_address + 1]; + operand = operand | temp_data; + *value = operand; + offset = offset + 2; + + }else{ + /* + * 111000000 nnnnnnnn nnnnnnnn N 0 - 65535 + */ + offset ++; + temp_data = buff[operand_address] & 0x1f; + operand = temp_data << 8; + temp_data = buff[operand_address + 1]; + operand = operand | temp_data; + *value = operand; + offset = offset + 2; + + } + }else{ + /* + * 0nnnnnnn N 0 - 127 + */ + operand = ( bytecode & 0x7f); + *value = operand; + offset ++; + } + + return offset; + +} + +/* + * The second operand type is the reference ($), which is always used to + * access a 2-byte value located elsewhere in the UDVM memory. The + * bytecode for a reference operand is decoded to be a constant integer + * from 0 to 65535 inclusive, which is interpreted as the memory address + * containing the actual value of the operand. + * Bytecode: Operand value: Range: + * + * 0nnnnnnn memory[2 * N] 0 - 65535 + * 10nnnnnn nnnnnnnn memory[2 * N] 0 - 65535 + * 11000000 nnnnnnnn nnnnnnnn memory[N] 0 - 65535 + * + * Figure 9: Bytecode for a reference ($) operand + */ +static int +dissect_udvm_reference_operand(guint8 *buff,guint operand_address, guint16 *value,guint *result_dest) +{ + guint bytecode; + guint16 operand; + guint offset = operand_address; + guint test_bits; + guint8 temp_data; + guint16 temp_data16; + + bytecode = buff[operand_address]; + test_bits = bytecode >> 7; + if (test_bits == 1){ + test_bits = bytecode >> 6; + if (test_bits == 2){ + /* + * 10nnnnnn nnnnnnnn memory[2 * N] 0 - 65535 + */ + temp_data = buff[operand_address] & 0x3f; + operand = temp_data << 8; + temp_data = buff[operand_address + 1]; + operand = operand | temp_data; + operand = (operand * 2); + *result_dest = operand; + temp_data16 = buff[operand] << 8; + temp_data16 = temp_data16 | buff[operand+1]; + *value = temp_data16; + offset = offset + 2; + + }else{ + /* + * 11000000 nnnnnnnn nnnnnnnn memory[N] 0 - 65535 + */ + operand_address++; + operand = buff[operand_address] << 8; + operand = operand | buff[operand_address + 1]; + *result_dest = operand; + temp_data16 = buff[operand] << 8; + temp_data16 = temp_data16 | buff[operand+1]; + *value = temp_data16; + offset = offset + 3; + + } + }else{ + /* + * 0nnnnnnn memory[2 * N] 0 - 65535 + */ + operand = ( bytecode & 0x7f); + operand = (operand * 2); + *result_dest = operand; + temp_data16 = buff[operand] << 8; + temp_data16 = temp_data16 | buff[operand+1]; + *value = temp_data16; + offset ++; + } + + return offset; +} + + /* RFC3320 + * Figure 10: Bytecode for a multitype (%) operand + * Bytecode: Operand value: Range: HEX val + * 00nnnnnn N 0 - 63 0x00 + * 01nnnnnn memory[2 * N] 0 - 65535 0x40 + * 1000011n 2 ^ (N + 6) 64 , 128 0x86 + * 10001nnn 2 ^ (N + 8) 256 , ... , 32768 0x88 + * 111nnnnn N + 65504 65504 - 65535 0xe0 + * 1001nnnn nnnnnnnn N + 61440 61440 - 65535 0x90 + * 101nnnnn nnnnnnnn N 0 - 8191 0xa0 + * 110nnnnn nnnnnnnn memory[N] 0 - 65535 0xc0 + * 10000000 nnnnnnnn nnnnnnnn N 0 - 65535 0x80 + * 10000001 nnnnnnnn nnnnnnnn memory[N] 0 - 65535 0x81 + */ +static int +decode_udvm_multitype_operand(guint8 *buff,guint operand_address, guint16 *value) +{ + guint test_bits; + guint bytecode; + guint offset = operand_address; + guint16 operand; + guint32 result; + guint8 temp_data; + guint16 temp_data16; + guint16 memmory_addr = 0; + + bytecode = buff[operand_address]; + test_bits = ( bytecode & 0xc0 ) >> 6; + switch (test_bits ){ + case 0: + /* + * 00nnnnnn N 0 - 63 + */ + operand = buff[operand_address]; + /* debug + *g_warning("Reading 0x%x From address %u",operand,offset); + */ + *value = operand; + offset ++; + break; + case 1: + /* + * 01nnnnnn memory[2 * N] 0 - 65535 + */ + memmory_addr = ( bytecode & 0x3f) * 2; + temp_data16 = buff[memmory_addr] << 8; + temp_data16 = temp_data16 | buff[memmory_addr+1]; + *value = temp_data16; + offset ++; + break; + case 2: + /* Check tree most significant bits */ + test_bits = ( bytecode & 0xe0 ) >> 5; + if ( test_bits == 5 ){ + /* + * 101nnnnn nnnnnnnn N 0 - 8191 + */ + temp_data = buff[operand_address] & 0x1f; + operand = temp_data << 8; + temp_data = buff[operand_address + 1]; + operand = operand | temp_data; + *value = operand; + offset = offset + 2; + }else{ + test_bits = ( bytecode & 0xf0 ) >> 4; + if ( test_bits == 9 ){ + /* + * 1001nnnn nnnnnnnn N + 61440 61440 - 65535 + */ + temp_data = buff[operand_address] & 0x0f; + operand = temp_data << 8; + temp_data = buff[operand_address + 1]; + operand = operand | temp_data; + operand = operand + 61440; + *value = operand; + offset = offset + 2; + }else{ + test_bits = ( bytecode & 0x08 ) >> 3; + if ( test_bits == 1){ + /* + * 10001nnn 2 ^ (N + 8) 256 , ... , 32768 + */ + + result = 1 << ((buff[operand_address] & 0x07) + 8); + operand = result & 0xffff; + *value = operand; + offset ++; + }else{ + test_bits = ( bytecode & 0x0e ) >> 1; + if ( test_bits == 3 ){ + /* + * 1000 011n 2 ^ (N + 6) 64 , 128 + */ + result = 1 << ((buff[operand_address] & 0x01) + 6); + operand = result & 0xffff; + *value = operand; + offset ++; + }else{ + /* + * 1000 0000 nnnnnnnn nnnnnnnn N 0 - 65535 + * 1000 0001 nnnnnnnn nnnnnnnn memory[N] 0 - 65535 + */ + offset ++; + temp_data16 = buff[operand_address + 1] << 8; + temp_data16 = temp_data16 | buff[operand_address + 2]; + /* debug + * g_warning("Reading 0x%x From address %u",temp_data16,operand_address); + */ + if ( (bytecode & 0x01) == 1 ){ + memmory_addr = temp_data16; + temp_data16 = buff[memmory_addr] << 8; + temp_data16 = temp_data16 | buff[memmory_addr+1]; + } + *value = temp_data16; + offset = offset +2; + } + + + } + } + } + break; + + case 3: + test_bits = ( bytecode & 0x20 ) >> 5; + if ( test_bits == 1 ){ + /* + * 111nnnnn N + 65504 65504 - 65535 + */ + operand = ( buff[operand_address] & 0x1f) + 65504; + *value = operand; + offset ++; + }else{ + /* + * 110nnnnn nnnnnnnn memory[N] 0 - 65535 + */ + memmory_addr = buff[operand_address] & 0x1f; + memmory_addr = memmory_addr << 8; + memmory_addr = memmory_addr | buff[operand_address + 1]; + temp_data16 = buff[memmory_addr] << 8; + temp_data16 = temp_data16 | buff[memmory_addr+1]; + *value = temp_data16; + /* debug + * g_warning("Reading 0x%x From address %u",temp_data16,memmory_addr); + */ + offset = offset +2; + } + + default : + break; + } + return offset; +} + /* + * + * The fourth operand type is the address (@). This operand is decoded + * as a multitype operand followed by a further step: the memory address + * of the UDVM instruction containing the address operand is added to + * obtain the correct operand value. So if the operand value from + * Figure 10 is D then the actual operand value of an address is + * calculated as follows: + * + * operand_value = (memory_address_of_instruction + D) modulo 2^16 + * + * Address operands are always used in instructions that control program + * flow, because they ensure that the UDVM bytecode is position- + * independent code (i.e., it will run independently of where it is + * placed in the UDVM memory). + */ +static int +decode_udvm_address_operand(guint8 *buff,guint operand_address, guint16 *value,guint current_address) +{ + guint32 result; + guint16 value1; + guint next_opreand_address; + + next_opreand_address = decode_udvm_multitype_operand(buff, operand_address, &value1); + result = value1 & 0xffff; + result = result + current_address; + *value = result & 0xffff; + return next_opreand_address; +} + + +/* + * This is a lookup table used to reverse the bits in a byte. + */ +static guint8 reverse [] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + + +static int +decomp_dispatch_get_bits( + tvbuff_t *message_tvb, + proto_tree *udvm_tree, + guint8 bit_order, + guint8 *buff, + guint16 *old_input_bit_order, + guint16 *remaining_bits, + guint16 *input_bits, + guint *input_address, + guint16 length, + guint16 *result_code, + guint msg_end) +{ + guint16 input_bit_order; + guint16 bits_still_required = length; + guint16 value = 0; + guint8 octet; + gint extra_bytes_available = msg_end - *input_address; + gint p_bit; + gint prev_p_bit = *old_input_bit_order & 0x0001; + gint bits_to_use = 0; + + + input_bit_order = buff[68] << 8; + input_bit_order = input_bit_order | buff[69]; + *result_code = 0; + p_bit = (input_bit_order & 0x0001) != 0; + + /* + * Discard any spare bits. + * Note: We take care to avoid remaining_bits having the value of 8. + */ + if (prev_p_bit != p_bit) + { + *remaining_bits = 0; + *old_input_bit_order = input_bit_order; + } + + /* + * Check we can suppy the required number of bits now, before we alter + * the input buffer's state. + */ + if (*remaining_bits + extra_bytes_available * 8 < length) + { + *result_code = 11; + return 0xfbad; + } + + /* Note: This is never called with length > 16, so the following loop + * never loops more than three time. */ + while (bits_still_required > 0) + { + /* + * We only put anything into input_bits if we know we will remove + * at least one bit. That ensures we can simply discard the spare + * bits if the P-bit changes. + */ + if (*remaining_bits == 0) + { + octet = tvb_get_guint8(message_tvb, *input_address); + if (print_level_1 ){ + proto_tree_add_text(udvm_tree, message_tvb, *input_address , 1, + " Geting value: %u (0x%x) From Addr: %u", octet, octet, *input_address); + } + *input_address = *input_address + 1; + + if (p_bit != 0) + { + octet = reverse[octet]; + } + *input_bits = octet; + *remaining_bits = 8; + } + + /* Add some more bits to the accumulated value. */ + bits_to_use = bits_still_required < *remaining_bits ? bits_still_required : *remaining_bits; + bits_still_required -= bits_to_use; + + *input_bits <<= bits_to_use; /* Shift bits into MSByte */ + value = (value << bits_to_use) /* Then add to the accumulated value */ + | ((*input_bits >> 8) & 0xFF); + *remaining_bits -= bits_to_use; + *input_bits &= 0x00FF; /* Leave just the remaining bits */ + } + + if (bit_order != 0) + { + /* Bit reverse the entire word. */ + guint16 lsb = reverse[(value >> 8) & 0xFF]; + guint16 msb = reverse[value & 0xFF]; + + value = ((msb << 8) | lsb) >> (16 - length); + } + + return value; +} + + +/* end udvm */ + diff --git a/src/main/resource/testFiles/wireshark/revFiles/c3a535_1153ff_print.c b/src/main/resource/testFiles/wireshark/revFiles/c3a535_1153ff_print.c new file mode 100644 index 0000000..46300e2 --- /dev/null +++ b/src/main/resource/testFiles/wireshark/revFiles/c3a535_1153ff_print.c @@ -0,0 +1,1739 @@ +/* print.c + * Routines for printing packet analysis trees. + * + * $Id$ + * + * Gilbert Ramirez + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "packet-range.h" +#include "print.h" +#include "isprint.h" +#include "ps.h" +#include "version_info.h" +#include +#include +#include +#include +#include + +#define PDML_VERSION "0" +#define PSML_VERSION "0" + +typedef struct { + int level; + print_stream_t *stream; + gboolean success; + GSList *src_list; + print_dissections_e print_dissections; + gboolean print_hex_for_data; + packet_char_enc encoding; + epan_dissect_t *edt; +} print_data; + +typedef struct { + int level; + FILE *fh; + GSList *src_list; + epan_dissect_t *edt; +} write_pdml_data; + +typedef struct { + output_fields_t* fields; + epan_dissect_t *edt; +} write_field_data_t; + +struct _output_fields { + gboolean print_header; + gchar separator; + gchar occurrence; + gchar aggregator; + GPtrArray* fields; + GHashTable* field_indicies; + emem_strbuf_t** field_values; + gchar quote; +}; + +GHashTable *output_only_tables = NULL; + +static gboolean write_headers = FALSE; + +static const gchar* get_field_hex_value(GSList* src_list, field_info *fi); +static void proto_tree_print_node(proto_node *node, gpointer data); +static void proto_tree_write_node_pdml(proto_node *node, gpointer data); +static const guint8 *get_field_data(GSList *src_list, field_info *fi); +static void write_pdml_field_hex_value(write_pdml_data *pdata, field_info *fi); +static gboolean print_hex_data_buffer(print_stream_t *stream, const guchar *cp, + guint length, packet_char_enc encoding); +static void ps_clean_string(unsigned char *out, const unsigned char *in, + int outbuf_size); +static void print_escaped_xml(FILE *fh, const char *unescaped_string); + +static void print_pdml_geninfo(proto_tree *tree, FILE *fh); + +static void proto_tree_get_node_field_values(proto_node *node, gpointer data); + +static FILE * +open_print_dest(int to_file, const char *dest) +{ + FILE *fh; + + /* Open the file or command for output */ + if (to_file) + fh = ws_fopen(dest, "w"); + else + fh = popen(dest, "w"); + + return fh; +} + +static gboolean +close_print_dest(int to_file, FILE *fh) +{ + /* Close the file or command */ + if (to_file) + return (fclose(fh) == 0); + else + return (pclose(fh) == 0); +} + +#define MAX_PS_LINE_LENGTH 256 + +gboolean +proto_tree_print(print_args_t *print_args, epan_dissect_t *edt, + print_stream_t *stream) +{ + print_data data; + + /* Create the output */ + data.level = 0; + data.stream = stream; + data.success = TRUE; + data.src_list = edt->pi.data_src; + data.encoding = edt->pi.fd->flags.encoding; + data.print_dissections = print_args->print_dissections; + /* If we're printing the entire packet in hex, don't + print uninterpreted data fields in hex as well. */ + data.print_hex_for_data = !print_args->print_hex; + data.edt = edt; + + proto_tree_children_foreach(edt->tree, proto_tree_print_node, &data); + return data.success; +} + +#define MAX_INDENT 160 + +/* Print a tree's data, and any child nodes. */ +static +void proto_tree_print_node(proto_node *node, gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + print_data *pdata = (print_data*) data; + const guint8 *pd; + gchar label_str[ITEM_LABEL_LENGTH]; + gchar *label_ptr; + + g_assert(fi && "dissection with an invisible proto tree?"); + + /* Don't print invisible entries. */ + if (PROTO_ITEM_IS_HIDDEN(node)) + return; + + /* Give up if we've already gotten an error. */ + if (!pdata->success) + return; + + /* was a free format label produced? */ + if (fi->rep) { + label_ptr = fi->rep->representation; + } + else { /* no, make a generic label */ + label_ptr = label_str; + proto_item_fill_label(fi, label_str); + } + + if (PROTO_ITEM_IS_GENERATED(node)) { + label_ptr = g_strdup_printf("[%s]", label_ptr); + } + + if (!print_line(pdata->stream, pdata->level, label_ptr)) { + pdata->success = FALSE; + return; + } + + /* + * If -O is specified, only display the protocols which are in the + * lookup table. Only check on the first level: once we start printing + * a tree, print the rest of the subtree. Otherwise we won't print + * subitems whose abbreviation doesn't match the protocol--for example + * text items (whose abbreviation is simply "text"). + */ + if (output_only_tables != NULL && pdata->level == 0 + && g_hash_table_lookup(output_only_tables, fi->hfinfo->abbrev) == NULL) { + pdata->success = TRUE; + return; + } + + if (PROTO_ITEM_IS_GENERATED(node)) { + g_free(label_ptr); + } + + /* If it's uninterpreted data, dump it (unless our caller will + be printing the entire packet in hex). */ + if (fi->hfinfo->id == proto_data && pdata->print_hex_for_data) { + /* + * Find the data for this field. + */ + pd = get_field_data(pdata->src_list, fi); + if (pd) { + if (!print_hex_data_buffer(pdata->stream, pd, + fi->length, pdata->encoding)) { + pdata->success = FALSE; + return; + } + } + } + + /* If we're printing all levels, or if this node is one with a + subtree and its subtree is expanded, recurse into the subtree, + if it exists. */ + g_assert(fi->tree_type >= -1 && fi->tree_type < num_tree_types); + if (pdata->print_dissections == print_dissections_expanded || + (pdata->print_dissections == print_dissections_as_displayed && + fi->tree_type >= 0 && tree_is_expanded[fi->tree_type])) { + if (node->first_child != NULL) { + pdata->level++; + proto_tree_children_foreach(node, + proto_tree_print_node, pdata); + pdata->level--; + if (!pdata->success) + return; + } + } +} + +#define PDML2HTML_XSL "pdml2html.xsl" +void +write_pdml_preamble(FILE *fh, const gchar* filename) +{ + time_t t=time(NULL); + char *ts=asctime(localtime(&t)); + ts[strlen(ts)-1]=0; /* overwrite \n */ + + fputs("\n", fh); + fputs("\n", fh); + fprintf(fh, "\n", get_datafile_dir()); + fputs("\n", PACKAGE, VERSION, ts, filename ? filename : ""); +} + +void +proto_tree_write_pdml(epan_dissect_t *edt, FILE *fh) +{ + write_pdml_data data; + + /* Create the output */ + data.level = 0; + data.fh = fh; + data.src_list = edt->pi.data_src; + data.edt = edt; + + fprintf(fh, "\n"); + + /* Print a "geninfo" protocol as required by PDML */ + print_pdml_geninfo(edt->tree, fh); + + proto_tree_children_foreach(edt->tree, proto_tree_write_node_pdml, + &data); + + fprintf(fh, "\n\n"); +} + +/* Write out a tree's data, and any child nodes, as PDML */ +static void +proto_tree_write_node_pdml(proto_node *node, gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + write_pdml_data *pdata = (write_pdml_data*) data; + const gchar *label_ptr; + gchar label_str[ITEM_LABEL_LENGTH]; + char *dfilter_string; + size_t chop_len; + int i; + gboolean wrap_in_fake_protocol; + + g_assert(fi && "dissection with an invisible proto tree?"); + + /* Will wrap up top-level field items inside a fake protocol wrapper to + preserve the PDML schema */ + wrap_in_fake_protocol = + (((fi->hfinfo->type != FT_PROTOCOL) || + (fi->hfinfo->id == proto_data)) && + (pdata->level == 0)); + + /* Indent to the correct level */ + for (i = -1; i < pdata->level; i++) { + fputs(" ", pdata->fh); + } + + if (wrap_in_fake_protocol) { + /* Open fake protocol wrapper */ + fputs("\n", pdata->fh); + + /* Indent to increased level before writing out field */ + pdata->level++; + for (i = -1; i < pdata->level; i++) { + fputs(" ", pdata->fh); + } + } + + /* Text label. It's printed as a field with no name. */ + if (fi->hfinfo->id == hf_text_only) { + /* Get the text */ + if (fi->rep) { + label_ptr = fi->rep->representation; + } + else { + label_ptr = ""; + } + + /* Show empty name since it is a required field */ + fputs("fh); + fputs("\" show=\"", pdata->fh); + print_escaped_xml(pdata->fh, label_ptr); + + fprintf(pdata->fh, "\" size=\"%d", fi->length); + if (node->parent && node->parent->finfo && (fi->start < node->parent->finfo->start)) { + fprintf(pdata->fh, "\" pos=\"%d", node->parent->finfo->start + fi->start); + } else { + fprintf(pdata->fh, "\" pos=\"%d", fi->start); + } + + fputs("\" value=\"", pdata->fh); + write_pdml_field_hex_value(pdata, fi); + + if (node->first_child != NULL) { + fputs("\">\n", pdata->fh); + } + else { + fputs("\"/>\n", pdata->fh); + } + } + + /* Uninterpreted data, i.e., the "Data" protocol, is + * printed as a field instead of a protocol. */ + else if (fi->hfinfo->id == proto_data) { + + /* Write out field with data */ + fputs("fh); + write_pdml_field_hex_value(pdata, fi); + fputs("\"/>\n", pdata->fh); + } + /* Normal protocols and fields */ + else { + if (fi->hfinfo->type == FT_PROTOCOL) { + fputs("fh); + } + else { + fputs("fh); + } + print_escaped_xml(pdata->fh, fi->hfinfo->abbrev); + +#if 0 + /* PDML spec, see: + * http://www.nbee.org/doku.php?id=netpdl:pdml_specification + * + * the show fields contains things in 'human readable' format + * showname: contains only the name of the field + * show: contains only the data of the field + * showdtl: contains additional details of the field data + * showmap: contains mappings of the field data (e.g. the hostname to an IP address) + * + * XXX - the showname shouldn't contain the field data itself + * (like it's contained in the fi->rep->representation). + * Unfortunately, we don't have the field data representation for + * all fields, so this isn't currently possible */ + fputs("\" showname=\"", pdata->fh); + print_escaped_xml(pdata->fh, fi->hfinfo->name); +#endif + + if (fi->rep) { + fputs("\" showname=\"", pdata->fh); + print_escaped_xml(pdata->fh, fi->rep->representation); + } + else { + label_ptr = label_str; + proto_item_fill_label(fi, label_str); + fputs("\" showname=\"", pdata->fh); + print_escaped_xml(pdata->fh, label_ptr); + } + + if (PROTO_ITEM_IS_HIDDEN(node)) + fprintf(pdata->fh, "\" hide=\"yes"); + + fprintf(pdata->fh, "\" size=\"%d", fi->length); + if (node->parent && node->parent->finfo && (fi->start < node->parent->finfo->start)) { + fprintf(pdata->fh, "\" pos=\"%d", node->parent->finfo->start + fi->start); + } else { + fprintf(pdata->fh, "\" pos=\"%d", fi->start); + } +/* fprintf(pdata->fh, "\" id=\"%d", fi->hfinfo->id);*/ + + /* show, value, and unmaskedvalue attributes */ + switch (fi->hfinfo->type) + { + case FT_PROTOCOL: + break; + case FT_NONE: + fputs("\" show=\"\" value=\"", pdata->fh); + break; + default: + /* XXX - this is a hack until we can just call + * fvalue_to_string_repr() for *all* FT_* types. */ + dfilter_string = proto_construct_match_selected_string(fi, + pdata->edt); + if (dfilter_string != NULL) { + chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */ + + /* XXX - Remove double-quotes. Again, once we + * can call fvalue_to_string_repr(), we can + * ask it not to produce the version for + * display-filters, and thus, no + * double-quotes. */ + if (dfilter_string[strlen(dfilter_string)-1] == '"') { + dfilter_string[strlen(dfilter_string)-1] = '\0'; + chop_len++; + } + + fputs("\" show=\"", pdata->fh); + print_escaped_xml(pdata->fh, &dfilter_string[chop_len]); + } + + /* + * XXX - should we omit "value" for any fields? + * What should we do for fields whose length is 0? + * They might come from a pseudo-header or from + * the capture header (e.g., time stamps), or + * they might be generated fields. + */ + if (fi->length > 0) { + fputs("\" value=\"", pdata->fh); + + if (fi->hfinfo->bitmask!=0) { + fprintf(pdata->fh, "%X", fvalue_get_uinteger(&fi->value)); + fputs("\" unmaskedvalue=\"", pdata->fh); + write_pdml_field_hex_value(pdata, fi); + } + else { + write_pdml_field_hex_value(pdata, fi); + } + } + } + + if (node->first_child != NULL) { + fputs("\">\n", pdata->fh); + } + else if (fi->hfinfo->id == proto_data) { + fputs("\">\n", pdata->fh); + } + else { + fputs("\"/>\n", pdata->fh); + } + } + + /* We always print all levels for PDML. Recurse here. */ + if (node->first_child != NULL) { + pdata->level++; + proto_tree_children_foreach(node, + proto_tree_write_node_pdml, pdata); + pdata->level--; + } + + /* Take back the extra level we added for fake wrapper protocol */ + if (wrap_in_fake_protocol) { + pdata->level--; + } + + if (node->first_child != NULL) { + /* Indent to correct level */ + for (i = -1; i < pdata->level; i++) { + fputs(" ", pdata->fh); + } + /* Close off current element */ + if (fi->hfinfo->id != proto_data) { /* Data protocol uses simple tags */ + if (fi->hfinfo->type == FT_PROTOCOL) { + fputs("\n", pdata->fh); + } + else { + fputs("\n", pdata->fh); + } + } else { + fputs("\n", pdata->fh); + } + } + + /* Close off fake wrapper protocol */ + if (wrap_in_fake_protocol) { + fputs("\n", pdata->fh); + } +} + +/* Print info for a 'geninfo' pseudo-protocol. This is required by + * the PDML spec. The information is contained in Wireshark's 'frame' protocol, + * but we produce a 'geninfo' protocol in the PDML to conform to spec. + * The 'frame' protocol follows the 'geninfo' protocol in the PDML. */ +static void +print_pdml_geninfo(proto_tree *tree, FILE *fh) +{ + guint32 num, len, caplen; + nstime_t *timestamp; + GPtrArray *finfo_array; + field_info *frame_finfo; + + /* Get frame protocol's finfo. */ + finfo_array = proto_find_finfo(tree, proto_frame); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + frame_finfo = (field_info *)finfo_array->pdata[0]; + g_ptr_array_free(finfo_array, TRUE); + + /* frame.number --> geninfo.num */ + finfo_array = proto_find_finfo(tree, hf_frame_number); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + num = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* frame.frame_len --> geninfo.len */ + finfo_array = proto_find_finfo(tree, hf_frame_len); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + len = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* frame.cap_len --> geninfo.caplen */ + finfo_array = proto_find_finfo(tree, hf_frame_capture_len); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + caplen = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* frame.time --> geninfo.timestamp */ + finfo_array = proto_find_finfo(tree, hf_frame_arrival_time); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + timestamp = (nstime_t *)fvalue_get(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* Print geninfo start */ + fprintf(fh, +" \n", + frame_finfo->length); + + /* Print geninfo.num */ + fprintf(fh, +" \n", + num, num, frame_finfo->length); + + /* Print geninfo.len */ + fprintf(fh, +" \n", + len, len, frame_finfo->length); + + /* Print geninfo.caplen */ + fprintf(fh, +" \n", + caplen, caplen, frame_finfo->length); + + /* Print geninfo.timestamp */ + fprintf(fh, +" \n", + abs_time_to_str(timestamp, ABSOLUTE_TIME_LOCAL, TRUE), (int) timestamp->secs, timestamp->nsecs, frame_finfo->length); + + /* Print geninfo end */ + fprintf(fh, +" \n"); +} + +void +write_pdml_finale(FILE *fh) +{ + fputs("\n", fh); +} + +void +write_psml_preamble(FILE *fh) +{ + fputs("\n", fh); + fputs("\n", PACKAGE, VERSION); + write_headers = TRUE; +} + +void +proto_tree_write_psml(epan_dissect_t *edt, FILE *fh) +{ + gint i; + + /* if this is the first packet, we have to create the PSML structure output */ + if(write_headers) { + fprintf(fh, "\n"); + + for(i=0; i < edt->pi.cinfo->num_cols; i++) { + fprintf(fh, "
"); + print_escaped_xml(fh, edt->pi.cinfo->col_title[i]); + fprintf(fh, "
\n"); + } + + fprintf(fh, "
\n\n"); + + write_headers = FALSE; + } + + fprintf(fh, "\n"); + + for(i=0; i < edt->pi.cinfo->num_cols; i++) { + fprintf(fh, "
"); + print_escaped_xml(fh, edt->pi.cinfo->col_data[i]); + fprintf(fh, "
\n"); + } + + fprintf(fh, "
\n\n"); +} + +void +write_psml_finale(FILE *fh) +{ + fputs("
\n", fh); +} + +void +write_csv_preamble(FILE *fh _U_) +{ + write_headers = TRUE; +} + +static gchar *csv_massage_str(const gchar *source, const gchar *exceptions) +{ + gchar *csv_str; + gchar *tmp_str; + + csv_str = g_strescape(source, exceptions); + tmp_str = csv_str; + while ( (tmp_str = strstr(tmp_str, "\\\"")) != NULL ) + *tmp_str = '\"'; + return csv_str; +} + +static void csv_write_str(const char *str, char sep, FILE *fh) +{ + gchar *csv_str; + + csv_str = csv_massage_str(str, NULL); + fprintf(fh, "\"%s\"%c", csv_str, sep); + g_free(csv_str); +} + +void +proto_tree_write_csv(epan_dissect_t *edt, FILE *fh) +{ + gint i; + + /* if this is the first packet, we have to write the CSV header */ + if(write_headers) { + for(i=0; i < edt->pi.cinfo->num_cols - 1; i++) + csv_write_str(edt->pi.cinfo->col_title[i], ',', fh); + csv_write_str(edt->pi.cinfo->col_title[i], '\n', fh); + write_headers = FALSE; + } + + for(i=0; i < edt->pi.cinfo->num_cols - 1; i++) + csv_write_str(edt->pi.cinfo->col_data[i], ',', fh); + csv_write_str(edt->pi.cinfo->col_data[i], '\n', fh); +} + +void +write_csv_finale(FILE *fh _U_) +{ + +} + +void +write_carrays_preamble(FILE *fh _U_) +{ + +} + +void +proto_tree_write_carrays(guint32 num, FILE *fh, epan_dissect_t *edt) +{ + guint32 i = 0, src_num = 0; + GSList *src_le; + data_source *src; + tvbuff_t *tvb; + const char *name; + const guchar *cp; + guint length; + char ascii[9]; + + for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) { + memset(ascii, 0, sizeof(ascii)); + src = (data_source *)src_le->data; + tvb = src->tvb; + length = tvb_length(tvb); + if (length == 0) + continue; + + cp = tvb_get_ptr(tvb, 0, length); + + name = get_data_source_name(src); + if (name) + fprintf(fh, "/* %s */\n", name); + if (src_num) { + fprintf(fh, "static const unsigned char pkt%u_%u[%u] = {\n", + num, src_num, length); + } else { + fprintf(fh, "static const unsigned char pkt%u[%u] = {\n", + num, length); + } + src_num++; + + for (i = 0; i < length; i++) { + fprintf(fh, "0x%02x", *(cp + i)); + ascii[i % 8] = isprint(*(cp + i)) ? *(cp + i) : '.'; + + if (i == (length - 1)) { + guint rem; + rem = length % 8; + if (rem) { + guint j; + for ( j = 0; j < 8 - rem; j++ ) + fprintf(fh, " "); + } + fprintf(fh, " /* %s */\n};\n\n", ascii); + break; + } + + if (!((i + 1) % 8)) { + fprintf(fh, ", /* %s */\n", ascii); + memset(ascii, 0, sizeof(ascii)); + } + else { + fprintf(fh, ", "); + } + } + } +} + +void +write_carrays_finale(FILE *fh _U_) +{ + +} + +/* + * Find the data source for a specified field, and return a pointer + * to the data in it. Returns NULL if the data is out of bounds. + */ +static const guint8 * +get_field_data(GSList *src_list, field_info *fi) +{ + GSList *src_le; + data_source *src; + tvbuff_t *src_tvb; + gint length, tvbuff_length; + + for (src_le = src_list; src_le != NULL; src_le = src_le->next) { + src = (data_source *)src_le->data; + src_tvb = src->tvb; + if (fi->ds_tvb == src_tvb) { + /* + * Found it. + * + * XXX - a field can have a length that runs past + * the end of the tvbuff. Ideally, that should + * be fixed when adding an item to the protocol + * tree, but checking the length when doing + * that could be expensive. Until we fix that, + * we'll do the check here. + */ + tvbuff_length = tvb_length_remaining(src_tvb, + fi->start); + if (tvbuff_length < 0) { + return NULL; + } + length = fi->length; + if (length > tvbuff_length) + length = tvbuff_length; + return tvb_get_ptr(src_tvb, fi->start, length); + } + } + g_assert_not_reached(); + return NULL; /* not found */ +} + +/* Print a string, escaping out certain characters that need to + * escaped out for XML. */ +static void +print_escaped_xml(FILE *fh, const char *unescaped_string) +{ + const char *p; + char temp_str[8]; + + for (p = unescaped_string; *p != '\0'; p++) { + switch (*p) { + case '&': + fputs("&", fh); + break; + case '<': + fputs("<", fh); + break; + case '>': + fputs(">", fh); + break; + case '"': + fputs(""", fh); + break; + case '\'': + fputs("'", fh); + break; + default: + if (g_ascii_isprint(*p)) + fputc(*p, fh); + else { + g_snprintf(temp_str, sizeof(temp_str), "\\x%x", (guint8)*p); + fputs(temp_str, fh); + } + } + } +} + +static void +write_pdml_field_hex_value(write_pdml_data *pdata, field_info *fi) +{ + int i; + const guint8 *pd; + + if (!fi->ds_tvb) + return; + + if (fi->length > tvb_length_remaining(fi->ds_tvb, fi->start)) { + fprintf(pdata->fh, "field length invalid!"); + return; + } + + /* Find the data for this field. */ + pd = get_field_data(pdata->src_list, fi); + + if (pd) { + /* Print a simple hex dump */ + for (i = 0 ; i < fi->length; i++) { + fprintf(pdata->fh, "%02x", pd[i]); + } + } +} + +gboolean +print_hex_data(print_stream_t *stream, epan_dissect_t *edt) +{ + gboolean multiple_sources; + GSList *src_le; + data_source *src; + tvbuff_t *tvb; + const char *name; + char *line; + const guchar *cp; + guint length; + + /* + * Set "multiple_sources" iff this frame has more than one + * data source; if it does, we need to print the name of + * the data source before printing the data from the + * data source. + */ + multiple_sources = (edt->pi.data_src->next != NULL); + + for (src_le = edt->pi.data_src; src_le != NULL; + src_le = src_le->next) { + src = (data_source *)src_le->data; + tvb = src->tvb; + if (multiple_sources) { + name = get_data_source_name(src); + print_line(stream, 0, ""); + line = g_strdup_printf("%s:", name); + print_line(stream, 0, line); + g_free(line); + } + length = tvb_length(tvb); + if (length == 0) + return TRUE; + cp = tvb_get_ptr(tvb, 0, length); + if (!print_hex_data_buffer(stream, cp, length, + edt->pi.fd->flags.encoding)) + return FALSE; + } + return TRUE; +} + +/* + * This routine is based on a routine created by Dan Lasley + * . + * + * It was modified for Wireshark by Gilbert Ramirez and others. + */ + +#define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */ +#define BYTES_PER_LINE 16 /* max byte values printed on a line */ +#define HEX_DUMP_LEN (BYTES_PER_LINE*3) + /* max number of characters hex dump takes - + 2 digits plus trailing blank */ +#define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE) + /* number of characters those bytes take; + 3 characters per byte of hex dump, + 2 blanks separating hex from ASCII, + 1 character per byte of ASCII dump */ +#define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN) + /* number of characters per line; + offset, 2 blanks separating offset + from data dump, data dump */ + +static gboolean +print_hex_data_buffer(print_stream_t *stream, const guchar *cp, + guint length, packet_char_enc encoding) +{ + register unsigned int ad, i, j, k, l; + guchar c; + guchar line[MAX_LINE_LEN + 1]; + unsigned int use_digits; + static guchar binhex[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + if (!print_line(stream, 0, "")) + return FALSE; + + /* + * How many of the leading digits of the offset will we supply? + * We always supply at least 4 digits, but if the maximum offset + * won't fit in 4 digits, we use as many digits as will be needed. + */ + if (((length - 1) & 0xF0000000) != 0) + use_digits = 8; /* need all 8 digits */ + else if (((length - 1) & 0x0F000000) != 0) + use_digits = 7; /* need 7 digits */ + else if (((length - 1) & 0x00F00000) != 0) + use_digits = 6; /* need 6 digits */ + else if (((length - 1) & 0x000F0000) != 0) + use_digits = 5; /* need 5 digits */ + else + use_digits = 4; /* we'll supply 4 digits */ + + ad = 0; + i = 0; + j = 0; + k = 0; + while (i < length) { + if ((i & 15) == 0) { + /* + * Start of a new line. + */ + j = 0; + l = use_digits; + do { + l--; + c = (ad >> (l*4)) & 0xF; + line[j++] = binhex[c]; + } while (l != 0); + line[j++] = ' '; + line[j++] = ' '; + memset(line+j, ' ', DATA_DUMP_LEN); + + /* + * Offset in line of ASCII dump. + */ + k = j + HEX_DUMP_LEN + 2; + } + c = *cp++; + line[j++] = binhex[c>>4]; + line[j++] = binhex[c&0xf]; + j++; + if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) { + c = EBCDIC_to_ASCII1(c); + } + line[k++] = c >= ' ' && c < 0x7f ? c : '.'; + i++; + if ((i & 15) == 0 || i == length) { + /* + * We'll be starting a new line, or + * we're finished printing this buffer; + * dump out the line we've constructed, + * and advance the offset. + */ + line[k] = '\0'; + if (!print_line(stream, 0, line)) + return FALSE; + ad += 16; + } + } + return TRUE; +} + +static +void ps_clean_string(unsigned char *out, const unsigned char *in, + int outbuf_size) +{ + int rd, wr; + char c; + + if (in == NULL) { + out[0] = '\0'; + return; + } + + for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) { + c = in[rd]; + switch (c) { + case '(': + case ')': + case '\\': + out[wr] = '\\'; + out[++wr] = c; + break; + + default: + out[wr] = c; + break; + } + + if (c == 0) { + break; + } + } +} + +/* Some formats need stuff at the beginning of the output */ +gboolean +print_preamble(print_stream_t *self, gchar *filename) +{ + return (self->ops->print_preamble)(self, filename); +} + +gboolean +print_line(print_stream_t *self, int indent, const char *line) +{ + return (self->ops->print_line)(self, indent, line); +} + +/* Insert bookmark */ +gboolean +print_bookmark(print_stream_t *self, const gchar *name, const gchar *title) +{ + return (self->ops->print_bookmark)(self, name, title); +} + +gboolean +new_page(print_stream_t *self) +{ + return (self->ops->new_page)(self); +} + +/* Some formats need stuff at the end of the output */ +gboolean +print_finale(print_stream_t *self) +{ + return (self->ops->print_finale)(self); +} + +gboolean +destroy_print_stream(print_stream_t *self) +{ + return (self->ops->destroy)(self); +} + +typedef struct { + int to_file; + FILE *fh; +} output_text; + +static gboolean +print_preamble_text(print_stream_t *self _U_, gchar *filename _U_) +{ + /* do nothing */ + return TRUE; /* always succeeds */ +} + +static gboolean +print_line_text(print_stream_t *self, int indent, const char *line) +{ + output_text *output = (output_text *)self->data; + char space[MAX_INDENT+1]; + int i; + int num_spaces; + + /* Prepare the tabs for printing, depending on tree level */ + num_spaces = indent * 4; + if (num_spaces > MAX_INDENT) { + num_spaces = MAX_INDENT; + } + for (i = 0; i < num_spaces; i++) { + space[i] = ' '; + } + /* The string is NUL-terminated */ + space[num_spaces] = '\0'; + + fputs(space, output->fh); + fputs(line, output->fh); + putc('\n', output->fh); + return !ferror(output->fh); +} + +static gboolean +print_bookmark_text(print_stream_t *self _U_, const gchar *name _U_, + const gchar *title _U_) +{ + /* do nothing */ + return TRUE; +} + +static gboolean +new_page_text(print_stream_t *self) +{ + output_text *output = (output_text *)self->data; + + fputs("\f", output->fh); + return !ferror(output->fh); +} + +static gboolean +print_finale_text(print_stream_t *self _U_) +{ + /* do nothing */ + return TRUE; /* always succeeds */ +} + +static gboolean +destroy_text(print_stream_t *self) +{ + output_text *output = (output_text *)self->data; + gboolean ret; + + ret = close_print_dest(output->to_file, output->fh); + g_free(output); + g_free(self); + return ret; +} + +static const print_stream_ops_t print_text_ops = { + print_preamble_text, + print_line_text, + print_bookmark_text, + new_page_text, + print_finale_text, + destroy_text +}; + +static print_stream_t * +print_stream_text_alloc(int to_file, FILE *fh) +{ + print_stream_t *stream; + output_text *output; + + output = (output_text *)g_malloc(sizeof *output); + output->to_file = to_file; + output->fh = fh; + stream = (print_stream_t *)g_malloc(sizeof (print_stream_t)); + stream->ops = &print_text_ops; + stream->data = output; + + return stream; +} + +print_stream_t * +print_stream_text_new(int to_file, const char *dest) +{ + FILE *fh; + + fh = open_print_dest(to_file, dest); + if (fh == NULL) + return NULL; + + return print_stream_text_alloc(to_file, fh); +} + +print_stream_t * +print_stream_text_stdio_new(FILE *fh) +{ + return print_stream_text_alloc(TRUE, fh); +} + +typedef struct { + int to_file; + FILE *fh; +} output_ps; + +static gboolean +print_preamble_ps(print_stream_t *self, gchar *filename) +{ + output_ps *output = (output_ps *)self->data; + unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */ + + print_ps_preamble(output->fh); + + fputs("%% the page title\n", output->fh); + ps_clean_string(psbuffer, filename, MAX_PS_LINE_LENGTH); + fprintf(output->fh, "/ws_pagetitle (%s - Wireshark " VERSION "%s) def\n", psbuffer, wireshark_svnversion); + fputs("\n", output->fh); + return !ferror(output->fh); +} + +static gboolean +print_line_ps(print_stream_t *self, int indent, const char *line) +{ + output_ps *output = (output_ps *)self->data; + unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */ + + ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH); + fprintf(output->fh, "%d (%s) putline\n", indent, psbuffer); + return !ferror(output->fh); +} + +static gboolean +print_bookmark_ps(print_stream_t *self, const gchar *name, const gchar *title) +{ + output_ps *output = (output_ps *)self->data; + unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */ + + /* + * See the Adobe "pdfmark reference": + * + * http://partners.adobe.com/asn/acrobat/docs/pdfmark.pdf + * + * The pdfmark stuff tells code that turns PostScript into PDF + * things that it should do. + * + * The /OUT stuff creates a bookmark that goes to the + * destination with "name" as the name and "title" as the title. + * + * The "/DEST" creates the destination. + */ + ps_clean_string(psbuffer, title, MAX_PS_LINE_LENGTH); + fprintf(output->fh, "[/Dest /%s /Title (%s) /OUT pdfmark\n", name, + psbuffer); + fputs("[/View [/XYZ -4 currentpoint matrix currentmatrix matrix defaultmatrix\n", + output->fh); + fputs("matrix invertmatrix matrix concatmatrix transform exch pop 20 add null]\n", + output->fh); + fprintf(output->fh, "/Dest /%s /DEST pdfmark\n", name); + return !ferror(output->fh); +} + +static gboolean +new_page_ps(print_stream_t *self) +{ + output_ps *output = (output_ps *)self->data; + + fputs("formfeed\n", output->fh); + return !ferror(output->fh); +} + +static gboolean +print_finale_ps(print_stream_t *self) +{ + output_ps *output = (output_ps *)self->data; + + print_ps_finale(output->fh); + return !ferror(output->fh); +} + +static gboolean +destroy_ps(print_stream_t *self) +{ + output_ps *output = (output_ps *)self->data; + gboolean ret; + + ret = close_print_dest(output->to_file, output->fh); + g_free(output); + g_free(self); + return ret; +} + +static const print_stream_ops_t print_ps_ops = { + print_preamble_ps, + print_line_ps, + print_bookmark_ps, + new_page_ps, + print_finale_ps, + destroy_ps +}; + +static print_stream_t * +print_stream_ps_alloc(int to_file, FILE *fh) +{ + print_stream_t *stream; + output_ps *output; + + output = (output_ps *)g_malloc(sizeof *output); + output->to_file = to_file; + output->fh = fh; + stream = (print_stream_t *)g_malloc(sizeof (print_stream_t)); + stream->ops = &print_ps_ops; + stream->data = output; + + return stream; +} + +print_stream_t * +print_stream_ps_new(int to_file, const char *dest) +{ + FILE *fh; + + fh = open_print_dest(to_file, dest); + if (fh == NULL) + return NULL; + + return print_stream_ps_alloc(to_file, fh); +} + +print_stream_t * +print_stream_ps_stdio_new(FILE *fh) +{ + return print_stream_ps_alloc(TRUE, fh); +} + +output_fields_t* output_fields_new(void) +{ + output_fields_t* fields = g_new(output_fields_t, 1); + fields->print_header = FALSE; + fields->separator = '\t'; + fields->occurrence = 'a'; + fields->aggregator = ','; + fields->fields = NULL; /*Do lazy initialisation */ + fields->field_indicies = NULL; + fields->field_values = NULL; + fields->quote='\0'; + return fields; +} + +gsize output_fields_num_fields(output_fields_t* fields) +{ + g_assert(fields); + + if(NULL == fields->fields) { + return 0; + } else { + return fields->fields->len; + } +} + +void output_fields_free(output_fields_t* fields) +{ + g_assert(fields); + + if(NULL != fields->field_indicies) { + /* Keys are stored in fields->fields, values are + * integers. + */ + g_hash_table_destroy(fields->field_indicies); + } + if(NULL != fields->fields) { + gsize i; + for(i = 0; i < fields->fields->len; ++i) { + gchar* field = (gchar *)g_ptr_array_index(fields->fields,i); + g_free(field); + } + g_ptr_array_free(fields->fields, TRUE); + } + + g_free(fields); +} + +void output_fields_add(output_fields_t* fields, const gchar* field) +{ + gchar* field_copy; + + g_assert(fields); + g_assert(field); + + + if(NULL == fields->fields) { + fields->fields = g_ptr_array_new(); + } + + field_copy = g_strdup(field); + + g_ptr_array_add(fields->fields, field_copy); +} + +gboolean output_fields_set_option(output_fields_t* info, gchar* option) +{ + const gchar* option_name; + const gchar* option_value; + + g_assert(info); + g_assert(option); + + if('\0' == *option) { + return FALSE; /* Is this guarded against by option parsing? */ + } + option_name = strtok(option,"="); + if (!option_name) { + return FALSE; + } + option_value = option + strlen(option_name) + 1; + if(0 == strcmp(option_name, "header")) { + switch(NULL == option_value ? '\0' : *option_value) { + case 'n': + info->print_header = FALSE; + break; + case 'y': + info->print_header = TRUE; + break; + default: + return FALSE; + } + return TRUE; + } + + if(0 == strcmp(option_name,"separator")) { + switch(NULL == option_value ? '\0' : *option_value) { + case '\0': + return FALSE; + case '/': + switch(*++option_value) { + case 't': + info->separator = '\t'; + break; + case 's': + info->separator = ' '; + break; + default: + info->separator = '\\'; + } + break; + default: + info->separator = *option_value; + break; + } + return TRUE; + } + + if(0 == strcmp(option_name, "occurrence")) { + switch(NULL == option_value ? '\0' : *option_value) { + case 'f': + case 'l': + case 'a': + info->occurrence = *option_value; + break; + default: + return FALSE; + } + return TRUE; + } + + if(0 == strcmp(option_name,"aggregator")) { + switch(NULL == option_value ? '\0' : *option_value) { + case '\0': + return FALSE; + case '/': + switch(*++option_value) { + case 's': + info->aggregator = ' '; + break; + default: + info->aggregator = '\\'; + } + break; + default: + info->aggregator = *option_value; + break; + } + return TRUE; + } + + if(0 == strcmp(option_name, "quote")) { + switch(NULL == option_value ? '\0' : *option_value) { + default: /* Fall through */ + case '\0': + info->quote='\0'; + return FALSE; + case 'd': + info->quote='"'; + break; + case 's': + info->quote='\''; + break; + case 'n': + info->quote='\0'; + break; + } + return TRUE; + } + + return FALSE; +} + +void output_fields_list_options(FILE *fh) +{ + fprintf(fh, "TShark: The available options for field output \"E\" are:\n"); + fputs("header=y|n Print field abbreviations as first line of output (def: N: no)\n", fh); + fputs("separator=/t|/s| Set the separator to use;\n \"/t\" = tab, \"/s\" = space (def: /t: tab)\n", fh); + fputs("occurrence=f|l|a Select the occurrence of a field to use;\n \"f\" = first, \"l\" = last, \"a\" = all (def: a: all)\n", fh); + fputs("aggregator=,|/s| Set the aggregator to use;\n \",\" = comma, \"/s\" = space (def: ,: comma)\n", fh); + fputs("quote=d|s|n Print either d: double-quotes, s: single quotes or \n n: no quotes around field values (def: n: none)\n", fh); +} + + +void write_fields_preamble(output_fields_t* fields, FILE *fh) +{ + gsize i; + + g_assert(fields); + g_assert(fh); + + if(!fields->print_header) { + return; + } + + for(i = 0; i < fields->fields->len; ++i) { + const gchar* field = (const gchar *)g_ptr_array_index(fields->fields,i); + if(i != 0 ) { + fputc(fields->separator, fh); + } + fputs(field, fh); + } + fputc('\n', fh); +} + +static void proto_tree_get_node_field_values(proto_node *node, gpointer data) +{ + write_field_data_t *call_data; + field_info *fi; + gpointer field_index; + + call_data = (write_field_data_t *)data; + fi = PNODE_FINFO(node); + + g_assert(fi && "dissection with an invisible proto tree?"); + + field_index = g_hash_table_lookup(call_data->fields->field_indicies, fi->hfinfo->abbrev); + if(NULL != field_index) { + const gchar* value; + + value = get_node_field_value(fi, call_data->edt); /* ep_alloced string */ + + if(NULL != value && '\0' != *value) { + guint actual_index; + actual_index = GPOINTER_TO_UINT(field_index); + /* Unwrap change made to disambiguiate zero / null */ + if ( call_data->fields->field_values[actual_index - 1] == NULL ) { + call_data->fields->field_values[actual_index - 1] = ep_strbuf_new(value); + } else if ( call_data->fields->occurrence == 'l' ) { + /* print only the value of the last occurrence of the field */ + ep_strbuf_printf(call_data->fields->field_values[actual_index - 1],"%s",value); + } else if ( call_data->fields->occurrence == 'a' ) { + /* print the value of all accurrences of the field */ + ep_strbuf_append_printf(call_data->fields->field_values[actual_index - 1], + "%c%s",call_data->fields->aggregator,value); + } + } + } + + /* Recurse here. */ + if (node->first_child != NULL) { + proto_tree_children_foreach(node, proto_tree_get_node_field_values, + call_data); + } +} + +void proto_tree_write_fields(output_fields_t* fields, epan_dissect_t *edt, FILE *fh) +{ + gsize i; + + write_field_data_t data; + + g_assert(fields); + g_assert(edt); + g_assert(fh); + + data.fields = fields; + data.edt = edt; + + if(NULL == fields->field_indicies) { + /* Prepare a lookup table from string abbreviation for field to its index. */ + fields->field_indicies = g_hash_table_new(g_str_hash, g_str_equal); + + i = 0; + while( i < fields->fields->len) { + gchar* field = (gchar *)g_ptr_array_index(fields->fields, i); + /* Store field indicies +1 so that zero is not a valid value, + * and can be distinguished from NULL as a pointer. + */ + ++i; + g_hash_table_insert(fields->field_indicies, field, GUINT_TO_POINTER(i)); + } + } + + /* Buffer to store values for this packet */ + fields->field_values = ep_alloc_array0(emem_strbuf_t*, fields->fields->len); + + proto_tree_children_foreach(edt->tree, proto_tree_get_node_field_values, + &data); + + for(i = 0; i < fields->fields->len; ++i) { + if(0 != i) { + fputc(fields->separator, fh); + } + if(NULL != fields->field_values[i]) { + if(fields->quote != '\0') { + fputc(fields->quote, fh); + } + fputs(fields->field_values[i]->str, fh); + if(fields->quote != '\0') { + fputc(fields->quote, fh); + } + } + } +} + +void write_fields_finale(output_fields_t* fields _U_ , FILE *fh _U_) +{ + /* Nothing to do */ +} + +/* Returns an ep_alloced string or a static constant*/ +const gchar* get_node_field_value(field_info* fi, epan_dissect_t* edt) +{ + if (fi->hfinfo->id == hf_text_only) { + /* Text label. + * Get the text */ + if (fi->rep) { + return fi->rep->representation; + } + else { + return get_field_hex_value(edt->pi.data_src, fi); + } + } + else if (fi->hfinfo->id == proto_data) { + /* Uninterpreted data, i.e., the "Data" protocol, is + * printed as a field instead of a protocol. */ + return get_field_hex_value(edt->pi.data_src, fi); + } + else { + /* Normal protocols and fields */ + gchar *dfilter_string; + size_t chop_len; + + switch (fi->hfinfo->type) + { + case FT_PROTOCOL: + /* Print out the full details for the protocol. */ + if (fi->rep) { + return fi->rep->representation; + } else { + /* Just print out the protocol abbreviation */ + return fi->hfinfo->abbrev;; + } + case FT_NONE: + /* Return "1" so that the presence of a field of type + * FT_NONE can be checked when using -T fields */ + return "1"; + default: + /* XXX - this is a hack until we can just call + * fvalue_to_string_repr() for *all* FT_* types. */ + dfilter_string = proto_construct_match_selected_string(fi, + edt); + if (dfilter_string != NULL) { + chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */ + + /* XXX - Remove double-quotes. Again, once we + * can call fvalue_to_string_repr(), we can + * ask it not to produce the version for + * display-filters, and thus, no + * double-quotes. */ + if (dfilter_string[strlen(dfilter_string)-1] == '"') { + dfilter_string[strlen(dfilter_string)-1] = '\0'; + chop_len++; + } + + return &(dfilter_string[chop_len]); + } else { + return get_field_hex_value(edt->pi.data_src, fi); + } + } + } +} + +static const gchar* +get_field_hex_value(GSList* src_list, field_info *fi) +{ + const guint8 *pd; + + if (!fi->ds_tvb) + return NULL; + + if (fi->length > tvb_length_remaining(fi->ds_tvb, fi->start)) { + return "field length invalid!"; + } + + /* Find the data for this field. */ + pd = get_field_data(src_list, fi); + + if (pd) { + int i; + gchar* buffer; + gchar* p; + int len; + const int chars_per_byte = 2; + + len = chars_per_byte * fi->length; + buffer = ep_alloc_array(gchar, len + 1); + buffer[len] = '\0'; /* Ensure NULL termination in bad cases */ + p = buffer; + /* Print a simple hex dump */ + for (i = 0 ; i < fi->length; i++) { + g_snprintf(p, chars_per_byte+1, "%02x", pd[i]); + p += chars_per_byte; + } + return buffer; + } else { + return NULL; + } +}